import React from 'react'; import { useTranslation } from 'react-i18next'; import { format } from 'date-fns'; import { de, enGB } from 'date-fns/locale'; import type { Locale } from 'date-fns'; import { Loader2, Palette, Plus, Power, Smile } from 'lucide-react'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Switch } from '@/components/ui/switch'; import { AdminLayout } from '../components/AdminLayout'; import { getEmotions, createEmotion, updateEmotion, TenantEmotion, EmotionPayload } from '../api'; import { isAuthError } from '../auth/tokens'; type EmotionFormState = { name: string; description: string; icon: string; color: string; is_active: boolean; sort_order: number; }; const DEFAULT_COLOR = '#6366f1'; const INITIAL_FORM_STATE: EmotionFormState = { name: '', description: '', icon: 'lucide-smile', color: DEFAULT_COLOR, is_active: true, sort_order: 0, }; export type EmotionsSectionProps = { embedded?: boolean; }; export function EmotionsSection({ embedded = false }: EmotionsSectionProps) { const { t, i18n } = useTranslation('management'); const [emotions, setEmotions] = React.useState([]); const [loading, setLoading] = React.useState(true); const [error, setError] = React.useState(null); const [dialogOpen, setDialogOpen] = React.useState(false); const [saving, setSaving] = React.useState(false); const [form, setForm] = React.useState(INITIAL_FORM_STATE); React.useEffect(() => { let cancelled = false; async function load() { setLoading(true); setError(null); try { const data = await getEmotions(); if (!cancelled) { setEmotions(data); } } catch (err) { if (!isAuthError(err)) { setError(t('emotions.errors.load')); } } finally { if (!cancelled) { setLoading(false); } } } void load(); return () => { cancelled = true; }; }, [t]); const openCreateDialog = React.useCallback(() => { setForm(INITIAL_FORM_STATE); setDialogOpen(true); }, []); async function handleCreate(event: React.FormEvent) { event.preventDefault(); if (!form.name.trim()) { setError(t('emotions.errors.nameRequired')); return; } setSaving(true); setError(null); const payload: EmotionPayload = { name: form.name.trim(), description: form.description.trim() || null, icon: form.icon.trim() || 'lucide-smile', color: form.color.trim() || DEFAULT_COLOR, is_active: form.is_active, sort_order: form.sort_order, }; try { const created = await createEmotion(payload); setEmotions((prev) => [created, ...prev]); setDialogOpen(false); } catch (err) { if (!isAuthError(err)) { setError(t('emotions.errors.create')); } } finally { setSaving(false); } } async function toggleEmotion(emotion: TenantEmotion) { try { const updated = await updateEmotion(emotion.id, { is_active: !emotion.is_active }); setEmotions((prev) => prev.map((item) => (item.id === updated.id ? updated : item))); } catch (err) { if (!isAuthError(err)) { setError(t('emotions.errors.toggle')); } } } const locale = i18n.language.startsWith('en') ? enGB : de; const title = embedded ? t('emotions.title') : t('emotions.title'); const subtitle = embedded ? t('emotions.subtitle') : t('emotions.subtitle'); return (
{error && ( {t('emotions.errors.genericTitle')} {error} )}
{title} {subtitle}
{loading ? ( ) : emotions.length === 0 ? ( ) : (
{emotions.map((emotion) => ( toggleEmotion(emotion)} locale={locale} /> ))}
)}
); } export default function EmotionsPage() { const { t } = useTranslation('management'); return ( ); } function EmotionCard({ emotion, onToggle, locale, }: { emotion: TenantEmotion; onToggle: () => void; locale: Locale; }) { const { t } = useTranslation('management'); const updated = emotion.updated_at ? format(new Date(emotion.updated_at), 'Pp', { locale }) : null; return (
{emotion.name} {emotion.description ? ( {emotion.description} ) : null}
{emotion.is_active ? t('emotions.status.active') : t('emotions.status.inactive')}
#{emotion.icon} {emotion.event_types?.length ? ( emotion.event_types.map((eventType) => ( {eventType.name} )) ) : ( {t('emotions.labels.noEventType')} )}
{updated ?

{t('emotions.labels.updated', { date: updated })}

: null}
); } function EmotionDialog({ open, onOpenChange, form, setForm, saving, onSubmit, }: { open: boolean; onOpenChange: (open: boolean) => void; form: EmotionFormState; setForm: React.Dispatch>; saving: boolean; onSubmit: (event: React.FormEvent) => void; }) { const { t } = useTranslation('management'); return ( {t('emotions.dialogs.createTitle')}
setForm((prev) => ({ ...prev, name: event.target.value }))} required />
setForm((prev) => ({ ...prev, description: event.target.value }))} />
setForm((prev) => ({ ...prev, icon: event.target.value }))} />
setForm((prev) => ({ ...prev, color: event.target.value }))} />

{t('emotions.dialogs.activeLabel')}

{t('emotions.dialogs.activeDescription')}

setForm((prev) => ({ ...prev, is_active: checked }))} />
); } function EmotionSkeleton() { return (
{Array.from({ length: 4 }).map((_, index) => (
))}
); } function EmptyEmotionsState({ onCreate }: { onCreate: () => void }) { const { t } = useTranslation('management'); return (

{t('emotions.empty.title')}

{t('emotions.empty.description')}

); }