import { useState } from 'react'; import { useMutation, useQueryClient, useQuery } from '@tanstack/react-query'; import { Bell, Trash2, Settings2, Edit2, Send, Loader2, CheckCircle, XCircle, Moon, Clock, ChevronDown, ChevronUp, Calendar } from 'lucide-react'; import { api } from '../api/client'; import { formatDateOnly, parseUTCDate } from '../utils/date'; import type { NotificationProvider, NotificationProviderUpdate } from '../api/client'; import { Card, CardContent } from './Card'; import { Button } from './Button'; import { ConfirmModal } from './ConfirmModal'; import { Toggle } from './Toggle'; interface NotificationProviderCardProps { provider: NotificationProvider; onEdit: (provider: NotificationProvider) => void; } const PROVIDER_LABELS: Record = { callmebot: 'CallMeBot/WhatsApp', ntfy: 'ntfy', pushover: 'Pushover', telegram: 'Telegram', email: 'Email', discord: 'Discord', webhook: 'Webhook', }; export function NotificationProviderCard({ provider, onEdit }: NotificationProviderCardProps) { const queryClient = useQueryClient(); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const [isExpanded, setIsExpanded] = useState(false); const [testResult, setTestResult] = useState<{ success: boolean; message: string } | null>(null); // Fetch printers for linking const { data: printers } = useQuery({ queryKey: ['printers'], queryFn: api.getPrinters, }); const linkedPrinter = printers?.find(p => p.id === provider.printer_id); // Update mutation const updateMutation = useMutation({ mutationFn: (data: NotificationProviderUpdate) => api.updateNotificationProvider(provider.id, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['notification-providers'] }); }, }); // Delete mutation const deleteMutation = useMutation({ mutationFn: () => api.deleteNotificationProvider(provider.id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['notification-providers'] }); }, }); // Test mutation const testMutation = useMutation({ mutationFn: () => api.testNotificationProvider(provider.id), onSuccess: (result) => { setTestResult(result); queryClient.invalidateQueries({ queryKey: ['notification-providers'] }); }, onError: (err: Error) => { setTestResult({ success: false, message: err.message }); }, }); // Format time for display const formatTime = (time: string | null) => { if (!time) return ''; return time; }; return ( <> {/* Header Row */}

{provider.name}

{PROVIDER_LABELS[provider.provider_type] || provider.provider_type}

{/* Quick enable/disable toggle + Status indicator */}
{provider.last_success && ( Last: {formatDateOnly(provider.last_success)} )} {/* Only show error if it's more recent than last success */} {provider.last_error && provider.last_error_at && ( !provider.last_success || (parseUTCDate(provider.last_error_at)?.getTime() || 0) > (parseUTCDate(provider.last_success)?.getTime() || 0) ) && ( Error )} updateMutation.mutate({ enabled: checked })} />
{/* Linked Printer */} {linkedPrinter && (
Printer: {linkedPrinter.name}
)} {!linkedPrinter && !provider.printer_id && (
All printers
)} {/* Event summary - show all event tags */}
{provider.on_print_start && ( Start )} {provider.on_plate_not_empty && ( Plate Check )} {provider.on_print_complete && ( Complete )} {provider.on_print_failed && ( Failed )} {provider.on_print_stopped && ( Stopped )} {provider.on_print_progress && ( Progress )} {provider.on_printer_offline && ( Offline )} {provider.on_printer_error && ( Error )} {provider.on_filament_low && ( Low Filament )} {provider.on_maintenance_due && ( Maintenance )} {provider.on_ams_humidity_high && ( AMS Humidity )} {provider.on_ams_temperature_high && ( AMS Temp )} {provider.on_ams_ht_humidity_high && ( AMS-HT Humidity )} {provider.on_ams_ht_temperature_high && ( AMS-HT Temp )} {provider.on_bed_cooled && ( Bed Cooled )} {provider.quiet_hours_enabled && ( Quiet )} {provider.daily_digest_enabled && ( Digest {provider.daily_digest_time} )}
{/* Test Button */}
{/* Test Result */} {testResult && (
{testResult.success ? ( ) : ( )} {testResult.message}
)} {/* Toggle Settings Panel */} {/* Expanded Settings */} {isExpanded && (
{/* Enabled Toggle */}

Enabled

Send notifications from this provider

updateMutation.mutate({ enabled: checked })} />
{/* Print Lifecycle Events */}

Print Events

Print Started

updateMutation.mutate({ on_print_start: checked })} />

Plate Not Empty

Objects detected before print

updateMutation.mutate({ on_plate_not_empty: checked })} />

Print Completed

updateMutation.mutate({ on_print_complete: checked })} />

Bed Cooled

Bed cooled below threshold after print

updateMutation.mutate({ on_bed_cooled: checked })} />

Print Failed

updateMutation.mutate({ on_print_failed: checked })} />

Print Stopped

updateMutation.mutate({ on_print_stopped: checked })} />

Progress Milestones

Notify at 25%, 50%, 75%

updateMutation.mutate({ on_print_progress: checked })} />
{/* Printer Status Events */}

Printer Status

Printer Offline

updateMutation.mutate({ on_printer_offline: checked })} />

Printer Error

updateMutation.mutate({ on_printer_error: checked })} />

Low Filament

updateMutation.mutate({ on_filament_low: checked })} />

Maintenance Due

Notify when maintenance is needed

updateMutation.mutate({ on_maintenance_due: checked })} />
{/* AMS Environmental Alarms (regular AMS) */}

AMS Alarms

AMS Humidity High

Regular AMS humidity exceeds threshold

updateMutation.mutate({ on_ams_humidity_high: checked })} />

AMS Temperature High

Regular AMS temperature exceeds threshold

updateMutation.mutate({ on_ams_temperature_high: checked })} />
{/* AMS-HT Environmental Alarms */}

AMS-HT Alarms

AMS-HT Humidity High

AMS-HT humidity exceeds threshold

updateMutation.mutate({ on_ams_ht_humidity_high: checked })} />

AMS-HT Temperature High

AMS-HT temperature exceeds threshold

updateMutation.mutate({ on_ams_ht_temperature_high: checked })} />
{/* Print Queue Events */}

Print Queue

Job Added

Job added to queue

updateMutation.mutate({ on_queue_job_added: checked })} />

Job Assigned

Model-based job assigned to printer

updateMutation.mutate({ on_queue_job_assigned: checked })} />

Job Started

Queue job started printing

updateMutation.mutate({ on_queue_job_started: checked })} />

Job Waiting

Job waiting for filament

updateMutation.mutate({ on_queue_job_waiting: checked })} />

Job Skipped

Job skipped (previous failed)

updateMutation.mutate({ on_queue_job_skipped: checked })} />

Job Failed

Job failed to start

updateMutation.mutate({ on_queue_job_failed: checked })} />

Queue Complete

All queue jobs finished

updateMutation.mutate({ on_queue_completed: checked })} />
{/* Quiet Hours */}

Quiet Hours

updateMutation.mutate({ quiet_hours_enabled: checked })} />
{provider.quiet_hours_enabled && (

No notifications during these hours

{formatTime(provider.quiet_hours_start) || '22:00'} - {formatTime(provider.quiet_hours_end) || '07:00'}

Edit provider to change quiet hours

)}
{/* Daily Digest */}

Daily Digest

updateMutation.mutate({ daily_digest_enabled: checked })} />
{provider.daily_digest_enabled && (

Batch notifications into a single daily summary

Send at {formatTime(provider.daily_digest_time) || '08:00'}

Edit provider to change digest time

)}
{/* Action Buttons */}
)}
{/* Delete Confirmation */} {showDeleteConfirm && ( { deleteMutation.mutate(); setShowDeleteConfirm(false); }} onCancel={() => setShowDeleteConfirm(false)} /> )} ); }