"use client"; import { useEffect, useId, useState } from "react"; import { useRouter } from "next/navigation"; import { useDmStore } from "@/app/store"; import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form"; import { sha512 } from 'js-sha512'; 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{10}$/.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 { actionLogin } = useDmStore((state) => state); 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 nextErrors = { ...p }; delete nextErrors.workerNo; return nextErrors; }); } }, [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 = "사업자등록번호는 10자리 숫자만 입력 가능합니다."; if (!noWorker && !w) next.workerNo = "종사업자등록번호를 입력해주세요."; else if (!noWorker && !/^\d{4}$/.test(w)) next.workerNo = "종사업자등록번호는 4자리 숫자만 입력 가능합니다."; if (!p) next.pw = "비밀번호를 입력해주세요."; else if (p.length < 8) next.pw = "비밀번호는 8자 이상 입력해주세요."; else if (p.length > 16) next.pw = "비밀번호는 16자 이내로 입력해주세요."; 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); const param = { dmdInfoId: bizNo + (noWorker ? "0000" : workerNo), // 종사업자번호 해당없음 "0000"으로 채움 dmdAplyPswd: sha512.hex(pw), }; await actionLogin(param as DemandCompType); const sessionUser = sessionStorage.getItem("user"); if (sessionUser) { const parsed = JSON.parse(sessionUser) as { dmdCoNm?: string }; onSuccess?.({ userName: parsed.dmdCoNm ?? "수요처" }); onClose(); router.refresh(); } } finally { setSubmitting(false); } }; const initPassword = () => { alert("관리자(031-0000-0000)에게 비밀번호 초기화 요청을 유선으로 하시기 바랍니다."); } return (
{/* Backdrop */}

수요처 로그인

{/* title underline gradient bar */}
{/* Body */}
{/* 사업자 등록번호 */}
{ setBizNo(e.target.value.replace(/\D/g, "").slice(0, 10)); 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.replace(/\D/g, "").slice(0, 4)); 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}

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