'use client'; import {useState} from 'react'; import {Dialog, DialogHeader} from '@astryxdesign/core/Dialog'; import {VStack, HStack} from '@astryxdesign/core/Layout'; import {TextInput} from '@astryxdesign/core/TextInput'; import {Selector} from '@astryxdesign/core/Selector'; import {Button} from '@astryxdesign/core/Button'; import {Banner} from '@astryxdesign/core/Banner'; import {useWorkspace} from '@/shared/providers/WorkspaceProvider'; import {engagementRepository} from '@/features/engagement/repositories/engagementRepository'; import type {ActivityRow} from '@/features/engagement/types/engagement'; const NO_SHOP = '__none__'; /** * Record that somebody took part in an activity — a selfie, a spin, a * challenge. The cameras cannot observe these, so without this the activity * panels stay at zero forever. * * ── One id per real-world event ────────────────────────────────────────── * `sourceEventId` is minted ONCE, when the dialog opens, and reused by every * retry of this submission. The platform de-duplicates on it, so tapping * "Record" again after a timeout cannot count the same spin twice. A new * dialog is a new event and gets a new id. */ export function RecordActivityDialog({ activities, onClose, onRecorded, }: { activities: ActivityRow[]; onClose: () => void; onRecorded: () => void; }) { const {stores, storeId} = useWorkspace(); const live = activities.filter((a) => a.isActive); const [sourceEventId] = useState(() => crypto.randomUUID()); const [kind, setKind] = useState(live[0]?.kind ?? ''); const [site, setSite] = useState(storeId !== 'all' ? storeId : NO_SHOP); const [customer, setCustomer] = useState(''); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); async function submit() { setBusy(true); setError(null); const res = await engagementRepository.recordEvent({ kind, sourceEventId, site: site === NO_SHOP ? undefined : site, visitorId: customer.trim() || undefined, }); setBusy(false); if (!res.ok) { setError(res.message ?? 'Could not record that.'); return; } onRecorded(); } return ( (open ? undefined : onClose())} purpose="required" width={440} aria-label="Record an activity" > (open ? undefined : onClose())} /> setKind(v)} options={live.map((a) => ({value: a.kind, label: a.name}))} /> setSite(v)} options={[ {value: NO_SHOP, label: 'Not at a shop'}, ...stores.filter((s) => s.id !== 'all').map((s) => ({value: s.id, label: s.name})), ]} /> {error ? : null} ); }