From 53ad72732f781e6c6d5ddb3776ea47aec010af8e Mon Sep 17 00:00:00 2001 From: dujinkim Date: Mon, 4 Aug 2025 09:39:21 +0000 Subject: (최겸) PQ/실사 수정 및 개발 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/mail/templates/audit-result-notice.hbs | 164 + lib/mail/templates/non-inspection-pq.hbs | 200 + lib/mail/templates/pq.hbs | 198 +- lib/mail/templates/site-visit-request.hbs | 260 + lib/pq/helper.ts | 190 +- lib/pq/pq-criteria/add-pq-dialog.tsx | 344 + lib/pq/pq-criteria/delete-pqs-dialog.tsx | 149 + lib/pq/pq-criteria/import-pq-button.tsx | 270 + lib/pq/pq-criteria/import-pq-handler.tsx | 142 + lib/pq/pq-criteria/pq-excel-template.tsx | 205 + lib/pq/pq-criteria/pq-table-column.tsx | 232 + lib/pq/pq-criteria/pq-table-toolbar-actions.tsx | 86 + lib/pq/pq-criteria/pq-table.tsx | 127 + lib/pq/pq-criteria/update-pq-sheet.tsx | 330 + .../cancel-investigation-dialog.tsx | 136 +- .../edit-investigation-dialog.tsx | 217 + .../pq-review-table-new/feature-flags-provider.tsx | 216 +- lib/pq/pq-review-table-new/pq-container.tsx | 300 +- lib/pq/pq-review-table-new/pq-filter-sheet.tsx | 1300 ++-- .../request-investigation-dialog.tsx | 667 +- lib/pq/pq-review-table-new/send-results-dialog.tsx | 279 +- lib/pq/pq-review-table-new/site-visit-dialog.tsx | 711 +++ lib/pq/pq-review-table-new/user-combobox.tsx | 242 +- .../pq-review-table-new/vendors-table-columns.tsx | 1425 +++-- .../vendors-table-toolbar-actions.tsx | 756 +-- lib/pq/pq-review-table-new/vendors-table.tsx | 772 ++- lib/pq/pq-review-table/vendors-table-columns.tsx | 4 +- lib/pq/service.ts | 6559 +++++++++++--------- lib/pq/table/add-pq-dialog.tsx | 454 -- lib/pq/table/add-pq-list-dialog.tsx | 231 + lib/pq/table/copy-pq-list-dialog.tsx | 244 + lib/pq/table/delete-pq-list-dialog.tsx | 139 + lib/pq/table/delete-pqs-dialog.tsx | 149 - lib/pq/table/import-pq-button.tsx | 270 - lib/pq/table/import-pq-handler.tsx | 145 - lib/pq/table/pq-excel-template.tsx | 205 - lib/pq/table/pq-lists-columns.tsx | 216 + lib/pq/table/pq-lists-table.tsx | 170 + lib/pq/table/pq-lists-toolbar.tsx | 61 + lib/pq/table/pq-table-column.tsx | 185 - lib/pq/table/pq-table-toolbar-actions.tsx | 87 - lib/pq/table/pq-table.tsx | 127 - lib/pq/table/update-pq-sheet.tsx | 264 - lib/pq/validations.ts | 199 +- lib/site-visit/client-site-visit-wrapper.tsx | 474 ++ lib/site-visit/service.ts | 668 ++ lib/site-visit/shi-attendees-dialog.tsx | 152 + lib/site-visit/site-visit-detail-dialog.tsx | 266 + lib/site-visit/vendor-info-sheet.tsx | 442 ++ lib/site-visit/vendor-info-view-dialog.tsx | 279 + 50 files changed, 14096 insertions(+), 7812 deletions(-) create mode 100644 lib/mail/templates/audit-result-notice.hbs create mode 100644 lib/mail/templates/non-inspection-pq.hbs create mode 100644 lib/mail/templates/site-visit-request.hbs create mode 100644 lib/pq/pq-criteria/add-pq-dialog.tsx create mode 100644 lib/pq/pq-criteria/delete-pqs-dialog.tsx create mode 100644 lib/pq/pq-criteria/import-pq-button.tsx create mode 100644 lib/pq/pq-criteria/import-pq-handler.tsx create mode 100644 lib/pq/pq-criteria/pq-excel-template.tsx create mode 100644 lib/pq/pq-criteria/pq-table-column.tsx create mode 100644 lib/pq/pq-criteria/pq-table-toolbar-actions.tsx create mode 100644 lib/pq/pq-criteria/pq-table.tsx create mode 100644 lib/pq/pq-criteria/update-pq-sheet.tsx create mode 100644 lib/pq/pq-review-table-new/edit-investigation-dialog.tsx create mode 100644 lib/pq/pq-review-table-new/site-visit-dialog.tsx delete mode 100644 lib/pq/table/add-pq-dialog.tsx create mode 100644 lib/pq/table/add-pq-list-dialog.tsx create mode 100644 lib/pq/table/copy-pq-list-dialog.tsx create mode 100644 lib/pq/table/delete-pq-list-dialog.tsx delete mode 100644 lib/pq/table/delete-pqs-dialog.tsx delete mode 100644 lib/pq/table/import-pq-button.tsx delete mode 100644 lib/pq/table/import-pq-handler.tsx delete mode 100644 lib/pq/table/pq-excel-template.tsx create mode 100644 lib/pq/table/pq-lists-columns.tsx create mode 100644 lib/pq/table/pq-lists-table.tsx create mode 100644 lib/pq/table/pq-lists-toolbar.tsx delete mode 100644 lib/pq/table/pq-table-column.tsx delete mode 100644 lib/pq/table/pq-table-toolbar-actions.tsx delete mode 100644 lib/pq/table/pq-table.tsx delete mode 100644 lib/pq/table/update-pq-sheet.tsx create mode 100644 lib/site-visit/client-site-visit-wrapper.tsx create mode 100644 lib/site-visit/service.ts create mode 100644 lib/site-visit/shi-attendees-dialog.tsx create mode 100644 lib/site-visit/site-visit-detail-dialog.tsx create mode 100644 lib/site-visit/vendor-info-sheet.tsx create mode 100644 lib/site-visit/vendor-info-view-dialog.tsx (limited to 'lib') diff --git a/lib/mail/templates/audit-result-notice.hbs b/lib/mail/templates/audit-result-notice.hbs new file mode 100644 index 00000000..1e5f7c65 --- /dev/null +++ b/lib/mail/templates/audit-result-notice.hbs @@ -0,0 +1,164 @@ + + + + + + {{subject}} + + + +
+

{{subject}}

+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{#if additionalNotes}} + + + + + {{/if}} +
PQ No.{{pqNumber}}
Vendor{{vendorCode}} | {{vendorName}}
수신자{{recipientName}} / {{recipientEmail}}
실사품목{{auditItem}}
실사공장주소{{auditFactoryAddress}}
실사방법{{auditMethod}}
실사결과 + {{auditResult}} +
추가 Comment{{additionalNotes}}
+
+ + + +
+

{{senderName}} / Procurement Manager / {{senderEmail}}

+
+ SAMSUNG HEAVY INDUSTRIES CO., LTD.
+ 80, Jangpyeong 3-ro, Geoje-si, Gyeongsangnam-do, Republic of Korea, 53261 +
+
+
+ + + + \ No newline at end of file diff --git a/lib/mail/templates/non-inspection-pq.hbs b/lib/mail/templates/non-inspection-pq.hbs new file mode 100644 index 00000000..add5396b --- /dev/null +++ b/lib/mail/templates/non-inspection-pq.hbs @@ -0,0 +1,200 @@ + + + + + + 미실사 PQ 요청 + + + +
+
+ +
미실사 PQ 요청
+
Non-Inspection Pre-Qualification Request
+
+ +
+

안녕하세요, {{vendorName}}

+ +

SHI에서 미실사 PQ(Pre-Qualification) 요청을 보냅니다.

+ +
+
+ PQ 번호: + {{pqNumber}} +
+
+ 요청자: + {{senderName}} +
+ {{#if dueDate}} +
+ 제출 마감일: + {{dueDate}} +
+ {{/if}} +
+ +
+
📋 미실사 PQ란?
+

미실사 PQ는 현장 방문 없이 서류 검토만으로 진행되는 사전 자격 검증입니다. + 일반 PQ와 동일한 기준으로 평가되지만, 현장 실사 과정이 생략됩니다.

+
+ + {{#if pqItems}} +
+
🎯 PQ 대상 품목
+
+ {{pqItems}} +
+
+ {{/if}} + + {{#if contracts.length}} +
+
📄 포함된 계약 항목
+
+ {{#each contracts}} +
• {{this}}
+ {{/each}} +
+
+ {{/if}} + + {{#if extraNote}} +
+
📝 추가 안내사항
+
+ {{extraNote}} +
+
+ {{/if}} + +
+
🚀 PQ 제출하기
+

아래 버튼을 클릭하여 미실사 PQ를 제출하세요:

+ PQ 제출하기 +
+ +
+
⚠️ 중요 안내
+
    +
  • 미실사 PQ는 서류 검토만으로 진행되므로, 모든 서류를 정확히 작성해주세요.
  • +
  • 제출 후에는 수정이 제한될 수 있으니 신중하게 작성해주세요.
  • +
  • 문의사항이 있으시면 언제든 연락주세요.
  • +
+
+
+ + +
+ + \ No newline at end of file diff --git a/lib/mail/templates/pq.hbs b/lib/mail/templates/pq.hbs index a8876eeb..0f54adb1 100644 --- a/lib/mail/templates/pq.hbs +++ b/lib/mail/templates/pq.hbs @@ -1,90 +1,120 @@ - + - - - eVCP 메일 - + + + eVCP PQ 초대 + -
- - - - -
- eVCP -
- -

- eVCP PQ 초대 -

- -

- {{vendorName}} 귀하, -

- -

- 귀사를 저희 업체 데이터베이스에 사전적격심사(PQ) 정보를 제출하도록 초대합니다. 이 과정을 완료하면 향후 프로젝트 및 조달 기회에 귀사가 고려될 수 있습니다. -

- -

- PQ 정보 제출 방법: -

- -
    -
  1. 아래 버튼을 클릭하여 저희 업체 포털에 접속하세요
  2. -
  3. 계정에 로그인하세요 (아직 계정이 없으면 등록하세요)
  4. -
  5. 대시보드에서 PQ 섹션으로 이동하세요
  6. -
  7. 귀사, 역량 및 경험에 관한 모든 필수 정보를 작성하세요
  8. -
- -

- - 업체 포털 접속 - -

- -

- 시스템에 최신 PQ 정보를 유지하는 것은 향후 기회에 귀사가 고려되기 위해 필수적입니다. -

- -

- 문의사항이 있거나 도움이 필요하시면 저희 업체 관리팀에 문의해 주세요. -

- -

- 귀사에 대해 더 알아보고 향후 프로젝트에서 함께 일할 수 있기를 기대합니다. -

- -

- 감사합니다,
- eVCP 팀 -

- - - - - -
-

© {{currentYear}} EVCP. {{t "email.vendor.invitation.copyright"}}

-

{{t "email.vendor.invitation.no_reply"}}

-
+ - \ No newline at end of file + diff --git a/lib/mail/templates/site-visit-request.hbs b/lib/mail/templates/site-visit-request.hbs new file mode 100644 index 00000000..6b2c3a2a --- /dev/null +++ b/lib/mail/templates/site-visit-request.hbs @@ -0,0 +1,260 @@ + + + + + + 방문실사 요청 + + + + + + \ No newline at end of file diff --git a/lib/pq/helper.ts b/lib/pq/helper.ts index 16aed0e4..efd50714 100644 --- a/lib/pq/helper.ts +++ b/lib/pq/helper.ts @@ -1,96 +1,96 @@ -import { - vendorPQSubmissions, - vendors, - projects, - users, - vendorInvestigations -} from "@/db/schema" -import { CustomColumnMapping } from "../filter-columns" - -/** - * Helper function to create custom column mapping for PQ submissions - */ -export function createPQFilterMapping(): CustomColumnMapping { - return { - // PQ 제출 관련 - pqNumber: { table: vendorPQSubmissions, column: "pqNumber" }, - status: { table: vendorPQSubmissions, column: "status" }, - type: { table: vendorPQSubmissions, column: "type" }, - createdAt: { table: vendorPQSubmissions, column: "createdAt" }, - updatedAt: { table: vendorPQSubmissions, column: "updatedAt" }, - submittedAt: { table: vendorPQSubmissions, column: "submittedAt" }, - approvedAt: { table: vendorPQSubmissions, column: "approvedAt" }, - rejectedAt: { table: vendorPQSubmissions, column: "rejectedAt" }, - - // 협력업체 관련 - vendorName: { table: vendors, column: "vendorName" }, - vendorCode: { table: vendors, column: "vendorCode" }, - taxId: { table: vendors, column: "taxId" }, - vendorStatus: { table: vendors, column: "status" }, - - // 프로젝트 관련 - projectName: { table: projects, column: "name" }, - projectCode: { table: projects, column: "code" }, - - // 요청자 관련 - requesterName: { table: users, column: "name" }, - requesterEmail: { table: users, column: "email" }, - - // 실사 관련 - evaluationResult: { table: vendorInvestigations, column: "evaluationResult" }, - evaluationType: { table: vendorInvestigations, column: "evaluationType" }, - investigationStatus: { table: vendorInvestigations, column: "investigationStatus" }, - investigationAddress: { table: vendorInvestigations, column: "investigationAddress" }, - qmManagerId: { table: vendorInvestigations, column: "qmManagerId" }, - } -} - -/** - * PQ 관련 조인 테이블들 - */ -export function getPQJoinedTables() { - return { - vendors, - projects, - users, - vendorInvestigations, - } -} - -/** - * 직접 컬럼 참조 방식의 매핑 (더 타입 안전) - */ -export function createPQDirectColumnMapping(): CustomColumnMapping { - return { - // PQ 제출 관련 - 직접 컬럼 참조 - pqNumber: vendorPQSubmissions.pqNumber, - status: vendorPQSubmissions.status, - type: vendorPQSubmissions.type, - createdAt: vendorPQSubmissions.createdAt, - updatedAt: vendorPQSubmissions.updatedAt, - submittedAt: vendorPQSubmissions.submittedAt, - approvedAt: vendorPQSubmissions.approvedAt, - rejectedAt: vendorPQSubmissions.rejectedAt, - - // 협력업체 관련 - vendorName: vendors.vendorName, - vendorCode: vendors.vendorCode, - taxId: vendors.taxId, - vendorStatus: vendors.status, - - // 프로젝트 관련 - projectName: projects.name, - projectCode: projects.code, - - // 요청자 관련 - requesterName: users.name, - requesterEmail: users.email, - - // 실사 관련 - evaluationResult: vendorInvestigations.evaluationResult, - evaluationType: vendorInvestigations.evaluationType, - investigationStatus: vendorInvestigations.investigationStatus, - investigationAddress: vendorInvestigations.investigationAddress, - qmManagerId: vendorInvestigations.qmManagerId, - } +import { + vendorPQSubmissions, + vendors, + projects, + users, + vendorInvestigations +} from "@/db/schema" +import { CustomColumnMapping } from "../filter-columns" + +/** + * Helper function to create custom column mapping for PQ submissions + */ +export function createPQFilterMapping(): CustomColumnMapping { + return { + // PQ 제출 관련 + pqNumber: { table: vendorPQSubmissions, column: "pqNumber" }, + status: { table: vendorPQSubmissions, column: "status" }, + type: { table: vendorPQSubmissions, column: "type" }, + createdAt: { table: vendorPQSubmissions, column: "createdAt" }, + updatedAt: { table: vendorPQSubmissions, column: "updatedAt" }, + submittedAt: { table: vendorPQSubmissions, column: "submittedAt" }, + approvedAt: { table: vendorPQSubmissions, column: "approvedAt" }, + rejectedAt: { table: vendorPQSubmissions, column: "rejectedAt" }, + + // 협력업체 관련 + vendorName: { table: vendors, column: "vendorName" }, + vendorCode: { table: vendors, column: "vendorCode" }, + taxId: { table: vendors, column: "taxId" }, + vendorStatus: { table: vendors, column: "status" }, + + // 프로젝트 관련 + projectName: { table: projects, column: "name" }, + projectCode: { table: projects, column: "code" }, + + // 요청자 관련 + requesterName: { table: users, column: "name" }, + requesterEmail: { table: users, column: "email" }, + + // 실사 관련 + evaluationResult: { table: vendorInvestigations, column: "evaluationResult" }, + evaluationType: { table: vendorInvestigations, column: "evaluationType" }, + investigationStatus: { table: vendorInvestigations, column: "investigationStatus" }, + investigationAddress: { table: vendorInvestigations, column: "investigationAddress" }, + qmManagerId: { table: vendorInvestigations, column: "qmManagerId" }, + } +} + +/** + * PQ 관련 조인 테이블들 + */ +export function getPQJoinedTables() { + return { + vendors, + projects, + users, + vendorInvestigations, + } +} + +/** + * 직접 컬럼 참조 방식의 매핑 (더 타입 안전) + */ +export function createPQDirectColumnMapping(): CustomColumnMapping { + return { + // PQ 제출 관련 - 직접 컬럼 참조 + pqNumber: vendorPQSubmissions.pqNumber, + status: vendorPQSubmissions.status, + type: vendorPQSubmissions.type, + createdAt: vendorPQSubmissions.createdAt, + updatedAt: vendorPQSubmissions.updatedAt, + submittedAt: vendorPQSubmissions.submittedAt, + approvedAt: vendorPQSubmissions.approvedAt, + rejectedAt: vendorPQSubmissions.rejectedAt, + + // 협력업체 관련 + vendorName: vendors.vendorName, + vendorCode: vendors.vendorCode, + taxId: vendors.taxId, + vendorStatus: vendors.status, + + // 프로젝트 관련 + projectName: projects.name, + projectCode: projects.code, + + // 요청자 관련 + requesterName: users.name, + requesterEmail: users.email, + + // 실사 관련 + evaluationResult: vendorInvestigations.evaluationResult, + evaluationType: vendorInvestigations.evaluationType, + investigationStatus: vendorInvestigations.investigationStatus, + investigationAddress: vendorInvestigations.investigationAddress, + qmManagerId: vendorInvestigations.qmManagerId, + } } \ No newline at end of file diff --git a/lib/pq/pq-criteria/add-pq-dialog.tsx b/lib/pq/pq-criteria/add-pq-dialog.tsx new file mode 100644 index 00000000..53fe28f1 --- /dev/null +++ b/lib/pq/pq-criteria/add-pq-dialog.tsx @@ -0,0 +1,344 @@ +"use client" + +import * as React from "react" +import { useForm } from "react-hook-form" +import { zodResolver } from "@hookform/resolvers/zod" +import { z } from "zod" +import { Plus } from "lucide-react" +import { useRouter } from "next/navigation" + +import { + Dialog, + DialogTrigger, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, + DialogFooter +} from "@/components/ui/dialog" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Textarea } from "@/components/ui/textarea" +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" + +import { useToast } from "@/hooks/use-toast" +import { createPqCriteria } from "../service" + +// PQ 생성을 위한 Zod 스키마 정의 +const createPqSchema = z.object({ + code: z.string().min(1, "Code is required"), + checkPoint: z.string().min(1, "Check point is required"), + groupName: z.string().min(1, "Group is required"), + subGroupName: z.string().min(1, "Sub group is required"), + description: z.string().optional(), + remarks: z.string().optional(), + inputFormat: z.string().default("TEXT"), + +}); + +type CreatePqFormType = z.infer; + +// 그룹 이름 옵션 +export const groupOptions = [ + "GENERAL", + "QMS", + "Warranty", + "HSE+", + "기타", +]; + +// 입력 형식 옵션 +const inputFormatOptions = [ + { value: "TEXT", label: "텍스트" }, + { value: "FILE", label: "파일" }, + { value: "EMAIL", label: "이메일" }, + { value: "PHONE", label: "전화번호" }, + { value: "NUMBER", label: "숫자" }, + { value: "TEXT_FILE", label: "텍스트 + 파일" }, +]; + +interface AddPqDialogProps { + pqListId: number; +} + +export function AddPqDialog({ pqListId }: AddPqDialogProps) { + const [open, setOpen] = React.useState(false) + const [isSubmitting, setIsSubmitting] = React.useState(false) + const router = useRouter() + const { toast } = useToast() + + // react-hook-form 설정 + const form = useForm({ + resolver: zodResolver(createPqSchema), + defaultValues: { + code: "", + checkPoint: "", + groupName: groupOptions[0], + subGroupName: "", + description: "", + remarks: "", + inputFormat: "TEXT", + + }, + }) + const formState = form.formState + + async function onSubmit(data: CreatePqFormType) { + try { + setIsSubmitting(true) + + // 서버 액션 호출 + const result = await createPqCriteria(pqListId, data) + + if (!result.success) { + toast({ + title: "오류", + description: result.message || "PQ 항목 생성에 실패했습니다", + variant: "destructive", + }) + return + } + + // 성공 시 처리 + toast({ + title: "성공", + description: result.message || "PQ 항목이 성공적으로 생성되었습니다", + }) + + // 모달 닫고 폼 리셋 + form.reset() + setOpen(false) + + // 페이지 새로고침 + router.refresh() + + } catch (error) { + console.error('Error creating PQ criteria:', error) + toast({ + title: "오류", + description: "예상치 못한 오류가 발생했습니다", + variant: "destructive", + }) + } finally { + setIsSubmitting(false) + } + } + + function handleDialogOpenChange(nextOpen: boolean) { + if (!nextOpen) { + form.reset() + } + setOpen(nextOpen) + } + + return ( + + + + + + + + PQ 항목 생성 + + 새 PQ 항목을 추가합니다. + + + +
+ +
+ {/* Group Name 필드 */} + ( + + 대분류 * + + + + )} + /> + + {/* Sub Group Name 필드 */} + ( + + 소분류 * + + + + + 세부 분류를 위한 서브 그룹명을 입력하세요 (선택사항) + + + + )} + /> + {/* Code 필드 */} + ( + + 일련번호 * + + + + + PQ 항목의 고유 코드를 입력하세요 + + + + )} + /> + {/* Check Point 필드 */} + ( + + PQ 항목 * + + + + + + )} + /> + + {/* Input Format 필드 */} + ( + + 협력업체 입력사항 * + + + + )} + /> + + {/* Description 필드 */} + ( + + 설명 + +