From dfdfae3018f8499240f48d28ce634f4a5c56e006 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Wed, 2 Apr 2025 09:54:08 +0000 Subject: 벤더 코멘트 처리 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/[lng]/evcp/equip-class/page.tsx | 2 +- app/[lng]/evcp/pq-criteria/[id]/page.tsx | 81 ++++++++++++++++++++ app/[lng]/evcp/pq-criteria/page.tsx | 37 +++++---- app/[lng]/evcp/pq/[vendorId]/page.tsx | 109 ++++++++++++++++++++++----- app/[lng]/evcp/vendor-candidates/page.tsx | 66 ++++++++++++++++ app/[lng]/evcp/vendor-investigation/page.tsx | 65 ++++++++++++++++ app/[lng]/partners/(partners)/info/page.tsx | 21 ++++++ app/[lng]/partners/pq/page.tsx | 62 +++++++++++++-- 8 files changed, 396 insertions(+), 47 deletions(-) create mode 100644 app/[lng]/evcp/pq-criteria/[id]/page.tsx create mode 100644 app/[lng]/evcp/vendor-candidates/page.tsx create mode 100644 app/[lng]/evcp/vendor-investigation/page.tsx create mode 100644 app/[lng]/partners/(partners)/info/page.tsx (limited to 'app') diff --git a/app/[lng]/evcp/equip-class/page.tsx b/app/[lng]/evcp/equip-class/page.tsx index fcda1c1d..375eb69e 100644 --- a/app/[lng]/evcp/equip-class/page.tsx +++ b/app/[lng]/evcp/equip-class/page.tsx @@ -38,7 +38,7 @@ export default async function IndexPage(props: IndexPageProps) { Object Class List from S-EDP

- 벤더 데이터 입력을 위한 Form 리스트입니다.{" "} + Object Class List를 확인할 수 있습니다.{" "} {/* 버튼 diff --git a/app/[lng]/evcp/pq-criteria/[id]/page.tsx b/app/[lng]/evcp/pq-criteria/[id]/page.tsx new file mode 100644 index 00000000..f040a0ca --- /dev/null +++ b/app/[lng]/evcp/pq-criteria/[id]/page.tsx @@ -0,0 +1,81 @@ +import * as React from "react" +import { type SearchParams } from "@/types/table" +import { getValidFilters } from "@/lib/data-table" +import { Skeleton } from "@/components/ui/skeleton" +import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton" +import { Shell } from "@/components/shell" +import { searchParamsCache } from "@/lib/pq/validations" +import { getPQs } from "@/lib/pq/service" +import { PqsTable } from "@/lib/pq/table/pq-table" +import { ProjectSelectorWrapper } from "@/components/pq/project-select-wrapper" +import { notFound } from "next/navigation" + +interface ProjectPageProps { + params: { id: string } + searchParams: Promise +} + +export default async function ProjectPage(props: ProjectPageProps) { + const resolvedParams = await props.params + const id = resolvedParams.id + + const projectId = parseInt(id, 10) + + // 유효하지 않은 projectId 확인 + if (isNaN(projectId)) { + notFound() + } + + const searchParams = await props.searchParams + const search = searchParamsCache.parse(searchParams) + + // filters가 없는 경우를 처리 + const validFilters = getValidFilters(search.filters) + + // 프로젝트별 PQ 데이터 가져오기 + const promises = Promise.all([ + getPQs({ + ...search, + filters: validFilters, + }, projectId, false) + ]) + + return ( + +

+
+

+ Pre-Qualification Check Sheet +

+

+ 벤더 등록을 위한, 벤더가 제출할 PQ 항목을: 프로젝트별로 관리할 수 있습니다. +

+
+ +
+ + }> + {/* */} + + + + } + > + + + + ) +} \ No newline at end of file diff --git a/app/[lng]/evcp/pq-criteria/page.tsx b/app/[lng]/evcp/pq-criteria/page.tsx index d924890d..778baa93 100644 --- a/app/[lng]/evcp/pq-criteria/page.tsx +++ b/app/[lng]/evcp/pq-criteria/page.tsx @@ -1,14 +1,13 @@ import * as React from "react" import { type SearchParams } from "@/types/table" - import { getValidFilters } from "@/lib/data-table" import { Skeleton } from "@/components/ui/skeleton" import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton" import { Shell } from "@/components/shell" - import { searchParamsCache } from "@/lib/pq/validations" import { getPQs } from "@/lib/pq/service" import { PqsTable } from "@/lib/pq/table/pq-table" +import { ProjectSelectorWrapper } from "@/components/pq/project-select-wrapper" interface IndexPageProps { searchParams: Promise @@ -17,34 +16,33 @@ interface IndexPageProps { export default async function IndexPage(props: IndexPageProps) { const searchParams = await props.searchParams const search = searchParamsCache.parse(searchParams) - + + // filters가 없는 경우를 처리 + const validFilters = getValidFilters(search.filters) + // onlyGeneral: true로 설정하여 일반 PQ 항목만 가져옴 const promises = Promise.all([ getPQs({ ...search, filters: validFilters, - }), + }, null, true) ]) return ( - -
-
-
-

- Pre-Qualification Check Sheet -

-

- 벤더 등록을 위한, 벤더가 제출할 PQ 항목을 관리할 수 있습니다. - -

-
+
+
+

+ Pre-Qualification Check Sheet +

+

+ 벤더 등록을 위한, 벤더가 제출할 PQ 항목을 관리할 수 있습니다. +

+
- }> {/* */} + } > - + ) -} +} \ No newline at end of file diff --git a/app/[lng]/evcp/pq/[vendorId]/page.tsx b/app/[lng]/evcp/pq/[vendorId]/page.tsx index cb4277f1..97c9a29a 100644 --- a/app/[lng]/evcp/pq/[vendorId]/page.tsx +++ b/app/[lng]/evcp/pq/[vendorId]/page.tsx @@ -1,38 +1,109 @@ import * as React from "react" import { Shell } from "@/components/shell" import { Skeleton } from "@/components/ui/skeleton" - import { type SearchParams } from "@/types/table" -import { getPQDataByVendorId } from "@/lib/pq/service" -import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton" +import { getPQDataByVendorId, getVendorPQsList } from "@/lib/pq/service" import { Vendor } from "@/db/schema/vendors" import { findVendorById } from "@/lib/vendors/service" -import VendorPQReviewPage from "@/components/pq/pq-review-detail" import VendorPQAdminReview from "@/components/pq/pq-review-detail" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { Badge } from "@/components/ui/badge" interface IndexPageProps { params: { - vendorId: string // Updated from 'id' to 'contractId' to match route parameter + vendorId: string } searchParams: Promise } -export default async function DocumentListPage(props: IndexPageProps) { +export default async function PQReviewPage(props: IndexPageProps) { const resolvedParams = await props.params - const vendorId = resolvedParams.vendorId // Updated from 'id' to 'contractId' - - const idAsNumber = Number(vendorId) - - const data = await getPQDataByVendorId(idAsNumber) - - const vendor: Vendor | null = await findVendorById(idAsNumber) - - // 4) 렌더링 + const vendorId = Number(resolvedParams.vendorId) + + // Fetch the vendor data + const vendor: Vendor | null = await findVendorById(vendorId) + if (!vendor) return
Vendor not found
+ + // Get list of all PQs (general + project-specific) for this vendor + const pqsList = await getVendorPQsList(vendorId) + + // Determine default active PQ to display + // If query param projectId exists, use that, otherwise use general PQ if available + const searchParams = await props.searchParams + const activeProjectId = searchParams.projectId ? Number(searchParams.projectId) : undefined + + // If no projectId query param, default to general PQ or first project PQ + const defaultTabId = activeProjectId ? + `project-${activeProjectId}` : + (pqsList.hasGeneralPq ? 'general' : `project-${pqsList.projectPQs[0]?.projectId}`) + + // Fetch PQ data for the active tab + let pqData; + if (activeProjectId) { + // Get project-specific PQ data + pqData = await getPQDataByVendorId(vendorId, activeProjectId) + } else { + // Get general PQ data + pqData = await getPQDataByVendorId(vendorId) + } + return ( - {vendor && - - } + {pqsList.hasGeneralPq || pqsList.projectPQs.length > 0 ? ( + +
+

+ {vendor.vendorName} PQ Review +

+ + + {pqsList.hasGeneralPq && ( + + General PQ Standard + + )} + + {pqsList.projectPQs.map((project) => ( + + {project.projectName} {project.status} + + ))} + +
+ + {/* Tab content for General PQ */} + {pqsList.hasGeneralPq && ( + + getPQDataByVendorId(vendorId)} + pqType="general" + /> + + )} + + {/* Tab content for each Project PQ */} + {pqsList.projectPQs.map((project) => ( + + getPQDataByVendorId(vendorId, project.projectId)} + pqType="project" + /> + + ))} +
+ ) : ( +
+

No PQ submissions found for this vendor

+
+ )}
) -} +} \ No newline at end of file diff --git a/app/[lng]/evcp/vendor-candidates/page.tsx b/app/[lng]/evcp/vendor-candidates/page.tsx new file mode 100644 index 00000000..668c0dc6 --- /dev/null +++ b/app/[lng]/evcp/vendor-candidates/page.tsx @@ -0,0 +1,66 @@ +import * as React from "react" +import { type SearchParams } from "@/types/table" + +import { getValidFilters } from "@/lib/data-table" +import { Skeleton } from "@/components/ui/skeleton" +import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton" +import { Shell } from "@/components/shell" + +import { getVendorCandidateCounts, getVendorCandidates } from "@/lib/vendor-candidates/service" +import { searchParamsCandidateCache } from "@/lib/vendor-candidates/validations" +import { VendorCandidateTable } from "@/lib/vendor-candidates/table/candidates-table" + +interface IndexPageProps { + searchParams: Promise +} + +export default async function IndexPage(props: IndexPageProps) { + const searchParams = await props.searchParams + const search = searchParamsCandidateCache.parse(searchParams) + + const validFilters = getValidFilters(search.filters) + + const promises = Promise.all([ + getVendorCandidates({ + ...search, + filters: validFilters, + }), + getVendorCandidateCounts() + ]) + + return ( + + +
+
+
+

+ Vendor Candidates Management +

+

+ 수집한 벤더 후보를 등록하고 초대 메일을 송부할 수 있습니다. + +

+
+
+
+ + + }> + + + } + > + + +
+ ) +} diff --git a/app/[lng]/evcp/vendor-investigation/page.tsx b/app/[lng]/evcp/vendor-investigation/page.tsx new file mode 100644 index 00000000..c59de869 --- /dev/null +++ b/app/[lng]/evcp/vendor-investigation/page.tsx @@ -0,0 +1,65 @@ +import * as React from "react" +import { type SearchParams } from "@/types/table" + +import { getValidFilters } from "@/lib/data-table" +import { Skeleton } from "@/components/ui/skeleton" +import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton" +import { Shell } from "@/components/shell" + +import { VendorsInvestigationTable } from "@/lib/vendor-investigation/table/investigation-table" +import { getVendorsInvestigation } from "@/lib/vendor-investigation/service" +import { searchParamsInvestigationCache } from "@/lib/vendor-investigation/validations" + +interface IndexPageProps { + searchParams: Promise +} + +export default async function IndexPage(props: IndexPageProps) { + const searchParams = await props.searchParams + const search = searchParamsInvestigationCache.parse(searchParams) + + const validFilters = getValidFilters(search.filters) + + const promises = Promise.all([ + getVendorsInvestigation({ + ...search, + filters: validFilters, + }), + ]) + + return ( + + +
+
+
+

+ Vendor Investigation Management +

+

+ 요청된 Vendor 실사에 대한 스케줄 정보를 관리하고 결과를 입력할 수 있습니다. + +

+
+
+
+ + + }> + + + } + > + + +
+ ) +} diff --git a/app/[lng]/partners/(partners)/info/page.tsx b/app/[lng]/partners/(partners)/info/page.tsx new file mode 100644 index 00000000..8215a451 --- /dev/null +++ b/app/[lng]/partners/(partners)/info/page.tsx @@ -0,0 +1,21 @@ +import { Suspense } from "react" +import { Metadata } from "next" +import { JoinFormSkeleton } from "@/components/signup/join-form-skeleton" +import { InfoForm } from "@/components/additional-info/join-form" + +// (Optional) If Next.js attempts to statically optimize this page and you need full runtime +// behavior for query params, you may also need: +// export const dynamic = "force-dynamic" + +export const metadata: Metadata = { + title: "Partner Portal", + description: "Authentication forms built using the components.", +} + +export default function IndexPage() { + return ( + }> + + + ) +} \ No newline at end of file diff --git a/app/[lng]/partners/pq/page.tsx b/app/[lng]/partners/pq/page.tsx index 8ad23f6e..42c88b21 100644 --- a/app/[lng]/partners/pq/page.tsx +++ b/app/[lng]/partners/pq/page.tsx @@ -3,11 +3,16 @@ import { authOptions } from "@/app/api/auth/[...nextauth]/route" import * as React from "react" import { Shell } from "@/components/shell" import { Skeleton } from "@/components/ui/skeleton" -import { getPQDataByVendorId } from "@/lib/pq/service" +import { getPQDataByVendorId, getPQProjectsByVendorId } from "@/lib/pq/service" import { PQInputTabs } from "@/components/pq/pq-input-tabs" +import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs" -export default async function PQInputPage() { +export default async function PQInputPage({ + searchParams +}: { + searchParams: { projectId?: string } +}) { // 세션 const session = await getServerSession(authOptions) // 예: 세션에서 vendorId 가져오기 @@ -15,25 +20,66 @@ export default async function PQInputPage() { const vendorId = 17 // 임시 const idAsNumber = Number(vendorId) - // 1) 서버에서 PQ 데이터 조회 (groupName별로 묶인 구조) - const pqData = await getPQDataByVendorId(idAsNumber) + const projectId = searchParams.projectId ? parseInt(searchParams.projectId, 10) : undefined + + // 벤더에게 요청된 프로젝트 PQ 목록 가져오기 (탭 표시용) + const projectPQs = await getPQProjectsByVendorId(idAsNumber) + + // PQ 데이터 조회 + const pqData = await getPQDataByVendorId(idAsNumber, projectId) + + // 현재 프로젝트 정보 (있다면) + const currentProject = projectId + ? projectPQs.find(p => p.projectId === projectId) + : null return ( + {/* 헤더 - 프로젝트 정보 포함 */}

Pre-Qualification Check Sheet + {currentProject && ( + + - {currentProject.projectCode} + + )}

- PQ에 적절한 응답을 제출하시기 바랍니다. 진행 중 문의가 있으면 담당자에게 연락바랍니다. + PQ에 적절한 응답을 제출하시기 바랍니다.

- {/* 클라이언트 탭 UI 로드 (Suspense는 여기서는 크게 필요치 않을 수도 있음) */} + {/* 일반/프로젝트 PQ 선택 탭 */} + {projectPQs.length > 0 && ( +
+ + + + 일반 PQ + + + {projectPQs.map(project => ( + + + {project.projectCode} + + + ))} + + +
+ )} + + {/* PQ 입력 탭 */} }> - + -
) } \ No newline at end of file -- cgit v1.2.3