dm: 상세주소 추가 및 기존비번 확인
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useMemo, useRef, useState, type ReactNode, type RefObject } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import PostcodeModal from "@/components/PostcodeModal";
|
||||
import type { AddrItem } from "@/app/store/sdmMng/postcode-service";
|
||||
@@ -162,6 +163,7 @@ function Hr({ className = "" }: { className?: string }) {
|
||||
}
|
||||
|
||||
export default function DemandPlaceRegisterPage() {
|
||||
const router = useRouter();
|
||||
const { Common, initializeCommonData } = useDmStore((state) => state);
|
||||
const { selectSidoData, selectSigunData } = Common;
|
||||
|
||||
@@ -184,6 +186,9 @@ export default function DemandPlaceRegisterPage() {
|
||||
const detailAddrRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
const [tel, setTel] = useState("");
|
||||
const [currentPw, setCurrentPw] = useState("");
|
||||
const [isCurrentPwVerified, setIsCurrentPwVerified] = useState(false);
|
||||
const [isCheckingCurrentPw, setIsCheckingCurrentPw] = useState(false);
|
||||
const [pw, setPw] = useState("");
|
||||
const [pw2, setPw2] = useState("");
|
||||
|
||||
@@ -248,6 +253,11 @@ export default function DemandPlaceRegisterPage() {
|
||||
return () => clearTimeout(timer);
|
||||
}, [addr1, focusDetailAddrOnClose, isPostcodeModalOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editMode) return;
|
||||
setIsCurrentPwVerified(false);
|
||||
}, [bizNo, workerNo, noWorkerNo, editMode]);
|
||||
|
||||
const sidoOptions = useMemo<Option[]>(() => {
|
||||
const mapped = selectSidoData
|
||||
.filter((item) => item.val !== "0")
|
||||
@@ -285,6 +295,57 @@ export default function DemandPlaceRegisterPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const checkCurrentPassword = async () => {
|
||||
const bizDigits = bizNo.replace(/\D/g, "").slice(0, 10);
|
||||
const workerDigits = workerNo.replace(/\D/g, "").slice(0, 4);
|
||||
const password = currentPw.trim();
|
||||
|
||||
if (!bizDigits) return alert("사업자등록번호를 입력해 주세요.");
|
||||
if (!/^\d{10}$/.test(bizDigits)) {
|
||||
return alert("사업자등록번호는 10자리 숫자만 입력 가능합니다.");
|
||||
}
|
||||
if (!noWorkerNo) {
|
||||
if (!workerDigits) return alert("종사업자등록번호를 입력해 주세요.");
|
||||
if (!/^\d{4}$/.test(workerDigits)) {
|
||||
return alert("종사업자등록번호는 4자리 숫자만 입력 가능합니다.");
|
||||
}
|
||||
}
|
||||
if (!password) return alert("기존 비밀번호를 입력해 주세요.");
|
||||
|
||||
const fullDmdInfoId = bizDigits + (noWorkerNo ? "0000" : workerDigits);
|
||||
const payload = {
|
||||
dmdInfoId: fullDmdInfoId,
|
||||
dmdAplyPswd: password,
|
||||
};
|
||||
|
||||
try {
|
||||
setIsCheckingCurrentPw(true);
|
||||
const response = await postFetch("/reuseFc/request/checkPassword", payload);
|
||||
const rawResult = await response.json().catch(() => null);
|
||||
const checkedCount =
|
||||
typeof rawResult === "number"
|
||||
? rawResult
|
||||
: typeof rawResult?.data === "number"
|
||||
? rawResult.data
|
||||
: Number(rawResult ?? 0);
|
||||
|
||||
if (checkedCount >= 1) {
|
||||
setIsCurrentPwVerified(true);
|
||||
alert("기존 비밀번호가 확인되었습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
setIsCurrentPwVerified(false);
|
||||
alert("기존 비밀번호가 일치하지 않습니다.");
|
||||
} catch (error) {
|
||||
setIsCurrentPwVerified(false);
|
||||
console.error("Failed to check current password", error);
|
||||
alert("기존 비밀번호 확인에 실패하였습니다.");
|
||||
} finally {
|
||||
setIsCheckingCurrentPw(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmit = async () => {
|
||||
const bizDigits = bizNo.replace(/\D/g, "").slice(0, 10);
|
||||
const workerDigits = workerNo.replace(/\D/g, "").slice(0, 4);
|
||||
@@ -327,8 +388,15 @@ export default function DemandPlaceRegisterPage() {
|
||||
}
|
||||
if (!passwordConfirm) return alert("비밀번호 확인을 입력해 주세요.");
|
||||
if (password !== passwordConfirm) return alert("비밀번호가 일치하지 않습니다.");
|
||||
} else if ((password || passwordConfirm) && password !== passwordConfirm) {
|
||||
return alert("비밀번호가 일치하지 않습니다.");
|
||||
} else { //수정
|
||||
if (password || passwordConfirm) {
|
||||
if (!password) return alert("비밀번호를 입력해 주세요.");
|
||||
if (password.length < 8 || password.length > 16) {
|
||||
return alert("비밀번호는 8자 이상 16자 이내로 입력해주세요.");
|
||||
}
|
||||
if (!passwordConfirm) return alert("비밀번호 확인을 입력해 주세요.");
|
||||
if (password !== passwordConfirm) return alert("비밀번호가 일치하지 않습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
const fullDmdInfoId = bizDigits + (noWorkerNo ? "0000" : workerDigits);
|
||||
@@ -336,6 +404,8 @@ export default function DemandPlaceRegisterPage() {
|
||||
if (!editMode) {
|
||||
const isDuplicate = await checkDuplicate(fullDmdInfoId);
|
||||
if (isDuplicate) return alert("이미 등록된 사업자등록번호입니다.");
|
||||
} else if (!isCurrentPwVerified) {
|
||||
return alert("기존 비밀번호 확인 후 수정해 주세요.");
|
||||
}
|
||||
|
||||
const myUUID = getCookie(TOKEN_ID_COOKIE) || FALLBACK_UUID;
|
||||
@@ -374,11 +444,24 @@ export default function DemandPlaceRegisterPage() {
|
||||
alert("수요처 수정 완료!");
|
||||
} else {
|
||||
await postFetch("/reuseFc/request", payload);
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
const nextSessionUser = {
|
||||
...(sessionUser ?? {}),
|
||||
...payload,
|
||||
};
|
||||
delete nextSessionUser.dmdAplyPswd;
|
||||
sessionStorage.setItem("user", JSON.stringify(nextSessionUser));
|
||||
}
|
||||
|
||||
alert("수요처 등록 완료!");
|
||||
router.push("/apply/register");
|
||||
}
|
||||
|
||||
setPw("");
|
||||
setPw2("");
|
||||
setCurrentPw("");
|
||||
setIsCurrentPwVerified(false);
|
||||
} catch (error) {
|
||||
console.error("Failed to submit demand place", error);
|
||||
alert(editMode ? "수요처 수정에 실패하였습니다." : "수요처 등록에 실패하였습니다.");
|
||||
@@ -563,6 +646,41 @@ export default function DemandPlaceRegisterPage() {
|
||||
|
||||
<Divider />
|
||||
|
||||
{editMode ? (
|
||||
<>
|
||||
<FormRow label="기존 비밀번호 확인" required>
|
||||
<div className="max-w-[520px]">
|
||||
<div className="flex gap-2">
|
||||
<div className="flex-1">
|
||||
<TextInput
|
||||
value={currentPw}
|
||||
onChange={(value) => {
|
||||
setCurrentPw(value);
|
||||
setIsCurrentPwVerified(false);
|
||||
}}
|
||||
placeholder="기존 비밀번호를 입력하세요."
|
||||
type="password"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={checkCurrentPassword}
|
||||
disabled={isCheckingCurrentPw}
|
||||
className="h-11 shrink-0 rounded bg-[#0B4EA2] px-4 text-base text-white hover:opacity-90 disabled:bg-neutral-400"
|
||||
>
|
||||
{isCheckingCurrentPw ? "확인 중..." : "확인"}
|
||||
</button>
|
||||
</div>
|
||||
<p className={`mt-2 text-sm ${isCurrentPwVerified ? "text-emerald-600" : "text-neutral-500"}`}>
|
||||
{isCurrentPwVerified ? "기존 비밀번호 확인이 완료되었습니다." : "수정 전 기존 비밀번호 확인이 필요합니다."}
|
||||
</p>
|
||||
</div>
|
||||
</FormRow>
|
||||
|
||||
<Divider />
|
||||
</>
|
||||
) : null}
|
||||
|
||||
<FormRow label="비밀번호" required={!editMode}>
|
||||
<div className="max-w-[420px]">
|
||||
<TextInput
|
||||
|
||||
@@ -30,15 +30,16 @@ function useDebouncedValue<T>(value: T, delay = 250) {
|
||||
export default function PostcodeModal({ open, onClose, onSelect }: Props) {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
const [q, setQ] = useState("");
|
||||
const [q, setQ] = useState(""); // 주소 입력값
|
||||
const dq = useDebouncedValue(q, 250);
|
||||
|
||||
const [items, setItems] = useState<AddrItem[]>([]);
|
||||
const [suggestions, setSuggestions] = useState<string[]>([]);
|
||||
const [items, setItems] = useState<AddrItem[]>([]); // 검색 결과
|
||||
const [suggestions, setSuggestions] = useState<string[]>([]); // 추천 주소 목록
|
||||
const [total, setTotal] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [searchMessage, setSearchMessage] = useState("");
|
||||
const [searchMessage, setSearchMessage] = useState(""); // 검색 실패/형식 오류/보조 안내 문구를 담는 상태값
|
||||
const [isInputFocused, setIsInputFocused] = useState(false);
|
||||
const [searchedInput, setSearchedInput] = useState(""); // 주소 검색 입력란 저장
|
||||
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
@@ -133,6 +134,7 @@ export default function PostcodeModal({ open, onClose, onSelect }: Props) {
|
||||
|
||||
setItems(result.items);
|
||||
setTotal(result.totalCount);
|
||||
setSearchedInput(keyword);
|
||||
} catch {
|
||||
if (!alive) return;
|
||||
setItems([]);
|
||||
@@ -151,7 +153,7 @@ export default function PostcodeModal({ open, onClose, onSelect }: Props) {
|
||||
};
|
||||
}, [dq, open]);
|
||||
|
||||
const showSuggest = open && isInputFocused && q.trim().length >= 1 && suggestions.length > 0;
|
||||
const showSuggest = open && isInputFocused && q.trim().length >= 1 && suggestions.length > 0 && dq.trim() !== searchedInput;
|
||||
|
||||
if (!mounted || !open) return null;
|
||||
|
||||
@@ -234,7 +236,7 @@ export default function PostcodeModal({ open, onClose, onSelect }: Props) {
|
||||
className="flex w-full items-center justify-between px-4 py-3 text-left text-[14px] hover:bg-slate-50"
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
onClick={() => {
|
||||
// ✅ 추천 클릭 → 입력값 채우고 드롭다운 닫기 + 포커스 유지
|
||||
// 추천 클릭 → 입력값 초기화 드롭다운 닫기 + 포커스 유지
|
||||
setQ(s);
|
||||
setSuggestions([]);
|
||||
requestAnimationFrame(() => inputRef.current?.focus());
|
||||
@@ -270,7 +272,9 @@ export default function PostcodeModal({ open, onClose, onSelect }: Props) {
|
||||
<button
|
||||
key={`${it.zipNo}-${it.roadAddr}`}
|
||||
type="button"
|
||||
onClick={() => onSelect(it)}
|
||||
onClick={() => {setQ("");
|
||||
setSuggestions([]);
|
||||
onSelect(it)}}
|
||||
className={[
|
||||
"w-full rounded-lg border p-4 text-left transition",
|
||||
"border-slate-200 hover:border-sky-300 hover:bg-sky-50/30",
|
||||
|
||||
Reference in New Issue
Block a user