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 { Palette, Plus, Power, Smile } from 'lucide-react'; import { AdminLayout } from '../components/AdminLayout'; import { getEmotions, createEmotion, updateEmotion, TenantEmotion, EmotionPayload, } from '../api'; import { isAuthError } from '../auth/tokens'; 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'; const DEFAULT_COLOR = '#6366f1'; type EmotionFormState = { name: string; description: string; icon: string; color: string; is_active: boolean; sort_order: number; }; const INITIAL_FORM_STATE: EmotionFormState = { name: '', description: '', icon: 'lucide-smile', color: DEFAULT_COLOR, is_active: true, sort_order: 0, }; export default function EmotionsPage(): JSX.Element { 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]); function openCreateDialog() { 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; return ( {t('emotions.actions.create')} } > {error && ( {t('emotions.errors.genericTitle')} {error} )} {t('emotions.title')} {t('emotions.subtitle')} {loading ? ( ) : emotions.length === 0 ? ( ) : (
{emotions.map((emotion) => ( toggleEmotion(emotion)} locale={locale} canToggle={!emotion.is_global} /> ))}
)}
); } function EmotionCard({ emotion, onToggle, locale, canToggle, }: { emotion: TenantEmotion; onToggle: () => void; locale: Locale; canToggle: boolean; }) { const { t } = useTranslation('management'); const updatedLabel = emotion.updated_at ? format(new Date(emotion.updated_at), 'PP', { locale }) : null; return (
{emotion.is_global ? t('emotions.scope.global') : t('emotions.scope.tenant')} {emotion.event_types.map((type) => type.name).join(', ') || t('emotions.labels.noEventType')}
{emotion.name} {emotion.description && ( {emotion.description} )}
{emotion.color}
{updatedLabel && {t('emotions.labels.updated', { date: updatedLabel })}}
{emotion.is_active ? t('emotions.status.active') : t('emotions.status.inactive')}
); } function EmptyEmotionsState() { const { t } = useTranslation('management'); return (

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

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

); } function EmotionSkeleton() { return (
{Array.from({ length: 4 }).map((_, index) => (
))}
); } 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 />