import {useState, type FormEvent} from 'react'; import {Link, Navigate, useLocation, useNavigate} from 'react-router'; import {LogIn} from 'lucide-react'; import {googleLogin, login} from '@/api'; import {GoogleSignInButton} from '@/components/auth/GoogleSignInButton'; import {Button} from '@/components/ui/button'; import {Input} from '@/components/ui/input'; import {isGoogleLoginEnabled} from '@/lib/googleIdentity'; import {notifyApiError} from '@/lib/notify'; import {establishSession} from '@/lib/session'; import {useAuthStore} from '@/stores/auth'; /** * 로그인 화면. **사장님 앱과 내부 운영 화면이 같이 쓴다.** * * ★ `selfServe` 로 갈린다 — 사장님 앱은 스스로 가입하고 구글로도 들어오지만, 내부 운영 계정은 * 우리가 만들어 준다. 가입 링크를 두 곳에 다 두면 admin 라우터에 없는 `/signup` 으로 보내 * 404 가 난다(admin/src/app/router.tsx 에 그 경로는 없다). */ export function LoginPage({selfServe = true}: {selfServe?: boolean}) { const navigate = useNavigate(); const location = useLocation(); const user = useAuthStore((s) => s.user); // 개발 편의로 기본값을 채워 둔다. 운영에 열기 전에 반드시 빈 문자열로 되돌린다. const [id, setId] = useState('admin'); const [password, setPassword] = useState('1234'); const [isSubmitting, setIsSubmitting] = useState(false); // ★ 기본 도착지를 '/' 로 둔다. 앱마다 홈이 다르고(사장님 → 빌더, 내부 → 사업장 목록) // 각 라우터의 '/' 리다이렉트가 이미 그걸 안다. 여기 경로를 박으면 한쪽에서 404 다. const from = (location.state as {from?: string} | null)?.from ?? '/'; if (user) return ; const handleSubmit = async (event: FormEvent) => { event.preventDefault(); setIsSubmitting(true); try { const res = await login({id, password}); if (res.result?.success === false) { notifyApiError({data: res}, '아이디 또는 비밀번호를 확인해 주세요.'); return; } if (!(await establishSession(res, id))) { notifyApiError({data: res}, '로그인 응답에 토큰이 없습니다.'); return; } navigate(from, {replace: true}); } catch (error) { notifyApiError(error, '로그인에 실패했습니다.'); } finally { setIsSubmitting(false); } }; const handleGoogle = async (credential: string) => { setIsSubmitting(true); try { const res = await googleLogin({credential}); if (res.result?.success === false) { notifyApiError({data: res}, '구글 로그인에 실패했습니다.'); return; } if (!(await establishSession(res, ''))) { notifyApiError({data: res}, '로그인 응답에 토큰이 없습니다.'); return; } navigate(from, {replace: true}); } catch (error) { notifyApiError(error, '구글 로그인에 실패했습니다.'); } finally { setIsSubmitting(false); } }; return (
Web4Ai

{selfServe ? '로그인' : '관리자'}

{selfServe ? '내 가게 사이트를 만들고 발행합니다.' : '사업장·정보 확인·발행을 관리합니다.'}

setId(e.target.value)} autoComplete="username" required />
setPassword(e.target.value)} autoComplete="current-password" required />
{selfServe && isGoogleLoginEnabled() && ( <>
또는
)} {selfServe && (

아직 계정이 없으신가요?{' '} 회원가입

)}
); }