session start works

This commit is contained in:
aodulov
2025-10-10 12:48:06 +03:00
parent 556df015e8
commit 3c192b136c
51 changed files with 29002 additions and 46 deletions

View File

@@ -0,0 +1,16 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import DesireForm from './DesireForm';
test('renders desire submission form with input fields and submit button', () => {
render(<DesireForm onSubmit={() => {}} />);
// Check for headings/labels for each category
expect(screen.getByLabelText(/What you WANT/i)).toBeInTheDocument();
expect(screen.getByLabelText(/What you ACCEPT/i)).toBeInTheDocument();
expect(screen.getByLabelText(/What you DO NOT WANT/i)).toBeInTheDocument();
// Check for the submit button
const submitButton = screen.getByRole('button', { name: /Submit Desires/i });
expect(submitButton).toBeInTheDocument();
});

View File

@@ -0,0 +1,87 @@
import React, { useState } from 'react';
import { TextField, Button, Box, Typography } from '@mui/material';
interface DesireFormProps {
onSubmit: (desires: { wants: string[], accepts: string[], noGoes: string[] }) => void;
}
const DesireForm: React.FC<DesireFormProps> = ({ onSubmit }) => {
const [wants, setWants] = useState('');
const [accepts, setAccepts] = useState('');
const [noGoes, setNoGoes] = useState('');
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) {
alert('Please enter at least one desire in any category.');
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) {
alert('You have conflicting desires (same item in different categories). Please resolve.');
return;
}
onSubmit({
wants: parsedWants,
accepts: parsedAccepts,
noGoes: parsedNoGoes,
});
};
return (
<Box component="form" onSubmit={handleSubmit} sx={{ mt: 3 }}>
<Typography variant="h6" gutterBottom>What you WANT</Typography>
<TextField
label="List items you want (one per line)"
multiline
rows={4}
fullWidth
value={wants}
onChange={(e) => setWants(e.target.value)}
margin="normal"
/>
<Typography variant="h6" gutterBottom sx={{ mt: 4 }}>What you ACCEPT</Typography>
<TextField
label="List items you accept (one per line)"
multiline
rows={4}
fullWidth
value={accepts}
onChange={(e) => setAccepts(e.target.value)}
margin="normal"
/>
<Typography variant="h6" gutterBottom sx={{ mt: 4 }}>What you DO NOT WANT</Typography>
<TextField
label="List items you absolutely do not want (one per line)"
multiline
rows={4}
fullWidth
value={noGoes}
onChange={(e) => setNoGoes(e.target.value)}
margin="normal"
/>
<Button
type="submit"
fullWidth
variant="contained"
sx={{ mt: 3, mb: 2 }}
>
Submit Desires
</Button>
</Box>
);
};
export default DesireForm;

View File

@@ -0,0 +1,29 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import ResultsDisplay from './ResultsDisplay';
import { Decision } from '../hooks/useSession';
const mockDecision: Decision = {
goTos: [{ title: 'Go to the beach', rawInputs: ['beach'] }],
alsoGoods: [{ title: 'Eat pizza', rawInputs: ['pizza'] }],
considerables: [{ title: 'Watch a movie', rawInputs: ['movie'] }],
noGoes: [{ title: 'Stay home', rawInputs: ['home'] }],
};
describe('ResultsDisplay', () => {
it('renders all categories correctly', () => {
render(<ResultsDisplay decision={mockDecision} />);
expect(screen.getByText('Go-to')).toBeInTheDocument();
expect(screen.getByText('Go to the beach')).toBeInTheDocument();
expect(screen.getByText('Also good')).toBeInTheDocument();
expect(screen.getByText('Eat pizza')).toBeInTheDocument();
expect(screen.getByText('Considerable')).toBeInTheDocument();
expect(screen.getByText('Watch a movie')).toBeInTheDocument();
expect(screen.getByText('No-goes')).toBeInTheDocument();
expect(screen.getByText('Stay home')).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,61 @@
import React from 'react';
import { Box, Typography, List, ListItem, ListItemText, Collapse, IconButton } from '@mui/material';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
import { Decision, SemanticDesire } from '../hooks/useSession';
interface ResultsDisplayProps {
decision: Decision;
}
const CategorySection: React.FC<{ title: string; desires: SemanticDesire[]; defaultExpanded?: boolean }>
= ({ title, desires, defaultExpanded = true }) => {
const [expanded, setExpanded] = React.useState(defaultExpanded);
if (!desires || desires.length === 0) {
return null;
}
return (
<Box sx={{ mt: 3, border: '1px solid #e0e0e0', borderRadius: '4px', p: 2 }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Typography variant="h6" component="h2" gutterBottom>
{title}
</Typography>
<IconButton onClick={() => setExpanded(!expanded)} size="small">
{expanded ? <ExpandLessIcon /> : <ExpandMoreIcon />}
</IconButton>
</Box>
<Collapse in={expanded} timeout="auto" unmountOnExit>
<List dense>
{desires.map((desire, index) => (
<ListItem key={index}>
<ListItemText primary={desire.title} />
</ListItem>
))}
</List>
</Collapse>
</Box>
);
};
const ResultsDisplay: React.FC<ResultsDisplayProps> = ({ decision }) => {
if (!decision) {
return <Typography>No decision available yet.</Typography>;
}
return (
<Box sx={{ mt: 4 }}>
<Typography variant="h5" component="h1" gutterBottom>
Cooperative Decision
</Typography>
<CategorySection title="Go-to" desires={decision.goTos} />
<CategorySection title="Also good" desires={decision.alsoGoods} />
<CategorySection title="Considerable" desires={decision.considerables} defaultExpanded={false} />
<CategorySection title="No-goes" desires={decision.noGoes} />
</Box>
);
};
export default ResultsDisplay;