TKK_E32230469/web_ups/src/components/Login.tsx

245 lines
9.7 KiB
TypeScript

"use client";
import React, { useState } from 'react';
import { useRouter } from 'next/navigation';
import { User, Lock, ArrowRight, Eye, EyeOff, Activity, ShieldCheck, Cloud } from 'lucide-react';
import styles from './Login.module.css';
export default function Login() {
const router = useRouter();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [showPassword, setShowPassword] = useState(false);
const [rememberMe, setRememberMe] = useState(false);
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (username.trim() === '' || password.trim() === '') return;
setLoading(true);
try {
const res = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
});
if (res.ok) {
router.push('/');
router.refresh();
} else {
setError(true);
setTimeout(() => setError(false), 2000);
setPassword('');
}
} catch (err) {
setError(true);
setTimeout(() => setError(false), 2000);
} finally {
setLoading(false);
}
};
return (
<div className={styles.container}>
{/* Kolom Kiri: Form Login Bahasa Indonesia Rapi */}
<div className={styles.leftColumn}>
<div /> {/* Spacer vertical alignments */}
<div className={styles.formContainer}>
<div className={styles.header}>
<h1 className={styles.title}>Selamat Datang</h1>
<p className={styles.subtitle}>
Masuk untuk memantau dan mengelola sistem UPS DC berbasis IoT Anda.
</p>
</div>
<form onSubmit={handleSubmit} className={styles.form}>
<div className={styles.fieldGroup}>
<label className={styles.label} htmlFor="username">Username</label>
<div className={styles.inputWrapper}>
<div className={styles.inputPrefix}>
<User size={18} />
</div>
<input
id="username"
type="text"
placeholder="Masukkan username"
className={`${styles.input} ${error ? styles.inputError : ''}`}
value={username}
onChange={(e) => {
setUsername(e.target.value);
setError(false);
}}
autoFocus
/>
</div>
</div>
<div className={styles.fieldGroup}>
<label className={styles.label} htmlFor="password">Password</label>
<div className={styles.inputWrapper}>
<div className={styles.inputPrefix}>
<Lock size={18} />
</div>
<input
id="password"
type={showPassword ? 'text' : 'password'}
placeholder="Masukkan password"
className={`${styles.input} ${error ? styles.inputError : ''}`}
value={password}
onChange={(e) => {
setPassword(e.target.value);
setError(false);
}}
/>
<button
type="button"
className={styles.inputSuffix}
onClick={() => setShowPassword(!showPassword)}
tabIndex={-1}
aria-label="Toggle password visibility"
>
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
</button>
</div>
</div>
{error && <span className={styles.errorMessage}>Username atau password salah</span>}
{/* Opsi Ingat Saya */}
<div className={styles.optionsRow}>
<label className={styles.rememberMe}>
<input
type="checkbox"
className={styles.checkbox}
checked={rememberMe}
onChange={(e) => setRememberMe(e.target.checked)}
/>
<span>Ingat saya</span>
</label>
</div>
{/* Tombol Masuk Solid (Tanpa Glow) */}
<button type="submit" className={styles.submitBtn} disabled={loading}>
<span>{loading ? 'Masuk...' : 'Masuk'}</span>
{!loading && <ArrowRight size={18} />}
</button>
</form>
</div>
<div /> {/* Bottom spacer for vertical alignment */}
</div>
{/* Kolom Kanan: Nuansa Biru Muda Lembut dengan Fitur Bahasa Indonesia */}
<div className={styles.rightColumn}>
<div className={styles.showcaseContainer}>
{/* Bagian Hero + Ilustrasi Tameng Konsentris di Kanan */}
<div className={styles.heroRow}>
<div className={styles.heroText}>
<span className={styles.subTag}>Sistem UPS DC</span>
<h2 className={styles.showcaseHeading}>
Monitoring <span>Cerdas.</span><br />
Perlindungan <span>Optimal.</span>
</h2>
<div className={styles.titleDivider} />
<p className={styles.showcaseDesc}>
Solusi pemantauan dan perlindungan daya DC secara real-time untuk memastikan sistem Anda selalu andal.
</p>
</div>
{/* Ilustrasi Lingkaran Konsentris Tameng Petir (Sesuai Referensi) */}
<div className={styles.shieldIllustration}>
<svg width="260" height="260" viewBox="0 0 280 280" fill="none" xmlns="http://www.w3.org/2000/svg">
{/* Lingkaran Luar Putus-Putus & Titik Orbit (Posisi Segitiga Emas: 11:00, 04:00, 07:00) */}
<g className={styles.orbitRing}>
<circle cx="140" cy="140" r="130" stroke="#bfdbfe" strokeWidth="1.5" strokeDasharray="5 8" />
<circle cx="75" cy="28" r="5" fill="#2563eb" /> {/* Pukul 11:00 */}
<circle cx="253" cy="205" r="5" fill="#3b82f6" /> {/* Pukul 04:00 */}
<circle cx="75" cy="252" r="4.5" fill="#60a5fa" /> {/* Pukul 07:00 */}
</g>
{/* Lingkaran Menengah & Dalam (Berdenyut Aktif/Pulse) */}
<circle cx="140" cy="140" r="100" stroke="#dbeafe" strokeWidth="1.5" />
<g className={styles.pulseGlow}>
<circle cx="140" cy="140" r="75" fill="#60a5fa" fillOpacity="0.18" stroke="#93c5fd" strokeWidth="2" />
</g>
<circle cx="140" cy="140" r="55" fill="#ffffff" fillOpacity="0.95" />
<circle cx="140" cy="140" r="55" stroke="#eff6ff" strokeWidth="6" />
{/* Ikon Shield & Petir di Tengah (Melayang Elegan) */}
<g className={styles.floatingShield}>
<path
d="M 140 105 L 175 118 C 175 145 162 168 140 177 C 118 168 105 145 105 118 L 140 105 Z"
stroke="#2563eb"
strokeWidth="3.5"
strokeLinecap="round"
strokeLinejoin="round"
fill="#eff6ff"
/>
<path
d="M 143 122 L 132 142 L 146 142 L 137 162 L 152 138 L 139 138 L 143 122 Z"
fill="#2563eb"
/>
</g>
</svg>
</div>
</div>
{/* Daftar 3 Poin Keunggulan (Boks Ikon Biru Muda Lembut #dbeafe) */}
<div className={styles.featureList}>
<div className={styles.featureItem}>
<div className={styles.featureIconBox}>
<Activity size={26} strokeWidth={2.2} />
</div>
<div className={styles.featureText}>
<span className={styles.featureTitle}>Pemantauan Real-time</span>
<span className={styles.featureDesc}>
Pantau tegangan, arus, dan status daya secara real-time dari mana saja.
</span>
</div>
</div>
<div className={styles.featureItem}>
<div className={styles.featureIconBox}>
<ShieldCheck size={26} strokeWidth={2.2} />
</div>
<div className={styles.featureText}>
<span className={styles.featureTitle}>Perlindungan Cerdas</span>
<span className={styles.featureDesc}>
Sistem proteksi otomatis terhadap over-voltage, over-discharge, dan kondisi abnormal lainnya.
</span>
</div>
</div>
<div className={styles.featureItem}>
<div className={styles.featureIconBox}>
<Cloud size={26} strokeWidth={2.2} />
</div>
<div className={styles.featureText}>
<span className={styles.featureTitle}>Akses Cloud IoT</span>
<span className={styles.featureDesc}>
Data tersimpan aman di cloud dan dapat diakses kapan saja untuk analisis dan histori.
</span>
</div>
</div>
</div>
</div>
{/* Dekorasi Dot Grid di Pojok Kiri/Kanan Bawah */}
<div className={styles.bgDots}>
<svg width="120" height="90" viewBox="0 0 120 90" fill="none" xmlns="http://www.w3.org/2000/svg">
{Array.from({ length: 5 }).map((_, r) =>
Array.from({ length: 7 }).map((_, c) => (
<circle key={`${r}-${c}`} cx={10 + c * 16} cy={10 + r * 16} r="2" fill="#93c5fd" />
))
)}
</svg>
</div>
</div>
</div>
);
}