diff options
Diffstat (limited to 'lib/rfqs/table/rfqs-table-floating-bar.tsx')
| -rw-r--r-- | lib/rfqs/table/rfqs-table-floating-bar.tsx | 338 |
1 files changed, 0 insertions, 338 deletions
diff --git a/lib/rfqs/table/rfqs-table-floating-bar.tsx b/lib/rfqs/table/rfqs-table-floating-bar.tsx deleted file mode 100644 index daef7e0b..00000000 --- a/lib/rfqs/table/rfqs-table-floating-bar.tsx +++ /dev/null @@ -1,338 +0,0 @@ -"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<RfqWithItemCount> -} - -/** - * 추가된 로직: - * - 달력(캘린더) 아이콘 버튼 - * - 눌렀을 때 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> | void - }>({ - title: "", - description: "", - onConfirm: () => {}, - }) - - // 캘린더 Popover 열림 여부 - const [calendarOpen, setCalendarOpen] = React.useState(false) - const [selectedDate, setSelectedDate] = React.useState<Date | null>(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 ( - <Portal> - <div className="fixed inset-x-0 bottom-10 z-50 mx-auto w-fit px-2.5"> - <div className="w-full overflow-x-auto"> - <div className="mx-auto flex w-fit items-center gap-2 rounded-md border bg-background p-2 text-foreground shadow"> - {/* Selection Info + Clear */} - <div className="flex h-7 items-center rounded-md border border-dashed pl-2.5 pr-1"> - <span className="whitespace-nowrap text-xs"> - {rows.length} selected - </span> - <Separator orientation="vertical" className="ml-2 mr-1" /> - <Tooltip> - <TooltipTrigger asChild> - <Button - variant="ghost" - size="icon" - className="size-5 hover:border" - onClick={() => table.toggleAllRowsSelected(false)} - > - <X className="size-3.5 shrink-0" aria-hidden="true" /> - </Button> - </TooltipTrigger> - <TooltipContent className="flex items-center border bg-accent px-2 py-1 font-semibold text-foreground dark:bg-zinc-900"> - <p className="mr-2">Clear selection</p> - <Kbd abbrTitle="Escape" variant="outline"> - Esc - </Kbd> - </TooltipContent> - </Tooltip> - </div> - - <Separator orientation="vertical" className="hidden h-5 sm:block" /> - - <div className="flex items-center gap-1.5"> - {/* 1) Status Update */} - <Select - onValueChange={(value: RfqWithItemCount["status"]) => handleSelectStatus(value)} - > - <Tooltip> - <SelectTrigger asChild> - <TooltipTrigger asChild> - <Button - variant="secondary" - size="icon" - className="size-7 border data-[state=open]:bg-accent data-[state=open]:text-accent-foreground" - disabled={isPending} - > - {isPending && action === "update-status" ? ( - <Loader className="size-3.5 animate-spin" aria-hidden="true" /> - ) : ( - <CheckCircle2 className="size-3.5" aria-hidden="true" /> - )} - </Button> - </TooltipTrigger> - </SelectTrigger> - <TooltipContent className="border bg-accent font-semibold text-foreground dark:bg-zinc-900"> - <p>Update status</p> - </TooltipContent> - </Tooltip> - <SelectContent align="center"> - <SelectGroup> - {rfqs.status.enumValues.map((status) => ( - <SelectItem key={status} value={status} className="capitalize"> - {status} - </SelectItem> - ))} - </SelectGroup> - </SelectContent> - </Select> - - {/* 2) Due Date Update: Calendar Popover */} - <Tooltip> - <TooltipTrigger asChild> - <Button - variant="secondary" - size="icon" - className="size-7 border" - disabled={isPending} - onClick={() => setCalendarOpen((open) => !open)} - > - {isPending && action === "update-dueDate" ? ( - <Loader className="size-3.5 animate-spin" aria-hidden="true" /> - ) : ( - <CalendarIcon className="size-3.5" aria-hidden="true" /> - )} - </Button> - </TooltipTrigger> - <TooltipContent className="border bg-accent font-semibold text-foreground dark:bg-zinc-900"> - <p>Update Due Date</p> - </TooltipContent> - </Tooltip> - - {/* Calendar Popover (간단 구현) */} - {calendarOpen && ( - <div className="absolute bottom-16 z-50 rounded-md border bg-background p-2 shadow"> - <Calendar - mode="single" - selected={selectedDate || new Date()} - onSelect={(date) => { - if (date) { - setSelectedDate(date) - handleDueDateSelect(date) - } - }} - initialFocus - /> - </div> - )} - - {/* 3) Export */} - <Tooltip> - <TooltipTrigger asChild> - <Button - variant="secondary" - size="icon" - className="size-7 border" - onClick={handleExport} - disabled={isPending} - > - {isPending && action === "export" ? ( - <Loader className="size-3.5 animate-spin" aria-hidden="true" /> - ) : ( - <Download className="size-3.5" aria-hidden="true" /> - )} - </Button> - </TooltipTrigger> - <TooltipContent className="border bg-accent font-semibold text-foreground dark:bg-zinc-900"> - <p>Export tasks</p> - </TooltipContent> - </Tooltip> - - {/* 4) Delete */} - <Tooltip> - <TooltipTrigger asChild> - <Button - variant="secondary" - size="icon" - className="size-7 border" - onClick={handleDeleteConfirm} - disabled={isPending} - > - {isPending && action === "delete" ? ( - <Loader className="size-3.5 animate-spin" aria-hidden="true" /> - ) : ( - <Trash2 className="size-3.5" aria-hidden="true" /> - )} - </Button> - </TooltipTrigger> - <TooltipContent className="border bg-accent font-semibold text-foreground dark:bg-zinc-900"> - <p>Delete tasks</p> - </TooltipContent> - </Tooltip> - </div> - </div> - </div> - </div> - - {/* 공용 Confirm Dialog */} - <ActionConfirmDialog - open={confirmDialogOpen} - onOpenChange={setConfirmDialogOpen} - title={confirmProps.title} - description={confirmProps.description} - onConfirm={confirmProps.onConfirm} - isLoading={ - isPending && (action === "delete" || action === "update-status" || action === "update-dueDate") - } - confirmLabel={ - action === "delete" - ? "Delete" - : action === "update-status" - ? "Update" - : action === "update-dueDate" - ? "Update" - : "Confirm" - } - confirmVariant={action === "delete" ? "destructive" : "default"} - /> - </Portal> - ) -}
\ No newline at end of file |
