5c085dc69e
· 다이얼로그 디자인: 프로젝트정보 박스(주문유형/제품구분/국내해외/고객사·유무상/접수일/견적환종/견적환율) + 품목정보 테이블(No/품번/품명/S/N/요청납기/고객요청사항/반납사유) · ProjectInfoData 정규화 props — 진행관리/판매관리/매출관리 각각 toProjectInfo 매핑으로 호출 · backend SQL 3곳 보강: exchange_rate (contract_mgmt) + customer_request (CI→CM fallback) + return_reason_name (CI CODE_NAME) · 판매관리/매출관리 page에 columns useMemo + project_no 셀 onClick + ProjectInfoDialog state 추가 · wace 운영 URL: /salesMgmt/salesRegForm.do?saleNo=detail 1:1 매핑 (새 창 → 같은 탭 다이얼로그) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
120 lines
4.9 KiB
TypeScript
120 lines
4.9 KiB
TypeScript
"use client";
|
|
|
|
// 프로젝트 상세 정보 다이얼로그 — wace 운영판 (salesRegForm.do?saleNo=detail) 1:1
|
|
// 사용처: 진행관리 / 영업관리 판매관리 / 매출관리 그리드의 프로젝트번호 셀 클릭 시.
|
|
// 페이지마다 row 키가 다르므로 ProjectInfoData 정규화 객체를 호출 측에서 매핑해 전달.
|
|
|
|
import React from "react";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle,
|
|
} from "@/components/ui/dialog";
|
|
|
|
export interface ProjectInfoData {
|
|
// 프로젝트정보 박스
|
|
orderType: string | null; // 주문유형
|
|
productType: string | null; // 제품구분
|
|
area: string | null; // 국내/해외
|
|
customer: string | null; // 고객사
|
|
paidType: string | null; // 유/무상
|
|
regDate: string | null; // 접수일
|
|
currency: string | null; // 견적환종
|
|
exchangeRate: string | null; // 견적환율
|
|
// 품목정보 테이블 (라인 1건)
|
|
partNo: string | null; // 품번
|
|
partName: string | null; // 품명
|
|
serialNo: string | null; // S/N
|
|
reqDelDate: string | null; // 요청납기
|
|
customerRequest: string | null; // 고객요청사항
|
|
returnReason: string | null; // 반납사유
|
|
}
|
|
|
|
interface Props {
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
data: ProjectInfoData | null;
|
|
}
|
|
|
|
const dash = (v: string | null | undefined) =>
|
|
v == null || v === "" ? "-" : v;
|
|
|
|
export function ProjectInfoDialog({ open, onOpenChange, data }: Props) {
|
|
return (
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
<DialogContent className="max-w-3xl">
|
|
<DialogHeader>
|
|
<DialogTitle>프로젝트 상세 정보</DialogTitle>
|
|
</DialogHeader>
|
|
|
|
{data ? (
|
|
<div className="space-y-4 py-2 text-sm">
|
|
{/* 프로젝트정보 */}
|
|
<section className="rounded border">
|
|
<div className="border-b bg-muted/40 px-3 py-2 text-sm font-semibold">프로젝트정보</div>
|
|
<div className="grid grid-cols-[88px_1fr_88px_1fr_88px_1fr_88px_1fr] gap-y-2 px-3 py-3 text-xs items-center">
|
|
<K>주문유형</K><V>{dash(data.orderType)}</V>
|
|
<K>제품구분</K><V>{dash(data.productType)}</V>
|
|
<K>국내/해외</K><V>{dash(data.area)}</V>
|
|
<K>고객사</K><V>{dash(data.customer)}</V>
|
|
|
|
<K>유/무상</K><V>{dash(data.paidType)}</V>
|
|
<K>접수일</K><V>{dash(data.regDate)}</V>
|
|
<K>견적환종</K><V>{dash(data.currency)}</V>
|
|
<K>견적환율</K><V>{dash(data.exchangeRate)}</V>
|
|
</div>
|
|
</section>
|
|
|
|
{/* 품목정보 */}
|
|
<section className="rounded border">
|
|
<div className="border-b bg-muted/40 px-3 py-2 text-sm font-semibold">품목정보</div>
|
|
<div className="overflow-x-auto">
|
|
<table className="w-full text-xs">
|
|
<thead className="bg-muted/20">
|
|
<tr className="text-center">
|
|
<Th className="w-12">No</Th>
|
|
<Th>품번</Th>
|
|
<Th>품명</Th>
|
|
<Th>S/N</Th>
|
|
<Th>요청납기</Th>
|
|
<Th>고객요청사항</Th>
|
|
<Th>반납사유</Th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr className="text-center">
|
|
<Td>1</Td>
|
|
<Td>{dash(data.partNo)}</Td>
|
|
<Td className="text-left">{dash(data.partName)}</Td>
|
|
<Td>{dash(data.serialNo)}</Td>
|
|
<Td>{dash(data.reqDelDate)}</Td>
|
|
<Td className="text-left">{dash(data.customerRequest)}</Td>
|
|
<Td>{dash(data.returnReason)}</Td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
) : null}
|
|
|
|
<DialogFooter>
|
|
<Button variant="outline" onClick={() => onOpenChange(false)}>닫기</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|
|
|
|
function K({ children }: { children: React.ReactNode }) {
|
|
return <div className="bg-muted/30 px-2 py-1.5 text-right text-muted-foreground rounded-sm">{children}</div>;
|
|
}
|
|
function V({ children }: { children: React.ReactNode }) {
|
|
return <div className="px-2 py-1.5">{children}</div>;
|
|
}
|
|
function Th({ children, className }: { children: React.ReactNode; className?: string }) {
|
|
return <th className={`border-b px-2 py-2 font-semibold ${className ?? ""}`}>{children}</th>;
|
|
}
|
|
function Td({ children, className }: { children: React.ReactNode; className?: string }) {
|
|
return <td className={`border-b px-2 py-2 ${className ?? ""}`}>{children}</td>;
|
|
}
|