You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
132 lines
4.1 KiB
TypeScript
132 lines
4.1 KiB
TypeScript
"use client";
|
|
|
|
import { useActionState } from "react";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Field,
|
|
FieldError,
|
|
FieldGroup,
|
|
FieldLabel,
|
|
} from "@/components/ui/field";
|
|
import { Input } from "@/components/ui/input";
|
|
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 (
|
|
<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>
|
|
);
|
|
}
|