Files
vexplor/frontend/components/common/AddressSearchButton.tsx
kjs d3491a79bb feat: Implement approval request validation and enhance UI components
- Added validation to prevent duplicate approval requests for the same target, ensuring that only one active or completed approval exists at a time.
- Implemented a check to disallow self-approval in the approval line unless the approval type is 'self'.
- Integrated the ApprovalDetailModal component into the main layout for improved user experience.
- Updated the SalesOrderPage to include approval status in the data structure, enhancing visibility of approval states.
- Enhanced BOM management modals across multiple company implementations to accommodate new UI requirements.
2026-04-16 10:26:38 +09:00

98 lines
2.7 KiB
TypeScript

"use client";
import React, { useCallback } from "react";
import { Button } from "@/components/ui/button";
import { Search } from "lucide-react";
import { toast } from "sonner";
interface AddressSearchButtonProps {
onComplete: (data: {
address: string; // 선택된 주소 (도로명 또는 지번)
roadAddress: string; // 도로명주소
jibunAddress: string; // 지번주소
zonecode: string; // 우편번호
buildingName?: string; // 건물명
}) => void;
size?: "sm" | "default" | "lg";
variant?: "default" | "outline" | "secondary" | "ghost";
className?: string;
label?: string;
}
declare global {
interface Window {
daum?: any;
}
}
const SCRIPT_SRC = "//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js";
// 스크립트 로드 (1회)
function loadScript(): Promise<void> {
return new Promise((resolve, reject) => {
if (typeof window === "undefined") return reject(new Error("SSR"));
if (window.daum?.Postcode) return resolve();
const existing = document.querySelector(`script[src="${SCRIPT_SRC}"]`) as HTMLScriptElement | null;
if (existing) {
existing.addEventListener("load", () => resolve());
existing.addEventListener("error", () => reject(new Error("load failed")));
return;
}
const script = document.createElement("script");
script.src = SCRIPT_SRC;
script.async = true;
script.onload = () => resolve();
script.onerror = () => reject(new Error("load failed"));
document.body.appendChild(script);
});
}
export const AddressSearchButton: React.FC<AddressSearchButtonProps> = ({
onComplete,
size = "sm",
variant = "outline",
className,
label = "주소 검색",
}) => {
const handleClick = useCallback(async () => {
try {
await loadScript();
if (!window.daum?.Postcode) {
toast.error("주소 검색 서비스를 불러올 수 없습니다.");
return;
}
new window.daum.Postcode({
oncomplete: (data: any) => {
const road = data.roadAddress || "";
const jibun = data.jibunAddress || data.autoJibunAddress || "";
const picked = road || jibun;
onComplete({
address: picked,
roadAddress: road,
jibunAddress: jibun,
zonecode: data.zonecode || "",
buildingName: data.buildingName || "",
});
},
}).open();
} catch {
toast.error("주소 검색 스크립트 로드에 실패했습니다.");
}
}, [onComplete]);
return (
<Button
type="button"
variant={variant}
size={size}
onClick={handleClick}
className={className}
>
<Search className="w-3.5 h-3.5" />
{label}
</Button>
);
};
export default AddressSearchButton;