diff options
Diffstat (limited to 'lib/basic-contract/status-detail')
3 files changed, 157 insertions, 8 deletions
diff --git a/lib/basic-contract/status-detail/basic-contract-detail-table-toolbar-actions.tsx b/lib/basic-contract/status-detail/basic-contract-detail-table-toolbar-actions.tsx index 3e965fac..daa410f0 100644 --- a/lib/basic-contract/status-detail/basic-contract-detail-table-toolbar-actions.tsx +++ b/lib/basic-contract/status-detail/basic-contract-detail-table-toolbar-actions.tsx @@ -2,7 +2,7 @@ import * as React from "react" import { type Table } from "@tanstack/react-table" -import { Download, FileDown, Mail, CheckCircle, AlertTriangle, Send, Check, FileSignature, FileText, ExternalLink, Globe } from "lucide-react" +import { Download, FileDown, Mail, CheckCircle, AlertTriangle, Send, Check, FileSignature, FileText, ExternalLink, Globe, Flag } from "lucide-react" import { exportTableToExcel } from "@/lib/export" import { downloadFile } from "@/lib/file-download" @@ -21,13 +21,21 @@ import { Badge } from "@/components/ui/badge" import { prepareFinalApprovalAction, quickFinalApprovalAction, resendContractsAction, updateLegalReviewStatusFromSSLVW } from "../service" import { BasicContractSignDialog } from "../vendor-table/basic-contract-sign-dialog" import { SSLVWPurInqReqDialog } from "@/components/common/legal/sslvw-pur-inq-req-dialog" +import { requestRedFlagResolution } from "@/lib/compliance/red-flag-resolution" interface BasicContractDetailTableToolbarActionsProps { table: Table<BasicContractView> gtcData?: Record<number, { gtcDocumentId: number | null; hasComments: boolean }> + redFlagData?: Record<number, boolean> + isComplianceTemplate?: boolean } -export function BasicContractDetailTableToolbarActions({ table, gtcData = {} }: BasicContractDetailTableToolbarActionsProps) { +export function BasicContractDetailTableToolbarActions({ + table, + gtcData = {}, + redFlagData = {}, + isComplianceTemplate = false +}: BasicContractDetailTableToolbarActionsProps) { // 선택된 행들 가져오기 const selectedRows = table.getSelectedRowModel().rows const hasSelectedRows = selectedRows.length > 0 @@ -383,6 +391,43 @@ export function BasicContractDetailTableToolbarActions({ table, gtcData = {} }: } } + // RED FLAG 해소요청 가능 여부 + const canRequestRedFlagResolution = hasSelectedRows && isComplianceTemplate && selectedRows.some(row => { + const contract = row.original + return redFlagData[contract.id] === true + }) + + // RED FLAG 해소요청 가능한 계약서들 + const redFlagResolutionContracts = selectedRows + .map(row => row.original) + .filter(contract => redFlagData[contract.id] === true) + + // RED FLAG 해소요청 + const handleRequestRedFlagResolution = async () => { + if (!canRequestRedFlagResolution) { + toast.error("RED FLAG가 있는 계약서를 선택해주세요") + return + } + + setLoading(true) + try { + const contractIds = redFlagResolutionContracts.map(c => c.id) + const result = await requestRedFlagResolution(contractIds) + + if (result.success) { + toast.success(result.message) + table.toggleAllPageRowsSelected(false) + } else { + toast.error(result.message) + } + } catch (error) { + console.error("RED FLAG 해소요청 오류:", error) + toast.error("RED FLAG 해소요청 중 오류가 발생했습니다") + } finally { + setLoading(false) + } + } + // 법무검토 요청 링크 목록 const legalReviewLinks = [ { @@ -445,6 +490,28 @@ export function BasicContractDetailTableToolbarActions({ table, gtcData = {} }: </span> </Button> + {/* RED FLAG 해소요청 버튼 (준법서약 템플릿만) */} + {isComplianceTemplate && ( + <Button + variant="outline" + size="sm" + onClick={handleRequestRedFlagResolution} + disabled={!canRequestRedFlagResolution || loading} + className="gap-2" + title={!hasSelectedRows + ? "계약서를 선택해주세요" + : !canRequestRedFlagResolution + ? "RED FLAG가 있는 계약서를 선택해주세요" + : `${redFlagResolutionContracts.length}건 RED FLAG 해소요청` + } + > + <Flag className="size-4" aria-hidden="true" /> + <span className="hidden sm:inline"> + RED FLAG 해소요청 {hasSelectedRows ? `(${redFlagResolutionContracts.length})` : ''} + </span> + </Button> + )} + {/* 재요청 버튼 */} <Button variant="outline" diff --git a/lib/basic-contract/status-detail/basic-contracts-detail-columns.tsx b/lib/basic-contract/status-detail/basic-contracts-detail-columns.tsx index c872aede..b2c811fd 100644 --- a/lib/basic-contract/status-detail/basic-contracts-detail-columns.tsx +++ b/lib/basic-contract/status-detail/basic-contracts-detail-columns.tsx @@ -38,6 +38,8 @@ interface GetColumnsProps { isLoadingGtcData: boolean redFlagData: Record<number, boolean> isLoadingRedFlagData: boolean + redFlagResolutionData: Record<number, { resolved: boolean; resolvedAt: Date | null }> + isLoadingRedFlagResolutionData: boolean isComplianceTemplate: boolean router: NextRouter; } @@ -61,6 +63,8 @@ export function getDetailColumns({ isLoadingGtcData, redFlagData, isLoadingRedFlagData, + redFlagResolutionData, + isLoadingRedFlagResolutionData, isComplianceTemplate, router }: GetColumnsProps): ColumnDef<BasicContractView>[] { @@ -223,6 +227,44 @@ export function getDetailColumns({ enableHiding: false, } + // Red Flag 해제 컬럼 (준법서약 템플릿만) + const redFlagResolutionColumn: ColumnDef<BasicContractView> = { + id: "redFlagResolution", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="Red Flag 해제" /> + ), + cell: ({ row }) => { + const contract = row.original; + const contractId = contract.id; + + // 로딩 중이면 로딩 표시 + if (isLoadingRedFlagResolutionData) { + return <Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />; + } + + const resolution = redFlagResolutionData[contractId]; + + if (resolution?.resolved && resolution.resolvedAt) { + return ( + <div className="text-sm"> + <Badge variant="default" className="font-medium bg-green-600"> + 해제됨 + </Badge> + <div className="text-xs text-gray-500 mt-1"> + {formatDateTime(resolution.resolvedAt, "KR")} + </div> + </div> + ); + } + + return ( + <div className="text-sm text-gray-400">-</div> + ); + }, + minSize: 140, + enableHiding: false, + } + // 기본 컬럼 배열 const baseColumns: ColumnDef<BasicContractView>[] = [ selectColumn, @@ -421,8 +463,9 @@ export function getDetailColumns({ ), cell: ({ row }) => { const status = row.getValue("legalReviewStatus") as string | null - const requestedDate = row.getValue("legalReviewRequestedAt") as Date | null - const completedDate = row.getValue("legalReviewCompletedAt") as Date | null + const contract = row.original + const requestedDate = contract.legalReviewRequestedAt as Date | null + const completedDate = contract.legalReviewCompletedAt as Date | null // 법무검토 상태 우선, 없으면 기존 로직으로 판단 if (status) { @@ -554,7 +597,7 @@ export function getDetailColumns({ actionsColumn, ] - // 준법서약 템플릿인 경우 Red Flag 컬럼을 법무검토 상태 뒤에 추가 + // 준법서약 템플릿인 경우 Red Flag 컬럼과 해제 컬럼을 법무검토 상태 뒤에 추가 if (isComplianceTemplate) { const legalReviewStatusIndex = baseColumns.findIndex((col) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -562,7 +605,7 @@ export function getDetailColumns({ }) if (legalReviewStatusIndex !== -1) { - baseColumns.splice(legalReviewStatusIndex + 1, 0, redFlagColumn) + baseColumns.splice(legalReviewStatusIndex + 1, 0, redFlagColumn, redFlagResolutionColumn) } } diff --git a/lib/basic-contract/status-detail/basic-contracts-detail-table.tsx b/lib/basic-contract/status-detail/basic-contracts-detail-table.tsx index 93853560..2d747c85 100644 --- a/lib/basic-contract/status-detail/basic-contracts-detail-table.tsx +++ b/lib/basic-contract/status-detail/basic-contracts-detail-table.tsx @@ -12,6 +12,7 @@ import { getDetailColumns } from "./basic-contracts-detail-columns" import { getBasicContractsByTemplateId } from "@/lib/basic-contract/service" import { checkGTCCommentsForContracts } from "@/lib/basic-contract/actions/check-gtc-comments" import { checkRedFlagsForContracts } from "@/lib/basic-contract/actions/check-red-flags" +import { checkRedFlagResolutionForContracts } from "@/lib/basic-contract/actions/check-red-flag-resolution" import { BasicContractView } from "@/db/schema" import { BasicContractDetailTableToolbarActions } from "./basic-contract-detail-table-toolbar-actions" import { toast } from "sonner" @@ -37,6 +38,10 @@ export function BasicContractsDetailTable({ templateId, promises }: BasicContrac // Red Flag data 상태 관리 const [redFlagData, setRedFlagData] = React.useState<Record<number, boolean>>({}) const [isLoadingRedFlagData, setIsLoadingRedFlagData] = React.useState(false) + + // Red Flag 해제 data 상태 관리 + const [redFlagResolutionData, setRedFlagResolutionData] = React.useState<Record<number, { resolved: boolean; resolvedAt: Date | null }>>({}) + const [isLoadingRedFlagResolutionData, setIsLoadingRedFlagResolutionData] = React.useState(false) const [{ data, pageCount }] = React.use(promises) const router = useRouter() @@ -98,6 +103,33 @@ export function BasicContractsDetailTable({ templateId, promises }: BasicContrac loadRedFlagData(); }, [data]); + // Red Flag 해제 data 로딩 + React.useEffect(() => { + const loadRedFlagResolutionData = async () => { + if (!data || data.length === 0) return; + + // 준법서약 템플릿이 있는지 확인 + const hasComplianceTemplates = data.some(contract => + contract.templateName?.includes('준법') + ); + + if (!hasComplianceTemplates) return; + + setIsLoadingRedFlagResolutionData(true); + try { + const resolutionResults = await checkRedFlagResolutionForContracts(data); + setRedFlagResolutionData(resolutionResults); + } catch (error) { + console.error('Error checking Red Flag Resolution data:', error); + toast.error("Red Flag 해제 데이터를 불러오는데 실패했습니다."); + } finally { + setIsLoadingRedFlagResolutionData(false); + } + }; + + loadRedFlagResolutionData(); + }, [data]); + // 준법서약 템플릿인지 확인 const isComplianceTemplate = React.useMemo(() => { if (!data || data.length === 0) return false; @@ -111,10 +143,12 @@ export function BasicContractsDetailTable({ templateId, promises }: BasicContrac isLoadingGtcData, redFlagData, isLoadingRedFlagData, + redFlagResolutionData, + isLoadingRedFlagResolutionData, isComplianceTemplate, router }), - [setRowAction, gtcData, isLoadingGtcData, redFlagData, isLoadingRedFlagData, isComplianceTemplate, router] + [setRowAction, gtcData, isLoadingGtcData, redFlagData, isLoadingRedFlagData, redFlagResolutionData, isLoadingRedFlagResolutionData, isComplianceTemplate, router] ) const advancedFilterFields: DataTableAdvancedFilterField<BasicContractView>[] = [ @@ -192,7 +226,12 @@ export function BasicContractsDetailTable({ templateId, promises }: BasicContrac table={table} filterFields={advancedFilterFields} > - <BasicContractDetailTableToolbarActions table={table} gtcData={gtcData} /> + <BasicContractDetailTableToolbarActions + table={table} + gtcData={gtcData} + redFlagData={redFlagData} + isComplianceTemplate={isComplianceTemplate} + /> </DataTableAdvancedToolbar> </DataTable> ) |
