geraete/components/ProfileAvatarModal.tsx
2025-12-05 13:53:29 +01:00

185 lines
5.1 KiB
TypeScript

// components/ProfileAvatarModal.tsx
'use client';
import { useEffect, useState } from 'react';
import Modal from '@/components/ui/Modal';
import PersonAvatar from '@/components/ui/UserAvatar';
import Button from './ui/Button';
type ProfileAvatarModalProps = {
open: boolean;
onClose: () => void;
avatarName: string;
avatarUrl?: string | null;
/**
* Wird aufgerufen, wenn eine neue Datei gespeichert werden soll.
*/
onAvatarSelected?: (file: File) => Promise<void> | void;
/**
* Optional: wird aufgerufen, wenn der Nutzer das Profilbild löschen möchte.
* Erwartet, dass Backend + Session angepasst werden (Avatar auf null).
*/
onAvatarDelete?: () => Promise<void> | void;
};
export default function ProfileAvatarModal({
open,
onClose,
avatarName,
avatarUrl,
onAvatarSelected,
onAvatarDelete,
}: ProfileAvatarModalProps) {
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
const [isSaving, setIsSaving] = useState(false);
// Wenn Modal geschlossen wird → State zurücksetzen
useEffect(() => {
if (!open) {
setSelectedFile(null);
setPreviewUrl(null);
setIsSaving(false);
}
}, [open]);
// Preview-URL für ausgewählte Datei erzeugen
useEffect(() => {
if (!selectedFile) {
setPreviewUrl(null);
return;
}
const url = URL.createObjectURL(selectedFile);
setPreviewUrl(url);
return () => URL.revokeObjectURL(url);
}, [selectedFile]);
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
setSelectedFile(file);
}
};
const handleSave = async () => {
if (!selectedFile) return;
try {
setIsSaving(true);
if (onAvatarSelected) {
await onAvatarSelected(selectedFile);
}
onClose();
} finally {
setIsSaving(false);
}
};
const handleDeleteClick = async () => {
if (!onAvatarDelete) return;
const sure = window.confirm('Profilbild wirklich entfernen?');
if (!sure) return;
try {
setIsSaving(true);
await onAvatarDelete();
// lokale Preview zurücksetzen
setSelectedFile(null);
setPreviewUrl(null);
onClose();
} finally {
setIsSaving(false);
}
};
const effectiveAvatarUrl = previewUrl ?? avatarUrl ?? undefined;
const hasDeletableAvatar = !!avatarUrl; // Button nur anzeigen, wenn ein Avatar existiert
return (
<Modal
open={open}
onClose={onClose}
title="Profilbild ändern"
tone="info"
size="sm"
primaryAction={{
label: 'Speichern',
onClick: handleSave,
disabled: !selectedFile || isSaving,
}}
secondaryAction={{
label: 'Abbrechen',
onClick: onClose,
variant: 'secondary',
disabled: isSaving,
}}
>
<div className="space-y-4">
<p className="text-sm text-gray-600 dark:text-gray-300">
Wähle ein neues Profilbild aus oder entferne das aktuelle Bild.<br />
Unterstützte Formate: JPG, PNG, GIF.
</p>
{/* Aktuell vs. Vorschau */}
<div className="mt-4 grid grid-cols-1 gap-6 sm:grid-cols-2">
<div className="flex flex-col items-center gap-2">
<span className="text-xs text-gray-500 dark:text-gray-400">
Aktuell
</span>
<PersonAvatar
name={avatarName}
avatarUrl={avatarUrl ?? undefined}
size="2xl"
/>
</div>
<div className="flex flex-col items-center gap-2">
<span className="text-xs text-gray-500 dark:text-gray-400">
Vorschau
</span>
<PersonAvatar
name={avatarName}
avatarUrl={effectiveAvatarUrl}
size="2xl"
/>
</div>
</div>
{/* File-Input + ggf. Lösch-Button */}
<div className="mt-4 space-y-3">
<input
type="file"
accept="image/*"
onChange={handleFileChange}
className="mt-2 block w-full text-sm text-gray-900
file:mr-4 file:rounded-md file:border-0
file:bg-indigo-50 file:px-3 file:py-1.5
file:text-sm file:font-semibold file:text-indigo-700
hover:file:bg-indigo-100
dark:text-gray-100
dark:file:bg-indigo-500/10 dark:file:text-indigo-200
dark:hover:file:bg-indigo-500/20"
/>
{/* Profilbild löschen nur, wenn wirklich eins vorhanden ist */}
{onAvatarDelete && hasDeletableAvatar && (
<Button
onClick={handleDeleteClick}
disabled={isSaving}
size='md'
variant='soft'
tone='rose'
className="w-full text-xs font-medium text-rose-600 hover:text-rose-700 dark:text-rose-400 dark:hover:text-rose-300"
>
Profilbild entfernen
</Button>
)}
</div>
</div>
</Modal>
);
}