diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-29 11:48:59 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-29 11:48:59 +0000 |
| commit | 10f90dc68dec42e9a64e081cc0dce6a484447290 (patch) | |
| tree | 5bc8bb30e03b09a602e7d414d943d0e7f24b1a0f /app | |
| parent | 792fb0c21136eededecf52b5b4aa1a252bdc4bfb (diff) | |
(대표님, 박서영, 최겸) document-list-only, gtc, vendorDocu, docu-list-rule
Diffstat (limited to 'app')
3 files changed, 244 insertions, 0 deletions
diff --git a/app/[lng]/partners/(partners)/document-list-only/[contractId]/page.tsx b/app/[lng]/partners/(partners)/document-list-only/[contractId]/page.tsx new file mode 100644 index 00000000..e3cda9b4 --- /dev/null +++ b/app/[lng]/partners/(partners)/document-list-only/[contractId]/page.tsx @@ -0,0 +1,175 @@ +// document-stages-management-page.tsx +import { Suspense } from "react" +import { Separator } from "@/components/ui/separator" +import { Skeleton } from "@/components/ui/skeleton" +import { type SearchParams } from "@/types/table" +import { getValidFilters } from "@/lib/data-table" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Progress } from "@/components/ui/progress" +import { + TrendingUp, + AlertTriangle, + Clock, + Target, + FileText, + Settings, + Download +} from "lucide-react" +import { Button } from "@/components/ui/button" +import { DocumentStagesTable } from "@/lib/vendor-document-list/plant/document-stages-table" +import { documentStageSearchParamsCache } from "@/lib/vendor-document-list/plant/document-stage-validations" +import { getDocumentProgressStats, getDocumentStagesOnly } from "@/lib/vendor-document-list/plant/document-stages-service" + +interface DocumentStagesManagementPageProps { + params: { + contractId: string + } + searchParams: Promise<SearchParams> +} + + +// 전체 진행률 카드 컴포넌트 +async function ProgressCard({ contractId }: { contractId: number }) { + const stats = await getDocumentProgressStats(contractId) + + return ( + <Card> + <CardHeader> + <div className="flex items-center justify-between"> + <CardTitle className="text-lg">전체 진행률</CardTitle> + <div className="flex items-center gap-2"> + </div> + </div> + </CardHeader> + <CardContent> + <div className="space-y-4"> + <div className="flex justify-between text-sm"> + <span>평균 진행률</span> + <span className="font-medium">{stats.avgProgress}%</span> + </div> + <Progress value={stats.avgProgress} className="w-full" /> + + <div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm"> + <div className="text-center p-2 bg-gray-50 rounded"> + <div className="font-medium text-lg text-gray-900">{stats.totalDocuments}</div> + <div className="text-gray-600">전체</div> + </div> + <div className="text-center p-2 bg-green-50 rounded"> + <div className="font-medium text-lg text-green-600">{stats.completedDocuments}</div> + <div className="text-green-600">완료</div> + </div> + <div className="text-center p-2 bg-blue-50 rounded"> + <div className="font-medium text-lg text-blue-600">{stats.inProgressDocuments}</div> + <div className="text-blue-600">진행중</div> + </div> + <div className="text-center p-2 bg-red-50 rounded"> + <div className="font-medium text-lg text-red-600">{stats.overdueDocuments}</div> + <div className="text-red-600">지연</div> + </div> + </div> + </div> + </CardContent> + </Card> + ) +} + +// 문서 테이블 래퍼 컴포넌트 +async function DocumentTableWrapper({ + contractId, + searchParams, + projectType +}: { + contractId: number + searchParams: any + projectType: "ship" | "plant" +}) { + const search = documentStageSearchParamsCache.parse(searchParams) + const validFilters = getValidFilters(search.filters) + + const documentsPromise = getDocumentStagesOnly({ + ...search, + filters: validFilters, + }, contractId) + + return ( + <DocumentStagesTable + promises={Promise.all([documentsPromise])} + contractId={contractId} + projectType={projectType} + initialDrawingKind={search.drawingKind} + /> + ) +} + + + +function TableLoadingSkeleton() { + return ( + <div className="space-y-4"> + <div className="flex items-center justify-between"> + <Skeleton className="h-6 w-32" /> + <div className="flex items-center gap-2"> + <Skeleton className="h-8 w-20" /> + <Skeleton className="h-8 w-24" /> + </div> + </div> + <div className="rounded-md border"> + <div className="p-4"> + <div className="space-y-3"> + {Array.from({ length: 5 }).map((_, i) => ( + <div key={i} className="flex items-center space-x-4"> + <Skeleton className="h-4 w-4" /> + <Skeleton className="h-4 w-24" /> + <Skeleton className="h-4 w-48" /> + <Skeleton className="h-4 w-20" /> + <Skeleton className="h-4 w-16" /> + <Skeleton className="h-4 w-12" /> + </div> + ))} + </div> + </div> + </div> + </div> + ) +} + +// 메인 페이지 컴포넌트 +export default async function DocumentStagesManagementPage({ + params, + searchParams +}: DocumentStagesManagementPageProps) { + const resolvedParams = await params + const contractId = Number(resolvedParams.contractId) + const resolvedSearchParams = await searchParams + + // 프로젝트 타입 결정 (실제로는 계약 정보에서 가져와야 함) + const projectType = resolvedSearchParams.projectType === "plant" ? "plant" : "ship" + + if (isNaN(contractId)) { + throw new Error("유효하지 않은 계약 ID입니다") + } + + return ( + <div className=" mx-auto"> + + {/* 문서 테이블 */} + {/* <div className="space-y-4"> */} + {/* <div className="flex items-center justify-between"> + <h2 className="text-xl font-semibold">문서 목록</h2> + <div className="text-sm text-muted-foreground"> + 스테이지 상태별로 문서를 관리하고 진행 상황을 추적할 수 있습니다 + </div> + </div> */} + + <Suspense fallback={<TableLoadingSkeleton />}> + <DocumentTableWrapper + contractId={contractId} + searchParams={resolvedSearchParams} + projectType={projectType} + /> + </Suspense> + {/* </div> */} + + </div> + ) +}
\ No newline at end of file diff --git a/app/[lng]/partners/(partners)/document-list-only/layout.tsx b/app/[lng]/partners/(partners)/document-list-only/layout.tsx new file mode 100644 index 00000000..8d486113 --- /dev/null +++ b/app/[lng]/partners/(partners)/document-list-only/layout.tsx @@ -0,0 +1,48 @@ + +import { cookies } from "next/headers" +import { Shell } from "@/components/shell" +import DocumentContainer from "@/components/documents/document-container" +import { getVendorProjectsAndContracts } from "@/lib/vendor-data/services" +import { getVendorDocumentLists } from "@/lib/vendor-document/service" +import VendorDocumentsClient from "@/components/documents/vendor-docs.client" +import VendorDocumentListClient from "@/components/document-lists/vendor-doc-list-client" +import { authOptions } from "@/app/api/auth/[...nextauth]/route"; +import { getServerSession } from "next-auth"; + + + +// Layout 컴포넌트는 서버 컴포넌트입니다 +export default async function VendorDocuments({ + children, +}: { + children: React.ReactNode +}) { + const session = await getServerSession(authOptions) + const vendorId = session?.user.companyId + // const vendorId = "17" + const idAsNumber = Number(vendorId) + + const projects = await getVendorProjectsAndContracts(idAsNumber); + const filteredProjects = projects.filter(v=>v.projectType === "plant") + + + // 레이아웃 설정 쿠키 가져오기 + // 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"> + <VendorDocumentListClient projects={filteredProjects}> + {children} + </VendorDocumentListClient> + </Shell> + ) +}
\ No newline at end of file diff --git a/app/[lng]/partners/(partners)/document-list-only/page.tsx b/app/[lng]/partners/(partners)/document-list-only/page.tsx new file mode 100644 index 00000000..721eb408 --- /dev/null +++ b/app/[lng]/partners/(partners)/document-list-only/page.tsx @@ -0,0 +1,21 @@ +// app/vendor-data/page.tsx +import * as React from "react" +import { Separator } from "@/components/ui/separator" + +export default async function IndexPage() { + return ( + <div className="space-y-6"> + + <div className="grid gap-4"> + <div className="rounded-lg border p-4"> + <h4 className="text-sm font-medium">시작하는 방법</h4> + <p className="text-sm text-muted-foreground mt-1"> + 오른쪽 상단에서 프로젝트/계약을 선택하세요.<br /> + + + </p> + </div> + </div> + </div> + ) +}
\ No newline at end of file |
