Files
maternal-app/maternal-web/app/(auth)/forgot-password/page.tsx
Andrei c22fa82521
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
feat: Implement comprehensive error handling and production deployment pipeline
## 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>
2025-10-09 21:27:39 +00:00

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&apos;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&apos;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&apos;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>
);
}