UI login page revamp

main
Deardo Satria 2 days ago
parent 3ffc94054b
commit a59eb3aef5

@ -2,13 +2,6 @@
import { useActionState } from "react";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import {
Field,
FieldError,
@ -20,45 +13,119 @@ import { login, type LoginState } from "../../actions";
const initialState: LoginState = {};
const fieldInputClass =
"h-9 rounded-none border-x-0 border-t-0 border-b border-dashed border-input bg-transparent px-0 font-mono text-[15px] text-foreground focus-visible:border-foreground focus-visible:ring-0 md:text-[15px] dark:bg-transparent";
const fieldLabelClass =
"font-mono text-[10px] font-normal tracking-[0.18em] text-muted-foreground uppercase";
export function LoginForm() {
const [state, formAction, pending] = useActionState(login, initialState);
return (
<Card>
<CardHeader>
<CardTitle>Masuk</CardTitle>
<CardDescription>Cleo Minimarket back-office</CardDescription>
</CardHeader>
<CardContent>
<form action={formAction}>
<FieldGroup>
<Field>
<FieldLabel htmlFor="username">Username</FieldLabel>
<Input
id="username"
name="username"
autoComplete="username"
defaultValue={state.username}
required
/>
</Field>
<Field>
<FieldLabel htmlFor="password">Password</FieldLabel>
<Input
id="password"
name="password"
type="password"
autoComplete="current-password"
required
/>
</Field>
{state.error && <FieldError>{state.error}</FieldError>}
<Button type="submit" disabled={pending}>
{pending ? "Memproses..." : "Masuk"}
</Button>
</FieldGroup>
</form>
</CardContent>
</Card>
<div className="animate-in fade-in slide-in-from-bottom-2 duration-500 ease-out motion-reduce:animate-none">
<div className="drop-shadow-[0_20px_32px_rgba(29,28,23,0.16)] dark:drop-shadow-[0_20px_32px_rgba(0,0,0,0.6)]">
<section className="bg-card px-7 pt-7 pb-6 text-card-foreground">
<p className="text-center text-[10px] font-medium tracking-[0.3em] text-muted-foreground uppercase">
Cleo Minimarket
</p>
<div
aria-hidden="true"
className="mt-4 border-t border-dashed border-border"
/>
<h1 className="mt-6 text-[28px] leading-none font-semibold tracking-[-0.04em]">
Masuk
</h1>
<span
aria-hidden="true"
className="mt-2 block h-1.5 w-14 -rotate-2 bg-[#FFD34D]"
/>
<p className="mt-4 text-[13px] leading-relaxed text-muted-foreground">
Gunakan akun karyawan toko untuk melanjutkan.
</p>
<form action={formAction} className="mt-7">
<FieldGroup className="gap-5">
<Field className="gap-1.5">
<FieldLabel htmlFor="username" className={fieldLabelClass}>
Username
</FieldLabel>
<Input
id="username"
name="username"
autoComplete="username"
defaultValue={state.username}
required
className={fieldInputClass}
/>
</Field>
<Field className="gap-1.5">
<FieldLabel htmlFor="password" className={fieldLabelClass}>
Password
</FieldLabel>
<Input
id="password"
name="password"
type="password"
autoComplete="current-password"
required
className={fieldInputClass}
/>
</Field>
{state.error && (
<FieldError className="border border-dashed border-destructive/40 bg-destructive/5 px-3 py-2 text-[12px] leading-relaxed">
{state.error}
</FieldError>
)}
<Button
type="submit"
disabled={pending}
className="h-11 w-full rounded-none text-[11px] tracking-[0.24em] uppercase"
>
{pending ? "Memproses..." : "Masuk"}
</Button>
</FieldGroup>
</form>
<div
aria-hidden="true"
className="mt-7 border-t border-dashed border-border"
/>
<p className="mt-4 text-center text-[10px] leading-relaxed text-muted-foreground">
Butuh akses? Hubungi petugas administrasi.
</p>
</section>
<ReceiptEdge />
</div>
</div>
);
}
function ReceiptEdge() {
const width = 320;
const depth = 9;
const tooth = 20;
let d = `M0 0H${width}`;
for (let x = width; x > 0; x -= tooth) {
d += `L${x - tooth / 2} ${depth}L${x - tooth} 0`;
}
return (
<svg
viewBox={`0 0 ${width} ${depth}`}
preserveAspectRatio="none"
aria-hidden="true"
className="block h-[9px] w-full fill-card"
>
<path d={`${d}Z`} />
</svg>
);
}

@ -5,13 +5,13 @@ import { redirectIfLoggedIn } from "@/data/auth";
import { LoginForm } from "./_components/login-form";
export const metadata: Metadata = {
title: "Masuk",
title: "Masuk · Cleo Minimarket",
};
export default function LoginPage() {
return (
<main className="flex flex-1 items-center justify-center p-4">
<div className="w-full max-w-sm">
<main className="flex flex-1 items-center justify-center bg-[#E8E7E1] px-4 py-8 sm:py-12 dark:bg-[#0B0C0B]">
<div className="w-full max-w-[22rem]">
<Suspense fallback={null}>
<LoginContent />
</Suspense>

Loading…
Cancel
Save