Files
maternal-app/maternal-web/components/legal/EULADialog.tsx
Andrei e4b97df0c0
Some checks failed
CI/CD Pipeline / Lint and Test (push) Has been cancelled
CI/CD Pipeline / E2E Tests (push) Has been cancelled
CI/CD Pipeline / Build Application (push) Has been cancelled
feat: Implement AI response feedback UI and complete high-priority features
Frontend Features:
- Add MessageFeedback component with thumbs up/down buttons
- Positive feedback submits immediately with success toast
- Negative feedback opens dialog for optional text input
- Integrate feedback buttons on all AI assistant messages
- Add success Snackbar confirmation message
- Translation keys added to ai.json (feedback section)

Backend Features:
- Add POST /api/v1/ai/feedback endpoint
- Create FeedbackDto with conversation ID validation
- Implement submitFeedback service method
- Store feedback in conversation metadata with timestamps
- Add audit logging for feedback submissions
- Fix conversationId regex validation to support nanoid format

Legal & Compliance:
- Implement complete EULA acceptance flow with modal
- Create reusable legal content components (Terms, Privacy, EULA)
- Add LegalDocumentViewer for nested modal viewing
- Cookie Consent Banner with GDPR compliance
- Legal pages with AppShell navigation
- EULA acceptance tracking in user entity

Branding Updates:
- Rebrand from "Maternal App" to "ParentFlow"
- Update all icons (72px to 512px) from high-res source
- PWA manifest updated with ParentFlow branding
- Contact email: hello@parentflow.com
- Address: Serbota 3, Bucharest, Romania

Bug Fixes:
- Fix chat endpoint validation (support nanoid conversation IDs)
- Fix EULA acceptance API call (use apiClient vs hardcoded localhost)
- Fix icon loading errors with proper PNG generation

Documentation:
- Mark 11 high-priority features as complete in REMAINING_FEATURES.md
- Update feature statistics: 73/139 complete (53%)
- All high-priority features now complete! 🎉

Files Changed:
Frontend: 21 files (components, pages, locales, icons)
Backend: 6 files (controller, service, DTOs, migrations)
Docs: 1 file (REMAINING_FEATURES.md)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-04 11:39:02 +00:00

237 lines
7.1 KiB
TypeScript

'use client';
import { useState } from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
Typography,
Box,
Checkbox,
FormControlLabel,
Link,
Alert,
Divider,
} from '@mui/material';
import { Gavel, Warning } from '@mui/icons-material';
import { LegalDocumentViewer } from './LegalDocumentViewer';
interface EULADialogProps {
open: boolean;
onAccept: () => void;
onDecline: () => void;
}
export function EULADialog({ open, onAccept, onDecline }: EULADialogProps) {
const [agreedToTerms, setAgreedToTerms] = useState(false);
const [agreedToPrivacy, setAgreedToPrivacy] = useState(false);
const [agreedToEULA, setAgreedToEULA] = useState(false);
const [viewingDocument, setViewingDocument] = useState<{
type: 'terms' | 'privacy' | 'eula' | null;
title: string;
}>({ type: null, title: '' });
const canAccept = agreedToTerms && agreedToPrivacy && agreedToEULA;
const handleAccept = () => {
if (canAccept) {
onAccept();
}
};
const openDocument = (type: 'terms' | 'privacy' | 'eula', title: string) => (e: React.MouseEvent) => {
e.preventDefault();
setViewingDocument({ type, title });
};
const closeDocumentViewer = () => {
setViewingDocument({ type: null, title: '' });
};
return (
<>
<Dialog
open={open}
maxWidth="md"
fullWidth
disableEscapeKeyDown
onClose={(event, reason) => {
// Prevent closing by clicking outside or pressing ESC
if (reason === 'backdropClick' || reason === 'escapeKeyDown') {
return;
}
}}
sx={{
'& .MuiDialog-paper': {
borderRadius: 2,
},
}}
>
<DialogTitle sx={{ pb: 2 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
<Gavel sx={{ fontSize: 32, color: 'primary.main' }} />
<Box>
<Typography variant="h5" component="div">
Welcome to ParentFlow
</Typography>
<Typography variant="body2" color="text.secondary">
Please review and accept our legal agreements
</Typography>
</Box>
</Box>
</DialogTitle>
<Divider />
<DialogContent sx={{ pt: 3 }}>
<Alert severity="info" icon={<Warning />} sx={{ mb: 3 }}>
<Typography variant="body2">
To use ParentFlow, you must read and accept our Terms of Service, Privacy Policy, and End User License Agreement (EULA).
Click on each link below to read the full documents.
</Typography>
</Alert>
<Box sx={{ mb: 3 }}>
<Typography variant="h6" gutterBottom>
Important Highlights
</Typography>
<Typography component="div" variant="body2" color="text.secondary">
<ul>
<li>
<strong>Medical Disclaimer:</strong> This app is NOT a medical device and does not provide medical advice.
Always consult qualified healthcare professionals for medical concerns.
</li>
<li>
<strong>AI Features:</strong> AI responses may not always be accurate. You use AI features at your own risk.
</li>
<li>
<strong>Children's Privacy:</strong> We comply with COPPA and GDPR. You control your child's data and can delete it anytime.
</li>
<li>
<strong>Data Collection:</strong> We collect activity data, photos, and AI chat messages to provide the service.
We do NOT sell your data.
</li>
<li>
<strong>Your Rights:</strong> You can access, export, or delete your data at any time through app settings.
</li>
</ul>
</Typography>
</Box>
<Divider sx={{ my: 3 }} />
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<FormControlLabel
control={
<Checkbox
checked={agreedToTerms}
onChange={(e) => setAgreedToTerms(e.target.checked)}
color="primary"
/>
}
label={
<Typography variant="body2">
I have read and agree to the{' '}
<Link
href="#"
onClick={openDocument('terms', 'Terms of Service')}
sx={{ fontWeight: 'bold', textDecoration: 'underline', cursor: 'pointer' }}
>
Terms of Service
</Link>
</Typography>
}
/>
<FormControlLabel
control={
<Checkbox
checked={agreedToPrivacy}
onChange={(e) => setAgreedToPrivacy(e.target.checked)}
color="primary"
/>
}
label={
<Typography variant="body2">
I have read and agree to the{' '}
<Link
href="#"
onClick={openDocument('privacy', 'Privacy Policy')}
sx={{ fontWeight: 'bold', textDecoration: 'underline', cursor: 'pointer' }}
>
Privacy Policy
</Link>
</Typography>
}
/>
<FormControlLabel
control={
<Checkbox
checked={agreedToEULA}
onChange={(e) => setAgreedToEULA(e.target.checked)}
color="primary"
/>
}
label={
<Typography variant="body2">
I have read and agree to the{' '}
<Link
href="#"
onClick={openDocument('eula', 'End User License Agreement (EULA)')}
sx={{ fontWeight: 'bold', textDecoration: 'underline', cursor: 'pointer' }}
>
End User License Agreement (EULA)
</Link>
</Typography>
}
/>
</Box>
<Alert severity="warning" sx={{ mt: 3 }}>
<Typography variant="body2">
<strong>Emergency Disclaimer:</strong> In case of medical emergencies, call your local emergency number immediately (911 in the US).
This app is not for emergency situations.
</Typography>
</Alert>
</DialogContent>
<Divider />
<DialogActions sx={{ p: 3, gap: 1 }}>
<Button
onClick={onDecline}
variant="outlined"
color="inherit"
size="large"
>
Decline & Exit
</Button>
<Button
onClick={handleAccept}
variant="contained"
color="primary"
size="large"
disabled={!canAccept}
sx={{ minWidth: 120 }}
>
I Accept
</Button>
</DialogActions>
</Dialog>
{/* Legal Document Viewer - appears on top of EULA dialog */}
{viewingDocument.type && (
<LegalDocumentViewer
open={true}
onClose={closeDocumentViewer}
documentType={viewingDocument.type}
title={viewingDocument.title}
/>
)}
</>
);
}