Files
unisono/frontend/src/components/DesireForm.tsx

133 lines
5.5 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { TextField, Button, Box, Typography, Snackbar, Alert } from '@mui/material';
interface DesireFormProps {
onSubmit: (desires: { wants: string[], accepts: string[], noGoes: string[], afraidToAsk: string }) => void;
externalError?: string | null;
}
const DesireForm: React.FC<DesireFormProps> = ({ onSubmit, externalError }) => {
const [wants, setWants] = useState('');
const [accepts, setAccepts] = useState('');
const [noGoes, setNoGoes] = useState('');
const [afraidToAsk, setAfraidToAsk] = useState('');
const [alertOpen, setAlertOpen] = useState(false);
const [alertMessage, setAlertMessage] = useState('');
const [alertSeverity, setAlertSeverity] = useState<'error' | 'warning' | 'info' | 'success'>('error');
// Effect to handle external errors
useEffect(() => {
if (externalError) {
setAlertMessage(externalError);
setAlertSeverity('error');
setAlertOpen(true);
}
}, [externalError]);
const handleCloseAlert = (event?: React.SyntheticEvent | Event, reason?: string) => {
if (reason === 'clickaway') {
return;
}
setAlertOpen(false);
};
const handleSubmit = (event: React.FormEvent) => {
event.preventDefault();
const parsedWants = wants.split('\n').map(s => s.trim()).filter(s => s);
const parsedAccepts = accepts.split('\n').map(s => s.trim()).filter(s => s);
const parsedNoGoes = noGoes.split('\n').map(s => s.trim()).filter(s => s);
// 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) {
setAlertMessage('Please enter at least one desire in any category.');
setAlertSeverity('error');
setAlertOpen(true);
return;
}
// FR-016: System MUST validate a user's submission to prevent the same item from appearing in conflicting categories
const allItems = [...parsedWants, ...parsedAccepts, ...parsedNoGoes];
const uniqueItems = new Set(allItems);
if (allItems.length !== uniqueItems.size) {
setAlertMessage('You have conflicting desires (same item in different categories). Please resolve.');
setAlertSeverity('error');
setAlertOpen(true);
return;
}
onSubmit({
wants: parsedWants,
accepts: parsedAccepts,
noGoes: parsedNoGoes,
afraidToAsk: afraidToAsk,
});
};
return (
<Box component="form" onSubmit={handleSubmit} sx={{ mt: 3, display: 'flex', flexDirection: 'column' }}>
<Typography variant="h6" gutterBottom>What You Want</Typography>
<TextField
multiline
rows={4}
fullWidth
value={wants}
onChange={(e) => setWants(e.target.value)}
margin="normal"
inputProps={{ maxLength: 500, 'aria-label': 'Enter items you want' }}
helperText={`Enter items you want, one per line. Max 500 characters per item. ${wants.length}/500`}
/>
<Typography variant="h6" gutterBottom sx={{ mt: 4 }}>Afraid to Ask (Private)</Typography>
<TextField
multiline
rows={4}
fullWidth
value={afraidToAsk}
onChange={(e) => setAfraidToAsk(e.target.value)}
margin="normal"
inputProps={{ maxLength: 500, 'aria-label': 'Enter sensitive ideas privately' }}
helperText={`Enter sensitive ideas privately. Max 500 characters. ${afraidToAsk.length}/500`}
/>
<Typography variant="h6" gutterBottom sx={{ mt: 4 }}>What You Accept</Typography>
<TextField
multiline
rows={4}
fullWidth
value={accepts}
onChange={(e) => setAccepts(e.target.value)}
margin="normal"
inputProps={{ maxLength: 500, 'aria-label': 'Enter items you accept' }}
helperText={`Enter items you accept, one per line. Max 500 characters per item. ${accepts.length}/500`}
/>
<Typography variant="h6" gutterBottom sx={{ mt: 4 }}>What You Do Not Want</Typography>
<TextField
multiline
rows={4}
fullWidth
value={noGoes}
onChange={(e) => setNoGoes(e.target.value)}
margin="normal"
inputProps={{ maxLength: 500, 'aria-label': 'Enter items you absolutely do not want' }}
helperText={`Enter items you absolutely do not want, one per line. Max 500 characters per item. ${noGoes.length}/500`}
/>
<Button
type="submit"
variant="contained"
sx={{ mt: 3, mb: 2, textTransform: 'none', alignSelf: 'flex-start' }}
>
Submit Desires
</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>
);
};
export default DesireForm;