feat: Add photo upload component for user and child profiles
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

- Created reusable PhotoUpload component with avatar preview
- Added photo upload to child create/edit dialog
- Added profile photo upload to settings page
- Show photo preview with fallback icon
- Display camera button for future file upload integration
- Support URL paste for immediate photo display
- Updated API types to support photoUrl field

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-04 08:11:38 +00:00
parent 426b5a309e
commit ac59e6fe82
5 changed files with 132 additions and 7 deletions

View File

@@ -0,0 +1,110 @@
'use client';
import { useState } from 'react';
import {
Box,
Avatar,
IconButton,
TextField,
Typography,
Paper,
} from '@mui/material';
import { PhotoCamera, Person } from '@mui/icons-material';
interface PhotoUploadProps {
value: string;
onChange: (url: string) => void;
label: string;
disabled?: boolean;
size?: number;
}
export function PhotoUpload({
value,
onChange,
label,
disabled = false,
size = 100
}: PhotoUploadProps) {
const [imageError, setImageError] = useState(false);
const handleImageError = () => {
setImageError(true);
};
const handleImageLoad = () => {
setImageError(false);
};
return (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, alignItems: 'center' }}>
<Paper
elevation={0}
sx={{
p: 2,
border: 1,
borderColor: 'divider',
borderRadius: 2,
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: 2
}}
>
<Typography variant="body2" color="text.secondary">
{label}
</Typography>
<Box sx={{ position: 'relative' }}>
<Avatar
src={!imageError && value ? value : undefined}
sx={{
width: size,
height: size,
bgcolor: 'primary.light',
fontSize: size / 3,
}}
onError={handleImageError}
onLoad={handleImageLoad}
>
{!value || imageError ? <Person sx={{ fontSize: size / 2 }} /> : null}
</Avatar>
<IconButton
sx={{
position: 'absolute',
bottom: -4,
right: -4,
bgcolor: 'background.paper',
border: 2,
borderColor: 'divider',
'&:hover': {
bgcolor: 'action.hover',
},
}}
size="small"
disabled={disabled}
onClick={() => {
// Future: Open file picker for actual upload
// For now, user can paste URL below
}}
>
<PhotoCamera fontSize="small" />
</IconButton>
</Box>
<TextField
label="Photo URL"
value={value}
onChange={(e) => onChange(e.target.value)}
fullWidth
size="small"
placeholder="https://example.com/photo.jpg"
disabled={disabled}
helperText="Paste an image URL or upload a photo"
/>
</Paper>
</Box>
);
}