summaryrefslogtreecommitdiff
path: root/lib/vendor-document-list/plant/upload/toolbar-actions.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-09-19 07:51:27 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-09-19 07:51:27 +0000
commit9ecdfb23fe3df6a5df86782385002c562dfc1198 (patch)
tree4188cb7e6bf2c862d9c86a59d79946bd41217227 /lib/vendor-document-list/plant/upload/toolbar-actions.tsx
parentb67861fbb424c7ad47ad1538f75e2945bd8890c5 (diff)
(대표님) rfq 히스토리, swp 등
Diffstat (limited to 'lib/vendor-document-list/plant/upload/toolbar-actions.tsx')
-rw-r--r--lib/vendor-document-list/plant/upload/toolbar-actions.tsx242
1 files changed, 242 insertions, 0 deletions
diff --git a/lib/vendor-document-list/plant/upload/toolbar-actions.tsx b/lib/vendor-document-list/plant/upload/toolbar-actions.tsx
new file mode 100644
index 00000000..072fd72d
--- /dev/null
+++ b/lib/vendor-document-list/plant/upload/toolbar-actions.tsx
@@ -0,0 +1,242 @@
+"use client"
+
+import * as React from "react"
+import { type Table } from "@tanstack/react-table"
+import { Download, RefreshCw, Upload, Send, AlertCircle } from "lucide-react"
+import { toast } from "sonner"
+
+import { exportTableToExcel } from "@/lib/export"
+import { Button } from "@/components/ui/button"
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+} from "@/components/ui/alert-dialog"
+import { StageSubmissionView } from "@/db/schema"
+import { DataTableRowAction } from "@/types/table"
+import { MultiUploadDialog } from "./components/multi-upload-dialog"
+import { useRouter, useSearchParams } from "next/navigation"
+
+interface StageSubmissionToolbarActionsProps {
+ table: Table<StageSubmissionView>
+ rowAction: DataTableRowAction<StageSubmissionView> | null
+ setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<StageSubmissionView> | null>>
+}
+
+export function StageSubmissionToolbarActions({
+ table,
+ rowAction,
+ setRowAction
+}: StageSubmissionToolbarActionsProps) {
+ const selectedRows = table.getFilteredSelectedRowModel().rows
+ const router = useRouter()
+ const searchParams = useSearchParams()
+
+ const projectId = searchParams.get('projectId')
+
+
+ const [isSyncing, setIsSyncing] = React.useState(false)
+ const [showSyncDialog, setShowSyncDialog] = React.useState(false)
+ const [syncTargets, setSyncTargets] = React.useState<typeof selectedRows>([])
+
+ const handleUploadComplete = () => {
+ // Refresh table
+ router.refresh()
+ }
+
+ const handleSyncClick = () => {
+ const rowsRequiringSync = selectedRows.filter(
+ row => row.original.requiresSync && row.original.latestSubmissionId
+ )
+ setSyncTargets(rowsRequiringSync)
+ setShowSyncDialog(true)
+ }
+
+ const handleSyncConfirm = async () => {
+ setShowSyncDialog(false)
+ setIsSyncing(true)
+
+ try {
+ // Extract submission IDs
+ const submissionIds = syncTargets
+ .map(row => row.original.latestSubmissionId)
+ .filter((id): id is number => id !== null)
+
+ if (submissionIds.length === 0) {
+ toast.error("No submissions to sync.")
+ return
+ }
+
+ // API call
+ const response = await fetch('/api/stage-submissions/sync', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ submissionIds }),
+ })
+
+ const result = await response.json()
+
+ if (result.success) {
+ toast.success(result.message)
+
+ // Display detailed information for successful items
+ if (result.results?.details) {
+ const successCount = result.results.details.filter((d: any) => d.success).length
+ const failedCount = result.results.details.filter((d: any) => !d.success).length
+
+ if (failedCount > 0) {
+ toast.warning(`${successCount} succeeded, ${failedCount} failed`)
+ }
+ }
+
+ // Refresh table
+ router.refresh()
+ table.toggleAllPageRowsSelected(false) // Deselect all
+ } else {
+ toast.error(result.error || "Sync failed")
+ }
+ } catch (error) {
+ console.error("Sync error:", error)
+ toast.error("An error occurred during synchronization.")
+ } finally {
+ setIsSyncing(false)
+ }
+ }
+
+ return (
+ <>
+ <div className="flex items-center gap-2">
+ {projectId && (
+ <MultiUploadDialog
+ projectId={parseInt(projectId)}
+ // projectCode={projectCode}
+ onUploadComplete={handleUploadComplete}
+ />
+ )}
+ {selectedRows.length > 0 && (
+ <>
+ {/* Bulk Upload for selected rows that require submission */}
+ {selectedRows.some(row => row.original.requiresSubmission) && (
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={() => {
+ // Filter selected rows that require submission
+ const rowsRequiringSubmission = selectedRows.filter(
+ row => row.original.requiresSubmission
+ )
+ // Open bulk upload dialog
+ console.log("Bulk upload for:", rowsRequiringSubmission)
+ }}
+ className="gap-2"
+ >
+ <Upload className="size-4" />
+ <span>Upload ({selectedRows.filter(r => r.original.requiresSubmission).length})</span>
+ </Button>
+ )}
+
+ {/* Bulk Sync for selected rows that need syncing */}
+ {selectedRows.some(row => row.original.requiresSync && row.original.latestSubmissionId) && (
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={handleSyncClick}
+ disabled={isSyncing}
+ className="gap-2"
+ >
+ {isSyncing ? (
+ <>
+ <RefreshCw className="size-4 animate-spin" />
+ <span>Syncing...</span>
+ </>
+ ) : (
+ <>
+ <RefreshCw className="size-4" />
+ <span>Sync ({selectedRows.filter(r => r.original.requiresSync && r.original.latestSubmissionId).length})</span>
+ </>
+ )}
+ </Button>
+ )}
+ </>
+ )}
+
+ {/* Export Button */}
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={() =>
+ exportTableToExcel(table, {
+ filename: `stage-submissions-${new Date().toISOString().split('T')[0]}`,
+ excludeColumns: ["select", "actions"],
+ })
+ }
+ className="gap-2"
+ >
+ <Download className="size-4" />
+ <span className="hidden sm:inline">Export</span>
+ </Button>
+ </div>
+
+ {/* Sync Confirmation Dialog */}
+ <AlertDialog open={showSyncDialog} onOpenChange={setShowSyncDialog}>
+ <AlertDialogContent>
+ <AlertDialogHeader>
+ <AlertDialogTitle className="flex items-center gap-2">
+ <RefreshCw className="size-5" />
+ Sync to Buyer System
+ </AlertDialogTitle>
+ <AlertDialogDescription className="space-y-3">
+ <div>
+ Are you sure you want to sync {syncTargets.length} selected submission(s) to the buyer system?
+ </div>
+ <div className="space-y-2 rounded-lg bg-muted p-3">
+ <div className="text-sm font-medium">Items to sync:</div>
+ <ul className="text-sm space-y-1">
+ {syncTargets.slice(0, 3).map((row, idx) => (
+ <li key={idx} className="flex items-center gap-2">
+ <span className="text-muted-foreground">•</span>
+ <span>{row.original.docNumber}</span>
+ <span className="text-muted-foreground">-</span>
+ <span>{row.original.stageName}</span>
+ <span className="text-muted-foreground">
+ (Rev.{row.original.latestRevisionNumber})
+ </span>
+ </li>
+ ))}
+ {syncTargets.length > 3 && (
+ <li className="text-muted-foreground">
+ ... and {syncTargets.length - 3} more
+ </li>
+ )}
+ </ul>
+ </div>
+ <div className="flex items-start gap-2 text-sm text-amber-600">
+ <AlertCircle className="size-4 mt-0.5 shrink-0" />
+ <div>
+ Synchronized files will be sent to the SHI Buyer System and
+ cannot be recalled after transmission.
+ </div>
+ </div>
+ </AlertDialogDescription>
+ </AlertDialogHeader>
+ <AlertDialogFooter>
+ <AlertDialogCancel>Cancel</AlertDialogCancel>
+ <AlertDialogAction
+ onClick={handleSyncConfirm}
+ // className="bg-samsung hover:bg-samsung/90"
+ >
+ Start Sync
+ </AlertDialogAction>
+ </AlertDialogFooter>
+ </AlertDialogContent>
+ </AlertDialog>
+ </>
+ )
+} \ No newline at end of file