summaryrefslogtreecommitdiff
path: root/lib/roles/table/add-role-dialog.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/roles/table/add-role-dialog.tsx')
-rw-r--r--lib/roles/table/add-role-dialog.tsx248
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