@openg2p/registry-widgets 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/components/PanelRenderer.d.ts.map +1 -1
  2. package/dist/components/SectionBuilder/JSONEditorPanel.d.ts +13 -0
  3. package/dist/components/SectionBuilder/JSONEditorPanel.d.ts.map +1 -0
  4. package/dist/components/SectionBuilder/PropertyEditor.d.ts +15 -0
  5. package/dist/components/SectionBuilder/PropertyEditor.d.ts.map +1 -0
  6. package/dist/components/SectionBuilder/SectionBuilder.d.ts +13 -0
  7. package/dist/components/SectionBuilder/SectionBuilder.d.ts.map +1 -0
  8. package/dist/components/SectionBuilder/SectionTree.d.ts +26 -0
  9. package/dist/components/SectionBuilder/SectionTree.d.ts.map +1 -0
  10. package/dist/components/SectionBuilder/VisualBuilderPanel.d.ts +19 -0
  11. package/dist/components/SectionBuilder/VisualBuilderPanel.d.ts.map +1 -0
  12. package/dist/components/SectionBuilder/index.d.ts +9 -0
  13. package/dist/components/SectionBuilder/index.d.ts.map +1 -0
  14. package/dist/components/SectionBuilder/schemas.d.ts +1947 -0
  15. package/dist/components/SectionBuilder/schemas.d.ts.map +1 -0
  16. package/dist/components/SectionRenderer.d.ts +4 -4
  17. package/dist/components/SectionRenderer.d.ts.map +1 -1
  18. package/dist/components/SectionsContainer.d.ts +2 -1
  19. package/dist/components/SectionsContainer.d.ts.map +1 -1
  20. package/dist/index.d.ts +6 -5
  21. package/dist/index.esm.js +558 -173
  22. package/dist/index.esm.js.map +1 -1
  23. package/dist/index.js +558 -173
  24. package/dist/index.js.map +1 -1
  25. package/dist/utils/schemaNamespace.d.ts +12 -0
  26. package/dist/utils/schemaNamespace.d.ts.map +1 -0
  27. package/dist/widgets/RadioWidget.d.ts.map +1 -1
  28. package/dist/widgets/SelectWidget.d.ts.map +1 -1
  29. package/dist/widgets/TableWidget.d.ts.map +1 -1
  30. package/package.json +11 -11
package/dist/index.esm.js CHANGED
@@ -1593,30 +1593,36 @@ const PanelRenderer = ({ panel, apiAdapter, schemaData, onValueChange, isEditMod
1593
1593
  const nestedPanels = panel.panels || [];
1594
1594
  const widgets = panel.widgets || [];
1595
1595
  // For horizontal orientation, use grid for equal-width columns
1596
- // Dynamic grid based on number of nested panels
1596
+ // Dynamic grid based on number of nested panels and their column spans
1597
1597
  // For vertical orientation, use flex column
1598
1598
  const getContainerClassAndStyle = () => {
1599
1599
  if (orientation === 'horizontal' && nestedPanels.length > 0) {
1600
- const numPanels = nestedPanels.length;
1601
- // Use predefined grid classes based on number of panels (1-5)
1600
+ // Calculate total columns needed based on panel column spans
1601
+ // Sum up all column spans, or use panel count if no spans specified
1602
+ let totalColumns = 0;
1603
+ nestedPanels.forEach(panel => {
1604
+ const columnSpan = panel['panel-column-span'] || 1;
1605
+ totalColumns += columnSpan;
1606
+ });
1607
+ // Ensure at least as many columns as panels (for panels without explicit span)
1608
+ totalColumns = Math.max(totalColumns, nestedPanels.length);
1609
+ // Use predefined grid classes for common cases (1-5)
1602
1610
  // For more than 5, use inline style
1603
1611
  // Removed gap to allow borders to show properly
1604
- console.log('numPanels', numPanels);
1605
- if (numPanels <= 5) {
1606
- const gridClasses = {
1607
- 1: 'grid grid-cols-1',
1608
- 2: 'grid grid-cols-2',
1609
- 3: 'grid grid-cols-3',
1610
- 4: 'grid grid-cols-4',
1611
- 5: 'grid grid-cols-5',
1612
+ if (totalColumns <= 5) {
1613
+ // For grid with column spans, we need to use inline styles to set minmax
1614
+ // This ensures each column is at least 200px wide
1615
+ return {
1616
+ className: 'grid',
1617
+ style: { gridTemplateColumns: `repeat(${totalColumns}, minmax(200px, 1fr))` },
1612
1618
  };
1613
- return { className: gridClasses[numPanels] || 'grid', style: {} };
1614
1619
  }
1615
1620
  else {
1616
- // For more than 5 panels, use inline style
1621
+ // For more than 5 columns, use inline style
1622
+ // Use minmax(200px, 1fr) to ensure minimum 200px per column
1617
1623
  return {
1618
1624
  className: 'grid',
1619
- style: { gridTemplateColumns: `repeat(${numPanels}, minmax(0, 1fr))` },
1625
+ style: { gridTemplateColumns: `repeat(${totalColumns}, minmax(200px, 1fr))` },
1620
1626
  };
1621
1627
  }
1622
1628
  }
@@ -1636,15 +1642,44 @@ const PanelRenderer = ({ panel, apiAdapter, schemaData, onValueChange, isEditMod
1636
1642
  }, children: [nestedPanels.map((nestedPanel, index) => {
1637
1643
  const isLastPanel = index === nestedPanels.length - 1;
1638
1644
  const isFirstPanel = index === 0;
1639
- const horizontalStyle = orientation === 'horizontal'
1640
- ? {
1641
- minWidth: '200px',
1642
- paddingRight: !isLastPanel ? '40px' : undefined,
1643
- paddingLeft: !isFirstPanel ? '40px' : undefined,
1644
- position: 'relative',
1645
+ const nestedOrientation = nestedPanel['panel-orientation'] || 'vertical';
1646
+ const columnSpan = nestedPanel['panel-column-span'];
1647
+ // Calculate style for nested panel based on orientation and column span
1648
+ const getNestedPanelStyle = () => {
1649
+ if (orientation === 'horizontal') {
1650
+ // When nested inside horizontal panel, check for column span
1651
+ const baseStyle = {
1652
+ minWidth: '200px',
1653
+ paddingRight: !isLastPanel ? '40px' : undefined,
1654
+ paddingLeft: !isFirstPanel ? '40px' : undefined,
1655
+ position: 'relative',
1656
+ };
1657
+ // If vertical panel has column span, use CSS grid-column-span
1658
+ if (nestedOrientation === 'vertical' && columnSpan && columnSpan > 1) {
1659
+ return {
1660
+ ...baseStyle,
1661
+ gridColumn: `span ${columnSpan}`,
1662
+ minWidth: 'auto', // Remove minWidth constraint when spanning columns
1663
+ };
1664
+ }
1665
+ return baseStyle;
1666
+ }
1667
+ else {
1668
+ // Vertical panel nested in vertical panel
1669
+ if (columnSpan && columnSpan > 1) {
1670
+ // If column span is specified, calculate width based on 200px per column
1671
+ const width = columnSpan * 200;
1672
+ return {
1673
+ width: `${width}px`,
1674
+ maxWidth: '100%',
1675
+ flexShrink: 0,
1676
+ };
1677
+ }
1678
+ return { width: '100%' };
1645
1679
  }
1646
- : { width: '100%' };
1647
- return (jsxRuntimeExports.jsx(React.Fragment, { children: jsxRuntimeExports.jsxs("div", { className: orientation === 'horizontal' ? 'min-w-200 relative' : 'w-full', style: horizontalStyle, children: [jsxRuntimeExports.jsx(PanelRenderer, { panel: nestedPanel, apiAdapter: apiAdapter, schemaData: schemaData, onValueChange: onValueChange, isEditMode: isEditMode }), orientation === 'horizontal' && !isLastPanel && (jsxRuntimeExports.jsx("div", { style: {
1680
+ };
1681
+ const nestedPanelStyle = getNestedPanelStyle();
1682
+ return (jsxRuntimeExports.jsx(React.Fragment, { children: jsxRuntimeExports.jsxs("div", { className: orientation === 'horizontal' ? 'min-w-200 relative' : 'w-full', style: nestedPanelStyle, "data-panel-column-span": columnSpan || undefined, children: [jsxRuntimeExports.jsx(PanelRenderer, { panel: nestedPanel, apiAdapter: apiAdapter, schemaData: schemaData, onValueChange: onValueChange, isEditMode: isEditMode }), orientation === 'horizontal' && !isLastPanel && (jsxRuntimeExports.jsx("div", { style: {
1648
1683
  position: 'absolute',
1649
1684
  right: 0,
1650
1685
  top: 0,
@@ -2246,6 +2281,104 @@ const FileInputWidget = ({ config }) => {
2246
2281
  } }, `modal-${previewFile ? (previewFile instanceof File ? previewFile.name : previewFile) : 'none'}`)] }));
2247
2282
  };
2248
2283
 
2284
+ /**
2285
+ * Namespace a data path by adding a namespace prefix
2286
+ */
2287
+ const namespaceDataPath = (dataPath, namespace) => {
2288
+ if (!dataPath)
2289
+ return dataPath;
2290
+ if (typeof dataPath === 'string') {
2291
+ // Add namespace prefix to the data path
2292
+ return `${namespace}.${dataPath}`;
2293
+ }
2294
+ // Multi-path: namespace each path
2295
+ const namespaced = {};
2296
+ for (const [key, path] of Object.entries(dataPath)) {
2297
+ namespaced[key] = `${namespace}.${path}`;
2298
+ }
2299
+ return namespaced;
2300
+ };
2301
+ /**
2302
+ * Recursively namespace widget IDs and data paths in a widget configuration
2303
+ * This ensures unique widget IDs and data paths when the same section is rendered multiple times
2304
+ */
2305
+ const namespaceWidgetConfig = (widgetConfig, namespace) => {
2306
+ const namespaced = { ...widgetConfig };
2307
+ // Namespace the widget-id
2308
+ if (namespaced['widget-id']) {
2309
+ namespaced['widget-id'] = `${namespace}__${namespaced['widget-id']}`;
2310
+ }
2311
+ // Namespace the widget-data-path to ensure values are stored separately
2312
+ if (namespaced['widget-data-path']) {
2313
+ namespaced['widget-data-path'] = namespaceDataPath(namespaced['widget-data-path'], namespace);
2314
+ }
2315
+ // Recursively namespace nested widgets (for layout widgets)
2316
+ if (namespaced.widgets && Array.isArray(namespaced.widgets)) {
2317
+ namespaced.widgets = namespaced.widgets.map((widget) => namespaceWidgetConfig(widget, namespace));
2318
+ }
2319
+ // Namespace widget-item (for array/group widgets)
2320
+ if (namespaced['widget-item']) {
2321
+ namespaced['widget-item'] = namespaceWidgetConfig(namespaced['widget-item'], namespace);
2322
+ }
2323
+ // Namespace widget-data-columns (for table widgets)
2324
+ if (namespaced['widget-data-columns'] && Array.isArray(namespaced['widget-data-columns'])) {
2325
+ namespaced['widget-data-columns'] = namespaced['widget-data-columns'].map((column) => {
2326
+ const namespacedColumn = { ...column };
2327
+ // Namespace column data paths if they exist (columns only support string paths, not multi-path)
2328
+ if (namespacedColumn['widget-data-path'] && typeof namespacedColumn['widget-data-path'] === 'string') {
2329
+ namespacedColumn['widget-data-path'] = namespaceDataPath(namespacedColumn['widget-data-path'], namespace); // Safe cast since we checked it's a string
2330
+ }
2331
+ return namespacedColumn;
2332
+ });
2333
+ }
2334
+ return namespaced;
2335
+ };
2336
+ /**
2337
+ * Recursively namespace widget IDs in a panel configuration
2338
+ */
2339
+ const namespacePanelConfig = (panel, namespace) => {
2340
+ const namespaced = { ...panel };
2341
+ // Recursively namespace nested panels
2342
+ if (namespaced.panels && Array.isArray(namespaced.panels)) {
2343
+ namespaced.panels = namespaced.panels.map((p) => namespacePanelConfig(p, namespace));
2344
+ }
2345
+ // Namespace widgets in panel
2346
+ if (namespaced.widgets && Array.isArray(namespaced.widgets)) {
2347
+ namespaced.widgets = namespaced.widgets.map((widget) => namespaceWidgetConfig(widget, namespace));
2348
+ }
2349
+ return namespaced;
2350
+ };
2351
+ /**
2352
+ * Namespace widget IDs and data paths in a section configuration
2353
+ * This ensures unique widget IDs and data paths when the same section is rendered multiple times
2354
+ * (e.g., in CRView mode showing old and new records side by side)
2355
+ *
2356
+ * @param section - Section configuration to namespace
2357
+ * @param namespace - Namespace prefix to add to widget IDs and data paths (e.g., "old", "new", "instance-1")
2358
+ * @returns Namespaced section configuration
2359
+ */
2360
+ const namespaceSectionConfig = (section, namespace) => {
2361
+ const namespaced = { ...section };
2362
+ // Namespace the section-id as well to ensure uniqueness
2363
+ if (namespaced['section-id']) {
2364
+ namespaced['section-id'] = `${namespace}__${namespaced['section-id']}`;
2365
+ }
2366
+ // Recursively namespace panels
2367
+ if (namespaced.panels && Array.isArray(namespaced.panels)) {
2368
+ namespaced.panels = namespaced.panels.map((panel) => namespacePanelConfig(panel, namespace));
2369
+ }
2370
+ // Namespace supporting documents data paths
2371
+ if (namespaced['section-supporting-documents'] && Array.isArray(namespaced['section-supporting-documents'])) {
2372
+ namespaced['section-supporting-documents'] = namespaced['section-supporting-documents'].map((doc) => ({
2373
+ ...doc,
2374
+ 'document-data-path': doc['document-data-path']
2375
+ ? `${namespace}.${doc['document-data-path']}`
2376
+ : doc['document-data-path'],
2377
+ }));
2378
+ }
2379
+ return namespaced;
2380
+ };
2381
+
2249
2382
  /**
2250
2383
  * Renders a section with its panels
2251
2384
  *
@@ -2254,7 +2387,7 @@ const FileInputWidget = ({ config }) => {
2254
2387
  * - Panels wrap when they exceed available width
2255
2388
  * - Sections can sit side-by-side if there's space
2256
2389
  */
2257
- const SectionRenderer = ({ section, apiAdapter, schemaData, onValueChange, gridColumnSpan, onSectionSave, hideEditButton = false, mode = 'RegistryView', }) => {
2390
+ const SectionRenderer = ({ section, apiAdapter, schemaData, onValueChange, gridColumnSpan, onSectionSave, hideEditButton = false, mode = 'RegistryView', namespace, }) => {
2258
2391
  const { translateConfig, translate } = useWidgetTranslation();
2259
2392
  const { schemaData: contextSchemaData } = useWidgetContext();
2260
2393
  const store = useStore();
@@ -2262,6 +2395,49 @@ const SectionRenderer = ({ section, apiAdapter, schemaData, onValueChange, gridC
2262
2395
  // Get CRView data from schemaData (prefer prop over context, then Redux store)
2263
2396
  const currentSchemaData = schemaData || contextSchemaData || {};
2264
2397
  const storeValues = useSelector((state) => state.widget?.values || {});
2398
+ // Namespace the section if namespace is provided
2399
+ // This ensures unique widget IDs when the same section is rendered multiple times
2400
+ const namespacedSection = useMemo(() => {
2401
+ if (namespace) {
2402
+ return namespaceSectionConfig(section, namespace);
2403
+ }
2404
+ return section;
2405
+ }, [section, namespace]);
2406
+ // Create namespaced schemaData if namespace is provided
2407
+ // This ensures widgets can read initial values from schemaData at namespaced paths
2408
+ const namespacedSchemaData = useMemo(() => {
2409
+ if (!namespace || !currentSchemaData) {
2410
+ return schemaData;
2411
+ }
2412
+ // Create a namespaced version of schemaData by copying values to namespaced paths
2413
+ const namespaced = { ...currentSchemaData };
2414
+ // Copy all top-level keys to namespaced paths
2415
+ Object.keys(currentSchemaData).forEach(key => {
2416
+ const namespacedKey = `${namespace}.${key}`;
2417
+ if (!(namespacedKey in namespaced)) {
2418
+ namespaced[namespacedKey] = currentSchemaData[key];
2419
+ }
2420
+ });
2421
+ // Also handle nested objects - copy nested values to namespaced paths
2422
+ const copyNestedValues = (obj, prefix = '') => {
2423
+ if (obj && typeof obj === 'object' && !Array.isArray(obj)) {
2424
+ Object.keys(obj).forEach(key => {
2425
+ const fullPath = prefix ? `${prefix}.${key}` : key;
2426
+ const namespacedPath = `${namespace}.${fullPath}`;
2427
+ if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
2428
+ copyNestedValues(obj[key], fullPath);
2429
+ // Also set the nested object at the namespaced path
2430
+ setValueByPath(namespaced, namespacedPath, obj[key]);
2431
+ }
2432
+ else {
2433
+ setValueByPath(namespaced, namespacedPath, obj[key]);
2434
+ }
2435
+ });
2436
+ }
2437
+ };
2438
+ copyNestedValues(currentSchemaData);
2439
+ return namespaced;
2440
+ }, [namespace, schemaData, currentSchemaData]);
2265
2441
  const crViewData = useMemo(() => {
2266
2442
  if (mode !== 'CRView')
2267
2443
  return null;
@@ -2280,11 +2456,14 @@ const SectionRenderer = ({ section, apiAdapter, schemaData, onValueChange, gridC
2280
2456
  }
2281
2457
  return result;
2282
2458
  }, [mode, currentSchemaData, storeValues]);
2283
- const sectionId = section['section-id'];
2459
+ // Use namespaced section for rendering
2460
+ const sectionToRender = namespacedSection;
2461
+ const sectionId = sectionToRender['section-id'];
2284
2462
  const gridId = `section-panels-${sectionId}`;
2285
2463
  const sectionClassId = `section-${sectionId}`;
2286
2464
  // Recursively count all vertical panels, especially those nested inside horizontal panels
2287
2465
  // Typically: horizontal panels at first level contain vertical panels at second level
2466
+ // Accounts for panel-column-span: a panel with column-span 3 counts as 3 columns
2288
2467
  const countVerticalPanels = (panels) => {
2289
2468
  let count = 0;
2290
2469
  for (const panel of panels) {
@@ -2294,8 +2473,9 @@ const SectionRenderer = ({ section, apiAdapter, schemaData, onValueChange, gridC
2294
2473
  count += countVerticalPanels(panel.panels);
2295
2474
  }
2296
2475
  else if (orientation === 'vertical') {
2297
- // Count this vertical panel
2298
- count += 1;
2476
+ // Count this vertical panel, accounting for column span
2477
+ const columnSpan = panel['panel-column-span'] || 1;
2478
+ count += columnSpan;
2299
2479
  // Also recursively count vertical panels nested inside this vertical panel
2300
2480
  if (panel.panels && panel.panels.length > 0) {
2301
2481
  count += countVerticalPanels(panel.panels);
@@ -2342,9 +2522,9 @@ const SectionRenderer = ({ section, apiAdapter, schemaData, onValueChange, gridC
2342
2522
  }
2343
2523
  return null;
2344
2524
  };
2345
- const hasTableWidget = checkForTableWidget(section.panels);
2346
- const tableWidgetColumnSpan = getTableWidgetColumnSpan(section.panels);
2347
- const verticalPanelsCount = countVerticalPanels(section.panels);
2525
+ const hasTableWidget = checkForTableWidget(sectionToRender.panels);
2526
+ const tableWidgetColumnSpan = getTableWidgetColumnSpan(sectionToRender.panels);
2527
+ const verticalPanelsCount = countVerticalPanels(sectionToRender.panels);
2348
2528
  // If section contains a table widget with explicit column span, use it
2349
2529
  // Otherwise, if it has a table widget, ensure it spans at least 2 columns
2350
2530
  // Otherwise, use the vertical panel count
@@ -2354,7 +2534,7 @@ const SectionRenderer = ({ section, apiAdapter, schemaData, onValueChange, gridC
2354
2534
  // Check if table widget has explicit column span (not default)
2355
2535
  const hasExplicitTableSpan = tableWidgetColumnSpan !== null;
2356
2536
  // Supporting documents configuration
2357
- const supportingDocuments = section['section-supporting-documents'] || [];
2537
+ const supportingDocuments = sectionToRender['section-supporting-documents'] || [];
2358
2538
  const hasSupportingDocuments = supportingDocuments.length > 0;
2359
2539
  // Edit mode state
2360
2540
  const [isEditMode, setIsEditMode] = useState(false);
@@ -2392,7 +2572,7 @@ const SectionRenderer = ({ section, apiAdapter, schemaData, onValueChange, gridC
2392
2572
  }, [isEditMode]);
2393
2573
  // Recursively modify panels to set readonly based on edit mode
2394
2574
  const makePanelsEditable = (panels, editable) => {
2395
- const sectionEditable = section['section-editable'] === true;
2575
+ const sectionEditable = sectionToRender['section-editable'] === true;
2396
2576
  return panels.map(panel => {
2397
2577
  const modifiedPanel = {
2398
2578
  ...panel,
@@ -2415,10 +2595,10 @@ const SectionRenderer = ({ section, apiAdapter, schemaData, onValueChange, gridC
2415
2595
  const editableSection = useMemo(() => {
2416
2596
  // Always apply readonly/editable state based on edit mode
2417
2597
  return {
2418
- ...section,
2419
- panels: makePanelsEditable(section.panels, isEditMode),
2598
+ ...sectionToRender,
2599
+ panels: makePanelsEditable(sectionToRender.panels, isEditMode),
2420
2600
  };
2421
- }, [section, isEditMode]);
2601
+ }, [sectionToRender, isEditMode]);
2422
2602
  // Handle edit button click
2423
2603
  const handleEdit = () => {
2424
2604
  // Capture height BEFORE entering edit mode to preserve space
@@ -2485,9 +2665,9 @@ const SectionRenderer = ({ section, apiAdapter, schemaData, onValueChange, gridC
2485
2665
  width: `${editSectionPosition.width}px`,
2486
2666
  maxHeight: '90vh',
2487
2667
  overflowY: 'auto',
2488
- }, children: [section['section-title'] && (jsxRuntimeExports.jsx("h2", { className: "text-xl font-semibold mb-4", style: { fontFamily: 'Roboto, sans-serif', marginTop: '35px' }, children: translateConfig(section['section-title']) })), jsxRuntimeExports.jsxs("div", { id: editGridId, className: "section-panels", children: [editableSection.panels.map((panel, index) => {
2668
+ }, children: [sectionToRender['section-title'] && (jsxRuntimeExports.jsx("h2", { className: "text-xl font-semibold mb-4", style: { fontFamily: 'Roboto, sans-serif', marginTop: '35px' }, children: translateConfig(sectionToRender['section-title']) })), jsxRuntimeExports.jsxs("div", { id: editGridId, className: "section-panels", children: [editableSection.panels.map((panel, index) => {
2489
2669
  const isLastPanel = index === editableSection.panels.length - 1;
2490
- return (jsxRuntimeExports.jsx("div", { className: `panel-wrapper ${isLastPanel ? 'last-panel-wrapper' : ''}`, children: jsxRuntimeExports.jsx(PanelRenderer, { panel: panel, apiAdapter: apiAdapter, schemaData: schemaData, onValueChange: onValueChange, isEditMode: true }) }, panel['panel-id'] || `section-panel-${index}`));
2670
+ return (jsxRuntimeExports.jsx("div", { className: `panel-wrapper ${isLastPanel ? 'last-panel-wrapper' : ''}`, children: jsxRuntimeExports.jsx(PanelRenderer, { panel: panel, apiAdapter: apiAdapter, schemaData: namespacedSchemaData, onValueChange: onValueChange, isEditMode: true }) }, panel['panel-id'] || `section-panel-${index}`));
2491
2671
  }), hasSupportingDocuments && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("hr", { className: "my-4 w-full", style: { height: '1px', backgroundColor: '#F2BA1A', border: 'none' } }), jsxRuntimeExports.jsxs("div", { className: "supporting-documents-container", children: [jsxRuntimeExports.jsxs("button", { type: "button", onClick: () => setIsDocumentsExpanded(!isDocumentsExpanded), className: "supporting-documents-title-button w-full flex items-center text-left", children: [jsxRuntimeExports.jsx("span", { className: "font-semibold", style: { fontFamily: 'Roboto, sans-serif', fontSize: '16px' }, children: translate('common.supportedDocuments') || 'Supported Documents' }), jsxRuntimeExports.jsx("svg", { className: `w-5 h-5 text-[#ED7C22] transition-transform ml-2 ${isDocumentsExpanded ? 'rotate-180' : ''}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), isDocumentsExpanded && (jsxRuntimeExports.jsx("div", { className: "supporting-documents-grid mt-4", children: supportingDocuments.map((doc, index) => {
2492
2672
  const docConfig = createDocumentWidgetConfig(doc, sectionId, index);
2493
2673
  return (jsxRuntimeExports.jsx("div", { className: "supporting-document-item", children: jsxRuntimeExports.jsx(FileInputWidget, { config: docConfig }) }, `${sectionId}-doc-${index}`));
@@ -2505,27 +2685,62 @@ const SectionRenderer = ({ section, apiAdapter, schemaData, onValueChange, gridC
2505
2685
  });
2506
2686
  return widgets;
2507
2687
  };
2508
- const buildSectionSnapshot = (widgets, sourceData) => {
2688
+ const trackSectionChages = (widgets, sourceData, useNamespacedPaths = false) => {
2509
2689
  const snapshot = {};
2690
+ let hasTable = false;
2691
+ const recordId = Object.keys(sourceData)[0];
2510
2692
  widgets.forEach(widget => {
2511
- const dataPath = widget['widget-data-path'];
2512
- if (!dataPath)
2693
+ const originalDataPath = widget['widget-data-path'];
2694
+ if (!originalDataPath)
2513
2695
  return;
2696
+ if (widget['widget-type'] === 'table' || widget['widget-type'] === 'simple-table') {
2697
+ hasTable = true;
2698
+ }
2699
+ // If namespace was used and we're reading from store, use namespaced paths
2700
+ useNamespacedPaths && namespace && originalDataPath
2701
+ ? (typeof originalDataPath === 'string'
2702
+ ? `${namespace}.${originalDataPath}`
2703
+ : Object.fromEntries(Object.entries(originalDataPath).map(([key, path]) => [key, `${namespace}.${path}`])))
2704
+ : originalDataPath;
2705
+ // Always store snapshot using original paths (for change tracking)
2514
2706
  // Handle multi-path (object) or single path (string)
2515
- if (typeof dataPath === 'object') {
2516
- // Multi-path: store each path separately
2517
- Object.entries(dataPath).forEach(([key, path]) => {
2707
+ if (typeof originalDataPath === 'object') {
2708
+ // Multi-path: store each path separately using original paths
2709
+ Object.entries(originalDataPath).forEach(([key, path]) => {
2518
2710
  if (typeof path === 'string') {
2519
- snapshot[path] = getValueByPath(sourceData, path);
2711
+ // Read from source using namespaced path if needed
2712
+ const readPath = useNamespacedPaths && namespace ? `${namespace}.${path}` : path;
2713
+ snapshot[path] = getValueByPath(sourceData, readPath);
2520
2714
  }
2521
2715
  });
2522
2716
  }
2523
- else if (typeof dataPath === 'string') {
2524
- snapshot[dataPath] = getValueByPath(sourceData, dataPath);
2717
+ else if (typeof originalDataPath === 'string') {
2718
+ // Read from source using namespaced path if needed
2719
+ const readPath = useNamespacedPaths && namespace ? `${namespace}.${originalDataPath}` : originalDataPath;
2720
+ snapshot[originalDataPath] = getValueByPath(sourceData, readPath);
2525
2721
  }
2526
2722
  });
2527
- return snapshot;
2723
+ if (hasTable === false) {
2724
+ const cleanedSnapshot = {};
2725
+ Object.entries(snapshot).forEach(([key, value]) => {
2726
+ const removedFirstLevelPath = key.includes('.')
2727
+ ? key.split('.').slice(1).join('.')
2728
+ : key;
2729
+ cleanedSnapshot[removedFirstLevelPath] = value;
2730
+ });
2731
+ return [
2732
+ { ...sourceData[recordId],
2733
+ ...cleanedSnapshot,
2734
+ edit_action: "UPDATE"
2735
+ }
2736
+ ];
2737
+ }
2738
+ const recordEntry = Object.entries(snapshot).find(([key, value]) => key.endsWith('.records') && Array.isArray(value));
2739
+ return recordEntry ? recordEntry[1] : snapshot;
2528
2740
  };
2741
+ // Get original section (without namespace) for building snapshots
2742
+ // This ensures we use the original data paths when saving
2743
+ const originalSection = section;
2529
2744
  // Handle save button click
2530
2745
  const handleSave = async () => {
2531
2746
  if (!store || !onSectionSave) {
@@ -2533,31 +2748,37 @@ const SectionRenderer = ({ section, apiAdapter, schemaData, onValueChange, gridC
2533
2748
  setIsEditMode(false);
2534
2749
  return;
2535
2750
  }
2536
- const sectionWidgets = collectWidgets(section.panels);
2751
+ // Use original section (without namespace) for collecting widgets
2752
+ // This ensures we use the original widget IDs and data paths
2753
+ const sectionWidgets = collectWidgets(originalSection.panels);
2537
2754
  const currentState = store.getState().widget;
2538
2755
  const currentSchemaData = currentState.values || {};
2756
+ // schema data before section change
2539
2757
  const oldSchemaData = schemaData || contextSchemaData;
2540
- const oldSectionValue = buildSectionSnapshot(sectionWidgets, oldSchemaData);
2541
- const newSectionValue = buildSectionSnapshot(sectionWidgets, currentSchemaData);
2758
+ // schema data after section change
2759
+ const newSchemaData = trackSectionChages(sectionWidgets, currentSchemaData);
2542
2760
  // Include supporting documents in the snapshot if they exist
2761
+ const sectionFiles = [];
2543
2762
  if (hasSupportingDocuments) {
2544
- supportingDocuments.forEach((doc, index) => {
2545
- const dataPath = doc['document-data-path'];
2546
- const oldValue = getValueByPath(oldSchemaData, dataPath);
2547
- const newValue = getValueByPath(currentSchemaData, dataPath);
2548
- oldSectionValue[dataPath] = oldValue;
2549
- newSectionValue[dataPath] = newValue;
2763
+ // Use original section's supporting documents to get original data paths
2764
+ const originalSupportingDocuments = originalSection['section-supporting-documents'] || [];
2765
+ originalSupportingDocuments.forEach((doc) => {
2766
+ const originalDataPath = doc['document-data-path'];
2767
+ // Read new value from store (with namespace if used)
2768
+ const storeDataPath = namespace && originalDataPath
2769
+ ? `${namespace}.${originalDataPath}`
2770
+ : originalDataPath;
2771
+ sectionFiles.push(getValueByPath(currentSchemaData, storeDataPath));
2550
2772
  });
2551
2773
  }
2552
- if (JSON.stringify(oldSectionValue) !== JSON.stringify(newSectionValue)) {
2553
- const changes = {
2554
- section_id: sectionId,
2555
- section_schema: section,
2556
- old_section_value: oldSectionValue,
2557
- new_section_value: newSectionValue,
2558
- };
2774
+ if (JSON.stringify(oldSchemaData) !== JSON.stringify(newSchemaData)) {
2559
2775
  try {
2560
- await onSectionSave(changes);
2776
+ const sectionchanges = {
2777
+ section_id: originalSection['section-id'],
2778
+ records: [...newSchemaData],
2779
+ files: [...sectionFiles]
2780
+ };
2781
+ await onSectionSave(sectionchanges);
2561
2782
  }
2562
2783
  catch (error) {
2563
2784
  console.error('Section Changes Save failed', error);
@@ -2568,40 +2789,58 @@ const SectionRenderer = ({ section, apiAdapter, schemaData, onValueChange, gridC
2568
2789
  // Handle cancel button click
2569
2790
  const handleCancel = () => {
2570
2791
  // Revert values in store to original schema data
2571
- const sectionWidgets = collectWidgets(section.panels);
2792
+ // Use original section (without namespace) for collecting widgets
2793
+ const sectionWidgets = collectWidgets(originalSection.panels);
2572
2794
  const oldSchemaData = schemaData || contextSchemaData;
2573
2795
  const currentStoreValues = store.getState().widget.values;
2574
2796
  let newStoreValues = currentStoreValues;
2575
2797
  sectionWidgets.forEach(widget => {
2576
- const widgetId = widget['widget-id'];
2577
- const dataPath = widget['widget-data-path'];
2578
- if (widgetId && dataPath) {
2798
+ const originalWidgetId = widget['widget-id'];
2799
+ // If namespace was used, we need to use namespaced widget ID and data path
2800
+ const namespacedWidgetId = namespace ? `${namespace}__${originalWidgetId}` : originalWidgetId;
2801
+ const widgetId = namespacedWidgetId;
2802
+ const originalDataPath = widget['widget-data-path'];
2803
+ // If namespace was used, data path in store is namespaced, but we read from original schema using original path
2804
+ const storeDataPath = namespace && originalDataPath
2805
+ ? (typeof originalDataPath === 'string'
2806
+ ? `${namespace}.${originalDataPath}`
2807
+ : Object.fromEntries(Object.entries(originalDataPath).map(([key, path]) => [key, `${namespace}.${path}`])))
2808
+ : originalDataPath;
2809
+ if (widgetId && originalDataPath) {
2579
2810
  // Handle multi-path (object) or single path (string)
2811
+ // Read from original schema data using original paths
2580
2812
  let oldValue;
2581
- if (typeof dataPath === 'object') {
2813
+ if (typeof originalDataPath === 'object') {
2582
2814
  // Multi-path: get values for each path
2583
2815
  oldValue = {};
2584
- Object.entries(dataPath).forEach(([key, path]) => {
2816
+ Object.entries(originalDataPath).forEach(([key, path]) => {
2585
2817
  if (typeof path === 'string') {
2586
2818
  oldValue[key] = getValueByPath(oldSchemaData, path);
2587
2819
  }
2588
2820
  });
2589
2821
  }
2590
- else if (typeof dataPath === 'string') {
2591
- oldValue = getValueByPath(oldSchemaData, dataPath);
2822
+ else if (typeof originalDataPath === 'string') {
2823
+ oldValue = getValueByPath(oldSchemaData, originalDataPath);
2592
2824
  }
2825
+ // Set in store using namespaced data path (if namespace was used)
2593
2826
  if (oldValue !== undefined) {
2594
- newStoreValues = setWidgetValue(newStoreValues, dataPath, widgetId, oldValue);
2827
+ newStoreValues = setWidgetValue(newStoreValues, storeDataPath, widgetId, oldValue);
2595
2828
  }
2596
2829
  }
2597
2830
  });
2598
2831
  // Also revert supporting documents if any
2599
2832
  if (hasSupportingDocuments) {
2600
- supportingDocuments.forEach((doc, index) => {
2833
+ // Use original section's supporting documents to get original data paths
2834
+ const originalSupportingDocuments = originalSection['section-supporting-documents'] || [];
2835
+ originalSupportingDocuments.forEach((doc, index) => {
2601
2836
  const widgetId = `supporting-doc-${sectionId}-${index}`;
2602
- const dataPath = doc['document-data-path'];
2603
- const oldValue = getValueByPath(oldSchemaData, dataPath);
2604
- newStoreValues = setWidgetValue(newStoreValues, dataPath, widgetId, oldValue);
2837
+ const originalDataPath = doc['document-data-path'];
2838
+ // If namespace was used, data path in store is namespaced
2839
+ const storeDataPath = namespace && originalDataPath
2840
+ ? `${namespace}.${originalDataPath}`
2841
+ : originalDataPath;
2842
+ const oldValue = getValueByPath(oldSchemaData, originalDataPath);
2843
+ newStoreValues = setWidgetValue(newStoreValues, storeDataPath, widgetId, oldValue);
2605
2844
  });
2606
2845
  }
2607
2846
  if (newStoreValues !== currentStoreValues) {
@@ -2616,11 +2855,13 @@ const SectionRenderer = ({ section, apiAdapter, schemaData, onValueChange, gridC
2616
2855
  (documentType === 'image' ? 'image/*' :
2617
2856
  documentType === 'pdf' ? '.pdf' :
2618
2857
  '*/*');
2858
+ // Use the namespaced section ID for widget ID to ensure uniqueness
2859
+ const widgetId = `supporting-doc-${sectionId}-${index}`;
2619
2860
  return {
2620
2861
  widget: 'file',
2621
2862
  'widget-type': 'input',
2622
2863
  'widget-label': doc['document-label'] || doc['document-data-path'] || `Document ${index + 1}`,
2623
- 'widget-id': `supporting-doc-${sectionId}-${index}`,
2864
+ 'widget-id': widgetId,
2624
2865
  'widget-data-path': doc['document-data-path'],
2625
2866
  'widget-required': doc['document-required'] || false,
2626
2867
  'widget-readonly': false,
@@ -2776,7 +3017,7 @@ const SectionRenderer = ({ section, apiAdapter, schemaData, onValueChange, gridC
2776
3017
  minHeight: 'auto',
2777
3018
  height: 'auto'
2778
3019
  }),
2779
- }, children: [section['section-title'] && (jsxRuntimeExports.jsx("h2", { className: "text-xl font-semibold mb-4", style: { marginTop: '35px' }, children: translateConfig(section['section-title']) })), jsxRuntimeExports.jsxs("div", { id: gridId, className: "section-panels", style: mode === 'RegistryView' && hideEditButton ? { paddingBottom: '40px' } : {}, children: [editableSection.panels.map((panel, index) => (jsxRuntimeExports.jsx("div", { className: "panel-wrapper", children: jsxRuntimeExports.jsx(PanelRenderer, { panel: panel, apiAdapter: apiAdapter, schemaData: schemaData, onValueChange: onValueChange }) }, panel['panel-id'] || `section-panel-${index}`))), mode === 'CRView' && crViewData && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("hr", { className: "border-gray-300 w-full", style: { height: '1px', marginTop: '20px', marginBottom: '0px' } }), jsxRuntimeExports.jsxs("div", { className: "cr-view-container", style: {
3020
+ }, children: [sectionToRender['section-title'] && (jsxRuntimeExports.jsx("h2", { className: "text-xl font-semibold mb-4", style: { marginTop: '35px' }, children: translateConfig(sectionToRender['section-title']) })), jsxRuntimeExports.jsxs("div", { id: gridId, className: "section-panels", style: mode === 'RegistryView' && hideEditButton ? { paddingBottom: '40px' } : {}, children: [editableSection.panels.map((panel, index) => (jsxRuntimeExports.jsx("div", { className: "panel-wrapper", children: jsxRuntimeExports.jsx(PanelRenderer, { panel: panel, apiAdapter: apiAdapter, schemaData: namespacedSchemaData, onValueChange: onValueChange }) }, panel['panel-id'] || `section-panel-${index}`))), mode === 'CRView' && crViewData && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("hr", { className: "border-gray-300 w-full", style: { height: '1px', marginTop: '20px', marginBottom: '0px' } }), jsxRuntimeExports.jsxs("div", { className: "cr-view-container", style: {
2780
3021
  marginTop: '20px',
2781
3022
  paddingBottom: '30px',
2782
3023
  display: 'flex',
@@ -2880,6 +3121,7 @@ const getTableWidgetColumnSpan = (panels) => {
2880
3121
  /**
2881
3122
  * Recursively count all vertical panels in a section
2882
3123
  * Handles nested structure: horizontal panels containing vertical panels
3124
+ * Accounts for panel-column-span: a panel with column-span 3 counts as 3 columns
2883
3125
  */
2884
3126
  const countVerticalPanels = (panels) => {
2885
3127
  let count = 0;
@@ -2890,8 +3132,9 @@ const countVerticalPanels = (panels) => {
2890
3132
  count += countVerticalPanels(panel.panels);
2891
3133
  }
2892
3134
  else if (orientation === 'vertical') {
2893
- // Count this vertical panel
2894
- count += 1;
3135
+ // Count this vertical panel, accounting for column span
3136
+ const columnSpan = panel['panel-column-span'] || 1;
3137
+ count += columnSpan;
2895
3138
  // Also recursively count vertical panels nested inside this vertical panel
2896
3139
  if (panel.panels && panel.panels.length > 0) {
2897
3140
  count += countVerticalPanels(panel.panels);
@@ -2910,7 +3153,7 @@ const countVerticalPanels = (panels) => {
2910
3153
  * - All sections align to the same grid, ensuring right-side alignment
2911
3154
  * - Handles nested structure: multiple horizontal panels, each with multiple vertical panels
2912
3155
  */
2913
- const SectionsContainer = ({ sections, apiAdapter, schemaData, onValueChange, className = '', onSectionSave, hideEditButton = false, mode = 'RegistryView', }) => {
3156
+ const SectionsContainer = ({ sections, apiAdapter, schemaData, onValueChange, className = '', onSectionSave, hideEditButton = false, mode = 'RegistryView', namespace, }) => {
2914
3157
  // Find the maximum number of vertical panels across all sections
2915
3158
  // This determines the grid size (minimum 3 columns)
2916
3159
  // Also account for table widgets and their explicit column spans
@@ -2952,10 +3195,14 @@ const SectionsContainer = ({ sections, apiAdapter, schemaData, onValueChange, cl
2952
3195
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
2953
3196
  }
2954
3197
  }
2955
- ` }), jsxRuntimeExports.jsx("div", { id: containerId, className: `sections-container ${className}`, children: sections.map((section) => {
3198
+ ` }), jsxRuntimeExports.jsx("div", { id: containerId, className: `sections-container ${className}`, children: sections.map((section, index) => {
3199
+ // Determine namespace for this section
3200
+ const sectionNamespace = namespace
3201
+ ? (typeof namespace === 'string' ? namespace : namespace(section['section-id'], index))
3202
+ : undefined;
2956
3203
  // Check if section has explicit column span
2957
3204
  if (section['section-column-span']) {
2958
- return (jsxRuntimeExports.jsx(SectionRenderer, { section: section, apiAdapter: apiAdapter, schemaData: schemaData, onValueChange: onValueChange, gridColumnSpan: section['section-column-span'], onSectionSave: onSectionSave, hideEditButton: hideEditButton, mode: mode }, section['section-id']));
3205
+ return (jsxRuntimeExports.jsx(SectionRenderer, { section: section, apiAdapter: apiAdapter, schemaData: schemaData, onValueChange: onValueChange, gridColumnSpan: section['section-column-span'], onSectionSave: onSectionSave, hideEditButton: hideEditButton, mode: mode, namespace: sectionNamespace }, section['section-id']));
2959
3206
  }
2960
3207
  const verticalPanelsCount = countVerticalPanels(section.panels);
2961
3208
  const tableWidgetColumnSpan = getTableWidgetColumnSpan(section.panels);
@@ -2966,7 +3213,7 @@ const SectionsContainer = ({ sections, apiAdapter, schemaData, onValueChange, cl
2966
3213
  const columnSpan = tableWidgetColumnSpan !== null
2967
3214
  ? tableWidgetColumnSpan
2968
3215
  : (containsTable ? Math.max(verticalPanelsCount, 2) : verticalPanelsCount);
2969
- return (jsxRuntimeExports.jsx(SectionRenderer, { section: section, apiAdapter: apiAdapter, schemaData: schemaData, onValueChange: onValueChange, gridColumnSpan: columnSpan, onSectionSave: onSectionSave, hideEditButton: hideEditButton, mode: mode }, section['section-id']));
3216
+ return (jsxRuntimeExports.jsx(SectionRenderer, { section: section, apiAdapter: apiAdapter, schemaData: schemaData, onValueChange: onValueChange, gridColumnSpan: columnSpan, onSectionSave: onSectionSave, hideEditButton: hideEditButton, mode: mode, namespace: sectionNamespace }, section['section-id']));
2970
3217
  }) })] }));
2971
3218
  };
2972
3219
 
@@ -4400,6 +4647,14 @@ const DateTimeInputWidget = ({ config }) => {
4400
4647
  const SelectWidget = ({ config }) => {
4401
4648
  const { value, error, touched, isEnabled, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
4402
4649
  const { translate, translateConfig } = useWidgetTranslation();
4650
+ // For readonly mode, render as display text showing only the selected label
4651
+ if (widgetConfig['widget-readonly']) {
4652
+ const label = translateConfig(widgetConfig['widget-label']);
4653
+ // Find the selected option's label
4654
+ const selectedOption = dataSourceOptions.find((option) => option.value === value);
4655
+ const displayValue = selectedOption ? selectedOption.label : (value || '-');
4656
+ return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] SelectDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", children: displayValue }) })] }));
4657
+ }
4403
4658
  return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("select", { value: value || '', onChange: (e) => onChange(e.target.value), onBlur: onBlur, disabled: !isEnabled || loading || widgetConfig['widget-readonly'], className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (!value || value === ''))
4404
4659
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
4405
4660
  : 'border-gray-300'} ${!isEnabled || loading || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: translateConfig(widgetConfig['widget-data-tooltip']), children: [jsxRuntimeExports.jsx("option", { value: "", children: translate('common.select') }), dataSourceOptions.map((option) => (jsxRuntimeExports.jsx("option", { value: option.value, children: option.label }, option.value)))] }), loading && (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 mt-1", children: translate('common.loadingOptions') })), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
@@ -4425,54 +4680,87 @@ const RadioWidget = ({ config }) => {
4425
4680
  }
4426
4681
  return options;
4427
4682
  }, [dataSourceOptions, sortOptions]);
4428
- // Handle value change
4429
- const handleChange = useCallback((optionValue) => {
4430
- onChange(optionValue);
4683
+ // Handle radio button change - simple and direct
4684
+ useCallback((selectedValue) => {
4685
+ onChange(selectedValue);
4431
4686
  }, [onChange]);
4432
- // Handle unset (clear selection) - only if optional
4433
- const handleUnset = useCallback(() => {
4434
- if (allowUnset) {
4435
- onChange(null);
4436
- }
4437
- }, [allowUnset, onChange]);
4438
- // Determine current value (handle null/undefined for optional fields)
4687
+ // Determine current value
4439
4688
  const currentValue = useMemo(() => {
4689
+ console.log('[RadioWidget] Value from useBaseWidget:', {
4690
+ value,
4691
+ type: typeof value,
4692
+ widgetId: widgetConfig['widget-id'],
4693
+ dataPath: widgetConfig['widget-data-path']
4694
+ });
4440
4695
  if (value === null || value === undefined) {
4441
4696
  return null;
4442
4697
  }
4443
4698
  return value;
4444
- }, [value]);
4445
- // Get layout classes and styles
4446
- const layoutConfig = useMemo(() => {
4699
+ }, [value, widgetConfig]);
4700
+ // Helper to safely compare values (handles type coercion)
4701
+ const isValueSelected = useCallback((optionValue) => {
4702
+ // Handle null/undefined cases
4703
+ if (currentValue === null || currentValue === undefined) {
4704
+ return optionValue === null || optionValue === undefined;
4705
+ }
4706
+ if (optionValue === null || optionValue === undefined) {
4707
+ return false;
4708
+ }
4709
+ // Convert both to strings and compare (handles any type mismatch)
4710
+ const currentStr = String(currentValue).trim();
4711
+ const optionStr = String(optionValue).trim();
4712
+ return currentStr === optionStr;
4713
+ }, [currentValue]);
4714
+ // Get layout classes
4715
+ const getLayoutClass = useMemo(() => {
4447
4716
  switch (layout) {
4448
4717
  case 'horizontal':
4449
- return {
4450
- className: 'flex flex-row flex-wrap gap-4',
4451
- style: undefined,
4452
- };
4718
+ return 'flex flex-row flex-wrap gap-4';
4453
4719
  case 'grid':
4454
- // Calculate grid columns based on option count (max 4 columns, min 2)
4455
- const cols = Math.max(2, Math.min(processedOptions.length, 4));
4456
- return {
4457
- className: 'grid gap-3',
4458
- style: { gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))` },
4459
- };
4720
+ Math.max(2, Math.min(processedOptions.length, 4));
4721
+ return 'grid gap-3';
4460
4722
  case 'vertical':
4461
4723
  default:
4462
- return {
4463
- className: 'flex flex-col space-y-2',
4464
- style: undefined,
4465
- };
4724
+ return 'flex flex-col space-y-2';
4466
4725
  }
4467
4726
  }, [layout, processedOptions.length]);
4727
+ const getLayoutStyle = useMemo(() => {
4728
+ if (layout === 'grid') {
4729
+ const cols = Math.max(2, Math.min(processedOptions.length, 4));
4730
+ return { gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))` };
4731
+ }
4732
+ return undefined;
4733
+ }, [layout, processedOptions.length]);
4468
4734
  // For readonly mode, render as display text
4469
4735
  if (widgetConfig['widget-readonly']) {
4470
4736
  const label = translateConfig(widgetConfig['widget-label']);
4471
- const selectedOption = processedOptions.find(opt => opt.value === currentValue);
4737
+ const selectedOption = processedOptions.find(opt => isValueSelected(opt.value));
4472
4738
  const displayValue = selectedOption ? selectedOption.label : (allowUnset && currentValue === null ? '-' : '');
4473
4739
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] RadioDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", children: displayValue }) })] }));
4474
4740
  }
4475
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("div", { className: layoutConfig.className, style: layoutConfig.style, onBlur: onBlur, children: loading ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500", children: translate('common.loading') })) : (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [allowUnset && (jsxRuntimeExports.jsxs("label", { className: `flex items-center cursor-pointer ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : ''}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: widgetConfig['widget-id'], checked: currentValue === null, onChange: handleUnset, disabled: !isEnabled || widgetConfig['widget-readonly'], className: "mr-2 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-700", children: "-" })] })), processedOptions.map((option) => (jsxRuntimeExports.jsxs("label", { className: `flex items-center cursor-pointer ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : ''}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: widgetConfig['widget-id'], value: option.value, checked: currentValue === option.value, onChange: (e) => handleChange(option.value), disabled: !isEnabled || widgetConfig['widget-readonly'], className: "mr-2 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-700", children: translateConfig(option.label) })] }, option.value)))] })) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
4741
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsxs("label", { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, children: [translateConfig(widgetConfig['widget-label']), widgetConfig['widget-required'] && (jsxRuntimeExports.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsx("div", { className: getLayoutClass, style: getLayoutStyle, children: loading ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500", children: translate('common.loading') })) : (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [allowUnset && (jsxRuntimeExports.jsxs("label", { className: `flex items-center cursor-pointer ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : ''}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: widgetConfig['widget-id'], checked: currentValue === null,
4742
+ // onChange={() => handleRadioChange(null)}
4743
+ disabled: !isEnabled || widgetConfig['widget-readonly'], className: "mr-2 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-700", children: "-" })] })), processedOptions.map((option, index) => {
4744
+ const isChecked = isValueSelected(option.value);
4745
+ // Debug log (remove after testing)
4746
+ if (index === 0) {
4747
+ console.log('[RadioWidget] Value check:', {
4748
+ currentValue,
4749
+ optionValue: option.value,
4750
+ currentType: typeof currentValue,
4751
+ optionType: typeof option.value,
4752
+ isChecked,
4753
+ currentStr: String(currentValue),
4754
+ optionStr: String(option.value)
4755
+ });
4756
+ }
4757
+ return (jsxRuntimeExports.jsxs("label", { className: `flex items-center cursor-pointer ${!isEnabled || widgetConfig['widget-readonly'] ? 'opacity-50 cursor-not-allowed' : ''}`, children: [jsxRuntimeExports.jsx("input", { type: "radio", name: widgetConfig['widget-id'], value: String(option.value), checked: isChecked,
4758
+ // onChange={() => {
4759
+ // console.log('[RadioWidget] onChange called:', option.value);
4760
+ // handleRadioChange(option.value);
4761
+ // }}
4762
+ disabled: !isEnabled || widgetConfig['widget-readonly'], className: "mr-2 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" }), jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-700", children: translateConfig(option.label) })] }, `${widgetConfig['widget-id']}-${option.value}-${index}`));
4763
+ })] })) }), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
4476
4764
  };
4477
4765
 
4478
4766
  const CheckboxWidget = ({ config }) => {
@@ -4800,6 +5088,17 @@ const TableCellSelect = ({ config, value, onValueChange }) => {
4800
5088
  const isReadonly = config['widget-readonly'] || false;
4801
5089
  return (jsxRuntimeExports.jsxs("select", { value: value || '', onChange: (e) => onValueChange(e.target.value), disabled: isReadonly || loading, className: `w-full h-[28px] px-2 text-sm border focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 ${isReadonly || loading ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'} border-gray-300`, style: { borderRadius: '10px' }, children: [jsxRuntimeExports.jsx("option", { value: "", children: translate('common.select') || 'Select' }), dataSourceOptions.map((option) => (jsxRuntimeExports.jsx("option", { value: option.value, children: option.label }, option.value)))] }));
4802
5090
  };
5091
+ const SelectDisplayValue = ({ config, value }) => {
5092
+ const { dataSourceOptions, loading } = useBaseWidget({ config });
5093
+ if (loading) {
5094
+ return jsxRuntimeExports.jsx("span", { children: "-" });
5095
+ }
5096
+ if (value === null || value === undefined || value === '') {
5097
+ return jsxRuntimeExports.jsx("span", { children: "-" });
5098
+ }
5099
+ const selectedOption = dataSourceOptions.find((option) => option.value === value);
5100
+ return jsxRuntimeExports.jsx("span", { children: selectedOption ? selectedOption.label : String(value) });
5101
+ };
4803
5102
  const TableCellText = ({ config, value, onValueChange }) => {
4804
5103
  const isReadonly = config['widget-readonly'] || false;
4805
5104
  const placeholder = config['widget-data-placeholder'] || '';
@@ -4836,6 +5135,7 @@ const TableWidget = ({ config }) => {
4836
5135
  const { translate, translateConfig } = useWidgetTranslation();
4837
5136
  const { apiAdapter } = useWidgetContext();
4838
5137
  const dispatch = useDispatch();
5138
+ const storeValues = useSelector((state) => state.widget?.values || {});
4839
5139
  const rows = Array.isArray(value) ? value : [];
4840
5140
  const columns = widgetConfig['widget-data-columns'] || [];
4841
5141
  const operations = widgetConfig['widget-data-operations'] || {};
@@ -4847,10 +5147,13 @@ const TableWidget = ({ config }) => {
4847
5147
  const [confirmationState, setConfirmationState] = useState(null);
4848
5148
  const [isAdding, setIsAdding] = useState(false);
4849
5149
  const [newRowData, setNewRowData] = useState(null);
4850
- // When section is in edit mode (isReadonly is false), all rows are automatically editable
5150
+ // Track original rows when entering section edit mode for edit_action tracking
5151
+ const [originalRows, setOriginalRows] = useState(null);
5152
+ // When section is in edit mode (isReadonly is false), rows can be edited individually
5153
+ // But they are NOT automatically editable - user must click Edit button for each row
4851
5154
  const isSectionEditMode = !isReadonly && operations.edit;
4852
5155
  // Check if any row is being edited (either manually or via section edit mode)
4853
- const isAnyRowEditing = editingState !== null || isAdding || isSectionEditMode;
5156
+ const isAnyRowEditing = editingState !== null || isAdding;
4854
5157
  // Show confirmation dialog
4855
5158
  const showConfirmation = useCallback((message, onConfirm, onCancel) => {
4856
5159
  setConfirmationState({
@@ -4916,19 +5219,8 @@ const TableWidget = ({ config }) => {
4916
5219
  }, [isAnyRowEditing, rows, showConfirmation, cancelEdit, translate]);
4917
5220
  // Update cell value during edit
4918
5221
  const updateCellValue = useCallback((columnKey, newValue, rowIndex) => {
4919
- if (isSectionEditMode && rowIndex !== undefined) {
4920
- // When section is in edit mode, update the row directly
4921
- const newRows = [...rows];
4922
- if (!newRows[rowIndex]) {
4923
- newRows[rowIndex] = {};
4924
- }
4925
- newRows[rowIndex] = {
4926
- ...newRows[rowIndex],
4927
- [columnKey]: newValue,
4928
- };
4929
- onChange(newRows);
4930
- }
4931
- else if (editingState) {
5222
+ if (editingState && rowIndex !== undefined) {
5223
+ // Update editing state (works for both section edit mode and normal mode)
4932
5224
  setEditingState({
4933
5225
  ...editingState,
4934
5226
  currentValue: {
@@ -4936,6 +5228,9 @@ const TableWidget = ({ config }) => {
4936
5228
  [columnKey]: newValue,
4937
5229
  },
4938
5230
  });
5231
+ // Also update Redux store for the cell widget
5232
+ const cellWidgetId = `${widgetConfig['widget-id']}-row-${rowIndex}-col-${columnKey}`;
5233
+ dispatch(setValue({ widgetId: cellWidgetId, value: newValue }));
4939
5234
  }
4940
5235
  else if (isAdding && newRowData) {
4941
5236
  setNewRowData({
@@ -4943,7 +5238,7 @@ const TableWidget = ({ config }) => {
4943
5238
  [columnKey]: newValue,
4944
5239
  });
4945
5240
  }
4946
- }, [editingState, isAdding, newRowData, isSectionEditMode, rows, onChange]);
5241
+ }, [editingState, isAdding, newRowData, widgetConfig, dispatch]);
4947
5242
  // Save edited row
4948
5243
  const saveEdit = useCallback(async () => {
4949
5244
  if (!editingState)
@@ -4968,8 +5263,53 @@ const TableWidget = ({ config }) => {
4968
5263
  }
4969
5264
  // Update local state
4970
5265
  const newRows = [...rows];
4971
- newRows[rowIndex] = rowData;
5266
+ const currentRow = newRows[rowIndex] || {};
5267
+ const wasDeleted = currentRow.edit_action === 'DELETE';
5268
+ // Determine edit_action (for color coding)
5269
+ let editAction = currentRow.edit_action;
5270
+ if (isSectionEditMode) {
5271
+ // If row was deleted but is being saved, un-delete it
5272
+ if (wasDeleted) {
5273
+ // Check if this row exists in original rows
5274
+ if (originalRows) {
5275
+ const rowId = rowData.id;
5276
+ const existsInOriginal = rowId !== undefined
5277
+ ? originalRows.some(or => or.id === rowId)
5278
+ : rowIndex < originalRows.length;
5279
+ editAction = existsInOriginal ? 'UPDATE' : 'ADD';
5280
+ }
5281
+ else {
5282
+ editAction = 'UPDATE';
5283
+ }
5284
+ }
5285
+ else if (!editAction && originalRows) {
5286
+ // Check if this row exists in original rows
5287
+ const rowId = rowData.id;
5288
+ const existsInOriginal = rowId !== undefined
5289
+ ? originalRows.some(or => or.id === rowId)
5290
+ : rowIndex < originalRows.length;
5291
+ editAction = existsInOriginal ? 'UPDATE' : 'ADD';
5292
+ }
5293
+ else if (!editAction) {
5294
+ editAction = 'UPDATE';
5295
+ }
5296
+ }
5297
+ else {
5298
+ // In non-section edit mode, mark as UPDATE if not already set
5299
+ if (!editAction && !wasDeleted) {
5300
+ editAction = 'UPDATE';
5301
+ }
5302
+ }
5303
+ newRows[rowIndex] = {
5304
+ ...rowData,
5305
+ ...(editAction ? { edit_action: editAction } : {}),
5306
+ };
4972
5307
  onChange(newRows);
5308
+ // Clear editing state and reset widget values in Redux
5309
+ columns.forEach((col) => {
5310
+ const cellWidgetId = `${widgetConfig['widget-id']}-row-${rowIndex}-col-${col['column-key']}`;
5311
+ dispatch(resetWidget(cellWidgetId));
5312
+ });
4973
5313
  setEditingState(null);
4974
5314
  }
4975
5315
  catch (error) {
@@ -4980,7 +5320,7 @@ const TableWidget = ({ config }) => {
4980
5320
  finally {
4981
5321
  setLoadingRowIndex(null);
4982
5322
  }
4983
- }, [editingState, rows, onChange, apiAdapter, apiConfig, translate]);
5323
+ }, [editingState, rows, onChange, apiAdapter, apiConfig, translate, isSectionEditMode, originalRows, columns, widgetConfig, dispatch]);
4984
5324
  // Add new row
4985
5325
  const startAdd = useCallback(() => {
4986
5326
  // If there's an unsaved edit, cancel it first (no confirmation needed)
@@ -5014,6 +5354,8 @@ const TableWidget = ({ config }) => {
5014
5354
  savedRow = { ...savedRow, ...response };
5015
5355
  }
5016
5356
  }
5357
+ // Mark new row with edit_action: 'ADD' (for color coding)
5358
+ savedRow = { ...savedRow, edit_action: 'ADD' };
5017
5359
  // Add to local state
5018
5360
  onChange([...rows, savedRow]);
5019
5361
  setIsAdding(false);
@@ -5026,7 +5368,7 @@ const TableWidget = ({ config }) => {
5026
5368
  finally {
5027
5369
  setLoadingRowIndex(null);
5028
5370
  }
5029
- }, [isAdding, newRowData, rows, onChange, apiAdapter, apiConfig, translate]);
5371
+ }, [isAdding, newRowData, rows, onChange, apiAdapter, apiConfig, translate, isSectionEditMode]);
5030
5372
  // Delete row
5031
5373
  const deleteRow = useCallback(async (rowIndex) => {
5032
5374
  if (isAnyRowEditing) {
@@ -5058,9 +5400,20 @@ const TableWidget = ({ config }) => {
5058
5400
  headers: deleteConfig.headers || {},
5059
5401
  });
5060
5402
  }
5061
- // Remove from local state
5062
- const newRows = rows.filter((_, i) => i !== rowIndex);
5063
- onChange(newRows);
5403
+ // In section edit mode, mark row as deleted instead of removing it
5404
+ if (isSectionEditMode) {
5405
+ const newRows = [...rows];
5406
+ newRows[rowIndex] = {
5407
+ ...newRows[rowIndex],
5408
+ edit_action: 'DELETE',
5409
+ };
5410
+ onChange(newRows);
5411
+ }
5412
+ else {
5413
+ // Remove from local state (non-section edit mode)
5414
+ const newRows = rows.filter((_, i) => i !== rowIndex);
5415
+ onChange(newRows);
5416
+ }
5064
5417
  }
5065
5418
  catch (error) {
5066
5419
  console.error('Error deleting record:', error);
@@ -5069,28 +5422,37 @@ const TableWidget = ({ config }) => {
5069
5422
  finally {
5070
5423
  setLoadingRowIndex(null);
5071
5424
  }
5072
- }, [rows, onChange, apiAdapter, apiConfig, translate]);
5425
+ }, [rows, onChange, apiAdapter, apiConfig, translate, isSectionEditMode]);
5073
5426
  // Get cell value (from editing state or row data)
5074
5427
  const getCellValue = useCallback((rowIndex, columnKey) => {
5075
- // When section is in edit mode, use row data directly
5076
- if (isSectionEditMode) {
5077
- return rows[rowIndex]?.[columnKey];
5078
- }
5428
+ // When a specific row is being edited (either in section edit mode or normal mode)
5079
5429
  if (editingState && editingState.rowIndex === rowIndex) {
5430
+ // Check Redux store first for most up-to-date value
5431
+ const cellWidgetId = `${widgetConfig['widget-id']}-row-${rowIndex}-col-${columnKey}`;
5432
+ const storeValue = storeValues[cellWidgetId];
5433
+ if (storeValue !== undefined) {
5434
+ return storeValue;
5435
+ }
5080
5436
  return editingState.currentValue[columnKey];
5081
5437
  }
5082
5438
  if (isAdding && rowIndex === rows.length) {
5083
5439
  return newRowData?.[columnKey];
5084
5440
  }
5085
5441
  return rows[rowIndex]?.[columnKey];
5086
- }, [editingState, isAdding, rows, newRowData, isSectionEditMode]);
5442
+ }, [editingState, isAdding, rows, newRowData, widgetConfig, storeValues]);
5087
5443
  // Get formatted display value for a cell
5088
5444
  const getDisplayValue = useCallback((rowIndex, column) => {
5089
5445
  const columnKey = column['column-key'];
5090
5446
  const cellValue = getCellValue(rowIndex, columnKey);
5447
+ const widgetType = column.widget || 'text';
5091
5448
  if (cellValue === null || cellValue === undefined || cellValue === '') {
5092
5449
  return '-';
5093
5450
  }
5451
+ // For select widgets, we'll use SelectDisplayValue component instead
5452
+ // This function is kept for other widget types
5453
+ if (widgetType === 'select') {
5454
+ return null; // Will be handled by SelectDisplayValue component
5455
+ }
5094
5456
  // Use formatValue if format config exists
5095
5457
  if (column['widget-data-format']) {
5096
5458
  return formatValue(cellValue, column['widget-data-format'], column.widget);
@@ -5098,29 +5460,22 @@ const TableWidget = ({ config }) => {
5098
5460
  return cellValue?.toString() || '-';
5099
5461
  }, [getCellValue]);
5100
5462
  // Check if row is being edited
5101
- // When section is in edit mode, all rows are editable
5463
+ // In section edit mode, only the row with active editingState is editable
5102
5464
  const isRowEditing = useCallback((rowIndex) => {
5103
- if (isSectionEditMode) {
5104
- return true; // All rows are editable when section is in edit mode
5105
- }
5106
5465
  return editingState?.rowIndex === rowIndex || (isAdding && rowIndex === rows.length);
5107
- }, [editingState, isAdding, rows.length, isSectionEditMode]);
5108
- // Set cell widget value in Redux when entering edit mode
5466
+ }, [editingState, isAdding, rows.length]);
5467
+ // Store original rows when entering section edit mode (for edit_action tracking)
5109
5468
  useEffect(() => {
5110
- if (isSectionEditMode) {
5111
- // When section is in edit mode, set values for all rows
5112
- rows.forEach((row, rowIndex) => {
5113
- columns.forEach((col) => {
5114
- const columnKey = col['column-key'];
5115
- const cellWidgetId = `${widgetConfig['widget-id']}-row-${rowIndex}-col-${columnKey}`;
5116
- const cellValue = row[columnKey];
5117
- const defaultValue = cellValue !== undefined ? cellValue : (col['widget-data-default'] ?? '');
5118
- // Set value in Redux store
5119
- dispatch(setValue({ widgetId: cellWidgetId, value: defaultValue }));
5120
- });
5121
- });
5469
+ if (isSectionEditMode && originalRows === null) {
5470
+ setOriginalRows(JSON.parse(JSON.stringify(rows))); // Deep clone
5471
+ }
5472
+ else if (!isSectionEditMode && originalRows !== null) {
5473
+ setOriginalRows(null);
5122
5474
  }
5123
- else if (editingState) {
5475
+ }, [isSectionEditMode, rows, originalRows]);
5476
+ // Set cell widget value in Redux when entering edit mode for a specific row
5477
+ useEffect(() => {
5478
+ if (editingState) {
5124
5479
  columns.forEach((col) => {
5125
5480
  const columnKey = col['column-key'];
5126
5481
  const cellWidgetId = `${widgetConfig['widget-id']}-row-${editingState.rowIndex}-col-${columnKey}`;
@@ -5130,7 +5485,7 @@ const TableWidget = ({ config }) => {
5130
5485
  dispatch(setValue({ widgetId: cellWidgetId, value: defaultValue }));
5131
5486
  });
5132
5487
  }
5133
- }, [isSectionEditMode, editingState, columns, widgetConfig, dispatch, rows]);
5488
+ }, [editingState, columns, widgetConfig, dispatch]);
5134
5489
  useEffect(() => {
5135
5490
  if (isAdding && newRowData) {
5136
5491
  columns.forEach((col) => {
@@ -5177,18 +5532,48 @@ const TableWidget = ({ config }) => {
5177
5532
  } }) }));
5178
5533
  }, [widgetConfig, updateCellValue]);
5179
5534
  // Render cell content (widget in edit mode, formatted value in view mode)
5180
- const renderCell = useCallback((rowIndex, column) => {
5535
+ const renderCell = useCallback((rowIndex, column, row) => {
5181
5536
  const columnKey = column['column-key'];
5182
5537
  const isEditing = isRowEditing(rowIndex);
5183
5538
  const cellValue = getCellValue(rowIndex, columnKey);
5184
5539
  const columnReadonly = column['widget-readonly'] === true;
5540
+ // Get color styling based on edit_action
5541
+ const getCellStyle = () => {
5542
+ if (isEditing)
5543
+ return {}; // No special styling when editing
5544
+ const editAction = row?.edit_action;
5545
+ if (editAction === 'ADD') {
5546
+ return { color: '#16a34a' }; // green-600
5547
+ }
5548
+ else if (editAction === 'DELETE') {
5549
+ return { color: '#dc2626', textDecoration: 'line-through' }; // red-600 with strikethrough
5550
+ }
5551
+ else if (editAction === 'UPDATE') {
5552
+ return { color: '#ea580c' }; // orange-600
5553
+ }
5554
+ return {};
5555
+ };
5185
5556
  if (isEditing) {
5186
5557
  // Use lightweight cell renderer
5187
5558
  return renderTableCell(rowIndex, column, cellValue, columnReadonly);
5188
5559
  }
5189
5560
  else {
5190
- // Display formatted value in view mode
5191
- return (jsxRuntimeExports.jsx("div", { className: "text-sm text-gray-900", children: getDisplayValue(rowIndex, column) }));
5561
+ // Display formatted value in view mode with color styling
5562
+ const widgetType = column.widget || 'text';
5563
+ const displayValue = getDisplayValue(rowIndex, column);
5564
+ // For select widgets, use SelectDisplayValue component to show label
5565
+ if (widgetType === 'select' && displayValue === null) {
5566
+ const cellWidgetId = `${widgetConfig['widget-id']}-row-${rowIndex}-col-${columnKey}`;
5567
+ const cellConfig = {
5568
+ ...column,
5569
+ 'widget-id': cellWidgetId,
5570
+ 'widget-label': '',
5571
+ 'widget-readonly': true,
5572
+ 'widget-data-path': undefined,
5573
+ };
5574
+ return (jsxRuntimeExports.jsx("div", { className: "text-sm", style: getCellStyle(), children: jsxRuntimeExports.jsx(SelectDisplayValue, { config: cellConfig, value: cellValue }) }));
5575
+ }
5576
+ return (jsxRuntimeExports.jsx("div", { className: "text-sm", style: getCellStyle(), children: displayValue }));
5192
5577
  }
5193
5578
  }, [isRowEditing, getCellValue, getDisplayValue, renderTableCell]);
5194
5579
  const tableWidgetId = `table-widget-${widgetConfig['widget-id']}`;
@@ -5248,11 +5633,13 @@ const TableWidget = ({ config }) => {
5248
5633
  .${tableWidgetId} button {
5249
5634
  border-radius: 10px !important;
5250
5635
  }
5251
- ` }), jsxRuntimeExports.jsxs("div", { className: `table-widget-container ${tableWidgetId}`, children: [confirmationState?.show && (jsxRuntimeExports.jsx("div", { className: "fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50", children: jsxRuntimeExports.jsxs("div", { className: "bg-white rounded-lg p-6 max-w-md w-full mx-4", children: [jsxRuntimeExports.jsx("h3", { className: "text-lg font-semibold mb-4", children: translate('table.confirm') || 'Confirm Action' }), jsxRuntimeExports.jsx("p", { className: "text-gray-700 mb-6", children: confirmationState.message }), jsxRuntimeExports.jsxs("div", { className: "flex justify-end gap-3", children: [jsxRuntimeExports.jsx("button", { onClick: confirmationState.onCancel, className: "px-4 py-2 text-sm font-medium text-gray-700 bg-gray-200 hover:bg-gray-300", style: { borderRadius: '15px' }, children: translate('common.cancel') || 'Cancel' }), jsxRuntimeExports.jsx("button", { onClick: confirmationState.onConfirm, className: "px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700", style: { borderRadius: '15px' }, children: translate('table.discard') || 'Discard & Continue' })] })] }) })), operations.add && !isReadonly && isEnabled && (jsxRuntimeExports.jsx("div", { className: "flex justify-end mb-2", children: jsxRuntimeExports.jsx("button", { type: "button", onClick: startAdd, disabled: loadingRowIndex !== null, className: "px-3 py-1 text-sm bg-blue-500 text-white hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed", style: { borderRadius: '15px' }, children: translate('table.addRecord') || 'Add New Record' }) })), rows.length === 0 && !isAdding ? (jsxRuntimeExports.jsxs("div", { className: "text-gray-500 text-sm py-4 text-center border border-gray-300", style: { borderRadius: '15px' }, children: [translate('table.noData') || 'No records available.', operations.add && !isReadonly && ` ${translate('table.clickToAdd') || 'Click "Add New Record" to add one.'}`] })) : (jsxRuntimeExports.jsx("div", { className: "overflow-x-auto border border-gray-300", style: { borderRadius: '15px' }, children: jsxRuntimeExports.jsxs("table", { className: "min-w-full divide-y divide-gray-200", children: [jsxRuntimeExports.jsx("thead", { className: "bg-gray-50", children: jsxRuntimeExports.jsxs("tr", { children: [columns.map((col) => (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: translateConfig(col['widget-label']) }, col['column-key']))), ((operations.edit || operations.remove) && !isReadonly) || isAnyRowEditing ? (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: translate('common.actions') || 'Actions' })) : null] }) }), jsxRuntimeExports.jsxs("tbody", { className: "bg-white divide-y divide-gray-200", children: [rows.map((row, rowIndex) => {
5636
+ ` }), jsxRuntimeExports.jsxs("div", { className: `table-widget-container ${tableWidgetId}`, children: [confirmationState?.show && (jsxRuntimeExports.jsx("div", { className: "fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50", children: jsxRuntimeExports.jsxs("div", { className: "bg-white rounded-lg p-6 max-w-md w-full mx-4", children: [jsxRuntimeExports.jsx("h3", { className: "text-lg font-semibold mb-4", children: translate('table.confirm') || 'Confirm Action' }), jsxRuntimeExports.jsx("p", { className: "text-gray-700 mb-6", children: confirmationState.message }), jsxRuntimeExports.jsxs("div", { className: "flex justify-end gap-3", children: [jsxRuntimeExports.jsx("button", { onClick: confirmationState.onCancel, className: "px-4 py-2 text-sm font-medium text-gray-700 bg-gray-200 hover:bg-gray-300", style: { borderRadius: '15px' }, children: translate('common.cancel') || 'Cancel' }), jsxRuntimeExports.jsx("button", { onClick: confirmationState.onConfirm, className: "px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700", style: { borderRadius: '15px' }, children: translate('table.discard') || 'Discard & Continue' })] })] }) })), operations.add && !isReadonly && isEnabled && (isSectionEditMode || !isAnyRowEditing) && (jsxRuntimeExports.jsx("div", { className: "flex justify-end mb-2", children: jsxRuntimeExports.jsx("button", { type: "button", onClick: startAdd, disabled: loadingRowIndex !== null, className: "px-3 py-1 text-sm bg-blue-500 text-white hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed", style: { borderRadius: '15px' }, children: translate('table.addRecord') || 'Add New Record' }) })), rows.length === 0 && !isAdding ? (jsxRuntimeExports.jsxs("div", { className: "text-gray-500 text-sm py-4 text-center border border-gray-300", style: { borderRadius: '15px' }, children: [translate('table.noData') || 'No records available.', operations.add && !isReadonly && ` ${translate('table.clickToAdd') || 'Click "Add New Record" to add one.'}`] })) : (jsxRuntimeExports.jsx("div", { className: "overflow-x-auto border border-gray-300", style: { borderRadius: '15px' }, children: jsxRuntimeExports.jsxs("table", { className: "min-w-full divide-y divide-gray-200", children: [jsxRuntimeExports.jsx("thead", { className: "bg-gray-50", children: jsxRuntimeExports.jsxs("tr", { children: [columns.map((col) => (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: translateConfig(col['widget-label']) }, col['column-key']))), ((operations.edit || operations.remove) && !isReadonly) || isAnyRowEditing || isSectionEditMode ? (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: translate('common.actions') || 'Actions' })) : null] }) }), jsxRuntimeExports.jsxs("tbody", { className: "bg-white divide-y divide-gray-200", children: [rows.map((row, rowIndex) => {
5252
5637
  const isEditing = isRowEditing(rowIndex);
5253
5638
  const isLoading = loadingRowIndex === rowIndex;
5254
- return (jsxRuntimeExports.jsxs("tr", { className: isEditing ? 'bg-blue-50' : isLoading ? 'opacity-50' : '', children: [columns.map((col) => (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: renderCell(rowIndex, col) }, col['column-key']))), ((operations.edit || operations.remove) && !isReadonly) || isEditing ? (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", style: { minWidth: '120px' }, children: isEditing && !isSectionEditMode ? (
5255
- // Show Save/Cancel buttons only for manual row editing (not section edit mode)
5639
+ return (jsxRuntimeExports.jsxs("tr", { className: isEditing ? 'bg-blue-50' : isLoading ? 'opacity-50' : row.edit_action === 'DELETE' ? 'bg-red-50' : '', children: [columns.map((col) => {
5640
+ return (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: renderCell(rowIndex, col, row) }, col['column-key']));
5641
+ }), ((operations.edit || operations.remove) && !isReadonly) || isEditing || isSectionEditMode ? (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", style: { minWidth: '120px' }, children: isEditing ? (
5642
+ // Show OK (Save)/Cancel buttons when row is being edited (works in both section edit mode and normal mode)
5256
5643
  jsxRuntimeExports.jsxs("div", { className: "flex flex-row gap-2 items-center", style: { width: '100%' }, children: [jsxRuntimeExports.jsx("button", { type: "button", onClick: saveEdit, disabled: isLoading, className: "px-3 py-1 text-xs font-medium hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed whitespace-nowrap flex-shrink-0", style: {
5257
5644
  display: 'inline-block',
5258
5645
  minWidth: '60px',
@@ -5260,12 +5647,10 @@ const TableWidget = ({ config }) => {
5260
5647
  color: '#ffffff', // white text
5261
5648
  border: 'none',
5262
5649
  borderRadius: '15px'
5263
- }, children: "Save" }), jsxRuntimeExports.jsx("button", { type: "button", onClick: cancelEdit, disabled: isLoading, className: "px-3 py-1 text-xs font-medium bg-gray-200 text-gray-700 hover:bg-gray-300 disabled:opacity-50 disabled:cursor-not-allowed whitespace-nowrap flex-shrink-0", style: { display: 'inline-block', minWidth: '60px', borderRadius: '15px' }, children: "Cancel" })] })) : !isSectionEditMode ? (
5264
- // Show Edit/Delete buttons only when not in section edit mode
5265
- jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [operations.edit && (jsxRuntimeExports.jsx("button", { type: "button", onClick: () => startEdit(rowIndex), disabled: isAnyRowEditing || isLoading, className: "px-3 py-1 text-xs text-blue-600 hover:text-blue-800 hover:bg-blue-50 disabled:opacity-50 disabled:cursor-not-allowed", style: { borderRadius: '15px' }, children: translate('common.edit') || 'Edit' })), operations.remove && (jsxRuntimeExports.jsx("button", { type: "button", onClick: () => deleteRow(rowIndex), disabled: isAnyRowEditing || isLoading, className: "px-3 py-1 text-xs text-red-600 hover:text-red-800 hover:bg-red-50 disabled:opacity-50 disabled:cursor-not-allowed", style: { borderRadius: '15px' }, children: translate('common.remove') || 'Delete' }))] })) : (
5266
- // In section edit mode, show only Delete button if remove is enabled
5267
- operations.remove && (jsxRuntimeExports.jsx("div", { className: "flex gap-2", children: jsxRuntimeExports.jsx("button", { type: "button", onClick: () => deleteRow(rowIndex), disabled: isLoading, className: "px-3 py-1 text-xs text-red-600 hover:text-red-800 hover:bg-red-50 disabled:opacity-50 disabled:cursor-not-allowed", style: { borderRadius: '15px' }, children: translate('common.remove') || 'Delete' }) }))) })) : null] }, rowIndex));
5268
- }), isAdding && newRowData && (jsxRuntimeExports.jsxs("tr", { className: "bg-blue-50", children: [columns.map((col) => (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: renderCell(rows.length, col) }, col['column-key']))), jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [jsxRuntimeExports.jsx("button", { type: "button", onClick: saveAdd, disabled: loadingRowIndex === -1, className: "px-3 py-1 text-xs bg-green-600 text-white hover:bg-green-700 disabled:opacity-50", style: { borderRadius: '15px' }, children: translate('common.save') || 'Save' }), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => {
5650
+ }, children: translate('common.ok') || 'OK' }), jsxRuntimeExports.jsx("button", { type: "button", onClick: cancelEdit, disabled: isLoading, className: "px-3 py-1 text-xs font-medium bg-gray-200 text-gray-700 hover:bg-gray-300 disabled:opacity-50 disabled:cursor-not-allowed whitespace-nowrap flex-shrink-0", style: { display: 'inline-block', minWidth: '60px', borderRadius: '15px' }, children: translate('common.cancel') || 'Cancel' })] })) : (
5651
+ // Show Edit/Delete buttons when row is not being edited
5652
+ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [operations.edit && (jsxRuntimeExports.jsx("button", { type: "button", onClick: () => startEdit(rowIndex), disabled: isAnyRowEditing || isLoading, className: "px-3 py-1 text-xs text-blue-600 hover:text-blue-800 hover:bg-blue-50 disabled:opacity-50 disabled:cursor-not-allowed", style: { borderRadius: '15px' }, children: translate('common.edit') || 'Edit' })), operations.remove && (jsxRuntimeExports.jsx("button", { type: "button", onClick: () => deleteRow(rowIndex), disabled: isAnyRowEditing || isLoading, className: "px-3 py-1 text-xs text-red-600 hover:text-red-800 hover:bg-red-50 disabled:opacity-50 disabled:cursor-not-allowed", style: { borderRadius: '15px' }, children: translate('common.remove') || 'Delete' }))] })) })) : null] }, rowIndex));
5653
+ }), isAdding && newRowData && (jsxRuntimeExports.jsxs("tr", { className: "bg-blue-50", children: [columns.map((col) => (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: renderCell(rows.length, col, { ...newRowData, edit_action: 'ADD' }) }, col['column-key']))), jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [jsxRuntimeExports.jsx("button", { type: "button", onClick: saveAdd, disabled: loadingRowIndex === -1, className: "px-3 py-1 text-xs bg-green-600 text-white hover:bg-green-700 disabled:opacity-50", style: { borderRadius: '15px' }, children: translate('common.save') || 'Save' }), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => {
5269
5654
  setIsAdding(false);
5270
5655
  setNewRowData(null);
5271
5656
  }, disabled: loadingRowIndex === -1, className: "px-3 py-1 text-xs bg-gray-200 text-gray-700 hover:bg-gray-300 disabled:opacity-50", style: { borderRadius: '15px' }, children: translate('common.cancel') || 'Cancel' })] }) })] }))] })] }) })), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }));