SPB Git forge
1commits 1branches 0releases
189.0 KBsize
maindefault branch
6 h agolast push
Python 54.7% TypeScript 42.5% SQL 1.9%
2.1 KB · 56 lines tsx
Raw Blame History
1"use client";2import { Gauge } from "lucide-react";3import { useRouter, useSearchParams } from "next/navigation";4import { Suspense, useState } from "react";5import { api, ApiError } from "@/lib/api";6import { Button, Field, Input } from "@/components/ui";78function LoginForm() {9  const router = useRouter();10  const params = useSearchParams();11  const [pw, setPw] = useState("");12  const [err, setErr] = useState<string | null>(null);13  const [busy, setBusy] = useState(false);14  async function submit(e: React.FormEvent) {15    e.preventDefault();16    setBusy(true);17    setErr(null);18    try {19      await api("/api/auth/login", { method: "POST", json: { password: pw } });20      const next = params.get("next");21      router.replace(next && next.startsWith("/") ? next : "/");22    } catch (e) {23      setErr(e instanceof ApiError ? e.message : "login failed");24    } finally {25      setBusy(false);26    }27  }28  return (29    <form onSubmit={submit} className="w-full max-w-sm rounded-lg border border-line bg-bg-elev p-6">30      <div className="mb-5 flex items-center gap-2">31        <Gauge className="h-5 w-5 text-accent" aria-hidden />32        <div>33          <h1 className="text-base font-semibold">ktrade</h1>34          <p className="text-xs text-fg-muted">Kraken quantitative trading terminal — owner access</p>35        </div>36      </div>37      <Field label="Owner password" htmlFor="pw">38        <Input id="pw" type="password" autoComplete="current-password" value={pw} onChange={(e) => setPw(e.target.value)} required autoFocus />39      </Field>40      {err && <p role="alert" className="mt-3 text-xs text-down">{err}</p>}41      <Button type="submit" variant="primary" className="mt-4 w-full" loading={busy}>Sign in</Button>42      <p className="mt-4 text-[11px] text-fg-dim">Sessions are HttpOnly cookies signed server-side. Live trading stays disabled regardless of login.</p>43    </form>44  );45}4647export default function LoginPage() {48  return (49    <div className="flex min-h-screen items-center justify-center p-4">50      <Suspense>51        <LoginForm />52      </Suspense>53    </div>54  );55}56