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