diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-06-15 04:41:55 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-06-15 04:41:55 +0000 |
| commit | 7305a614ca20d50e6ab50bbcfbb128a6f1f90e53 (patch) | |
| tree | 92317f538a4368fa54447ef25fc3ad3616f1b349 /app | |
| parent | c5002d77087b256599b174ada611621657fcc523 (diff) | |
(최겸) 기술영업 벤더 개발
Diffstat (limited to 'app')
5 files changed, 315 insertions, 0 deletions
diff --git a/app/[lng]/evcp/(evcp)/tech-vendors/[id]/info/items/page.tsx b/app/[lng]/evcp/(evcp)/tech-vendors/[id]/info/items/page.tsx new file mode 100644 index 00000000..69c36576 --- /dev/null +++ b/app/[lng]/evcp/(evcp)/tech-vendors/[id]/info/items/page.tsx @@ -0,0 +1,48 @@ +// import { Separator } from "@/components/ui/separator" +// import { getTechVendorById, getVendorItemsByType } from "@/lib/tech-vendors/service" +// import { type SearchParams } from "@/types/table" +// import { TechVendorItemsTable } from "@/lib/tech-vendors/items-table/item-table" + +// interface IndexPageProps { +// // Next.js 13 App Router에서 기본으로 주어지는 객체들 +// params: { +// lng: string +// id: string +// } +// searchParams: Promise<SearchParams> +// } + +// export default async function TechVendorItemsPage(props: IndexPageProps) { +// const resolvedParams = await props.params +// const id = resolvedParams.id + +// const idAsNumber = Number(id) + +// // 벤더 정보 가져오기 (벤더 타입 필요) +// const vendorInfo = await getTechVendorById(idAsNumber) +// const vendorType = vendorInfo.data?.techVendorType || "조선" + +// const promises = getVendorItemsByType(idAsNumber, vendorType) + +// // 4) 렌더링 +// return ( +// <div className="space-y-6"> +// <div> +// <h3 className="text-lg font-medium"> +// 공급품목 +// </h3> +// <p className="text-sm text-muted-foreground"> +// 기술영업 벤더의 공급 가능한 품목을 확인하세요. +// </p> +// </div> +// <Separator /> +// <div> +// <TechVendorItemsTable +// promises={promises} +// vendorId={idAsNumber} +// vendorType={vendorType} +// /> +// </div> +// </div> +// ) +// }
\ No newline at end of file diff --git a/app/[lng]/evcp/(evcp)/tech-vendors/[id]/info/layout.tsx b/app/[lng]/evcp/(evcp)/tech-vendors/[id]/info/layout.tsx new file mode 100644 index 00000000..7c389720 --- /dev/null +++ b/app/[lng]/evcp/(evcp)/tech-vendors/[id]/info/layout.tsx @@ -0,0 +1,82 @@ +import { Metadata } from "next" + +import { Separator } from "@/components/ui/separator" +import { SidebarNav } from "@/components/layout/sidebar-nav" +import { findTechVendorById } from "@/lib/tech-vendors/service" +import { TechVendor } from "@/db/schema/techVendors" +import { Button } from "@/components/ui/button" +import { ArrowLeft } from "lucide-react" +import Link from "next/link" +export const metadata: Metadata = { + title: "Tech Vendor Detail", +} + +export default async function SettingsLayout({ + children, + params, +}: { + children: React.ReactNode + params: { lng: string , id: string} +}) { + + // 1) URL 파라미터에서 id 추출, Number로 변환 + const resolvedParams = await params + const lng = resolvedParams.lng + const id = resolvedParams.id + + const idAsNumber = Number(id) + // 2) DB에서 해당 협력업체 정보 조회 + const vendor: TechVendor | null = await findTechVendorById(idAsNumber) + + // 3) 사이드바 메뉴 + const sidebarNavItems = [ + { + title: "연락처", + href: `/${lng}/evcp/tech-vendors/${id}/info`, + }, + // { + // title: "자재 리스트", + // href: `/${lng}/evcp/tech-vendors/${id}/info/items`, + // }, + // { + // title: "견적 히스토리", + // href: `/${lng}/evcp/tech-vendors/${id}/info/rfq-history`, + // }, + ] + + return ( + <> + <div className="container py-6"> + <section className="overflow-hidden rounded-[0.5rem] border bg-background shadow"> + <div className="hidden space-y-6 p-10 pb-16 md:block"> + {/* RFQ 목록으로 돌아가는 링크 추가 */} + <div className="flex items-center justify-end mb-4"> + <Link href={`/${lng}/evcp/tech-vendors`} passHref> + <Button variant="ghost" className="flex items-center text-primary hover:text-primary/80 transition-colors p-0 h-auto"> + <ArrowLeft className="mr-1 h-4 w-4" /> + <span>기술영업 벤더 목록으로 돌아가기</span> + </Button> + </Link> + </div> + <div className="space-y-0.5"> + {/* 4) 협력업체 정보가 있으면 코드 + 이름 + "상세 정보" 표기 */} + <h2 className="text-2xl font-bold tracking-tight"> + {vendor + ? `${vendor.vendorCode ?? ""} - ${vendor.vendorName} 상세 정보` + : "Loading Vendor..."} + </h2> + <p className="text-muted-foreground">기술영업 벤더 관련 상세사항을 확인하세요.</p> + </div> + <Separator className="my-6" /> + <div className="flex flex-col space-y-8 lg:flex-row lg:space-x-12 lg:space-y-0"> + <aside className="-mx-4 lg:w-1/5"> + <SidebarNav items={sidebarNavItems} /> + </aside> + <div className="flex-1">{children}</div> + </div> + </div> + </section> + </div> + </> + ) +}
\ No newline at end of file diff --git a/app/[lng]/evcp/(evcp)/tech-vendors/[id]/info/page.tsx b/app/[lng]/evcp/(evcp)/tech-vendors/[id]/info/page.tsx new file mode 100644 index 00000000..a57d6df7 --- /dev/null +++ b/app/[lng]/evcp/(evcp)/tech-vendors/[id]/info/page.tsx @@ -0,0 +1,55 @@ +import { Separator } from "@/components/ui/separator" +import { getTechVendorContacts } from "@/lib/tech-vendors/service" +import { type SearchParams } from "@/types/table" +import { getValidFilters } from "@/lib/data-table" +import { searchParamsContactCache } from "@/lib/tech-vendors/validations" +import { TechVendorContactsTable } from "@/lib/tech-vendors/contacts-table/contact-table" + +interface IndexPageProps { + // Next.js 13 App Router에서 기본으로 주어지는 객체들 + params: { + lng: string + id: string + } + searchParams: Promise<SearchParams> +} + +export default async function SettingsAccountPage(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 = searchParamsContactCache.parse(searchParams) + const validFilters = getValidFilters(search.filters) + + + + const promises = Promise.all([ + getTechVendorContacts({ + ...search, + filters: validFilters, + }, + idAsNumber) + ]) + // 4) 렌더링 + return ( + <div className="space-y-6"> + <div> + <h3 className="text-lg font-medium"> + Contacts + </h3> + <p className="text-sm text-muted-foreground"> + 업무별 담당자 정보를 확인하세요. + </p> + </div> + <Separator /> + <div> + <TechVendorContactsTable promises={promises} vendorId={idAsNumber}/> + </div> + </div> + ) +}
\ No newline at end of file diff --git a/app/[lng]/evcp/(evcp)/tech-vendors/[id]/info/rfq-history/page.tsx b/app/[lng]/evcp/(evcp)/tech-vendors/[id]/info/rfq-history/page.tsx new file mode 100644 index 00000000..4ed2b39f --- /dev/null +++ b/app/[lng]/evcp/(evcp)/tech-vendors/[id]/info/rfq-history/page.tsx @@ -0,0 +1,55 @@ +// import { Separator } from "@/components/ui/separator"
+// import { getRfqHistory } from "@/lib/vendors/service"
+// import { type SearchParams } from "@/types/table"
+// import { getValidFilters } from "@/lib/data-table"
+// import { searchParamsRfqHistoryCache } from "@/lib/vendors/validations"
+// import { TechVendorRfqHistoryTable } from "@/lib/tech-vendors/rfq-history-table/rfq-history-table"
+
+// interface IndexPageProps {
+// // Next.js 13 App Router에서 기본으로 주어지는 객체들
+// params: {
+// lng: string
+// id: string
+// }
+// searchParams: Promise<SearchParams>
+// }
+
+// export default async function RfqHistoryPage(props: IndexPageProps) {
+// const resolvedParams = await props.params
+// const lng = resolvedParams.lng
+// const id = resolvedParams.id
+
+// const idAsNumber = Number(id)
+
+// // 2) SearchParams 파싱 (Zod)
+// // - "filters", "page", "perPage", "sort" 등 contact 전용 컬럼
+// const searchParams = await props.searchParams
+// const search = searchParamsRfqHistoryCache.parse(searchParams)
+// const validFilters = getValidFilters(search.filters)
+
+// const promises = Promise.all([
+// getRfqHistory({
+// ...search,
+// filters: validFilters,
+// },
+// idAsNumber)
+// ])
+
+// // 4) 렌더링
+// return (
+// <div className="space-y-6">
+// <div>
+// <h3 className="text-lg font-medium">
+// RFQ History
+// </h3>
+// <p className="text-sm text-muted-foreground">
+// 협력업체의 RFQ 참여 이력을 확인할 수 있습니다.
+// </p>
+// </div>
+// <Separator />
+// <div>
+// <TechVendorRfqHistoryTable promises={promises} vendorId={idAsNumber} />
+// </div>
+// </div>
+// )
+// }
\ No newline at end of file diff --git a/app/[lng]/evcp/(evcp)/tech-vendors/page.tsx b/app/[lng]/evcp/(evcp)/tech-vendors/page.tsx new file mode 100644 index 00000000..64e8737f --- /dev/null +++ b/app/[lng]/evcp/(evcp)/tech-vendors/page.tsx @@ -0,0 +1,75 @@ +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 { Ellipsis } from "lucide-react"
+
+import { searchParamsCache } from "@/lib/tech-vendors/validations"
+import { getTechVendors, getTechVendorStatusCounts } from "@/lib/tech-vendors/service"
+import { TechVendorsTable } from "@/lib/tech-vendors/table/tech-vendors-table"
+
+interface IndexPageProps {
+ searchParams: Promise<SearchParams>
+}
+
+export default async function IndexPage(props: IndexPageProps) {
+ const searchParams = await props.searchParams
+ const search = searchParamsCache.parse(searchParams)
+
+ const validFilters = getValidFilters(search.filters)
+
+ const promises = Promise.all([
+ getTechVendors({
+ ...search,
+ filters: validFilters,
+ }),
+ getTechVendorStatusCounts(),
+ ])
+
+ 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>
+ <h2 className="text-2xl font-bold tracking-tight">
+ 기술영업 벤더 리스트
+ </h2>
+ <p className="text-muted-foreground">
+ 기술영업 벤더에 대한 요약 정보를 확인하고{" "}
+ <span className="inline-flex items-center whitespace-nowrap">
+ <Ellipsis className="size-3" />
+ <span className="ml-1">버튼</span>
+ </span>
+ 을 통해 담당자 연락처, 입찰 이력, 계약 이력, 패키지 내용 등을 확인 할 수 있습니다.
+ </p>
+ </div>
+ </div>
+ </div>
+
+ <React.Suspense fallback={<Skeleton className="h-7 w-52" />}>
+ {/* <DateRangePicker
+ triggerSize="sm"
+ triggerClassName="ml-auto w-56 sm:w-60"
+ align="end"
+ shallow={false}
+ /> */}
+ </React.Suspense>
+ <React.Suspense
+ fallback={
+ <DataTableSkeleton
+ columnCount={6}
+ searchableColumnCount={1}
+ filterableColumnCount={2}
+ cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
+ shrinkZero
+ />
+ }
+ >
+ <TechVendorsTable promises={promises} />
+ </React.Suspense>
+ </Shell>
+ )
+}
\ No newline at end of file |
