From 4eb7532f822c821fb6b69bf103bd075fefba769b Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Tue, 15 Jul 2025 10:07:09 +0000
Subject: (대표님) 20250715 협력사 정기평가, spreadJS, roles 서비스에 함수 추가
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 lib/roles/table/add-role-dialog.tsx | 248 ++++++++++++++++++++++--------------
 1 file changed, 150 insertions(+), 98 deletions(-)

(limited to 'lib/roles/table/add-role-dialog.tsx')

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
-- 
cgit v1.2.3

