Some checks failed
ParentFlow CI/CD Pipeline / Backend Tests (push) Has been cancelled
ParentFlow CI/CD Pipeline / Frontend Tests (push) Has been cancelled
ParentFlow CI/CD Pipeline / Security Scanning (push) Has been cancelled
ParentFlow CI/CD Pipeline / Build Docker Images (map[context:maternal-app/maternal-app-backend dockerfile:Dockerfile.production name:backend]) (push) Has been cancelled
ParentFlow CI/CD Pipeline / Build Docker Images (map[context:maternal-web dockerfile:Dockerfile.production name:frontend]) (push) Has been cancelled
ParentFlow CI/CD Pipeline / Deploy to Development (push) Has been cancelled
ParentFlow CI/CD Pipeline / Deploy to Production (push) Has been cancelled
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
## Error Handling System - Add centralized error handling utilities (errorHandler.ts) - Create reusable error components (ErrorMessage, ErrorToast) - Implement multilingual error support (preserves backend error messages in 5 languages) - Update 15+ forms and components with consistent error handling - Auth forms: login, register, forgot-password - Family management: family page, join family dialog - Child management: child dialog - All tracking forms: feeding, sleep, diaper, medicine, growth, activity ## Production Build Fixes - Fix backend TypeScript errors: InviteCode.uses → InviteCode.useCount (5 instances) - Remove non-existent savedFamily variable from registration response - Fix admin panel TypeScript errors: SimpleMDE toolbar type, PieChart label type ## User Experience Improvements - Auto-uppercase invite code and share code inputs - Visual feedback for case conversion with helper text - Improved form validation with error codes ## CI/CD Pipeline - Create comprehensive production deployment checklist (PRODUCTION_DEPLOYMENT_CHECKLIST.md) - Add automated pre-deployment check script (pre-deploy-check.sh) - Validates frontend, backend, and admin panel builds - Checks git status, branch, and sync state - Verifies environment files and migrations - Add quick start deployment guide (DEPLOYMENT_QUICK_START.md) - Add production deployment automation template (deploy-production.sh) ## Cleanup - Remove outdated push notifications documentation files - Remove outdated PWA implementation plan 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
246 lines
7.4 KiB
TypeScript
246 lines
7.4 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import {
|
|
Box,
|
|
TextField,
|
|
Button,
|
|
Typography,
|
|
Paper,
|
|
Alert,
|
|
CircularProgress,
|
|
Link as MuiLink,
|
|
} from '@mui/material';
|
|
import { Email, ArrowBack } from '@mui/icons-material';
|
|
import { motion } from 'framer-motion';
|
|
import Link from 'next/link';
|
|
import apiClient from '@/lib/api/client';
|
|
import { useErrorMessage } from '@/components/common/ErrorMessage';
|
|
import { formatErrorMessage } from '@/lib/utils/errorHandler';
|
|
|
|
export default function ForgotPasswordPage() {
|
|
const [email, setEmail] = useState('');
|
|
const [loading, setLoading] = useState(false);
|
|
const { error, showError, clearError, hasError } = useErrorMessage();
|
|
const [success, setSuccess] = useState(false);
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
|
|
if (!email.trim()) {
|
|
showError({ message: 'Please enter your email address', code: 'VALIDATION_EMAIL_REQUIRED' });
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
clearError();
|
|
|
|
try {
|
|
await apiClient.post('/api/v1/auth/password/forgot', { email });
|
|
setSuccess(true);
|
|
} catch (err: any) {
|
|
showError(err);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Box
|
|
sx={{
|
|
minHeight: '100vh',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
background: 'linear-gradient(135deg, #FFE4E1 0%, #FFDAB9 100%)',
|
|
px: 2,
|
|
}}
|
|
>
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.5 }}
|
|
>
|
|
<Paper
|
|
elevation={0}
|
|
sx={{
|
|
maxWidth: 480,
|
|
width: '100%',
|
|
p: 4,
|
|
borderRadius: 4,
|
|
background: 'rgba(255, 255, 255, 0.95)',
|
|
backdropFilter: 'blur(10px)',
|
|
}}
|
|
>
|
|
{!success ? (
|
|
<>
|
|
<Box sx={{ textAlign: 'center', mb: 4 }}>
|
|
<Box
|
|
sx={{
|
|
width: 64,
|
|
height: 64,
|
|
borderRadius: '50%',
|
|
bgcolor: 'primary.light',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
mx: 'auto',
|
|
mb: 2,
|
|
}}
|
|
>
|
|
<Email sx={{ fontSize: 32, color: 'primary.main' }} />
|
|
</Box>
|
|
<Typography variant="h4" fontWeight="600" gutterBottom>
|
|
Forgot Password?
|
|
</Typography>
|
|
<Typography variant="body2" color="text.secondary">
|
|
No worries! Enter your email address and we'll send you a link to reset your password.
|
|
</Typography>
|
|
</Box>
|
|
|
|
{hasError && (
|
|
<Alert severity="error" sx={{ mb: 3, borderRadius: 2 }} onClose={clearError}>
|
|
{formatErrorMessage(error)}
|
|
</Alert>
|
|
)}
|
|
|
|
<form onSubmit={handleSubmit}>
|
|
<TextField
|
|
fullWidth
|
|
label="Email Address"
|
|
type="email"
|
|
value={email}
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
disabled={loading}
|
|
required
|
|
sx={{
|
|
mb: 3,
|
|
'& .MuiOutlinedInput-root': {
|
|
borderRadius: 3,
|
|
},
|
|
}}
|
|
/>
|
|
|
|
<Button
|
|
fullWidth
|
|
type="submit"
|
|
variant="contained"
|
|
size="large"
|
|
disabled={loading}
|
|
sx={{
|
|
borderRadius: 3,
|
|
py: 1.5,
|
|
textTransform: 'none',
|
|
fontSize: 16,
|
|
fontWeight: 600,
|
|
}}
|
|
>
|
|
{loading ? <CircularProgress size={24} color="inherit" /> : 'Send Reset Link'}
|
|
</Button>
|
|
</form>
|
|
|
|
<Box sx={{ mt: 3, textAlign: 'center' }}>
|
|
<MuiLink
|
|
component={Link}
|
|
href="/login"
|
|
sx={{
|
|
display: 'inline-flex',
|
|
alignItems: 'center',
|
|
gap: 0.5,
|
|
color: 'primary.main',
|
|
textDecoration: 'none',
|
|
fontWeight: 500,
|
|
'&:hover': {
|
|
textDecoration: 'underline',
|
|
},
|
|
}}
|
|
>
|
|
<ArrowBack sx={{ fontSize: 18 }} />
|
|
Back to Login
|
|
</MuiLink>
|
|
</Box>
|
|
</>
|
|
) : (
|
|
<Box sx={{ textAlign: 'center' }}>
|
|
<Box
|
|
sx={{
|
|
width: 64,
|
|
height: 64,
|
|
borderRadius: '50%',
|
|
bgcolor: 'success.light',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
mx: 'auto',
|
|
mb: 3,
|
|
}}
|
|
>
|
|
<Email sx={{ fontSize: 32, color: 'success.main' }} />
|
|
</Box>
|
|
|
|
<Typography variant="h5" fontWeight="600" gutterBottom>
|
|
Check Your Email 📧
|
|
</Typography>
|
|
|
|
<Typography variant="body1" color="text.secondary" sx={{ mb: 3 }}>
|
|
If an account with that email exists, we've sent you a password reset link. Please check your inbox and follow the instructions.
|
|
</Typography>
|
|
|
|
<Alert severity="info" sx={{ mb: 3, borderRadius: 2, textAlign: 'left' }}>
|
|
<Typography variant="body2" fontWeight="600" gutterBottom>
|
|
Didn't receive the email?
|
|
</Typography>
|
|
<Typography variant="body2">
|
|
• Check your spam or junk folder
|
|
<br />
|
|
• Make sure you entered the correct email
|
|
<br />
|
|
• The link expires in 1 hour
|
|
</Typography>
|
|
</Alert>
|
|
|
|
<Button
|
|
fullWidth
|
|
variant="outlined"
|
|
onClick={() => {
|
|
setSuccess(false);
|
|
setEmail('');
|
|
}}
|
|
sx={{
|
|
borderRadius: 3,
|
|
py: 1.5,
|
|
textTransform: 'none',
|
|
fontSize: 16,
|
|
fontWeight: 600,
|
|
mb: 2,
|
|
}}
|
|
>
|
|
Try Another Email
|
|
</Button>
|
|
|
|
<MuiLink
|
|
component={Link}
|
|
href="/login"
|
|
sx={{
|
|
display: 'inline-flex',
|
|
alignItems: 'center',
|
|
gap: 0.5,
|
|
color: 'primary.main',
|
|
textDecoration: 'none',
|
|
fontWeight: 500,
|
|
'&:hover': {
|
|
textDecoration: 'underline',
|
|
},
|
|
}}
|
|
>
|
|
<ArrowBack sx={{ fontSize: 18 }} />
|
|
Back to Login
|
|
</MuiLink>
|
|
</Box>
|
|
)}
|
|
</Paper>
|
|
</motion.div>
|
|
</Box>
|
|
);
|
|
}
|