fix: Add missing COPPA fields to registration payload
- 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:
@@ -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>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user