From 023b6615a0ef408277d998bbb5629ba50e84c933 Mon Sep 17 00:00:00 2001
From: deep1401 <gandhi0869@gmail.com>
Date: Tue, 25 Feb 2025 14:19:54 -0800
Subject: [PATCH] Finally fixed editing of the widget and also moved widget to
 a separate file

---
 .../Experiment/DynamicPluginForm.tsx          | 250 ++++++++++--------
 .../Widgets/CustomEvaluationWidget.tsx        | 159 +++++++++++
 2 files changed, 306 insertions(+), 103 deletions(-)
 create mode 100644 src/renderer/components/Experiment/Widgets/CustomEvaluationWidget.tsx

diff --git a/src/renderer/components/Experiment/DynamicPluginForm.tsx b/src/renderer/components/Experiment/DynamicPluginForm.tsx
index 5876bd07..8000699d 100644
--- a/src/renderer/components/Experiment/DynamicPluginForm.tsx
+++ b/src/renderer/components/Experiment/DynamicPluginForm.tsx
@@ -23,11 +23,10 @@ import {
   Stack,
   Option,
   Autocomplete,
-  Button,
-  Textarea,
 } from '@mui/joy';
 import { useMemo } from 'react';
 import ModelProviderWidget from 'renderer/components/Experiment/Widgets/ModelProviderWidget';
+import CustomEvaluationWidget from './Widgets/CustomEvaluationWidget';
 
 import {
   RegistryWidgetsType,
@@ -462,107 +461,152 @@ function CustomAutocompleteWidget<T = any, S extends StrictRJSFSchema = RJSFSche
   );
 }
 
-type EvaluationField = {
-  name: string;
-  expression: string;
-  return_type: 'boolean' | 'number';
-};
-
-const CustomEvaluationWidget = (props: WidgetProps<any>) => {
-  const { id, value, onChange, disabled, readonly } = props;
-  // If a value exists and is an array, use it to initialize the state.
-  const [evalMetrics, setEvalMetrics] = React.useState<EvaluationField[]>(Array.isArray(value) ? value : []);
-
-  React.useEffect(() => {
-      onChange(evalMetrics);
-  }, [evalMetrics]);
-
-  const handleAddField = () => {
-      setEvalMetrics([
-          ...evalMetrics,
-          { name: '', expression: '', return_type: 'boolean' }
-      ]);
-  };
-
-  const handleFieldChange = (
-      index: number,
-      field: keyof EvaluationField,
-      newValue: string
-  ) => {
-      const updated = evalMetrics.map((evaluation, i) =>
-          i === index ? { ...evaluation, [field]: newValue } : evaluation
-      );
-      setEvalMetrics(updated);
-  };
-
-  const handleRemoveField = (index: number) => {
-      const updated = evalMetrics.filter((_, i) => i !== index);
-      setEvalMetrics(updated);
-  };
-
-  return (
-      <div id={id}>
-          {evalMetrics.map((evaluation, index) => (
-              <div
-                  key={index}
-                  style={{
-                      marginBottom: '1rem',
-                      border: '1px solid #ccc',
-                      padding: '0.5rem'
-                  }}
-              >
-                  <Input
-                      placeholder="Evaluation Name"
-                      value={evaluation.name}
-                      onChange={(e) =>
-                          handleFieldChange(index, 'name', e.target.value)
-                      }
-                      disabled={disabled || readonly}
-                      style={{ marginBottom: '0.5rem' }}
-                  />
-                  <Textarea
-                      placeholder="Regular Expression"
-                      value={evaluation.expression}
-                      onChange={(e) =>
-                          handleFieldChange(index, 'expression', e.target.value)
-                      }
-                      disabled={disabled || readonly}
-                      style={{ marginBottom: '0.5rem' }}
-                  />
-                  <Select
-                      placeholder="Output Type"
-                      value={evaluation.return_type}
-                      onChange={(e, newValue) =>
-                          handleFieldChange(index, 'return_type', newValue as string)
-                      }
-                      disabled={disabled || readonly}
-                      style={{ marginBottom: '0.5rem' }}
-                  >
-                      <Option value="boolean">Boolean</Option>
-                      <Option value="number">Number</Option>
-                  </Select>
-                  <Button
-                      onClick={() => handleRemoveField(index)}
-                      disabled={disabled || readonly}
-                      size="sm"
-                      variant="outlined"
-                  >
-                      Remove Field
-                  </Button>
-              </div>
-          ))}
-          <Button
-              onClick={handleAddField}
-              disabled={disabled || readonly}
-              variant="solid"
-          >
-              Add Field
-          </Button>
-          {/* Hidden input to capture the JSON result on form submission */}
-          <input type="hidden" id={id} name={id} value={JSON.stringify(evalMetrics)} />
-      </div>
-  );
-};
+// type EvaluationField = {
+//   name: string;
+//   expression: string;
+//   return_type: 'boolean' | 'number';
+// };
+
+// const CustomEvaluationWidget = (props: WidgetProps<any>) => {
+//   const { id, value, onChange, disabled, readonly } = props;
+//   const valueSent = value;
+//   console.log("Value", valueSent);
+
+
+//   const parseValue = (val: any): EvaluationField[] => {
+//     if (Array.isArray(val)) {
+//       if (val.every(item => typeof item === "string")) {
+//         // If every element is a string: join them and parse the result.
+//         try {
+//           const joined = val.join(',');
+//           console.log("Joined", joined);
+//           console.log("TYPE", typeof joined);
+//           const parsed = JSON.parse(joined);
+//           console.log("PARSED HERE", parsed);
+//           return Array.isArray(parsed) ? parsed : [];
+//         } catch (err) {
+//           console.error("Error parsing evaluation widget value:", err);
+//           return [];
+//         }
+//       } else {
+//         // If not all elements are strings, assume it's already an array of EvaluationField.
+//         return val;
+//       }
+//     } else if (typeof val === "string") {
+//       try {
+//         return JSON.parse(val);
+//       } catch (err) {
+//         console.error("Error parsing evaluation widget value string:", err);
+//         return [];
+//       }
+//     }
+//     return [];
+//   };
+
+//   // const parsed = parseValue(value);
+
+//   // Initialize state by parsing the incoming value
+//   const [evalMetrics, setEvalMetrics] = React.useState<EvaluationField[]>(parseValue(valueSent));
+
+//   // Update state if a new default value is provided
+//   React.useEffect(() => {
+//     const parsed = parseValue(valueSent);
+//     if (JSON.stringify(parsed) !== JSON.stringify(evalMetrics)) {
+//       setEvalMetrics(parsed);
+//     }
+//   }, [value]);
+
+//   // Propagate state changes upstream.
+//   React.useEffect(() => {
+//     onChange(evalMetrics);
+//   }, [evalMetrics]);
+
+//   const handleAddField = () => {
+//     setEvalMetrics([
+//       ...evalMetrics,
+//       { name: '', expression: '', return_type: 'boolean' }
+//     ]);
+//   };
+
+//   const handleFieldChange = (
+//     index: number,
+//     field: keyof EvaluationField,
+//     newValue: string
+//   ) => {
+//     const updated = evalMetrics.map((evaluation, i) =>
+//       i === index ? { ...evaluation, [field]: newValue } : evaluation
+//     );
+//     setEvalMetrics(updated);
+//   };
+
+//   const handleRemoveField = (index: number) => {
+//     const updated = evalMetrics.filter((_, i) => i !== index);
+//     setEvalMetrics(updated);
+//   };
+
+//   return (
+//     <div id={id}>
+//       {evalMetrics.map((evaluation, index) => (
+//         <div
+//           key={index}
+//           style={{
+//             marginBottom: '1rem',
+//             border: '1px solid #ccc',
+//             padding: '0.5rem'
+//           }}
+//         >
+//           <Input
+//             placeholder="Evaluation Name"
+//             value={evaluation.name}
+//             onChange={(e) =>
+//               handleFieldChange(index, 'name', e.target.value)
+//             }
+//             disabled={disabled || readonly}
+//             style={{ marginBottom: '0.5rem' }}
+//           />
+//           <Textarea
+//             placeholder="Regular Expression"
+//             value={evaluation.expression}
+//             onChange={(e) =>
+//               handleFieldChange(index, 'expression', e.target.value)
+//             }
+//             disabled={disabled || readonly}
+//             style={{ marginBottom: '0.5rem' }}
+//           />
+//           <Select
+//             placeholder="Output Type"
+//             value={evaluation.return_type}
+//             onChange={(e, newValue) =>
+//               handleFieldChange(index, 'return_type', newValue as string)
+//             }
+//             disabled={disabled || readonly}
+//             style={{ marginBottom: '0.5rem' }}
+//           >
+//             <Option value="boolean">Boolean</Option>
+//             <Option value="number">Number</Option>
+//           </Select>
+//           <Button
+//             onClick={() => handleRemoveField(index)}
+//             disabled={disabled || readonly}
+//             size="sm"
+//             variant="outlined"
+//           >
+//             Remove Field
+//           </Button>
+//         </div>
+//       ))}
+//       <Button
+//         onClick={handleAddField}
+//         disabled={disabled || readonly}
+//         variant="solid"
+//       >
+//         Add Field
+//       </Button>
+//       {/* Hidden input to capture the JSON result on form submission */}
+//       <input type="hidden" id={id} name={id} value={JSON.stringify(evalMetrics)} />
+//     </div>
+//   );
+// };
 
 function CustomFieldTemplate(props: FieldTemplateProps) {
   const {
diff --git a/src/renderer/components/Experiment/Widgets/CustomEvaluationWidget.tsx b/src/renderer/components/Experiment/Widgets/CustomEvaluationWidget.tsx
new file mode 100644
index 00000000..396c57e3
--- /dev/null
+++ b/src/renderer/components/Experiment/Widgets/CustomEvaluationWidget.tsx
@@ -0,0 +1,159 @@
+import React from 'react';
+import { WidgetProps } from '@rjsf/core';
+import {
+  Button,
+  Input,
+  Select,
+  Option,
+  Textarea
+} from '@mui/joy';
+
+type EvaluationField = {
+  name: string;
+  expression: string;
+  return_type: 'boolean' | 'number';
+};
+
+
+const CustomEvaluationWidget = (props: WidgetProps<any>) => {
+  const { id, value, onChange, disabled, readonly } = props;
+  const valueSent = value;
+  console.log("Value", valueSent);
+
+
+  const parseValue = (val: any): EvaluationField[] => {
+    if (Array.isArray(val)) {
+      if (val.every(item => typeof item === "string")) {
+        // If every element is a string: join them and parse the result.
+        try {
+          const joined = val.join(',');
+          console.log("Joined", joined);
+          console.log("TYPE", typeof joined);
+          const parsed = JSON.parse(joined);
+          console.log("PARSED HERE", parsed);
+          return Array.isArray(parsed) ? parsed : [];
+        } catch (err) {
+          console.error("Error parsing evaluation widget value:", err);
+          return [];
+        }
+      } else {
+        // If not all elements are strings, assume it's already an array of EvaluationField.
+        return val;
+      }
+    } else if (typeof val === "string") {
+      try {
+        return JSON.parse(val);
+      } catch (err) {
+        console.error("Error parsing evaluation widget value string:", err);
+        return [];
+      }
+    }
+    return [];
+  };
+
+  // const parsed = parseValue(value);
+
+  // Initialize state by parsing the incoming value
+  const [evalMetrics, setEvalMetrics] = React.useState<EvaluationField[]>(parseValue(valueSent));
+
+  // Update state if a new default value is provided
+  React.useEffect(() => {
+    const parsed = parseValue(valueSent);
+    if (JSON.stringify(parsed) !== JSON.stringify(evalMetrics)) {
+      setEvalMetrics(parsed);
+    }
+  }, [value]);
+
+  // Propagate state changes upstream.
+  React.useEffect(() => {
+    onChange(evalMetrics);
+  }, [evalMetrics]);
+
+  const handleAddField = () => {
+    setEvalMetrics([
+      ...evalMetrics,
+      { name: '', expression: '', return_type: 'boolean' }
+    ]);
+  };
+
+  const handleFieldChange = (
+    index: number,
+    field: keyof EvaluationField,
+    newValue: string
+  ) => {
+    const updated = evalMetrics.map((evaluation, i) =>
+      i === index ? { ...evaluation, [field]: newValue } : evaluation
+    );
+    setEvalMetrics(updated);
+  };
+
+  const handleRemoveField = (index: number) => {
+    const updated = evalMetrics.filter((_, i) => i !== index);
+    setEvalMetrics(updated);
+  };
+
+  return (
+    <div id={id}>
+      {evalMetrics.map((evaluation, index) => (
+        <div
+          key={index}
+          style={{
+            marginBottom: '1rem',
+            border: '1px solid #ccc',
+            padding: '0.5rem'
+          }}
+        >
+          <Input
+            placeholder="Evaluation Name"
+            value={evaluation.name}
+            onChange={(e) =>
+              handleFieldChange(index, 'name', e.target.value)
+            }
+            disabled={disabled || readonly}
+            style={{ marginBottom: '0.5rem' }}
+          />
+          <Textarea
+            placeholder="Regular Expression"
+            value={evaluation.expression}
+            onChange={(e) =>
+              handleFieldChange(index, 'expression', e.target.value)
+            }
+            disabled={disabled || readonly}
+            style={{ marginBottom: '0.5rem' }}
+          />
+          <Select
+            placeholder="Output Type"
+            value={evaluation.return_type}
+            onChange={(e, newValue) =>
+              handleFieldChange(index, 'return_type', newValue as string)
+            }
+            disabled={disabled || readonly}
+            style={{ marginBottom: '0.5rem' }}
+          >
+            <Option value="boolean">Boolean</Option>
+            <Option value="number">Number</Option>
+          </Select>
+          <Button
+            onClick={() => handleRemoveField(index)}
+            disabled={disabled || readonly}
+            size="sm"
+            variant="outlined"
+          >
+            Remove Field
+          </Button>
+        </div>
+      ))}
+      <Button
+        onClick={handleAddField}
+        disabled={disabled || readonly}
+        variant="solid"
+      >
+        Add Field
+      </Button>
+      {/* Hidden input to capture the JSON result on form submission */}
+      <input type="hidden" id={id} name={id} value={JSON.stringify(evalMetrics)} />
+    </div>
+  );
+};
+
+export default CustomEvaluationWidget;
-- 
GitLab