5662fd2910
PartDetailDialog 재작성 (운영판 부적합 → 정정):
- 기존: 임의 섹션 분리("기본정보/크기·형상/분류·단위/Y-N 플래그") + 운영판 없는 필드
(두께/너비/높이/외경/내경/길이/단위/수량/후가공/공급업체) 노출
- 정정: PartFormDialog 와 동일 레이아웃을 readonly 박스(Ro)로 표시
· 운영판 22필드 그대로 (운영판 없는 9필드 제거)
· 부속 행 추가 (운영 detail 만 표시) : EO No / EO Date / EO구분(CHANGE_TYPE) / EO사유(CHANGE_OPTION)
· CAD Data 3행 : 3D / 2D(Drawing) / 2D(PDF) — 첨부 카운트 표시 (실제 파일 다운로드는 DEV-7 별 PR)
· 헤더: 파란색 바 "품목 상세" (운영판 헤더 1:1)
· 코드값 → 라벨 매핑 : ODRFG (구매/생산/Phantom), LOT_FG (미사용/사용),
USE_YN (미사용/사용), QC_FG (무검사/검사), SETITEM/REQ (부/여)
colgroup 비율 정정 (Form + Detail 공통):
- 기존: 4컬럼 단순 (라벨 110px / input / 라벨 110px / input) → 첫 input 25% 좁음
- 정정: 운영판 JSP colgroup 1:1 (12% / 12% / 25% / 12% / *) + 매 행 첫 Td 에 colSpan={2}
· 결과 input1 ≈ 37%, input2 ≈ 39% — 양쪽 input 비율 거의 동일 (운영판 일치)
· 규격/비고 행 colSpan={4} (운영 JSP colspan=4)
· CAD Data 행 colSpan={3} (작은 라벨 1칸 + 컨텐츠 3칸)
· Th 의 고정 너비 w-[110px] 제거 — colgroup 이 폭 결정
· table-fixed 추가로 colgroup 비율 강제 적용
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
226 lines
9.7 KiB
TypeScript
226 lines
9.7 KiB
TypeScript
"use client";
|
|
|
|
// 개발관리 > PART 상세 다이얼로그 — wace partMng/partMngDetailPopUp.jsp 1:1
|
|
//
|
|
// 운영판은 form 과 동일 화면을 disabled 로 표시 후 "수정" 클릭 시 활성화.
|
|
// RPS 에서는 PartFormDialog 와 분리 유지 (호환). 본 다이얼로그는 Form 레이아웃 readonly +
|
|
// 부속 정보 행 추가 (EO_NO / EO_DATE / EO구분(CHANGE_TYPE) / EO사유(CHANGE_OPTION)) +
|
|
// CAD Data 영역 (3D / 2D(Drawing) / 2D(PDF)) 표시.
|
|
//
|
|
// "수정" 버튼: 부모가 본 다이얼로그를 닫고 PartFormDialog(mode='edit') 오픈하도록 onEdit 콜백 호출.
|
|
|
|
import React, { useEffect, useState } from "react";
|
|
import {
|
|
Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter,
|
|
} from "@/components/ui/dialog";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Loader2, Pencil, FileText } from "lucide-react";
|
|
import { toast } from "sonner";
|
|
import { devPartApi, PartRow } from "@/lib/api/devPart";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
const LABEL_ODRFG: Record<string, string> = { "0": "구매", "1": "생산", "8": "Phantom" };
|
|
const LABEL_LOT_FG: Record<string, string> = { "0": "미사용", "1": "사용" };
|
|
const LABEL_USE_YN: Record<string, string> = { "0": "미사용", "1": "사용" };
|
|
const LABEL_QC_FG: Record<string, string> = { "0": "무검사", "1": "검사" };
|
|
const LABEL_YESNO: Record<string, string> = { "0": "부", "1": "여" };
|
|
|
|
interface Props {
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
objid: string | null;
|
|
/** "수정" 버튼 클릭 시 호출 — 부모는 본 다이얼로그 닫고 PartFormDialog(mode='edit') 오픈 */
|
|
onEdit?: (objid: string) => void;
|
|
}
|
|
|
|
export function PartDetailDialog({ open, onOpenChange, objid, onEdit }: Props) {
|
|
const [row, setRow] = useState<PartRow | null>(null);
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (!open || !objid) return;
|
|
let alive = true;
|
|
setLoading(true);
|
|
devPartApi.detail(objid)
|
|
.then((data) => { if (alive) setRow(data); })
|
|
.catch((e: any) => {
|
|
toast.error(e?.response?.data?.message ?? e?.message ?? "조회 실패");
|
|
onOpenChange(false);
|
|
})
|
|
.finally(() => { if (alive) setLoading(false); });
|
|
return () => { alive = false; };
|
|
}, [open, objid, onOpenChange]);
|
|
|
|
if (!open) return null;
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
<DialogContent className="max-w-[1100px] w-[95vw] max-h-[92vh] flex flex-col p-0 overflow-hidden">
|
|
<DialogHeader className="bg-blue-600 px-4 py-3">
|
|
<DialogTitle className="text-white">품목 상세</DialogTitle>
|
|
</DialogHeader>
|
|
|
|
{loading || !row ? (
|
|
<div className="flex h-64 items-center justify-center">
|
|
<Loader2 className="h-6 w-6 animate-spin" />
|
|
</div>
|
|
) : (
|
|
<div className="flex-1 overflow-y-auto px-4 py-3">
|
|
{/* 운영판 colgroup 1:1 (12% / 12% / 25% / 12% / *) */}
|
|
<table className="w-full border-collapse text-sm table-fixed">
|
|
<colgroup>
|
|
<col style={{ width: "12%" }} />
|
|
<col style={{ width: "12%" }} />
|
|
<col style={{ width: "25%" }} />
|
|
<col style={{ width: "12%" }} />
|
|
<col />
|
|
</colgroup>
|
|
<tbody>
|
|
<Tr>
|
|
<Th>품번</Th><Td colSpan={2}><Ro>{row.part_no}</Ro></Td>
|
|
<Th>품명</Th><Td><Ro>{row.part_name}</Ro></Td>
|
|
</Tr>
|
|
<Tr>
|
|
<Th>재료</Th><Td colSpan={2}><Ro>{row.material}</Ro></Td>
|
|
<Th>열처리경도</Th><Td><Ro>{row.heat_treatment_hardness}</Ro></Td>
|
|
</Tr>
|
|
<Tr>
|
|
<Th>열처리방법</Th><Td colSpan={2}><Ro>{row.heat_treatment_method}</Ro></Td>
|
|
<Th>표면처리</Th><Td><Ro>{row.surface_treatment}</Ro></Td>
|
|
</Tr>
|
|
<Tr>
|
|
<Th>메이커</Th><Td colSpan={2}><Ro>{row.maker}</Ro></Td>
|
|
<Th>범주 이름</Th><Td><Ro>{row.part_type_title}</Ro></Td>
|
|
</Tr>
|
|
<Tr>
|
|
<Th>규격</Th><Td colSpan={4}><Ro>{row.spec}</Ro></Td>
|
|
</Tr>
|
|
<Tr>
|
|
<Th>계정구분</Th><Td colSpan={2}><Ro>{row.acctfg_nm}</Ro></Td>
|
|
<Th>조달구분</Th><Td><Ro>{LABEL_ODRFG[row.odrfg ?? ""] ?? row.odrfg_nm ?? ""}</Ro></Td>
|
|
</Tr>
|
|
<Tr>
|
|
<Th>재고단위</Th><Td colSpan={2}><Ro>{row.unit_dc_nm}</Ro></Td>
|
|
<Th>관리단위</Th><Td><Ro>{row.unitmang_dc_nm}</Ro></Td>
|
|
</Tr>
|
|
<Tr>
|
|
<Th>환산수량</Th>
|
|
<Td colSpan={2}><Ro align="right">{row.unitchng_nb != null && row.unitchng_nb !== "" ? String(row.unitchng_nb) : ""}</Ro></Td>
|
|
<Th>LOT구분</Th><Td><Ro>{LABEL_LOT_FG[row.lot_fg ?? ""] ?? row.lot_fg_nm ?? ""}</Ro></Td>
|
|
</Tr>
|
|
<Tr>
|
|
<Th>사용여부</Th><Td colSpan={2}><Ro>{LABEL_USE_YN[row.use_yn ?? ""] ?? row.use_yn_nm ?? ""}</Ro></Td>
|
|
<Th>검사여부</Th><Td><Ro>{LABEL_QC_FG[row.qc_fg ?? ""] ?? row.qc_fg_nm ?? ""}</Ro></Td>
|
|
</Tr>
|
|
<Tr>
|
|
<Th>SET품여부</Th><Td colSpan={2}><Ro>{LABEL_YESNO[row.setitem_fg ?? ""] ?? row.setitem_fg_nm ?? ""}</Ro></Td>
|
|
<Th>의뢰여부</Th><Td><Ro>{LABEL_YESNO[row.req_fg ?? ""] ?? row.req_fg_nm ?? ""}</Ro></Td>
|
|
</Tr>
|
|
<Tr>
|
|
<Th>개당길이</Th><Td colSpan={2}><Ro align="right">{row.unit_length}</Ro></Td>
|
|
<Th>개당소요량</Th><Td><Ro align="right">{row.unit_qty}</Ro></Td>
|
|
</Tr>
|
|
<Tr>
|
|
<Th>비고</Th><Td colSpan={4}><Ro>{row.remark}</Ro></Td>
|
|
</Tr>
|
|
|
|
{/* 부속 — wace detail 만 표시 */}
|
|
<Tr>
|
|
<Th>EO No</Th><Td colSpan={2}><Ro>{row.eo_no}</Ro></Td>
|
|
<Th>EO Date</Th><Td><Ro>{row.eo_date}</Ro></Td>
|
|
</Tr>
|
|
<Tr>
|
|
<Th>EO구분</Th><Td colSpan={2}><Ro>{row.change_type}</Ro></Td>
|
|
<Th>EO사유</Th><Td><Ro>{row.change_option_name ?? row.change_option}</Ro></Td>
|
|
</Tr>
|
|
|
|
{/* CAD Data */}
|
|
<tr>
|
|
<th className="border bg-muted/30 px-3 py-2 text-left align-top font-medium" rowSpan={3}>
|
|
CAD Data
|
|
</th>
|
|
<th className="border bg-muted/30 px-3 py-2 text-left font-medium">3D</th>
|
|
<td className="border px-3 py-2" colSpan={3}>
|
|
<CadCount label="3D" count={Number(row.cu01_cnt ?? 0)} />
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th className="border bg-muted/30 px-3 py-2 text-left font-medium">2D(Drawing)</th>
|
|
<td className="border px-3 py-2" colSpan={3}>
|
|
<CadCount label="2D(Drawing)" count={Number(row.cu02_cnt ?? 0)} />
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th className="border bg-muted/30 px-3 py-2 text-left font-medium">2D(PDF)</th>
|
|
<td className="border px-3 py-2" colSpan={3}>
|
|
<CadCount label="2D(PDF)" count={Number(row.cu03_cnt ?? 0)} />
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<div className="mt-2 text-[11px] text-muted-foreground">
|
|
CAD Data 첨부 다운로드/미리보기는 DEV-7 (도면업로드) 별 PR 에서 활성화됩니다.
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<DialogFooter className="border-t bg-muted/20 px-4 py-3 sm:justify-center">
|
|
{row && onEdit && (
|
|
<Button onClick={() => onEdit(row.objid)}>
|
|
<Pencil className="h-4 w-4" /><span className="ml-1">수정</span>
|
|
</Button>
|
|
)}
|
|
<Button variant="outline" onClick={() => onOpenChange(false)}>닫기</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|
|
|
|
// ─── 보조 컴포넌트 ──────────────────────────────────────────
|
|
|
|
function Tr({ children }: { children: React.ReactNode }) {
|
|
return <tr>{children}</tr>;
|
|
}
|
|
function Th({ children }: { children: React.ReactNode }) {
|
|
return (
|
|
<th className="border bg-muted/30 px-3 py-2 text-left align-middle font-medium">
|
|
{children}
|
|
</th>
|
|
);
|
|
}
|
|
function Td({ children, colSpan }: { children: React.ReactNode; colSpan?: number }) {
|
|
return <td className="border px-3 py-1.5" colSpan={colSpan}>{children}</td>;
|
|
}
|
|
|
|
// 운영판 disabled input 의 readonly 박스
|
|
function Ro({ children, align }: { children: React.ReactNode; align?: "left" | "center" | "right" }) {
|
|
const cls = align === "right" ? "text-right" : align === "center" ? "text-center" : "text-left";
|
|
const empty = children == null || children === "";
|
|
return (
|
|
<div className={cn(
|
|
"min-h-9 rounded border bg-muted/30 px-2 py-1.5",
|
|
cls,
|
|
)}>
|
|
{empty ? <span className="text-muted-foreground">—</span> : children}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function CadCount({ label, count }: { label: string; count: number }) {
|
|
if (count > 0) {
|
|
return (
|
|
<div className="flex items-center gap-2 text-xs">
|
|
<FileText className="h-4 w-4 text-blue-600" />
|
|
<span>{label} 첨부 {count.toLocaleString()}건</span>
|
|
</div>
|
|
);
|
|
}
|
|
return (
|
|
<div className="border-2 border-dashed border-muted-foreground/30 rounded text-muted-foreground py-3 text-center text-xs">
|
|
첨부된 {label} 파일 없음
|
|
</div>
|
|
);
|
|
}
|