"use client"; import { useEffect, useId, useState } from "react"; import { useRouter } from "next/navigation"; import { useDmStore } from "@/app/store"; import { sha512 } from 'js-sha512'; import toast from "react-hot-toast"; import { Button, Input } from "@common/components"; type LoginModalProps = { open: boolean; onClose: () => void; onSuccess?: (payload: { userName: string; accessToken?: string }) => void; }; type FieldKey = "bizNo" | "workerNo" | "pw"; const REGISTER_HREF = "/apply/dmdco"; function isBizNoValid(v: string) { return /^\d{3}-\d{2}-\d{5}$/.test(v); } function inputCls(opts: { hasError?: boolean; disabled?: boolean }) { const base = "h-[48px] w-full rounded-[4px] border px-5 text-[18px] outline-none"; if (opts.disabled) return `${base} border-gray-200 bg-gray-50 text-gray-400`; if (opts.hasError) return `${base} border-red-500 ring-1 ring-red-400 focus:border-red-500`; return `${base} border-gray-300 focus:border-sky-400`; } export default function LoginModal({ open, onClose, onSuccess }: LoginModalProps) { const titleId = useId(); const router = useRouter(); const [bizNo, setBizNo] = useState(""); // 사업자번호 const [workerNo, setWorkerNo] = useState(""); // 종사업자번호 const [noWorker, setNoWorker] = useState(true); // 종사업자번호 해당없음 checked const [pw, setPw] = useState(""); // ✅ 유효성/서버 에러 표시용 const [errors, setErrors] = useState>>({}); const [touched, setTouched] = useState>>({}); const [submitting, setSubmitting] = useState(false); useEffect(() => { if (!open) return; const onKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); // ESC 키로 모달 닫기 }; window.addEventListener("keydown", onKeyDown); // 스크롤 잠금 const prev = document.body.style.overflow; document.body.style.overflow = "hidden"; // 모달 오픈 시 배경 스크롤 방지 // 모달 오픈 시 에러/터치 초기화 setErrors({}); setTouched({}); return () => { window.removeEventListener("keydown", onKeyDown); document.body.style.overflow = prev; }; }, [open, onClose]); useEffect(() => { if (noWorker) { setWorkerNo(""); setErrors((p) => { const { workerNo: _unused, ...rest } = p; return rest; }); } }, [noWorker]); if (!open) return null; const validate = () => { const next: Partial> = {}; const b = bizNo.trim(); const w = workerNo.trim(); const p = pw.trim(); if (!b) next.bizNo = "사업자등록번호를 입력해주세요."; else if (!isBizNoValid(b)) next.bizNo = "사업자등록번호 형식이 올바르지 않습니다. (예: 123-45-67890)"; if (!noWorker && !w) next.workerNo = "종사자등록번호를 입력해주세요."; if (!p) next.pw = "비밀번호를 입력해주세요."; setErrors(next); return next; }; const formatBusinessNumber = (value: string) => { // 숫자만 추출 const digitsOnly = value.replace(/\D/g, ""); // 10자리까지만 처리 const limitedDigits = digitsOnly.slice(0, 10); // 3-2-5 형식으로 포맷팅 if (limitedDigits.length <= 3) { return limitedDigits; } else if (limitedDigits.length <= 5) { return `${limitedDigits.slice(0, 3)}-${limitedDigits.slice(3)}`; } else { return `${limitedDigits.slice(0, 3)}-${limitedDigits.slice(3, 5)}-${limitedDigits.slice(5)}`; } }; const markTouched = (key: FieldKey) => setTouched((p) => ({ ...p, [key]: true })); const markAllTouched = () => setTouched({ bizNo: true, workerNo: true, pw: true }); const submit = async (e: React.FormEvent) => { e.preventDefault(); markAllTouched(); const v = validate(); if (Object.keys(v).length) return; try { setSubmitting(true); // ✅ TODO: 실제 로그인 API 연결 지점 // const res = await fetch("/api/demand/login", { // method: "POST", // headers: { "Content-Type": "application/json" }, // body: JSON.stringify({ bizNo, workerNo: noWorker ? null : workerNo, pw }), // }); // if (!res.ok) { ... } // const data = await res.json(); // onSuccess?.({ userName: data.userName, accessToken: data.token }); // --- 데모: 비밀번호가 "1234"면 성공 처리(원하면 제거) --- const serverOk = pw.trim() === "1234"; if (!serverOk) { setErrors((p) => ({ ...p, pw: "잘못된 비밀번호입니다. (데모: 1234 입력 시 성공)" })); setTouched((p) => ({ ...p, pw: true })); return; } onSuccess?.({ userName: "마인드원", accessToken: "demo_token" }); onClose(); router.refresh(); } finally { setSubmitting(false); } }; // const handleLogin = () => { // const data = form.getValues(); // const passwordStr = sha512.hex(data.password) // data.dmdAplyPswd = passwordStr; // const param = { // dmdInfoId: data.dmdInfoId + (isBznmTotNoDisabled ? '0000' : data.bznmTotNo), // dmdAplyPswd: data.dmdAplyPswd, // } // actionLogin(param as unknown as DemandCompType); // }; const initPassword = () => { alert("관리자(031-0000-0000)에게 비밀번호 초기화 요청을 유선으로 하시기 바랍니다."); } return (
{/* Backdrop */}

수요처 로그인

{/* title underline gradient bar */}
{/* Body */}
{/* 사업자 등록번호 */}
{ setBizNo(e.target.value); setErrors((p) => ({ ...p, bizNo: undefined })); }} onBlur={() => { markTouched("bizNo"); validate(); }} placeholder="사업자등록번호를 입력하세요. (예: 123-45-67890)" className={inputCls({ hasError: !!(touched.bizNo && errors.bizNo) })} /> {touched.bizNo && errors.bizNo && (

{errors.bizNo}

)}
{/* 종사자등록번호 + 해당없음 */}
{ setWorkerNo(e.target.value); setErrors((p) => ({ ...p, workerNo: undefined })); }} onBlur={() => { markTouched("workerNo"); validate(); }} disabled={noWorker} placeholder="종사자등록번호를 입력하세요." className={inputCls({ disabled: noWorker, hasError: !noWorker && !!(touched.workerNo && errors.workerNo), })} /> {!noWorker && touched.workerNo && errors.workerNo && (

{errors.workerNo}

)}
{/* 비밀번호 */}
{ setPw(e.target.value); setErrors((p) => ({ ...p, pw: undefined })); }} onBlur={() => { markTouched("pw"); validate(); }} placeholder="비밀번호를 입력하세요." className={inputCls({ hasError: !!(touched.pw && errors.pw) })} /> {touched.pw && errors.pw && (

{errors.pw}

)}
{/* 로그인 버튼 */} {/* 비밀번호 초기화 요청 */} {/* 하단 안내 박스 + 수요처등록 버튼 */}
수요처를 등록하지 않았다면
먼저 수요처등록을 해주세요.
); }