import { useState, useEffect } from 'react'; import { GoogleLogin, type CredentialResponse } from '@react-oauth/google'; import { useAuthStore } from '../../store/authStore'; /* Demo accounts (개발 모드 전용) */ const DEMO_ACCOUNTS = [{ id: 'admin', password: 'admin1234', label: '관리자 (경정)' }]; export function LoginPage() { const [userId, setUserId] = useState(''); const [password, setPassword] = useState(''); const [remember, setRemember] = useState(false); const { login, googleLogin, isLoading, error, pendingMessage, clearError } = useAuthStore(); const GOOGLE_ENABLED = !!import.meta.env.VITE_GOOGLE_CLIENT_ID; useEffect(() => { const saved = localStorage.getItem('wing_remember'); if (saved) { // eslint-disable-next-line react-hooks/set-state-in-effect setUserId(saved); setRemember(true); } }, []); const handleGoogleSuccess = async (response: CredentialResponse) => { if (response.credential) { clearError(); try { await googleLogin(response.credential); } catch { // 에러는 authStore에서 관리 } } }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); clearError(); if (!userId.trim() || !password.trim()) { return; } try { await login(userId.trim(), password); if (remember) { localStorage.setItem('wing_remember', userId.trim()); } else { localStorage.removeItem('wing_remember'); } } catch { // 에러는 authStore에서 관리 } }; return (
{/* Background image */}
{/* Overlay */}
{/* Center: Login Form */}
{/* Logo */}
WING 해양환경 위기대응 통합시스템
{/* Form card */}
{/* User ID */}
{ setUserId(e.target.value); clearError(); }} placeholder="사용자 아이디 입력" autoComplete="username" autoFocus className="w-full bg-bg-elevated border border-stroke rounded-md text-title-4 outline-none" style={{ padding: '11px 14px 11px 38px', transition: 'border-color 0.2s, box-shadow 0.2s', }} onFocus={(e) => { e.currentTarget.style.borderColor = 'rgba(6,182,212,0.4)'; e.currentTarget.style.boxShadow = '0 0 0 3px rgba(6,182,212,0.08)'; }} onBlur={(e) => { e.currentTarget.style.borderColor = 'var(--stroke-default)'; e.currentTarget.style.boxShadow = 'none'; }} />
{/* Password */}
{ setPassword(e.target.value); clearError(); }} placeholder="비밀번호 입력" autoComplete="current-password" className="w-full bg-bg-elevated border border-stroke rounded-md text-title-4 outline-none" style={{ padding: '11px 14px 11px 38px', transition: 'border-color 0.2s, box-shadow 0.2s', }} onFocus={(e) => { e.currentTarget.style.borderColor = 'rgba(6,182,212,0.4)'; e.currentTarget.style.boxShadow = '0 0 0 3px rgba(6,182,212,0.08)'; }} onBlur={(e) => { e.currentTarget.style.borderColor = 'var(--stroke-default)'; e.currentTarget.style.boxShadow = 'none'; }} />
{/* Remember + Forgot */}
{/* Pending approval */} {pendingMessage && (
{pendingMessage}
)} {/* Error */} {error && (
{error}
)} {/* Login button */}
{/* Divider */}
또는
{/* Google / Certificate */}
{GOOGLE_ENABLED && (
{ /* 팝업 닫힘 등 — 별도 처리 불필요 */ }} theme="filled_black" size="large" shape="rectangular" width={304} />
)}
{/* Demo accounts info (DEV only) */} {import.meta.env.DEV && (
데모 계정
{DEMO_ACCOUNTS.map((acc) => (
{ setUserId(acc.id); setPassword(acc.password); clearError(); }} className="flex justify-between items-center cursor-pointer rounded" style={{ padding: '4px 6px', transition: 'background 0.15s', }} onMouseEnter={(e) => (e.currentTarget.style.background = 'rgba(6,182,212,0.06)') } onMouseLeave={(e) => (e.currentTarget.style.background = 'transparent')} > {acc.id} / {acc.password} {acc.label}
))}
)}
{/* end form card */} {/* Footer */}
WING V2.0 | 해양경찰청 기동방제과 위기대응 통합시스템
© 2026 Korea Coast Guard. All rights reserved.
); }