From 36dd60ca6fce7712b35e6d7c1b9602710f442ada Mon Sep 17 00:00:00 2001 From: dujinkim Date: Wed, 28 May 2025 12:26:28 +0000 Subject: (최겸) 기술영업 해양 rfq 개발v1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/rfqs-tech/table/rfqs-table-floating-bar.tsx | 338 ++++++++++++++++++++++++ 1 file changed, 338 insertions(+) create mode 100644 lib/rfqs-tech/table/rfqs-table-floating-bar.tsx (limited to 'lib/rfqs-tech/table/rfqs-table-floating-bar.tsx') diff --git a/lib/rfqs-tech/table/rfqs-table-floating-bar.tsx b/lib/rfqs-tech/table/rfqs-table-floating-bar.tsx new file mode 100644 index 00000000..daef7e0b --- /dev/null +++ b/lib/rfqs-tech/table/rfqs-table-floating-bar.tsx @@ -0,0 +1,338 @@ +"use client" + +import * as React from "react" +import { Table } from "@tanstack/react-table" +import { toast } from "sonner" +import { Calendar, type CalendarProps } from "@/components/ui/calendar" +import { Button } from "@/components/ui/button" +import { Portal } from "@/components/ui/portal" +import { + Select, + SelectTrigger, + SelectContent, + SelectGroup, + SelectItem, + SelectValue, +} from "@/components/ui/select" +import { Separator } from "@/components/ui/separator" +import { + Tooltip, + TooltipTrigger, + TooltipContent, +} from "@/components/ui/tooltip" +import { Kbd } from "@/components/kbd" +import { ActionConfirmDialog } from "@/components/ui/action-dialog" + +import { ArrowUp, CheckCircle2, Download, Loader, Trash2, X, CalendarIcon } from "lucide-react" + +import { exportTableToExcel } from "@/lib/export" + +import { RfqWithItemCount, rfqs } from "@/db/schema/rfq" +import { modifyRfqs, removeRfqs } from "../service" + +interface RfqsTableFloatingBarProps { + table: Table +} + +/** + * 추가된 로직: + * - 달력(캘린더) 아이콘 버튼 + * - 눌렀을 때 Popover로 Calendar 표시 + * - 날짜 선택 시 Confirm 다이얼로그 → modifyRfqs({ dueDate }) + */ +export function RfqsTableFloatingBar({ table }: RfqsTableFloatingBarProps) { + const rows = table.getFilteredSelectedRowModel().rows + const [isPending, startTransition] = React.useTransition() + const [action, setAction] = React.useState<"update-status" | "export" | "delete" | "update-dueDate">() + const [confirmDialogOpen, setConfirmDialogOpen] = React.useState(false) + + const [confirmProps, setConfirmProps] = React.useState<{ + title: string + description?: string + onConfirm: () => Promise | void + }>({ + title: "", + description: "", + onConfirm: () => {}, + }) + + // 캘린더 Popover 열림 여부 + const [calendarOpen, setCalendarOpen] = React.useState(false) + const [selectedDate, setSelectedDate] = React.useState(null) + + // Clear selection on Escape key press + React.useEffect(() => { + function handleKeyDown(event: KeyboardEvent) { + if (event.key === "Escape") { + table.toggleAllRowsSelected(false) + } + } + window.addEventListener("keydown", handleKeyDown) + return () => window.removeEventListener("keydown", handleKeyDown) + }, [table]) + + function handleDeleteConfirm() { + setAction("delete") + setConfirmProps({ + title: `Delete ${rows.length} RFQ${rows.length > 1 ? "s" : ""}?`, + description: "This action cannot be undone.", + onConfirm: async () => { + startTransition(async () => { + const { error } = await removeRfqs({ + ids: rows.map((row) => row.original.rfqId), + }) + if (error) { + toast.error(error) + return + } + toast.success("RFQs deleted") + table.toggleAllRowsSelected(false) + setConfirmDialogOpen(false) + }) + }, + }) + setConfirmDialogOpen(true) + } + + function handleSelectStatus(newStatus: RfqWithItemCount["status"]) { + setAction("update-status") + setConfirmProps({ + title: `Update ${rows.length} RFQ${rows.length > 1 ? "s" : ""} with status: ${newStatus}?`, + description: "This action will override their current status.", + onConfirm: async () => { + startTransition(async () => { + const { error } = await modifyRfqs({ + ids: rows.map((row) => row.original.rfqId), + status: newStatus as "DRAFT" | "PUBLISHED" | "EVALUATION" | "AWARDED", + }) + if (error) { + toast.error(error) + return + } + toast.success("RFQs updated") + setConfirmDialogOpen(false) + }) + }, + }) + setConfirmDialogOpen(true) + } + + // 1) 달력에서 날짜를 선택했을 때 → Confirm 다이얼로그 + function handleDueDateSelect(newDate: Date) { + setAction("update-dueDate") + + setConfirmProps({ + title: `Update ${rows.length} RFQ${rows.length > 1 ? "s" : ""} Due Date to ${newDate.toDateString()}?`, + description: "This action will override their current due date.", + onConfirm: async () => { + startTransition(async () => { + const { error } = await modifyRfqs({ + ids: rows.map((r) => r.original.rfqId), + dueDate: newDate, + }) + if (error) { + toast.error(error) + return + } + toast.success("Due date updated") + setConfirmDialogOpen(false) + setCalendarOpen(false) + }) + }, + }) + setConfirmDialogOpen(true) + } + + // 2) Export + function handleExport() { + setAction("export") + startTransition(() => { + exportTableToExcel(table, { + excludeColumns: ["select", "actions"], + onlySelected: true, + }) + }) + } + + // Floating bar UI + return ( + +
+
+
+ {/* Selection Info + Clear */} +
+ + {rows.length} selected + + + + + + + +

Clear selection

+ + Esc + +
+
+
+ + + +
+ {/* 1) Status Update */} + + + {/* 2) Due Date Update: Calendar Popover */} + + + + + +

Update Due Date

+
+
+ + {/* Calendar Popover (간단 구현) */} + {calendarOpen && ( +
+ { + if (date) { + setSelectedDate(date) + handleDueDateSelect(date) + } + }} + initialFocus + /> +
+ )} + + {/* 3) Export */} + + + + + +

Export tasks

+
+
+ + {/* 4) Delete */} + + + + + +

Delete tasks

+
+
+
+
+
+
+ + {/* 공용 Confirm Dialog */} + +
+ ) +} \ No newline at end of file -- cgit v1.2.3