import { writeFileSync, mkdirSync } from "fs"; // ─── Update settings/page.tsx to include 2FA link ──────────────────────────── writeFileSync("app/settings/page.tsx", `import Link from "next/link"; import { AppShell } from "../../components/app-shell"; const settingsItems = [ { title: "Profile", description: "Update company details, contact info, and onboarding fields.", href: "/settings/profile", }, { title: "Two-Factor Auth", description: "Add a TOTP authenticator app for extra security.", href: "/settings/2fa", }, { title: "Subscription", description: "View plan details, upgrade options, and billing cadence.", href: "/settings/subscription", }, ]; export default function SettingsPage() { return (
{settingsItems.map((item) => (

{item.title}

{item.description}

Open ))}
); } `); // ─── settings/2fa/page.tsx ─────────────────────────────────────────────────── mkdirSync("app/settings/2fa", { recursive: true }); writeFileSync("app/settings/2fa/page.tsx", `"use client"; import { useEffect, useState } from "react"; import { AppShell } from "../../../components/app-shell"; import { apiFetch } from "../../../lib/api"; type ApiResponse = { data: T; meta: { timestamp: string; version: "v1" }; error: null | { message: string; code?: string }; }; type TwoFaGenerateData = { qrCode: string; otpAuthUrl: string }; type UserData = { user: { twoFactorEnabled: boolean } }; export default function TwoFAPage() { const [enabled, setEnabled] = useState(null); const [qrCode, setQrCode] = useState(""); const [otpAuthUrl, setOtpAuthUrl] = useState(""); const [token, setToken] = useState(""); const [status, setStatus] = useState(""); const [isError, setIsError] = useState(false); const [step, setStep] = useState<"idle" | "scan" | "done">("idle"); useEffect(() => { apiFetch("/api/auth/me") .then((res) => { if (!res.error && res.data) { // me returns { user: {...} } const data = res.data as unknown as UserData; setEnabled(data.user?.twoFactorEnabled ?? false); } }) .catch(() => {}); }, []); const handleGenerate = async () => { setStatus("Generating QR code..."); setIsError(false); const res = await apiFetch("/api/2fa/generate", { method: "POST" }); if (res.error) { setStatus(res.error.message ?? "Failed to generate 2FA secret."); setIsError(true); return; } setQrCode(res.data.qrCode); setOtpAuthUrl(res.data.otpAuthUrl); setStep("scan"); setStatus("Scan the QR code with your authenticator app, then enter the code below."); }; const handleEnable = async (event: React.FormEvent) => { event.preventDefault(); if (!token || token.length !== 6) { setStatus("Please enter the 6-digit code."); setIsError(true); return; } setStatus("Verifying..."); setIsError(false); const res = await apiFetch<{ message: string }>("/api/2fa/enable", { method: "POST", body: JSON.stringify({ token }), }); if (res.error) { setStatus(res.error.message ?? "Verification failed. Try again."); setIsError(true); return; } setEnabled(true); setStep("done"); setStatus("Two-factor authentication is now active."); }; const handleDisable = async (event: React.FormEvent) => { event.preventDefault(); if (!token || token.length !== 6) { setStatus("Please enter the 6-digit code to confirm."); setIsError(true); return; } setStatus("Disabling 2FA..."); setIsError(false); const res = await apiFetch<{ message: string }>("/api/2fa/disable", { method: "DELETE", body: JSON.stringify({ token }), }); if (res.error) { setStatus(res.error.message ?? "Failed. Check your authenticator code."); setIsError(true); return; } setEnabled(false); setToken(""); setStep("idle"); setStatus("Two-factor authentication has been disabled."); }; return (
{enabled === null ? (
) : enabled ? ( <>

2FA is Active

Your account is protected with TOTP.

To disable two-factor authentication, enter the current code from your authenticator app.

setToken(e.target.value.replace(/\\D/g, ""))} className="block w-full rounded-lg border border-border bg-background/50 px-3 py-2 text-sm text-center tracking-widest focus:border-primary focus:outline-none focus:ring-primary transition-all" required />
) : step === "idle" ? ( <>

2FA Not Enabled

Add an extra layer of protection.

Use any TOTP authenticator app (Google Authenticator, Authy, 1Password) to generate login codes.

) : step === "scan" ? ( <>

Scan this QR code

Open your authenticator app and scan the code below, or enter the key manually.

{qrCode && (
{/* eslint-disable-next-line @next/next/no-img-element */} 2FA QR Code
)} {otpAuthUrl && (

{otpAuthUrl}

)}
setToken(e.target.value.replace(/\\D/g, ""))} className="block w-full rounded-lg border border-border bg-background/50 px-3 py-2 text-sm text-center tracking-widest focus:border-primary focus:outline-none focus:ring-primary transition-all" required autoFocus />
) : (

2FA Enabled Successfully

Your account now requires a code on each login.

)} {status && (
{status}
)}
); } `); // ─── settings/subscription/page.tsx ───────────────────────────────────────── mkdirSync("app/settings/subscription", { recursive: true }); writeFileSync("app/settings/subscription/page.tsx", `"use client"; import { useEffect, useState } from "react"; import { AppShell } from "../../../components/app-shell"; import { apiFetch } from "../../../lib/api"; type ApiResponse = { data: T; meta: { timestamp: string; version: "v1" }; error: null | { message: string; code?: string }; }; type SubscriptionData = { plan?: string; status?: string; billingCycleAnchor?: number; cancelAtPeriodEnd?: boolean; }; const PLAN_LABELS: Record = { free: "Free", pro: "Pro", elite: "Elite", }; const PLAN_DESCRIPTIONS: Record = { free: "Up to 2 accounts, basic CSV export, 30-day history.", pro: "Unlimited accounts, Google Sheets, 24-month history, priority support.", elite: "Everything in Pro + tax return module, AI rule suggestions, dedicated support.", }; export default function SubscriptionPage() { const [sub, setSub] = useState(null); const [loading, setLoading] = useState(true); const [actionStatus, setActionStatus] = useState(""); const [actionLoading, setActionLoading] = useState(false); useEffect(() => { apiFetch("/api/stripe/subscription") .then((res) => { if (!res.error) setSub(res.data); }) .catch(() => {}) .finally(() => setLoading(false)); }, []); const handleUpgrade = async (plan: string) => { setActionLoading(true); setActionStatus("Redirecting to checkout..."); const appUrl = typeof window !== "undefined" ? window.location.origin : ""; const res = await apiFetch<{ url: string }>("/api/stripe/checkout", { method: "POST", body: JSON.stringify({ plan, successUrl: \`\${appUrl}/settings/subscription?upgraded=1\`, cancelUrl: \`\${appUrl}/settings/subscription\`, }), }); setActionLoading(false); if (res.error || !res.data?.url) { setActionStatus(res.error?.message ?? "Could not start checkout. Check Stripe configuration."); return; } window.location.href = res.data.url; }; const handlePortal = async () => { setActionLoading(true); setActionStatus("Redirecting to billing portal..."); const appUrl = typeof window !== "undefined" ? window.location.origin : ""; const res = await apiFetch<{ url: string }>("/api/stripe/portal", { method: "POST", body: JSON.stringify({ returnUrl: \`\${appUrl}/settings/subscription\` }), }); setActionLoading(false); if (res.error || !res.data?.url) { setActionStatus(res.error?.message ?? "Could not open billing portal. Check Stripe configuration."); return; } window.location.href = res.data.url; }; const currentPlan = sub?.plan ?? "free"; const planLabel = PLAN_LABELS[currentPlan] ?? currentPlan; const planDesc = PLAN_DESCRIPTIONS[currentPlan] ?? ""; return (
{/* Current plan card */}

Current Plan

{loading ? (
) : ( <>
{planLabel} {sub?.status && sub.status !== "active" && sub.status !== "free" && ( {sub.status} )} {(sub?.status === "active" || currentPlan !== "free") && ( Active )}

{planDesc}

{sub?.cancelAtPeriodEnd && (

Cancels at end of billing period.

)} )} {!loading && currentPlan !== "free" && ( )}
{/* Upgrade options */} {currentPlan === "free" && (
{(["pro", "elite"] as const).map((plan) => (

{plan}

{PLAN_DESCRIPTIONS[plan]}

))}
)} {currentPlan === "pro" && (

Elite

{PLAN_DESCRIPTIONS.elite}

)} {actionStatus && (

{actionStatus}

)}
); } `); // ─── settings/profile/page.tsx (fix stale class names) ─────────────────────── mkdirSync("app/settings/profile", { recursive: true }); writeFileSync("app/settings/profile/page.tsx", `"use client"; import { useEffect, useState } from "react"; import { AppShell } from "../../../components/app-shell"; import { apiFetch } from "../../../lib/api"; type ProfileData = { user: { id: string; email: string; fullName?: string | null; phone?: string | null; companyName?: string | null; addressLine1?: string | null; addressLine2?: string | null; city?: string | null; state?: string | null; postalCode?: string | null; country?: string | null; }; }; export default function ProfilePage() { const [status, setStatus] = useState(""); const [isError, setIsError] = useState(false); const [fullName, setFullName] = useState(""); const [phone, setPhone] = useState(""); const [companyName, setCompanyName] = useState(""); const [addressLine1, setAddressLine1] = useState(""); const [addressLine2, setAddressLine2] = useState(""); const [city, setCity] = useState(""); const [state, setState] = useState(""); const [postalCode, setPostalCode] = useState(""); const [country, setCountry] = useState(""); useEffect(() => { apiFetch("/api/auth/me") .then((res) => { if (!res.error && res.data) { const u = (res.data as unknown as ProfileData).user; if (u) { setFullName(u.fullName ?? ""); setPhone(u.phone ?? ""); setCompanyName(u.companyName ?? ""); setAddressLine1(u.addressLine1 ?? ""); setAddressLine2(u.addressLine2 ?? ""); setCity(u.city ?? ""); setState(u.state ?? ""); setPostalCode(u.postalCode ?? ""); setCountry(u.country ?? ""); } } }) .catch(() => {}); }, []); const onSubmit = async (event: React.FormEvent) => { event.preventDefault(); setStatus("Saving profile..."); setIsError(false); const res = await apiFetch("/api/auth/profile", { method: "PATCH", body: JSON.stringify({ fullName: fullName || undefined, phone: phone || undefined, companyName: companyName || undefined, addressLine1: addressLine1 || undefined, addressLine2: addressLine2 || undefined, city: city || undefined, state: state || undefined, postalCode: postalCode || undefined, country: country || undefined, }), }); if (res.error) { setStatus(res.error.message ?? "Profile update failed."); setIsError(true); return; } setStatus("Profile saved successfully."); }; const inputCls = "w-full rounded-lg border border-border bg-background/50 px-3 py-2 text-sm focus:border-primary focus:outline-none focus:ring-primary transition-all"; const labelCls = "block text-xs font-medium text-muted-foreground mb-1 uppercase tracking-wide"; return (

Personal & Business Details

These details appear on tax exports and CSV reports.

setFullName(e.target.value)} required />
setPhone(e.target.value)} />
setCompanyName(e.target.value)} />
setAddressLine1(e.target.value)} />
setAddressLine2(e.target.value)} />
setCity(e.target.value)} />
setState(e.target.value)} />
setPostalCode(e.target.value)} />
setCountry(e.target.value)} />
{status && (
{status}
)}
); } `); console.log("✅ settings/page.tsx, settings/2fa/page.tsx, settings/subscription/page.tsx, settings/profile/page.tsx written");