diff options
Diffstat (limited to 'lib/roles/table/update-roles-sheet.tsx')
| -rw-r--r-- | lib/roles/table/update-roles-sheet.tsx | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/lib/roles/table/update-roles-sheet.tsx b/lib/roles/table/update-roles-sheet.tsx new file mode 100644 index 00000000..cbe20352 --- /dev/null +++ b/lib/roles/table/update-roles-sheet.tsx @@ -0,0 +1,331 @@ +"use client" + +import * as React from "react" +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import { toast } from "sonner" +import { + Sheet, + SheetClose, + SheetContent, + SheetDescription, + SheetFooter, + SheetHeader, + SheetTitle, +} from "@/components/ui/sheet" +import { Button } from "@/components/ui/button" +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" +import { Input } from "@/components/ui/input" +import { + Select, + SelectTrigger, + SelectContent, + SelectItem, + SelectValue, + SelectGroup, +} from "@/components/ui/select" +// import your MultiSelect or other role selection +import { RoleView, userRoles, type UserView } from "@/db/schema/users" +import { getAllCompanies, modifiUser } from "@/lib/admin-users/service" +import { modifiRole } from "../services" +import { updateRoleSchema, UpdateRoleSchema } from "../validations" +import { Check, ChevronsUpDown, Loader } from "lucide-react" +import { Textarea } from "@/components/ui/textarea" +import { + Popover, + PopoverTrigger, + PopoverContent, +} from "@/components/ui/popover" +import { + Command, + CommandInput, + CommandList, + CommandGroup, + CommandItem, + CommandEmpty, +} from "@/components/ui/command" +import { Company } from "@/db/schema/companies" +import { cn } from "@/lib/utils" + +export interface UpdateRoleSheetProps + extends React.ComponentPropsWithRef<typeof Sheet> { + role: RoleView | null +} + +const domainOptions = [ + { value: "partners", label: "협력업체" }, + { value: "evcp", label: "삼성중공업" }, +] + + + +export function UpdateRolesSheet({ role, ...props }: UpdateRoleSheetProps) { + const [isUpdatePending, startUpdateTransition] = React.useTransition() + const [companies, setCompanies] = React.useState<Company[]>([]) // 회사 목록 + + React.useEffect(() => { + getAllCompanies().then((res) => { + setCompanies(res) + }) + }, []) + + + // 1) RHF 설정 + const form = useForm<UpdateRoleSchema>({ + resolver: zodResolver(updateRoleSchema), + defaultValues: { + name: role?.name ?? "", + description: role?.description ?? "", + domain: (role?.domain === "evcp" || role?.domain === "partners") + ? role?.domain + : undefined, + }, + }) + + // 2) user prop 바뀔 때마다 form.reset + React.useEffect(() => { + if (role) { + form.reset({ + name: role.name, + description: role.description, + domain: role.domain as "evcp" | "partners" | undefined, + }) + } + }, [role, form]) + + const selectedDomain = form.watch("domain") + + + // 3) onSubmit + async function onSubmit(input: UpdateRoleSchema) { + startUpdateTransition(async () => { + if (!role) return + + const { error } = await modifiRole({ + id: role.id, // user.userId + ...input, + }) + + if (error) { + toast.error(error) + return + } + + // 성공 시 + form.reset() + props.onOpenChange?.(false) + toast.success("User updated successfully!") + }) + } + + return ( + <Sheet {...props}> + <SheetContent className="flex flex-col gap-6 sm:max-w-md"> + <SheetHeader className="text-left"> + <SheetTitle>Update user</SheetTitle> + <SheetDescription> + Update the user details and save the changes + </SheetDescription> + </SheetHeader> + + {/* 4) RHF Form */} + <Form {...form}> + <form + onSubmit={form.handleSubmit(onSubmit)} + className="flex flex-col gap-4" + > + {/* name */} + <FormField + control={form.control} + name="name" + render={({ field }) => ( + <FormItem> + <FormLabel>Role Name</FormLabel> + <FormControl> + <Input + placeholder="e.g. admin" + {...field} + /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + + <FormField + control={form.control} + name="description" + render={({ field }) => ( + <FormItem> + <FormLabel>Role Description</FormLabel> + <FormControl> + <Textarea + placeholder="Describe role" + className="resize-none" + {...field} + /> + </FormControl> + {/* <FormDescription> + You can <span>@mention</span> other users and organizations to + link to them. + </FormDescription> */} + <FormMessage /> + </FormItem> + )} + /> + + + {/* language Select */} + <FormField + control={form.control} + name="domain" + render={({ field }) => ( + <FormItem> + <FormLabel>Domain</FormLabel> + <FormControl> + <Select + onValueChange={field.onChange} + // 'value'로 현재 값 연결. defaultValue 대신 Controlled 컴포넌트로 + value={field.value} + > + <SelectTrigger> + <SelectValue placeholder="Select Domain" /> + </SelectTrigger> + <SelectContent> + {domainOptions.map((v, index) => ( + <SelectItem key={index} value={v.value}> + {v.label} + </SelectItem> + ))} + </SelectContent> + </Select> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + + {selectedDomain === "partners" && ( + <FormField + control={form.control} + name="company_id" + render={({ field }) => { + // 현재 선택된 회사 ID (number) → 문자열 + const valueString = field.value ? String(field.value) : "" + + + // 현재 선택된 회사 + const selectedCompany = companies.find( + (c) => String(c.id) === valueString + ) + + const selectedCompanyLabel = selectedCompany && `${selectedCompany.name} ${selectedCompany.taxID}` + + const [popoverOpen, setPopoverOpen] = React.useState(false) + + + return ( + <FormItem> + <FormLabel>Company</FormLabel> + <FormControl> + <Popover + open={popoverOpen} + onOpenChange={setPopoverOpen} + modal={true} + > + <PopoverTrigger asChild> + <Button + variant="outline" + role="combobox" + aria-expanded={popoverOpen} + className="w-full justify-between" + > + {selectedCompany + ? `${selectedCompany.name} ${selectedCompany.taxID}` + : "Select company..."} + <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" /> + </Button> + </PopoverTrigger> + + <PopoverContent className="w-full p-0" side="bottom" > + <Command> + <CommandInput + placeholder="Search company..." + className="h-9" + + /> + <CommandList> + <CommandEmpty>No company found.</CommandEmpty> + <CommandGroup> + {companies.map((comp) => { + // string(comp.id) + const compIdStr = String(comp.id) + const label = `${comp.name}${comp.taxID}` + const label2 = `${comp.name} ${comp.taxID}` + return ( + <CommandItem + key={comp.id} + value={label2} + onSelect={() => { + // 회사 ID를 number로 + field.onChange(Number(comp.id)) + setPopoverOpen(false) + + }} + > + {label2} + <Check + className={cn( + "ml-auto h-4 w-4", + selectedCompanyLabel === label2 + ? "opacity-100" + : "opacity-0" + )} + /> + </CommandItem> + ) + })} + </CommandGroup> + </CommandList> + </Command> + </PopoverContent> + </Popover> + </FormControl> + <FormMessage /> + </FormItem> + ) + }} + /> + )} + + {/* 5) Footer: Cancel, Save */} + <SheetFooter className="gap-2 pt-2 sm:space-x-0"> + <SheetClose asChild> + <Button type="button" variant="outline"> + Cancel + </Button> + </SheetClose> + + <Button type="submit" disabled={isUpdatePending}> + {isUpdatePending && ( + <Loader + className="mr-2 size-4 animate-spin" + aria-hidden="true" + /> + )} + Save + </Button> + </SheetFooter> + </form> + </Form> + </SheetContent> + </Sheet> + ) +}
\ No newline at end of file |
