From 5870b73785715d1585531e655c06d8c068eb64ac Mon Sep 17 00:00:00 2001 From: joonhoekim <26rote@gmail.com> Date: Thu, 27 Nov 2025 17:53:34 +0900 Subject: (김준회) Revert "(대표님) EDP 작업사항" 태그 가져오기 실패 등 에러로 인한 Revert 처리 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/client-data-table/data-table.tsx | 300 ++++++------ .../form-data-plant/delete-form-data-dialog.tsx | 9 +- .../form-data-report-batch-dialog.tsx | 21 +- .../form-data-plant/form-data-report-dialog.tsx | 21 +- .../form-data-report-temp-upload-dialog.tsx | 12 +- .../form-data-report-temp-upload-tab.tsx | 7 +- .../form-data-report-temp-uploaded-list-tab.tsx | 19 +- components/form-data-plant/form-data-table.tsx | 91 ++-- components/form-data-plant/import-excel-form.tsx | 13 +- components/form-data-plant/publish-dialog.tsx | 226 ++++----- components/form-data-plant/spreadJS-dialog.tsx | 15 +- components/form-data-plant/update-form-sheet.tsx | 9 +- components/vendor-data-plant/project-swicher.tsx | 163 ++++--- components/vendor-data-plant/sidebar.tsx | 479 ++++++++++--------- .../vendor-data-plant/vendor-data-container.tsx | 523 ++++++++++++++++----- 15 files changed, 1108 insertions(+), 800 deletions(-) (limited to 'components') diff --git a/components/client-data-table/data-table.tsx b/components/client-data-table/data-table.tsx index 371a1dab..3e009302 100644 --- a/components/client-data-table/data-table.tsx +++ b/components/client-data-table/data-table.tsx @@ -49,9 +49,8 @@ interface DataTableProps { children?: React.ReactNode /** 선택 상태 초기화 트리거 */ clearSelection?: boolean - initialColumnPinning?: ColumnPinningState - /** Table 인스턴스를 상위 컴포넌트에 전달하는 콜백 */ - onTableReady?: (table: Table) => void + initialColumnPinning?: ColumnPinningState // 추가 + } export function ClientDataTable({ @@ -64,8 +63,7 @@ export function ClientDataTable({ maxHeight, onSelectedRowsChange, clearSelection, - initialColumnPinning, - onTableReady + initialColumnPinning }: DataTableProps) { // (1) React Table 상태 @@ -120,13 +118,6 @@ export function ClientDataTable({ useAutoSizeColumns(table, autoSizeColumns) - // 🆕 Table 인스턴스를 상위 컴포넌트에 전달 - React.useEffect(() => { - if (onTableReady) { - onTableReady(table) - } - }, [table, onTableReady]) - React.useEffect(() => { if (!onSelectedRowsChange) return const selectedRows = table @@ -173,7 +164,6 @@ export function ClientDataTable({ }), } } - // 🎯 테이블 총 너비 계산 const getTableWidth = React.useCallback(() => { const totalSize = table.getCenterTotalSize() + table.getLeftTotalSize() + table.getRightTotalSize() @@ -216,172 +206,174 @@ export function ClientDataTable({ {children} -
- + thead]:sticky [&>thead]:top-0 [&>thead]:z-10", !hasNestedHeader && "table-fixed" // nested header가 없으면 table-fixed 적용 )} style={{ minWidth: hasNestedHeader ? getTableWidth() : undefined }}> - {/* nested header가 있으면 table-fixed 제거, 없으면 적용 */} - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - // 만약 이 컬럼이 현재 "그룹핑" 상태라면 헤더도 표시하지 않음 - if (header.column.getIsGrouped()) { - return null - } + {/* nested header가 있으면 table-fixed 제거, 없으면 적용 */} + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + // 만약 이 컬럼이 현재 "그룹핑" 상태라면 헤더도 표시하지 않음 + if (header.column.getIsGrouped()) { + return null + } - return ( - -
- {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext() + return ( + +
+ {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + {/* 부모 그룹 헤더는 리사이즈 불가, 자식 헤더만 리사이즈 가능 */} + {header.column.getCanResize() && !('columns' in header.column.columnDef) && ( + )} - - {/* 부모 그룹 헤더는 리사이즈 불가, 자식 헤더만 리사이즈 가능 */} - {header.column.getCanResize() && !('columns' in header.column.columnDef) && ( - - )} -
-
- ) - })} - - ))} - - - {table.getRowModel().rows?.length ? ( - table.getRowModel().rows.map((row) => { - // --------------------------------------------------- - // 1) "그룹핑 헤더" Row인지 확인 - // --------------------------------------------------- - if (row.getIsGrouped()) { - // row.groupingColumnId로 어떤 컬럼을 기준으로 그룹화 되었는지 알 수 있음 - const groupingColumnId = row.groupingColumnId ?? "" - const groupingColumn = table.getColumn(groupingColumnId) // 해당 column 객체 +
+
+ ) + })} +
+ ))} +
+ + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => { + // --------------------------------------------------- + // 1) "그룹핑 헤더" Row인지 확인 + // --------------------------------------------------- + if (row.getIsGrouped()) { + // row.groupingColumnId로 어떤 컬럼을 기준으로 그룹화 되었는지 알 수 있음 + const groupingColumnId = row.groupingColumnId ?? "" + const groupingColumn = table.getColumn(groupingColumnId) // 해당 column 객체 - // 컬럼 라벨 가져오기 - let columnLabel = groupingColumnId - if (groupingColumn) { - const headerDef = groupingColumn.columnDef.meta?.excelHeader - if (typeof headerDef === "string") { - columnLabel = headerDef + // 컬럼 라벨 가져오기 + let columnLabel = groupingColumnId + if (groupingColumn) { + const headerDef = groupingColumn.columnDef.meta?.excelHeader + if (typeof headerDef === "string") { + columnLabel = headerDef + } } + + return ( + + {/* 그룹 헤더는 한 줄에 합쳐서 보여주고, 토글 버튼 + 그룹 라벨 + 값 표기 */} + + {/* 확장/축소 버튼 (아이콘 중앙 정렬 + Indent) */} + {row.getCanExpand() && ( + + )} + + {/* Group Label + 값 */} + + {columnLabel}: {row.getValue(groupingColumnId)} + + + ({row.subRows.length} rows) + + + + ) } + // --------------------------------------------------- + // 2) 일반 Row + // → "그룹핑된 컬럼"은 숨긴다 + // --------------------------------------------------- return ( - {/* 그룹 헤더는 한 줄에 합쳐서 보여주고, 토글 버튼 + 그룹 라벨 + 값 표기 */} - - {/* 확장/축소 버튼 (아이콘 중앙 정렬 + Indent) */} - {row.getCanExpand() && ( - - )} - - {/* Group Label + 값 */} - - {columnLabel}: {row.getValue(groupingColumnId)} - - - ({row.subRows.length} rows) - - + + ) + })} ) - } - + }) + ) : ( // --------------------------------------------------- - // 2) 일반 Row - // → "그룹핑된 컬럼"은 숨긴다 + // 3) 데이터가 없을 때 // --------------------------------------------------- - return ( - + - {row.getVisibleCells().map((cell) => { - // 이 셀의 컬럼이 grouped라면 숨긴다 - if (cell.column.getIsGrouped()) { - return null - } + No results. + + + )} + +
+
- return ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext() - )} - - ) - })} - - ) - }) - ) : ( - // --------------------------------------------------- - // 3) 데이터가 없을 때 - // --------------------------------------------------- - - - No results. - - - )} - - - diff --git a/components/form-data-plant/delete-form-data-dialog.tsx b/components/form-data-plant/delete-form-data-dialog.tsx index 2406407e..6ac8f67c 100644 --- a/components/form-data-plant/delete-form-data-dialog.tsx +++ b/components/form-data-plant/delete-form-data-dialog.tsx @@ -40,8 +40,7 @@ interface DeleteFormDataDialogProps extends React.ComponentPropsWithoutRef { formData: GenericData[] formCode: string - projectCode: string - packageCode: string + contractItemId: number projectId?: number showTrigger?: boolean onSuccess?: () => void @@ -51,8 +50,7 @@ interface DeleteFormDataDialogProps export function DeleteFormDataDialog({ formData, formCode, - projectCode, - packageCode, + contractItemId, projectId, showTrigger = true, onSuccess, @@ -79,8 +77,7 @@ export function DeleteFormDataDialog({ const result = await deleteFormDataByTags({ formCode, - projectCode, - packageCode, + contractItemId, tagIdxs, projectId, }) diff --git a/components/form-data-plant/form-data-report-batch-dialog.tsx b/components/form-data-plant/form-data-report-batch-dialog.tsx index ba41a3c2..24b5827b 100644 --- a/components/form-data-plant/form-data-report-batch-dialog.tsx +++ b/components/form-data-plant/form-data-report-batch-dialog.tsx @@ -71,8 +71,7 @@ interface FormDataReportBatchDialogProps { setOpen: Dispatch>; columnsJSON: DataTableColumnJSON[]; reportData: ReportData[]; - projectCode: string; - packageCode: string; + packageId: number; formId: number; formCode: string; } @@ -82,8 +81,7 @@ export const FormDataReportBatchDialog: FC = ({ setOpen, columnsJSON, reportData, - projectCode, - packageCode, + packageId, formId, formCode, }) => { @@ -102,8 +100,8 @@ export const FormDataReportBatchDialog: FC = ({ const [generatedFileBlob, setGeneratedFileBlob] = useState(null); useEffect(() => { - updateReportTempList(projectCode, packageCode, formId, setTempList); - }, [projectCode, packageCode, formId]); + updateReportTempList(packageId, formId, setTempList); + }, [packageId, formId]); const onClose = () => { if (isUploading) { @@ -363,8 +361,7 @@ export const FormDataReportBatchDialog: FC = ({ @@ -412,19 +409,17 @@ const UploadFileItem: FC = ({ }; type UpdateReportTempList = ( - projectCode: string, - packageCode: string, + packageId: number, formId: number, setPrevReportTemp: Dispatch> ) => void; const updateReportTempList: UpdateReportTempList = async ( - projectCode, - packageCode, + packageId, formId, setTempList ) => { - const tempList = await getReportTempList(projectCode,packageCode, formId); + const tempList = await getReportTempList(packageId, formId); setTempList( tempList.map((c) => { diff --git a/components/form-data-plant/form-data-report-dialog.tsx b/components/form-data-plant/form-data-report-dialog.tsx index 2413fc28..9177ab36 100644 --- a/components/form-data-plant/form-data-report-dialog.tsx +++ b/components/form-data-plant/form-data-report-dialog.tsx @@ -49,8 +49,7 @@ interface FormDataReportDialogProps { columnsJSON: DataTableColumnJSON[]; reportData: ReportData[]; setReportData: Dispatch>; - projectCode: string; - packageCode: string; + packageId: number; formId: number; formCode: string; } @@ -59,8 +58,7 @@ export const FormDataReportDialog: FC = ({ columnsJSON, reportData, setReportData, - projectCode, - packageCode, + packageId, formId, formCode, }) => { @@ -78,8 +76,8 @@ export const FormDataReportDialog: FC = ({ const [generatedFileBlob, setGeneratedFileBlob] = useState(null); useEffect(() => { - updateReportTempList(projectCode, packageCode, formId, setTempList); - }, [projectCode,packageCode, formId]); + updateReportTempList(packageId, formId, setTempList); + }, [packageId, formId]); const onClose = async (value: boolean) => { if (fileLoading) { @@ -199,8 +197,7 @@ export const FormDataReportDialog: FC = ({ @@ -397,19 +394,17 @@ const importReportData: ImportReportData = async ( }; type UpdateReportTempList = ( - projectCode: string, - packageCode: string, + packageId: number, formId: number, setPrevReportTemp: Dispatch> ) => void; const updateReportTempList: UpdateReportTempList = async ( - projectCode, - packageCode, + packageId, formId, setTempList ) => { - const tempList = await getReportTempList(projectCode,packageCode, formId); + const tempList = await getReportTempList(packageId, formId); setTempList( tempList.map((c) => { diff --git a/components/form-data-plant/form-data-report-temp-upload-dialog.tsx b/components/form-data-plant/form-data-report-temp-upload-dialog.tsx index 66915198..59ea6ade 100644 --- a/components/form-data-plant/form-data-report-temp-upload-dialog.tsx +++ b/components/form-data-plant/form-data-report-temp-upload-dialog.tsx @@ -23,8 +23,7 @@ interface FormDataReportTempUploadDialogProps { columnsJSON: DataTableColumnJSON[]; open: boolean; setOpen: Dispatch>; - projectCode: string; - packageCode: string; + packageId: number; formCode: string; formId: number; uploaderType: string; @@ -36,8 +35,7 @@ export const FormDataReportTempUploadDialog: FC< columnsJSON, open, setOpen, - projectCode, - packageCode, + packageId, formId, formCode, uploaderType, @@ -85,16 +83,14 @@ export const FormDataReportTempUploadDialog: FC< diff --git a/components/form-data-plant/form-data-report-temp-upload-tab.tsx b/components/form-data-plant/form-data-report-temp-upload-tab.tsx index 41466f90..81186ba4 100644 --- a/components/form-data-plant/form-data-report-temp-upload-tab.tsx +++ b/components/form-data-plant/form-data-report-temp-upload-tab.tsx @@ -36,15 +36,14 @@ import { uploadReportTemp } from "@/lib/forms-plant/services"; const MAX_FILE_SIZE = 3000000; interface FormDataReportTempUploadTabProps { - projectCode: string; - packageCode: string; + packageId: number; formId: number; uploaderType: string; } export const FormDataReportTempUploadTab: FC< FormDataReportTempUploadTabProps -> = ({ projectCode,packageCode, formId, uploaderType }) => { +> = ({ packageId, formId, uploaderType }) => { const { toast } = useToast(); const params = useParams(); const lng = (params?.lng as string) || "ko"; @@ -95,7 +94,7 @@ export const FormDataReportTempUploadTab: FC< formData.append("customFileName", file.name); formData.append("uploaderType", uploaderType); - await uploadReportTemp(projectCode, packageCode, formId, formData); + await uploadReportTemp(packageId, formId, formData); successCount++; setUploadProgress(Math.round((successCount / totalFiles) * 100)); diff --git a/components/form-data-plant/form-data-report-temp-uploaded-list-tab.tsx b/components/form-data-plant/form-data-report-temp-uploaded-list-tab.tsx index 1b6cefaf..4cfbad69 100644 --- a/components/form-data-plant/form-data-report-temp-uploaded-list-tab.tsx +++ b/components/form-data-plant/form-data-report-temp-uploaded-list-tab.tsx @@ -39,14 +39,13 @@ import { getReportTempList, deleteReportTempFile } from "@/lib/forms-plant/servi import { VendorDataReportTemps } from "@/db/schema/vendorData"; interface FormDataReportTempUploadedListTabProps { - projectCode: string; - packageCode: string; + packageId: number; formId: number; } export const FormDataReportTempUploadedListTab: FC< FormDataReportTempUploadedListTabProps -> = ({ projectCode,packageCode , formId }) => { +> = ({ packageId, formId }) => { const params = useParams(); const lng = (params?.lng as string) || "ko"; const { t } = useTranslation(lng, "engineering"); @@ -58,12 +57,12 @@ export const FormDataReportTempUploadedListTab: FC< useEffect(() => { const getTempFiles = async () => { - await updateReportTempList(projectCode,packageCode, formId, setPrevReportTemp); + await updateReportTempList(packageId, formId, setPrevReportTemp); setIsLoading(false); }; getTempFiles(); - }, [projectCode,packageCode, formId]); + }, [packageId, formId]); return (
@@ -71,7 +70,7 @@ export const FormDataReportTempUploadedListTab: FC< - updateReportTempList(projectCode,packageCode, formId, setPrevReportTemp) + updateReportTempList(packageId, formId, setPrevReportTemp) } isLoading={isLoading} t={t} @@ -81,19 +80,17 @@ export const FormDataReportTempUploadedListTab: FC< }; type UpdateReportTempList = ( - projectCode: string, - packageCode: string, + packageId: number, formId: number, setPrevReportTemp: Dispatch> ) => Promise; const updateReportTempList: UpdateReportTempList = async ( - projectCode, - packageCode, + packageId, formId, setPrevReportTemp ) => { - const tempList = await getReportTempList(projectCode, packageCode, formId); + const tempList = await getReportTempList(packageId, formId); setPrevReportTemp(tempList); }; diff --git a/components/form-data-plant/form-data-table.tsx b/components/form-data-plant/form-data-table.tsx index c6c79a69..30c176bd 100644 --- a/components/form-data-plant/form-data-table.tsx +++ b/components/form-data-plant/form-data-table.tsx @@ -76,8 +76,7 @@ interface GenericData { export interface DynamicTableProps { dataJSON: GenericData[]; columnsJSON: DataTableColumnJSON[]; - projectCode: string; - packageCode: string; + contractItemId: number; formCode: string; formId: number; projectId: number; @@ -90,8 +89,7 @@ export interface DynamicTableProps { export default function DynamicTable({ dataJSON, columnsJSON, - projectCode, - packageCode, + contractItemId, formCode, formId, projectId, @@ -158,8 +156,7 @@ export default function DynamicTable({ // 서버 액션 호출 const result = await excludeFormDataByTags({ formCode, - projectCode, - packageCode, + contractItemId, tagNumbers, }); @@ -291,7 +288,7 @@ export default function DynamicTable({ try { setIsLoadingStats(true); // getFormStatusByVendor 서버 액션 직접 호출 - const data = await getFormStatusByVendor(projectId, projectCode, packageCode,formCode); + const data = await getFormStatusByVendor(projectId, contractItemId, formCode); if (data && data.length > 0) { setFormStats(data[0]); @@ -342,7 +339,9 @@ export default function DynamicTable({ // SEDP compare dialog state const [sedpCompareOpen, setSedpCompareOpen] = React.useState(false); - const projectType = "plant"; + const [projectCode, setProjectCode] = React.useState(''); + const [projectType, setProjectType] = React.useState('plant'); + const [packageCode, setPackageCode] = React.useState(''); // 새로 추가된 Template 다이얼로그 상태 const [templateDialogOpen, setTemplateDialogOpen] = React.useState(false); @@ -375,13 +374,43 @@ const [isLoadingRegisters, setIsLoadingRegisters] = React.useState(false); React.useEffect(() => { const getTempCount = async () => { - const tempList = await getReportTempList(projectCode, packageCode, formId); + const tempList = await getReportTempList(contractItemId, formId); setTempCount(tempList.length); }; getTempCount(); - }, [projectCode,packageCode, formId, tempUpDialog]); + }, [contractItemId, formId, tempUpDialog]); + React.useEffect(() => { + const getPackageCode = async () => { + try { + const packageCode = await getPackageCodeById(contractItemId); + setPackageCode(packageCode || ''); // 빈 문자열이나 다른 기본값 + } catch (error) { + console.error('패키지 조회 실패:', error); + setPackageCode(''); + } + }; + + getPackageCode(); + }, [contractItemId]) + // Get project code when component mounts + React.useEffect(() => { + const getProjectCode = async () => { + try { + const project = await getProjectById(projectId); + setProjectCode(project.code); + setProjectType(project.type); + } catch (error) { + console.error("Error fetching project code:", error); + toast.error("Failed to fetch project code"); + } + }; + + if (projectId) { + getProjectCode(); + } + }, [projectId]); // 선택된 행들의 실제 데이터 가져오기 const getSelectedRowsData = React.useCallback(() => { @@ -500,7 +529,7 @@ React.useEffect(() => { async function handleSyncTags() { try { setIsSyncingTags(true); - const result = await syncMissingTags(projectCode,packageCode, formCode); + const result = await syncMissingTags(contractItemId, formCode); // Prepare the toast messages based on what changed const changes = []; @@ -533,9 +562,9 @@ React.useEffect(() => { setIsLoadingTags(true); // API 엔드포인트 호출 - 작업 시작만 요청 - const response = await fetch('/api/cron/form-tags-plant/start', { + const response = await fetch('/api/cron/form-tags/start', { method: 'POST', - body: JSON.stringify({ projectCode, formCode, packageCode }) + body: JSON.stringify({ projectCode, formCode, contractItemId }) }); if (!response.ok) { @@ -574,7 +603,7 @@ React.useEffect(() => { // 5초마다 상태 확인 pollingRef.current = setInterval(async () => { try { - const response = await fetch(`/api/cron/form-tags-plant/status?id=${id}`); + const response = await fetch(`/api/cron/form-tags/status?id=${id}`); if (!response.ok) { throw new Error('Failed to get tag import status'); @@ -637,8 +666,7 @@ React.useEffect(() => { tableData, columnsJSON, formCode, - projectCode, - packageCode, + contractItemId, editableFieldsMap, // 추가: 편집 가능 필드 정보 전달 onPendingChange: setIsImporting, // Let importExcelData handle loading state onDataUpdate: (newData) => { @@ -719,8 +747,7 @@ React.useEffect(() => { const sedpResult = await sendFormDataToSEDP( formCode, // Send formCode instead of formName projectId, // Project ID - projectCode, - packageCode, + contractItemId, tableData.filter(v=>v.status !== 'excluded'), // Table data columnsJSON // Column definitions ); @@ -1199,8 +1226,7 @@ React.useEffect(() => { columns={columnsJSON} rowData={rowAction?.row.original ?? null} formCode={formCode} - projectCode={projectCode} - packageCode={packageCode} + contractItemId={contractItemId} editableFieldsMap={editableFieldsMap} onUpdateSuccess={(updatedValues) => { // Update the specific row in tableData when a single row is updated @@ -1218,8 +1244,7 @@ React.useEffect(() => { { @@ -1232,6 +1257,16 @@ React.useEffect(() => { showTrigger={false} /> + {/* Dialog for adding tags */} + {/* */} {/* 새로 추가된 Template 다이얼로그 */} { selectedRow={selectedRowsData[0]} // SPR_ITM_LST_SETUP용 tableData={tableData} // SPR_LST_SETUP용 - 새로 추가 formCode={formCode} - projectCode={projectCode} - packageCode={packageCode} + contractItemId={contractItemId} editableFieldsMap={editableFieldsMap} columnsJSON={columnsJSON} onUpdateSuccess={(updatedValues) => { @@ -1310,8 +1344,7 @@ React.useEffect(() => { columnsJSON={columnsJSON} open={tempUpDialog} setOpen={setTempUpDialog} - projectCode={projectCode} - packageCode={packageCode} + packageId={contractItemId} formCode={formCode} formId={formId} uploaderType="vendor" @@ -1323,8 +1356,7 @@ React.useEffect(() => { columnsJSON={columnsJSON} reportData={reportData} setReportData={setReportData} - projectCode={projectCode} - packageCode={packageCode} + packageId={contractItemId} formCode={formCode} formId={formId} /> @@ -1336,8 +1368,7 @@ React.useEffect(() => { setOpen={setBatchDownDialog} columnsJSON={columnsJSON} reportData={selectedRowCount > 0 ? getSelectedRowsData() : tableData} - projectCode={projectCode} - packageCode={packageCode} + packageId={contractItemId} formCode={formCode} formId={formId} /> diff --git a/components/form-data-plant/import-excel-form.tsx b/components/form-data-plant/import-excel-form.tsx index 8ac70c59..ffc6f2f9 100644 --- a/components/form-data-plant/import-excel-form.tsx +++ b/components/form-data-plant/import-excel-form.tsx @@ -23,8 +23,7 @@ export interface ImportExcelOptions { tableData: GenericData[]; columnsJSON: DataTableColumnJSON[]; formCode?: string; - projectCode: string; - packageCode: string; + contractItemId?: number; editableFieldsMap?: Map; // 새로 추가 onPendingChange?: (isPending: boolean) => void; onDataUpdate?: (updater: ((prev: GenericData[]) => GenericData[]) | GenericData[]) => void; @@ -219,8 +218,7 @@ export async function importExcelData({ tableData, columnsJSON, formCode, - projectCode, - packageCode, + contractItemId, editableFieldsMap = new Map(), // 새로 추가 onPendingChange, onDataUpdate @@ -529,14 +527,14 @@ export async function importExcelData({ } }); + // If formCode and contractItemId are provided, save directly to DB // importExcelData 함수에서 DB 저장 부분 - if (formCode && projectCode && packageCode) { + if (formCode && contractItemId) { try { // 배치 업데이트 함수 호출 const result = await updateFormDataBatchInDB( formCode, - projectCode, - packageCode, + contractItemId, importedData // 모든 imported rows를 한번에 전달 ); @@ -635,6 +633,7 @@ export async function importExcelData({ } } else { + // formCode나 contractItemId가 없는 경우 - 로컬 업데이트만 if (onDataUpdate) { onDataUpdate(() => mergedData); } diff --git a/components/form-data-plant/publish-dialog.tsx b/components/form-data-plant/publish-dialog.tsx index f63c2db8..a3a2ef0b 100644 --- a/components/form-data-plant/publish-dialog.tsx +++ b/components/form-data-plant/publish-dialog.tsx @@ -37,21 +37,19 @@ import { Loader2, Check, ChevronsUpDown } from "lucide-react"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; import { - createSubmissionAction, // 새로운 액션 이름 - fetchDocumentsByProjectAndPackage, // 업데이트된 액션 - fetchStagesByDocumentIdPlant, - fetchSubmissionsByStageParams, // revisions 대신 submissions + createRevisionAction, + fetchDocumentsByPackageId, + fetchStagesByDocumentId, + fetchRevisionsByStageParams, + Document, + IssueStage, + Revision } from "@/lib/vendor-document/service"; -import type { - StageDocument, - StageIssueStage, -} from "@/db/schema/vendorDocu"; interface PublishDialogProps { open: boolean; onOpenChange: (open: boolean) => void; - projectCode: string; - packageCode: string; + packageId: number; formCode: string; fileBlob?: Blob; } @@ -59,8 +57,7 @@ interface PublishDialogProps { export const PublishDialog: React.FC = ({ open, onOpenChange, - projectCode, - packageCode, + packageId, formCode, fileBlob, }) => { @@ -68,10 +65,9 @@ export const PublishDialog: React.FC = ({ const { data: session } = useSession(); // State for form data - const [documents, setDocuments] = useState([]); - const [stages, setStages] = useState([]); - const [latestRevisionCode, setLatestRevisionCode] = useState(""); - const [latestRevisionNumber, setLatestRevisionNumber] = useState(0); + const [documents, setDocuments] = useState([]); + const [stages, setStages] = useState([]); + const [latestRevision, setLatestRevision] = useState(""); // State for document search const [openDocumentCombobox, setOpenDocumentCombobox] = useState(false); @@ -81,10 +77,9 @@ export const PublishDialog: React.FC = ({ const [selectedDocId, setSelectedDocId] = useState(""); const [selectedDocumentDisplay, setSelectedDocumentDisplay] = useState(""); const [selectedStage, setSelectedStage] = useState(""); - const [revisionCodeInput, setRevisionCodeInput] = useState(""); - const [submitterName, setSubmitterName] = useState(""); - const [submissionTitle, setSubmissionTitle] = useState(""); - const [submissionDescription, setSubmissionDescription] = useState(""); + const [revisionInput, setRevisionInput] = useState(""); + const [uploaderName, setUploaderName] = useState(""); + const [comment, setComment] = useState(""); const [customFileName, setCustomFileName] = useState(`${formCode}_document.docx`); // Loading states @@ -99,10 +94,10 @@ export const PublishDialog: React.FC = ({ ) : documents; - // Set submitter name from session when dialog opens + // Set uploader name from session when dialog opens useEffect(() => { if (open && session?.user?.name) { - setSubmitterName(session.user.name); + setUploaderName(session.user.name); } }, [open, session]); @@ -112,26 +107,24 @@ export const PublishDialog: React.FC = ({ setSelectedDocId(""); setSelectedDocumentDisplay(""); setSelectedStage(""); - setRevisionCodeInput(""); - setSubmissionTitle(""); - setSubmissionDescription(""); - // Only set submitterName if not already set from session - if (!session?.user?.name) setSubmitterName(""); - setLatestRevisionCode(""); - setLatestRevisionNumber(0); + setRevisionInput(""); + // Only set uploaderName if not already set from session + if (!session?.user?.name) setUploaderName(""); + setComment(""); + setLatestRevision(""); setCustomFileName(`${formCode}_document.docx`); setDocumentSearchValue(""); } }, [open, formCode, session]); - // Fetch documents based on projectCode and packageCode + // Fetch documents based on packageId useEffect(() => { async function loadDocuments() { - if (projectCode && packageCode && open) { + if (packageId && open) { setIsLoading(true); try { - const docs = await fetchDocumentsByProjectAndPackage(projectCode, packageCode); + const docs = await fetchDocumentsByPackageId(packageId); setDocuments(docs); } catch (error) { console.error("Error fetching documents:", error); @@ -143,7 +136,7 @@ export const PublishDialog: React.FC = ({ } loadDocuments(); - }, [projectCode, packageCode, open]); + }, [packageId, open]); // Fetch stages when document is selected useEffect(() => { @@ -153,12 +146,11 @@ export const PublishDialog: React.FC = ({ // Reset dependent fields setSelectedStage(""); - setRevisionCodeInput(""); - setLatestRevisionCode(""); - setLatestRevisionNumber(0); + setRevisionInput(""); + setLatestRevision(""); try { - const stagesList = await fetchStagesByDocumentIdPlant(parseInt(selectedDocId, 10)); + const stagesList = await fetchStagesByDocumentId(parseInt(selectedDocId, 10)); setStages(stagesList); } catch (error) { console.error("Error fetching stages:", error); @@ -174,78 +166,65 @@ export const PublishDialog: React.FC = ({ loadStages(); }, [selectedDocId]); - // Fetch latest submission (revision) when stage is selected + // Fetch latest revision when stage is selected (for reference) useEffect(() => { - async function loadLatestSubmission() { + async function loadLatestRevision() { if (selectedDocId && selectedStage) { setIsLoading(true); try { - const submissionsList = await fetchSubmissionsByStageParams( + const revsList = await fetchRevisionsByStageParams( parseInt(selectedDocId, 10), selectedStage ); - // Find the latest submission (assuming sorted by revision number) - if (submissionsList.length > 0) { - // Sort submissions by revision number descending - const sortedSubmissions = [...submissionsList].sort((a, b) => - b.revisionNumber - a.revisionNumber - ); + // Find the latest revision (assuming revisions are sorted by revision number) + if (revsList.length > 0) { + // Sort revisions if needed + const sortedRevisions = [...revsList].sort((a, b) => { + return b.revision.localeCompare(a.revision, undefined, { numeric: true }); + }); - const latestSubmission = sortedSubmissions[0]; - setLatestRevisionCode(latestSubmission.revisionCode); - setLatestRevisionNumber(latestSubmission.revisionNumber); + setLatestRevision(sortedRevisions[0].revision); - // Auto-increment revision code - if (latestSubmission.revisionCode.match(/^\d+$/)) { + // Pre-fill the revision input with an incremented value if possible + if (sortedRevisions[0].revision.match(/^\d+$/)) { // If it's a number, increment it - const nextRevision = String(parseInt(latestSubmission.revisionCode, 10) + 1); - setRevisionCodeInput(nextRevision); - } else if (latestSubmission.revisionCode.match(/^[A-Za-z]$/)) { + const nextRevision = String(parseInt(sortedRevisions[0].revision, 10) + 1); + setRevisionInput(nextRevision); + } else if (sortedRevisions[0].revision.match(/^[A-Za-z]$/)) { // If it's a single letter, get the next letter - const currentChar = latestSubmission.revisionCode.charCodeAt(0); + const currentChar = sortedRevisions[0].revision.charCodeAt(0); const nextChar = String.fromCharCode(currentChar + 1); - setRevisionCodeInput(nextChar); - } else if (latestSubmission.revisionCode.toLowerCase().startsWith("rev")) { - // Handle "Rev0", "Rev1" format - const numMatch = latestSubmission.revisionCode.match(/\d+$/); - if (numMatch) { - const nextNum = parseInt(numMatch[0], 10) + 1; - setRevisionCodeInput(`Rev${nextNum}`); - } else { - setRevisionCodeInput(""); - } + setRevisionInput(nextChar); } else { // For other formats, just show the latest as reference - setRevisionCodeInput(""); + setRevisionInput(""); } } else { - // If no submissions exist, set default values - setLatestRevisionCode(""); - setLatestRevisionNumber(0); - setRevisionCodeInput("Rev0"); // Start with Rev0 + // If no revisions exist, set default values + setLatestRevision(""); + setRevisionInput("0"); } } catch (error) { - console.error("Error fetching submissions:", error); - toast.error("Failed to load submission information"); + console.error("Error fetching revisions:", error); + toast.error("Failed to load revision information"); } finally { setIsLoading(false); } } else { - setLatestRevisionCode(""); - setLatestRevisionNumber(0); - setRevisionCodeInput(""); + setLatestRevision(""); + setRevisionInput(""); } } - loadLatestSubmission(); + loadLatestRevision(); }, [selectedDocId, selectedStage]); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); - if (!selectedDocId || !selectedStage || !revisionCodeInput || !fileBlob) { + if (!selectedDocId || !selectedStage || !revisionInput || !fileBlob) { toast.error("Please fill in all required fields"); return; } @@ -256,30 +235,17 @@ export const PublishDialog: React.FC = ({ // Create FormData const formData = new FormData(); formData.append("documentId", selectedDocId); - formData.append("stageName", selectedStage); - formData.append("revisionCode", revisionCodeInput); + formData.append("stage", selectedStage); + formData.append("revision", revisionInput); formData.append("customFileName", customFileName); + formData.append("uploaderType", "vendor"); // Default value - if (submitterName) { - formData.append("submittedBy", submitterName); + if (uploaderName) { + formData.append("uploaderName", uploaderName); } - if (session?.user?.email) { - formData.append("submittedByEmail", session.user.email); - } - - if (submissionTitle) { - formData.append("submissionTitle", submissionTitle); - } - - if (submissionDescription) { - formData.append("submissionDescription", submissionDescription); - } - - // Get vendor info from selected document - const selectedDoc = documents.find(doc => doc.id === parseInt(selectedDocId, 10)); - if (selectedDoc) { - formData.append("vendorId", String(selectedDoc.vendorId)); + if (comment) { + formData.append("comment", comment); } // Append file as attachment @@ -290,14 +256,12 @@ export const PublishDialog: React.FC = ({ formData.append("attachment", file); } - // Call server action - const result = await createSubmissionAction(formData); + // Call server action directly + const result = await createRevisionAction(formData); - if (result.success) { + if (result) { toast.success("Document published successfully!"); onOpenChange(false); - } else { - toast.error(result.error || "Failed to publish document"); } } catch (error) { console.error("Error publishing document:", error); @@ -337,6 +301,7 @@ export const PublishDialog: React.FC = ({ className="w-full justify-between" disabled={isLoading || documents.length === 0} > + {/* Add text-overflow handling for selected document display */} {selectedDocumentDisplay ? selectedDocumentDisplay @@ -373,6 +338,7 @@ export const PublishDialog: React.FC = ({ : "opacity-0" )} /> + {/* Add text-overflow handling for document items */} {doc.docNumber} - {doc.title} ))} @@ -400,6 +366,7 @@ export const PublishDialog: React.FC = ({ {stages.map((stage) => ( + {/* Add text-overflow handling for stage names */} {stage.stageName} ))} @@ -408,41 +375,27 @@ export const PublishDialog: React.FC = ({
- {/* Revision Code Input */} + {/* Revision Input */}
-
-
- -
- setSubmissionTitle(e.target.value)} - placeholder="Optional submission title" - /> -
-
-
-