/** * 조건부 표시 규칙 평가 유틸 * * [사용처] * - CanvasComponent.tsx : 캔버스 내 컴포넌트 조건부 표시 * - ReportPreviewModal.tsx : 미리보기 시 조건부 표시 * - ReportListPreviewModal.tsx : 목록 미리보기 시 조건부 표시 */ import type { ConditionalRule } from "@/types/report"; export type { ConditionalRule }; type QueryResultGetter = ( queryId: string ) => { fields: string[]; rows: Record[] } | null; /** * 단일 조건 규칙을 평가한다. * 쿼리 결과의 첫 번째 행에서 필드 값을 가져와 연산자로 비교한다. */ export function evaluateSingleRule( rule: ConditionalRule, getQueryResult: QueryResultGetter ): boolean { const queryResult = getQueryResult(rule.queryId); if (!queryResult?.rows?.length) return false; const rawValue = queryResult.rows[0][rule.field]; const fieldStr = rawValue !== null && rawValue !== undefined ? String(rawValue) : ""; const fieldNum = parseFloat(fieldStr); const compareNum = parseFloat(rule.value); switch (rule.operator) { case "eq": return fieldStr === rule.value; case "ne": return fieldStr !== rule.value; case "gt": return !isNaN(fieldNum) && !isNaN(compareNum) && fieldNum > compareNum; case "lt": return !isNaN(fieldNum) && !isNaN(compareNum) && fieldNum < compareNum; case "gte": return !isNaN(fieldNum) && !isNaN(compareNum) && fieldNum >= compareNum; case "lte": return !isNaN(fieldNum) && !isNaN(compareNum) && fieldNum <= compareNum; case "contains": return fieldStr.includes(rule.value); case "notEmpty": return fieldStr !== ""; case "empty": return fieldStr === ""; default: return false; } } /** * 다중 조건 규칙을 AND로 평가한다. * 모든 규칙이 충족되면 action에 따라 표시/숨김을 결정한다. */ export function evaluateConditionalRules( rules: ConditionalRule[] | undefined | null, getQueryResult: QueryResultGetter ): boolean { if (!rules || rules.length === 0) return true; const validRules = rules.filter( (r) => r.queryId && r.field && r.operator ); if (validRules.length === 0) return true; const action = validRules[0].action || "show"; const allMet = validRules.every((r) => evaluateSingleRule(r, getQueryResult) ); return action === "show" ? allMet : !allMet; }