diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-05-15 01:34:49 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-05-15 01:34:49 +0000 |
| commit | ed864fa46c7ce0aac2de4c5ba5d311ebfd7e6a88 (patch) | |
| tree | b04695b9c8a4d2b03ed1f44f318fa38d8c490364 | |
| parent | 9beaabc8d1e0ac3a5c54c8202d3c690577bdbd39 (diff) | |
(대표님) 벤더 문서 관련 개발사항
| -rw-r--r-- | app/[lng]/partners/(partners)/vendor-data/form/[packageId]/[formId]/[projectId]/[contractId]/page.tsx (renamed from app/[lng]/partners/(partners)/vendor-data/form/[packageId]/[formId]/page.tsx) | 32 | ||||
| -rw-r--r-- | components/documents/StageList.tsx | 4 | ||||
| -rw-r--r-- | components/vendor-data/sidebar.tsx | 9 | ||||
| -rw-r--r-- | components/vendor-data/vendor-data-container.tsx | 8 | ||||
| -rw-r--r-- | db/schema/vendorDocu.ts | 3 | ||||
| -rw-r--r-- | lib/form-list/table/formLists-table-toolbar-actions.tsx | 4 | ||||
| -rw-r--r-- | lib/vendor-document-list/table/doc-table.tsx | 2 | ||||
| -rw-r--r-- | lib/vendor-document/table/doc-table-column.tsx | 13 | ||||
| -rw-r--r-- | lib/vendors/table/vendors-table-columns.tsx | 45 | ||||
| -rw-r--r-- | lib/vendors/validations.ts | 2 |
10 files changed, 93 insertions, 29 deletions
diff --git a/app/[lng]/partners/(partners)/vendor-data/form/[packageId]/[formId]/page.tsx b/app/[lng]/partners/(partners)/vendor-data/form/[packageId]/[formId]/[projectId]/[contractId]/page.tsx index dc8df262..71a02ab3 100644 --- a/app/[lng]/partners/(partners)/vendor-data/form/[packageId]/[formId]/page.tsx +++ b/app/[lng]/partners/(partners)/vendor-data/form/[packageId]/[formId]/[projectId]/[contractId]/page.tsx @@ -1,11 +1,15 @@ import DynamicTable from "@/components/form-data/form-data-table"; -import { getFormData, getFormId } from "@/lib/forms/services"; +import { findContractItemId, getFormData, getFormId } from "@/lib/forms/services"; interface IndexPageProps { params: { lng: string; packageId: string; formId: string; + projectId: string; + contractId: string; + + }; searchParams?: { mode?: string; @@ -20,19 +24,35 @@ export default async function FormPage({ params, searchParams }: IndexPageProps) const resolvedSearchParams = await searchParams; // 3) 구조 분해 할당 - const { lng, packageId, formId: formCode } = resolvedParams; + const { lng, packageId, formId: formCode, projectId,contractId } = resolvedParams; // URL 쿼리 파라미터에서 mode 가져오기 (await 해서 사용) const mode = resolvedSearchParams?.mode === "ENG" ? "ENG" : "IM"; // 기본값은 IM // 4) 변환 - const packageIdAsNumber = Number(packageId); + 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, projectId } = await getFormData(formCode, packageIdAsNumber); + const { columns, data } = await getFormData(formCode, packageIdAsNumber); + // 6) formId 및 report temp file 조회 - const { formId } = await getFormId(packageId, formCode); + const { formId } = await getFormId(String(packageIdAsNumber), formCode); // 7) 예외 처리 if (!columns) { @@ -50,7 +70,7 @@ export default async function FormPage({ params, searchParams }: IndexPageProps) formId={formId} columnsJSON={columns} dataJSON={data} - projectId={projectId} + projectId={Number(projectId)} mode={mode} // 모드 전달 /> </div> diff --git a/components/documents/StageList.tsx b/components/documents/StageList.tsx index 8d82b741..64510dda 100644 --- a/components/documents/StageList.tsx +++ b/components/documents/StageList.tsx @@ -175,6 +175,8 @@ export default function StageList({ document }: StageListProps) { <TableHead className="w-[100px]">Stage</TableHead> <TableHead className="w-[100px]">Revision</TableHead> <TableHead className="w-[150px]">첨부파일</TableHead> + <TableHead className="w-[150px]">등록자</TableHead> + <TableHead className="w-[150px]">Comment</TableHead> <TableHead className="w-[150px]">생성일</TableHead> <TableHead className="w-[120px]">계획일</TableHead> <TableHead className="w-[120px]">실제일</TableHead> @@ -232,6 +234,8 @@ export default function StageList({ document }: StageListProps) { )} </div> </TableCell> + <TableCell>{ver.uploaderName}</TableCell> + <TableCell>{ver.comment}</TableCell> <TableCell>{formatDate(ver.DocumentSubmitDate) ?? "-"}</TableCell> <TableCell>{ver.planDate ?? "-"}</TableCell> <TableCell>{ver.actualDate ?? "-"}</TableCell> diff --git a/components/vendor-data/sidebar.tsx b/components/vendor-data/sidebar.tsx index 2dff6bc1..3805d216 100644 --- a/components/vendor-data/sidebar.tsx +++ b/components/vendor-data/sidebar.tsx @@ -24,6 +24,8 @@ interface SidebarProps extends React.HTMLAttributes<HTMLDivElement> { isCollapsed: boolean packages: PackageData[] selectedPackageId: number | null + selectedProjectId: number | null + selectedContractId: number | null onSelectPackage: (itemId: number) => void forms: FormInfo[] selectedForm: string | null @@ -37,6 +39,8 @@ export function Sidebar({ isCollapsed, packages, selectedPackageId, + selectedProjectId, + selectedContractId, onSelectPackage, forms, selectedForm, @@ -48,6 +52,7 @@ export function Sidebar({ const rawPathname = usePathname() const pathname = rawPathname ?? "" + /** * --------------------------- * 1) URL에서 현재 패키지 / 폼 코드 추출 @@ -105,7 +110,7 @@ export function Sidebar({ if (mode === "ENG") { // ENG 모드에서는 첫 번째 패키지 ID 또는 현재 URL에서 추출한 ID 사용 - packageId = currentItemId || (packages[0]?.itemId || 0); + packageId = 0; } else { // IM 모드에서는 반드시 선택된 패키지 ID 필요 if (selectedPackageId === null) return; @@ -119,7 +124,7 @@ export function Sidebar({ // 예: /vendor-data/form/[packageId]/[formCode] const baseSegments = segments.slice(0, segments.indexOf("vendor-data") + 1).join("/") // 모드 정보를 쿼리 파라미터로 추가 - router.push(`/${baseSegments}/form/${packageId}/${form.formCode}?mode=${mode}`) + router.push(`/${baseSegments}/form/${packageId}/${form.formCode}/${selectedProjectId}/${selectedContractId}?mode=${mode}`) } return ( diff --git a/components/vendor-data/vendor-data-container.tsx b/components/vendor-data/vendor-data-container.tsx index 77b36abf..fcecae43 100644 --- a/components/vendor-data/vendor-data-container.tsx +++ b/components/vendor-data/vendor-data-container.tsx @@ -274,6 +274,8 @@ export function VendorDataContainer({ isCollapsed={isCollapsed} packages={currentContract?.packages || []} selectedPackageId={selectedPackageId} + selectedProjectId={selectedProjectId} + selectedContractId={selectedContractId} onSelectPackage={handleSelectPackage} forms={formList} selectedForm={ @@ -305,6 +307,8 @@ export function VendorDataContainer({ isCollapsed={isCollapsed} packages={currentContract?.packages || []} selectedPackageId={selectedPackageId} + selectedContractId={selectedContractId} + selectedProjectId={selectedProjectId} onSelectPackage={handleSelectPackage} forms={formList} selectedForm={ @@ -324,6 +328,8 @@ export function VendorDataContainer({ isCollapsed={isCollapsed} packages={currentContract?.packages || []} selectedPackageId={selectedPackageId} + selectedContractId={selectedContractId} + selectedProjectId={selectedProjectId} onSelectPackage={handleSelectPackage} forms={formList} selectedForm={ @@ -368,6 +374,8 @@ export function VendorDataContainer({ isCollapsed={isCollapsed} packages={currentContract?.packages || []} selectedPackageId={selectedPackageId} + selectedProjectId={selectedProjectId} + selectedContractId={selectedContractId} onSelectPackage={handleSelectPackage} forms={formList} selectedForm={ diff --git a/db/schema/vendorDocu.ts b/db/schema/vendorDocu.ts index 1e166e6b..8eb9cbc6 100644 --- a/db/schema/vendorDocu.ts +++ b/db/schema/vendorDocu.ts @@ -13,6 +13,7 @@ export const documents = pgTable( // documentType: varchar("document_type", { length: 50 }).notNull(), + pic: varchar("pic", { length: 50 }), // 어느 계약(Contract) 소속인지 contractId: integer("contract_id") @@ -128,6 +129,7 @@ export const vendorDocumentsView = pgView("vendor_documents_view", { id: integer("id").notNull(), docNumber: varchar("doc_number", { length: 100 }).notNull(), title: varchar("title", { length: 255 }).notNull(), + pic: varchar("pic", { length: 255 }).notNull(), status: varchar("status", { length: 50 }).notNull(), issuedDate: date("issued_date"), @@ -152,6 +154,7 @@ export const vendorDocumentsView = pgView("vendor_documents_view", { d.id, d.doc_number, d.title, + d.pic, d.status, d.issued_date, diff --git a/lib/form-list/table/formLists-table-toolbar-actions.tsx b/lib/form-list/table/formLists-table-toolbar-actions.tsx index cf874985..97db9a91 100644 --- a/lib/form-list/table/formLists-table-toolbar-actions.tsx +++ b/lib/form-list/table/formLists-table-toolbar-actions.tsx @@ -98,13 +98,13 @@ export function FormListsTableToolbarActions({ table }: ItemsTableToolbarActions ) // 테이블 데이터 업데이트 - 전체 페이지 새로고침 대신 데이터만 갱신 - table.resetRowSelection() + // table.resetRowSelection() // 여기서 테이블 데이터를 다시 불러오는 함수를 호출 // 예: refetchTableData() // 또는 SWR/React Query 등을 사용하고 있다면 mutate() 호출 // 리로드가 꼭 필요하다면 아래 주석 해제 - // window.location.reload() + window.location.reload() } else if (data.status === 'failed') { // 에러 처리 if (pollingRef.current) { diff --git a/lib/vendor-document-list/table/doc-table.tsx b/lib/vendor-document-list/table/doc-table.tsx index f70ce365..db5dc409 100644 --- a/lib/vendor-document-list/table/doc-table.tsx +++ b/lib/vendor-document-list/table/doc-table.tsx @@ -31,8 +31,6 @@ export function DocumentsTable({ // 1) 데이터를 가져옴 (server component -> use(...) pattern) const [{ data, pageCount }] = React.use(promises) - console.log(data) - const [rowAction, setRowAction] = React.useState<DataTableRowAction<DocumentStagesView> | null>(null) diff --git a/lib/vendor-document/table/doc-table-column.tsx b/lib/vendor-document/table/doc-table-column.tsx index e53b03b9..20d43fff 100644 --- a/lib/vendor-document/table/doc-table-column.tsx +++ b/lib/vendor-document/table/doc-table-column.tsx @@ -76,6 +76,19 @@ export function getColumns({ size: 160, }, { + accessorKey: "pic", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="SHI PIC" /> + ), + cell: ({ row }) => <div>{row.getValue("pic")}</div>, + meta: { + excelHeader: "SHI PIC" + }, + enableResizing: true, + minSize: 100, + size: 160, + }, + { accessorKey: "latestStageName", header: ({ column }) => ( <DataTableColumnHeaderSimple column={column} title="Latest Stage Name" /> diff --git a/lib/vendors/table/vendors-table-columns.tsx b/lib/vendors/table/vendors-table-columns.tsx index c768b587..21086918 100644 --- a/lib/vendors/table/vendors-table-columns.tsx +++ b/lib/vendors/table/vendors-table-columns.tsx @@ -51,9 +51,12 @@ type NextRouter = ReturnType<typeof useRouter>; interface GetColumnsProps { setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<VendorWithType> | null>>; router: NextRouter; - userId: number; + userId: number; } + + + /** * tanstack table 컬럼 정의 (중첩 헤더 버전) */ @@ -110,13 +113,13 @@ export function getColumns({ setRowAction, router, userId }: GetColumnsProps): C </Button> </DropdownMenuTrigger> <DropdownMenuContent align="end" className="w-56"> - {(isApproved ||afterApproved) && ( - <DropdownMenuItem - onSelect={() => setRowAction({ row, type: "update" })} - > - 레코드 편집 - </DropdownMenuItem> - )} + {(isApproved || afterApproved) && ( + <DropdownMenuItem + onSelect={() => setRowAction({ row, type: "update" })} + > + 레코드 편집 + </DropdownMenuItem> + )} <DropdownMenuItem onSelect={() => { @@ -130,10 +133,18 @@ export function getColumns({ setRowAction, router, userId }: GetColumnsProps): C 상세보기 </DropdownMenuItem> <DropdownMenuItem - onSelect={() => setRowAction({ row, type: "log" })} - > - 감사 로그 보기 - </DropdownMenuItem> + onSelect={() => { + // 새창으로 열기 위해 window.open() 사용 + window.open(`/evcp/vendors/${row.original.id}/info`, '_blank'); + }} + > + 상세보기(새창) + </DropdownMenuItem> + <DropdownMenuItem + onSelect={() => setRowAction({ row, type: "log" })} + > + 감사 로그 보기 + </DropdownMenuItem> <Separator /> <DropdownMenuSub> @@ -409,10 +420,12 @@ export function getColumns({ setRowAction, router, userId }: GetColumnsProps): C <DataTableColumnHeaderSimple column={column} title="" /> ), cell: ({ row }) => { - // hasAttachments 및 attachmentsList 속성이 추가되었다고 가정 - const hasAttachments = (row.original as VendorWithAttachments).hasAttachments; - const attachmentsList = (row.original as VendorWithAttachments).attachmentsList || []; - + const vendor = row.original as unknown as VendorWithAttachments; + + // 속성이 undefined일 수 있으므로 옵셔널 체이닝과 기본값 사용 + const hasAttachments = vendor.hasAttachments ?? false; + const attachmentsList = vendor.attachmentsList ?? []; + if (hasAttachments) { // 서버 액션을 사용하는 컴포넌트로 교체 return ( diff --git a/lib/vendors/validations.ts b/lib/vendors/validations.ts index e01fa8b9..10ea3e78 100644 --- a/lib/vendors/validations.ts +++ b/lib/vendors/validations.ts @@ -172,7 +172,7 @@ export const createVendorSchema = z .min(1, "국가 선택은 필수입니다.") .max(100, "Max length 100"), phone: z.string().max(50, "Max length 50").optional(), - website: z.string().url("Invalid URL").max(255).optional(), + website: z.string().url("유효하지 않은 URL입니다. https:// 혹은 http:// 로 시작하는 주소를 입력해주세요.").max(255).optional(), attachedFiles: z.any() .refine( |
