import { Spinner } from "../ui/spinner"; import { Icon } from "../ui/icon"; import { Card, CardBody } from '../ui/wp-card'; import { DismissibleAlert } from '../ui/dismissible-alert'; import { Flex, FlexItem } from '../ui/flex'; import { Camera, PlusCircle, XCircle, ImageIcon as image } from 'lucide-react'; import './PhotoAttachment.css'; import { PersonBadge } from '../ui/person-badge'; import { Attachment } from 'fhir/r4'; import { ChangeEvent, useState, useRef } from 'react'; import { usePhotoUploadService } from '../../services/PhotoUploadService'; import { usePhotoUrl } from '../../hooks/usePhotoUrl'; interface PhotoAttachmentProps { photo?: Attachment; description?: string; cover?: boolean; compact?: boolean; onPhotoChange: (photo: Attachment | undefined) => void; disabled?: boolean; display?: boolean; uploadType?: string; initialsFor?: string; } const PhotoAttachment = ({ photo, description, cover = false, compact = false, onPhotoChange, disabled = false, display = false, uploadType = 'profile-picture', initialsFor }: PhotoAttachmentProps) => { const [error, setError] = useState(null); const [uploading, setUploading] = useState(false); const fileInputRef = useRef(null); const { uploadPhoto } = usePhotoUploadService(); const { url: imageUrl } = usePhotoUrl(photo); const handleFileSelect = () => { if (fileInputRef.current) { fileInputRef.current.click(); } }; const onFileSelected = async (event: ChangeEvent) => { setError(null); if (!event.target.files?.[0]) return; const file = event.target.files[0]; if (!file.type.startsWith('image/')) { setError('Selected file is not an image.'); return; } setUploading(true); try { const { key, contentType, size } = await uploadPhoto(file, uploadType); const newPhoto: Attachment = { // Named by key only: the server fills the bytes once this exact upload scans clean. contentType, url: key, title: file.name, size, creation: new Date().toISOString() }; onPhotoChange(newPhoto); } catch (err) { setError('Failed to upload the image. Please try again.'); console.error('Error uploading image:', err); } finally { setUploading(false); } if (event.target.value) { event.target.value = ''; } }; const removePhoto = () => { onPhotoChange(undefined); }; const isDisabled = disabled || uploading; return ( {!photo && cover ? (
{uploading ? ( ) : ( )}

{display ? 'No photo available' : 'Upload a photo of the service'}

{/* `btn-icon-overlay` is what keeps these glyphs at the 32px they render today — see the rule in index.css. The BEM class still carries all the geometry (the 40px disc, the absolute placement, the white fill); every one of its declarations is `!important`, so it survives the move to a native button untouched and only the icon size needed a home in the btn vocabulary. */} {!display && ( )}
) : ( <>
{uploading ? (
) : !photo && initialsFor ? ( ) : ( {photo?.title )} {!display && !cover && ( )} {!display && photo && !cover && ( )}
{!display && cover && (
)} )} {error && ( setError(null)}> {error} )} {description && (

{description}

)}
); }; export default PhotoAttachment;