diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-15 10:07:09 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-15 10:07:09 +0000 |
| commit | 4eb7532f822c821fb6b69bf103bd075fefba769b (patch) | |
| tree | b4bcf6c0bf791d71569f3f35498ed256bf7cfaf3 /lib/roles/table/add-role-dialog.tsx | |
| parent | 660c7888d885badab7af3e96f9c16bd0172ad0f1 (diff) | |
(대표님) 20250715 협력사 정기평가, spreadJS, roles 서비스에 함수 추가
Diffstat (limited to 'lib/roles/table/add-role-dialog.tsx')
| -rw-r--r-- | lib/roles/table/add-role-dialog.tsx | 248 |
1 files changed, 150 insertions, 98 deletions
diff --git a/lib/roles/table/add-role-dialog.tsx b/lib/roles/table/add-role-dialog.tsx index 365daf29..162aaa89 100644 --- a/lib/roles/table/add-role-dialog.tsx +++ b/lib/roles/table/add-role-dialog.tsx @@ -21,12 +21,13 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select" -import { Check, ChevronsUpDown, Loader } from "lucide-react" +import { Check, ChevronsUpDown, Loader, AlertTriangle } from "lucide-react" import { cn } from "@/lib/utils" import { toast } from "sonner" +import { Alert, AlertDescription } from "@/components/ui/alert" import { createRoleSchema, type CreateRoleSchema } from "../validations" -import { createRole } from "../services" +import { createRole, checkRegularEvaluationRoleExists } from "../services" import { Textarea } from "@/components/ui/textarea" import { Company } from "@/db/schema/companies" import { getAllCompanies } from "@/lib/admin-users/service" @@ -44,8 +45,6 @@ import { CommandEmpty, } from "@/components/ui/command" - - const domainOptions = [ { value: "partners", label: "협력업체" }, { value: "evcp", label: "삼성중공업" }, @@ -54,7 +53,9 @@ const domainOptions = [ export function AddRoleDialog() { const [open, setOpen] = React.useState(false) const [isAddPending, startAddTransition] = React.useTransition() - const [companies, setCompanies] = React.useState<Company[]>([]) // 회사 목록 + const [companies, setCompanies] = React.useState<Company[]>([]) + const [regularEvaluationExists, setRegularEvaluationExists] = React.useState(false) + const [isCheckingRegularEvaluation, setIsCheckingRegularEvaluation] = React.useState(false) React.useEffect(() => { getAllCompanies().then((res) => { @@ -67,12 +68,39 @@ export function AddRoleDialog() { resolver: zodResolver(createRoleSchema), defaultValues: { name: "", - domain: "evcp", // 기본값 + domain: "evcp", description: "", - // companyId: null, // optional }, }) + // name 필드 watch + const watchedName = form.watch("name") + + // "정기평가"가 포함된 이름인지 체크 + const isRegularEvaluationRole = watchedName.includes("정기평가") + + // 정기평가 role 존재 여부 체크 (debounced) + React.useEffect(() => { + if (!isRegularEvaluationRole) { + setRegularEvaluationExists(false) + return + } + + const timeoutId = setTimeout(async () => { + setIsCheckingRegularEvaluation(true) + try { + const exists = await checkRegularEvaluationRoleExists() + setRegularEvaluationExists(exists) + } catch (error) { + console.error("정기평가 role 체크 실패:", error) + } finally { + setIsCheckingRegularEvaluation(false) + } + }, 500) // 500ms debounce + + return () => clearTimeout(timeoutId) + }, [isRegularEvaluationRole, watchedName]) + async function onSubmit(data: CreateRoleSchema) { startAddTransition(async () => { const result = await createRole(data) @@ -82,19 +110,21 @@ export function AddRoleDialog() { } form.reset() setOpen(false) - toast.success("Role added") + setRegularEvaluationExists(false) + toast.success("Role이 성공적으로 추가되었습니다") }) } function handleDialogOpenChange(nextOpen: boolean) { if (!nextOpen) { form.reset() + setRegularEvaluationExists(false) } setOpen(nextOpen) } - // domain이 partners일 경우 companyId 입력 필드 보이게 const selectedDomain = form.watch("domain") + const canSubmit = !isRegularEvaluationRole || !regularEvaluationExists return ( <Dialog open={open} onOpenChange={handleDialogOpenChange}> @@ -129,6 +159,35 @@ export function AddRoleDialog() { /> </FormControl> <FormMessage /> + + {/* 정기평가 관련 경고 메시지 */} + {isRegularEvaluationRole && ( + <div className="mt-2"> + {isCheckingRegularEvaluation ? ( + <Alert> + <Loader className="h-4 w-4 animate-spin" /> + <AlertDescription> + 정기평가 role 존재 여부를 확인하고 있습니다... + </AlertDescription> + </Alert> + ) : regularEvaluationExists ? ( + <Alert variant="destructive"> + <AlertTriangle className="h-4 w-4" /> + <AlertDescription> + <strong>경고:</strong> "정기평가"가 포함된 role이 이미 존재합니다. + 정기평가 role은 시스템에서 하나만 허용됩니다. + </AlertDescription> + </Alert> + ) : ( + <Alert> + <Check className="h-4 w-4" /> + <AlertDescription> + 정기평가 role을 생성할 수 있습니다. + </AlertDescription> + </Alert> + )} + </div> + )} </FormItem> )} /> @@ -161,7 +220,6 @@ export function AddRoleDialog() { <FormLabel>Domain</FormLabel> <FormControl> <Select - // domain이 바뀔 때마다 form state에도 반영 onValueChange={field.onChange} value={field.value} > @@ -184,96 +242,85 @@ export function AddRoleDialog() { {/* 4) companyId => domain이 partners인 경우만 노출 */} {selectedDomain === "partners" && ( - <FormField - control={form.control} - name="companyId" - render={({ field }) => { - // 현재 선택된 회사 ID (number) → 문자열 - const valueString = field.value ? String(field.value) : "" - + <FormField + control={form.control} + name="companyId" + render={({ field }) => { + 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) - // 현재 선택된 회사 - 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" + return ( + <FormItem> + <FormLabel>Company</FormLabel> + <FormControl> + <Popover + open={popoverOpen} + onOpenChange={setPopoverOpen} + modal={true} > - {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"> - <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) + <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> - }} - > - {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> - ) - }} - /> + <PopoverContent className="w-full p-0"> + <Command> + <CommandInput + placeholder="Search company..." + className="h-9" + /> + <CommandList> + <CommandEmpty>No company found.</CommandEmpty> + <CommandGroup> + {companies.map((comp) => { + 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={() => { + 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> + ) + }} + /> )} </div> @@ -289,7 +336,12 @@ export function AddRoleDialog() { </Button> <Button type="submit" - disabled={form.formState.isSubmitting || isAddPending} + disabled={ + form.formState.isSubmitting || + isAddPending || + !canSubmit || + isCheckingRegularEvaluation + } > {isAddPending && ( <Loader |
