Skip to content

Commit 44f1e3f

Browse files
committed
feat: improved ui
1 parent 93cb130 commit 44f1e3f

14 files changed

Lines changed: 982 additions & 1350 deletions

File tree

backend/migrations/20260401000002_add_sandbox_to_payments.js

Whitespace-only changes.

frontend/src/app/(authenticated)/api-keys/page.tsx

Lines changed: 47 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,25 @@ import { toast } from "sonner";
88
export default function ApiKeysPage() {
99
const storedApiKey = useMerchantApiKey();
1010
const [isRotating, setIsRotating] = useState(false);
11+
const [revealed, setRevealed] = useState(false);
1112

12-
const handleRotate = async () => {
13-
if (!confirm("Are you sure you want to rotate your API key? The old one will be invalidated immediately.")) {
14-
return;
15-
}
13+
const displayKey = storedApiKey
14+
? revealed
15+
? storedApiKey
16+
: storedApiKey.slice(0, 7) + "•".repeat(storedApiKey.length - 13) + storedApiKey.slice(-6)
17+
: "sk_••••••••••••••••••••••••";
1618

19+
const handleRotate = async () => {
20+
if (!confirm("Rotate your API key? The old one will be invalidated immediately.")) return;
1721
setIsRotating(true);
1822
try {
1923
const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:4000";
2024
const res = await fetch(`${apiUrl}/api/rotate-key`, {
2125
method: "POST",
2226
headers: { "x-api-key": storedApiKey || "" },
2327
});
24-
2528
if (!res.ok) throw new Error("Rotation failed");
26-
27-
toast.success("API key rotated successfully. Please update your env files.");
28-
// Note: The store will handle the update if it's wired with cookies or session
29-
// For now, we tell the user to refresh or rely on the store's hydration.
29+
toast.success("API key rotated. Update your integrations.");
3030
} catch {
3131
toast.error("Failed to rotate API key");
3232
} finally {
@@ -35,45 +35,55 @@ export default function ApiKeysPage() {
3535
};
3636

3737
return (
38-
<div className="flex flex-col gap-8">
38+
<div className="flex flex-col gap-8 animate-in fade-in duration-500">
3939
<div>
40-
<h1 className="text-3xl font-bold text-white">API Keys</h1>
41-
<p className="mt-2 text-slate-400">
40+
<p className="text-[10px] font-bold uppercase tracking-[0.4em] text-[#6B6B6B] mb-2">Security</p>
41+
<h1 className="text-4xl font-bold text-[#0A0A0A] tracking-tight">API Keys</h1>
42+
<p className="mt-2 text-sm font-medium text-[#6B6B6B]">
4243
Manage your secret keys to authenticate server-side requests.
4344
</p>
4445
</div>
4546

46-
<div className="rounded-2xl border border-white/10 bg-white/5 p-8">
47-
<div className="flex flex-col gap-6">
48-
<div className="flex flex-col gap-2">
49-
<label className="text-xs font-medium uppercase tracking-wider text-slate-500">
47+
<div className="max-w-xl rounded-2xl border border-[#E8E8E8] bg-white p-8 flex flex-col gap-6">
48+
<div className="flex flex-col gap-2">
49+
<div className="flex items-center justify-between">
50+
<label className="text-[10px] font-bold uppercase tracking-widest text-[#6B6B6B]">
5051
Live API Key
5152
</label>
52-
<div className="flex items-center gap-3">
53-
<code className="flex-1 rounded-xl border border-white/10 bg-black/40 px-4 py-3 text-sm text-mint">
54-
{storedApiKey || "sk_test_••••••••••••••••••••••••"}
55-
</code>
56-
<CopyButton text={storedApiKey || ""} />
57-
</div>
58-
</div>
59-
60-
<div className="rounded-xl border border-yellow-500/20 bg-yellow-500/5 p-4 text-sm text-yellow-200/80">
61-
<p className="font-semibold">Security Warning</p>
62-
<p className="mt-1">
63-
Never share your secret API keys in publicly accessible areas
64-
like GitHub, client-side code, or public forums.
65-
</p>
66-
</div>
67-
68-
<div className="flex justify-end">
6953
<button
70-
onClick={handleRotate}
71-
disabled={isRotating}
72-
className="rounded-xl border border-red-500/30 bg-red-500/10 px-6 py-2.5 text-sm font-semibold text-red-400 transition hover:bg-red-500/20 disabled:opacity-50"
54+
type="button"
55+
onClick={() => setRevealed(v => !v)}
56+
className="text-[10px] font-bold uppercase tracking-widest text-[#6B6B6B] hover:text-[#0A0A0A] transition-colors"
7357
>
74-
{isRotating ? "Rotating..." : "Rotate API Key"}
58+
{revealed ? "Hide" : "Reveal"}
7559
</button>
7660
</div>
61+
<div className="flex items-center gap-2 overflow-hidden rounded-xl border border-[#E8E8E8] bg-[#F9F9F9] p-1 pl-4">
62+
<code className={`flex-1 truncate text-sm font-bold tracking-widest transition-colors ${revealed ? "text-[#0A0A0A]" : "text-[#E8E8E8]"}`}>
63+
{displayKey}
64+
</code>
65+
{revealed && storedApiKey && <CopyButton text={storedApiKey} />}
66+
</div>
67+
<p className="text-[10px] font-medium text-[#6B6B6B]">
68+
Pass this as the <code className="text-[#0A0A0A]">x-api-key</code> header on every API request.
69+
</p>
70+
</div>
71+
72+
<div className="rounded-xl border border-yellow-200 bg-yellow-50 p-4 text-sm text-yellow-800">
73+
<p className="font-bold mb-1">Security Warning</p>
74+
<p className="text-yellow-700 text-xs leading-relaxed">
75+
Never share your secret API keys in publicly accessible areas like GitHub, client-side code, or public forums.
76+
</p>
77+
</div>
78+
79+
<div className="flex justify-end">
80+
<button
81+
onClick={handleRotate}
82+
disabled={isRotating}
83+
className="rounded-xl border border-red-200 bg-red-50 px-6 py-2.5 text-sm font-bold text-red-600 transition hover:bg-red-100 disabled:opacity-50"
84+
>
85+
{isRotating ? "Rotating..." : "Rotate API Key"}
86+
</button>
7787
</div>
7888
</div>
7989
</div>

0 commit comments

Comments
 (0)