diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-09 12:19:05 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-09 12:19:05 +0000 |
| commit | 6d654b1ba2c19e0bf1745b636908e3b00a0f02c7 (patch) | |
| tree | f6d48c0d3a65b428a828acea5db65db8e7bf0db8 /lib | |
| parent | 44794a8628997c0d979adb5bd6711cd848b3e397 (diff) | |
(대표님) 20250709 변경사항 (약 18시 30분까지)
Diffstat (limited to 'lib')
86 files changed, 1537 insertions, 350 deletions
diff --git a/lib/admin-users/table/ausers-table-columns.tsx b/lib/admin-users/table/ausers-table-columns.tsx index 38281c7e..41b78adb 100644 --- a/lib/admin-users/table/ausers-table-columns.tsx +++ b/lib/admin-users/table/ausers-table-columns.tsx @@ -173,7 +173,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<UserVie if (cfg.id === "created_at") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } if (cfg.id === "roles") { diff --git a/lib/b-rfq/attachment/attachment-columns.tsx b/lib/b-rfq/attachment/attachment-columns.tsx index c611e06c..b726ebc8 100644 --- a/lib/b-rfq/attachment/attachment-columns.tsx +++ b/lib/b-rfq/attachment/attachment-columns.tsx @@ -156,13 +156,13 @@ export function getAttachmentColumns({ const updated = row.original.updatedAt as Date return ( <div> - <div>{formatDate(created)}</div> + <div>{formatDate(created, "KR")}</div> <div className="text-xs text-muted-foreground"> {row.original.createdByName} </div> {updated && new Date(updated) > new Date(created) && ( <div className="text-xs text-blue-600"> - 수정: {formatDate(updated)} + 수정: {formatDate(updated, "KR")} </div> )} </div> @@ -252,10 +252,6 @@ export function getAttachmentColumns({ <MessageSquare className="mr-2 h-4 w-4" /> 벤더 응답 보기 </DropdownMenuItem> - <DropdownMenuItem> - <Eye className="mr-2 h-4 w-4" /> - 미리보기 - </DropdownMenuItem> <DropdownMenuItem onClick={() => row.original.filePath && window.open(row.original.filePath, "_blank")} > diff --git a/lib/b-rfq/attachment/revision-dialog.tsx b/lib/b-rfq/attachment/revision-dialog.tsx index b1fe1576..d26abedb 100644 --- a/lib/b-rfq/attachment/revision-dialog.tsx +++ b/lib/b-rfq/attachment/revision-dialog.tsx @@ -143,7 +143,7 @@ export function RevisionDialog({ </TableCell> <TableCell> - {formatDate(revision.createdAt)} + {formatDate(revision.createdAt, "KR")} </TableCell> <TableCell> diff --git a/lib/b-rfq/final/final-rfq-detail-columns.tsx b/lib/b-rfq/final/final-rfq-detail-columns.tsx index 832923eb..88d62765 100644 --- a/lib/b-rfq/final/final-rfq-detail-columns.tsx +++ b/lib/b-rfq/final/final-rfq-detail-columns.tsx @@ -351,7 +351,7 @@ export function getFinalRfqDetailColumns({ const deliveryDate = row.getValue("deliveryDate") as Date return deliveryDate ? ( <div className="text-sm"> - {formatDate(deliveryDate)} + {formatDate(deliveryDate, "KR")} </div> ) : ( <span className="text-muted-foreground">-</span> @@ -487,7 +487,7 @@ export function getFinalRfqDetailColumns({ const updated = row.getValue("updatedAt") as Date return updated ? ( <div className="text-sm"> - {formatDate(updated)} + {formatDate(updated, "KR")} </div> ) : ( <span className="text-muted-foreground">-</span> diff --git a/lib/b-rfq/initial/initial-rfq-detail-columns.tsx b/lib/b-rfq/initial/initial-rfq-detail-columns.tsx index f2be425c..2d9c3a68 100644 --- a/lib/b-rfq/initial/initial-rfq-detail-columns.tsx +++ b/lib/b-rfq/initial/initial-rfq-detail-columns.tsx @@ -288,7 +288,7 @@ export function getInitialRfqDetailColumns({ const validDate = row.getValue("validDate") as Date return validDate ? ( <div className="text-sm"> - {formatDate(validDate)} + {formatDate(validDate, "KR")} </div> ) : ( <span className="text-muted-foreground">-</span> @@ -307,7 +307,7 @@ export function getInitialRfqDetailColumns({ return dueDate ? ( <div className={`${isOverdue ? 'text-red-600' : ''}`}> - <div className="font-medium">{formatDate(dueDate)}</div> + <div className="font-medium">{formatDate(dueDate, "KR")}</div> {isOverdue && ( <div className="text-xs text-red-600">지연</div> )} @@ -384,7 +384,7 @@ export function getInitialRfqDetailColumns({ return ( <div className="space-y-1"> - <div className="text-sm">{formatDate(created)}</div> + <div className="text-sm">{formatDate(created, "KR")}</div> {updated && new Date(updated) > new Date(created) && ( <div className="text-xs text-blue-600"> 수정: {formatDate(updated, "KR")} diff --git a/lib/b-rfq/service.ts b/lib/b-rfq/service.ts index 4def634b..2ef57b34 100644 --- a/lib/b-rfq/service.ts +++ b/lib/b-rfq/service.ts @@ -498,7 +498,7 @@ export async function getVendorResponsesForAttachment( rfqType: 'INITIAL' | 'FINAL' = 'INITIAL' ) { try { - // 1. 기본 벤더 응답 정보 가져오기 + // 1. 기본 벤더 응답 정보 가져오기 (첨부파일 정보와 조인) const responses = await db .select({ id: vendorAttachmentResponses.id, @@ -510,10 +510,16 @@ export async function getVendorResponsesForAttachment( rfqType: vendorAttachmentResponses.rfqType, rfqRecordId: vendorAttachmentResponses.rfqRecordId, responseStatus: vendorAttachmentResponses.responseStatus, - currentRevision: vendorAttachmentResponses.currentRevision, + + // 첨부파일의 현재 리비전 (가장 중요!) + currentRevision: bRfqsAttachments.currentRevision, + + // 벤더가 응답한 리비전 respondedRevision: vendorAttachmentResponses.respondedRevision, + responseComment: vendorAttachmentResponses.responseComment, vendorComment: vendorAttachmentResponses.vendorComment, + // 새로 추가된 필드들 revisionRequestComment: vendorAttachmentResponses.revisionRequestComment, revisionRequestedAt: vendorAttachmentResponses.revisionRequestedAt, @@ -523,6 +529,7 @@ export async function getVendorResponsesForAttachment( }) .from(vendorAttachmentResponses) .leftJoin(vendors, eq(vendorAttachmentResponses.vendorId, vendors.id)) + .leftJoin(bRfqsAttachments, eq(vendorAttachmentResponses.attachmentId, bRfqsAttachments.id)) .where( and( eq(vendorAttachmentResponses.attachmentId, attachmentId), @@ -553,15 +560,23 @@ export async function getVendorResponsesForAttachment( .orderBy(desc(vendorResponseAttachmentsB.uploadedAt)); } - // 3. 응답에 파일 정보 병합 - const enhancedResponses = responses.map(response => ({ - ...response, - files: responseFiles.filter(file => file.vendorResponseId === response.id), - totalFiles: responseFiles.filter(file => file.vendorResponseId === response.id).length, - latestFile: responseFiles - .filter(file => file.vendorResponseId === response.id) - .sort((a, b) => new Date(b.uploadedAt).getTime() - new Date(a.uploadedAt).getTime())[0] || null, - })); + // 3. 응답에 파일 정보 병합 및 리비전 상태 체크 + const enhancedResponses = responses.map(response => { + const files = responseFiles.filter(file => file.vendorResponseId === response.id); + const latestFile = files + .sort((a, b) => new Date(b.uploadedAt).getTime() - new Date(a.uploadedAt).getTime())[0] || null; + + // 벤더가 최신 리비전에 응답했는지 체크 + const isUpToDate = response.respondedRevision === response.currentRevision; + + return { + ...response, + files, + totalFiles: files.length, + latestFile, + isUpToDate, // 최신 리비전 응답 여부 + }; + }); return enhancedResponses; } catch (err) { diff --git a/lib/b-rfq/vendor-response/vendor-responses-table.tsx b/lib/b-rfq/vendor-response/vendor-responses-table.tsx index 251b1ad0..02a5fa59 100644 --- a/lib/b-rfq/vendor-response/vendor-responses-table.tsx +++ b/lib/b-rfq/vendor-response/vendor-responses-table.tsx @@ -52,20 +52,12 @@ export function VendorResponsesTable({ promises }: VendorResponsesTableProps) { ] }, - { - id: "rfqCode", - label: "RFQ 번호", - placeholder: "RFQ 번호 검색...", - } + ]; // 고급 필터 필드 const advancedFilterFields: DataTableAdvancedFilterField<VendorRfqResponseSummary>[] = [ - { - id: "rfqCode", - label: "RFQ 번호", - type: "text", - }, + { id: "overallStatus", label: "전체 상태", diff --git a/lib/basic-contract/status/basic-contract-columns.tsx b/lib/basic-contract/status/basic-contract-columns.tsx index 54504be4..cc9d9bff 100644 --- a/lib/basic-contract/status/basic-contract-columns.tsx +++ b/lib/basic-contract/status/basic-contract-columns.tsx @@ -106,7 +106,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<BasicCo // 날짜 형식 처리 if (cfg.id === "createdAt" || cfg.id === "updatedAt") { const dateVal = cell.getValue() as Date - return formatDateTime(dateVal) + return formatDateTime(dateVal, "KR") } // Status 컬럼에 Badge 적용 (확장) diff --git a/lib/basic-contract/template/basic-contract-template-columns.tsx b/lib/basic-contract/template/basic-contract-template-columns.tsx index b0486fe4..5f4433d1 100644 --- a/lib/basic-contract/template/basic-contract-template-columns.tsx +++ b/lib/basic-contract/template/basic-contract-template-columns.tsx @@ -186,7 +186,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<BasicCo // 날짜 형식 처리
if (cfg.id === "createdAt" || cfg.id === "updatedAt") {
const dateVal = cell.getValue() as Date
- return formatDateTime(dateVal)
+ return formatDateTime(dateVal, "KR")
}
// Status 컬럼에 Badge 적용
diff --git a/lib/bidding-projects/table/projects-table-columns.tsx b/lib/bidding-projects/table/projects-table-columns.tsx index b8f3b91b..f1e23be9 100644 --- a/lib/bidding-projects/table/projects-table-columns.tsx +++ b/lib/bidding-projects/table/projects-table-columns.tsx @@ -43,7 +43,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<Bidding cell: ({ row, cell }) => { if (cfg.id === "createdAt" || cfg.id === "updatedAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } return row.getValue(cfg.id) ?? "" }, diff --git a/lib/cbe/table/cbe-table-columns.tsx b/lib/cbe/table/cbe-table-columns.tsx index 2da62ea8..552a0249 100644 --- a/lib/cbe/table/cbe-table-columns.tsx +++ b/lib/cbe/table/cbe-table-columns.tsx @@ -158,7 +158,7 @@ export function getColumns({ if (cfg.id === "respondedAt") { const dateVal = val as Date | undefined if (!dateVal) return null - return formatDate(dateVal) + return formatDate(dateVal, "KR") } // 그 외 필드는 기본 값 표시 diff --git a/lib/cbe/table/comments-sheet.tsx b/lib/cbe/table/comments-sheet.tsx index 2ac9049f..b4647e7a 100644 --- a/lib/cbe/table/comments-sheet.tsx +++ b/lib/cbe/table/comments-sheet.tsx @@ -176,7 +176,7 @@ export function CommentSheet({ </div> )} </TableCell> - <TableCell> { c.createdAt ? formatDate(c.createdAt): "-"}</TableCell> + <TableCell> { c.createdAt ? formatDate(c.createdAt, "KR"): "-"}</TableCell> <TableCell> {c.commentedByEmail ?? "-"} </TableCell> diff --git a/lib/dashboard/dashboard-client.tsx b/lib/dashboard/dashboard-client.tsx index cda1ed8e..398a18f2 100644 --- a/lib/dashboard/dashboard-client.tsx +++ b/lib/dashboard/dashboard-client.tsx @@ -68,14 +68,14 @@ export function DashboardClient({ initialData }: DashboardClientProps) { <div className="flex items-center justify-between"> <div> <h2 className="text-2xl font-bold tracking-tight"> - {getDomainDisplayName(domain)} Dashboard + {getDomainDisplayName(domain)} 대시보드 </h2> - <p className="text-muted-foreground"> + {/* <p className="text-muted-foreground"> {domain === "partners" ? "회사와 개인에게 할당된 일들을 보여줍니다." : "팀과 개인에게 할당된 일들을 보여줍니다." } - </p> + </p> */} </div> <Button onClick={handleRefresh} diff --git a/lib/equip-class/table/equipClass-table-columns.tsx b/lib/equip-class/table/equipClass-table-columns.tsx index d149c836..0dfbf06f 100644 --- a/lib/equip-class/table/equipClass-table-columns.tsx +++ b/lib/equip-class/table/equipClass-table-columns.tsx @@ -50,7 +50,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<Extende if (cfg.id === "createdAt"||cfg.id === "updatedAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } return row.getValue(cfg.id) ?? "" diff --git a/lib/esg-check-list/table/esg-evaluations-table-columns.tsx b/lib/esg-check-list/table/esg-evaluations-table-columns.tsx index 399f9f4a..48139c75 100644 --- a/lib/esg-check-list/table/esg-evaluations-table-columns.tsx +++ b/lib/esg-check-list/table/esg-evaluations-table-columns.tsx @@ -206,7 +206,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<EsgEval ), cell: ({ row }) => { const date = row.getValue("createdAt") as Date - return formatDate(date) + return formatDate(date, "KR") }, }, { @@ -216,7 +216,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<EsgEval ), cell: ({ row }) => { const date = row.getValue("updatedAt") as Date - return formatDate(date) + return formatDate(date, "KR") }, }, ] diff --git a/lib/evaluation-submit/table/evaluation-submissions-table-columns.tsx b/lib/evaluation-submit/table/evaluation-submissions-table-columns.tsx index 1ec0284f..8d097aff 100644 --- a/lib/evaluation-submit/table/evaluation-submissions-table-columns.tsx +++ b/lib/evaluation-submit/table/evaluation-submissions-table-columns.tsx @@ -376,7 +376,7 @@ export function getColumns({ setRowAction, router }: GetColumnsProps): ColumnDef ), cell: ({ row }) => { const date = row.getValue("reviewerEvaluationCreatedAt") as Date; - return formatDate(date); + return formatDate(date, "KR"); }, size: 140, }, @@ -387,7 +387,7 @@ export function getColumns({ setRowAction, router }: GetColumnsProps): ColumnDef ), cell: ({ row }) => { const date = row.getValue("reviewerEvaluationUpdatedAt") as Date; - return formatDate(date); + return formatDate(date, "KR"); }, size: 140, }, diff --git a/lib/form-list/table/formLists-table-columns.tsx b/lib/form-list/table/formLists-table-columns.tsx index 5b120796..b992feb3 100644 --- a/lib/form-list/table/formLists-table-columns.tsx +++ b/lib/form-list/table/formLists-table-columns.tsx @@ -91,7 +91,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<FormLis if (cfg.id === "createdAt"||cfg.id === "updatedAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } return row.getValue(cfg.id) ?? "" diff --git a/lib/incoterms/table/incoterms-table-columns.tsx b/lib/incoterms/table/incoterms-table-columns.tsx index 91ce4482..3e25e1d3 100644 --- a/lib/incoterms/table/incoterms-table-columns.tsx +++ b/lib/incoterms/table/incoterms-table-columns.tsx @@ -159,7 +159,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<typeof }, cell: ({ row }) => { const dateVal = row.getValue("createdAt") as Date - return formatDateTime(dateVal) + return formatDateTime(dateVal, "KR") }, minSize: 80 } diff --git a/lib/items-tech/table/hull/offshore-hull-table-columns.tsx b/lib/items-tech/table/hull/offshore-hull-table-columns.tsx index efc6c583..19782c42 100644 --- a/lib/items-tech/table/hull/offshore-hull-table-columns.tsx +++ b/lib/items-tech/table/hull/offshore-hull-table-columns.tsx @@ -162,7 +162,7 @@ export function getOffshoreHullColumns({ setRowAction }: GetColumnsProps): Colum header: ({ column }) => (
<DataTableColumnHeaderSimple column={column} title="생성일" />
),
- cell: ({ row }) => formatDate(row.original.createdAt),
+ cell: ({ row }) => formatDate(row.original.createdAt, "KR"),
enableSorting: true,
enableHiding: true,
meta: {
@@ -174,7 +174,7 @@ export function getOffshoreHullColumns({ setRowAction }: GetColumnsProps): Colum header: ({ column }) => (
<DataTableColumnHeaderSimple column={column} title="수정일" />
),
- cell: ({ row }) => formatDate(row.original.updatedAt),
+ cell: ({ row }) => formatDate(row.original.updatedAt, "KR"),
enableSorting: true,
enableHiding: true,
meta: {
diff --git a/lib/items-tech/table/ship/items-ship-table-columns.tsx b/lib/items-tech/table/ship/items-ship-table-columns.tsx index 13ba2480..7018ae43 100644 --- a/lib/items-tech/table/ship/items-ship-table-columns.tsx +++ b/lib/items-tech/table/ship/items-ship-table-columns.tsx @@ -165,7 +165,7 @@ export function getShipbuildingColumns({ setRowAction }: GetColumnsProps): Colum header: ({ column }) => (
<DataTableColumnHeaderSimple column={column} title="생성일" />
),
- cell: ({ row }) => formatDate(row.original.createdAt),
+ cell: ({ row }) => formatDate(row.original.createdAt, "KR"),
enableSorting: true,
enableHiding: true,
meta: {
@@ -177,7 +177,7 @@ export function getShipbuildingColumns({ setRowAction }: GetColumnsProps): Colum header: ({ column }) => (
<DataTableColumnHeaderSimple column={column} title="수정일" />
),
- cell: ({ row }) => formatDate(row.original.updatedAt),
+ cell: ({ row }) => formatDate(row.original.updatedAt, "KR"),
enableSorting: true,
enableHiding: true,
meta: {
diff --git a/lib/items-tech/table/top/offshore-top-table-columns.tsx b/lib/items-tech/table/top/offshore-top-table-columns.tsx index 93f27492..c2df4b75 100644 --- a/lib/items-tech/table/top/offshore-top-table-columns.tsx +++ b/lib/items-tech/table/top/offshore-top-table-columns.tsx @@ -162,7 +162,7 @@ export function getOffshoreTopColumns({ setRowAction }: GetColumnsProps): Column header: ({ column }) => (
<DataTableColumnHeaderSimple column={column} title="생성일" />
),
- cell: ({ row }) => formatDate(row.original.createdAt),
+ cell: ({ row }) => formatDate(row.original.createdAt, "KR"),
enableSorting: true,
enableHiding: true,
meta: {
@@ -174,7 +174,7 @@ export function getOffshoreTopColumns({ setRowAction }: GetColumnsProps): Column header: ({ column }) => (
<DataTableColumnHeaderSimple column={column} title="수정일" />
),
- cell: ({ row }) => formatDate(row.original.updatedAt),
+ cell: ({ row }) => formatDate(row.original.updatedAt, "KR"),
enableSorting: true,
enableHiding: true,
meta: {
diff --git a/lib/items/table/items-table-columns.tsx b/lib/items/table/items-table-columns.tsx index 8dd84c58..e321fe1a 100644 --- a/lib/items/table/items-table-columns.tsx +++ b/lib/items/table/items-table-columns.tsx @@ -138,7 +138,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<Item>[] if (cfg.id === "createdAt"||cfg.id === "updatedAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } return row.getValue(cfg.id) ?? "" diff --git a/lib/login-session/table/login-sessions-table-columns.tsx b/lib/login-session/table/login-sessions-table-columns.tsx index 5d2389be..899678f3 100644 --- a/lib/login-session/table/login-sessions-table-columns.tsx +++ b/lib/login-session/table/login-sessions-table-columns.tsx @@ -89,7 +89,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<Extende </div> </TooltipTrigger> <TooltipContent> - {formatDate(date)} + {formatDate(date, "KR")} </TooltipContent> </Tooltip> ) @@ -113,7 +113,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<Extende </div> </TooltipTrigger> <TooltipContent> - {formatDate(date)} + {formatDate(date, "KR")} </TooltipContent> </Tooltip> ) diff --git a/lib/page-visits/table/page-visits-table-columns.tsx b/lib/page-visits/table/page-visits-table-columns.tsx index e1d2fed4..86f2d9c3 100644 --- a/lib/page-visits/table/page-visits-table-columns.tsx +++ b/lib/page-visits/table/page-visits-table-columns.tsx @@ -125,7 +125,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<Extende </div> </TooltipTrigger> <TooltipContent> - {formatDate(date)} + {formatDate(date, "KR")} </TooltipContent> </Tooltip> ) diff --git a/lib/payment-terms/table/payment-terms-table-columns.tsx b/lib/payment-terms/table/payment-terms-table-columns.tsx index 08d30482..ecf20229 100644 --- a/lib/payment-terms/table/payment-terms-table-columns.tsx +++ b/lib/payment-terms/table/payment-terms-table-columns.tsx @@ -159,7 +159,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<typeof }, cell: ({ row }) => { const dateVal = row.getValue("createdAt") as Date - return formatDateTime(dateVal) + return formatDateTime(dateVal, "KR") }, minSize: 80 } diff --git a/lib/po/service.ts b/lib/po/service.ts index f62b57fa..99033854 100644 --- a/lib/po/service.ts +++ b/lib/po/service.ts @@ -289,7 +289,7 @@ export async function requestSignatures( }, { tabLabel: "remark", - value: `결제 조건: ${contract.paymentTerms} + value: `지급 조건: ${contract.paymentTerms} 납품 조건: ${contract.deliveryTerms} 납품 기한: ${contract.deliveryDate} 납품 장소: ${contract.deliveryLocation} diff --git a/lib/po/table/item-dialog.tsx b/lib/po/table/item-dialog.tsx index a6690e75..ce4e4741 100644 --- a/lib/po/table/item-dialog.tsx +++ b/lib/po/table/item-dialog.tsx @@ -147,7 +147,7 @@ export function ItemsDialog({ open, onOpenChange, po }: ItemsDialogProps) { <div className="mt-3 pt-2 border-t text-xs text-muted-foreground flex items-center"> <Clock className="mr-1 h-3 w-3" /> - Updated: {formatDate(item.updatedAt)} + Updated: {formatDate(item.updatedAt, "KR")} </div> </CardContent> </Card> diff --git a/lib/po/table/po-table-columns.tsx b/lib/po/table/po-table-columns.tsx index 6834e543..7edd2435 100644 --- a/lib/po/table/po-table-columns.tsx +++ b/lib/po/table/po-table-columns.tsx @@ -250,7 +250,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<Contrac // 날짜 포맷, 숫자 포맷 등 처리 if (cfg.type === "date") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } // ... return row.getValue(cfg.id) ?? "" diff --git a/lib/poa/table/poa-table-columns.tsx b/lib/poa/table/poa-table-columns.tsx index b362e54c..7aad609e 100644 --- a/lib/poa/table/poa-table-columns.tsx +++ b/lib/poa/table/poa-table-columns.tsx @@ -112,7 +112,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<POADeta const dateVal = value as Date; return ( <div className="text-sm"> - {formatDate(dateVal)} + {formatDate(dateVal, "KR")} </div> ); } diff --git a/lib/pq/pq-review-table-new/vendors-table-columns.tsx b/lib/pq/pq-review-table-new/vendors-table-columns.tsx index 0491f1dc..6bfa8c7f 100644 --- a/lib/pq/pq-review-table-new/vendors-table-columns.tsx +++ b/lib/pq/pq-review-table-new/vendors-table-columns.tsx @@ -508,10 +508,10 @@ export function getColumns({ setRowAction, router }: GetColumnsProps): ColumnDef ), cell: ({ row }) => { if (row.original.approvedAt) { - return <span className="text-green-600">{formatDate(row.original.approvedAt)}</span> + return <span className="text-green-600">{formatDate(row.original.approvedAt, "KR")}</span> } if (row.original.rejectedAt) { - return <span className="text-red-600">{formatDate(row.original.rejectedAt)}</span> + return <span className="text-red-600">{formatDate(row.original.rejectedAt, "KR")}</span> } return "-" }, diff --git a/lib/pq/pq-review-table/vendors-table-columns.tsx b/lib/pq/pq-review-table/vendors-table-columns.tsx index 8673443f..dfa1c44f 100644 --- a/lib/pq/pq-review-table/vendors-table-columns.tsx +++ b/lib/pq/pq-review-table/vendors-table-columns.tsx @@ -160,12 +160,12 @@ export function getColumns({ setRowAction, router }: GetColumnsProps): ColumnDef if (cfg.id === "createdAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } if (cfg.id === "updatedAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } diff --git a/lib/pq/table/pq-table-column.tsx b/lib/pq/table/pq-table-column.tsx index 7efed645..b9317570 100644 --- a/lib/pq/table/pq-table-column.tsx +++ b/lib/pq/table/pq-table-column.tsx @@ -124,7 +124,7 @@ export function getColumns({ header: ({ column }) => ( <DataTableColumnHeaderSimple column={column} title="Created At" /> ), - cell: ({ cell }) => formatDateTime(cell.getValue() as Date), + cell: ({ cell }) => formatDateTime(cell.getValue() as Date, "KR"), meta: { excelHeader: "created At" }, @@ -137,7 +137,7 @@ export function getColumns({ header: ({ column }) => ( <DataTableColumnHeaderSimple column={column} title="Updated At" /> ), - cell: ({ cell }) => formatDateTime(cell.getValue() as Date), + cell: ({ cell }) => formatDateTime(cell.getValue() as Date, "KR"), meta: { excelHeader: "updated At" }, diff --git a/lib/procurement-rfqs/services.ts b/lib/procurement-rfqs/services.ts index 3816605c..9cca4c73 100644 --- a/lib/procurement-rfqs/services.ts +++ b/lib/procurement-rfqs/services.ts @@ -940,8 +940,8 @@ export async function sendRfq(rfqId: number) { projectCode: rfq.project?.code || '', projectName: rfq.project?.name || '', description: rfq.remark || '', - dueDate: rfq.dueDate ? formatDate(rfq.dueDate) : 'N/A', - deliveryDate: detail.deliveryDate ? formatDate(detail.deliveryDate) : 'N/A', + dueDate: rfq.dueDate ? formatDate(rfq.dueDate, "KR") : 'N/A', + deliveryDate: detail.deliveryDate ? formatDate(detail.deliveryDate, "KR") : 'N/A', }, vendor: { id: detail.vendor.id, diff --git a/lib/procurement-rfqs/table/detail-table/vendor-communication-drawer.tsx b/lib/procurement-rfqs/table/detail-table/vendor-communication-drawer.tsx index 34efdfc2..e43fc676 100644 --- a/lib/procurement-rfqs/table/detail-table/vendor-communication-drawer.tsx +++ b/lib/procurement-rfqs/table/detail-table/vendor-communication-drawer.tsx @@ -278,7 +278,7 @@ export function VendorCommunicationDrawer({ {selectedAttachment.fileName} </DialogTitle> <DialogDescription> - {formatFileSize(selectedAttachment.fileSize)} • {formatDateTime(selectedAttachment.uploadedAt)} + {formatFileSize(selectedAttachment.fileSize)} • {formatDateTime(selectedAttachment.uploadedAt, "KR")} </DialogDescription> </DialogHeader> @@ -417,7 +417,7 @@ export function VendorCommunicationDrawer({ )} <div className="text-xs mt-1 opacity-70 flex items-center gap-1 justify-end"> - {formatDateTime(comment.createdAt)} + {formatDateTime(comment.createdAt, "KR")} </div> </div> diff --git a/lib/procurement-rfqs/table/pr-item-dialog.tsx b/lib/procurement-rfqs/table/pr-item-dialog.tsx index 4523295d..aada8438 100644 --- a/lib/procurement-rfqs/table/pr-item-dialog.tsx +++ b/lib/procurement-rfqs/table/pr-item-dialog.tsx @@ -217,7 +217,7 @@ export function PrDetailsDialog({ <TableCell>{item.materialDescription || "-"}</TableCell> <TableCell className="whitespace-nowrap">{item.size || "-"}</TableCell> <TableCell className="whitespace-nowrap"> - {item.deliveryDate ? formatDate(item.deliveryDate) : "-"} + {item.deliveryDate ? formatDate(item.deliveryDate, "KR") : "-"} </TableCell> <TableCell className="whitespace-nowrap">{item.quantity || "-"}</TableCell> <TableCell className="whitespace-nowrap">{item.uom || "-"}</TableCell> diff --git a/lib/project-avl/table/projectAVL-table-columns.tsx b/lib/project-avl/table/projectAVL-table-columns.tsx index 916380e3..27e0ab42 100644 --- a/lib/project-avl/table/projectAVL-table-columns.tsx +++ b/lib/project-avl/table/projectAVL-table-columns.tsx @@ -64,7 +64,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<Project if (cfg.id === "submitted_at"||cfg.id === "approved_at") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } return row.getValue(cfg.id) ?? "" diff --git a/lib/project-gtc/table/project-gtc-table-columns.tsx b/lib/project-gtc/table/project-gtc-table-columns.tsx index 141d5737..2605e365 100644 --- a/lib/project-gtc/table/project-gtc-table-columns.tsx +++ b/lib/project-gtc/table/project-gtc-table-columns.tsx @@ -273,7 +273,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<Project return ( <div className="flex items-center"> <span> - {formatDateTime(new Date(date))} + {formatDateTime(new Date(date), "KR")} </span> </div> ) @@ -290,7 +290,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<Project return ( <div className="flex items-center"> <span> - {formatDate(new Date(date))} + {formatDate(new Date(date), "KR")} </span> </div> ) diff --git a/lib/projects/table/projects-table-columns.tsx b/lib/projects/table/projects-table-columns.tsx index 77899212..6926429a 100644 --- a/lib/projects/table/projects-table-columns.tsx +++ b/lib/projects/table/projects-table-columns.tsx @@ -50,7 +50,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<Project if (cfg.id === "createdAt"||cfg.id === "updatedAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } return row.getValue(cfg.id) ?? "" diff --git a/lib/qna/table/qna-table-columns.tsx b/lib/qna/table/qna-table-columns.tsx index 01431e35..0aa30d88 100644 --- a/lib/qna/table/qna-table-columns.tsx +++ b/lib/qna/table/qna-table-columns.tsx @@ -242,7 +242,7 @@ export function getColumns({ setRowAction, router, currentUserId }: GetColumnsOp ), cell: ({ row }) => ( <div className="text-sm"> - {formatDate(row.original.createdAt)} + {formatDate(row.original.createdAt, "KR")} </div> ), enableSorting: true, @@ -262,7 +262,7 @@ export function getColumns({ setRowAction, router, currentUserId }: GetColumnsOp return ( <div className="text-sm"> - {lastActivity ? formatDate(lastActivity) : "없음"} + {lastActivity ? formatDate(lastActivity, "KR") : "없음"} </div> ) }, diff --git a/lib/rfqs/cbe-table/cbe-table-columns.tsx b/lib/rfqs/cbe-table/cbe-table-columns.tsx index bc16496f..aa244c75 100644 --- a/lib/rfqs/cbe-table/cbe-table-columns.tsx +++ b/lib/rfqs/cbe-table/cbe-table-columns.tsx @@ -157,7 +157,7 @@ export function getColumns({ if (cfg.id === "respondedAt" ) { const dateVal = val as Date | undefined if (!dateVal) return null - return formatDate(dateVal) + return formatDate(dateVal, "KR") } // 그 외 필드는 기본 값 표시 diff --git a/lib/rfqs/cbe-table/comments-sheet.tsx b/lib/rfqs/cbe-table/comments-sheet.tsx index e91a0617..b040d734 100644 --- a/lib/rfqs/cbe-table/comments-sheet.tsx +++ b/lib/rfqs/cbe-table/comments-sheet.tsx @@ -167,7 +167,7 @@ export function CommentSheet({ </div> )} </TableCell> - <TableCell> {c.createdAt ? formatDate(c.createdAt) : "-"}</TableCell> + <TableCell> {c.createdAt ? formatDate(c.createdAt, "KR") : "-"}</TableCell> <TableCell>{c.commentedByEmail ?? "-"}</TableCell> </TableRow> ))} diff --git a/lib/rfqs/cbe-table/invite-vendors-dialog.tsx b/lib/rfqs/cbe-table/invite-vendors-dialog.tsx index 18edbe80..8d69e765 100644 --- a/lib/rfqs/cbe-table/invite-vendors-dialog.tsx +++ b/lib/rfqs/cbe-table/invite-vendors-dialog.tsx @@ -50,7 +50,7 @@ import { createCbeEvaluation } from "../service" // 컴포넌트 내부에서 사용할 폼 스키마 정의 const formSchema = z.object({ - paymentTerms: z.string().min(1, "결제 조건을 입력하세요"), + paymentTerms: z.string().min(1, "지급 조건을 입력하세요"), incoterms: z.string().min(1, "Incoterms를 입력하세요"), deliverySchedule: z.string().min(1, "배송 일정을 입력하세요"), notes: z.string().optional(), @@ -233,14 +233,14 @@ export function InviteVendorsDialog({ </div> )} - {/* 결제 조건 - 필수 필드 */} + {/* 지급 조건 - 필수 필드 */} <FormField control={form.control} name="paymentTerms" render={({ field }) => ( <FormItem> <FormLabel> - 결제 조건{RequiredLabel} + 지급 조건{RequiredLabel} </FormLabel> <FormControl> <Input {...field} placeholder="예: Net 30" /> diff --git a/lib/rfqs/table/attachment-rfq-sheet.tsx b/lib/rfqs/table/attachment-rfq-sheet.tsx index 57a170e1..75235b32 100644 --- a/lib/rfqs/table/attachment-rfq-sheet.tsx +++ b/lib/rfqs/table/attachment-rfq-sheet.tsx @@ -291,7 +291,7 @@ export function RfqAttachmentsSheet({ )} {field.createdAt && ( <span className="text-xs text-muted-foreground"> - Created at {formatDate(field.createdAt)} + Created at {formatDate(field.createdAt, "KR")} </span> )} </div> diff --git a/lib/rfqs/table/rfqs-table-columns.tsx b/lib/rfqs/table/rfqs-table-columns.tsx index 98df3bc8..5c09fcf0 100644 --- a/lib/rfqs/table/rfqs-table-columns.tsx +++ b/lib/rfqs/table/rfqs-table-columns.tsx @@ -278,7 +278,7 @@ export function getColumns({ if (cfg.id === "createdAt" || cfg.id === "updatedAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } return row.getValue(cfg.id) ?? "" diff --git a/lib/rfqs/tbe-table/comments-sheet.tsx b/lib/rfqs/tbe-table/comments-sheet.tsx index 6efd631f..b3cdbc60 100644 --- a/lib/rfqs/tbe-table/comments-sheet.tsx +++ b/lib/rfqs/tbe-table/comments-sheet.tsx @@ -165,7 +165,7 @@ export function CommentSheet({ </div> )} </TableCell> - <TableCell> {c.createdAt ? formatDate(c.createdAt) : "-"}</TableCell> + <TableCell> {c.createdAt ? formatDate(c.createdAt, "KR") : "-"}</TableCell> <TableCell>{c.commentedByEmail ?? "-"}</TableCell> </TableRow> ))} diff --git a/lib/rfqs/tbe-table/file-dialog.tsx b/lib/rfqs/tbe-table/file-dialog.tsx index 772eb930..e19430a3 100644 --- a/lib/rfqs/tbe-table/file-dialog.tsx +++ b/lib/rfqs/tbe-table/file-dialog.tsx @@ -118,7 +118,7 @@ export function TBEFileDialog({ <FileListInfo className="flex-1 min-w-0"> <FileListName className="text-sm font-medium truncate">{file.fileName}</FileListName> <FileListDescription className="text-xs text-muted-foreground"> - {file.uploadedAt ? formatDateTime(file.uploadedAt) : ""} + {file.uploadedAt ? formatDateTime(file.uploadedAt, "KR") : ""} </FileListDescription> </FileListInfo> </div> diff --git a/lib/rfqs/tbe-table/tbe-table-columns.tsx b/lib/rfqs/tbe-table/tbe-table-columns.tsx index e8566831..0538d354 100644 --- a/lib/rfqs/tbe-table/tbe-table-columns.tsx +++ b/lib/rfqs/tbe-table/tbe-table-columns.tsx @@ -208,7 +208,7 @@ export function getColumns({ if (cfg.id === "tbeUpdated") { const dateVal = val as Date | undefined if (!dateVal) return null - return formatDate(dateVal) + return formatDate(dateVal, "KR") } // 그 외 필드는 기본 값 표시 diff --git a/lib/rfqs/tbe-table/vendor-contact/vendor-contact-table-column.tsx b/lib/rfqs/tbe-table/vendor-contact/vendor-contact-table-column.tsx index fcd0c3fb..efc395b4 100644 --- a/lib/rfqs/tbe-table/vendor-contact/vendor-contact-table-column.tsx +++ b/lib/rfqs/tbe-table/vendor-contact/vendor-contact-table-column.tsx @@ -55,7 +55,7 @@ export function getColumns(): ColumnDef<VendorData>[] { header: ({ column }) => ( <ClientDataTableColumnHeaderSimple column={column} title="Created At" /> ), - cell: ({ cell }) => formatDate(cell.getValue() as Date), + cell: ({ cell }) => formatDate(cell.getValue() as Date, "KR"), }, // Updated At @@ -64,7 +64,7 @@ export function getColumns(): ColumnDef<VendorData>[] { header: ({ column }) => ( <ClientDataTableColumnHeaderSimple column={column} title="Updated At" /> ), - cell: ({ cell }) => formatDate(cell.getValue() as Date), + cell: ({ cell }) => formatDate(cell.getValue() as Date, "KR"), }, ] }
\ No newline at end of file diff --git a/lib/rfqs/validations.ts b/lib/rfqs/validations.ts index 7f39d0a6..8752f693 100644 --- a/lib/rfqs/validations.ts +++ b/lib/rfqs/validations.ts @@ -287,7 +287,7 @@ export type GetCBESchema = Awaited<ReturnType<typeof searchParamsCBECache.parse> export const createCbeEvaluationSchema = z.object({ - paymentTerms: z.string().min(1, "결제 조건을 입력하세요"), + paymentTerms: z.string().min(1, "지급 조건을 입력하세요"), incoterms: z.string().min(1, "Incoterms를 입력하세요"), deliverySchedule: z.string().min(1, "배송 일정을 입력하세요"), notes: z.string().optional(), diff --git a/lib/roles/table/roles-table-columns.tsx b/lib/roles/table/roles-table-columns.tsx index 3a491585..16249999 100644 --- a/lib/roles/table/roles-table-columns.tsx +++ b/lib/roles/table/roles-table-columns.tsx @@ -149,7 +149,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<RoleVie if (cfg.id === "created_at") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } if (cfg.id === "domain") { diff --git a/lib/roles/userTable/assginedUsers-table-columns.tsx b/lib/roles/userTable/assginedUsers-table-columns.tsx index b317a465..950c6d7a 100644 --- a/lib/roles/userTable/assginedUsers-table-columns.tsx +++ b/lib/roles/userTable/assginedUsers-table-columns.tsx @@ -106,7 +106,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<UserVie if (cfg.id === "created_at") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } if (cfg.id === "roles") { diff --git a/lib/sedp/sync-form copy.ts b/lib/sedp/sync-form copy.ts new file mode 100644 index 00000000..2cb677b7 --- /dev/null +++ b/lib/sedp/sync-form copy.ts @@ -0,0 +1,1037 @@ +// src/lib/cron/syncTagFormMappings.ts +import db from "@/db/db"; +import { projects, tagTypes, tagClasses, tagTypeClassFormMappings, formMetas, forms, contractItems, items, contracts } from '@/db/schema'; +import { eq, and, inArray, ilike } from 'drizzle-orm'; +import { getSEDPToken } from "./sedp-token"; + +// 환경 변수 +const SEDP_API_BASE_URL = process.env.SEDP_API_BASE_URL || 'http://sedpwebapi.ship.samsung.co.kr/api'; + +// 인터페이스 정의 +interface TagTypeClassFormMapping { + projectId: number; + tagTypeLabel: string; + classLabel: string; + formCode: string; + formName: string; + remark: string | null; + ep: string; + createdAt: Date; + updatedAt: Date; +} + +interface FormMeta { + projectId: number; + formCode: string; + formName: string; + columns: string; // JSON 문자열 + createdAt: Date; + updatedAt: Date; +} + +interface FormRecord { + contractItemId: number; + formCode: string; + formName: string; + eng: boolean; + createdAt: Date; + updatedAt: Date; +} +interface Register { + PROJ_NO: string; + TYPE_ID: string; + EP_ID: string; + DESC: string; + REMARK: string | null; + NEW_TAG_YN: boolean; + ALL_TAG_YN: boolean; + VND_YN: boolean; + SEQ: number; + CMPLX_YN: boolean; + CMPL_SETT: any | null; + MAP_ATT: any[]; + MAP_CLS_ID: string[]; + MAP_OPER: any | null; + LNK_ATT: LinkAttribute[]; + JOIN_TABLS: any[]; + DELETED: boolean; + CRTER_NO: string; + CRTE_DTM: string; + CHGER_NO: string | null; + CHGE_DTM: string | null; + _id: string; +} + +interface LinkAttribute { + ATT_ID: string; + CPY_DESC: string; + JOIN_KEY_ATT_ID: string | null; + JOIN_VAL_ATT_ID: string | null; + KEY_YN: boolean; + EDIT_YN: boolean; + PUB_YN: boolean; + VND_YN: boolean; + DEF_VAL: string | null; + UOM_ID: string | null; +} + +interface Attribute { + PROJ_NO: string; + ATT_ID: string; + DESC: string; + GROUP: string | null; + REMARK: string | null; + VAL_TYPE: string; + IGN_LIST_VAL: boolean; + CL_ID: string | null; + UOM_ID: string | null; + DEF_VAL: string | null; + MIN_VAL: number; + MAX_VAL: number; + ESS_YN: boolean; + SEQ: number; + FORMAT: string | null; + REG_EXPS: string | null; + ATTRIBUTES: any[]; + DELETED: boolean; + CRTER_NO: string; + CRTE_DTM: string; + CHGER_NO: string | null; + CHGE_DTM: string | null; + _id: string; +} + +interface CodeList { + PROJ_NO: string; + CL_ID: string; + DESC: string; + REMARK: string | null; + PRNT_CD_ID: string | null; + REG_TYPE_ID: string | null; + VAL_ATT_ID: string | null; + VALUES: CodeValue[]; + LNK_ATT: any[]; + DELETED: boolean; + CRTER_NO: string; + CRTE_DTM: string; + CHGER_NO: string | null; + CHGE_DTM: string | null; + _id: string; +} + +interface CodeValue { + PRNT_VALUE: string | null; + VALUE: string; + DESC: string; + REMARK: string; + USE_YN: boolean; + SEQ: number; + ATTRIBUTES: any[]; +} + +interface UOM { + PROJ_NO: string; + UOM_ID: string; + DESC: string; + SYMBOL: string; + CONV_RATE: number; + DELETED: boolean; + CRTER_NO: string; + CRTE_DTM: string; + CHGER_NO: string | null; + CHGE_DTM: string | null; + _id: string; +} + +interface Project { + id: number; + code: string; + name: string; + type?: string; + createdAt?: Date; + updatedAt?: Date; +} + +interface SyncResult { + project: string; + success: boolean; + count?: number; + error?: string; +} + +interface FormColumn { + key: string; + label: string; + type: string; + options?: string[]; + uom?: string; + uomId?: string; + shi?: Boolean; +} + +// 아이템 코드 추출 함수 +function extractItemCodes(remark: string | null): string[] { + if (!remark) return []; + + // 검색용으로만 소문자로 변환 + const remarkLower = remark.toLowerCase(); + + // 'vd_' 접두사 확인 + const hasVD_ = remarkLower.includes("vd_"); + + if (!hasVD_) return []; + + let vdPart = ""; + + // 'vd_'가 있으면 원본 문자열에서 추출 (소문자 버전이 아님) + if (hasVD_) { + const vdIndex = remarkLower.indexOf("vd_"); + vdPart = remark.substring(vdIndex + 3); // 원본 문자열에서 추출 + } + + if (!vdPart) return []; + + // 쉼표로 구분된 여러 itemCode 처리 + return vdPart.split(",").map(code => code.trim()); +} + +async function getDefaulTAttributes(): Promise<string[]> { + try { + const apiKey = await getSEDPToken(); + + const response = await fetch( + `${SEDP_API_BASE_URL}/Dictionary/GetByKey`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'accept': '*/*', + 'ApiKey': apiKey, + }, + body: JSON.stringify({ + Key: "DefaultAttributesToCompare", + }) + } + ); + + if (!response.ok) { + if (response.status === 404) { + console.warn(`디폴트 속성 찾을 수 없음`); + return []; + } + throw new Error(`코드 리스트 요청 실패: ${response.status} ${response.statusText}`); + } + + // 안전하게 JSON 파싱 + try { + const data = await response.json(); + // 데이터가 배열인지 확인하고 문자열 배열로 변환 + if (Array.isArray(data)) { + return data as string[]; + } else { + console.warn('응답이 배열 형식이 아닙니다'); + return []; + } + } catch (parseError) { + console.error(`디폴트 속성 응답 파싱 실패:`, parseError); + // 응답 내용 로깅 + try { + const text = await response.clone().text(); + console.log(`응답 내용: ${text.substring(0, 200)}${text.length > 200 ? '...' : ''}`); + } catch (textError) { + console.error('응답 내용 로깅 실패:', textError); + } + return []; + } + } catch (error) { + console.error(`디폴트 어트리뷰트 가져오기 실패:`, error); + throw error; + } +} + +// 레지스터 데이터 가져오기 +async function getRegisters(projectCode: string): Promise<Register[]> { + try { + // 토큰(API 키) 가져오기 + const apiKey = await getSEDPToken(); + + const response = await fetch( + `${SEDP_API_BASE_URL}/Register/Get`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'accept': '*/*', + 'ApiKey': apiKey, + 'ProjectNo': projectCode + }, + body: JSON.stringify({ + ProjectNo: projectCode, + ContainDeleted: false + }) + } + ); + + if (!response.ok) { + throw new Error(`레지스터 요청 실패: ${response.status} ${response.statusText}`); + } + + // 안전하게 JSON 파싱 + let data; + try { + data = await response.json(); + } catch (parseError) { + console.error(`프로젝트 ${projectCode}의 레지스터 응답 파싱 실패:`, parseError); + // 응답 내용 로깅 + const text = await response.clone().text(); + console.log(`응답 내용: ${text.substring(0, 200)}${text.length > 200 ? '...' : ''}`); + throw new Error(`레지스터 응답 파싱 실패: ${parseError instanceof Error ? parseError.message : String(parseError)}`); + } + + // 결과를 배열로 변환 (단일 객체인 경우 배열로 래핑) + let registers: Register[] = Array.isArray(data) ? data : [data]; + + // MAP_CLS_ID가 비어있지 않고 REMARK가 vd, VD, vD, Vd 중 하나인 레지스터만 필터링 + registers = registers.filter(register => { + // 삭제된 레지스터 제외 + if (register.DELETED) return false; + + // MAP_CLS_ID 배열이 존재하고 요소가 하나 이상 있는지 확인 + const hasValidMapClsId = Array.isArray(register.MAP_CLS_ID) && register.MAP_CLS_ID.length > 0; + + // REMARK가 'vd_' 또는 'vd' 포함 확인 (대소문자 구분 없이) + const remarkLower = register.REMARK && register.REMARK.toLowerCase(); + const hasValidRemark = remarkLower && (remarkLower.includes('vd')); + + // 두 조건 모두 충족해야 함 + return hasValidMapClsId && hasValidRemark; + }); + + console.log(`프로젝트 ${projectCode}에서 ${registers.length}개의 유효한 레지스터를 가져왔습니다.`); + return registers; + } catch (error) { + console.error(`프로젝트 ${projectCode}의 레지스터 가져오기 실패:`, error); + throw error; + } +} + +// 프로젝트의 모든 속성을 가져와 맵으로 반환 +async function getAttributes(projectCode: string): Promise<Map<string, Attribute>> { + try { + // 토큰(API 키) 가져오기 + const apiKey = await getSEDPToken(); + + const response = await fetch( + `${SEDP_API_BASE_URL}/Attributes/Get`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'accept': '*/*', + 'ApiKey': apiKey, + 'ProjectNo': projectCode + }, + body: JSON.stringify({ + ProjectNo: projectCode, + ContainDeleted: false + }) + } + ); + + if (!response.ok) { + throw new Error(`속성 요청 실패: ${response.status} ${response.statusText}`); + } + + // 안전하게 JSON 파싱 + try { + const data = await response.json(); + + // 데이터가 배열인지 확인 + const attributes: Attribute[] = Array.isArray(data) ? data : [data]; + + // ATT_ID로 효율적인 조회를 위한 맵 생성 + const attributeMap = new Map<string, Attribute>(); + for (const attribute of attributes) { + if (!attribute.DELETED) { + attributeMap.set(attribute.ATT_ID, attribute); + } + } + + console.log(`프로젝트 ${projectCode}에서 ${attributeMap.size}개의 속성을 가져왔습니다`); + return attributeMap; + + } catch (parseError) { + console.error(`프로젝트 ${projectCode}의 속성 응답 파싱 실패:`, parseError); + // 응답 내용 로깅 + try { + const text = await response.clone().text(); + console.log(`응답 내용: ${text.substring(0, 200)}${text.length > 200 ? '...' : ''}`); + } catch (textError) { + console.error('응답 내용 로깅 실패:', textError); + } + return new Map(); + } + } catch (error) { + console.error(`프로젝트 ${projectCode}의 속성 가져오기 실패:`, error); + return new Map(); + } +} + +// 특정 속성 가져오기 (하위 호환성을 위해 유지) +async function getAttributeById(projectCode: string, attributeId: string, register: string): Promise<Attribute | null> { + try { + // 토큰(API 키) 가져오기 + const apiKey = await getSEDPToken(); + + const response = await fetch( + `${SEDP_API_BASE_URL}/Attributes/GetByID`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'accept': '*/*', + 'ApiKey': apiKey, + 'ProjectNo': projectCode + }, + body: JSON.stringify({ + ProjectNo: projectCode, + ATT_ID: attributeId, + ContainDeleted: false + }) + } + ); + + if (!response.ok) { + if (response.status === 404) { + console.warn(`속성 ID ${attributeId}를 찾을 수 없음`); + return null; + } + throw new Error(`속성 요청 실패: ${response.status} ${response.statusText}`); + } + + // 안전하게 JSON 파싱 + try { + const data = await response.json(); + return data; + } catch (parseError) { + console.error(`속성 ID ${attributeId} ${register} ${projectCode} 응답 파싱 실패:`, parseError); + // 응답 내용 로깅 + try { + const text = await response.clone().text(); + console.log(`응답 내용: ${text.substring(0, 200)}${text.length > 200 ? '...' : ''}`); + } catch (textError) { + console.error('응답 내용 로깅 실패:', textError); + } + return null; + } + } catch (error) { + console.error(`속성 ID ${attributeId} 가져오기 실패:`, error); + return null; + } +} + +// 프로젝트의 모든 코드 리스트를 가져와 맵으로 반환 +async function getCodeLists(projectCode: string): Promise<Map<string, CodeList>> { + try { + // 토큰(API 키) 가져오기 + const apiKey = await getSEDPToken(); + + const response = await fetch( + `${SEDP_API_BASE_URL}/CodeList/Get`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'accept': '*/*', + 'ApiKey': apiKey, + 'ProjectNo': projectCode + }, + body: JSON.stringify({ + ProjectNo: projectCode, + ContainDeleted: false + }) + } + ); + + if (!response.ok) { + throw new Error(`코드 리스트 요청 실패: ${response.status} ${response.statusText}`); + } + + // 안전하게 JSON 파싱 + try { + const data = await response.json(); + + // 데이터가 배열인지 확인 + const codeLists: CodeList[] = Array.isArray(data) ? data : [data]; + + // CL_ID로 효율적인 조회를 위한 맵 생성 + const codeListMap = new Map<string, CodeList>(); + for (const codeList of codeLists) { + if (!codeList.DELETED) { + codeListMap.set(codeList.CL_ID, codeList); + } + } + + console.log(`프로젝트 ${projectCode}에서 ${codeListMap.size}개의 코드 리스트를 가져왔습니다`); + return codeListMap; + + } catch (parseError) { + console.error(`프로젝트 ${projectCode}의 코드 리스트 응답 파싱 실패:`, parseError); + // 응답 내용 로깅 + try { + const text = await response.clone().text(); + console.log(`응답 내용: ${text.substring(0, 200)}${text.length > 200 ? '...' : ''}`); + } catch (textError) { + console.error('응답 내용 로깅 실패:', textError); + } + return new Map(); + } + } catch (error) { + console.error(`프로젝트 ${projectCode}의 코드 리스트 가져오기 실패:`, error); + return new Map(); + } +} + +// 특정 코드 리스트 가져오기 (하위 호환성을 위해 유지) +async function getCodeListById(projectCode: string, codeListId: string): Promise<CodeList | null> { + try { + // 토큰(API 키) 가져오기 + const apiKey = await getSEDPToken(); + + const response = await fetch( + `${SEDP_API_BASE_URL}/CodeList/GetByID`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'accept': '*/*', + 'ApiKey': apiKey, + 'ProjectNo': projectCode + }, + body: JSON.stringify({ + ProjectNo: projectCode, + CL_ID: codeListId, + ContainDeleted: false + }) + } + ); + + if (!response.ok) { + if (response.status === 404) { + console.warn(`코드 리스트 ID ${codeListId}를 찾을 수 없음`); + return null; + } + throw new Error(`코드 리스트 요청 실패: ${response.status} ${response.statusText}`); + } + + // 안전하게 JSON 파싱 + try { + const data = await response.json(); + return data; + } catch (parseError) { + console.error(`코드 리스트 ID ${codeListId} 응답 파싱 실패:`, parseError); + // 응답 내용 로깅 + try { + const text = await response.clone().text(); + console.log(`응답 내용: ${text.substring(0, 200)}${text.length > 200 ? '...' : ''}`); + } catch (textError) { + console.error('응답 내용 로깅 실패:', textError); + } + return null; + } + } catch (error) { + console.error(`코드 리스트 ID ${codeListId} 가져오기 실패:`, error); + return null; + } +} + +// 프로젝트의 모든 UOM을 가져와 맵으로 반환 +async function getUOMs(projectCode: string): Promise<Map<string, UOM>> { + try { + // 토큰(API 키) 가져오기 + const apiKey = await getSEDPToken(); + + const response = await fetch( + `${SEDP_API_BASE_URL}/UOM/Get`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'accept': '*/*', + 'ApiKey': apiKey, + 'ProjectNo': projectCode + }, + body: JSON.stringify({ + ProjectNo: projectCode, + ContainDeleted: false + }) + } + ); + + if (!response.ok) { + throw new Error(`UOM 요청 실패: ${response.status} ${response.statusText}`); + } + + // 안전하게 JSON 파싱 + try { + const data = await response.json(); + + // 데이터가 배열인지 확인 + const uoms: UOM[] = Array.isArray(data) ? data : [data]; + + // UOM_ID로 효율적인 조회를 위한 맵 생성 + const uomMap = new Map<string, UOM>(); + for (const uom of uoms) { + if (!uom.DELETED) { + uomMap.set(uom.UOM_ID, uom); + } + } + + console.log(`프로젝트 ${projectCode}에서 ${uomMap.size}개의 UOM을 가져왔습니다`); + return uomMap; + + } catch (parseError) { + console.error(`프로젝트 ${projectCode}의 UOM 응답 파싱 실패:`, parseError); + // 응답 내용 로깅 + try { + const text = await response.clone().text(); + console.log(`응답 내용: ${text.substring(0, 200)}${text.length > 200 ? '...' : ''}`); + } catch (textError) { + console.error('응답 내용 로깅 실패:', textError); + } + return new Map(); + } + } catch (error) { + console.error(`프로젝트 ${projectCode}의 UOM 가져오기 실패:`, error); + return new Map(); + } +} + +// UOM 가져오기 (하위 호환성을 위해 유지) +async function getUomById(projectCode: string, uomId: string): Promise<UOM | null> { + try { + // 토큰(API 키) 가져오기 + const apiKey = await getSEDPToken(); + + const response = await fetch( + `${SEDP_API_BASE_URL}/UOM/GetByID`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'accept': '*/*', + 'ApiKey': apiKey, + 'ProjectNo': projectCode + }, + body: JSON.stringify({ + UOMID: uomId, // API 명세서에 따라 UOMID 사용 + ProjectNo: projectCode, + ContainDeleted: false + }) + } + ); + + if (!response.ok) { + if (response.status === 404) { + console.warn(`UOM ID ${uomId}를 찾을 수 없음`); + return null; + } + throw new Error(`UOM 요청 실패: ${response.status} ${response.statusText}`); + } + + // 안전하게 JSON 파싱 + try { + const data = await response.json(); + return data; + } catch (parseError) { + console.error(`UOM ID ${uomId} 응답 파싱 실패:`, parseError); + // 응답 내용 로깅 + try { + const text = await response.clone().text(); + console.log(`응답 내용: ${text.substring(0, 200)}${text.length > 200 ? '...' : ''}`); + } catch (textError) { + console.error('응답 내용 로깅 실패:', textError); + } + return null; + } + } catch (error) { + console.error(`UOM ID ${uomId} 가져오기 실패:`, error); + return null; + } +} + +// contractItemId 조회 함수 +async function getContractItemsByItemCodes(itemCodes: string[], projectId: number): Promise<Map<string, number>> { + try { + if (!itemCodes.length) return new Map(); + + // 먼저 itemCodes에 해당하는 item 레코드를 조회 + const itemRecords = await db.select({ + id: items.id, + itemCode: items.itemCode + }) + .from(items) + .where(inArray(items.itemCode, itemCodes)); + + if (!itemRecords.length) { + console.log(`No items found for itemCodes: ${itemCodes.join(', ')}`); + return new Map(); + } + + // item ID 목록 추출 + const itemIds = itemRecords.map(item => item.id); + + // contracts와 join하여 projectId로 필터링하면서 contractItems 조회 + const contractItemRecords = await db.select({ + id: contractItems.id, + itemId: contractItems.itemId + }) + .from(contractItems) + .innerJoin(contracts, eq(contractItems.contractId, contracts.id)) + .where( + and( + inArray(contractItems.itemId, itemIds), + eq(contracts.projectId, projectId) + ) + ); + + // itemCode와 contractItemId의 매핑 생성 + const itemCodeToContractItemId = new Map<string, number>(); + + for (const item of itemRecords) { + // itemCode가 null이 아닌 경우에만 처리 + if (item.itemCode) { + const matchedContractItems = contractItemRecords.filter(ci => ci.itemId === item.id); + if (matchedContractItems.length > 0) { + // 일치하는 첫 번째 contractItem 사용 + itemCodeToContractItemId.set(item.itemCode, matchedContractItems[0].id); + } + } + } + + return itemCodeToContractItemId; + } catch (error) { + console.error('ContractItems 조회 중 오류 발생:', error); + return new Map(); + } +} + +// 데이터베이스에 태그 타입 클래스 폼 매핑 및 폼 메타 저장 +async function saveFormMappingsAndMetas( + projectId: number, + projectCode: string, + registers: Register[] +): Promise<number> { + try { + // 프로젝트의 태그 타입과 클래스 가져오기 + const tagTypeRecords = await db.select() + .from(tagTypes) + .where(eq(tagTypes.projectId, projectId)); + + const tagClassRecords = await db.select() + .from(tagClasses) + .where(eq(tagClasses.projectId, projectId)); + + // 태그 타입과 클래스 매핑 + const tagTypeMap = new Map(tagTypeRecords.map(type => [type.code, type])); + const tagClassMap = new Map(tagClassRecords.map(cls => [cls.code, cls])); + + // 모든 속성, 코드 리스트, UOM을 한 번에 가져와 반복 API 호출 방지 + const attributeMap = await getAttributes(projectCode); + const codeListMap = await getCodeLists(projectCode); + const uomMap = await getUOMs(projectCode); + + // 기본 속성 가져오기 + const defaultAttributes = await getDefaulTAttributes(); + + // 모든 register에서 itemCode를 추출하여 한 번에 조회 + const allItemCodes: string[] = []; + registers.forEach(register => { + if (register.REMARK) { + const itemCodes = extractItemCodes(register.REMARK); + allItemCodes.push(...itemCodes); + } + }); + + // 중복 제거 + const uniqueItemCodes = [...new Set(allItemCodes)]; + + // 모든 itemCode에 대한 contractItemId 조회 + const itemCodeToContractItemId = await getContractItemsByItemCodes(uniqueItemCodes , projectId); + + console.log(`${uniqueItemCodes.length}개의 고유 itemCode 중 ${itemCodeToContractItemId.size}개의 contractItem을 찾았습니다`); + + // 저장할 데이터 준비 + const mappingsToSave: TagTypeClassFormMapping[] = []; + const formMetasToSave: FormMeta[] = []; + const formsToSave: FormRecord[] = []; + + // 폼이 있는 contractItemId 트래킹 + const contractItemIdsWithForms = new Set<number>(); + + // 각 register 처리 + for (const register of registers) { + // 삭제된 register 건너뛰기 + if (register.DELETED) continue; + + // REMARK에서 itemCodes 추출 + + + // 폼 메타용 columns 구성 + const columns: FormColumn[] = []; + + for (const linkAtt of register.LNK_ATT) { + let attribute = null; + + // 기본 속성인지 확인 + if (defaultAttributes && defaultAttributes.includes(linkAtt.ATT_ID)) { + // 기본 속성에 대한 기본 attribute 객체 생성 + attribute = { + DESC: linkAtt.ATT_ID, + VAL_TYPE: 'STRING' + }; + } else { + // 맵에서 속성 조회 + attribute = attributeMap.get(linkAtt.ATT_ID); + + // 속성을 찾지 못한 경우 다음으로 넘어감 + if (!attribute) continue; + } + + // 컬럼 정보 생성 + const column: FormColumn = { + key: linkAtt.ATT_ID, + label: attribute.DESC, + type: (attribute.VAL_TYPE === 'LIST' || attribute.VAL_TYPE === 'DYNAMICLIST') + ? 'LIST' + : (attribute.VAL_TYPE || 'STRING'), + shi: attribute.REMARK?.toLocaleLowerCase() === "shi" + }; + + // 리스트 타입에 대한 옵션 추가 (기본 속성이 아닌 경우) + if (!defaultAttributes.includes(linkAtt.ATT_ID) && + (attribute.VAL_TYPE === 'LIST' || attribute.VAL_TYPE === 'DYNAMICLIST') && + attribute.CL_ID) { + + // 맵에서 코드 리스트 조회 + const codeList = codeListMap.get(attribute.CL_ID); + + if (codeList && codeList.VALUES) { + const options = [...new Set( + codeList.VALUES + .filter(value => value.USE_YN) + .map(value => value.VALUE) + )]; + + if (options.length > 0) { + column.options = options; + } + } + } + + // UOM 정보 추가 + if (linkAtt.UOM_ID) { + const uom = uomMap.get(linkAtt.UOM_ID); + + if (uom) { + column.uom = uom.SYMBOL; + column.uomId = uom.UOM_ID; + } + } + + columns.push(column); + } + + // 컬럼이 없으면 건너뛰기 + if (columns.length === 0) { + console.log(`폼 ${register.TYPE_ID} (${register.DESC})에 컬럼이 없어 건너뜁니다`); + continue; + } + + // 폼 메타 데이터 준비 + formMetasToSave.push({ + projectId, + formCode: register.TYPE_ID, + formName: register.DESC, + columns: JSON.stringify(columns), + createdAt: new Date(), + updatedAt: new Date() + }); + + // 클래스 매핑 처리 + for (const classId of register.MAP_CLS_ID) { + const tagClass = tagClassMap.get(classId); + + if (!tagClass) { + console.warn(`프로젝트 ID ${projectId}에서 클래스 ID ${classId}를 찾을 수 없습니다`); + continue; + } + + const tagTypeCode = tagClass.tagTypeCode; + const tagType = tagTypeMap.get(tagTypeCode); + + if (!tagType) { + console.warn(`프로젝트 ID ${projectId}에서 태그 타입 ${tagTypeCode}를 찾을 수 없습니다`); + continue; + } + + // 매핑 정보 저장 + mappingsToSave.push({ + projectId, + tagTypeLabel: tagType.description, + classLabel: tagClass.label, + formCode: register.TYPE_ID, + formName: register.DESC, + remark: register.REMARK, + ep: register.EP_ID, + createdAt: new Date(), + updatedAt: new Date() + }); + } + + const itemCodes = extractItemCodes(register.REMARK || ''); + if (!itemCodes.length) { + console.log(`Register ${register.TYPE_ID} (${register.DESC})의 REMARK에 유효한 itemCode가 없습니다`); + continue; + } + // 폼 레코드 준비 + for (const itemCode of itemCodes) { + const contractItemId = itemCodeToContractItemId.get(itemCode); + + if (!contractItemId) { + console.warn(`itemCode: ${itemCode}에 대한 contractItemId를 찾을 수 없습니다`); + continue; + } + + // 폼이 있는 contractItemId 추적 + contractItemIdsWithForms.add(contractItemId); + + formsToSave.push({ + contractItemId, + formCode: register.TYPE_ID, + formName: register.DESC, + eng: true, + createdAt: new Date(), + updatedAt: new Date() + }); + } + } + + // 트랜잭션으로 모든 작업 처리 + let totalSaved = 0; + + await db.transaction(async (tx) => { + // 기존 데이터 삭제 + await tx.delete(tagTypeClassFormMappings).where(eq(tagTypeClassFormMappings.projectId, projectId)); + await tx.delete(formMetas).where(eq(formMetas.projectId, projectId)); + + // 해당 contractItemId에 대한 기존 폼 삭제 + if (contractItemIdsWithForms.size > 0) { + await tx.delete(forms).where(inArray(forms.contractItemId, [...contractItemIdsWithForms])); + } + + // 매핑 저장 + if (mappingsToSave.length > 0) { + await tx.insert(tagTypeClassFormMappings).values(mappingsToSave); + totalSaved += mappingsToSave.length; + console.log(`프로젝트 ID ${projectId}에 대해 ${mappingsToSave.length}개의 태그 타입-클래스-폼 매핑을 저장했습니다`); + } + + // 폼 메타 저장 + if (formMetasToSave.length > 0) { + await tx.insert(formMetas).values(formMetasToSave); + totalSaved += formMetasToSave.length; + console.log(`프로젝트 ID ${projectId}에 대해 ${formMetasToSave.length}개의 폼 메타 레코드를 저장했습니다`); + } + + // 폼 레코드 저장 + if (formsToSave.length > 0) { + await tx.insert(forms).values(formsToSave); + totalSaved += formsToSave.length; + console.log(`프로젝트 ID ${projectId}에 대해 ${formsToSave.length}개의 폼 레코드를 저장했습니다`); + } + }); + + return totalSaved; + } catch (error) { + console.error(`폼 매핑 및 메타 저장 실패 (프로젝트 ID: ${projectId}):`, error); + throw error; + } +} + +// 메인 동기화 함수 +export async function syncTagFormMappings() { + try { + console.log('태그 폼 매핑 동기화 시작:', new Date().toISOString()); + + // 모든 프로젝트 가져오기 + const allProjects = await db.select().from(projects); + + // 각 프로젝트에 대해 폼 매핑 동기화 + const results = await Promise.allSettled( + allProjects.map(async (project: Project) => { + try { + // 레지스터 데이터 가져오기 + const registers = await getRegisters(project.code); + + // 데이터베이스에 저장 + const count = await saveFormMappingsAndMetas(project.id, project.code, registers); + return { + project: project.code, + success: true, + count + } as SyncResult; + } catch (error) { + console.error(`프로젝트 ${project.code} 폼 매핑 동기화 실패:`, error); + return { + project: project.code, + success: false, + error: error instanceof Error ? error.message : String(error) + } as SyncResult; + } + }) + ); + + // 결과 처리를 위한 배열 준비 + const successfulResults: SyncResult[] = []; + const failedResults: SyncResult[] = []; + + // 결과 분류 + results.forEach((result) => { + if (result.status === 'fulfilled') { + if (result.value.success) { + successfulResults.push(result.value); + } else { + failedResults.push(result.value); + } + } else { + // 거부된 프로미스는 실패로 간주 + failedResults.push({ + project: 'unknown', + success: false, + error: result.reason?.toString() || 'Unknown error' + }); + } + }); + + const successCount = successfulResults.length; + const failCount = failedResults.length; + + // 이제 안전하게 count 속성에 접근 가능 + const totalItems = successfulResults.reduce((sum, result) => + sum + (result.count || 0), 0 + ); + + console.log(`태그 폼 매핑 동기화 완료: ${successCount}개 프로젝트 성공 (총 ${totalItems}개 항목), ${failCount}개 프로젝트 실패`); + + return { + success: successCount, + failed: failCount, + items: totalItems, + timestamp: new Date().toISOString() + }; + } catch (error) { + console.error('태그 폼 매핑 동기화 중 오류 발생:', error); + throw error; + } +}
\ No newline at end of file diff --git a/lib/sedp/sync-form.ts b/lib/sedp/sync-form.ts index 2cb677b7..c293c98e 100644 --- a/lib/sedp/sync-form.ts +++ b/lib/sedp/sync-form.ts @@ -1,6 +1,6 @@ // src/lib/cron/syncTagFormMappings.ts import db from "@/db/db"; -import { projects, tagTypes, tagClasses, tagTypeClassFormMappings, formMetas, forms, contractItems, items, contracts } from '@/db/schema'; +import { projects, tagTypes, tagClasses, tagTypeClassFormMappings, formMetas, forms, contractItems, items, contracts, templateItems } from '@/db/schema'; import { eq, and, inArray, ilike } from 'drizzle-orm'; import { getSEDPToken } from "./sedp-token"; @@ -37,6 +37,51 @@ interface FormRecord { createdAt: Date; updatedAt: Date; } + +interface TemplateItem { + TMPL_ID: string; + NAME: string; + TMPL_TYPE: string; + SPR_LST_SETUP: { + ACT_SHEET: string; + HIDN_SHEETS: Array<string>; + CONTENT?: string; + DATA_SHEETS: Array<{ + SHEET_NAME: string; + REG_TYPE_ID: string; + MAP_CELL_ATT: Array<{ + ATT_ID: string; + IN: string; + }>; + }>; + }; + GRD_LST_SETUP: { + REG_TYPE_ID: string; + SPR_ITM_IDS: Array<string>; + ATTS: Array<{ + ATT_ID: string; + ALIAS: string; + HEAD_TEXT: string; + HIDN_YN: boolean; + SEQ:number; + DFLT_YN:boolean; + }>; + }; + SPR_ITM_LST_SETUP: { + ACT_SHEET: string; + HIDN_SHEETS: Array<string>; + CONTENT?: string; + DATA_SHEETS: Array<{ + SHEET_NAME: string; + REG_TYPE_ID: string; + MAP_CELL_ATT: Array<{ + ATT_ID: string; + IN: string; + }>; + }>; + }; +} + interface Register { PROJ_NO: string; TYPE_ID: string; @@ -75,6 +120,42 @@ interface LinkAttribute { UOM_ID: string | null; } +interface newRegister { + PROJ_NO: string; + MAP_ID: string; + EP_ID: string; + CATEGORY: string; + BYPASS: boolean; + REG_TYPE_ID: string; + TOOL_ID: string; + TOOL_TYPE: string; + MAP_CLS: { + TOOL_ATT_NAME: string; + ITEMS: ClassItmes[]; + }; + MAP_ATT: MapAttribute[]; + MAP_TMPLS: string[]; + CRTER_NO: string; + CRTE_DTM: string; + CHGER_NO: string; + _id: string; +} + + + +interface ClassItmes { + SEDP_OBJ_CLS_ID: string; + TOOL_VALS: string; + ISDEFALUT: boolean; +} + +interface MapAttribute { + SEDP_ATT_ID: string; + TOOL_ATT_NAME: string; + KEY_YN: boolean; + INOUT: string | null; +} + interface Attribute { PROJ_NO: string; ATT_ID: string; @@ -167,6 +248,9 @@ interface FormColumn { uom?: string; uomId?: string; shi?: Boolean; + hidden?: boolean; + seq?: number; + head?: string; } // 아이템 코드 추출 함수 @@ -249,6 +333,63 @@ async function getDefaulTAttributes(): Promise<string[]> { } } +async function fetchTemplateFromSEDP(projectCode: string, formCode: string): Promise<TemplateItem[]> { + try { + // Get the token + const apiKey = await getSEDPToken(); + + // Make the API call + const response = await fetch( + `${SEDP_API_BASE_URL}/Template/GetByRegisterID`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'accept': '*/*', + 'ApiKey': apiKey, + 'ProjectNo': projectCode + }, + body: JSON.stringify({ + WithContent: true, + ProjectNo: projectCode, + REG_TYPE_ID: formCode + }) + } + ); + + if (!response.ok) { + if (response.status === 404) { + console.warn(`템플릿을 찾을 수 없음: ${formCode}`); + return []; + } + const errorText = await response.text(); + throw new Error(`SEDP Template API request failed: ${response.status} ${response.statusText} - ${errorText}`); + } + + // 안전하게 JSON 파싱 + try { + const data = await response.json(); + // 데이터가 배열인지 확인 + const templates: TemplateItem[] = Array.isArray(data) ? data : [data]; + return templates.filter(template => template && template.TMPL_ID); + } catch (parseError) { + console.error(`템플릿 응답 파싱 실패:`, parseError); + // 응답 내용 로깅 + try { + const text = await response.clone().text(); + console.log(`응답 내용: ${text.substring(0, 200)}${text.length > 200 ? '...' : ''}`); + } catch (textError) { + console.error('응답 내용 로깅 실패:', textError); + } + return []; + } + } catch (error: any) { + console.error('Error calling SEDP Template API:', error); + console.warn(`템플릿 가져오기 실패 (${formCode}): ${error.message || 'Unknown error'}`); + return []; + } +} + // 레지스터 데이터 가져오기 async function getRegisters(projectCode: string): Promise<Register[]> { try { @@ -315,6 +456,55 @@ async function getRegisters(projectCode: string): Promise<Register[]> { } } +async function getNewRegisters(projectCode: string): Promise<newRegister[]> { + try { + // 토큰(API 키) 가져오기 + const apiKey = await getSEDPToken(); + + const response = await fetch( + `${SEDP_API_BASE_URL}/AdapterDataMapping/GetByToolID`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'accept': '*/*', + 'ApiKey': apiKey, + 'ProjectNo': projectCode + }, + body: JSON.stringify({ + ProjectNo: projectCode, + "TOOL_ID": "eVCP" + }) + } + ); + + if (!response.ok) { + throw new Error(`새 레지스터 요청 실패: ${response.status} ${response.statusText}`); + } + + // 안전하게 JSON 파싱 + let data; + try { + data = await response.json(); + } catch (parseError) { + console.error(`프로젝트 ${projectCode}의 새 레지스터 응답 파싱 실패:`, parseError); + // 응답 내용 로깅 + const text = await response.clone().text(); + console.log(`응답 내용: ${text.substring(0, 200)}${text.length > 200 ? '...' : ''}`); + throw new Error(`새 레지스터 응답 파싱 실패: ${parseError instanceof Error ? parseError.message : String(parseError)}`); + } + + // 결과를 배열로 변환 (단일 객체인 경우 배열로 래핑) + let registers: newRegister[] = Array.isArray(data) ? data : [data]; + + console.log(`프로젝트 ${projectCode}에서 ${registers.length}개의 새 레지스터를 가져왔습니다.`); + return registers; + } catch (error) { + console.error(`프로젝트 ${projectCode}의 새 레지스터 가져오기 실패:`, error); + throw error; + } +} + // 프로젝트의 모든 속성을 가져와 맵으로 반환 async function getAttributes(projectCode: string): Promise<Map<string, Attribute>> { try { @@ -716,249 +906,167 @@ async function getContractItemsByItemCodes(itemCodes: string[], projectId: numbe } } -// 데이터베이스에 태그 타입 클래스 폼 매핑 및 폼 메타 저장 -async function saveFormMappingsAndMetas( +// UPDATED: saveFormMappingsAndMetas() +// ------------------------------------ +// Primary loop is **newRegisters**‑first; legacy **registers** are consulted +// only for supplemental details. +// +// 2025‑07‑09 fix: newRegister.MAP_CLS is a **single object**, not an array. +// Updated class‑ID extraction accordingly. +// +export async function saveFormMappingsAndMetas( projectId: number, projectCode: string, - registers: Register[] + registers: Register[], // legacy SEDP Register list (supplemental) + newRegisters: newRegister[] // AdapterDataMapping list (primary) ): Promise<number> { try { - // 프로젝트의 태그 타입과 클래스 가져오기 - const tagTypeRecords = await db.select() - .from(tagTypes) - .where(eq(tagTypes.projectId, projectId)); + /* ------------------------------------------------------------------ */ + /* 1. Prepare look‑up structures & common data */ + /* ------------------------------------------------------------------ */ - const tagClassRecords = await db.select() - .from(tagClasses) - .where(eq(tagClasses.projectId, projectId)); + const tagTypeRecords = await db.select().from(tagTypes).where(eq(tagTypes.projectId, projectId)); + const tagClassRecords = await db.select().from(tagClasses).where(eq(tagClasses.projectId, projectId)); + const tagTypeMap = new Map(tagTypeRecords.map(t => [t.code, t])); + const tagClassMap = new Map(tagClassRecords.map(c => [c.code, c])); - // 태그 타입과 클래스 매핑 - const tagTypeMap = new Map(tagTypeRecords.map(type => [type.code, type])); - const tagClassMap = new Map(tagClassRecords.map(cls => [cls.code, cls])); + const registerMap = new Map(registers.map(r => [r.TYPE_ID, r])); - // 모든 속성, 코드 리스트, UOM을 한 번에 가져와 반복 API 호출 방지 const attributeMap = await getAttributes(projectCode); - const codeListMap = await getCodeLists(projectCode); - const uomMap = await getUOMs(projectCode); - - // 기본 속성 가져오기 + const codeListMap = await getCodeLists(projectCode); + const uomMap = await getUOMs(projectCode); const defaultAttributes = await getDefaulTAttributes(); - // 모든 register에서 itemCode를 추출하여 한 번에 조회 - const allItemCodes: string[] = []; - registers.forEach(register => { - if (register.REMARK) { - const itemCodes = extractItemCodes(register.REMARK); - allItemCodes.push(...itemCodes); - } - }); - - // 중복 제거 - const uniqueItemCodes = [...new Set(allItemCodes)]; - - // 모든 itemCode에 대한 contractItemId 조회 - const itemCodeToContractItemId = await getContractItemsByItemCodes(uniqueItemCodes , projectId); - - console.log(`${uniqueItemCodes.length}개의 고유 itemCode 중 ${itemCodeToContractItemId.size}개의 contractItem을 찾았습니다`); + /* ------------------------------------------------------------------ */ + /* 2. Contract‑item look‑up (TOOL_TYPE) */ + /* ------------------------------------------------------------------ */ + const uniqueItemCodes = [...new Set(newRegisters.filter(nr => nr.TOOL_TYPE).map(nr => nr.TOOL_TYPE as string))]; + const itemCodeToContractItemId = await getContractItemsByItemCodes(uniqueItemCodes, projectId); - // 저장할 데이터 준비 + /* ------------------------------------------------------------------ */ + /* 3. Buffers for bulk insert */ + /* ------------------------------------------------------------------ */ const mappingsToSave: TagTypeClassFormMapping[] = []; const formMetasToSave: FormMeta[] = []; const formsToSave: FormRecord[] = []; - - // 폼이 있는 contractItemId 트래킹 const contractItemIdsWithForms = new Set<number>(); + const templateDataByFormCode: Map<string, TemplateItem[]> = new Map(); - // 각 register 처리 - for (const register of registers) { - // 삭제된 register 건너뛰기 - if (register.DELETED) continue; + /* ------------------------------------------------------------------ */ + /* 4. Iterate over newRegisters */ + /* ------------------------------------------------------------------ */ + for (const newReg of newRegisters) { + const formCode = newReg.REG_TYPE_ID; + const legacy = registerMap.get(formCode); - // REMARK에서 itemCodes 추출 + /* ---------- 4‑a. templates ------------------------------------ */ + let templates: TemplateItem[] = []; + try { + const fetched = await fetchTemplateFromSEDP(projectCode, formCode); + templates = fetched.filter(t => (newReg.MAP_TMPLS?.length ? newReg.MAP_TMPLS.includes(t.TMPL_ID) : true)); + if (templates.length) templateDataByFormCode.set(formCode, templates); + } catch (e) { + console.warn(`템플릿 가져오기 실패 (${formCode})`, e); + } + const templateAttrMap = new Map<string, { hidden: boolean; seq: number; head: string }>(); + templates.forEach(t => t.GRD_LST_SETUP?.ATTS?.forEach(att => { + if (!templateAttrMap.has(att.ATT_ID)) templateAttrMap.set(att.ATT_ID, { hidden: att.HIDN_YN, seq: att.SEQ, head: att.HEAD_TEXT }); + })); - // 폼 메타용 columns 구성 + /* ---------- 4‑b. columns -------------------------------------- */ const columns: FormColumn[] = []; - - for (const linkAtt of register.LNK_ATT) { - let attribute = null; - - // 기본 속성인지 확인 - if (defaultAttributes && defaultAttributes.includes(linkAtt.ATT_ID)) { - // 기본 속성에 대한 기본 attribute 객체 생성 - attribute = { - DESC: linkAtt.ATT_ID, - VAL_TYPE: 'STRING' - }; - } else { - // 맵에서 속성 조회 - attribute = attributeMap.get(linkAtt.ATT_ID); - - // 속성을 찾지 못한 경우 다음으로 넘어감 - if (!attribute) continue; + for (const mapAtt of newReg.MAP_ATT) { + const attId = mapAtt.SEDP_ATT_ID; + const attribute = defaultAttributes.includes(attId) ? { DESC: attId, VAL_TYPE: "STRING" } as Partial<Attribute> : attributeMap.get(attId); + if (!attribute) continue; + + const tmplMeta = templateAttrMap.get(attId); + const isShi = mapAtt.INOUT === "OUT"; + + let uomSymbol: string | undefined; let uomId: string | undefined; + if (legacy?.LNK_ATT) { + const l = legacy.LNK_ATT.find(a => a.ATT_ID === attId); + if (l?.UOM_ID) { const u = uomMap.get(l.UOM_ID); if (u) { uomSymbol = u.SYMBOL; uomId = u.UOM_ID; } } } - // 컬럼 정보 생성 - const column: FormColumn = { - key: linkAtt.ATT_ID, - label: attribute.DESC, - type: (attribute.VAL_TYPE === 'LIST' || attribute.VAL_TYPE === 'DYNAMICLIST') - ? 'LIST' - : (attribute.VAL_TYPE || 'STRING'), - shi: attribute.REMARK?.toLocaleLowerCase() === "shi" + const col: FormColumn = { + key: attId, + label: attribute.DESC as string, + type: (attribute.VAL_TYPE === "LIST" || attribute.VAL_TYPE === "DYNAMICLIST") ? "LIST" : (attribute.VAL_TYPE || "STRING"), + shi: isShi, + hidden: tmplMeta?.hidden ?? false, + seq: tmplMeta?.seq ?? 0, + head: tmplMeta?.head ?? "", + ...(uomSymbol ? { uom: uomSymbol, uomId } : {}) }; - // 리스트 타입에 대한 옵션 추가 (기본 속성이 아닌 경우) - if (!defaultAttributes.includes(linkAtt.ATT_ID) && - (attribute.VAL_TYPE === 'LIST' || attribute.VAL_TYPE === 'DYNAMICLIST') && - attribute.CL_ID) { - - // 맵에서 코드 리스트 조회 - const codeList = codeListMap.get(attribute.CL_ID); - - if (codeList && codeList.VALUES) { - const options = [...new Set( - codeList.VALUES - .filter(value => value.USE_YN) - .map(value => value.VALUE) - )]; - - if (options.length > 0) { - column.options = options; - } - } + if (!defaultAttributes.includes(attId) && (attribute.VAL_TYPE === "LIST" || attribute.VAL_TYPE === "DYNAMICLIST") && attribute.CL_ID) { + const cl = codeListMap.get(attribute.CL_ID); + if (cl?.VALUES?.length) col.options = [...new Set(cl.VALUES.filter(v => v.USE_YN).map(v => v.VALUE))]; } - // UOM 정보 추가 - if (linkAtt.UOM_ID) { - const uom = uomMap.get(linkAtt.UOM_ID); - - if (uom) { - column.uom = uom.SYMBOL; - column.uomId = uom.UOM_ID; - } - } - - columns.push(column); - } - - // 컬럼이 없으면 건너뛰기 - if (columns.length === 0) { - console.log(`폼 ${register.TYPE_ID} (${register.DESC})에 컬럼이 없어 건너뜁니다`); - continue; + columns.push(col); } + if (!columns.length) { console.log(`폼 ${formCode} 건너뜀 (컬럼 없음)`); continue; } + columns.sort((a, b) => (a.seq ?? 999999) - (b.seq ?? 999999)); - // 폼 메타 데이터 준비 - formMetasToSave.push({ - projectId, - formCode: register.TYPE_ID, - formName: register.DESC, - columns: JSON.stringify(columns), - createdAt: new Date(), - updatedAt: new Date() - }); - - // 클래스 매핑 처리 - for (const classId of register.MAP_CLS_ID) { - const tagClass = tagClassMap.get(classId); - - if (!tagClass) { - console.warn(`프로젝트 ID ${projectId}에서 클래스 ID ${classId}를 찾을 수 없습니다`); - continue; - } - - const tagTypeCode = tagClass.tagTypeCode; - const tagType = tagTypeMap.get(tagTypeCode); - - if (!tagType) { - console.warn(`프로젝트 ID ${projectId}에서 태그 타입 ${tagTypeCode}를 찾을 수 없습니다`); - continue; - } + formMetasToSave.push({ projectId, formCode, formName: legacy?.DESC || formCode, columns: JSON.stringify(columns), createdAt: new Date(), updatedAt: new Date() }); - // 매핑 정보 저장 - mappingsToSave.push({ - projectId, - tagTypeLabel: tagType.description, - classLabel: tagClass.label, - formCode: register.TYPE_ID, - formName: register.DESC, - remark: register.REMARK, - ep: register.EP_ID, - createdAt: new Date(), - updatedAt: new Date() - }); + /* ---------- 4‑c. class mappings -------------------------------- */ + const classIds = new Set<string>(); + if (newReg.MAP_CLS?.ITEMS?.length) { + newReg.MAP_CLS.ITEMS.forEach(it => classIds.add(it.SEDP_OBJ_CLS_ID)); } - const itemCodes = extractItemCodes(register.REMARK || ''); - if (!itemCodes.length) { - console.log(`Register ${register.TYPE_ID} (${register.DESC})의 REMARK에 유효한 itemCode가 없습니다`); - continue; - } - // 폼 레코드 준비 - for (const itemCode of itemCodes) { - const contractItemId = itemCodeToContractItemId.get(itemCode); - - if (!contractItemId) { - console.warn(`itemCode: ${itemCode}에 대한 contractItemId를 찾을 수 없습니다`); - continue; - } - - // 폼이 있는 contractItemId 추적 - contractItemIdsWithForms.add(contractItemId); + classIds.forEach(classId => { + const cls = tagClassMap.get(classId); + if (!cls) { console.warn(`클래스 ${classId} 없음`); return; } + const tp = tagTypeMap.get(cls.tagTypeCode); + if (!tp) { console.warn(`태그 타입 ${cls.tagTypeCode} 없음`); return; } + mappingsToSave.push({ projectId, tagTypeLabel: tp.description, classLabel: cls.label, formCode, formName: legacy?.DESC || formCode, remark: newReg.TOOL_TYPE || null, ep: newReg.EP_ID || legacy?.EP_ID || "", createdAt: new Date(), updatedAt: new Date() }); + }); - formsToSave.push({ - contractItemId, - formCode: register.TYPE_ID, - formName: register.DESC, - eng: true, - createdAt: new Date(), - updatedAt: new Date() - }); + /* ---------- 4‑d. contractItem ↔ form --------------------------- */ + if (newReg.TOOL_TYPE) { + const cId = itemCodeToContractItemId.get(newReg.TOOL_TYPE); + if (cId) { contractItemIdsWithForms.add(cId); formsToSave.push({ contractItemId: cId, formCode, formName: legacy?.DESC || formCode, eng: true, createdAt: new Date(), updatedAt: new Date() }); } + else console.warn(`itemCode ${newReg.TOOL_TYPE} 의 contractItemId 없음`); } } - // 트랜잭션으로 모든 작업 처리 + /* ------------------------------------------------------------------ */ + /* 5. DB transaction */ + /* ------------------------------------------------------------------ */ let totalSaved = 0; - - await db.transaction(async (tx) => { - // 기존 데이터 삭제 + await db.transaction(async tx => { + const old = await tx.select({ id: tagTypeClassFormMappings.id }).from(tagTypeClassFormMappings).where(eq(tagTypeClassFormMappings.projectId, projectId)); + if (old.length) await tx.delete(templateItems).where(inArray(templateItems.formMappingId, old.map(o => o.id))); await tx.delete(tagTypeClassFormMappings).where(eq(tagTypeClassFormMappings.projectId, projectId)); await tx.delete(formMetas).where(eq(formMetas.projectId, projectId)); + if (contractItemIdsWithForms.size) await tx.delete(forms).where(inArray(forms.contractItemId, [...contractItemIdsWithForms])); - // 해당 contractItemId에 대한 기존 폼 삭제 - if (contractItemIdsWithForms.size > 0) { - await tx.delete(forms).where(inArray(forms.contractItemId, [...contractItemIdsWithForms])); - } - - // 매핑 저장 - if (mappingsToSave.length > 0) { - await tx.insert(tagTypeClassFormMappings).values(mappingsToSave); - totalSaved += mappingsToSave.length; - console.log(`프로젝트 ID ${projectId}에 대해 ${mappingsToSave.length}개의 태그 타입-클래스-폼 매핑을 저장했습니다`); - } + const savedMappings = mappingsToSave.length ? await tx.insert(tagTypeClassFormMappings).values(mappingsToSave).returning({ id: tagTypeClassFormMappings.id, formCode: tagTypeClassFormMappings.formCode }) : []; + totalSaved += mappingsToSave.length; - // 폼 메타 저장 - if (formMetasToSave.length > 0) { - await tx.insert(formMetas).values(formMetasToSave); - totalSaved += formMetasToSave.length; - console.log(`프로젝트 ID ${projectId}에 대해 ${formMetasToSave.length}개의 폼 메타 레코드를 저장했습니다`); + if (savedMappings.length) { + const rows: any[] = []; + savedMappings.forEach(m => (templateDataByFormCode.get(m.formCode) || []).forEach(t => rows.push({ formMappingId: m.id, tmplId: t.TMPL_ID, name: t.NAME, tmplType: t.TMPL_TYPE, sprLstSetup: t.SPR_LST_SETUP, grdLstSetup: t.GRD_LST_SETUP, sprItmLstSetup: t.SPR_ITM_LST_SETUP, description: `Template for form ${m.formCode}`, isActive: true, createdAt: new Date(), updatedAt: new Date() }))); + if (rows.length) { await tx.insert(templateItems).values(rows); totalSaved += rows.length; } } - // 폼 레코드 저장 - if (formsToSave.length > 0) { - await tx.insert(forms).values(formsToSave); - totalSaved += formsToSave.length; - console.log(`프로젝트 ID ${projectId}에 대해 ${formsToSave.length}개의 폼 레코드를 저장했습니다`); - } + if (formMetasToSave.length) { await tx.insert(formMetas).values(formMetasToSave); totalSaved += formMetasToSave.length; } + if (formsToSave.length) { await tx.insert(forms).values(formsToSave); totalSaved += formsToSave.length; } }); return totalSaved; - } catch (error) { - console.error(`폼 매핑 및 메타 저장 실패 (프로젝트 ID: ${projectId}):`, error); - throw error; + } catch (err) { + console.error(`폼 매핑 및 메타 저장 실패 (프로젝트 ID:${projectId})`, err); + throw err; } } + // 메인 동기화 함수 export async function syncTagFormMappings() { try { @@ -973,9 +1081,10 @@ export async function syncTagFormMappings() { try { // 레지스터 데이터 가져오기 const registers = await getRegisters(project.code); + const newRegisters = await getNewRegisters(project.code); // 데이터베이스에 저장 - const count = await saveFormMappingsAndMetas(project.id, project.code, registers); + const count = await saveFormMappingsAndMetas(project.id, project.code, registers, newRegisters); return { project: project.code, success: true, diff --git a/lib/tag-numbering/table/tagNumbering-table-columns.tsx b/lib/tag-numbering/table/tagNumbering-table-columns.tsx index 6e9b8191..1f3abfc4 100644 --- a/lib/tag-numbering/table/tagNumbering-table-columns.tsx +++ b/lib/tag-numbering/table/tagNumbering-table-columns.tsx @@ -90,7 +90,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<ViewTag if (cfg.id === "createdAt"||cfg.id === "updatedAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } return row.getValue(cfg.id) ?? "" diff --git a/lib/tags/table/tag-table-column.tsx b/lib/tags/table/tag-table-column.tsx index 47746000..80c25464 100644 --- a/lib/tags/table/tag-table-column.tsx +++ b/lib/tags/table/tag-table-column.tsx @@ -100,7 +100,7 @@ export function getColumns({ header: ({ column }) => ( <DataTableColumnHeaderSimple column={column} title="Created At" /> ), - cell: ({ cell }) => formatDate(cell.getValue() as Date), + cell: ({ cell }) => formatDate(cell.getValue() as Date, "KR"), meta: { excelHeader: "created At" }, @@ -113,7 +113,7 @@ export function getColumns({ header: ({ column }) => ( <DataTableColumnHeaderSimple column={column} title="Updated At" /> ), - cell: ({ cell }) => formatDate(cell.getValue() as Date), + cell: ({ cell }) => formatDate(cell.getValue() as Date, "KR"), meta: { excelHeader: "updated At" }, diff --git a/lib/tasks/table/tasks-table-columns.tsx b/lib/tasks/table/tasks-table-columns.tsx index 3737c2e5..7b0991dc 100644 --- a/lib/tasks/table/tasks-table-columns.tsx +++ b/lib/tasks/table/tasks-table-columns.tsx @@ -219,7 +219,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<Task>[] if (cfg.id === "createdAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } // code etc... diff --git a/lib/tbe/table/comments-sheet.tsx b/lib/tbe/table/comments-sheet.tsx index 0952209d..35c29d39 100644 --- a/lib/tbe/table/comments-sheet.tsx +++ b/lib/tbe/table/comments-sheet.tsx @@ -178,7 +178,7 @@ export function CommentSheet({ </div> )} </TableCell> - <TableCell> { c.createdAt ? formatDate(c.createdAt): "-"}</TableCell> + <TableCell> { c.createdAt ? formatDate(c.createdAt, "KR"): "-"}</TableCell> <TableCell> {c.commentedBy ?? "-"} </TableCell> diff --git a/lib/tbe/table/file-dialog.tsx b/lib/tbe/table/file-dialog.tsx index b569f2b1..d22671da 100644 --- a/lib/tbe/table/file-dialog.tsx +++ b/lib/tbe/table/file-dialog.tsx @@ -118,7 +118,7 @@ export function TBEFileDialog({ <FileListInfo className="flex-1 min-w-0"> <FileListName className="text-sm font-medium truncate">{file.fileName}</FileListName> <FileListDescription className="text-xs text-muted-foreground"> - {file.uploadedAt ? formatDateTime(file.uploadedAt) : ""} + {file.uploadedAt ? formatDateTime(file.uploadedAt, "KR") : ""} </FileListDescription> </FileListInfo> </div> diff --git a/lib/tbe/table/tbe-table-columns.tsx b/lib/tbe/table/tbe-table-columns.tsx index 8f0de88c..f30cd0e0 100644 --- a/lib/tbe/table/tbe-table-columns.tsx +++ b/lib/tbe/table/tbe-table-columns.tsx @@ -215,7 +215,7 @@ export function getColumns({ if (cfg.id === "tbeUpdated") { const dateVal = val as Date | undefined if (!dateVal) return null - return formatDate(dateVal) + return formatDate(dateVal, "KR") } // 그 외 필드는 기본 값 표시 diff --git a/lib/tech-project-avl/table/accepted-quotations-table-columns.tsx b/lib/tech-project-avl/table/accepted-quotations-table-columns.tsx index 68a61f0a..dae19395 100644 --- a/lib/tech-project-avl/table/accepted-quotations-table-columns.tsx +++ b/lib/tech-project-avl/table/accepted-quotations-table-columns.tsx @@ -293,7 +293,7 @@ export function getColumns(): ColumnDef<AcceptedQuotationItem>[] { ),
cell: ({ row }) => (
<div>
- {row.original.dueDate ? formatDate(row.original.dueDate) : "-"}
+ {row.original.dueDate ? formatDate(row.original.dueDate, "KR") : "-"}
</div>
),
enableSorting: true,
@@ -309,7 +309,7 @@ export function getColumns(): ColumnDef<AcceptedQuotationItem>[] { ),
cell: ({ row }) => (
<div>
- {row.original.acceptedAt ? formatDate(row.original.acceptedAt) : "-"}
+ {row.original.acceptedAt ? formatDate(row.original.acceptedAt, "KR") : "-"}
</div>
),
enableSorting: true,
diff --git a/lib/tech-vendor-candidates/table/candidates-table-columns.tsx b/lib/tech-vendor-candidates/table/candidates-table-columns.tsx index 113927cf..aa6d0ef1 100644 --- a/lib/tech-vendor-candidates/table/candidates-table-columns.tsx +++ b/lib/tech-vendor-candidates/table/candidates-table-columns.tsx @@ -156,7 +156,7 @@ const actionsColumn: ColumnDef<VendorCandidatesWithVendorInfo> = { if (cfg.id === "createdAt" ||cfg.id === "updatedAt" ) { const dateVal = cell.getValue() as Date - return formatDateTime(dateVal) + return formatDateTime(dateVal, "KR") } // code etc... diff --git a/lib/tech-vendors/contacts-table/contact-table-columns.tsx b/lib/tech-vendors/contacts-table/contact-table-columns.tsx index f80fae33..fece5013 100644 --- a/lib/tech-vendors/contacts-table/contact-table-columns.tsx +++ b/lib/tech-vendors/contacts-table/contact-table-columns.tsx @@ -146,12 +146,12 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<VendorC if (cfg.id === "createdAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } if (cfg.id === "updatedAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } diff --git a/lib/tech-vendors/table/tech-vendors-table-columns.tsx b/lib/tech-vendors/table/tech-vendors-table-columns.tsx index f690d266..69396c99 100644 --- a/lib/tech-vendors/table/tech-vendors-table-columns.tsx +++ b/lib/tech-vendors/table/tech-vendors-table-columns.tsx @@ -318,7 +318,7 @@ export function getColumns({ setRowAction, router, openItemsDialog }: GetColumns // 날짜 컬럼 포맷팅 if (cfg.type === "date" && cell.getValue()) { - return formatDate(cell.getValue() as Date); + return formatDate(cell.getValue() as Date, "KR"); } return cell.getValue(); diff --git a/lib/techsales-rfq/service.ts b/lib/techsales-rfq/service.ts index 14d7a45e..9689e855 100644 --- a/lib/techsales-rfq/service.ts +++ b/lib/techsales-rfq/service.ts @@ -634,7 +634,7 @@ export async function sendTechSalesRfqToVendors(input: { projectCode: rfq.biddingProject?.pspid || '', projectName: rfq.biddingProject?.projNm || '', description: rfq.remark || '', - dueDate: rfq.dueDate ? formatDate(rfq.dueDate) : 'N/A', + dueDate: rfq.dueDate ? formatDate(rfq.dueDate, "KR") : 'N/A', materialCode: rfq.materialCode || '', type: rfq.rfqType || 'SHIP', }, diff --git a/lib/techsales-rfq/table/detail-table/quotation-history-dialog.tsx b/lib/techsales-rfq/table/detail-table/quotation-history-dialog.tsx index 0195b10c..ce701e13 100644 --- a/lib/techsales-rfq/table/detail-table/quotation-history-dialog.tsx +++ b/lib/techsales-rfq/table/detail-table/quotation-history-dialog.tsx @@ -137,7 +137,7 @@ function QuotationCard({ <div>
<p className="text-sm font-medium text-muted-foreground">유효 기한</p>
<p className="text-sm">
- {data.validUntil ? formatDate(data.validUntil) : "미설정"}
+ {data.validUntil ? formatDate(data.validUntil, "KR") : "미설정"}
</p>
</div>
</div>
@@ -187,8 +187,8 @@ function QuotationCard({ <Clock className="size-3" />
<span>
{isCurrent
- ? `수정: ${data.updatedAt ? formatDate(data.updatedAt) : "N/A"}`
- : `변경: ${revisedAt ? formatDate(revisedAt) : "N/A"}`
+ ? `수정: ${data.updatedAt ? formatDate(data.updatedAt, "KR") : "N/A"}`
+ : `변경: ${revisedAt ? formatDate(revisedAt, "KR") : "N/A"}`
}
</span>
</div>
diff --git a/lib/techsales-rfq/table/detail-table/vendor-communication-drawer.tsx b/lib/techsales-rfq/table/detail-table/vendor-communication-drawer.tsx index 4172ccd7..0312451d 100644 --- a/lib/techsales-rfq/table/detail-table/vendor-communication-drawer.tsx +++ b/lib/techsales-rfq/table/detail-table/vendor-communication-drawer.tsx @@ -351,7 +351,7 @@ export function VendorCommunicationDrawer({ {selectedAttachment.fileName} </DialogTitle> <DialogDescription> - {formatFileSize(selectedAttachment.fileSize)} • {formatDateTime(selectedAttachment.uploadedAt)} + {formatFileSize(selectedAttachment.fileSize)} • {formatDateTime(selectedAttachment.uploadedAt, "KR")} </DialogDescription> </DialogHeader> diff --git a/lib/techsales-rfq/table/rfq-table-column.tsx b/lib/techsales-rfq/table/rfq-table-column.tsx index 3009e036..89054d0e 100644 --- a/lib/techsales-rfq/table/rfq-table-column.tsx +++ b/lib/techsales-rfq/table/rfq-table-column.tsx @@ -234,7 +234,7 @@ export function getColumns({ ), cell: ({ cell }) => { const value = cell.getValue(); - return value ? formatDateTime(value as Date) : ""; + return value ? formatDateTime(value as Date, "KR") : ""; }, meta: { excelHeader: "등록일" @@ -249,7 +249,7 @@ export function getColumns({ ), cell: ({ cell }) => { const value = cell.getValue(); - return value ? formatDateTime(value as Date) : ""; + return value ? formatDateTime(value as Date, "KR") : ""; }, meta: { excelHeader: "수정일" diff --git a/lib/techsales-rfq/table/tech-sales-quotation-attachments-sheet.tsx b/lib/techsales-rfq/table/tech-sales-quotation-attachments-sheet.tsx index 21c61773..6d6bde5a 100644 --- a/lib/techsales-rfq/table/tech-sales-quotation-attachments-sheet.tsx +++ b/lib/techsales-rfq/table/tech-sales-quotation-attachments-sheet.tsx @@ -190,7 +190,7 @@ export function TechSalesQuotationAttachmentsSheet({ </Badge>
</div>
<p className="text-xs text-muted-foreground mt-1">
- {formatDate(attachment.createdAt)}
+ {formatDate(attachment.createdAt, "KR")}
</p>
{attachment.description && (
<p className="text-xs text-muted-foreground mt-1 break-words">
diff --git a/lib/techsales-rfq/table/tech-sales-rfq-attachments-sheet.tsx b/lib/techsales-rfq/table/tech-sales-rfq-attachments-sheet.tsx index fa9be9e3..0593206a 100644 --- a/lib/techsales-rfq/table/tech-sales-rfq-attachments-sheet.tsx +++ b/lib/techsales-rfq/table/tech-sales-rfq-attachments-sheet.tsx @@ -379,7 +379,7 @@ export function TechSalesRfqAttachmentsSheet({ {existingFields.map((field, index) => { const typeLabel = attachmentConfig.fileTypeLabel const sizeText = field.fileSize ? prettyBytes(field.fileSize) : "알 수 없음" - const dateText = field.createdAt ? formatDate(field.createdAt) : "" + const dateText = field.createdAt ? formatDate(field.createdAt, "KR") : "" return ( <div key={field.id} className="flex items-start justify-between p-3 border rounded-md gap-3"> diff --git a/lib/users/access-control/users-table-columns.tsx b/lib/users/access-control/users-table-columns.tsx index 7e510b96..f832085d 100644 --- a/lib/users/access-control/users-table-columns.tsx +++ b/lib/users/access-control/users-table-columns.tsx @@ -95,7 +95,7 @@ export function getColumns(): ColumnDef<User>[] { if (cfg.id === "createdAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } if (cfg.id === "domain") { diff --git a/lib/users/table/users-table-columns.tsx b/lib/users/table/users-table-columns.tsx index c0eb9520..217fefcf 100644 --- a/lib/users/table/users-table-columns.tsx +++ b/lib/users/table/users-table-columns.tsx @@ -99,7 +99,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<UserVie if (cfg.id === "created_at") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } if (cfg.id === "roles") { diff --git a/lib/vendor-candidates/table/candidates-table-columns.tsx b/lib/vendor-candidates/table/candidates-table-columns.tsx index 113927cf..aa6d0ef1 100644 --- a/lib/vendor-candidates/table/candidates-table-columns.tsx +++ b/lib/vendor-candidates/table/candidates-table-columns.tsx @@ -156,7 +156,7 @@ const actionsColumn: ColumnDef<VendorCandidatesWithVendorInfo> = { if (cfg.id === "createdAt" ||cfg.id === "updatedAt" ) { const dateVal = cell.getValue() as Date - return formatDateTime(dateVal) + return formatDateTime(dateVal, "KR") } // code etc... diff --git a/lib/vendor-candidates/table/view-candidate_logs-dialog.tsx b/lib/vendor-candidates/table/view-candidate_logs-dialog.tsx index 8f0ea2eb..c429826a 100644 --- a/lib/vendor-candidates/table/view-candidate_logs-dialog.tsx +++ b/lib/vendor-candidates/table/view-candidate_logs-dialog.tsx @@ -105,7 +105,7 @@ export function ViewCandidateLogsDialog({ `"${log.comment?.replace(/"/g, '""') || ''}"`, `"${log.userName || ''}"`, `"${log.userEmail || ''}"`, - `"${formatDateTime(log.createdAt)}"` + `"${formatDateTime(log.createdAt, "KR")}"` ].join(",")) ].join("\n") @@ -136,7 +136,7 @@ export function ViewCandidateLogsDialog({ <Dialog open={open} onOpenChange={onOpenChange}> <DialogContent className="sm:max-w-[700px]"> <DialogHeader> - <DialogTitle>Audit Logs</DialogTitle> + <DialogTitle>협력업체 상태변경 히스토리</DialogTitle> </DialogHeader> {/* Filters and search */} @@ -211,7 +211,7 @@ export function ViewCandidateLogsDialog({ <div className="flex justify-between items-start mb-2"> <Badge className="text-xs">{log.action}</Badge> <div className="text-xs text-muted-foreground"> - {formatDateTime(log.createdAt)} + {formatDateTime(log.createdAt, "KR")} </div> </div> diff --git a/lib/vendor-evaluation-submit/table/evaluation-submissions-table-columns.tsx b/lib/vendor-evaluation-submit/table/evaluation-submissions-table-columns.tsx index aa6255bc..86355d54 100644 --- a/lib/vendor-evaluation-submit/table/evaluation-submissions-table-columns.tsx +++ b/lib/vendor-evaluation-submit/table/evaluation-submissions-table-columns.tsx @@ -212,7 +212,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<Evaluat ), cell: ({ row }) => { const date = row.getValue("submittedAt") as Date; - return date ? formatDate(date) : ( + return date ? formatDate(date, "KR") : ( <span className="text-muted-foreground">-</span> ); }, @@ -234,7 +234,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<Evaluat return ( <div className="space-y-1"> - <div className="text-sm">{formatDate(reviewedAt)}</div> + <div className="text-sm">{formatDate(reviewedAt, "KR")}</div> {reviewedBy && ( <div className="text-xs text-muted-foreground">{reviewedBy}</div> )} @@ -436,7 +436,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<Evaluat ), cell: ({ row }) => { const date = row.getValue("createdAt") as Date; - return formatDate(date); + return formatDate(date, "KR"); }, size: 140, }, @@ -447,7 +447,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<Evaluat ), cell: ({ row }) => { const date = row.getValue("updatedAt") as Date; - return formatDate(date); + return formatDate(date, "KR"); }, size: 140, }, diff --git a/lib/vendor-type/table/vendorTypes-table-columns.tsx b/lib/vendor-type/table/vendorTypes-table-columns.tsx index b5cfca71..a713ecc2 100644 --- a/lib/vendor-type/table/vendorTypes-table-columns.tsx +++ b/lib/vendor-type/table/vendorTypes-table-columns.tsx @@ -137,7 +137,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<VendorT if (cfg.id === "createdAt"||cfg.id === "updatedAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } return row.getValue(cfg.id) ?? "" diff --git a/lib/vendor-users/table/ausers-table-columns.tsx b/lib/vendor-users/table/ausers-table-columns.tsx index 38281c7e..41b78adb 100644 --- a/lib/vendor-users/table/ausers-table-columns.tsx +++ b/lib/vendor-users/table/ausers-table-columns.tsx @@ -173,7 +173,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<UserVie if (cfg.id === "created_at") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } if (cfg.id === "roles") { diff --git a/lib/vendors/contacts-table/contact-table-columns.tsx b/lib/vendors/contacts-table/contact-table-columns.tsx index f80fae33..fece5013 100644 --- a/lib/vendors/contacts-table/contact-table-columns.tsx +++ b/lib/vendors/contacts-table/contact-table-columns.tsx @@ -146,12 +146,12 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<VendorC if (cfg.id === "createdAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } if (cfg.id === "updatedAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } diff --git a/lib/vendors/items-table/item-table-columns.tsx b/lib/vendors/items-table/item-table-columns.tsx index 769722e4..61bd0325 100644 --- a/lib/vendors/items-table/item-table-columns.tsx +++ b/lib/vendors/items-table/item-table-columns.tsx @@ -146,12 +146,12 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<VendorI if (cfg.id === "createdAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } if (cfg.id === "updatedAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } diff --git a/lib/vendors/materials-table/item-table-columns.tsx b/lib/vendors/materials-table/item-table-columns.tsx index d2aa0f8f..6a37d404 100644 --- a/lib/vendors/materials-table/item-table-columns.tsx +++ b/lib/vendors/materials-table/item-table-columns.tsx @@ -146,12 +146,12 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<VendorM if (cfg.id === "createdAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } if (cfg.id === "updatedAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } diff --git a/lib/vendors/rfq-history-table/rfq-history-table-columns.tsx b/lib/vendors/rfq-history-table/rfq-history-table-columns.tsx index 7e22e96a..66ddee47 100644 --- a/lib/vendors/rfq-history-table/rfq-history-table-columns.tsx +++ b/lib/vendors/rfq-history-table/rfq-history-table-columns.tsx @@ -189,7 +189,7 @@ export function getColumns({ setRowAction, openItemsModal }: GetColumnsProps): C if (cfg.id === "dueDate" || cfg.id === "createdAt") {
column.cell = ({ row }) => (
<div className="whitespace-nowrap">
- {formatDate(row.getValue(cfg.id))}
+ {formatDate(row.getValue(cfg.id), "KR")}
</div>
)
}
diff --git a/lib/vendors/table/request-vendor-investigate-dialog.tsx b/lib/vendors/table/request-vendor-investigate-dialog.tsx index b3deafce..a0d84128 100644 --- a/lib/vendors/table/request-vendor-investigate-dialog.tsx +++ b/lib/vendors/table/request-vendor-investigate-dialog.tsx @@ -210,10 +210,10 @@ export function RequestVendorsInvestigateDialog({ <TableCell> <Badge variant={status.variant as any}>{status.text}</Badge> </TableCell> - <TableCell>{formatDate(investigation.createdAt)}</TableCell> + <TableCell>{formatDate(investigation.createdAt, "KR")}</TableCell> <TableCell> {investigation.scheduledStartAt - ? formatDate(investigation.scheduledStartAt) + ? formatDate(investigation.scheduledStartAt, "KR") : "미정"} </TableCell> </TableRow> diff --git a/lib/vendors/table/vendor-all-export.ts b/lib/vendors/table/vendor-all-export.ts index cef801fd..31ab2b52 100644 --- a/lib/vendors/table/vendor-all-export.ts +++ b/lib/vendors/table/vendor-all-export.ts @@ -184,7 +184,7 @@ function createBasicInfoSheet( representativeName: vendor.representativeName, creditRating: vendor.creditRating, cashFlowRating: vendor.cashFlowRating, - createdAt: vendor.createdAt ? formatDate(vendor.createdAt) : "", + createdAt: vendor.createdAt ? formatDate(vendor.createdAt, "KR") : "", }); }); } @@ -280,7 +280,7 @@ function createItemsSheet( itemCode: item.itemCode, itemName: item.itemName, description: item.description || "", - createdAt: item.createdAt ? formatDate(item.createdAt) : "", + createdAt: item.createdAt ? formatDate(item.createdAt, "KR") : "", }); }); } else { @@ -335,8 +335,8 @@ function createRFQsSheet( rfqNumber: rfq.rfqNumber, title: rfq.title, status: rfq.status, - requestDate: rfq.requestDate ? formatDate(rfq.requestDate) : "", - dueDate: rfq.dueDate ? formatDate(rfq.dueDate) : "", + requestDate: rfq.requestDate ? formatDate(rfq.requestDate, "KR") : "", + dueDate: rfq.dueDate ? formatDate(rfq.dueDate, "KR") : "", description: rfq.description || "", }); }); @@ -405,10 +405,10 @@ function createContractsSheet( status: contract.status, paymentTerms: contract.paymentTerms, deliveryTerms: contract.deliveryTerms, - deliveryDate: contract.deliveryDate ? formatDate(contract.deliveryDate) : "", + deliveryDate: contract.deliveryDate ? formatDate(contract.deliveryDate, "KR") : "", deliveryLocation: contract.deliveryLocation, - startDate: contract.startDate ? formatDate(contract.startDate) : "", - endDate: contract.endDate ? formatDate(contract.endDate) : "", + startDate: contract.startDate ? formatDate(contract.startDate, "KR") : "", + endDate: contract.endDate ? formatDate(contract.endDate, "KR") : "", currency: contract.currency, totalAmount: contract.totalAmount ? formatAmount(contract.totalAmount) : "", }); diff --git a/lib/vendors/table/vendors-table-columns.tsx b/lib/vendors/table/vendors-table-columns.tsx index 21086918..0a5f066f 100644 --- a/lib/vendors/table/vendors-table-columns.tsx +++ b/lib/vendors/table/vendors-table-columns.tsx @@ -375,12 +375,12 @@ export function getColumns({ setRowAction, router, userId }: GetColumnsProps): C if (cfg.id === "createdAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } if (cfg.id === "updatedAt") { const dateVal = cell.getValue() as Date - return formatDate(dateVal) + return formatDate(dateVal, "KR") } // code etc... diff --git a/lib/vendors/table/view-vendors_logs-dialog.tsx b/lib/vendors/table/view-vendors_logs-dialog.tsx index 7402ae55..521c52e9 100644 --- a/lib/vendors/table/view-vendors_logs-dialog.tsx +++ b/lib/vendors/table/view-vendors_logs-dialog.tsx @@ -104,7 +104,7 @@ export function ViewVendorLogsDialog({ `"${log.comment?.replace(/"/g, '""') || ''}"`, `"${log.userName || ''}"`, `"${log.userEmail || ''}"`, - `"${formatDateTime(log.createdAt)}"` + `"${formatDateTime(log.createdAt, "KR")}"` ].join(",")) ].join("\n") @@ -135,7 +135,7 @@ export function ViewVendorLogsDialog({ <Dialog open={open} onOpenChange={onOpenChange}> <DialogContent className="sm:max-w-[700px]"> <DialogHeader> - <DialogTitle>Audit Logs</DialogTitle> + <DialogTitle>협력업체 상태변경 히스토리</DialogTitle> </DialogHeader> {/* Filters and search */} @@ -209,7 +209,7 @@ export function ViewVendorLogsDialog({ <div className="flex justify-between items-start mb-2"> <Badge className="text-xs">{log.action}</Badge> <div className="text-xs text-muted-foreground"> - {formatDateTime(log.createdAt)} + {formatDateTime(log.createdAt, "KR")} </div> </div> diff --git a/lib/welding/repository.ts b/lib/welding/repository.ts index 1e96867b..e3351358 100644 --- a/lib/welding/repository.ts +++ b/lib/welding/repository.ts @@ -36,6 +36,8 @@ export async function selectOcrRows( // ocrRows의 모든 필드 id: ocrRows.id, tableId: ocrRows.tableId, + fileName: ocrRows.fileName, + inspectionDate: ocrRows.inspectionDate, sessionId: ocrRows.sessionId, rowIndex: ocrRows.rowIndex, reportNo: ocrRows.reportNo, diff --git a/lib/welding/table/ocr-table-columns.tsx b/lib/welding/table/ocr-table-columns.tsx index 93ee1004..d4ca9f5f 100644 --- a/lib/welding/table/ocr-table-columns.tsx +++ b/lib/welding/table/ocr-table-columns.tsx @@ -51,7 +51,25 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<OcrRow> enableSorting: false, enableHiding: false, }, - + { + accessorKey: "fileName", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="file Name" /> + ), + cell: ({ getValue }) => { + const fileName = getValue() as string + return ( + <div className="text-sm"> + {/* <Badge variant="outline" className="font-mono text-xs"> */} + {fileName || "N/A"} + {/* </Badge> */} + + </div> + ) + }, + enableSorting: true, + enableHiding: false, + }, // Report No 컬럼 { accessorKey: "reportNo", @@ -233,7 +251,25 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<OcrRow> // }, // enableSorting: true, // }, - + { + accessorKey: "inspectionDate", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="Inspection(Confirmed) Date" /> + ), + cell: ({ getValue }) => { + const inspectionDate = getValue() as string + return ( + <div className="text-sm"> + {/* <Badge variant="outline" className="font-mono text-xs"> */} + {inspectionDate || "N/A"} + {/* </Badge> */} + + </div> + ) + }, + enableSorting: true, + enableHiding: false, + }, { accessorKey: "userName", |
