- Remove all custom styling patterns and utilities (glow, gradients, custom colors) - Fix Tailwind v4 CSS variable configuration with proper @theme directive - Replace custom OKLCH colors with standard shadcn neutral theme - Remove complex button styling with glow effects and gradients - Standardize error colors to use semantic destructive tokens - Remove custom background gradients and complex header styling - Clean up globals.css removing custom utilities and animations - Ensure proper dark/light mode theme switching functionality ✅ All components now use standard shadcn/Tailwind patterns ✅ Dark/light mode works correctly ✅ No custom styling or inline classes ✅ Passes lint and typecheck 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
148 lines
3.8 KiB
TypeScript
148 lines
3.8 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
type DiagnosticsResponse = {
|
|
timestamp: string;
|
|
env: {
|
|
POSTGRES_URL: boolean;
|
|
BETTER_AUTH_SECRET: boolean;
|
|
GOOGLE_CLIENT_ID: boolean;
|
|
GOOGLE_CLIENT_SECRET: boolean;
|
|
OPENAI_API_KEY: boolean;
|
|
NEXT_PUBLIC_APP_URL: boolean;
|
|
};
|
|
database: {
|
|
connected: boolean;
|
|
schemaApplied: boolean;
|
|
error?: string;
|
|
};
|
|
auth: {
|
|
configured: boolean;
|
|
routeResponding: boolean | null;
|
|
};
|
|
ai: {
|
|
configured: boolean;
|
|
};
|
|
overallStatus: "ok" | "warn" | "error";
|
|
};
|
|
|
|
function StatusIcon({ ok }: { ok: boolean }) {
|
|
return ok ? (
|
|
<span aria-label="ok" title="ok">
|
|
✅
|
|
</span>
|
|
) : (
|
|
<span aria-label="not-ok" title="not ok">
|
|
❌
|
|
</span>
|
|
);
|
|
}
|
|
|
|
export function SetupChecklist() {
|
|
const [data, setData] = useState<DiagnosticsResponse | null>(null);
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
async function load() {
|
|
setLoading(true);
|
|
setError(null);
|
|
try {
|
|
const res = await fetch("/api/diagnostics", { cache: "no-store" });
|
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
const json = (await res.json()) as DiagnosticsResponse;
|
|
setData(json);
|
|
} catch (e) {
|
|
setError(e instanceof Error ? e.message : "Failed to load diagnostics");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
load();
|
|
}, []);
|
|
|
|
const steps = [
|
|
{
|
|
key: "env",
|
|
label: "Environment variables",
|
|
ok:
|
|
!!data?.env.POSTGRES_URL &&
|
|
!!data?.env.BETTER_AUTH_SECRET &&
|
|
!!data?.env.GOOGLE_CLIENT_ID &&
|
|
!!data?.env.GOOGLE_CLIENT_SECRET,
|
|
detail:
|
|
"Requires POSTGRES_URL, BETTER_AUTH_SECRET, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET",
|
|
},
|
|
{
|
|
key: "db",
|
|
label: "Database connected & schema",
|
|
ok: !!data?.database.connected && !!data?.database.schemaApplied,
|
|
detail: data?.database.error
|
|
? `Error: ${data.database.error}`
|
|
: undefined,
|
|
},
|
|
{
|
|
key: "auth",
|
|
label: "Auth configured",
|
|
ok: !!data?.auth.configured,
|
|
detail:
|
|
data?.auth.routeResponding === false
|
|
? "Auth route not responding"
|
|
: undefined,
|
|
},
|
|
{
|
|
key: "ai",
|
|
label: "AI integration (optional)",
|
|
ok: !!data?.ai.configured,
|
|
detail: !data?.ai.configured
|
|
? "Set OPENAI_API_KEY for AI chat"
|
|
: undefined,
|
|
},
|
|
] as const;
|
|
|
|
const completed = steps.filter((s) => s.ok).length;
|
|
|
|
return (
|
|
<div className="p-6 border rounded-lg text-left">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<div>
|
|
<h3 className="font-semibold">Setup checklist</h3>
|
|
<p className="text-sm text-muted-foreground">
|
|
{completed}/{steps.length} completed
|
|
</p>
|
|
</div>
|
|
<Button size="sm" onClick={load} disabled={loading}>
|
|
{loading ? "Checking..." : "Re-check"}
|
|
</Button>
|
|
</div>
|
|
|
|
{error ? <div className="text-sm text-destructive">{error}</div> : null}
|
|
|
|
<ul className="space-y-2">
|
|
{steps.map((s) => (
|
|
<li key={s.key} className="flex items-start gap-2">
|
|
<div className="mt-0.5">
|
|
<StatusIcon ok={Boolean(s.ok)} />
|
|
</div>
|
|
<div>
|
|
<div className="font-medium">{s.label}</div>
|
|
{s.detail ? (
|
|
<div className="text-sm text-muted-foreground">{s.detail}</div>
|
|
) : null}
|
|
</div>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
{data ? (
|
|
<div className="mt-4 text-xs text-muted-foreground">
|
|
Last checked: {new Date(data.timestamp).toLocaleString()}
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|