"use client"; import { useState } from "react"; import { AnimatedModal } from "~/components/ui/AnimatedModal.js"; import { trpc } from "~/lib/trpc/client.js"; const TARGET_TYPES = [ { value: "all", label: "All Users" }, { value: "role", label: "By Role" }, { value: "project", label: "By Project" }, { value: "orgUnit", label: "By Org Unit" }, ] as const; const ROLES = ["ADMIN", "MANAGER", "CONTROLLER", "USER", "VIEWER"] as const; const PRIORITY_OPTIONS = [ { value: "LOW", label: "Low" }, { value: "NORMAL", label: "Normal" }, { value: "HIGH", label: "High" }, { value: "URGENT", label: "Urgent" }, ] as const; const CHANNEL_OPTIONS = [ { value: "in_app", label: "In-App" }, { value: "email", label: "Email" }, { value: "both", label: "Both" }, ] as const; interface BroadcastModalProps { onClose: () => void; onSuccess: () => void; } export function BroadcastModal({ onClose, onSuccess }: BroadcastModalProps) { const [title, setTitle] = useState(""); const [body, setBody] = useState(""); const [targetType, setTargetType] = useState("all"); const [targetValue, setTargetValue] = useState(""); const [priority, setPriority] = useState("NORMAL"); const [channel, setChannel] = useState("in_app"); const [link, setLink] = useState(""); const [serverError, setServerError] = useState(null); const [result, setResult] = useState<{ recipientCount: number } | null>(null); const utils = trpc.useUtils(); const createMutation = trpc.notification.createBroadcast.useMutation({ onSuccess: async (data) => { await utils.notification.listBroadcasts.invalidate(); const count = (data as { recipientCount?: number }).recipientCount ?? 0; setResult({ recipientCount: count }); }, onError: (err) => setServerError(err.message), }); const isPending = createMutation.isPending; function handleSubmit(e: React.FormEvent) { e.preventDefault(); setServerError(null); if (!title.trim()) { setServerError("Title is required."); return; } createMutation.mutate({ title: title.trim(), ...(body.trim() ? { body: body.trim() } : {}), targetType: targetType as "all" | "role" | "project" | "orgUnit" | "user", ...(targetType !== "all" && targetValue.trim() ? { targetValue: targetValue.trim() } : {}), priority: priority as "LOW" | "NORMAL" | "HIGH" | "URGENT", channel: channel as "in_app" | "email" | "both", ...(link.trim() ? { link: link.trim() } : {}), }); } const inputClass = "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 text-sm dark:bg-gray-900 dark:text-gray-100"; const labelClass = "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"; // After successful send, show result if (result) { return ( { onSuccess(); onClose(); }} maxWidth="max-w-lg">

Broadcast Sent

Sent to {result.recipientCount} recipient{result.recipientCount !== 1 ? "s" : ""}

); } return ( {/* Header */}

Send Broadcast

{/* Title */}
setTitle(e.target.value)} maxLength={200} className={inputClass} required placeholder="Broadcast title..." />
{/* Body */}