Files
temetro/frontend/components/auth/auth-ui.tsx
T
Claude 448159873e Use shadcn login-01 / signup-03 card blocks (no animation)
Add the shadcn login-01 and signup-03 blocks (with the base-luma field/label
components) and wire their forms to the Better Auth client: login →
signIn.email, signup → signUp.email (12-char rule, confirm match), with error
+ submitting states. Dropped the non-functional "Login with Google" button.

Render the block forms from the existing (auth)/login and (auth)/signup pages
(deleting the blocks' duplicate app/login + app/signup routes). Replace the
animated AuthShell with a static, card-based shell + brand header so
onboarding/verify/reset/accept-invite match. Drop the unsupported `eslint`
key from next.config.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 22:22:58 +03:00

113 lines
2.4 KiB
TypeScript

import Image from "next/image";
import type { ReactNode } from "react";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { cn } from "@/lib/utils";
// temetro logo + wordmark, centered.
export function AuthBrand() {
return (
<div className="flex items-center justify-center gap-2.5">
<Image
alt="temetro"
className="size-8"
height={32}
priority
src="/temetro-logo.png"
width={32}
/>
<span className="text-lg font-semibold tracking-tight">temetro</span>
</div>
);
}
// Centered page wrapper for every auth screen: brand on top, content below.
export function AuthLayout({ children }: { children: ReactNode }) {
return (
<div className="flex min-h-full w-full items-center justify-center px-4 py-12">
<div className="flex w-full max-w-sm flex-col gap-6">
<AuthBrand />
{children}
</div>
</div>
);
}
// Card-based shell for the non-block auth pages (onboarding, verify, reset,
// forgot-password, accept-invite).
export function AuthShell({
title,
subtitle,
children,
footer,
}: {
title: string;
subtitle?: ReactNode;
children: ReactNode;
footer?: ReactNode;
}) {
return (
<AuthLayout>
<Card>
<CardHeader className="text-center">
<CardTitle className="text-xl">{title}</CardTitle>
{subtitle && <CardDescription>{subtitle}</CardDescription>}
</CardHeader>
<CardContent>{children}</CardContent>
</Card>
{footer && (
<div className="text-center text-sm text-muted-foreground">{footer}</div>
)}
</AuthLayout>
);
}
export function Field({
label,
htmlFor,
hint,
children,
}: {
label: string;
htmlFor: string;
hint?: ReactNode;
children: ReactNode;
}) {
return (
<div className="flex flex-col gap-1.5">
<label className="text-sm font-medium text-foreground" htmlFor={htmlFor}>
{label}
</label>
{children}
{hint && <p className="text-xs text-muted-foreground">{hint}</p>}
</div>
);
}
export function FormAlert({
tone = "error",
children,
}: {
tone?: "error" | "success";
children: ReactNode;
}) {
return (
<p
className={cn(
"rounded-2xl px-3 py-2 text-sm",
tone === "error"
? "bg-destructive/10 text-destructive"
: "bg-primary/10 text-primary"
)}
>
{children}
</p>
);
}