From a3525f8bdfcf849cc1716fab81cb8facadbe9a8e Mon Sep 17 00:00:00 2001 From: dujinkim Date: Mon, 27 Oct 2025 10:03:06 +0000 Subject: (최겸) 구매 협력업체 관리(PQ/실사관리, 정기평가 협력업체 제출 상세 dialog 개발, MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(master-data)/pq-criteria/[pqListId]/page.tsx | 7 +- components/notice/notice-client.tsx | 2 +- components/pq-input/pq-input-tabs.tsx | 92 ++- components/pq-input/pq-review-wrapper.tsx | 36 +- config/menuConfig.ts | 6 + config/vendorInvestigationsColumnsConfig.ts | 8 +- db/schema/pq.ts | 9 - i18n/locales/en/menu.json | 6 +- i18n/locales/ko/menu.json | 2 + lib/evaluation/table/evaluation-columns.tsx | 46 +- lib/evaluation/table/evaluation-table.tsx | 11 + lib/evaluation/table/vendor-submission-dialog.tsx | 623 +++++++++++++++++++++ lib/evaluation/vendor-submission-service.ts | 369 ++++++++++++ lib/export.ts | 6 +- lib/mail/templates/audit-result-notice.hbs | 3 +- lib/mail/templates/data-room-invitation.hbs | 210 +++++++ lib/pq/pq-criteria/add-pq-dialog.tsx | 2 + lib/pq/pq-criteria/pq-table-column.tsx | 50 +- lib/pq/pq-criteria/pq-table-toolbar-actions.tsx | 48 +- lib/pq/pq-criteria/pq-table.tsx | 10 +- lib/pq/pq-criteria/update-pq-sheet.tsx | 1 + .../edit-investigation-dialog.tsx | 2 +- .../request-investigation-dialog.tsx | 6 +- lib/pq/pq-review-table-new/send-results-dialog.tsx | 8 +- .../pq-review-table-new/vendors-table-columns.tsx | 80 ++- lib/pq/service.ts | 583 +++++++++++-------- lib/pq/table/pq-lists-table.tsx | 2 +- .../table/investigation-table.tsx | 4 +- .../table/update-investigation-sheet.tsx | 6 +- lib/vendors/service.ts | 60 +- 30 files changed, 1908 insertions(+), 390 deletions(-) create mode 100644 lib/evaluation/table/vendor-submission-dialog.tsx create mode 100644 lib/evaluation/vendor-submission-service.ts create mode 100644 lib/mail/templates/data-room-invitation.hbs diff --git a/app/[lng]/evcp/(evcp)/(master-data)/pq-criteria/[pqListId]/page.tsx b/app/[lng]/evcp/(evcp)/(master-data)/pq-criteria/[pqListId]/page.tsx index 15cb3bf3..cc356f0e 100644 --- a/app/[lng]/evcp/(evcp)/(master-data)/pq-criteria/[pqListId]/page.tsx +++ b/app/[lng]/evcp/(evcp)/(master-data)/pq-criteria/[pqListId]/page.tsx @@ -4,7 +4,7 @@ import { getValidFilters } from "@/lib/data-table" import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton" import { Shell } from "@/components/shell" import { searchParamsCache } from "@/lib/pq/validations" -import { getPQsByListId } from "@/lib/pq/service" +import { getPQsByListId, getPQListInfo } from "@/lib/pq/service" import { PqsTable } from "@/lib/pq/pq-criteria/pq-table" import { notFound } from "next/navigation" @@ -26,12 +26,13 @@ export default async function PQDetailPage(props: PQDetailPageProps) { // filters가 없는 경우를 처리 const validFilters = getValidFilters(search.filters) - // PQ 항목들 가져오기 + // PQ 리스트 정보와 항목들 가져오기 const promises = Promise.all([ getPQsByListId(pqListId, { ...search, filters: validFilters, - }) + }), + getPQListInfo(pqListId) ]) return ( diff --git a/components/notice/notice-client.tsx b/components/notice/notice-client.tsx index ae8ccebc..c68add0b 100644 --- a/components/notice/notice-client.tsx +++ b/components/notice/notice-client.tsx @@ -296,7 +296,7 @@ export function NoticeClient({ initialData = [], currentUserId }: NoticeClientPr 팝업 - 게시기간 + 팝업게시기간 {/* 다시보지않기 */} 작성자 상태 diff --git a/components/pq-input/pq-input-tabs.tsx b/components/pq-input/pq-input-tabs.tsx index 3f7e1718..4e6b7ed2 100644 --- a/components/pq-input/pq-input-tabs.tsx +++ b/components/pq-input/pq-input-tabs.tsx @@ -15,6 +15,13 @@ import { import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Textarea } from "@/components/ui/textarea" +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" import { X, Save, CheckCircle2, AlertTriangle, ChevronsUpDown, Download, Loader2 } from "lucide-react" import prettyBytes from "pretty-bytes" import { useToast } from "@/hooks/use-toast" @@ -129,6 +136,12 @@ const pqFormSchema = z.object({ type PQFormValues = z.infer +// 통화 단위 옵션 +const currencyUnits = [ + "USD", "EUR", "GBP", "JPY", "CNY", "KRW", "AUD", "CAD", "CHF", "HKD", + "SGD", "THB", "PHP", "IDR", "MYR", "VND", "INR", "BRL", "MXN", "RUB" +] + // ---------------------------------------------------------------------- // 3) Main Component: PQInputTabs // ---------------------------------------------------------------------- @@ -369,12 +382,12 @@ export function PQInputTabs({ break case "PHONE": case "FAX": - // 전화번호/팩스번호는 숫자만 허용 - const phoneRegex = /^\d+$/ + // 전화번호/팩스번호는 숫자와 하이픈 허용 + const phoneRegex = /^[\d-]+$/ if (!phoneRegex.test(answerData.answer)) { toast({ title: `${inputFormat === "PHONE" ? "전화번호" : "팩스번호"} 형식 오류`, - description: `숫자만 입력해주세요.`, + description: `숫자와 하이픈(-)만 입력해주세요.`, variant: "destructive", }) return @@ -391,6 +404,9 @@ export function PQInputTabs({ return } break + case "NUMBER_WITH_UNIT": + // 숫자+단위는 별도 검증 없음 (숫자와 단위가 분리되어 있음) + break case "TEXT": case "TEXT_FILE": case "FILE": @@ -767,7 +783,7 @@ export function PQInputTabs({ {data.map((group) => ( {/* 2-column grid */} -
+
{sortByCode(group.items).map((item) => { const { criteriaId, code, checkPoint, remarks, description, contractInfo, additionalRequirement } = item const answerIndex = getAnswerIndex(criteriaId) @@ -789,7 +805,7 @@ export function PQInputTabs({ return ( - +
@@ -849,7 +865,7 @@ export function PQInputTabs({ - + {/* 프로젝트별 추가 필드 (contractInfo, additionalRequirement) */} {projectId && contractInfo && (
@@ -884,8 +900,12 @@ export function PQInputTabs({ return "이메일 주소"; case "PHONE": return "전화번호"; + case "FAX": + return "팩스번호"; case "NUMBER": return "숫자 값"; + case "NUMBER_WITH_UNIT": + return "숫자+단위"; case "TEXT_FILE": return "텍스트 답변"; default: @@ -905,6 +925,7 @@ export function PQInputTabs({ type="email" disabled={isDisabled} placeholder="example@company.com" + className="h-12" onChange={(e) => { field.onChange(e) form.setValue( @@ -923,6 +944,7 @@ export function PQInputTabs({ type="tel" disabled={isDisabled} placeholder="02-1234-5678" + className="h-12" onChange={(e) => { // 전화번호 형식만 허용 (숫자, -, +, 공백) const value = e.target.value; @@ -943,6 +965,7 @@ export function PQInputTabs({ type="text" disabled={isDisabled} placeholder="숫자를 입력하세요" + className="h-12" onChange={(e) => { // 숫자만 허용 const value = e.target.value; @@ -957,13 +980,60 @@ export function PQInputTabs({ }} /> ); + case "NUMBER_WITH_UNIT": + return ( +
+ { + const unit = field.value?.split(' ')[1] || '' + const newValue = e.target.value + (unit ? ` ${unit}` : '') + field.onChange(newValue) + form.setValue( + `answers.${answerIndex}.saved`, + false, + { shouldDirty: true } + ) + }} + /> + +
+ ); case "TEXT_FILE": return (