import { useMutation, useQuery } from "@tanstack/react-query"; import { Bell, Plus, Trash2 } from "lucide-react"; import { useEffect, useState } from "react"; import { toast } from "sonner"; import { Button } from "~/client/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/client/components/ui/card"; import { Switch } from "~/client/components/ui/switch"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/client/components/ui/select"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "~/client/components/ui/table"; import { Badge } from "~/client/components/ui/badge"; import { getScheduleNotificationsOptions, updateScheduleNotificationsMutation, } from "~/client/api-client/@tanstack/react-query.gen"; import { parseError } from "~/client/lib/errors"; import type { NotificationDestination } from "~/client/lib/types"; type Props = { scheduleId: number; destinations: NotificationDestination[]; }; type NotificationAssignment = { destinationId: number; notifyOnStart: boolean; notifyOnSuccess: boolean; notifyOnFailure: boolean; }; export const ScheduleNotificationsConfig = ({ scheduleId, destinations }: Props) => { const [assignments, setAssignments] = useState>(new Map()); const [hasChanges, setHasChanges] = useState(false); const [isAddingNew, setIsAddingNew] = useState(false); const { data: currentAssignments } = useQuery({ ...getScheduleNotificationsOptions({ path: { scheduleId: scheduleId.toString() } }), }); const updateNotifications = useMutation({ ...updateScheduleNotificationsMutation(), onSuccess: () => { toast.success("Notification settings saved successfully"); setHasChanges(false); }, onError: (error) => { toast.error("Failed to save notification settings", { description: parseError(error)?.message, }); }, }); useEffect(() => { if (currentAssignments) { const map = new Map(); for (const assignment of currentAssignments) { map.set(assignment.destinationId, { destinationId: assignment.destinationId, notifyOnStart: assignment.notifyOnStart, notifyOnSuccess: assignment.notifyOnSuccess, notifyOnFailure: assignment.notifyOnFailure, }); } setAssignments(map); } }, [currentAssignments]); const addDestination = (destinationId: string) => { const id = Number.parseInt(destinationId, 10); const newAssignments = new Map(assignments); newAssignments.set(id, { destinationId: id, notifyOnStart: false, notifyOnSuccess: false, notifyOnFailure: true, }); setAssignments(newAssignments); setHasChanges(true); setIsAddingNew(false); }; const removeDestination = (destinationId: number) => { const newAssignments = new Map(assignments); newAssignments.delete(destinationId); setAssignments(newAssignments); setHasChanges(true); }; const toggleEvent = (destinationId: number, event: "notifyOnStart" | "notifyOnSuccess" | "notifyOnFailure") => { const assignment = assignments.get(destinationId); if (!assignment) return; const newAssignments = new Map(assignments); newAssignments.set(destinationId, { ...assignment, [event]: !assignment[event], }); setAssignments(newAssignments); setHasChanges(true); }; const handleSave = () => { const assignmentsList = Array.from(assignments.values()); updateNotifications.mutate({ path: { scheduleId: scheduleId.toString() }, body: { assignments: assignmentsList, }, }); }; const handleReset = () => { if (currentAssignments) { const map = new Map(); for (const assignment of currentAssignments) { map.set(assignment.destinationId, { destinationId: assignment.destinationId, notifyOnStart: assignment.notifyOnStart, notifyOnSuccess: assignment.notifyOnSuccess, notifyOnFailure: assignment.notifyOnFailure, }); } setAssignments(map); setHasChanges(false); } }; const getDestinationById = (id: number) => { return destinations?.find((d) => d.id === id); }; const availableDestinations = destinations?.filter((d) => !assignments.has(d.id)) || []; const assignedDestinations = Array.from(assignments.keys()) .map((id) => getDestinationById(id)) .filter((d) => d !== undefined); return (
Notifications Configure which notifications to send for this backup schedule
{!isAddingNew && availableDestinations.length > 0 && ( )}
{isAddingNew && (
)} {assignedDestinations.length === 0 ? (

No notifications configured for this schedule.

Click "Add notification" to get started.

) : (
Destination Start Success Failure {assignedDestinations.map((destination) => { const assignment = assignments.get(destination.id); if (!assignment) return null; return (
{destination.name} {destination.type}
toggleEvent(destination.id, "notifyOnStart")} /> toggleEvent(destination.id, "notifyOnSuccess")} /> toggleEvent(destination.id, "notifyOnFailure")} />
); })}
)} {hasChanges && (
)}
); };