chore: initial project import
Some checks failed
CI - Production Readiness / Verify (push) Has been cancelled
Some checks failed
CI - Production Readiness / Verify (push) Has been cancelled
This commit is contained in:
121
app/login/page.tsx
Normal file
121
app/login/page.tsx
Normal file
@ -0,0 +1,121 @@
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
|
||||
import { Button } from "@/components/ui";
|
||||
import { getLocale, getTranslator } from "@/lib/i18n";
|
||||
import { getSession } from "@/lib/auth";
|
||||
|
||||
export default async function LoginPage({
|
||||
searchParams
|
||||
}: {
|
||||
searchParams?: Promise<{ error?: string; next?: string }>;
|
||||
}) {
|
||||
const locale = await getLocale();
|
||||
const t = getTranslator(locale);
|
||||
|
||||
const params = await (searchParams ?? Promise.resolve({ error: undefined, next: undefined }));
|
||||
const error = params?.error;
|
||||
const next = params?.next ?? "";
|
||||
const session = await getSession();
|
||||
|
||||
const errorMessage = error === "credentials_required"
|
||||
? t("login", "error_credentials_required")
|
||||
: error === "invalid_credentials"
|
||||
? t("login", "error_invalid_credentials")
|
||||
: error === "rate_limited"
|
||||
? t("login", "error_rate_limited")
|
||||
: null;
|
||||
|
||||
return (
|
||||
<main className="flex min-h-screen items-center justify-center bg-background px-6 py-14">
|
||||
<div className="grid w-full max-w-5xl overflow-hidden rounded-[2rem] bg-surface-container-lowest shadow-floating">
|
||||
<section className="bg-surface-container-lowest border-b border-line px-10 py-16 text-center md:border-r md:border-b-0 md:px-14 md:py-20">
|
||||
<Image
|
||||
src="/logo_zappcare.png"
|
||||
alt="ZappCare"
|
||||
width={56}
|
||||
height={56}
|
||||
className="mx-auto h-14 w-auto rounded-full"
|
||||
priority
|
||||
/>
|
||||
<h1 className="mt-8 text-4xl font-extrabold font-headline text-on-surface">{t("login", "title")}</h1>
|
||||
<p className="mx-auto mt-3 max-w-sm text-sm text-on-surface-variant">
|
||||
{t("login", "signin_subtitle")}
|
||||
</p>
|
||||
<div className="mx-auto mt-10 flex w-full max-w-sm flex-col gap-3">
|
||||
<div className="rounded-[1.5rem] bg-surface-container-low p-4">
|
||||
<p className="text-2xl font-black text-on-surface">3</p>
|
||||
<p className="mt-1 text-sm text-on-surface-variant">Role aktif saat ini</p>
|
||||
</div>
|
||||
<div className="rounded-[1.5rem] bg-surface-container-low p-4">
|
||||
<p className="text-2xl font-black text-on-surface">10+</p>
|
||||
<p className="mt-1 text-sm text-on-surface-variant">Modul operasi aktif</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="px-8 py-10 md:px-12 md:py-16">
|
||||
<div className="mx-auto max-w-md">
|
||||
<p className="text-sm font-black uppercase tracking-[0.22em] text-primary">{t("login", "signin_label")}</p>
|
||||
<h2 className="mt-3 text-3xl font-black font-headline text-on-surface">{t("login", "signin_subtitle")}</h2>
|
||||
<p className="mt-3 text-sm text-on-surface-variant">{t("login", "signin_help")}</p>
|
||||
{session ? (
|
||||
<p className="mt-4 rounded-[1rem] border border-outline-variant bg-surface-container-high p-4 text-sm text-on-surface-variant">
|
||||
Session aktif: {session.fullName} • {session.role} • {session.tenantName}
|
||||
</p>
|
||||
) : null}
|
||||
{errorMessage ? (
|
||||
<p className="mt-4 rounded-[1rem] border border-error-container bg-error-container p-3 text-sm text-on-error-container">
|
||||
{errorMessage}
|
||||
</p>
|
||||
) : null}
|
||||
<form action="/auth/login" method="post" className="mt-8 space-y-4">
|
||||
<input type="hidden" name="next" value={next} />
|
||||
<label className="block text-sm text-on-surface-variant">
|
||||
{t("login", "email_label")}
|
||||
<div className="mt-1.5">
|
||||
<input
|
||||
name="email"
|
||||
autoComplete="email"
|
||||
required
|
||||
className="h-12 w-full rounded-full border-none bg-surface-container-highest px-4 text-sm outline-none ring-1 ring-outline/40 focus:ring-2 focus:ring-primary"
|
||||
placeholder={t("login", "work_email_placeholder")}
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
<label className="block text-sm text-on-surface-variant">
|
||||
{t("login", "password_label")}
|
||||
<div className="mt-1.5 relative">
|
||||
<input
|
||||
name="password"
|
||||
type="password"
|
||||
autoComplete="current-password"
|
||||
required
|
||||
className="h-12 w-full rounded-full border-none bg-surface-container-highest px-4 text-sm outline-none ring-1 ring-outline/40 focus:ring-2 focus:ring-primary"
|
||||
placeholder={t("login", "password_placeholder")}
|
||||
/>
|
||||
<span className="material-symbols-outlined absolute right-4 top-1/2 -translate-y-1/2 text-outline">visibility</span>
|
||||
</div>
|
||||
</label>
|
||||
<Button className="w-full">{t("login", "sign_in_button")}</Button>
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex h-12 w-full items-center justify-center gap-2 rounded-xl border border-line bg-surface-container-low px-4 py-2 text-sm font-semibold text-on-surface transition hover:bg-surface-container-high"
|
||||
>
|
||||
<span className="material-symbols-outlined text-[20px]">fingerprint</span>
|
||||
<span>{t("login", "sso_button")}</span>
|
||||
</button>
|
||||
<p className="text-center text-sm text-on-surface-variant">
|
||||
{t("login", "no_account_label")} <Link href="/" className="font-bold text-primary hover:text-on-primary-container">{t("login", "contact_admin")}</Link>
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-4 text-sm text-on-surface-variant">
|
||||
<Link href="/forgot-password" className="font-semibold text-primary hover:text-on-primary-container">
|
||||
{t("login", "remember_label")}
|
||||
</Link>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user