fix: Add missing COPPA fields to registration payload
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

- Added dateOfBirth, parentalEmail, and coppaConsentGiven to RegisterData interface
- Updated register function to include all required COPPA compliance fields in API payload
- Added debug logging to see registration payload
- Fixed 400 error during registration due to missing required dateOfBirth field

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-03 15:23:02 +00:00
parent d81010ef91
commit 952efa6d37
3 changed files with 59 additions and 23 deletions

View File

@@ -55,9 +55,9 @@ export default function TrackPage() {
{t('selectActivity')} {t('selectActivity')}
</Typography> </Typography>
<Grid container spacing={3}> <Grid container spacing={3} sx={{ justifyContent: 'flex-start' }}>
{trackingOptions.map((option) => ( {trackingOptions.map((option) => (
<Grid item xs={12} sm={6} md={3} key={option.title}> <Grid item xs={6} sm={4} md={2.4} key={option.title}>
<Card <Card
sx={{ sx={{
height: '180px', // Fixed height for consistency height: '180px', // Fixed height for consistency
@@ -78,12 +78,32 @@ export default function TrackPage() {
flexDirection: 'column', flexDirection: 'column',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
py: 4,
}} }}
> >
<CardContent sx={{ textAlign: 'center' }}> <CardContent sx={{
textAlign: 'center',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
width: '100%',
height: '100%',
p: 2,
'&:last-child': { pb: 2 }
}}>
{option.icon} {option.icon}
<Typography variant="h6" fontWeight="600" sx={{ mt: 2 }}> <Typography
variant="h6"
fontWeight="600"
sx={{
mt: 2,
wordWrap: 'break-word',
wordBreak: 'break-word',
hyphens: 'auto',
textAlign: 'center',
width: '100%',
}}
>
{option.title} {option.title}
</Typography> </Typography>
</CardContent> </CardContent>

View File

@@ -363,8 +363,8 @@ export const InsightsDashboard: React.FC = () => {
animate={{ opacity: 1, scale: 1 }} animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.3, delay: 0 }} transition={{ duration: 0.3, delay: 0 }}
> >
<Card sx={{ bgcolor: COLORS.feeding, color: 'white', height: '160px', minHeight: '160px' }}> <Card sx={{ bgcolor: COLORS.feeding, color: 'white', height: '160px', minHeight: '160px', width: '100%' }}>
<CardContent sx={{ height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'center' }}> <CardContent sx={{ height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'center', p: 2, '&:last-child': { pb: 2 } }}>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}> <Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
<Restaurant sx={{ fontSize: 32, mr: 1 }} /> <Restaurant sx={{ fontSize: 32, mr: 1 }} />
<Typography variant="h6" fontWeight="600"> <Typography variant="h6" fontWeight="600">
@@ -388,8 +388,8 @@ export const InsightsDashboard: React.FC = () => {
animate={{ opacity: 1, scale: 1 }} animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.3, delay: 0.1 }} transition={{ duration: 0.3, delay: 0.1 }}
> >
<Card sx={{ bgcolor: COLORS.sleep, color: 'white', height: '160px', minHeight: '160px' }}> <Card sx={{ bgcolor: COLORS.sleep, color: 'white', height: '160px', minHeight: '160px', width: '100%' }}>
<CardContent sx={{ height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'center' }}> <CardContent sx={{ height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'center', p: 2, '&:last-child': { pb: 2 } }}>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}> <Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
<Hotel sx={{ fontSize: 32, mr: 1 }} /> <Hotel sx={{ fontSize: 32, mr: 1 }} />
<Typography variant="h6" fontWeight="600"> <Typography variant="h6" fontWeight="600">
@@ -413,8 +413,8 @@ export const InsightsDashboard: React.FC = () => {
animate={{ opacity: 1, scale: 1 }} animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.3, delay: 0.2 }} transition={{ duration: 0.3, delay: 0.2 }}
> >
<Card sx={{ bgcolor: COLORS.diaper, color: 'white', height: '160px', minHeight: '160px' }}> <Card sx={{ bgcolor: COLORS.diaper, color: 'white', height: '160px', minHeight: '160px', width: '100%' }}>
<CardContent sx={{ height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'center' }}> <CardContent sx={{ height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'center', p: 2, '&:last-child': { pb: 2 } }}>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}> <Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
<BabyChangingStation sx={{ fontSize: 32, mr: 1 }} /> <BabyChangingStation sx={{ fontSize: 32, mr: 1 }} />
<Typography variant="h6" fontWeight="600"> <Typography variant="h6" fontWeight="600">
@@ -438,8 +438,8 @@ export const InsightsDashboard: React.FC = () => {
animate={{ opacity: 1, scale: 1 }} animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.3, delay: 0.3 }} transition={{ duration: 0.3, delay: 0.3 }}
> >
<Card sx={{ bgcolor: COLORS.milestone, color: 'white', height: '160px', minHeight: '160px' }}> <Card sx={{ bgcolor: COLORS.milestone, color: 'white', height: '160px', minHeight: '160px', width: '100%' }}>
<CardContent sx={{ height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'center' }}> <CardContent sx={{ height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'center', p: 2, '&:last-child': { pb: 2 } }}>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}> <Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
<TrendingUp sx={{ fontSize: 32, mr: 1 }} /> <TrendingUp sx={{ fontSize: 32, mr: 1 }} />
<Typography variant="h6" fontWeight="600"> <Typography variant="h6" fontWeight="600">
@@ -461,8 +461,8 @@ export const InsightsDashboard: React.FC = () => {
{/* Charts */} {/* Charts */}
<Grid container spacing={3} sx={{ mb: 3 }}> <Grid container spacing={3} sx={{ mb: 3 }}>
<Grid item xs={12} md={6}> <Grid item xs={12} md={6}>
<Card sx={{ height: '350px', minHeight: '350px' }}> <Card sx={{ height: '350px', minHeight: '350px', width: '100%' }}>
<CardContent sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}> <CardContent sx={{ height: '100%', display: 'flex', flexDirection: 'column', p: 2, '&:last-child': { pb: 2 } }}>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}> <Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<Restaurant sx={{ mr: 1, color: COLORS.feeding }} /> <Restaurant sx={{ mr: 1, color: COLORS.feeding }} />
<Typography variant="h6" fontWeight="600"> <Typography variant="h6" fontWeight="600">
@@ -483,8 +483,8 @@ export const InsightsDashboard: React.FC = () => {
</Grid> </Grid>
<Grid item xs={12} md={6}> <Grid item xs={12} md={6}>
<Card sx={{ height: '350px', minHeight: '350px' }}> <Card sx={{ height: '350px', minHeight: '350px', width: '100%' }}>
<CardContent sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}> <CardContent sx={{ height: '100%', display: 'flex', flexDirection: 'column', p: 2, '&:last-child': { pb: 2 } }}>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}> <Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<Hotel sx={{ mr: 1, color: COLORS.sleep }} /> <Hotel sx={{ mr: 1, color: COLORS.sleep }} />
<Typography variant="h6" fontWeight="600"> <Typography variant="h6" fontWeight="600">
@@ -513,8 +513,8 @@ export const InsightsDashboard: React.FC = () => {
{diaperData.length > 0 && ( {diaperData.length > 0 && (
<Grid item xs={12} md={6}> <Grid item xs={12} md={6}>
<Card> <Card sx={{ height: '350px', minHeight: '350px', width: '100%' }}>
<CardContent> <CardContent sx={{ height: '100%', display: 'flex', flexDirection: 'column', p: 2, '&:last-child': { pb: 2 } }}>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}> <Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<BabyChangingStation sx={{ mr: 1, color: COLORS.diaper }} /> <BabyChangingStation sx={{ mr: 1, color: COLORS.diaper }} />
<Typography variant="h6" fontWeight="600"> <Typography variant="h6" fontWeight="600">
@@ -546,8 +546,8 @@ export const InsightsDashboard: React.FC = () => {
)} )}
<Grid item xs={12} md={diaperData.length > 0 ? 6 : 12}> <Grid item xs={12} md={diaperData.length > 0 ? 6 : 12}>
<Card sx={{ height: '350px', minHeight: '350px' }}> <Card sx={{ height: '350px', minHeight: '350px', width: '100%' }}>
<CardContent sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}> <CardContent sx={{ height: '100%', display: 'flex', flexDirection: 'column', p: 2, '&:last-child': { pb: 2 } }}>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}> <Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<Assessment sx={{ mr: 1, color: 'primary.main' }} /> <Assessment sx={{ mr: 1, color: 'primary.main' }} />
<Typography variant="h6" fontWeight="600"> <Typography variant="h6" fontWeight="600">

View File

@@ -28,6 +28,9 @@ export interface RegisterData {
password: string; password: string;
name: string; name: string;
role?: string; role?: string;
dateOfBirth: string; // COPPA compliance - required
parentalEmail?: string; // For users 13-17
coppaConsentGiven?: boolean; // For users 13-17
} }
interface AuthContextType { interface AuthContextType {
@@ -144,13 +147,26 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
// Auto-detect timezone from user's device // Auto-detect timezone from user's device
const detectedTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; const detectedTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const response = await apiClient.post('/api/v1/auth/register', { const payload: any = {
email: data.email, email: data.email,
password: data.password, password: data.password,
name: data.name, name: data.name,
timezone: detectedTimezone || 'UTC', timezone: detectedTimezone || 'UTC',
dateOfBirth: data.dateOfBirth,
deviceInfo, deviceInfo,
}); };
// Add optional COPPA fields if provided
if (data.parentalEmail) {
payload.parentalEmail = data.parentalEmail;
}
if (data.coppaConsentGiven !== undefined) {
payload.coppaConsentGiven = data.coppaConsentGiven;
}
console.log('[Auth] Registration payload:', JSON.stringify(payload, null, 2));
const response = await apiClient.post('/api/v1/auth/register', payload);
// Backend returns { success, data: { user, family, tokens } } // Backend returns { success, data: { user, family, tokens } }
const { data: responseData } = response.data; const { data: responseData } = response.data;