diff options
Diffstat (limited to 'lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx')
| -rw-r--r-- | lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx | 181 |
1 files changed, 125 insertions, 56 deletions
diff --git a/lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx b/lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx index 8bc5254c..d1c7e500 100644 --- a/lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx +++ b/lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx @@ -14,6 +14,7 @@ import { } from "lucide-react" import { toast } from "sonner" import { useRouter } from "next/navigation" +import { useSession } from "next-auth/react" import { Button } from "@/components/ui/button" import { @@ -31,6 +32,8 @@ import { } from "./evaluation-target-action-dialogs" import { EvaluationTargetWithDepartments } from "@/db/schema" import { exportTableToExcel } from "@/lib/export" +import { autoGenerateEvaluationTargets } from "../service" // 서버 액션 import +import { useAuthRole } from "@/hooks/use-auth-role" interface EvaluationTargetsTableToolbarActionsProps { table: Table<EvaluationTargetWithDepartments> @@ -47,6 +50,16 @@ export function EvaluationTargetsTableToolbarActions({ const [excludeDialogOpen, setExcludeDialogOpen] = React.useState(false) const [reviewDialogOpen, setReviewDialogOpen] = React.useState(false) const router = useRouter() + const { data: session } = useSession() + + // 권한 체크 + const { hasRole, isLoading: roleLoading } = useAuthRole() + const canManageEvaluations = hasRole('정기평가') || hasRole('admin') + + // 사용자 ID 가져오기 + const userId = React.useMemo(() => { + return session?.user?.id ? Number(session.user.id) : 1; + }, [session]); // 선택된 행들 const selectedRows = table.getFilteredSelectedRowModel().rows @@ -141,16 +154,36 @@ export function EvaluationTargetsTableToolbarActions({ const handleAutoGenerate = React.useCallback(async () => { setIsLoading(true) try { - // TODO: 발주실적에서 자동 추출 API 호출 - toast.success("평가 대상이 자동으로 생성되었습니다.") - router.refresh() + // 현재 년도를 기준으로 평가 대상 자동 생성 + const currentYear = new Date().getFullYear() + const result = await autoGenerateEvaluationTargets(currentYear, userId) + + if (result.success) { + if (result.generatedCount === 0) { + toast.info(result.message, { + description: result.skippedCount + ? `이미 존재하는 평가 대상: ${result.skippedCount}개` + : undefined + }) + } else { + toast.success(result.message, { + description: result.details + ? `해양: ${result.details.shipTargets}개, 조선: ${result.details.plantTargets}개 생성${result.details.duplicateSkipped > 0 ? `, 중복 건너뜀: ${result.details.duplicateSkipped}개` : ''}` + : undefined + }) + } + onRefresh?.() + router.refresh() + } else { + toast.error(result.error || "자동 생성 중 오류가 발생했습니다.") + } } catch (error) { console.error('Error auto generating targets:', error) toast.error("자동 생성 중 오류가 발생했습니다.") } finally { setIsLoading(false) } - }, [router]) + }, [router, onRefresh, userId]) // ---------------------------------------------------------------- // 신규 평가 대상 생성 (수동) @@ -178,33 +211,54 @@ export function EvaluationTargetsTableToolbarActions({ }) }, [table]) + // 권한이 없거나 로딩 중인 경우 내보내기 버튼만 표시 + if (roleLoading) { + return ( + <div className="flex items-center gap-2"> + <div className="flex items-center gap-1 border-l pl-2 ml-2"> + <Button + variant="outline" + size="sm" + disabled + className="gap-2" + > + <Download className="size-4 animate-spin" aria-hidden="true" /> + <span className="hidden sm:inline">로딩중...</span> + </Button> + </div> + </div> + ) + } + return ( <> <div className="flex items-center gap-2"> - {/* 신규 생성 드롭다운 */} - <DropdownMenu> - <DropdownMenuTrigger asChild> - <Button - variant="default" - size="sm" - className="gap-2" - disabled={isLoading} - > - <Plus className="size-4" aria-hidden="true" /> - <span className="hidden sm:inline">신규 생성</span> - </Button> - </DropdownMenuTrigger> - <DropdownMenuContent align="start"> - <DropdownMenuItem onClick={handleAutoGenerate} disabled={isLoading}> - <RefreshCw className="size-4 mr-2" /> - 자동 생성 (발주실적 기반) - </DropdownMenuItem> - <DropdownMenuItem onClick={handleManualCreate}> - <Plus className="size-4 mr-2" /> - 수동 생성 - </DropdownMenuItem> - </DropdownMenuContent> - </DropdownMenu> + {/* 신규 생성 드롭다운 - 정기평가 권한이 있는 경우만 표시 */} + {canManageEvaluations && ( + <DropdownMenu> + <DropdownMenuTrigger asChild> + <Button + variant="default" + size="sm" + className="gap-2" + disabled={isLoading} + > + <Plus className="size-4" aria-hidden="true" /> + <span className="hidden sm:inline">신규 생성</span> + </Button> + </DropdownMenuTrigger> + <DropdownMenuContent align="start"> + <DropdownMenuItem onClick={handleAutoGenerate} disabled={isLoading}> + <RefreshCw className={`size-4 mr-2 ${isLoading ? 'animate-spin' : ''}`} /> + 자동 생성 (발주실적 기반) + </DropdownMenuItem> + <DropdownMenuItem onClick={handleManualCreate}> + <Plus className="size-4 mr-2" /> + 수동 생성 + </DropdownMenuItem> + </DropdownMenuContent> + </DropdownMenu> + )} {/* 유틸리티 버튼들 */} <div className="flex items-center gap-1 border-l pl-2 ml-2"> @@ -219,8 +273,8 @@ export function EvaluationTargetsTableToolbarActions({ </Button> </div> - {/* 선택된 항목 액션 버튼들 */} - {hasSelection && ( + {/* 선택된 항목 액션 버튼들 - 정기평가 권한이 있는 경우만 표시 */} + {canManageEvaluations && hasSelection && ( <div className="flex items-center gap-1 border-l pl-2 ml-2"> {/* 확정 버튼 */} {selectedStats.canConfirm && ( @@ -271,37 +325,52 @@ export function EvaluationTargetsTableToolbarActions({ )} </div> )} + + {/* 권한이 없는 경우 안내 메시지 (선택사항) */} + {!canManageEvaluations && hasSelection && ( + <div className="flex items-center gap-1 border-l pl-2 ml-2"> + <div className="text-xs text-muted-foreground px-2 py-1"> + 평가 관리 권한이 필요합니다 + </div> + </div> + )} </div> - {/* 수동 생성 다이얼로그 */} - <ManualCreateEvaluationTargetDialog - open={manualCreateDialogOpen} - onOpenChange={setManualCreateDialogOpen} - /> + {/* 다이얼로그들 - 권한이 있는 경우만 렌더링 */} + {canManageEvaluations && ( + <> + {/* 수동 생성 다이얼로그 */} + <ManualCreateEvaluationTargetDialog + open={manualCreateDialogOpen} + onOpenChange={setManualCreateDialogOpen} + onSuccess={handleActionSuccess} + /> - {/* 확정 컨펌 다이얼로그 */} - <ConfirmTargetsDialog - open={confirmDialogOpen} - onOpenChange={setConfirmDialogOpen} - targets={selectedTargets} - onSuccess={handleActionSuccess} - /> + {/* 확정 컨펌 다이얼로그 */} + <ConfirmTargetsDialog + open={confirmDialogOpen} + onOpenChange={setConfirmDialogOpen} + targets={selectedTargets} + onSuccess={handleActionSuccess} + /> - {/* 제외 컨펌 다이얼로그 */} - <ExcludeTargetsDialog - open={excludeDialogOpen} - onOpenChange={setExcludeDialogOpen} - targets={selectedTargets} - onSuccess={handleActionSuccess} - /> + {/* 제외 컨펌 다이얼로그 */} + <ExcludeTargetsDialog + open={excludeDialogOpen} + onOpenChange={setExcludeDialogOpen} + targets={selectedTargets} + onSuccess={handleActionSuccess} + /> - {/* 의견 요청 다이얼로그 */} - <RequestReviewDialog - open={reviewDialogOpen} - onOpenChange={setReviewDialogOpen} - targets={selectedTargets} - onSuccess={handleActionSuccess} - /> + {/* 의견 요청 다이얼로그 */} + <RequestReviewDialog + open={reviewDialogOpen} + onOpenChange={setReviewDialogOpen} + targets={selectedTargets} + onSuccess={handleActionSuccess} + /> + </> + )} </> ) }
\ No newline at end of file |
