diff --git a/app/components/RuleManager/RuleManager.tsx b/app/components/RuleManager/RuleManager.tsx index 5c28992..ed4fdf0 100644 --- a/app/components/RuleManager/RuleManager.tsx +++ b/app/components/RuleManager/RuleManager.tsx @@ -1,5 +1,5 @@ "use client"; -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useCallback } from "react"; import dynamic from "next/dynamic"; import { Flex, Spin, message } from "antd"; import { Simulation, DecisionGraphType } from "@gorules/jdm-editor"; @@ -66,15 +66,15 @@ export default function RuleManager({ } }; - const updateScenarios = async () => { + const updateScenarios = useCallback(async () => { const updatedScenarios: Scenario[] = await getScenariosByFilename(jsonFile); setScenarios(updatedScenarios); - }; + }, [jsonFile]); useEffect(() => { setRuleContent(initialRuleContent); updateScenarios(); - }, [initialRuleContent]); + }, [initialRuleContent, updateScenarios]); useEffect(() => { const canBeSchemaMapped = () => { diff --git a/app/components/RuleViewerEditor/RuleViewerEditor.tsx b/app/components/RuleViewerEditor/RuleViewerEditor.tsx index 83d80d5..f0858a1 100644 --- a/app/components/RuleViewerEditor/RuleViewerEditor.tsx +++ b/app/components/RuleViewerEditor/RuleViewerEditor.tsx @@ -1,5 +1,5 @@ "use client"; -import { useState, useMemo, useEffect, useRef } from "react"; +import { useState, useMemo, useEffect, useRef, useCallback } from "react"; import type { ReactFlowInstance } from "reactflow"; import "@gorules/jdm-editor/dist/style.css"; import { Spin, Modal } from "antd"; @@ -102,6 +102,59 @@ export default function RuleViewerEditor({ } }; + const handleFileUpload = useCallback( + async (_event: any, uploadedContent: { tests?: any[] }) => { + if (!uploadedContent?.tests) return; + + try { + const [existingScenarios, scenarios] = await Promise.all([ + getScenariosByFilename(jsonFilename), + Promise.resolve(convertTestsToScenarios(uploadedContent.tests)), + ]); + + const existingTitles: Set = new Set(existingScenarios.map((scenario: Scenario) => scenario.title)); + const newScenarios = scenarios.filter((scenario) => !existingTitles.has(scenario.title)); + + if (newScenarios.length > 0) { + let selectedScenarios: Scenario[] = []; + + Modal.confirm({ + title: "Import Scenarios", + width: 600, + maskClosable: false, + closable: false, + centered: true, + content: ( + { + selectedScenarios = selected; + }} + /> + ), + okText: "Import Selected", + cancelText: "Cancel", + onOk: async () => { + if (selectedScenarios.length > 0) { + const ruleId = getRuleIdFromPath(); + await Promise.all( + selectedScenarios.map((scenario) => + createScenario({ ...scenario, ruleID: ruleId, filepath: jsonFilename }) + ) + ).then(() => { + updateScenarios(); + }); + } + }, + }); + } + } catch (error) { + console.error("Failed to process scenarios:", error); + } + }, + [jsonFilename, updateScenarios] + ); + useEffect(() => { const handleFileSelect = (event: any) => { if ( @@ -133,7 +186,7 @@ export default function RuleViewerEditor({ return () => { document.removeEventListener("change", handleFileSelect, true); }; - }, [decisionGraphRef, ruleContent]); + }, [decisionGraphRef, handleFileUpload]); useEffect(() => { const clickHandler = (event: any) => { @@ -153,56 +206,6 @@ export default function RuleViewerEditor({ return match?.[1] ?? null; }; - const handleFileUpload = async (_event: any, uploadedContent: { tests?: any[] }) => { - if (!uploadedContent?.tests) return; - - try { - const [existingScenarios, scenarios] = await Promise.all([ - getScenariosByFilename(jsonFilename), - Promise.resolve(convertTestsToScenarios(uploadedContent.tests)), - ]); - - const existingTitles: Set = new Set(existingScenarios.map((scenario: Scenario) => scenario.title)); - const newScenarios = scenarios.filter((scenario) => !existingTitles.has(scenario.title)); - - if (newScenarios.length > 0) { - let selectedScenarios: Scenario[] = []; - - Modal.confirm({ - title: "Import Scenarios", - width: 600, - maskClosable: false, - closable: false, - centered: true, - content: ( - { - selectedScenarios = selected; - }} - /> - ), - okText: "Import Selected", - cancelText: "Cancel", - onOk: async () => { - if (selectedScenarios.length > 0) { - const ruleId = getRuleIdFromPath(); - await Promise.all( - selectedScenarios.map((scenario) => - createScenario({ ...scenario, ruleID: ruleId, filepath: jsonFilename }) - ) - ).then(() => { - updateScenarios(); - }); - } - }, - }); - } - } catch (error) { - console.error("Failed to process scenarios:", error); - } - }; - const additionalComponents: NodeSpecification[] = useMemo( () => [ // This is to add the decision node - note that this may be added to the DecisionGraph library eventually diff --git a/app/components/ScenariosManager/ScenariosManager.tsx b/app/components/ScenariosManager/ScenariosManager.tsx index 25680aa..7a8a250 100644 --- a/app/components/ScenariosManager/ScenariosManager.tsx +++ b/app/components/ScenariosManager/ScenariosManager.tsx @@ -1,4 +1,4 @@ -import React, { useState, useMemo } from "react"; +import React, { useState, useMemo, useCallback } from "react"; import { Flex, Button, Tabs } from "antd"; import type { TabsProps } from "antd"; import { DecisionGraphType } from "@gorules/jdm-editor"; @@ -63,88 +63,91 @@ export default function ScenariosManager({ }); }; - const handleReset = (callback?: () => void) => { - setSimulationContext({}); - setScenarioName(""); - const ruleMapInputs = createRuleMap(rulemap?.inputs); - setSimulationContext(ruleMapInputs); - setResetTrigger((prev) => !prev); - if (callback) callback(); - }; - - const scenariosTab = scenarios && rulemap && ( - - - + const handleReset = useCallback( + (callback?: () => void) => { + setSimulationContext({}); + setScenarioName(""); + const ruleMapInputs = createRuleMap(rulemap?.inputs); + setSimulationContext(ruleMapInputs); + setResetTrigger((prev) => !prev); + if (callback) callback(); + }, + [createRuleMap, rulemap?.inputs, setSimulationContext, setScenarioName, setResetTrigger] ); - const inputsTab = scenarios && rulemap && ruleId && ( - - - - - ); + const filteredItems = useMemo(() => { + const scenariosTab = scenarios && rulemap && ( + + + + ); - const resultsTab = ( - - - - ); + const inputsTab = scenarios && rulemap && ruleId && ( + + + + + ); - const csvTab = ( - - - - ); + const resultsTab = ( + + + + ); - const isolationTestTab = ( - - - - - ); + const csvTab = ( + + + + ); + + const isolationTestTab = ( + + + + + ); - const filteredItems = useMemo(() => { const items: TabsProps["items"] = [ { key: ScenariosManagerTabs.ScenariosTab, @@ -179,7 +182,26 @@ export default function ScenariosManager({ ]; return showAllScenarioTabs ? items : items.filter((item) => !item.disabled); - }, [showAllScenarioTabs, scenariosTab, inputsTab, resultsTab, csvTab, isolationTestTab]); + }, [ + showAllScenarioTabs, + scenarios, + rulemap, + ruleId, + jsonFile, + setSimulationContext, + resultsOfSimulation, + runSimulation, + isEditing, + setActiveTabKey, + setResetTrigger, + setScenarioName, + ruleContent, + simulationContext, + resetTrigger, + handleReset, + scenarioName, + setScenarios, + ]); return (