summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-10-23 10:10:21 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-10-23 10:10:21 +0000
commitf7f5069a2209cfa39b65f492f32270a5f554bed0 (patch)
tree933c731ec2cb7d8bc62219a0aeed45a5e97d5f15 /app
parentd49ad5dee1e5a504e1321f6db802b647497ee9ff (diff)
(대표님) EDP 해양 관련 개발 사항들
Diffstat (limited to 'app')
-rw-r--r--app/[lng]/partners/(partners)/vendor-data-plant/form/[packageId]/[formId]/[projectId]/[contractId]/page.tsx82
-rw-r--r--app/[lng]/partners/(partners)/vendor-data-plant/layout.tsx85
-rw-r--r--app/[lng]/partners/(partners)/vendor-data-plant/page.tsx38
-rw-r--r--app/[lng]/partners/(partners)/vendor-data-plant/tag/[id]/page.tsx43
4 files changed, 248 insertions, 0 deletions
diff --git a/app/[lng]/partners/(partners)/vendor-data-plant/form/[packageId]/[formId]/[projectId]/[contractId]/page.tsx b/app/[lng]/partners/(partners)/vendor-data-plant/form/[packageId]/[formId]/[projectId]/[contractId]/page.tsx
new file mode 100644
index 00000000..00fd23da
--- /dev/null
+++ b/app/[lng]/partners/(partners)/vendor-data-plant/form/[packageId]/[formId]/[projectId]/[contractId]/page.tsx
@@ -0,0 +1,82 @@
+import DynamicTable from "@/components/form-data-plant/form-data-table";
+import { findContractItemId, getFormData, getFormId } from "@/lib/forms-plant/services";
+import { useTranslation } from "@/i18n";
+
+interface IndexPageProps {
+ params: {
+ lng: string;
+ packageId: string;
+ formId: string;
+ projectId: string;
+ contractId: string;
+ };
+ searchParams?: {
+ mode?: string;
+ };
+}
+
+export default async function FormPage({ params, searchParams }: IndexPageProps) {
+ // 1) 구조 분해 할당
+ const resolvedParams = await params;
+
+ // 2) searchParams도 await 필요
+ const resolvedSearchParams = await searchParams;
+
+ // 3) 구조 분해 할당
+ const { lng, packageId, formId: formCode, projectId, contractId } = resolvedParams;
+
+ // i18n 설정
+ const { t } = await useTranslation(lng, 'engineering');
+
+ // URL 쿼리 파라미터에서 mode 가져오기 (await 해서 사용)
+ const mode = resolvedSearchParams?.mode === "ENG" ? "ENG" : "IM"; // 기본값은 IM
+
+ // 4) 변환
+ let packageIdAsNumber = Number(packageId);
+ const contractIdAsNumber = Number(contractId);
+
+ // packageId가 0이면 contractId와 formCode로 실제 contractItemId 찾기
+ if (packageIdAsNumber === 0 && contractIdAsNumber > 0) {
+ console.log(`packageId가 0이므로 contractId ${contractIdAsNumber}와 formCode ${formCode}로 contractItemId 조회`);
+
+ const foundContractItemId = await findContractItemId(contractIdAsNumber, formCode);
+
+ if (foundContractItemId) {
+ console.log(`contractItemId ${foundContractItemId}를 찾았습니다. 이 값을 사용합니다.`);
+ packageIdAsNumber = foundContractItemId;
+ } else {
+ console.warn(`contractItemId를 찾을 수 없습니다. packageId는 계속 0으로 유지됩니다.`);
+ }
+ }
+
+ // 5) DB 조회
+ const { columns, data, editableFieldsMap } = await getFormData(formCode, packageIdAsNumber);
+
+ // 6) formId 및 report temp file 조회
+ const { formId } = await getFormId(String(packageIdAsNumber), formCode);
+
+ // 7) 예외 처리
+ if (!columns) {
+ return (
+ <p className="text-red-500">
+ {t('errors.form_meta_not_found')}
+ </p>
+ );
+ }
+
+ // 8) 렌더링
+ return (
+ <div className="space-y-6">
+ <DynamicTable
+ contractItemId={packageIdAsNumber}
+ formCode={formCode}
+ formId={formId}
+ columnsJSON={columns}
+ dataJSON={data}
+ projectId={Number(projectId)}
+ editableFieldsMap={editableFieldsMap} // 새로 추가
+ mode={mode} // 모드 전달
+ />
+ </div>
+ );
+} \ No newline at end of file
diff --git a/app/[lng]/partners/(partners)/vendor-data-plant/layout.tsx b/app/[lng]/partners/(partners)/vendor-data-plant/layout.tsx
new file mode 100644
index 00000000..d2d63c28
--- /dev/null
+++ b/app/[lng]/partners/(partners)/vendor-data-plant/layout.tsx
@@ -0,0 +1,85 @@
+// app/vendor-data-plant/layout.tsx
+import * as React from "react"
+import { cookies } from "next/headers"
+import { Shell } from "@/components/shell"
+import { getVendorProjectsAndContracts } from "@/lib/vendor-data-plant/services"
+import { VendorDataContainer } from "@/components/vendor-data-plant/vendor-data-container"
+import { authOptions } from "@/app/api/auth/[...nextauth]/route"
+import { getServerSession } from "next-auth"
+import { InformationButton } from "@/components/information/information-button"
+import { useTranslation } from "@/i18n"
+
+interface VendorDataLayoutProps {
+ children: React.ReactNode
+ params: { lng?: string }
+}
+
+// Layout 컴포넌트는 서버 컴포넌트입니다
+export default async function VendorDataLayout({
+ children,
+ params,
+}: VendorDataLayoutProps) {
+ // 기본 언어는 'ko'로 설정, params.locale이 있으면 사용
+ const { lng } = await params;
+ const language = lng || 'en'
+ const { t } = await useTranslation(language, 'engineering')
+
+ const session = await getServerSession(authOptions)
+ const vendorId = session?.user.companyId
+ // const vendorId = "17"
+ const idAsNumber = Number(vendorId)
+
+ // 프로젝트 데이터 가져오기
+ const projects = await getVendorProjectsAndContracts(idAsNumber)
+
+ // 레이아웃 설정 쿠키 가져오기
+ // Next.js 15에서는 cookies()가 Promise를 반환하므로 await 사용
+ const cookieStore = await cookies()
+
+ // 이제 cookieStore.get() 메서드 사용 가능
+ const layout = cookieStore.get("react-resizable-panels:layout:mail")
+ const collapsed = cookieStore.get("react-resizable-panels:collapsed")
+
+ const defaultLayout = layout ? JSON.parse(layout.value) : undefined
+ const defaultCollapsed = collapsed ? JSON.parse(collapsed.value) : undefined
+
+ return (
+ <Shell className="gap-2">
+ <div className="flex items-center justify-between space-y-2">
+ <div className="flex items-center justify-between space-y-2">
+ <div>
+ <div className="flex items-center gap-2">
+ <h2 className="text-2xl font-bold tracking-tight">
+ {t('layout.page_title')}
+ </h2>
+ <InformationButton pagePath="partners/vendor-data-plant" />
+ </div>
+ {/* <p className="text-muted-foreground">
+ 각종 Data 입력할 수 있습니다
+ </p> */}
+ </div>
+ </div>
+ </div>
+
+ <section className="overflow-hidden rounded-[0.5rem] border bg-background shadow">
+ <div className="hidden flex-col md:flex">
+ {projects.length === 0 ? (
+ <div className="p-4 text-center text-sm text-muted-foreground">
+ {t('layout.no_projects')}
+ </div>
+ ) : (
+ <VendorDataContainer
+ projects={projects}
+ defaultLayout={defaultLayout}
+ defaultCollapsed={defaultCollapsed}
+ navCollapsedSize={4}
+ >
+ {/* 페이지별 콘텐츠가 여기에 들어갑니다 */}
+ {children}
+ </VendorDataContainer>
+ )}
+ </div>
+ </section>
+ </Shell>
+ )
+} \ No newline at end of file
diff --git a/app/[lng]/partners/(partners)/vendor-data-plant/page.tsx b/app/[lng]/partners/(partners)/vendor-data-plant/page.tsx
new file mode 100644
index 00000000..0fbb6f0a
--- /dev/null
+++ b/app/[lng]/partners/(partners)/vendor-data-plant/page.tsx
@@ -0,0 +1,38 @@
+import * as React from "react"
+import { Separator } from "@/components/ui/separator"
+import { useTranslation } from "@/i18n"
+
+interface Props {
+ params: { lng?: string }
+}
+
+export default async function VendorDataPage({ params }: Props) {
+ // 기본 언어는 'ko'로 설정, params.lng이 있으면 사용
+ const { lng } = await params
+ const language = lng || 'en'
+ const { t } = await useTranslation(language, 'engineering')
+
+ return (
+ <div className="space-y-6">
+ <div>
+ <h3 className="text-lg font-medium">{t('layout.title')}</h3>
+ <p className="text-sm text-muted-foreground">
+ {t('layout.description')}
+ </p>
+ </div>
+ <Separator />
+ <div className="grid gap-4">
+ <div className="rounded-lg border p-4">
+ <h4 className="text-sm font-medium">{t('layout.getting_started.title')}</h4>
+ <p className="text-sm text-muted-foreground mt-1">
+ 1. {t('layout.getting_started.step1')}<br />
+ 2. {t('layout.getting_started.step2')}<br />
+ 3. {t('layout.getting_started.step3')}<br />
+ 4. {t('layout.getting_started.step4')}<br />
+ 5. {t('layout.getting_started.step5')}
+ </p>
+ </div>
+ </div>
+ </div>
+ )
+} \ No newline at end of file
diff --git a/app/[lng]/partners/(partners)/vendor-data-plant/tag/[id]/page.tsx b/app/[lng]/partners/(partners)/vendor-data-plant/tag/[id]/page.tsx
new file mode 100644
index 00000000..c5d93525
--- /dev/null
+++ b/app/[lng]/partners/(partners)/vendor-data-plant/tag/[id]/page.tsx
@@ -0,0 +1,43 @@
+import { Separator } from "@/components/ui/separator"
+import { type SearchParams } from "@/types/table"
+import { getValidFilters } from "@/lib/data-table"
+import { TagsTable } from "@/lib/tags-plant/table/tag-table"
+import { searchParamsCache } from "@/lib/tags-plant/validations"
+import { getTags } from "@/lib/tags-plant/service"
+
+interface IndexPageProps {
+ params: {
+ id: string
+ }
+ searchParams: Promise<SearchParams>
+}
+
+export default async function TagPage(props: IndexPageProps) {
+ const resolvedParams = await props.params
+ const id = resolvedParams.id
+
+ const idAsNumber = Number(id)
+
+ // 2) SearchParams 파싱 (Zod)
+ // - "filters", "page", "perPage", "sort" 등 contact 전용 컬럼
+ const searchParams = await props.searchParams
+ const search = searchParamsCache.parse(searchParams)
+ const validFilters = getValidFilters(search.filters)
+
+ const promises = Promise.all([
+ getTags({
+ ...search,
+ filters: validFilters,
+ },
+ idAsNumber)
+ ])
+
+ // 4) 렌더링
+ return (
+ <div className="space-y-6">
+ <div>
+ <TagsTable promises={promises} selectedPackageId={idAsNumber}/>
+ </div>
+ </div>
+ )
+} \ No newline at end of file