From 55ef3e05ef5e6c1be4607d8d366e93498c697e18 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 19:40:37 +0000 Subject: [PATCH] feat: Add automatic email signature --- src/App.tsx | 2 + src/components/layout/AdminSidebar.tsx | 6 +- src/pages/admin/AdminEmailSettings.tsx | 201 ++++++++++++++++++ .../functions/send-admin-email-reply/index.ts | 18 +- 4 files changed, 224 insertions(+), 3 deletions(-) create mode 100644 src/pages/admin/AdminEmailSettings.tsx diff --git a/src/App.tsx b/src/App.tsx index 717298c6..ab3c85d2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -54,6 +54,7 @@ const AdminUsers = lazy(() => import("./pages/AdminUsers")); const AdminBlog = lazy(() => import("./pages/AdminBlog")); const AdminSettings = lazy(() => import("./pages/AdminSettings")); const AdminContact = lazy(() => import("./pages/admin/AdminContact")); +const AdminEmailSettings = lazy(() => import("./pages/admin/AdminEmailSettings")); // User routes (lazy-loaded) const Profile = lazy(() => import("./pages/Profile")); @@ -136,6 +137,7 @@ function AppContent() { } /> } /> } /> + } /> {/* Utility routes - lazy loaded */} } /> diff --git a/src/components/layout/AdminSidebar.tsx b/src/components/layout/AdminSidebar.tsx index e159d218..db51ead4 100644 --- a/src/components/layout/AdminSidebar.tsx +++ b/src/components/layout/AdminSidebar.tsx @@ -1,4 +1,4 @@ -import { LayoutDashboard, FileText, Flag, Users, Settings, ArrowLeft, ScrollText, BookOpen, Inbox } from 'lucide-react'; +import { LayoutDashboard, FileText, Flag, Users, Settings, ArrowLeft, ScrollText, BookOpen, Inbox, Mail } from 'lucide-react'; import { NavLink } from 'react-router-dom'; import { useUserRole } from '@/hooks/useUserRole'; import { useSidebar } from '@/hooks/useSidebar'; @@ -62,6 +62,10 @@ export function AdminSidebar() { title: 'Settings', url: '/admin/settings', icon: Settings, + }, { + title: 'Email Settings', + url: '/admin/email-settings', + icon: Mail, }] : []), ]; diff --git a/src/pages/admin/AdminEmailSettings.tsx b/src/pages/admin/AdminEmailSettings.tsx new file mode 100644 index 00000000..26816ede --- /dev/null +++ b/src/pages/admin/AdminEmailSettings.tsx @@ -0,0 +1,201 @@ +import { useState, useEffect } from 'react'; +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; +import { Save, Loader2, Mail } from 'lucide-react'; +import { supabase } from '@/integrations/supabase/client'; +import { Button } from '@/components/ui/button'; +import { Textarea } from '@/components/ui/textarea'; +import { Label } from '@/components/ui/label'; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from '@/components/ui/card'; +import { AdminLayout } from '@/components/layout/AdminLayout'; +import { useUserRole } from '@/hooks/useUserRole'; +import { handleError, handleSuccess } from '@/lib/errorHandler'; +import { Alert, AlertDescription } from '@/components/ui/alert'; + +export default function AdminEmailSettings() { + const queryClient = useQueryClient(); + const { isSuperuser, loading: rolesLoading } = useUserRole(); + const [signature, setSignature] = useState(''); + + // Fetch email signature + const { data: signatureSetting, isLoading } = useQuery({ + queryKey: ['admin-email-signature'], + queryFn: async () => { + const { data, error } = await supabase + .from('admin_settings') + .select('setting_value') + .eq('setting_key', 'email.signature') + .single(); + + if (error && error.code !== 'PGRST116') { // PGRST116 = no rows returned + throw error; + } + + // Type guard for the setting value + const settingValue = data?.setting_value as { signature?: string } | null; + return settingValue?.signature || ''; + }, + }); + + useEffect(() => { + if (signatureSetting !== undefined) { + setSignature(signatureSetting); + } + }, [signatureSetting]); + + // Update email signature mutation + const updateSignatureMutation = useMutation({ + mutationFn: async (newSignature: string) => { + const { data: existing } = await supabase + .from('admin_settings') + .select('id') + .eq('setting_key', 'email.signature') + .single(); + + if (existing) { + // Update existing + const { error } = await supabase + .from('admin_settings') + .update({ + setting_value: { signature: newSignature }, + updated_at: new Date().toISOString(), + }) + .eq('setting_key', 'email.signature'); + + if (error) throw error; + } else { + // Insert new + const { error } = await supabase + .from('admin_settings') + .insert({ + setting_key: 'email.signature', + setting_value: { signature: newSignature }, + category: 'email', + description: 'Email signature automatically appended to all contact submission replies', + }); + + if (error) throw error; + } + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['admin-email-signature'] }); + handleSuccess('Saved', 'Email signature has been updated successfully'); + }, + onError: (error) => { + handleError(error, { action: 'update_email_signature' }); + }, + }); + + const handleSave = () => { + updateSignatureMutation.mutate(signature); + }; + + // Show loading state while roles are being fetched + if (rolesLoading) { + return ( +
+ +
+ ); + } + + // Superuser-only access check + if (!isSuperuser()) { + return ( + +
+ + + +

Access Denied

+

+ Email settings can only be managed by superusers. +

+
+
+
+
+ ); + } + + return ( + +
+

Email Settings

+

+ Configure automatic email signature for contact submission replies +

+
+ + + + + + Email Signature + + + This signature will be automatically appended to all email replies sent to users from the Contact Submissions page. + The signature will be added after a separator line. + + + + + + Preview: The signature will appear as: +
+ [Your reply message] +
+
+ --- +
+ {signature || '[No signature set]'} +
+
+
+ +
+ +