import React from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { YStack, XStack } from '@tamagui/stacks'; import { SizableText as Text } from '@tamagui/text'; import { Pressable } from '@tamagui/react-native-web-lite'; import { useTheme } from '@tamagui/core'; import { RefreshCcw, Users, User } from 'lucide-react'; import toast from 'react-hot-toast'; import { MobileShell } from './components/MobileShell'; import { MobileCard, CTAButton, PillBadge } from './components/Primitives'; import { useEventContext } from '../context/EventContext'; import { GuestNotificationSummary, SendGuestNotificationPayload, getEvents, listGuestNotifications, sendGuestNotification, TenantEvent, } from '../api'; import { isAuthError } from '../auth/tokens'; import { getApiErrorMessage } from '../lib/apiError'; import { adminPath } from '../constants'; import { formatGuestMessageDate } from './guestMessages'; type FormState = { title: string; message: string; audience: 'all' | 'guest'; guest_identifier: string; cta_label: string; cta_url: string; expires_in_minutes: string; priority: string; }; export default function MobileEventGuestNotificationsPage() { const { slug: slugParam } = useParams<{ slug?: string }>(); const navigate = useNavigate(); const { t, i18n } = useTranslation('management'); const theme = useTheme(); const { activeEvent, selectEvent } = useEventContext(); const slug = slugParam ?? activeEvent?.slug ?? null; const [history, setHistory] = React.useState([]); const [loading, setLoading] = React.useState(true); const [sending, setSending] = React.useState(false); const [error, setError] = React.useState(null); const [fallbackAttempted, setFallbackAttempted] = React.useState(false); const [form, setForm] = React.useState({ title: '', message: '', audience: 'all', guest_identifier: '', cta_label: '', cta_url: '', expires_in_minutes: '', priority: '1', }); const inputStyle = React.useMemo(() => { const border = String(theme.borderColor?.val ?? '#e5e7eb'); const surface = String(theme.surface?.val ?? 'white'); const text = String(theme.color?.val ?? '#111827'); return { width: '100%', borderRadius: 10, border: `1px solid ${border}`, padding: '10px 12px', fontSize: 13, background: surface, color: text, }; }, [theme]); React.useEffect(() => { if (slugParam && activeEvent?.slug !== slugParam) { selectEvent(slugParam); } }, [slugParam, activeEvent?.slug, selectEvent]); const loadHistory = React.useCallback(async () => { if (!slug) { if (!fallbackAttempted) { setFallbackAttempted(true); try { const events = await getEvents({ force: true }); const first = events[0] as TenantEvent | undefined; if (first?.slug) { selectEvent(first.slug); navigate(adminPath(`/mobile/events/${first.slug}/guest-notifications`), { replace: true }); } } catch { // ignore } } setLoading(false); setError(t('events.errors.missingSlug', 'No event selected.')); return; } setLoading(true); setError(null); try { const notifications = await listGuestNotifications(slug); setHistory(notifications); } catch (err) { if (!isAuthError(err)) { const message = getApiErrorMessage(err, t('guestMessages.errorLoad', 'Messages could not be loaded.')); setError(message); toast.error(message); } } finally { setLoading(false); } }, [slug, t, fallbackAttempted, selectEvent, navigate]); React.useEffect(() => { void loadHistory(); }, [loadHistory]); const canSend = form.title.trim().length > 0 && form.message.trim().length > 0 && (form.audience === 'all' || form.guest_identifier.trim().length > 0); async function handleSend() { if (!slug || sending) return; if (!canSend) { const message = t('guestMessages.form.validation', 'Add a title, message, and target guest when needed.'); setError(message); toast.error(message); return; } const ctaLabel = form.cta_label.trim(); const ctaUrl = form.cta_url.trim(); if ((ctaLabel && !ctaUrl) || (!ctaLabel && ctaUrl)) { const message = t('guestMessages.form.ctaError', 'CTA label and link are required together.'); setError(message); toast.error(message); return; } const payload: SendGuestNotificationPayload = { title: form.title.trim(), message: form.message.trim(), audience: form.audience, }; if (form.audience === 'guest' && form.guest_identifier.trim()) { payload.guest_identifier = form.guest_identifier.trim(); } if (ctaLabel && ctaUrl) { payload.cta = { label: ctaLabel, url: ctaUrl }; } if (form.expires_in_minutes.trim()) { payload.expires_in_minutes = Number(form.expires_in_minutes); } if (form.priority.trim()) { payload.priority = Number(form.priority); } setSending(true); setError(null); try { const created = await sendGuestNotification(slug, payload); setHistory((prev) => [created, ...prev]); setForm((prev) => ({ ...prev, title: '', message: '', guest_identifier: '', cta_label: '', cta_url: '', expires_in_minutes: '', })); toast.success(t('guestMessages.sendSuccess', 'Notification sent to guests.')); } catch (err) { if (!isAuthError(err)) { const message = getApiErrorMessage(err, t('guestMessages.errorSend', 'Message could not be sent.')); setError(message); toast.error(message); } } finally { setSending(false); } } const locale = i18n.language?.startsWith('en') ? 'en-GB' : 'de-DE'; const mutedText = String(theme.gray?.val ?? '#6b7280'); return ( navigate(-1)} headerActions={ loadHistory()}> } > {error ? ( {error} ) : null} {t('guestMessages.composeTitle', 'Send a message')} setForm((prev) => ({ ...prev, title: e.target.value }))} placeholder={t('guestMessages.form.titlePlaceholder', 'Gallery reminder, upload nudge, ...')} style={{ ...inputStyle, height: 40 }} />