@openg2p/registry-widgets 0.1.4 → 0.1.5

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.
package/dist/index.js CHANGED
@@ -951,32 +951,63 @@ const useBaseWidget = (options) => {
951
951
  if (isLayoutWidget) {
952
952
  return undefined; // Layout widgets don't have values
953
953
  }
954
+ // Helper to extract displayable value from object (especially geo hierarchy objects)
955
+ const extractValueFromObject = (obj) => {
956
+ if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
957
+ return obj;
958
+ }
959
+ // Check for geo hierarchy structure first
960
+ if ('geo_code_hierarchy_json' in obj || 'geo_lowest_level_value_id' in obj) {
961
+ if ('geo_lowest_level_value_id' in obj) {
962
+ return obj.geo_lowest_level_value_id;
963
+ }
964
+ // If it's a geo hierarchy object but no extractable ID, return undefined to avoid rendering object
965
+ return undefined;
966
+ }
967
+ // Try common value fields
968
+ if ('value' in obj) {
969
+ return obj.value;
970
+ }
971
+ if ('id' in obj) {
972
+ return obj.id;
973
+ }
974
+ if ('label' in obj) {
975
+ return obj.label;
976
+ }
977
+ if ('name' in obj) {
978
+ return obj.name;
979
+ }
980
+ // If no extractable value found, return undefined to avoid rendering object as React child
981
+ // This prevents "Objects are not valid as a React child" errors
982
+ return undefined;
983
+ };
954
984
  // Try to get value from widgetId first (this should have the actual selected value)
955
985
  // For geo widgets with dataPath, widgetId stores the actual ID, while dataPath stores the hierarchy object
956
986
  let value = values[widgetId];
987
+ // Extract value if it's an object (handles geo hierarchy objects stored in widgetId)
988
+ if (value !== null && value !== undefined && typeof value === 'object' && !Array.isArray(value)) {
989
+ value = extractValueFromObject(value);
990
+ }
957
991
  // If widgetId doesn't have a value, try dataPath
958
992
  if (value === undefined && config['widget-data-path']) {
959
993
  value = getWidgetValue(values, config['widget-data-path'], widgetId);
960
- // CRITICAL: For geo widgets with dataPath, the value might be stored as a hierarchy object
961
- // Extract the actual value (geo_lowest_level_value_id) if it's an object
962
- const geoConfig = config['widget-geo-config'];
963
- if (geoConfig?.isLastLevel && value && typeof value === 'object' && !Array.isArray(value)) {
964
- // If it's a geo hierarchy object, extract the actual value
965
- if ('geo_lowest_level_value_id' in value) {
966
- value = value.geo_lowest_level_value_id;
967
- }
968
- else if ('value' in value) {
969
- value = value.value;
970
- }
971
- else if ('id' in value) {
972
- value = value.id;
973
- }
994
+ // Extract value if it's an object (handles geo hierarchy objects from dataPath)
995
+ if (value !== null && value !== undefined && typeof value === 'object' && !Array.isArray(value)) {
996
+ value = extractValueFromObject(value);
974
997
  }
975
998
  }
976
999
  // If value is still undefined and user has set a value, try reading from widgetId as backup
977
1000
  // This handles cases where dataPath lookup might fail temporarily
978
1001
  if (value === undefined && userHasSetValueRef.current && values[widgetId] !== undefined) {
979
1002
  value = values[widgetId];
1003
+ // Extract value if it's an object (handles geo hierarchy objects)
1004
+ if (value !== null && value !== undefined && typeof value === 'object' && !Array.isArray(value)) {
1005
+ value = extractValueFromObject(value);
1006
+ }
1007
+ }
1008
+ // Final safety check: if value is still an object, extract displayable value
1009
+ if (value !== null && value !== undefined && typeof value === 'object' && !Array.isArray(value)) {
1010
+ value = extractValueFromObject(value);
980
1011
  }
981
1012
  // If user has explicitly set a value, always return it (even if undefined/null)
982
1013
  // This prevents the default from overwriting user selections
@@ -2246,7 +2277,7 @@ const WidgetProvider = ({ store, dataSourceRequestHandler, schemaData, translate
2246
2277
  widgetStore.dispatch(setValues(schemaData));
2247
2278
  }
2248
2279
  // eslint-disable-next-line react-hooks/exhaustive-deps
2249
- }, []); // Only run on mount
2280
+ }, [schemaData, widgetStore]); // Only run on mount
2250
2281
  const content = (jsxRuntimeExports.jsx(reactRedux.Provider, { store: widgetStore, children: jsxRuntimeExports.jsx(WidgetContext.Provider, { value: contextValue, children: jsxRuntimeExports.jsx(WidgetEventBusContext.Provider, { value: eventBus, children: children }) }) }));
2251
2282
  return content;
2252
2283
  };
@@ -2256,8 +2287,11 @@ const WidgetRenderer = ({ config, dataSourceRequestHandler: propDataSourceReques
2256
2287
  const context = useWidgetContext();
2257
2288
  const dataSourceRequestHandler = propDataSourceRequestHandler || context.dataSourceRequestHandler;
2258
2289
  const schemaData = propSchemaData || context.schemaData;
2290
+ // Warn if dataSourceRequestHandler is missing for API data sources, but don't break rendering
2291
+ // This allows widgets to render in read-only or static modes (e.g., CRView)
2259
2292
  if (!dataSourceRequestHandler && config['widget-data-source']?.type === 'api') {
2260
- console.error(`[WidgetRenderer] dataSourceRequestHandler is required for widget ${config['widget-id']} with API data source`);
2293
+ console.warn(`[WidgetRenderer] dataSourceRequestHandler is not provided for widget ${config['widget-id']} with API data source. ` +
2294
+ `The widget will render but API data source functionality will be disabled.`);
2261
2295
  }
2262
2296
  // Get values from Redux for cascade hooks
2263
2297
  const values = reactRedux.useSelector((state) => state.widget.values);
@@ -3193,7 +3227,7 @@ const namespaceSectionConfig = (section, namespace) => {
3193
3227
  * - Panels wrap when they exceed available width
3194
3228
  * - Sections can sit side-by-side if there's space
3195
3229
  */
3196
- const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequestHandler, schemaData, onValueChange, gridColumnSpan, onSectionSave, hideEditButton = false, mode = 'RegistryView', namespace, }) => {
3230
+ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequestHandler, schemaData, onValueChange, gridColumnSpan, onSectionSave, hideEditButton = false, mode = 'RegistryView', namespace, changeRequestType, showChangeRequestLabel = true, }) => {
3197
3231
  const { translateConfig, translate } = useWidgetTranslation();
3198
3232
  const { schemaData: contextSchemaData, dataSourceRequestHandler: contextDataSourceRequestHandler } = useWidgetContext();
3199
3233
  const store = reactRedux.useStore();
@@ -3811,11 +3845,12 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
3811
3845
  align-items: center;
3812
3846
  gap: 0.5rem;
3813
3847
  }
3814
- ` }), jsxRuntimeExports.jsxs("div", { ref: sectionRef, className: `section ${sectionClassId} px-4 sm:px-6 lg:px-8 border-2 border-white `, "data-section-id": sectionId, "data-has-table": hasTableWidget ? 'true' : 'false', "data-has-explicit-span": hasExplicitTableSpan ? 'true' : 'false', "data-edit-mode": isEditMode ? 'true' : 'false', "data-column-span": columnSpan, style: {
3848
+ ` }), jsxRuntimeExports.jsxs("div", { ref: sectionRef, className: `section ${sectionClassId} px-4 sm:px-6 lg:px-8 border-2 border-white `, "data-section-id": sectionId, "data-has-table": hasTableWidget ? 'true' : 'false', "data-has-explicit-span": hasExplicitTableSpan ? 'true' : 'false', "data-edit-mode": isEditMode ? 'true' : 'false', "data-column-span": columnSpan, "data-change-request-type": changeRequestType, style: {
3815
3849
  gridColumn: `span ${columnSpan}`,
3816
3850
  width: '100%',
3817
3851
  borderRadius: '10px',
3818
- backgroundColor: '#FFFFFF',
3852
+ backgroundColor: changeRequestType === 'old' ? '#F9F9F9' : '#FFFFFF', // Faded background for old change requests
3853
+ opacity: changeRequestType === 'old' ? 0.95 : 1, // Slight opacity reduction for old sections
3819
3854
  ...(isEditMode && sectionHeight ? {
3820
3855
  height: `${sectionHeight}px`,
3821
3856
  minHeight: `${sectionHeight}px`
@@ -3824,7 +3859,27 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
3824
3859
  minHeight: 'auto',
3825
3860
  height: 'auto'
3826
3861
  }),
3827
- }, 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, dataSourceRequestHandler: dataSourceRequestHandler, 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: {
3862
+ }, children: [sectionToRender['section-title'] && (jsxRuntimeExports.jsxs("div", { style: {
3863
+ marginTop: '35px',
3864
+ marginBottom: '16px',
3865
+ display: 'flex',
3866
+ alignItems: 'center',
3867
+ gap: '12px',
3868
+ flexWrap: 'wrap',
3869
+ }, children: [jsxRuntimeExports.jsx("h2", { className: "text-xl font-semibold", style: { margin: 0 }, children: translateConfig(sectionToRender['section-title']) }), mode === 'CRView' && changeRequestType && showChangeRequestLabel && (jsxRuntimeExports.jsx("span", { style: {
3870
+ display: 'inline-flex',
3871
+ alignItems: 'center',
3872
+ padding: '4px 12px',
3873
+ borderRadius: '4px',
3874
+ fontSize: '12px',
3875
+ fontWeight: 600,
3876
+ textTransform: 'uppercase',
3877
+ letterSpacing: '0.5px',
3878
+ backgroundColor: changeRequestType === 'new' ? '#28a745' : '#ffcccc', // Green for new, faded red for old
3879
+ color: changeRequestType === 'new' ? '#FFFFFF' : '#cc0000',
3880
+ whiteSpace: 'nowrap',
3881
+ boxShadow: changeRequestType === 'new' ? '0 2px 4px rgba(40, 167, 69, 0.3)' : 'none',
3882
+ }, children: changeRequestType === 'new' ? 'New' : 'Old' }))] })), 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, dataSourceRequestHandler: dataSourceRequestHandler, 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: {
3828
3883
  marginTop: '20px',
3829
3884
  paddingBottom: '30px',
3830
3885
  display: 'flex',
@@ -7397,8 +7452,41 @@ const CurrencyInputWidget = ({ config }) => {
7397
7452
  const DisplayWidget = ({ config }) => {
7398
7453
  const { value, formattedValue, config: widgetConfig, } = useBaseWidget({ config });
7399
7454
  const { translateConfig } = useWidgetTranslation();
7400
- // Use formatted value if available, otherwise raw value
7401
- const displayValue = formattedValue !== undefined ? formattedValue : (value || '');
7455
+ // Helper to safely convert value to display string
7456
+ const getDisplayValue = (val) => {
7457
+ if (val === null || val === undefined) {
7458
+ return '';
7459
+ }
7460
+ // If it's already a string or number, return as string
7461
+ if (typeof val === 'string' || typeof val === 'number') {
7462
+ return String(val);
7463
+ }
7464
+ // If it's an object (like geo hierarchy), try to extract a meaningful value
7465
+ if (typeof val === 'object' && !Array.isArray(val)) {
7466
+ // For geo hierarchy objects, try to extract the actual value
7467
+ if ('geo_lowest_level_value_id' in val) {
7468
+ return String(val.geo_lowest_level_value_id || '');
7469
+ }
7470
+ if ('value' in val) {
7471
+ return String(val.value || '');
7472
+ }
7473
+ if ('id' in val) {
7474
+ return String(val.id || '');
7475
+ }
7476
+ // If no extractable value, return empty string to avoid rendering object
7477
+ return '';
7478
+ }
7479
+ // For arrays, join them or return empty
7480
+ if (Array.isArray(val)) {
7481
+ return val.length > 0 ? val.map(String).join(', ') : '';
7482
+ }
7483
+ // Fallback: convert to string
7484
+ return String(val);
7485
+ };
7486
+ // Use formatted value if available, otherwise safely convert raw value
7487
+ const displayValue = formattedValue !== undefined
7488
+ ? (typeof formattedValue === 'object' ? getDisplayValue(formattedValue) : String(formattedValue))
7489
+ : getDisplayValue(value);
7402
7490
  const label = translateConfig(widgetConfig['widget-label']);
7403
7491
  // If no label, render as paragraph text
7404
7492
  if (!label || label.trim() === '') {