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-2 border border-border rounded-md text-[13px] 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(--bd)' e.currentTarget.style.boxShadow = 'none' }} />
{/* Password */}
{ setPassword(e.target.value); clearError() }} placeholder="비밀번호 입력" autoComplete="current-password" className="w-full bg-bg-2 border border-border rounded-md text-[13px] 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(--bd)' 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.
) }