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.
This commit is contained in:
Jonny Nguyen 2026-06-26 23:55:39 -07:00
parent ed3ec86025
commit 7f47ec4809

View file

@ -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<Settings>({});
const [message, setMessage] = useState('');
const [error, setError] = useState('');
const [runningJob, setRunningJob] = useState<string | null>(null);
const queryClient = useQueryClient()
const { apiKey, baseUrl, login } = useAuth()
const [localKey, setLocalKey] = useState(apiKey)
const [localUrl, setLocalUrl] = useState(baseUrl)
const [form, setForm] = useState<Settings>({})
const [message, setMessage] = useState("")
const [error, setError] = useState("")
const [runningJob, setRunningJob] = useState<string | null>(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<PostmasterIntegrationStatus>('/api/integrations/google/status'),
});
queryKey: ["integrations", "google", "status"],
queryFn: () =>
apiGet<PostmasterIntegrationStatus>("/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<PostmasterIntegrationStatus>('/api/integrations/google/credentials', {
google_oauth_client_id: googleClientId,
google_oauth_client_secret: googleClientSecret,
}),
apiPost<PostmasterIntegrationStatus>(
"/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<Settings>('/api/settings'),
});
queryKey: ["settings"],
queryFn: () => apiGet<Settings>("/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<Settings>('/api/settings', payload),
mutationFn: (payload: Settings) =>
apiPatch<Settings>("/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() {
<KeyRound className="h-5 w-5" />
API Configuration
</CardTitle>
<CardDescription>Connection settings for this dashboard session</CardDescription>
<CardDescription>
Connection settings for this dashboard session
</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSaveApiConfig} className="space-y-4 max-w-md">
@ -201,8 +231,10 @@ export function SettingsPage() {
<Label htmlFor="dispatcher_cron">Dispatcher Cron</Label>
<Input
id="dispatcher_cron"
value={form.dispatcher_cron ?? ''}
onChange={(e) => setForm({ ...form, dispatcher_cron: e.target.value })}
value={form.dispatcher_cron ?? ""}
onChange={(e) =>
setForm({ ...form, dispatcher_cron: e.target.value })
}
placeholder="*/5 * * * *"
/>
</div>
@ -210,8 +242,10 @@ export function SettingsPage() {
<Label htmlFor="rescuer_cron">Rescuer Cron</Label>
<Input
id="rescuer_cron"
value={form.rescuer_cron ?? ''}
onChange={(e) => setForm({ ...form, rescuer_cron: e.target.value })}
value={form.rescuer_cron ?? ""}
onChange={(e) =>
setForm({ ...form, rescuer_cron: e.target.value })
}
placeholder="*/10 * * * *"
/>
</div>
@ -219,28 +253,36 @@ export function SettingsPage() {
<Label htmlFor="ramp_increment">Ramp Increment</Label>
<Input
id="ramp_increment"
value={form.ramp_increment ?? ''}
onChange={(e) => setForm({ ...form, ramp_increment: e.target.value })}
value={form.ramp_increment ?? ""}
onChange={(e) =>
setForm({ ...form, ramp_increment: e.target.value })
}
/>
</div>
<div className="space-y-2">
<Label htmlFor="ai_model">AI Model</Label>
<Input
id="ai_model"
value={form.ai_model ?? ''}
onChange={(e) => setForm({ ...form, ai_model: e.target.value })}
value={form.ai_model ?? ""}
onChange={(e) =>
setForm({ ...form, ai_model: e.target.value })
}
/>
</div>
<div className="space-y-2">
<Label htmlFor="industries">Industries (JSON array)</Label>
<Input
id="industries"
value={form.industries ?? ''}
onChange={(e) => setForm({ ...form, industries: e.target.value })}
value={form.industries ?? ""}
onChange={(e) =>
setForm({ ...form, industries: e.target.value })
}
/>
</div>
<Button type="submit" disabled={saveMutation.isPending}>
{saveMutation.isPending && <Loader2 className="h-4 w-4 animate-spin" />}
{saveMutation.isPending && (
<Loader2 className="h-4 w-4 animate-spin" />
)}
Save Settings
</Button>
</form>
@ -250,17 +292,18 @@ export function SettingsPage() {
<Card id="microsoft-outlook">
<CardHeader>
<CardTitle>Microsoft Outlook (@live.com)</CardTitle>
<CardTitle>Microsoft Outlook</CardTitle>
<CardDescription>
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
</CardDescription>
</CardHeader>
<CardContent className="space-y-4 max-w-2xl">
{microsoftStatus && (
<Alert variant={microsoftStatus.configured ? 'success' : 'warning'}>
<Alert variant={microsoftStatus.configured ? "success" : "warning"}>
{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."}
</Alert>
)}
@ -268,7 +311,7 @@ export function SettingsPage() {
<p className="font-medium">Setup (one-time)</p>
<ol className="list-decimal pl-5 space-y-1">
<li>
In{' '}
In{" "}
<a
href="https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade"
target="_blank"
@ -277,20 +320,24 @@ export function SettingsPage() {
>
Azure App registrations
</a>
, create a Web app. Supported account types: personal Microsoft accounts.
, create a Web app. Supported account types: personal Microsoft
accounts.
</li>
<li>
Add redirect URI:{' '}
Add redirect URI:{" "}
<code className="text-xs bg-white px-1 py-0.5 rounded">
http://localhost:3000/api/integrations/microsoft/callback
</code>
</li>
<li>
Under API permissions, add delegated{' '}
<strong>IMAP.AccessAsUser.All</strong> and <strong>SMTP.Send</strong> for Office
365 / Outlook.
Under API permissions, add delegated{" "}
<strong>IMAP.AccessAsUser.All</strong> and{" "}
<strong>SMTP.Send</strong> for Office 365 / Outlook.
</li>
<li>
Create a client secret, paste ID + secret below, then add
mailboxes via Connect Microsoft.
</li>
<li>Create a client secret, paste ID + secret below, then add mailboxes via Connect Microsoft.</li>
</ol>
</div>
@ -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() {
<CardHeader>
<CardTitle>Google Postmaster Tools</CardTitle>
<CardDescription>
Connect once to auto-verify Gmail/Workspace domains in campaign compliance checks
Connect once to auto-verify Gmail/Workspace domains in campaign
compliance checks
</CardDescription>
</CardHeader>
<CardContent className="space-y-4 max-w-2xl">
{postmasterStatus && (
<Alert variant={postmasterStatus.connected ? 'success' : 'warning'}>
<Alert variant={postmasterStatus.connected ? "success" : "warning"}>
{postmasterStatus.message}
{postmasterStatus.domains.length > 0 && (
<span className="block mt-1 text-sm">
Domains: {postmasterStatus.domains.join(', ')}
Domains: {postmasterStatus.domains.join(", ")}
</span>
)}
</Alert>
@ -349,7 +399,7 @@ export function SettingsPage() {
<p className="font-medium">Setup (one-time)</p>
<ol className="list-decimal pl-5 space-y-1">
<li>
In{' '}
In{" "}
<a
href="https://console.cloud.google.com/apis/library/gmailpostmastertools.googleapis.com"
target="_blank"
@ -361,13 +411,14 @@ export function SettingsPage() {
, enable the <strong>Gmail Postmaster Tools API</strong>.
</li>
<li>
Create OAuth 2.0 credentials (Web application). Add redirect URI:{' '}
Create OAuth 2.0 credentials (Web application). Add redirect
URI:{" "}
<code className="text-xs bg-white px-1 py-0.5 rounded">
http://localhost:3000/api/integrations/google/callback
</code>
</li>
<li>
Add your sending domain at{' '}
Add your sending domain at{" "}
<a
href="https://postmaster.google.com"
target="_blank"
@ -375,10 +426,12 @@ export function SettingsPage() {
className="text-primary-600 underline"
>
postmaster.google.com
</a>{' '}
</a>{" "}
and complete DNS verification there.
</li>
<li>Paste Client ID and Secret below, save, then click Connect.</li>
<li>
Paste Client ID and Secret below, save, then click Connect.
</li>
</ol>
</div>
@ -405,7 +458,11 @@ export function SettingsPage() {
<Button
type="button"
variant="outline"
disabled={!googleClientId || !googleClientSecret || saveGoogleCredsMutation.isPending}
disabled={
!googleClientId ||
!googleClientSecret ||
saveGoogleCredsMutation.isPending
}
onClick={() => saveGoogleCredsMutation.mutate()}
>
Save OAuth Credentials
@ -439,10 +496,10 @@ export function SettingsPage() {
<span className="text-slate-500">
{job.lastRunAt
? `${new Date(job.lastRunAt).toLocaleString()}${
job.success ? 'OK' : 'Failed'
job.success ? "OK" : "Failed"
}`
: 'Never run'}
{job.message ? ` (${job.message})` : ''}
: "Never run"}
{job.message ? ` (${job.message})` : ""}
</span>
</div>
))}
@ -469,5 +526,5 @@ export function SettingsPage() {
</CardContent>
</Card>
</div>
);
)
}