Minor UI improvements

This commit is contained in:
AG
2025-10-15 07:44:57 +03:00
parent e361a278ef
commit 9096e7db38
3 changed files with 65 additions and 23 deletions

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Button, SxProps, Theme } from '@mui/material'; import { Button, SxProps, Theme, Snackbar, Alert } from '@mui/material';
import ContentCopyIcon from '@mui/icons-material/ContentCopy'; import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
@@ -10,10 +10,15 @@ interface CopyLinkButtonProps {
const CopyLinkButton: React.FC<CopyLinkButtonProps> = ({ linkToCopy, sx }) => { const CopyLinkButton: React.FC<CopyLinkButtonProps> = ({ linkToCopy, sx }) => {
const [copied, setCopied] = useState(false); const [copied, setCopied] = useState(false);
const [alertOpen, setAlertOpen] = useState(false);
const [alertMessage, setAlertMessage] = useState('');
const [alertSeverity, setAlertSeverity] = useState<'error' | 'warning' | 'info' | 'success'>('error');
const handleCopy = async () => { const handleCopy = async () => {
if (!navigator.clipboard || !navigator.clipboard.writeText) { if (!navigator.clipboard || !navigator.clipboard.writeText) {
alert('Clipboard API not supported in this browser. Please copy the link manually:' + linkToCopy); setAlertMessage('Clipboard API not supported in this browser. Please copy the link manually.');
setAlertSeverity('warning');
setAlertOpen(true);
console.warn('Clipboard API not supported.'); console.warn('Clipboard API not supported.');
return; return;
} }
@@ -21,13 +26,25 @@ const CopyLinkButton: React.FC<CopyLinkButtonProps> = ({ linkToCopy, sx }) => {
try { try {
await navigator.clipboard.writeText(linkToCopy); await navigator.clipboard.writeText(linkToCopy);
setCopied(true); setCopied(true);
setAlertMessage('Link copied to clipboard!');
setAlertSeverity('success');
setAlertOpen(true);
console.log('Link copied to clipboard:', linkToCopy); console.log('Link copied to clipboard:', linkToCopy);
} catch (err) { } catch (err) {
console.error('Failed to copy link:', err); console.error('Failed to copy link:', err);
alert('Failed to copy the link. Please try again or copy manually.'); setAlertMessage('Failed to copy the link. Please try again or copy manually.');
setAlertSeverity('error');
setAlertOpen(true);
} }
}; };
const handleCloseAlert = (event?: React.SyntheticEvent | Event, reason?: string) => {
if (reason === 'clickaway') {
return;
}
setAlertOpen(false);
};
useEffect(() => { useEffect(() => {
let timer: NodeJS.Timeout; let timer: NodeJS.Timeout;
if (copied) { if (copied) {
@@ -39,19 +56,26 @@ const CopyLinkButton: React.FC<CopyLinkButtonProps> = ({ linkToCopy, sx }) => {
}, [copied]); }, [copied]);
return ( return (
<Button <>
variant="outlined" <Button
onClick={handleCopy} variant="outlined"
startIcon={copied ? <CheckCircleOutlineIcon color="success" /> : <ContentCopyIcon />} onClick={handleCopy}
color={copied ? "success" : "primary"} startIcon={copied ? <CheckCircleOutlineIcon color="success" /> : <ContentCopyIcon />}
sx={{ color={copied ? "success" : "primary"}
minWidth: 120, sx={{
textTransform: 'none', minWidth: 120,
...(sx ? sx : {}), textTransform: 'none',
}} ...(sx ? sx : {}),
> }}
{copied ? 'Copied!' : 'Copy Link'} >
</Button> {copied ? 'Copied!' : 'Copy Link'}
</Button>
<Snackbar open={alertOpen} autoHideDuration={6000} onClose={handleCloseAlert} anchorOrigin={{ vertical: 'top', horizontal: 'right' }} sx={{ mt: 8 }}>
<Alert onClose={handleCloseAlert} severity={alertSeverity} sx={{ width: '100%' }}>
{alertMessage}
</Alert>
</Snackbar>
</>
); );
}; };

View File

@@ -1,5 +1,5 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { TextField, Button, Box, Typography } from '@mui/material'; import { TextField, Button, Box, Typography, Snackbar, Alert } from '@mui/material';
interface DesireFormProps { interface DesireFormProps {
onSubmit: (desires: { wants: string[], accepts: string[], noGoes: string[], afraidToAsk: string }) => void; onSubmit: (desires: { wants: string[], accepts: string[], noGoes: string[], afraidToAsk: string }) => void;
@@ -10,6 +10,16 @@ const DesireForm: React.FC<DesireFormProps> = ({ onSubmit }) => {
const [accepts, setAccepts] = useState(''); const [accepts, setAccepts] = useState('');
const [noGoes, setNoGoes] = useState(''); const [noGoes, setNoGoes] = useState('');
const [afraidToAsk, setAfraidToAsk] = useState(''); const [afraidToAsk, setAfraidToAsk] = useState('');
const [alertOpen, setAlertOpen] = useState(false);
const [alertMessage, setAlertMessage] = useState('');
const [alertSeverity, setAlertSeverity] = useState<'error' | 'warning' | 'info' | 'success'>('error');
const handleCloseAlert = (event?: React.SyntheticEvent | Event, reason?: string) => {
if (reason === 'clickaway') {
return;
}
setAlertOpen(false);
};
const handleSubmit = (event: React.FormEvent) => { const handleSubmit = (event: React.FormEvent) => {
event.preventDefault(); event.preventDefault();
@@ -19,7 +29,9 @@ const DesireForm: React.FC<DesireFormProps> = ({ onSubmit }) => {
// FR-020: The system MUST require a user to enter at least one desire in at least one of the three categories // FR-020: The system MUST require a user to enter at least one desire in at least one of the three categories
if (parsedWants.length === 0 && parsedAccepts.length === 0 && parsedNoGoes.length === 0 && afraidToAsk.length === 0) { if (parsedWants.length === 0 && parsedAccepts.length === 0 && parsedNoGoes.length === 0 && afraidToAsk.length === 0) {
alert('Please enter at least one desire in any category.'); setAlertMessage('Please enter at least one desire in any category.');
setAlertSeverity('error');
setAlertOpen(true);
return; return;
} }
@@ -27,7 +39,9 @@ const DesireForm: React.FC<DesireFormProps> = ({ onSubmit }) => {
const allItems = [...parsedWants, ...parsedAccepts, ...parsedNoGoes]; const allItems = [...parsedWants, ...parsedAccepts, ...parsedNoGoes];
const uniqueItems = new Set(allItems); const uniqueItems = new Set(allItems);
if (allItems.length !== uniqueItems.size) { if (allItems.length !== uniqueItems.size) {
alert('You have conflicting desires (same item in different categories). Please resolve.'); setAlertMessage('You have conflicting desires (same item in different categories). Please resolve.');
setAlertSeverity('error');
setAlertOpen(true);
return; return;
} }
@@ -40,7 +54,7 @@ const DesireForm: React.FC<DesireFormProps> = ({ onSubmit }) => {
}; };
return ( return (
<Box component="form" onSubmit={handleSubmit} sx={{ mt: 3 }}> <Box component="form" onSubmit={handleSubmit} sx={{ mt: 3, display: 'flex', flexDirection: 'column' }}>
<Typography variant="h6" gutterBottom>What You Want</Typography> <Typography variant="h6" gutterBottom>What You Want</Typography>
<TextField <TextField
multiline multiline
@@ -91,12 +105,16 @@ const DesireForm: React.FC<DesireFormProps> = ({ onSubmit }) => {
<Button <Button
type="submit" type="submit"
fullWidth
variant="contained" variant="contained"
sx={{ mt: 3, mb: 2 }} sx={{ mt: 3, mb: 2, textTransform: 'none', alignSelf: 'flex-start' }}
> >
Submit Desires Submit Desires
</Button> </Button>
<Snackbar open={alertOpen} autoHideDuration={6000} onClose={handleCloseAlert} anchorOrigin={{ vertical: 'top', horizontal: 'right' }} sx={{ mt: 8 }}>
<Alert onClose={handleCloseAlert} severity={alertSeverity} sx={{ width: '100%' }}>
{alertMessage}
</Alert>
</Snackbar>
</Box> </Box>
); );
}; };

View File

@@ -112,7 +112,7 @@ const SessionPage = () => {
<Button <Button
type="button" type="button"
variant="contained" variant="contained"
sx={{ mt: 2 }} sx={{ mt: 2, textTransform: 'none' }}
onClick={handleSetupSession} onClick={handleSetupSession}
> >
Start Session Start Session