"use client"; import Link from "next/link"; import { useRouter, useSearchParams } from "next/navigation"; import { Suspense, useState } from "react"; import { Background } from "../../components/background"; import { PageSchema } from "../../components/page-schema"; import { SiteFooter } from "../../components/site-footer"; import { SiteHeader } from "../../components/site-header"; import { defaultFaqs } from "../../data/faq"; import { siteInfo } from "../../data/site"; import { storeAuthTokens } from "@/lib/api"; type ApiResponse = { data: T; meta: { timestamp: string; version: "v1" }; error: null | { message: string; code?: string }; }; type AuthData = { user: { id: string; email: string; fullName?: string; emailVerified?: boolean }; accessToken: string; refreshToken: string; requiresTwoFactor?: boolean; }; const socialButtonClass = "flex w-full items-center justify-center gap-3 rounded-xl border border-border bg-white py-3 px-4 text-sm font-medium text-foreground shadow-sm transition-all hover:shadow-md hover:border-primary/30 focus:outline-none focus:ring-2 focus:ring-primary/20"; function LoginForm() { const router = useRouter(); const searchParams = useSearchParams(); const nextPath = searchParams.get("next") ?? "/app"; const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [staySignedIn, setStaySignedIn] = useState(true); const [totpToken, setTotpToken] = useState(""); const [requiresTwoFactor, setRequiresTwoFactor] = useState(false); const [status, setStatus] = useState(""); const [isError, setIsError] = useState(false); const onSubmit = async (event: React.FormEvent) => { event.preventDefault(); setStatus("Signing in..."); setIsError(false); try { const res = await fetch("/api/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email, password, ...(requiresTwoFactor ? { totpToken } : {}) }), }); const payload = (await res.json()) as ApiResponse; if (!res.ok || payload.error) { setStatus(payload.error?.message ?? "Login failed."); setIsError(true); return; } if (payload.data.requiresTwoFactor) { setRequiresTwoFactor(true); setStatus("Enter the code from your authenticator app."); return; } storeAuthTokens({ accessToken: payload.data.accessToken, refreshToken: payload.data.refreshToken, user: payload.data.user, }); setStatus(`Welcome back, ${payload.data.user.email}`); router.push(nextPath); } catch { setStatus("Login failed. Please try again."); setIsError(true); } }; return (
{/* Social sign in */}
or
setEmail(e.target.value)} className="block w-full appearance-none rounded-lg border border-border bg-background/50 px-3 py-2 text-foreground placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 sm:text-sm transition-all" />
setPassword(e.target.value)} className="block w-full appearance-none rounded-lg border border-border bg-background/50 px-3 py-2 text-foreground placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 sm:text-sm transition-all" />
setStaySignedIn(e.target.checked)} className="h-4 w-4 rounded border-border bg-white text-primary focus:ring-2 focus:ring-primary/20 focus:ring-offset-0" />
{requiresTwoFactor && (
setTotpToken(e.target.value.replace(/\D/g, ""))} className="block w-full appearance-none rounded-lg border border-border bg-background/50 px-3 py-2 text-foreground placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 sm:text-sm transition-all tracking-widest text-center" />
)}
Forgot your password?
{status && (

{status}

)} ); } export default function LoginPage() { const schema = [ { "@context": "https://schema.org", "@type": "WebPage", name: "LedgerOne Login", description: "Sign in to LedgerOne to access your audit-ready ledger.", url: `${siteInfo.url}/login`, }, { "@context": "https://schema.org", "@type": "FAQPage", mainEntity: defaultFaqs.map((item) => ({ "@type": "Question", name: item.question, acceptedAnswer: { "@type": "Answer", text: item.answer }, })), }, ]; return (
L1

Sign In

Start your 14-day free trial

); }