From ef4c533ebacc2cdc97e518f30e9a9350004fcdfb Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Mon, 28 Apr 2025 02:13:30 +0000
Subject: ~20250428 작업사항
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 components/form-data/form-data-table copy.tsx | 539 ++++++++++++++++++++++++++
 1 file changed, 539 insertions(+)
 create mode 100644 components/form-data/form-data-table copy.tsx

(limited to 'components/form-data/form-data-table copy.tsx')

diff --git a/components/form-data/form-data-table copy.tsx b/components/form-data/form-data-table copy.tsx
new file mode 100644
index 00000000..aa16513a
--- /dev/null
+++ b/components/form-data/form-data-table copy.tsx	
@@ -0,0 +1,539 @@
+"use client";
+
+import * as React from "react";
+import { useParams, useRouter } from "next/navigation";
+import { useTranslation } from "@/i18n/client";
+
+import { ClientDataTable } from "../client-data-table/data-table";
+import {
+  getColumns,
+  DataTableRowAction,
+  DataTableColumnJSON,
+  ColumnType,
+} from "./form-data-table-columns";
+import type { DataTableAdvancedFilterField } from "@/types/table";
+import { Button } from "../ui/button";
+import { 
+  Download, 
+  Loader, 
+  Save, 
+  Upload, 
+  Plus, 
+  Tag, 
+  TagsIcon, 
+  FileText, 
+  FileSpreadsheet,
+  FileOutput,
+  Clipboard, 
+  Send
+} from "lucide-react";
+import { toast } from "sonner";
+import {
+  getReportTempList,
+  sendFormDataToSEDP,
+  syncMissingTags,
+  updateFormDataInDB,
+} from "@/lib/forms/services";
+import { UpdateTagSheet } from "./update-form-sheet";
+import { FormDataReportTempUploadDialog } from "./form-data-report-temp-upload-dialog";
+import { FormDataReportDialog } from "./form-data-report-dialog";
+import { FormDataReportBatchDialog } from "./form-data-report-batch-dialog";
+import {
+  DropdownMenu,
+  DropdownMenuContent,
+  DropdownMenuItem,
+  DropdownMenuTrigger,
+  DropdownMenuSeparator,
+} from "@/components/ui/dropdown-menu";
+import { AddFormTagDialog } from "./add-formTag-dialog";
+import { importExcelData } from "./import-excel-form";
+import { exportExcelData } from "./export-excel-form";
+import { SEDPConfirmationDialog, SEDPStatusDialog } from "./sedp-components";
+
+interface GenericData {
+  [key: string]: any;
+}
+
+export interface DynamicTableProps {
+  dataJSON: GenericData[];
+  columnsJSON: DataTableColumnJSON[];
+  contractItemId: number;
+  formCode: string;
+  formId: number;
+  projectId: number;
+  formName?: string;
+  objectCode?: string;
+}
+
+export default function DynamicTable({
+  dataJSON,
+  columnsJSON,
+  contractItemId,
+  formCode,
+  formId,
+  projectId,
+  formName = `VD)${formCode}`, // Default form name based on formCode
+  objectCode = "LO_PT_CLAS",   // Default object code
+}: DynamicTableProps) {
+  const params = useParams();
+  const router = useRouter();
+  const lng = (params?.lng as string) || "ko";
+  const { t } = useTranslation(lng, "translation");
+
+  const [rowAction, setRowAction] =
+    React.useState<DataTableRowAction<GenericData> | null>(null);
+  const [tableData, setTableData] = React.useState<GenericData[]>(dataJSON);
+
+  
+  console.log(tableData)
+  console.log(columnsJSON)
+
+  // Update tableData when dataJSON changes
+  React.useEffect(() => {
+    setTableData(dataJSON);
+  }, [dataJSON]);
+  
+  // Separate loading states for different operations
+  const [isSyncingTags, setIsSyncingTags] = React.useState(false);
+  const [isImporting, setIsImporting] = React.useState(false);
+  const [isExporting, setIsExporting] = React.useState(false);
+  const [isSaving, setIsSaving] = React.useState(false);
+  const [isSendingSEDP, setIsSendingSEDP] = React.useState(false);
+  
+  // Any operation in progress
+  const isAnyOperationPending = isSyncingTags || isImporting || isExporting || isSaving || isSendingSEDP;
+  
+  // SEDP dialogs state
+  const [sedpConfirmOpen, setSedpConfirmOpen] = React.useState(false);
+  const [sedpStatusOpen, setSedpStatusOpen] = React.useState(false);
+  const [sedpStatusData, setSedpStatusData] = React.useState({
+    status: 'success' as 'success' | 'error' | 'partial',
+    message: '',
+    successCount: 0,
+    errorCount: 0,
+    totalCount: 0
+  });
+  
+  const [tempUpDialog, setTempUpDialog] = React.useState(false);
+  const [reportData, setReportData] = React.useState<GenericData[]>([]);
+  const [batchDownDialog, setBatchDownDialog] = React.useState(false);
+  const [tempCount, setTempCount] = React.useState(0);
+  const [addTagDialogOpen, setAddTagDialogOpen] = React.useState(false);
+
+  React.useEffect(() => {
+    const getTempCount = async () => {
+      const tempList = await getReportTempList(contractItemId, formId);
+      setTempCount(tempList.length);
+    };
+
+    getTempCount();
+  }, [contractItemId, formId, tempUpDialog]);
+
+  const columns = React.useMemo(
+    () => getColumns<GenericData>({ columnsJSON, setRowAction, setReportData, tempCount }),
+    [columnsJSON, setRowAction, setReportData, tempCount]
+  );
+
+  function mapColumnTypeToAdvancedFilterType(
+    columnType: ColumnType
+  ): DataTableAdvancedFilterField<GenericData>["type"] {
+    switch (columnType) {
+      case "STRING":
+        return "text";
+      case "NUMBER":
+        return "number";
+      case "LIST":
+        return "select";
+      default:
+        return "text";
+    }
+  }
+
+  const advancedFilterFields = React.useMemo<
+    DataTableAdvancedFilterField<GenericData>[]
+  >(() => {
+    return columnsJSON.map((col) => ({
+      id: col.key,
+      label: col.label,
+      type: mapColumnTypeToAdvancedFilterType(col.type),
+      options:
+        col.type === "LIST"
+          ? col.options?.map((v) => ({ label: v, value: v }))
+          : undefined,
+    }));
+  }, [columnsJSON]);
+
+  // 태그 불러오기
+  async function handleSyncTags() {
+    try {
+      setIsSyncingTags(true);
+      const result = await syncMissingTags(contractItemId, formCode);
+
+      // Prepare the toast messages based on what changed
+      const changes = [];
+      if (result.createdCount > 0)
+        changes.push(`${result.createdCount}건 태그 생성`);
+      if (result.updatedCount > 0)
+        changes.push(`${result.updatedCount}건 태그 업데이트`);
+      if (result.deletedCount > 0)
+        changes.push(`${result.deletedCount}건 태그 삭제`);
+
+      if (changes.length > 0) {
+        // If any changes were made, show success message and reload
+        toast.success(`동기화 완료: ${changes.join(", ")}`);
+        router.refresh(); // Use router.refresh instead of location.reload
+      } else {
+        // If no changes were made, show an info message
+        toast.info("변경사항이 없습니다. 모든 태그가 최신 상태입니다.");
+      }
+    } catch (err) {
+      console.error(err);
+      toast.error("태그 동기화 중 에러가 발생했습니다.");
+    } finally {
+      setIsSyncingTags(false);
+    }
+  }
+  
+  // Excel Import - Modified to directly save to DB
+  async function handleImportExcel(e: React.ChangeEvent<HTMLInputElement>) {
+    const file = e.target.files?.[0];
+    if (!file) return;
+
+    try {
+      setIsImporting(true);
+      
+      // Call the updated importExcelData function with direct save capability
+      const result = await importExcelData({
+        file,
+        tableData,
+        columnsJSON,
+        formCode,  // Pass formCode for direct save
+        contractItemId,  // Pass contractItemId for direct save
+        onPendingChange: setIsImporting,
+        onDataUpdate: (newData) => {
+          // This is called only after successful DB save
+          setTableData(Array.isArray(newData) ? newData : newData(tableData));
+        }
+      });
+      
+      // If import and save was successful, refresh the page
+      if (result.success) {
+        router.refresh();
+      }
+    } catch (error) {
+      console.error("Import failed:", error);
+      toast.error("Failed to import Excel data");
+    } finally {
+      // Always clear the file input value
+      e.target.value = "";
+      setIsImporting(false);
+    }
+  }
+  
+  // SEDP Send handler (with confirmation)
+  function handleSEDPSendClick() {
+    if (tableData.length === 0) {
+      toast.error("No data to send to SEDP");
+      return;
+    }
+    
+    // Open confirmation dialog
+    setSedpConfirmOpen(true);
+  }
+  
+  // Actual SEDP send after confirmation
+// In your DynamicTable component, update the handler for SEDP sending
+
+async function handleSEDPSendConfirmed() {
+  try {
+    setIsSendingSEDP(true);
+    
+    // Validate data
+    const invalidData = tableData.filter((item) => !item.TAG_NO?.trim());
+    if (invalidData.length > 0) {
+      toast.error(`태그 번호가 없는 항목이 ${invalidData.length}개 있습니다.`);
+      setSedpConfirmOpen(false);
+      return;
+    }
+
+    // Then send to SEDP - pass formCode instead of formName
+    const sedpResult = await sendFormDataToSEDP(
+      formCode,        // Send formCode instead of formName
+      projectId,       // Project ID
+      tableData,       // Table data
+      columnsJSON      // Column definitions
+    );
+
+    // Close confirmation dialog
+    setSedpConfirmOpen(false);
+    
+    // Set status data based on result
+    if (sedpResult.success) {
+      setSedpStatusData({
+        status: 'success',
+        message: "Data successfully sent to SEDP",
+        successCount: tableData.length,
+        errorCount: 0,
+        totalCount: tableData.length
+      });
+    } else {
+      setSedpStatusData({
+        status: 'error',
+        message: sedpResult.message || "Failed to send data to SEDP",
+        successCount: 0,
+        errorCount: tableData.length,
+        totalCount: tableData.length
+      });
+    }
+    
+    // Open status dialog to show result
+    setSedpStatusOpen(true);
+    
+    // Refresh the route to get fresh data
+    router.refresh();
+    
+  } catch (err: any) {
+    console.error("SEDP error:", err);
+    
+    // Set error status
+    setSedpStatusData({
+      status: 'error',
+      message: err.message || "An unexpected error occurred",
+      successCount: 0,
+      errorCount: tableData.length,
+      totalCount: tableData.length
+    });
+    
+    // Close confirmation and open status
+    setSedpConfirmOpen(false);
+    setSedpStatusOpen(true);
+    
+  } finally {
+    setIsSendingSEDP(false);
+  }
+}
+  // Template Export
+  async function handleExportExcel() {
+    try {
+      setIsExporting(true);
+      await exportExcelData({
+        tableData,
+        columnsJSON,
+        formCode,
+        onPendingChange: setIsExporting
+      });
+    } finally {
+      setIsExporting(false);
+    }
+  }
+
+  // Handle batch document check
+  const handleBatchDocument = () => {
+    if (tempCount > 0) {
+      setBatchDownDialog(true);
+    } else {
+      toast.error("업로드된 Template File이 없습니다.");
+    }
+  };
+
+  return (
+    <>
+      <ClientDataTable
+        data={tableData}
+        columns={columns}
+        advancedFilterFields={advancedFilterFields}
+      >
+        {/* 버튼 그룹 */}
+        <div className="flex items-center gap-2">
+          {/* 태그 관리 드롭다운 */}
+          <DropdownMenu>
+            <DropdownMenuTrigger asChild>
+              <Button variant="outline" size="sm" disabled={isAnyOperationPending}>
+                {isSyncingTags && (
+                  <Loader className="mr-2 size-4 animate-spin" aria-hidden="true" />
+                )}
+                <TagsIcon className="size-4" />
+                Tag Operations
+              </Button>
+            </DropdownMenuTrigger>
+            <DropdownMenuContent align="end">
+              <DropdownMenuItem onClick={handleSyncTags} disabled={isAnyOperationPending}>
+                <Tag className="mr-2 h-4 w-4" />
+                Sync Tags
+              </DropdownMenuItem>
+              <DropdownMenuItem onClick={() => setAddTagDialogOpen(true)} disabled={isAnyOperationPending}>
+                <Plus className="mr-2 h-4 w-4" />
+                Add Tags
+              </DropdownMenuItem>
+            </DropdownMenuContent>
+          </DropdownMenu>
+          
+          {/* 리포트 관리 드롭다운 */}
+          <DropdownMenu>
+            <DropdownMenuTrigger asChild>
+              <Button variant="outline" size="sm" disabled={isAnyOperationPending}>
+                <Clipboard className="size-4" />
+                Report Operations
+              </Button>
+            </DropdownMenuTrigger>
+            <DropdownMenuContent align="end">
+              <DropdownMenuItem onClick={() => setTempUpDialog(true)} disabled={isAnyOperationPending}>
+                <Upload className="mr-2 h-4 w-4" />
+                Upload Template
+              </DropdownMenuItem>
+              <DropdownMenuItem onClick={handleBatchDocument} disabled={isAnyOperationPending}>
+                <FileOutput className="mr-2 h-4 w-4" />
+                Batch Document
+              </DropdownMenuItem>
+            </DropdownMenuContent>
+          </DropdownMenu>
+
+          {/* IMPORT 버튼 (파일 선택) */}
+          <Button asChild variant="outline" size="sm" disabled={isAnyOperationPending}>
+            <label>
+              {isImporting ? (
+                <Loader className="mr-2 size-4 animate-spin" aria-hidden="true" />
+              ) : (
+                <Upload className="size-4" />
+              )}
+              Import
+              <input
+                type="file"
+                accept=".xlsx,.xls"
+                onChange={handleImportExcel}
+                style={{ display: "none" }}
+                disabled={isAnyOperationPending}
+              />
+            </label>
+          </Button>
+
+          {/* EXPORT 버튼 */}
+          <Button
+            variant="outline"
+            size="sm"
+            onClick={handleExportExcel}
+            disabled={isAnyOperationPending}
+          >
+            {isExporting ? (
+              <Loader className="mr-2 size-4 animate-spin" />
+            ) : (
+              <Download className="mr-2 size-4" />
+            )}
+            Export
+          </Button>
+
+
+          {/* SEDP 전송 버튼 */}
+          <Button
+            variant="samsung"
+            size="sm"
+            onClick={handleSEDPSendClick}
+            disabled={isAnyOperationPending}
+          >
+            {isSendingSEDP ? (
+              <>
+                <Loader className="mr-2 size-4 animate-spin" />
+                SEDP 전송 중...
+              </>
+            ) : (
+              <>
+                <Send className="size-4" />
+                Send to SHI
+              </>
+            )}
+          </Button>
+        </div>
+      </ClientDataTable>
+
+      {/* Modal dialog for tag update */}
+      <UpdateTagSheet
+        open={rowAction?.type === "update"}
+        onOpenChange={(open) => {
+          if (!open) setRowAction(null);
+        }}
+        columns={columnsJSON}
+        rowData={rowAction?.row.original ?? null}
+        formCode={formCode}
+        contractItemId={contractItemId}
+        onUpdateSuccess={(updatedValues) => {
+          // Update the specific row in tableData when a single row is updated
+          if (rowAction?.row.original?.TAG_NO) {
+            const tagNo = rowAction.row.original.TAG_NO;
+            setTableData(prev => 
+              prev.map(item => 
+                item.TAG_NO === tagNo ? updatedValues : item
+              )
+            );
+          }
+        }}
+      />
+      
+      {/* Dialog for adding tags */}
+      <AddFormTagDialog 
+        projectId={projectId}
+        formCode={formCode}
+        formName={`Form ${formCode}`}
+        contractItemId={contractItemId}
+        open={addTagDialogOpen}
+        onOpenChange={setAddTagDialogOpen}
+      />
+      
+      {/* SEDP Confirmation Dialog */}
+      <SEDPConfirmationDialog
+        isOpen={sedpConfirmOpen}
+        onClose={() => setSedpConfirmOpen(false)}
+        onConfirm={handleSEDPSendConfirmed}
+        formName={formName}
+        tagCount={tableData.length}
+        isLoading={isSendingSEDP}
+      />
+      
+      {/* SEDP Status Dialog */}
+      <SEDPStatusDialog
+        isOpen={sedpStatusOpen}
+        onClose={() => setSedpStatusOpen(false)}
+        status={sedpStatusData.status}
+        message={sedpStatusData.message}
+        successCount={sedpStatusData.successCount}
+        errorCount={sedpStatusData.errorCount}
+        totalCount={sedpStatusData.totalCount}
+      />
+      
+      {/* Other dialogs */}
+      {tempUpDialog && (
+        <FormDataReportTempUploadDialog
+          columnsJSON={columnsJSON}
+          open={tempUpDialog}
+          setOpen={setTempUpDialog}
+          packageId={contractItemId}
+          formCode={formCode}
+          formId={formId}
+          uploaderType="vendor"
+        />
+      )}
+
+      {reportData.length > 0 && (
+        <FormDataReportDialog
+          columnsJSON={columnsJSON}
+          reportData={reportData}
+          setReportData={setReportData}
+          packageId={contractItemId}
+          formCode={formCode}
+          formId={formId}
+        />
+      )}
+
+      {batchDownDialog && (
+        <FormDataReportBatchDialog
+          open={batchDownDialog}
+          setOpen={setBatchDownDialog}
+          columnsJSON={columnsJSON}
+          reportData={tableData}
+          packageId={contractItemId}
+          formCode={formCode}
+          formId={formId}
+        />
+      )}
+    </>
+  );
+}
\ No newline at end of file
-- 
cgit v1.2.3

