From 7f47ec48098060995ea51ff85c84865711ff2520 Mon Sep 17 00:00:00 2001 From: Jonny Nguyen Date: Fri, 26 Jun 2026 23:55:39 -0700 Subject: [PATCH] Refactor SettingsPage component for improved readability and consistency Update formatting in SettingsPage.tsx to enhance code clarity by standardizing quotation marks and restructuring object definitions. Maintain functionality while improving overall code style and organization. --- apps/web/src/pages/SettingsPage.tsx | 309 ++++++++++++++++------------ 1 file changed, 183 insertions(+), 126 deletions(-) diff --git a/apps/web/src/pages/SettingsPage.tsx b/apps/web/src/pages/SettingsPage.tsx index 8ef4a96..351a6a3 100644 --- a/apps/web/src/pages/SettingsPage.tsx +++ b/apps/web/src/pages/SettingsPage.tsx @@ -1,149 +1,177 @@ -import { useEffect, useState } from 'react'; -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { Loader2, Play, KeyRound } from 'lucide-react'; -import { apiGet, apiPatch, apiPost } from '../lib/api'; -import { useAuth } from '../lib/auth'; -import type { Settings, JobStatus } from '../lib/types'; -import { PageHeader } from '../components/PageHeader'; -import { Button } from '../components/ui/Button'; -import { Input } from '../components/ui/Input'; -import { Label } from '../components/ui/Label'; -import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '../components/ui/Card'; -import { Alert } from '../components/ui/Alert'; -import { Skeleton } from '../components/ui/Skeleton'; +import { useEffect, useState } from "react" +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" +import { Loader2, Play, KeyRound } from "lucide-react" +import { apiGet, apiPatch, apiPost } from "../lib/api" +import { useAuth } from "../lib/auth" +import type { Settings, JobStatus } from "../lib/types" +import { PageHeader } from "../components/PageHeader" +import { Button } from "../components/ui/Button" +import { Input } from "../components/ui/Input" +import { Label } from "../components/ui/Label" +import { + Card, + CardContent, + CardHeader, + CardTitle, + CardDescription, +} from "../components/ui/Card" +import { Alert } from "../components/ui/Alert" +import { Skeleton } from "../components/ui/Skeleton" const JOBS = [ - { id: 'dispatcher', label: 'Campaign Dispatcher', path: '/api/jobs/dispatcher/run' }, - { id: 'rollover', label: 'Campaign Rollover', path: '/api/jobs/rollover/run' }, - { id: 'rescuer', label: 'Spam Rescuer', path: '/api/jobs/rescuer/run' }, - { id: 'rollup', label: 'Stats Rollup', path: '/api/jobs/rollup/run' }, -] as const; + { + id: "dispatcher", + label: "Campaign Dispatcher", + path: "/api/jobs/dispatcher/run", + }, + { + id: "rollover", + label: "Campaign Rollover", + path: "/api/jobs/rollover/run", + }, + { id: "rescuer", label: "Spam Rescuer", path: "/api/jobs/rescuer/run" }, + { id: "rollup", label: "Stats Rollup", path: "/api/jobs/rollup/run" }, +] as const type PostmasterIntegrationStatus = { - configured: boolean; - connected: boolean; - domains: string[]; - message: string; - hasClientId: boolean; -}; + configured: boolean + connected: boolean + domains: string[] + message: string + hasClientId: boolean +} export function SettingsPage() { - const queryClient = useQueryClient(); - const { apiKey, baseUrl, login } = useAuth(); - const [localKey, setLocalKey] = useState(apiKey); - const [localUrl, setLocalUrl] = useState(baseUrl); - const [form, setForm] = useState({}); - const [message, setMessage] = useState(''); - const [error, setError] = useState(''); - const [runningJob, setRunningJob] = useState(null); + const queryClient = useQueryClient() + const { apiKey, baseUrl, login } = useAuth() + const [localKey, setLocalKey] = useState(apiKey) + const [localUrl, setLocalUrl] = useState(baseUrl) + const [form, setForm] = useState({}) + const [message, setMessage] = useState("") + const [error, setError] = useState("") + const [runningJob, setRunningJob] = useState(null) - const [googleClientId, setGoogleClientId] = useState(''); - const [googleClientSecret, setGoogleClientSecret] = useState(''); - const [microsoftClientId, setMicrosoftClientId] = useState(''); - const [microsoftClientSecret, setMicrosoftClientSecret] = useState(''); + const [googleClientId, setGoogleClientId] = useState("") + const [googleClientSecret, setGoogleClientSecret] = useState("") + const [microsoftClientId, setMicrosoftClientId] = useState("") + const [microsoftClientSecret, setMicrosoftClientSecret] = useState("") const { data: postmasterStatus, refetch: refetchPostmaster } = useQuery({ - queryKey: ['integrations', 'google', 'status'], - queryFn: () => apiGet('/api/integrations/google/status'), - }); + queryKey: ["integrations", "google", "status"], + queryFn: () => + apiGet("/api/integrations/google/status"), + }) const { data: microsoftStatus, refetch: refetchMicrosoft } = useQuery({ - queryKey: ['integrations', 'microsoft', 'status'], - queryFn: () => apiGet<{ configured: boolean; hasClientId: boolean }>( - '/api/integrations/microsoft/status', - ), - }); + queryKey: ["integrations", "microsoft", "status"], + queryFn: () => + apiGet<{ configured: boolean; hasClientId: boolean }>( + "/api/integrations/microsoft/status", + ), + }) const saveMicrosoftCredsMutation = useMutation({ mutationFn: () => - apiPost('/api/integrations/microsoft/credentials', { + apiPost("/api/integrations/microsoft/credentials", { microsoft_oauth_client_id: microsoftClientId, microsoft_oauth_client_secret: microsoftClientSecret, }), onSuccess: () => { - setMessage('Microsoft OAuth credentials saved'); - refetchMicrosoft(); + setMessage("Microsoft OAuth credentials saved") + refetchMicrosoft() }, onError: (err: Error) => setError(err.message), - }); + }) const saveGoogleCredsMutation = useMutation({ mutationFn: () => - apiPost('/api/integrations/google/credentials', { - google_oauth_client_id: googleClientId, - google_oauth_client_secret: googleClientSecret, - }), + apiPost( + "/api/integrations/google/credentials", + { + google_oauth_client_id: googleClientId, + google_oauth_client_secret: googleClientSecret, + }, + ), onSuccess: () => { - setMessage('Google OAuth credentials saved'); - refetchPostmaster(); + setMessage("Google OAuth credentials saved") + refetchPostmaster() }, onError: (err: Error) => setError(err.message), - }); + }) async function connectPostmaster() { - setError(''); + setError("") try { - const { url } = await apiGet<{ url: string }>('/api/integrations/google/auth-url'); - window.open(url, '_blank', 'noopener,noreferrer'); - setMessage('Complete Google authorization in the new tab, then refresh this page.'); + const { url } = await apiGet<{ url: string }>( + "/api/integrations/google/auth-url", + ) + window.open(url, "_blank", "noopener,noreferrer") + setMessage( + "Complete Google authorization in the new tab, then refresh this page.", + ) } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to start Google OAuth'); + setError( + err instanceof Error ? err.message : "Failed to start Google OAuth", + ) } } useEffect(() => { - if (window.location.hash === '#google-postmaster') { - document.getElementById('google-postmaster')?.scrollIntoView({ behavior: 'smooth' }); + if (window.location.hash === "#google-postmaster") { + document + .getElementById("google-postmaster") + ?.scrollIntoView({ behavior: "smooth" }) } - }, []); + }, []) const { data, isLoading } = useQuery({ - queryKey: ['settings'], - queryFn: () => apiGet('/api/settings'), - }); + queryKey: ["settings"], + queryFn: () => apiGet("/api/settings"), + }) const { data: jobStatus } = useQuery({ - queryKey: ['jobs', 'status'], - queryFn: () => apiGet<{ jobs: JobStatus[] }>('/api/jobs/status'), + queryKey: ["jobs", "status"], + queryFn: () => apiGet<{ jobs: JobStatus[] }>("/api/jobs/status"), refetchInterval: 30_000, - }); + }) useEffect(() => { - if (data) setForm(data); - }, [data]); + if (data) setForm(data) + }, [data]) const saveMutation = useMutation({ - mutationFn: (payload: Settings) => apiPatch('/api/settings', payload), + mutationFn: (payload: Settings) => + apiPatch("/api/settings", payload), onSuccess: (updated) => { - setForm(updated); - queryClient.setQueryData(['settings'], updated); - setMessage('Settings saved'); - setError(''); + setForm(updated) + queryClient.setQueryData(["settings"], updated) + setMessage("Settings saved") + setError("") }, onError: (err: Error) => setError(err.message), - }); + }) function handleSaveSettings(e: React.FormEvent) { - e.preventDefault(); - saveMutation.mutate(form); + e.preventDefault() + saveMutation.mutate(form) } function handleSaveApiConfig(e: React.FormEvent) { - e.preventDefault(); - login(localKey.trim(), localUrl.trim()); - setMessage('API configuration updated'); + e.preventDefault() + login(localKey.trim(), localUrl.trim()) + setMessage("API configuration updated") } async function runJob(path: string, id: string) { - setRunningJob(id); - setError(''); + setRunningJob(id) + setError("") try { - await apiPost(path); - setMessage(`Job "${id}" completed`); + await apiPost(path) + setMessage(`Job "${id}" completed`) } catch (err) { - setError(err instanceof Error ? err.message : 'Job failed'); + setError(err instanceof Error ? err.message : "Job failed") } finally { - setRunningJob(null); + setRunningJob(null) } } @@ -160,7 +188,9 @@ export function SettingsPage() { API Configuration - Connection settings for this dashboard session + + Connection settings for this dashboard session +
@@ -201,8 +231,10 @@ export function SettingsPage() { setForm({ ...form, dispatcher_cron: e.target.value })} + value={form.dispatcher_cron ?? ""} + onChange={(e) => + setForm({ ...form, dispatcher_cron: e.target.value }) + } placeholder="*/5 * * * *" /> @@ -210,8 +242,10 @@ export function SettingsPage() { setForm({ ...form, rescuer_cron: e.target.value })} + value={form.rescuer_cron ?? ""} + onChange={(e) => + setForm({ ...form, rescuer_cron: e.target.value }) + } placeholder="*/10 * * * *" /> @@ -219,28 +253,36 @@ export function SettingsPage() { setForm({ ...form, ramp_increment: e.target.value })} + value={form.ramp_increment ?? ""} + onChange={(e) => + setForm({ ...form, ramp_increment: e.target.value }) + } />
setForm({ ...form, ai_model: e.target.value })} + value={form.ai_model ?? ""} + onChange={(e) => + setForm({ ...form, ai_model: e.target.value }) + } />
setForm({ ...form, industries: e.target.value })} + value={form.industries ?? ""} + onChange={(e) => + setForm({ ...form, industries: e.target.value }) + } />
@@ -250,17 +292,18 @@ export function SettingsPage() { - Microsoft Outlook (@live.com) + Microsoft Outlook - Required for consumer Outlook accounts — app passwords no longer work for SMTP/IMAP + Required for consumer Outlook accounts — app passwords no longer + work for SMTP/IMAP {microsoftStatus && ( - + {microsoftStatus.configured - ? 'Microsoft OAuth credentials are configured. Connect mailboxes from Mailboxes → Add.' - : 'Add Azure app credentials below before connecting @live.com mailboxes.'} + ? "Microsoft OAuth credentials are configured. Connect mailboxes from Mailboxes → Add." + : "Add Azure app credentials below before connecting @live.com mailboxes."} )} @@ -268,7 +311,7 @@ export function SettingsPage() {

Setup (one-time)

  1. - In{' '} + In{" "} Azure App registrations - , create a Web app. Supported account types: personal Microsoft accounts. + , create a Web app. Supported account types: personal Microsoft + accounts.
  2. - Add redirect URI:{' '} + Add redirect URI:{" "} http://localhost:3000/api/integrations/microsoft/callback
  3. - Under API permissions, add delegated{' '} - IMAP.AccessAsUser.All and SMTP.Send for Office - 365 / Outlook. + Under API permissions, add delegated{" "} + IMAP.AccessAsUser.All and{" "} + SMTP.Send for Office 365 / Outlook. +
  4. +
  5. + Create a client secret, paste ID + secret below, then add + mailboxes via Connect Microsoft.
  6. -
  7. Create a client secret, paste ID + secret below, then add mailboxes via Connect Microsoft.
@@ -316,7 +363,9 @@ export function SettingsPage() { type="button" variant="outline" disabled={ - !microsoftClientId || !microsoftClientSecret || saveMicrosoftCredsMutation.isPending + !microsoftClientId || + !microsoftClientSecret || + saveMicrosoftCredsMutation.isPending } onClick={() => saveMicrosoftCredsMutation.mutate()} > @@ -330,16 +379,17 @@ export function SettingsPage() { Google Postmaster Tools - Connect once to auto-verify Gmail/Workspace domains in campaign compliance checks + Connect once to auto-verify Gmail/Workspace domains in campaign + compliance checks {postmasterStatus && ( - + {postmasterStatus.message} {postmasterStatus.domains.length > 0 && ( - Domains: {postmasterStatus.domains.join(', ')} + Domains: {postmasterStatus.domains.join(", ")} )} @@ -349,7 +399,7 @@ export function SettingsPage() {

Setup (one-time)

  1. - In{' '} + In{" "} Gmail Postmaster Tools API.
  2. - Create OAuth 2.0 credentials (Web application). Add redirect URI:{' '} + Create OAuth 2.0 credentials (Web application). Add redirect + URI:{" "} http://localhost:3000/api/integrations/google/callback
  3. - Add your sending domain at{' '} + Add your sending domain at{" "} postmaster.google.com - {' '} + {" "} and complete DNS verification there.
  4. -
  5. Paste Client ID and Secret below, save, then click Connect.
  6. +
  7. + Paste Client ID and Secret below, save, then click Connect. +
@@ -405,7 +458,11 @@ export function SettingsPage() {