From 1a2241c40e10193c5ff7008a7b7b36cc1d855d96 Mon Sep 17 00:00:00 2001 From: joonhoekim <26rote@gmail.com> Date: Tue, 25 Mar 2025 15:55:45 +0900 Subject: initial commit --- .../table/ausers-table-floating-bar.tsx | 389 +++++++++++++++++++++ 1 file changed, 389 insertions(+) create mode 100644 lib/admin-users/table/ausers-table-floating-bar.tsx (limited to 'lib/admin-users/table/ausers-table-floating-bar.tsx') diff --git a/lib/admin-users/table/ausers-table-floating-bar.tsx b/lib/admin-users/table/ausers-table-floating-bar.tsx new file mode 100644 index 00000000..ae950252 --- /dev/null +++ b/lib/admin-users/table/ausers-table-floating-bar.tsx @@ -0,0 +1,389 @@ +"use client" + +import * as React from "react" +import { userRoles, users, UserView, type User } from "@/db/schema/users" +import { SelectTrigger } from "@radix-ui/react-select" +import { type Table } from "@tanstack/react-table" +import { + ArrowUp, + CheckCircle2, + Download, + Loader, + Trash2, + X, Check +} from "lucide-react" +import { toast } from "sonner" + +import { exportTableToExcel } from "@/lib/export" +import { Button } from "@/components/ui/button" +import { Portal } from "@/components/ui/portal" +import { + Select, + SelectContent, + SelectGroup, + SelectItem, +} from "@/components/ui/select" +import { Separator } from "@/components/ui/separator" +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip" +import { Kbd } from "@/components/kbd" + +import { modifiUsers, getAllCompanies, removeUsers } from "@/lib//admin-users/service" +import { type Company } from "@/db/schema/companies" +import { + Popover, + PopoverTrigger, + PopoverContent, +} from "@/components/ui/popover" +import { + Command, + CommandInput, + CommandList, + CommandGroup, + CommandItem, + CommandEmpty, +} from "@/components/ui/command" +import { cn } from "@/lib/utils" +import { MultiSelect } from "@/components/ui/multi-select" +import { ActionConfirmDialog } from "@/components/ui/action-dialog" + +interface AusersTableFloatingBarProps { + table: Table +} + + +export function AusersTableFloatingBar({ table }: AusersTableFloatingBarProps) { + const rows = table.getFilteredSelectedRowModel().rows + + const [isPending, startTransition] = React.useTransition() + const [action, setAction] = React.useState< + "update-company" | "update-roles" | "export" | "delete" + >() + const [companies, setCompanies] = React.useState([]) // 회사 목록 + + // 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]) + + React.useEffect(() => { + // 회사 목록 불러오기 (예시) + getAllCompanies().then((res) => { + setCompanies(res) + }) + }, []) + + const [popoverOpen, setPopoverOpen] = React.useState(false) + const [rolesPopoverOpen, setRolesPopoverOpen] = React.useState(false) + + + // 공용 confirm dialog state + const [confirmDialogOpen, setConfirmDialogOpen] = React.useState(false) + const [confirmProps, setConfirmProps] = React.useState<{ + title: string + description?: string + onConfirm: () => Promise | void + }>({ + title: "", + description: "", + onConfirm: () => { }, + }) + + // 1) "삭제" Confirm 열기 + function handleDeleteConfirm() { + setAction("delete") + setConfirmProps({ + title: `Delete ${rows.length} user${rows.length > 1 ? "s" : ""}?`, + description: "This action cannot be undone.", + onConfirm: async () => { + startTransition(async () => { + const { error } = await removeUsers({ + ids: rows.map((row) => row.original.user_id), + }) + if (error) { + toast.error(error) + return + } + toast.success("Users deleted") + table.toggleAllRowsSelected(false) + setConfirmDialogOpen(false) + }) + }, + }) + setConfirmDialogOpen(true) + } + + // 2) "회사 업데이트"에서 회사 선택 시 → Confirm Dialog + function handleSelectCompany(comp: Company) { + setAction("update-company") + setPopoverOpen(false) + + // Confirm Dialog에 전달할 내용 + setConfirmProps({ + title: `Update ${rows.length} user${rows.length > 1 ? "s" : ""} to "${comp.name}"?`, + description: `TaxID: ${comp.taxID}. This action will overwrite their current company.`, + onConfirm: async () => { + startTransition(async () => { + const { error } = await modifiUsers({ + ids: rows.map((row) => row.original.user_id), + companyId: comp.id, + }) + if (error) { + toast.error(error) + return + } + toast.success("Users updated") + setConfirmDialogOpen(false) + }) + }, + }) + setConfirmDialogOpen(true) + } + + // 3) "역할 업데이트" MultiSelect 후 → Confirm Dialog + function handleSelectRoles(newRoles: string[]) { + setAction("update-roles") + setRolesPopoverOpen(false) + + setConfirmProps({ + title: `Update ${rows.length} user${rows.length > 1 ? "s" : ""} with roles: ${newRoles.join(", ")}?`, + description: "This action will override their current roles.", + onConfirm: async () => { + startTransition(async () => { + const { error } = await modifiUsers({ + ids: rows.map((row) => row.original.user_id), + roles: newRoles as ("admin" | "normal")[], + }) + if (error) { + toast.error(error) + return + } + toast.success("Users updated") + setConfirmDialogOpen(false) + }) + }, + }) + setConfirmDialogOpen(true) + } + + return ( + +
+
+
+
+ + {rows.length} selected + + + + + + + +

Clear selection

+ + Esc + +
+
+
+ +
+ + + + + + + + + +

Update company

+
+
+ + + + + + No company found. + + {companies.map((comp) => { + const label = `${comp.name} (${comp.taxID})` + return ( + handleSelectCompany(comp)} + > + {label} + + ) + })} + + + + +
+ + + + + + + + + + +

Update roles

+
+
+ + { + handleSelectRoles(newRoles) + }} + /> + + +
+ + + + + + + +

Export users

+
+
+ + + + + + +

Delete users

+
+
+
+
+
+
+ + {/* 공용 Confirm Dialog */} + +
+ ) +} -- cgit v1.2.3