summaryrefslogtreecommitdiff
path: root/lib/pq/pq-review-table-new
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pq/pq-review-table-new')
-rw-r--r--lib/pq/pq-review-table-new/request-investigation-dialog.tsx48
-rw-r--r--lib/pq/pq-review-table-new/vendors-table-columns.tsx64
-rw-r--r--lib/pq/pq-review-table-new/vendors-table-toolbar-actions.tsx23
3 files changed, 118 insertions, 17 deletions
diff --git a/lib/pq/pq-review-table-new/request-investigation-dialog.tsx b/lib/pq/pq-review-table-new/request-investigation-dialog.tsx
index f5a7ff91..febc308c 100644
--- a/lib/pq/pq-review-table-new/request-investigation-dialog.tsx
+++ b/lib/pq/pq-review-table-new/request-investigation-dialog.tsx
@@ -83,6 +83,7 @@ interface RequestInvestigationDialogProps {
investigationMethod?: string,
investigationNotes?: string
}
+ vendorCountry?: string | null
}
export function RequestInvestigationDialog({
@@ -91,7 +92,9 @@ export function RequestInvestigationDialog({
onSubmit,
selectedCount,
initialData,
+ vendorCountry,
}: RequestInvestigationDialogProps) {
+ const isDomesticVendor = vendorCountry === "KR" || vendorCountry === "한국"
const [isPending, setIsPending] = React.useState(false)
const [qmManagers, setQMManagers] = React.useState<QMUser[]>([])
const [isLoadingManagers, setIsLoadingManagers] = React.useState(false)
@@ -121,6 +124,36 @@ export function RequestInvestigationDialog({
}
}, [isOpen, initialData, form]);
+ // 도로명 주소 검색 결과 수신 및 주소 포맷팅
+ React.useEffect(() => {
+ if (!isOpen || !isDomesticVendor) return
+
+ const handleMessage = (event: MessageEvent) => {
+ if (!event.data || event.data.type !== "JUSO_SELECTED") return
+ const { zipNo, roadAddrPart1, roadAddrPart2, addrDetail } = event.data.payload || {}
+ const road = [roadAddrPart1, roadAddrPart2].filter(Boolean).join(" ").trim()
+ const baseAddress = [zipNo ? `(${zipNo})` : "", road].filter(Boolean).join(" ").trim()
+ const detail = (addrDetail || "").trim()
+ const formatted = detail ? `${baseAddress}, ${detail}` : baseAddress
+
+ if (formatted) {
+ form.setValue("investigationAddress", formatted, { shouldDirty: true })
+ }
+ }
+
+ window.addEventListener("message", handleMessage)
+ return () => window.removeEventListener("message", handleMessage)
+ }, [isOpen, isDomesticVendor, form])
+
+ const handleJusoSearch = () => {
+ if (!isDomesticVendor) return
+ window.open(
+ "/api/juso",
+ "jusoSearch",
+ "width=570,height=420,scrollbars=yes,resizable=yes"
+ )
+ }
+
// Dialog가 열릴 때 QM 담당자 목록 로드
React.useEffect(() => {
if (isOpen && qmManagers.length === 0) {
@@ -227,7 +260,20 @@ export function RequestInvestigationDialog({
name="investigationAddress"
render={({ field }) => (
<FormItem>
- <FormLabel>실사 장소</FormLabel>
+ <div className="flex items-center justify-between gap-2">
+ <FormLabel>실사 장소</FormLabel>
+ {isDomesticVendor && (
+ <Button
+ type="button"
+ size="sm"
+ variant="secondary"
+ onClick={handleJusoSearch}
+ disabled={isPending}
+ >
+ 도로명 주소 검색
+ </Button>
+ )}
+ </div>
<FormControl>
<Textarea
placeholder="실사가 진행될 주소를 입력하세요"
diff --git a/lib/pq/pq-review-table-new/vendors-table-columns.tsx b/lib/pq/pq-review-table-new/vendors-table-columns.tsx
index ae684172..dc2afa66 100644
--- a/lib/pq/pq-review-table-new/vendors-table-columns.tsx
+++ b/lib/pq/pq-review-table-new/vendors-table-columns.tsx
@@ -46,6 +46,7 @@ export interface PQSubmission {
taxId: string
vendorStatus: string
email: string
+ vendorCountry?: string | null
// 프로젝트 정보
projectId: number | null
projectName: string | null
@@ -294,6 +295,10 @@ export function getColumns({ setRowAction, router }: GetColumnsProps): ExtendedC
return { status: "PQ_IN_PROGRESS", label: "PQ 진행 중", variant: "secondary" as const };
case "SUBMITTED":
return { status: "PQ_SUBMITTED", label: "PQ 제출됨", variant: "default" as const };
+ case "SAFETY_APPROVED":
+ return { status: "PQ_SAFETY_APPROVED", label: "안전 승인됨", variant: "secondary" as const };
+ case "SAFETY_REJECTED":
+ return { status: "PQ_SAFETY_REJECTED", label: "안전 거절됨", variant: "destructive" as const };
case "APPROVED":
return { status: "PQ_APPROVED", label: "PQ 승인됨", variant: "success" as const };
case "REJECTED":
@@ -553,25 +558,56 @@ export function getColumns({ setRowAction, router }: GetColumnsProps): ExtendedC
<DataTableColumnHeaderSimple column={column} title="실사품목" />
),
cell: ({ row }) => {
- const pqItems = row.original.pqItems;
+ const pqItems = row.original.pqItems
if (!pqItems) {
- return <span className="text-muted-foreground">-</span>;
+ return <span className="text-muted-foreground">-</span>
}
- // JSON 파싱하여 첫 번째 아이템 표시
- const items = typeof pqItems === 'string' ? JSON.parse(pqItems) : pqItems;
- if (Array.isArray(items) && items.length > 0) {
- const firstItem = items[0];
- return (
- <div className="flex items-center gap-2">
- <span className="text-sm">{firstItem.itemCode} - {firstItem.itemName}</span>
- {items.length > 1 && (
- <span className="text-xs text-muted-foreground">외 {items.length - 1}건</span>
- )}
- </div>
- );
+ // 문자열이면 JSON 파싱을 시도하고, 실패 시 원문 그대로 표시
+ const parsed =
+ typeof pqItems === "string"
+ ? (() => {
+ try {
+ return JSON.parse(pqItems)
+ } catch {
+ return pqItems
+ }
+ })()
+ : pqItems
+
+ if (Array.isArray(parsed)) {
+ if (parsed.length === 0) {
+ return <span className="text-muted-foreground">-</span>
}
+
+ return (
+ <div className="flex flex-wrap gap-1">
+ {parsed.map((item, idx) => {
+ if (!item || typeof item !== "object") return null
+ const itemObj = item as Record<string, any>
+ const displayName =
+ itemObj.materialGroupDescription || itemObj.itemName || ""
+ const displayCode =
+ itemObj.materialGroupCode || itemObj.itemCode || ""
+
+ if (!displayName && !displayCode) return null
+
+ return (
+ <Badge key={`${row.original.id}-pqitem-${idx}`} variant="outline">
+ {displayName || displayCode || "품목"}
+ {displayCode && displayName !== displayCode
+ ? ` (${displayCode})`
+ : ""}
+ </Badge>
+ )
+ })}
+ </div>
+ )
+ }
+
+ // 배열이 아닌 경우 문자열 그대로 표시
+ return <span className="text-sm">{String(parsed) || "-"}</span>
},
meta: {
excelHeader: "실사품목",
diff --git a/lib/pq/pq-review-table-new/vendors-table-toolbar-actions.tsx b/lib/pq/pq-review-table-new/vendors-table-toolbar-actions.tsx
index 4584e772..b7e54f3d 100644
--- a/lib/pq/pq-review-table-new/vendors-table-toolbar-actions.tsx
+++ b/lib/pq/pq-review-table-new/vendors-table-toolbar-actions.tsx
@@ -38,6 +38,7 @@ interface InvestigationInitialData {
createdAt?: Date;
investigationAddress?: string;
investigationNotes?: string;
+ vendorCountry?: string | null;
}
export function VendorsTableToolbarActions({ table }: VendorsTableToolbarActionsProps) {
@@ -87,6 +88,7 @@ const handleOpenRequestDialog = async () => {
// 선택된 행이 정확히 1개인 경우에만 초기값 설정
if (selectedRows.length === 1) {
const row = selectedRows[0].original;
+ initialData.vendorCountry = row.vendorCountry ?? null;
// 승인된 PQ이고 아직 실사가 없는 경우
if (row.status === "APPROVED" && !row.investigation) {
@@ -570,6 +572,11 @@ const handleOpenRequestDialog = async () => {
row.original.type === "NON_INSPECTION"
)
+ // 승인되지 않은 PQ가 포함되었는지 확인
+ const hasNonApprovedStatus = selectedRows.some(row =>
+ row.original.status !== "APPROVED"
+ )
+
// 실사 방법 라벨 변환 함수
const getInvestigationMethodLabel = (method: string): string => {
switch (method) {
@@ -674,9 +681,20 @@ const handleOpenRequestDialog = async () => {
variant="outline"
size="sm"
onClick={handleOpenRequestDialog} // 여기를 수정: 새로운 핸들러 함수 사용
- disabled={isLoading || selectedRows.length === 0 || hasNonInspectionPQ}
+ disabled={
+ isLoading ||
+ selectedRows.length === 0 ||
+ hasNonInspectionPQ ||
+ hasNonApprovedStatus
+ }
className="gap-2"
- title={hasNonInspectionPQ ? "미실사 PQ는 실사 의뢰할 수 없습니다." : undefined}
+ title={
+ hasNonInspectionPQ
+ ? "미실사 PQ는 실사 의뢰할 수 없습니다."
+ : hasNonApprovedStatus
+ ? "승인된 PQ만 실사 의뢰할 수 있습니다."
+ : undefined
+ }
>
<ClipboardCheck className="size-4" aria-hidden="true" />
<span className="hidden sm:inline">실사 의뢰</span>
@@ -784,6 +802,7 @@ const handleOpenRequestDialog = async () => {
onSubmit={handleRequestInvestigation}
selectedCount={approvedPQsCount}
initialData={dialogInitialData} // 초기 데이터 전달
+ vendorCountry={dialogInitialData?.vendorCountry}
/>