summaryrefslogtreecommitdiff
path: root/lib/basic-contract
diff options
context:
space:
mode:
Diffstat (limited to 'lib/basic-contract')
-rw-r--r--lib/basic-contract/actions/check-red-flags.ts48
-rw-r--r--lib/basic-contract/status-detail/basic-contracts-detail-columns.tsx56
-rw-r--r--lib/basic-contract/status-detail/basic-contracts-detail-table.tsx45
3 files changed, 146 insertions, 3 deletions
diff --git a/lib/basic-contract/actions/check-red-flags.ts b/lib/basic-contract/actions/check-red-flags.ts
new file mode 100644
index 00000000..dd45a56a
--- /dev/null
+++ b/lib/basic-contract/actions/check-red-flags.ts
@@ -0,0 +1,48 @@
+"use server";
+
+import { BasicContractView } from "@/db/schema";
+import { getTriggeredRedFlagQuestions } from "@/lib/compliance/red-flag-notifier";
+
+/**
+ * 여러 계약서에 대한 Red Flag 발생 여부를 한 번에 확인
+ */
+export async function checkRedFlagsForContracts(
+ contracts: BasicContractView[]
+): Promise<Record<number, boolean>> {
+ const result: Record<number, boolean> = {};
+
+ // 준법서약 템플릿인 계약서만 필터링
+ const complianceContracts = contracts.filter(contract =>
+ contract.templateName?.includes('준법')
+ );
+
+ if (complianceContracts.length === 0) {
+ return result;
+ }
+
+ // 각 계약서에 대해 Red Flag 발생 여부 확인
+ const redFlagChecks = await Promise.all(
+ complianceContracts.map(async (contract) => {
+ try {
+ const triggeredFlags = await getTriggeredRedFlagQuestions(contract.id);
+ return {
+ contractId: contract.id,
+ hasRedFlag: triggeredFlags.length > 0
+ };
+ } catch (error) {
+ console.error(`Error checking red flags for contract ${contract.id}:`, error);
+ return {
+ contractId: contract.id,
+ hasRedFlag: false
+ };
+ }
+ })
+ );
+
+ // 결과를 Record 형태로 변환
+ redFlagChecks.forEach(check => {
+ result[check.contractId] = check.hasRedFlag;
+ });
+
+ return result;
+}
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 5a875541..c872aede 100644
--- a/lib/basic-contract/status-detail/basic-contracts-detail-columns.tsx
+++ b/lib/basic-contract/status-detail/basic-contracts-detail-columns.tsx
@@ -36,6 +36,9 @@ interface GetColumnsProps {
setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<BasicContractView> | null>>
gtcData: Record<number, { gtcDocumentId: number | null; hasComments: boolean }>
isLoadingGtcData: boolean
+ redFlagData: Record<number, boolean>
+ isLoadingRedFlagData: boolean
+ isComplianceTemplate: boolean
router: NextRouter;
}
@@ -56,6 +59,9 @@ export function getDetailColumns({
setRowAction,
gtcData,
isLoadingGtcData,
+ redFlagData,
+ isLoadingRedFlagData,
+ isComplianceTemplate,
router
}: GetColumnsProps): ColumnDef<BasicContractView>[] {
@@ -184,7 +190,41 @@ export function getDetailColumns({
maxSize: 80,
}
- return [
+ // Red Flag 발생여부 컬럼 (준법서약 템플릿만)
+ const redFlagColumn: ColumnDef<BasicContractView> = {
+ id: "redFlag",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="Red Flag" />
+ ),
+ cell: ({ row }) => {
+ const contract = row.original;
+ const contractId = contract.id;
+
+ // 로딩 중이면 로딩 표시
+ if (isLoadingRedFlagData) {
+ return <Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />;
+ }
+
+ const hasRedFlag = redFlagData[contractId] || false;
+
+ if (hasRedFlag) {
+ return (
+ <Badge variant="destructive" className="font-medium">
+ Red Flag
+ </Badge>
+ );
+ }
+
+ return (
+ <div className="text-sm text-gray-400">-</div>
+ );
+ },
+ minSize: 120,
+ enableHiding: false,
+ }
+
+ // 기본 컬럼 배열
+ const baseColumns: ColumnDef<BasicContractView>[] = [
selectColumn,
// 업체 코드
@@ -513,4 +553,18 @@ export function getDetailColumns({
actionsColumn,
]
+
+ // 준법서약 템플릿인 경우 Red Flag 컬럼을 법무검토 상태 뒤에 추가
+ if (isComplianceTemplate) {
+ const legalReviewStatusIndex = baseColumns.findIndex((col) => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ return (col as any).accessorKey === 'legalReviewStatus'
+ })
+
+ if (legalReviewStatusIndex !== -1) {
+ baseColumns.splice(legalReviewStatusIndex + 1, 0, redFlagColumn)
+ }
+ }
+
+ return baseColumns
} \ No newline at end of file
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 0df46066..93853560 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 { checkRedFlagsForContracts } from "@/lib/basic-contract/actions/check-red-flags"
import { BasicContractView } from "@/db/schema"
import { BasicContractDetailTableToolbarActions } from "./basic-contract-detail-table-toolbar-actions"
import { toast } from "sonner"
@@ -32,6 +33,10 @@ export function BasicContractsDetailTable({ templateId, promises }: BasicContrac
// GTC data 상태 관리
const [gtcData, setGtcData] = React.useState<Record<number, { gtcDocumentId: number | null; hasComments: boolean }>>({})
const [isLoadingGtcData, setIsLoadingGtcData] = React.useState(false)
+
+ // Red Flag data 상태 관리
+ const [redFlagData, setRedFlagData] = React.useState<Record<number, boolean>>({})
+ const [isLoadingRedFlagData, setIsLoadingRedFlagData] = React.useState(false)
const [{ data, pageCount }] = React.use(promises)
const router = useRouter()
@@ -66,14 +71,50 @@ export function BasicContractsDetailTable({ templateId, promises }: BasicContrac
loadGtcData();
}, [data]);
+ // Red Flag data 로딩
+ React.useEffect(() => {
+ const loadRedFlagData = async () => {
+ if (!data || data.length === 0) return;
+
+ // 준법서약 템플릿이 있는지 확인
+ const hasComplianceTemplates = data.some(contract =>
+ contract.templateName?.includes('준법')
+ );
+
+ if (!hasComplianceTemplates) return;
+
+ setIsLoadingRedFlagData(true);
+ try {
+ const redFlagResults = await checkRedFlagsForContracts(data);
+ setRedFlagData(redFlagResults);
+ } catch (error) {
+ console.error('Error checking Red Flag data:', error);
+ toast.error("Red Flag 데이터를 불러오는데 실패했습니다.");
+ } finally {
+ setIsLoadingRedFlagData(false);
+ }
+ };
+
+ loadRedFlagData();
+ }, [data]);
+
+ // 준법서약 템플릿인지 확인
+ const isComplianceTemplate = React.useMemo(() => {
+ if (!data || data.length === 0) return false;
+ return data.some(contract => contract.templateName?.includes('준법'));
+ }, [data]);
+
const columns = React.useMemo(
() => getDetailColumns({
setRowAction,
gtcData,
- isLoadingGtcData ,
+ isLoadingGtcData,
+ redFlagData,
+ isLoadingRedFlagData,
+ isComplianceTemplate,
router
}),
- [setRowAction, gtcData, isLoadingGtcData, router]
+ [setRowAction, gtcData, isLoadingGtcData, redFlagData, isLoadingRedFlagData, isComplianceTemplate, router]
)
const advancedFilterFields: DataTableAdvancedFilterField<BasicContractView>[] = [