diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-11-21 09:44:33 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-11-21 09:44:33 +0000 |
| commit | a2e0785c8749c4d3766ecf3b70edfb7c2fe4df20 (patch) | |
| tree | 4b03bbec838baf307b38e0c5692da8da7bde2f9b /lib/basic-contract/status-detail | |
| parent | 204fbfb126daf057a4567f64cfb7ab03a5679e82 (diff) | |
(임수민) 준법 Red Flag 해제, 코멘트 수정
Diffstat (limited to 'lib/basic-contract/status-detail')
3 files changed, 121 insertions, 15 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 daa410f0..e62a6cb7 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 @@ -414,15 +414,17 @@ export function BasicContractDetailTableToolbarActions({ 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) - } + toast.success("RED FLAG 해소요청 결재가 상신되었습니다.", { + description: `결재 ID: ${result.approvalId}`, + }) + table.toggleAllPageRowsSelected(false) } catch (error) { console.error("RED FLAG 해소요청 오류:", error) - toast.error("RED FLAG 해소요청 중 오류가 발생했습니다") + toast.error( + error instanceof Error + ? error.message + : "RED FLAG 해소요청 중 오류가 발생했습니다." + ) } finally { setLoading(false) } 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 b2c811fd..2ab39880 100644 --- a/lib/basic-contract/status-detail/basic-contracts-detail-columns.tsx +++ b/lib/basic-contract/status-detail/basic-contracts-detail-columns.tsx @@ -32,13 +32,21 @@ import { toast } from "sonner" import { useRouter } from "next/navigation" import { getComplianceResponseByBasicContractId } from "@/lib/compliance/services" -interface GetColumnsProps { +type RedFlagResolutionState = { + resolved: boolean + resolvedAt: Date | null + pendingApprovalId: string | null +} + +export interface GetColumnsProps { setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<BasicContractView> | null>> gtcData: Record<number, { gtcDocumentId: number | null; hasComments: boolean }> isLoadingGtcData: boolean + agreementCommentData: Record<number, { hasComments: boolean; commentCount: number }> + isLoadingAgreementCommentData: boolean redFlagData: Record<number, boolean> isLoadingRedFlagData: boolean - redFlagResolutionData: Record<number, { resolved: boolean; resolvedAt: Date | null }> + redFlagResolutionData: Record<number, RedFlagResolutionState> isLoadingRedFlagResolutionData: boolean isComplianceTemplate: boolean router: NextRouter; @@ -61,6 +69,8 @@ export function getDetailColumns({ setRowAction, gtcData, isLoadingGtcData, + agreementCommentData, + isLoadingAgreementCommentData, redFlagData, isLoadingRedFlagData, redFlagResolutionData, @@ -134,7 +144,7 @@ export function getDetailColumns({ } const handleResend = () => { - setRowAction({ type: "resend", row }) + setRowAction({ type: "resend", row } as DataTableRowAction<BasicContractView>) } return ( @@ -256,6 +266,19 @@ export function getDetailColumns({ </div> ); } + + if (resolution?.pendingApprovalId) { + return ( + <div className="text-sm"> + <Badge variant="secondary" className="font-medium"> + 해소요청 진행중 + </Badge> + <div className="text-xs text-gray-500 mt-1"> + 결재 ID: {resolution.pendingApprovalId.slice(-6)} + </div> + </div> + ); + } return ( <div className="text-sm text-gray-400">-</div> @@ -296,7 +319,10 @@ export function getDetailColumns({ const name = row.getValue("vendorName") as string | null const contract = row.original const isGTCTemplate = contract.templateName?.includes('GTC') + const isComplianceContract = contract.templateName?.includes('준법') const contractGtcData = gtcData[contract.id] + const complianceNegotiation = agreementCommentData[contract.id] + const isNegotiationCompleted = !!contract.negotiationCompletedAt const handleOpenGTC = (e: React.MouseEvent) => { e.stopPropagation() @@ -345,6 +371,48 @@ export function getDetailColumns({ )} </div> )} + {isComplianceContract && ( + <div className="flex items-center gap-1"> + {isLoadingAgreementCommentData ? ( + <Loader2 className="h-3 w-3 animate-spin text-gray-400" /> + ) : isNegotiationCompleted ? ( + <Badge + variant="outline" + className="text-xs bg-green-50 text-green-700 border-green-200" + > + <MessageCircle className="h-3 w-3 mr-1" /> + 협의 완료 + </Badge> + ) : complianceNegotiation?.hasComments ? ( + <Badge + variant="outline" + className="text-xs bg-orange-50 text-orange-700 border-orange-200" + title={`협의 코멘트 ${complianceNegotiation.commentCount}개`} + onClick={(event) => { + event.stopPropagation(); + if (typeof window === "undefined") return; + const params = new URLSearchParams(); + if (contract.templateId) { + params.set("templateId", contract.templateId.toString()); + } + if (contract.vendorId) { + params.set("vendorId", contract.vendorId.toString()); + } + if (contract.vendorName) { + params.set("vendorName", contract.vendorName); + } + const query = params.toString(); + const complianceUrl = `/evcp/basic-contract/compliance-comments/${contract.id}${query ? `?${query}` : ""}`; + window.open(complianceUrl, "_blank", "noopener,noreferrer"); + }} + style={{ cursor: "pointer" }} + > + <MessageCircle className="h-3 w-3 mr-1" /> + 협의 진행중 ({complianceNegotiation.commentCount}) + </Badge> + ) : null} + </div> + )} </div> ) }, 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 2d747c85..010b4713 100644 --- a/lib/basic-contract/status-detail/basic-contracts-detail-table.tsx +++ b/lib/basic-contract/status-detail/basic-contracts-detail-table.tsx @@ -11,6 +11,7 @@ import type { 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 { checkAgreementCommentsForContracts } from "@/lib/basic-contract/agreement-comments/actions" 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" @@ -35,20 +36,27 @@ export function BasicContractsDetailTable({ templateId, promises }: BasicContrac const [gtcData, setGtcData] = React.useState<Record<number, { gtcDocumentId: number | null; hasComments: boolean }>>({}) const [isLoadingGtcData, setIsLoadingGtcData] = React.useState(false) + // 협의 코멘트 상태 (준법 포함) 관리 + const [agreementCommentData, setAgreementCommentData] = React.useState<Record<number, { hasComments: boolean; commentCount: number }>>({}) + const [isLoadingAgreementCommentData, setIsLoadingAgreementCommentData] = React.useState(false) + // Red Flag data 상태 관리 const [redFlagData, setRedFlagData] = React.useState<Record<number, boolean>>({}) const [isLoadingRedFlagData, setIsLoadingRedFlagData] = React.useState(false) +type RedFlagResolutionState = { + resolved: boolean + resolvedAt: Date | null + pendingApprovalId: string | null +} + // Red Flag 해제 data 상태 관리 - const [redFlagResolutionData, setRedFlagResolutionData] = React.useState<Record<number, { resolved: boolean; resolvedAt: Date | null }>>({}) + const [redFlagResolutionData, setRedFlagResolutionData] = React.useState<Record<number, RedFlagResolutionState>>({}) const [isLoadingRedFlagResolutionData, setIsLoadingRedFlagResolutionData] = React.useState(false) const [{ data, pageCount }] = React.use(promises) const router = useRouter() - console.log(gtcData, "gtcData") - console.log(data, "data") - // GTC data 로딩 React.useEffect(() => { const loadGtcData = async () => { @@ -76,6 +84,32 @@ export function BasicContractsDetailTable({ templateId, promises }: BasicContrac loadGtcData(); }, [data]); + // 협의 코멘트 상태 로딩 (준법 포함) + React.useEffect(() => { + const loadAgreementComments = async () => { + if (!data || data.length === 0) return; + + const hasNegotiationTemplates = data.some(contract => + contract.templateName?.includes('준법') || contract.templateName?.includes('GTC') + ); + + if (!hasNegotiationTemplates) return; + + setIsLoadingAgreementCommentData(true); + try { + const results = await checkAgreementCommentsForContracts(data); + setAgreementCommentData(results); + } catch (error) { + console.error('협의 코멘트 정보를 불러오는데 실패했습니다:', error); + toast.error("협의 코멘트 정보를 불러오는데 실패했습니다."); + } finally { + setIsLoadingAgreementCommentData(false); + } + }; + + loadAgreementComments(); + }, [data]); + // Red Flag data 로딩 React.useEffect(() => { const loadRedFlagData = async () => { @@ -141,6 +175,8 @@ export function BasicContractsDetailTable({ templateId, promises }: BasicContrac setRowAction, gtcData, isLoadingGtcData, + agreementCommentData, + isLoadingAgreementCommentData, redFlagData, isLoadingRedFlagData, redFlagResolutionData, @@ -148,7 +184,7 @@ export function BasicContractsDetailTable({ templateId, promises }: BasicContrac isComplianceTemplate, router }), - [setRowAction, gtcData, isLoadingGtcData, redFlagData, isLoadingRedFlagData, redFlagResolutionData, isLoadingRedFlagResolutionData, isComplianceTemplate, router] + [setRowAction, gtcData, isLoadingGtcData, agreementCommentData, isLoadingAgreementCommentData, redFlagData, isLoadingRedFlagData, redFlagResolutionData, isLoadingRedFlagResolutionData, isComplianceTemplate, router] ) const advancedFilterFields: DataTableAdvancedFilterField<BasicContractView>[] = [ |
