diff --git a/bun.lockb b/bun.lockb index 2b9882d..e560d2f 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/src/integrations/supabase/types.ts b/src/integrations/supabase/types.ts index d940019..9c91749 100644 --- a/src/integrations/supabase/types.ts +++ b/src/integrations/supabase/types.ts @@ -968,6 +968,63 @@ export type Database = { }, ] } + email_logs: { + Row: { + bcc_addresses: string[] + body_html: string | null + body_text: string | null + case_id: string | null + cc_addresses: string[] + context: string | null + created_at: string + error_message: string | null + from_address: string | null + id: string + reply_to: string | null + sent_at: string + sent_by: string | null + status: string + subject: string + to_addresses: string[] + } + Insert: { + bcc_addresses?: string[] + body_html?: string | null + body_text?: string | null + case_id?: string | null + cc_addresses?: string[] + context?: string | null + created_at?: string + error_message?: string | null + from_address?: string | null + id?: string + reply_to?: string | null + sent_at?: string + sent_by?: string | null + status?: string + subject: string + to_addresses?: string[] + } + Update: { + bcc_addresses?: string[] + body_html?: string | null + body_text?: string | null + case_id?: string | null + cc_addresses?: string[] + context?: string | null + created_at?: string + error_message?: string | null + from_address?: string | null + id?: string + reply_to?: string | null + sent_at?: string + sent_by?: string | null + status?: string + subject?: string + to_addresses?: string[] + } + Relationships: [] + } expenses: { Row: { amount: number @@ -1789,6 +1846,7 @@ export type Database = { smtp_settings: { Row: { created_at: string + default_bcc: string | null enabled: boolean from_email: string from_name: string | null @@ -1804,6 +1862,7 @@ export type Database = { } Insert: { created_at?: string + default_bcc?: string | null enabled?: boolean from_email: string from_name?: string | null @@ -1819,6 +1878,7 @@ export type Database = { } Update: { created_at?: string + default_bcc?: string | null enabled?: boolean from_email?: string from_name?: string | null diff --git a/src/routes/settings.smtp.tsx b/src/routes/settings.smtp.tsx index 742a8a8..0b3b889 100644 --- a/src/routes/settings.smtp.tsx +++ b/src/routes/settings.smtp.tsx @@ -8,7 +8,8 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Switch } from "@/components/ui/switch"; -import { Loader2, Mail, Send, KeyRound } from "lucide-react"; +import { Badge } from "@/components/ui/badge"; +import { Loader2, Mail, Send, KeyRound, History } from "lucide-react"; import { toast } from "sonner"; export const Route = createFileRoute("/settings/smtp")({ @@ -23,10 +24,22 @@ const EMPTY = { from_email: "", from_name: "", reply_to: "", + default_bcc: "", enabled: true, notes: "", }; +interface EmailLog { + id: string; + sent_at: string; + to_addresses: string[]; + bcc_addresses: string[]; + subject: string; + status: string; + error_message: string | null; + context: string | null; +} + function SmtpSettingsPage() { const { user, isAdmin } = useAuth(); const [loading, setLoading] = useState(true); @@ -35,15 +48,24 @@ function SmtpSettingsPage() { const [recordId, setRecordId] = useState(null); const [form, setForm] = useState({ ...EMPTY }); const [testTo, setTestTo] = useState(""); + const [logs, setLogs] = useState([]); const load = async () => { setLoading(true); - const { data } = await supabase - .from("smtp_settings") - .select("*") - .order("updated_at", { ascending: false }) - .limit(1) - .maybeSingle(); + const [settingsRes, logsRes] = await Promise.all([ + supabase + .from("smtp_settings") + .select("*") + .order("updated_at", { ascending: false }) + .limit(1) + .maybeSingle(), + supabase + .from("email_logs") + .select("id, sent_at, to_addresses, bcc_addresses, subject, status, error_message, context") + .order("sent_at", { ascending: false }) + .limit(25), + ]); + const data = settingsRes.data; if (data) { setRecordId(data.id); setForm({ @@ -54,10 +76,12 @@ function SmtpSettingsPage() { from_email: data.from_email ?? "", from_name: data.from_name ?? "", reply_to: data.reply_to ?? "", + default_bcc: (data as any).default_bcc ?? "", enabled: data.enabled ?? true, notes: data.notes ?? "", }); } + setLogs((logsRes.data as EmailLog[]) ?? []); setLoading(false); }; @@ -83,6 +107,7 @@ function SmtpSettingsPage() { from_email: form.from_email.trim(), from_name: form.from_name || null, reply_to: form.reply_to || null, + default_bcc: form.default_bcc.trim() || null, enabled: form.enabled, notes: form.notes || null, updated_by: user?.id, @@ -111,6 +136,8 @@ function SmtpSettingsPage() { subject: "SMTP test from your firm CRM", html: `

This is a test email sent from ${form.from_email} via ${form.host}:${form.port}.

If you received this, your SMTP settings are working.

`, text: `Test email from ${form.from_email} via ${form.host}:${form.port}.`, + test: true, + context: "smtp_test", }, }); setTesting(false); @@ -118,9 +145,11 @@ function SmtpSettingsPage() { toast.error("Send failed", { description: (error as any)?.message || (data as any)?.error, }); + load(); return; } toast.success(`Test email sent to ${testTo}`); + load(); }; if (!isAdmin) { @@ -218,6 +247,16 @@ function SmtpSettingsPage() { onChange={(e) => update("reply_to", e.target.value)} /> + + update("default_bcc", e.target.value)} + placeholder="archive@yourfirm.com, partner@yourfirm.com" + /> +

+ Every outbound email will be silently BCC'd to these addresses (comma-separated). +

+