summaryrefslogtreecommitdiff
path: root/lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx
diff options
context:
space:
mode:
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.tsx181
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