"use client"; import React, { Suspense, useEffect, useMemo, useState } from "react"; import { useAuth } from "@/hooks/useAuth"; import { useRouter, useSearchParams } from "next/navigation"; import { apiFetch, fetchAllUsers } from "@/lib/api"; import { useDismissingState } from "@/hooks/useDismissingState"; import { MessageCircle } from "lucide-react"; // Attendee with preference info type Attendee = { id: string; name: string; phone: string; pref: string }; type UserEntry = { id: string; name: string; phone: string; pref: string }; function toLocalInputValue(d: Date) { const pad = (n: number) => String(n).padStart(2, "0"); return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`; } export default function WhatsAppAttendeesPage() { return ( Loading...}> ); } // ─── Attendees dropdown with preference indicators ──────────────────────────── function AttendeesCheckboxDropdown({ attendees, loading, selectedIds, onChange, channel = "whatsapp", }: { attendees: (Attendee | UserEntry)[]; loading: boolean; selectedIds: string[]; onChange: (ids: string[]) => void; channel?: "whatsapp" | "email"; }) { const [open, setOpen] = useState(false); const allIds = useMemo(() => attendees.map((a) => a.id), [attendees]); const allSelected = selectedIds.length > 0 && selectedIds.length === allIds.length; const toggleAll = (checked: boolean) => onChange(checked ? allIds : []); const toggleId = (id: string) => { if (selectedIds.includes(id)) onChange(selectedIds.filter((x) => x !== id)); else onChange([...selectedIds, id]); }; const prefMatch = (pref: string) => channel === "whatsapp" ? pref === "whatsapp" || pref === "both" : pref === "email" || pref === "both"; const prefLabel = (pref: string) => { if (pref === "both") return "both"; if (pref === "whatsapp") return "WA"; if (pref === "email") return "email"; return pref || "email"; }; const prefColor = (pref: string, match: boolean) => match ? "text-green-700 bg-green-50" : "text-amber-700 bg-amber-50"; const summary = loading ? "Loading…" : attendees.length === 0 ? "No attendees" : allSelected ? `All (${attendees.length})` : selectedIds.length === 0 ? "None selected" : `${selectedIds.length} selected`; const mismatched = selectedIds.filter((id) => { const a = attendees.find((x) => x.id === id); return a && !prefMatch(a.pref); }); return (
{open && (
{loading ? (
Loading…
) : attendees.length === 0 ? (
No attendees with phone numbers
) : ( )}
)}
); } // ─── Preference warning banner ──────────────────────────────────────────────── function PrefWarning({ attendees, selectedIds, channel }: { attendees: (Attendee|UserEntry)[]; selectedIds: string[]; channel: "whatsapp" | "email" }) { const prefMatch = (pref: string) => channel === "whatsapp" ? pref === "whatsapp" || pref === "both" : pref === "email" || pref === "both"; const mismatched = useMemo( () => selectedIds.filter((id) => { const a = attendees.find((x) => x.id === id); return a && !prefMatch(a.pref); }), [attendees, selectedIds, channel] ); if (mismatched.length === 0) return null; return (
{mismatched.length} selected attendee(s) have a notification preference that doesn't include{" "} {channel === "whatsapp" ? "WhatsApp" : "email"}. They will still receive the message, but it may not be their preferred channel. {" "}
); } // ─── WhatsApp Automations Panel ─────────────────────────────────────────────── function WAAutomationsPanel({ events, token, onInfo, onError }: { events: any[]; token?: string | null; onInfo: (s: string) => void; onError: (s: string) => void }) { const [autoEventId, setAutoEventId] = React.useState(""); const currentEvent = React.useMemo(() => (events || []).find((e: any) => e.id === autoEventId), [events, autoEventId]); const preWeekDefault = `Hi {{name}}\n\nJust a friendly reminder that {{event.title}} is one week away!\n\nEvent details: {{event.link}}`; const finalDefault = `Hi {{name}}\n\nFinal reminder for {{event.title}}.\nPlease have your QR code/ticket ready at entry.\n\nEvent details: {{event.link}}`; const thanksDefault = `Hi {{name}}\n\nThank you for joining us at {{event.title}}!\nWe hope you had a great time. See you next time!`; const promoDefault = `Hi {{name}}\n\nWe'd love to see you at our next event: {{promo.title}}.\nFind out more and register here: {{promo.link}}`; const [enablePre, setEnablePre] = React.useState(true); const [enableFinal, setEnableFinal] = React.useState(true); const [enableThanks, setEnableThanks] = React.useState(true); const [enablePromo, setEnablePromo] = React.useState(false); const [preBody, setPreBody] = React.useState(preWeekDefault); const [finalBody, setFinalBody] = React.useState(finalDefault); const [thanksBody, setThanksBody] = React.useState(thanksDefault); const [promoBody, setPromoBody] = React.useState(promoDefault); const [finalHours, setFinalHours] = React.useState<"24" | "48">("24"); const [preWhen, setPreWhen] = React.useState(""); const [finalWhen, setFinalWhen] = React.useState(""); const [thanksWhen, setThanksWhen] = React.useState(""); const [promoWhen, setPromoWhen] = React.useState(""); const [promoEventId, setPromoEventId] = React.useState(""); React.useEffect(() => { if (!currentEvent) { setPreWhen(""); setFinalWhen(""); setThanksWhen(""); setPromoWhen(""); return; } try { const start = new Date(currentEvent.startDate); const end = new Date(currentEvent.endDate || currentEvent.startDate); const pre = new Date(start); pre.setDate(pre.getDate() - 7); pre.setHours(9, 0, 0, 0); setPreWhen(toLocalInputValue(pre)); const fin = new Date(start); fin.setHours(fin.getHours() - (finalHours === "48" ? 48 : 24)); setFinalWhen(toLocalInputValue(fin)); const ty = new Date(end); ty.setDate(ty.getDate() + 1); ty.setHours(9, 0, 0, 0); setThanksWhen(toLocalInputValue(ty)); const pr = new Date(end); pr.setDate(pr.getDate() + 3); pr.setHours(9, 0, 0, 0); setPromoWhen(toLocalInputValue(pr)); } catch {} }, [currentEvent, finalHours]); const onSchedule = async () => { try { onError(null as any); onInfo(null as any); if (!token) { onError("Not authenticated"); return; } if (!autoEventId) { onError("Please select an event"); return; } const jobs: any[] = []; if (enablePre && preBody.trim() && preWhen) jobs.push({ message: preBody, scheduledAt: new Date(preWhen).toISOString() }); if (enableFinal && finalBody.trim() && finalWhen) jobs.push({ message: finalBody, scheduledAt: new Date(finalWhen).toISOString() }); if (enableThanks && thanksBody.trim() && thanksWhen) jobs.push({ message: thanksBody, scheduledAt: new Date(thanksWhen).toISOString() }); if (enablePromo && promoBody.trim() && promoWhen) jobs.push({ message: promoBody, scheduledAt: new Date(promoWhen).toISOString(), promoEventId: promoEventId || undefined }); if (jobs.length === 0) { onError("Please enable at least one automation and fill in details"); return; } let scheduled = 0; for (const job of jobs) { await apiFetch(`/api/events/${encodeURIComponent(autoEventId)}/whatsapp-attendees/schedule`, { method: "POST", authToken: token, body: { message: job.message, scheduledAt: job.scheduledAt, filter: { status: "paid" } }, }); scheduled++; } onInfo(`Scheduled ${scheduled} WhatsApp automation(s).`); } catch (e: any) { onError(e?.message || "Failed to schedule automations"); } }; return (
Pre-Event Reminder (1 week before)
setPreWhen(e.target.value)} />