'use client'; /** * Per-category send-from routing matrix. * * Renders one row per email category with a `noreply | sales` dropdown. * When the port has no sales SMTP credentials configured, the `sales` * option is disabled across the board and the card explains why. */ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { Loader2 } from 'lucide-react'; import { toast } from 'sonner'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { WarningCallout } from '@/components/ui/warning-callout'; import { apiFetch } from '@/lib/api/client'; import { toastError } from '@/lib/api/toast-error'; type Sender = 'noreply' | 'sales'; interface RoutingResponse { data: { routing: Record; isSalesAvailable: boolean; categories: readonly string[]; }; } const LABELS: Record = { account_activation: 'Account activation', password_reset: 'Password reset', notification_digest: 'Notification digest', eoi_signing_request: 'EOI signing request', reservation_signing_request: 'Reservation signing request', contract_signing_request: 'Contract signing request', brochure_send: 'Brochure send', berth_pdf_send: 'Berth PDF send', signed_doc_completion: 'Signed-doc completion', sales_sendout: 'Sales send-out', manual_compose: 'Manual rep compose', inquiry_confirmation: 'Inquiry confirmation (to client)', inquiry_sales_notification: 'Inquiry notification (to sales)', crm_invite: 'CRM user invite', admin_email_change: 'Admin email-change notice', gdpr_export: 'GDPR export delivery', system_other: 'Other system-generated', }; export function EmailRoutingCard() { const qc = useQueryClient(); const { data, isLoading, isError } = useQuery({ queryKey: ['admin', 'email', 'routing'], queryFn: () => apiFetch('/api/v1/admin/email/routing'), staleTime: 30_000, }); const update = useMutation({ mutationFn: (routing: Record) => apiFetch('/api/v1/admin/email/routing', { method: 'PATCH', body: { routing }, }), onSuccess: (resp) => { qc.setQueryData(['admin', 'email', 'routing'], resp); toast.success('Send-from routing saved'); }, onError: (err) => toastError(err, 'Failed to save send-from routing'), }); if (isLoading) { return ( Send-from routing ); } if (isError || !data) { return ( Send-from routing

Failed to load routing matrix.

); } const { routing, isSalesAvailable, categories } = data.data; const setCategory = (category: string, sender: Sender) => { update.mutate({ ...routing, [category]: sender }); }; return ( Send-from routing Pick which account each email category sends from. Automated traffic typically goes through noreply; human-touch traffic (brochures, send-outs) goes through{' '} sales. {!isSalesAvailable ? (

Sales sender is disabled - configure SMTP credentials in the "Sales send-from account" card below to enable the sales option.

) : null}
{categories.map((cat) => (
{LABELS[cat] ?? cat}
))}

Changes save automatically when you pick a new sender. The sales option falls back to noreply at send-time if creds are removed.

{update.isPending ? (
Saving…
) : null}
); }