Files
webgis_geospesial/web-gis-login-page/components/login-form.tsx
T
2026-06-11 13:17:31 +07:00

207 lines
8.7 KiB
TypeScript

"use client"
import type React from "react"
import { useState } from "react"
import { Eye, EyeOff, Lock, User, Loader2, ArrowRight } from "lucide-react"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Checkbox } from "@/components/ui/checkbox"
import { cn } from "@/lib/utils"
export function LoginForm() {
const [showPassword, setShowPassword] = useState(false)
const [loading, setLoading] = useState(false)
const [username, setUsername] = useState("")
const [password, setPassword] = useState("")
function handleSubmit(e: React.FormEvent) {
e.preventDefault()
setLoading(true)
setTimeout(() => setLoading(false), 1600)
}
const demoAccounts = [
{ icon: "👑", role: "Admin", user: "admin", pass: "password" },
{ icon: "📝", role: "Petugas", user: "petugas", pass: "password" },
{ icon: "👁️", role: "Guest", user: "guest", pass: "password" },
]
function fillDemo(user: string, pass: string) {
setUsername(user)
setPassword(pass)
}
return (
<div className="w-full max-w-md">
{/* Glassmorphism card */}
<div className="relative rounded-3xl border border-white/60 bg-white/70 p-8 shadow-[0_40px_120px_-30px_rgba(198,40,40,0.28),0_18px_50px_-20px_rgba(198,40,40,0.14)] backdrop-blur-xl sm:p-10">
{/* subtle red glow accent */}
<div
aria-hidden
className="pointer-events-none absolute -right-6 -top-6 h-24 w-24 rounded-full bg-primary/15 blur-2xl"
/>
{/* Brand */}
<div className="mb-8 flex items-center gap-3">
<div className="flex h-11 w-11 items-center justify-center rounded-2xl bg-primary text-primary-foreground shadow-lg shadow-primary/25">
<svg
viewBox="0 0 24 24"
fill="none"
className="h-6 w-6"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
<path d="M12 21s-7-5.6-7-11a7 7 0 1 1 14 0c0 5.4-7 11-7 11Z" />
<circle cx="12" cy="10" r="2.5" />
</svg>
</div>
<div>
<p className="font-heading text-base font-bold leading-none tracking-tight text-foreground">
WebGIS Geospasial
</p>
<p className="mt-1 text-xs font-medium text-muted-foreground">Sistem Analisis Spasial</p>
</div>
</div>
<div className="mb-7">
<h2 className="font-heading text-2xl font-bold tracking-tight text-foreground text-balance">
Selamat Datang Kembali
</h2>
<p className="mt-2 text-sm leading-relaxed text-muted-foreground text-pretty">
Masuk ke akun Anda untuk mengakses peta dan data analisis spasial.
</p>
</div>
<form onSubmit={handleSubmit} className="flex flex-col gap-5">
{/* Username */}
<div className="flex flex-col gap-2">
<Label htmlFor="username" className="text-sm font-semibold text-foreground">
Nama Pengguna
</Label>
<div className="group relative">
<User
aria-hidden
className="pointer-events-none absolute left-3.5 top-1/2 h-[18px] w-[18px] -translate-y-1/2 text-muted-foreground transition-colors group-focus-within:text-primary"
/>
<Input
id="username"
type="text"
required
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="Masukkan nama pengguna"
className="h-12 rounded-xl border-border/80 bg-white/80 pl-11 text-sm shadow-sm transition-all hover:border-primary/60 focus-visible:border-primary focus-visible:ring-[3px] focus-visible:ring-primary/20"
/>
</div>
</div>
{/* Password */}
<div className="flex flex-col gap-2">
<Label htmlFor="password" className="text-sm font-semibold text-foreground">
Kata Sandi
</Label>
<div className="group relative">
<Lock
aria-hidden
className="pointer-events-none absolute left-3.5 top-1/2 h-[18px] w-[18px] -translate-y-1/2 text-muted-foreground transition-colors group-focus-within:text-primary"
/>
<Input
id="password"
type={showPassword ? "text" : "password"}
required
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Masukkan kata sandi"
className="h-12 rounded-xl border-border/80 bg-white/80 pl-11 pr-11 text-sm shadow-sm transition-all hover:border-primary/60 focus-visible:border-primary focus-visible:ring-[3px] focus-visible:ring-primary/20"
/>
<button
type="button"
onClick={() => setShowPassword((s) => !s)}
aria-label={showPassword ? "Sembunyikan kata sandi" : "Tampilkan kata sandi"}
className="absolute right-3 top-1/2 -translate-y-1/2 rounded-md p-1 text-muted-foreground transition-colors hover:text-primary"
>
{showPassword ? <EyeOff className="h-[18px] w-[18px]" /> : <Eye className="h-[18px] w-[18px]" />}
</button>
</div>
</div>
{/* Remember + forgot */}
<div className="flex items-center justify-between">
<label htmlFor="remember" className="flex cursor-pointer items-center gap-2 select-none">
<Checkbox
id="remember"
className="data-[state=checked]:border-primary data-[state=checked]:bg-primary"
/>
<span className="text-sm font-medium text-muted-foreground">Ingat saya</span>
</label>
<a
href="#"
className="text-sm font-semibold text-primary transition-colors hover:text-primary/80"
>
Lupa sandi?
</a>
</div>
{/* Submit */}
<button
type="submit"
disabled={loading}
className={cn(
"group mt-1 flex h-12 items-center justify-center gap-2 rounded-xl bg-primary text-sm font-semibold text-primary-foreground",
"shadow-lg shadow-primary/25 transition-all duration-300",
"hover:-translate-y-0.5 hover:bg-[oklch(0.46_0.21_27)] hover:shadow-xl hover:shadow-primary/35",
"active:translate-y-0 disabled:cursor-not-allowed disabled:opacity-80",
)}
>
{loading ? (
<>
<Loader2 className="h-4 w-4 animate-spin" />
Memproses...
</>
) : (
<>
Masuk
<ArrowRight className="h-4 w-4 transition-transform duration-300 group-hover:translate-x-1" />
</>
)}
</button>
</form>
{/* Demo accounts helper */}
<div className="mt-7 rounded-xl border border-border/70 bg-gray-50 p-4">
<div className="mb-3 flex items-center gap-2">
<span className="h-1.5 w-1.5 rounded-full bg-primary" />
<p className="text-xs font-bold uppercase tracking-wide text-foreground">Info Akun Demo</p>
<span className="text-[11px] font-medium text-muted-foreground">· klik untuk mengisi</span>
</div>
<div className="flex flex-col gap-1.5">
{demoAccounts.map((acc) => (
<button
key={acc.role}
type="button"
onClick={() => fillDemo(acc.user, acc.pass)}
className="group flex cursor-pointer items-center justify-between gap-3 rounded-lg border border-transparent bg-white/60 px-3 py-2 text-left transition-all hover:border-primary/30 hover:bg-white hover:shadow-sm"
>
<span className="flex items-center gap-2.5">
<span aria-hidden className="text-sm leading-none">{acc.icon}</span>
<span className="text-xs font-semibold text-foreground">{acc.role}</span>
</span>
<span className="font-mono text-[11px] text-muted-foreground transition-colors group-hover:text-primary">
{acc.user} · {acc.pass}
</span>
</button>
))}
</div>
</div>
</div>
<p className="mt-6 text-center text-xs text-muted-foreground">
© {new Date().getFullYear()} WebGIS Geospasial · Kementerian Analisis Spasial
</p>
</div>
)
}