@openg2p/registry-widgets 0.1.8 → 0.1.9

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
@@ -3835,8 +3835,6 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
3835
3835
  return [];
3836
3836
  const snapshot = {};
3837
3837
  let hasTable = false;
3838
- if (!sectionRegisterId)
3839
- return [];
3840
3838
  const resolvePath = (path) => (pathPrefix ? `${pathPrefix}.${path}` : path);
3841
3839
  widgets.forEach(widget => {
3842
3840
  const widgetPath = widget['widget-data-path'];
@@ -3866,9 +3864,11 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
3866
3864
  : fullPath;
3867
3865
  cleanedSnapshot[fieldPath] = value;
3868
3866
  });
3869
- const sectionData = pathPrefix
3870
- ? getValueByPath(sourceData, resolvePath(sectionRegisterId))
3871
- : sourceData[sectionRegisterId];
3867
+ const sectionData = sectionRegisterId
3868
+ ? (pathPrefix
3869
+ ? getValueByPath(sourceData, resolvePath(sectionRegisterId))
3870
+ : sourceData[sectionRegisterId])
3871
+ : {};
3872
3872
  return [
3873
3873
  {
3874
3874
  ...sectionData,
@@ -4020,7 +4020,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4020
4020
  if (JSON.stringify(oldSchemaData) !== JSON.stringify(newSchemaData)) {
4021
4021
  try {
4022
4022
  const sectionchanges = {
4023
- section_id: dbSectionId,
4023
+ section_id: dbSectionId ?? originalSection['section-id'],
4024
4024
  section_register_id: sectionRegisterId,
4025
4025
  records: [...newSchemaData],
4026
4026
  files: [...sectionFiles]
@@ -4035,49 +4035,52 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4035
4035
  };
4036
4036
  // IntakeForm: save section then collapse current and expand next (or stay on final section)
4037
4037
  const handleIntakeFormSave = React.useCallback(async () => {
4038
- if (!store || !onSectionSave || sectionIndex === undefined)
4039
- return;
4040
- const sectionWidgets = collectWidgets(originalSection.panels);
4041
- const currentState = store.getState().widget;
4042
- const currentSchemaData = currentState.values || {};
4043
- const isSectionValid = sectionValidate(originalSection, currentSchemaData, dispatch);
4044
- if (!isSectionValid)
4038
+ if (sectionIndex === undefined)
4045
4039
  return;
4046
- const oldSchemaData = schemaData || contextSchemaData;
4047
- const newSchemaData = trackSectionChages(sectionWidgets, currentSchemaData, namespace);
4048
- const sectionFiles = [];
4049
- if (hasSupportingDocuments) {
4050
- const originalSupportingDocuments = originalSection['section-supporting-documents'] || [];
4051
- originalSupportingDocuments.forEach((doc) => {
4052
- const originalDataPath = doc['document-data-path'];
4053
- const storeDataPath = namespace && originalDataPath
4054
- ? `${namespace}.${originalDataPath}`
4055
- : originalDataPath;
4056
- sectionFiles.push(getValueByPath(currentSchemaData, storeDataPath));
4057
- });
4058
- }
4059
- if (JSON.stringify(oldSchemaData) !== JSON.stringify(newSchemaData)) {
4060
- try {
4061
- await onSectionSave({
4062
- section_id: dbSectionId,
4063
- section_register_id: sectionRegisterId,
4064
- records: [...newSchemaData],
4065
- files: [...sectionFiles],
4040
+ // Only run save/validation logic when in draft mode and handlers are available
4041
+ if (isDraft !== false && store && onSectionSave) {
4042
+ const sectionWidgets = collectWidgets(originalSection.panels);
4043
+ const currentState = store.getState().widget;
4044
+ const currentSchemaData = currentState.values || {};
4045
+ const isSectionValid = sectionValidate(originalSection, currentSchemaData, dispatch);
4046
+ if (!isSectionValid)
4047
+ return;
4048
+ const oldSchemaData = schemaData || contextSchemaData;
4049
+ const newSchemaData = trackSectionChages(sectionWidgets, currentSchemaData, namespace);
4050
+ const sectionFiles = [];
4051
+ if (hasSupportingDocuments) {
4052
+ const originalSupportingDocuments = originalSection['section-supporting-documents'] || [];
4053
+ originalSupportingDocuments.forEach((doc) => {
4054
+ const originalDataPath = doc['document-data-path'];
4055
+ const storeDataPath = namespace && originalDataPath
4056
+ ? `${namespace}.${originalDataPath}`
4057
+ : originalDataPath;
4058
+ sectionFiles.push(getValueByPath(currentSchemaData, storeDataPath));
4066
4059
  });
4067
4060
  }
4068
- catch (error) {
4069
- console.error('Section Changes Save failed', error);
4070
- return;
4061
+ if (JSON.stringify(oldSchemaData) !== JSON.stringify(newSchemaData)) {
4062
+ try {
4063
+ await onSectionSave({
4064
+ section_id: dbSectionId ?? originalSection['section-id'],
4065
+ section_register_id: sectionRegisterId,
4066
+ records: [...newSchemaData],
4067
+ files: [...sectionFiles],
4068
+ });
4069
+ }
4070
+ catch (error) {
4071
+ console.error('Section Changes Save failed', error);
4072
+ return;
4073
+ }
4071
4074
  }
4075
+ if (mode === 'IntakeForm') {
4076
+ baselineSnapshotRef.current = buildSectionSnapshot(currentSchemaData, namespace);
4077
+ setIntakeFormBaselineTrigger((prev) => prev + 1);
4078
+ }
4079
+ onSectionDirtyChange?.(sectionId, false);
4072
4080
  }
4073
- // IntakeForm only: update baseline so section is no longer dirty; trigger re-render so badge updates to "Saved"
4074
- if (mode === 'IntakeForm') {
4075
- baselineSnapshotRef.current = buildSectionSnapshot(currentSchemaData, namespace);
4076
- setIntakeFormBaselineTrigger((prev) => prev + 1);
4077
- }
4078
- onSectionDirtyChange?.(sectionId, false);
4081
+ // Always navigate to the next section
4079
4082
  onSectionSaveSuccess?.(sectionIndex);
4080
- }, [store, onSectionSave, onSectionSaveSuccess, sectionIndex, originalSection, schemaData, contextSchemaData, namespace, hasSupportingDocuments, dbSectionId, sectionRegisterId, dispatch, buildSectionSnapshot, sectionId, onSectionDirtyChange, mode]);
4083
+ }, [store, onSectionSave, onSectionSaveSuccess, sectionIndex, originalSection, schemaData, contextSchemaData, namespace, hasSupportingDocuments, dbSectionId, sectionRegisterId, dispatch, buildSectionSnapshot, sectionId, onSectionDirtyChange, mode, isDraft]);
4081
4084
  // Handle cancel button click
4082
4085
  const handleCancel = () => {
4083
4086
  // Revert values in store to original schema data
@@ -4398,7 +4401,7 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4398
4401
  gap: '12px',
4399
4402
  marginBottom: '20px',
4400
4403
  width: '100%',
4401
- }, children: [jsxRuntimeExports.jsxs("button", { type: "button", onClick: () => onPreviousSection?.(sectionIndex), disabled: sectionIndex === 0, className: "intake-form-prev-btn", style: {
4404
+ }, children: [typeof sectionIndex === 'number' && sectionIndex > 0 && (jsxRuntimeExports.jsxs("button", { type: "button", onClick: () => onPreviousSection?.(sectionIndex), className: "intake-form-prev-btn", style: {
4402
4405
  fontFamily: 'Roboto, sans-serif',
4403
4406
  fontSize: '14px',
4404
4407
  fontWeight: 400,
@@ -4406,25 +4409,25 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
4406
4409
  borderRadius: '10px',
4407
4410
  border: '1px solid #FD8C3E',
4408
4411
  background: '#F3F4F6',
4409
- color: sectionIndex === 0 ? 'rgba(0, 0, 0, 0.3)' : 'rgba(0, 0, 0, 0.5)',
4410
- cursor: sectionIndex === 0 ? 'not-allowed' : 'pointer',
4412
+ color: 'rgba(0, 0, 0, 0.5)',
4413
+ cursor: 'pointer',
4411
4414
  display: 'inline-flex',
4412
4415
  alignItems: 'center',
4413
4416
  gap: '8px',
4414
- }, children: [jsxRuntimeExports.jsx("img", { src: img$5, alt: "", "aria-hidden": true, style: { width: '14px', height: '14px', opacity: sectionIndex === 0 ? 0.5 : 0.5 } }), translate('common.previous') || 'Prev'] }), jsxRuntimeExports.jsxs("button", { type: "button", onClick: handleIntakeFormSave, disabled: isDraft === false, className: "intake-form-save-btn", style: {
4417
+ }, children: [jsxRuntimeExports.jsx("img", { src: img$5, alt: "", "aria-hidden": true, style: { width: '14px', height: '14px', opacity: 0.5 } }), translate('common.previous') || 'Prev'] })), jsxRuntimeExports.jsxs("button", { type: "button", onClick: handleIntakeFormSave, className: "intake-form-save-btn", style: {
4415
4418
  fontFamily: 'Roboto, sans-serif',
4416
4419
  fontSize: '14px',
4417
4420
  fontWeight: 400,
4418
4421
  padding: '8px 24px',
4419
4422
  borderRadius: '10px',
4420
4423
  border: '1px solid #FD8C3E',
4421
- background: isDraft === false ? '#9CA3AF' : '#F3F4F6',
4422
- color: isDraft === false ? 'rgba(0, 0, 0, 0.3)' : 'rgba(0, 0, 0, 0.5)',
4423
- cursor: isDraft === false ? 'not-allowed' : 'pointer',
4424
+ background: '#F3F4F6',
4425
+ color: 'rgba(0, 0, 0, 0.5)',
4426
+ cursor: 'pointer',
4424
4427
  display: 'inline-flex',
4425
4428
  alignItems: 'center',
4426
4429
  gap: '8px',
4427
- }, children: [translate('common.save') || 'Save', jsxRuntimeExports.jsx("img", { src: img$4, alt: "", "aria-hidden": true, style: { width: '14px', height: '14px' } })] })] })] }) }))] })) : (
4430
+ }, children: [translate('common.next') || 'Next', jsxRuntimeExports.jsx("img", { src: img$4, alt: "", "aria-hidden": true, style: { width: '14px', height: '14px' } })] })] })] }) }))] })) : (
4428
4431
  /* RegistryView / CRView: standard layout */
4429
4432
  jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [sectionToRender['section-title'] && (jsxRuntimeExports.jsxs("div", { style: {
4430
4433
  marginTop: '35px',
@@ -4681,7 +4684,9 @@ const SectionsContainer = ({ sections, dataSourceRequestHandler: propDataSourceR
4681
4684
  if (index + 1 < safeSections.length) {
4682
4685
  setExpandedSectionIndex(index + 1);
4683
4686
  }
4684
- // Last section: stay expanded (no action)
4687
+ else {
4688
+ setExpandedSectionIndex(null);
4689
+ }
4685
4690
  }, [safeSections.length]);
4686
4691
  // IntakeForm mode: called when Previous clicked - collapse current, expand previous
4687
4692
  const handlePreviousSection = React.useCallback((index) => {
@@ -8883,6 +8888,326 @@ const TextAreaWidget = ({ config }) => {
8883
8888
  }, children: charCounterText }))] }), hasError && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: errorMessage }))] })] }) }));
8884
8889
  };
8885
8890
 
8891
+ const DEFAULT_STATUS_COLORS = {
8892
+ active: '#16A34A',
8893
+ inactive: '#D97706',
8894
+ archived: '#6B7280',
8895
+ };
8896
+ // ── Hook: load data-source options for a single field ────────────
8897
+ // Options are loaded in both view and edit modes because view mode
8898
+ // needs them to resolve display labels (e.g. "active" → "Active").
8899
+ // For API data sources, loading is deferred to edit mode to avoid
8900
+ // unnecessary network calls when only labels are needed.
8901
+ function useFieldDataSource(fieldKey, fieldConfig, isReadonly) {
8902
+ const [options, setOptions] = React.useState([]);
8903
+ const { dataSourceRequestHandler, schemaData } = useWidgetContext();
8904
+ const values = reactRedux.useSelector((state) => state.widget.values);
8905
+ const dataSource = fieldConfig?.['data-source'];
8906
+ const dsType = dataSource?.type;
8907
+ const dsKey = dataSource
8908
+ ? `${fieldKey}-${dsType}-${JSON.stringify(dataSource)}`
8909
+ : '';
8910
+ React.useEffect(() => {
8911
+ if (!dataSource) {
8912
+ setOptions([]);
8913
+ return;
8914
+ }
8915
+ // API sources: only fetch when editable to avoid unnecessary calls
8916
+ if (dataSource.type === 'api' && isReadonly) {
8917
+ return;
8918
+ }
8919
+ let cancelled = false;
8920
+ const load = async () => {
8921
+ try {
8922
+ let raw = [];
8923
+ if (dataSource.type === 'static') {
8924
+ raw = getStaticDataSource(dataSource);
8925
+ }
8926
+ else if (dataSource.type === 'api') {
8927
+ if (!dataSourceRequestHandler)
8928
+ return;
8929
+ raw = await getApiDataSource(dataSource, values, dataSourceRequestHandler);
8930
+ }
8931
+ else if (dataSource.type === 'schema') {
8932
+ raw = getSchemaDataSource(dataSource, schemaData || {});
8933
+ }
8934
+ const transformed = transformDataSourceOptions(raw, dataSource.valueKey, dataSource.labelKey);
8935
+ if (!cancelled)
8936
+ setOptions(transformed);
8937
+ }
8938
+ catch (err) {
8939
+ console.error(`[HeaderSectionWidget] Error loading data-source for field "${fieldKey}":`, err);
8940
+ if (!cancelled)
8941
+ setOptions([]);
8942
+ }
8943
+ };
8944
+ load();
8945
+ return () => { cancelled = true; };
8946
+ // eslint-disable-next-line react-hooks/exhaustive-deps
8947
+ }, [dsKey, isReadonly, dataSourceRequestHandler]);
8948
+ return options;
8949
+ }
8950
+ // ── Main component ───────────────────────────────────────────────
8951
+ const HeaderSectionWidget = ({ config }) => {
8952
+ const { config: widgetConfig, getFieldValue, } = useBaseWidget({ config });
8953
+ const dispatch = reactRedux.useDispatch();
8954
+ const { translateConfig } = useWidgetTranslation();
8955
+ const { schemaData } = useWidgetContext();
8956
+ const values = reactRedux.useSelector((state) => state.widget.values);
8957
+ const isReadonly = widgetConfig['widget-readonly'] !== false;
8958
+ const dataPath = widgetConfig['widget-data-path'];
8959
+ // ── Per-field config map ──────────────────────────────────────
8960
+ const fieldConfigMap = React.useMemo(() => {
8961
+ return widgetConfig['widget-field-config'] || {};
8962
+ }, [widgetConfig['widget-field-config']]);
8963
+ // ── Load data source options for the status field ─────────────
8964
+ const statusOptions = useFieldDataSource('status', fieldConfigMap['status'], isReadonly);
8965
+ // ── Resolve field values ──────────────────────────────────────
8966
+ const paths = React.useMemo(() => {
8967
+ if (!dataPath || typeof dataPath !== 'object')
8968
+ return {};
8969
+ return dataPath;
8970
+ }, [dataPath]);
8971
+ const findValue = React.useCallback((fieldKey) => {
8972
+ const path = paths[fieldKey];
8973
+ if (!path)
8974
+ return undefined;
8975
+ const searchIn = (source) => {
8976
+ if (!source)
8977
+ return undefined;
8978
+ let v = getValueByPath(source, path);
8979
+ if (v !== undefined)
8980
+ return v;
8981
+ for (const obj of Object.values(source)) {
8982
+ if (obj && typeof obj === 'object' && !Array.isArray(obj)) {
8983
+ v = getValueByPath(obj, path);
8984
+ if (v !== undefined)
8985
+ return v;
8986
+ }
8987
+ }
8988
+ return undefined;
8989
+ };
8990
+ let result = searchIn(values);
8991
+ if (result === undefined)
8992
+ result = searchIn(schemaData);
8993
+ return result;
8994
+ }, [paths, values, schemaData]);
8995
+ const imageUrl = findValue('image') || null;
8996
+ const displayName = findValue('name') || '';
8997
+ const functionalId = findValue('functionalId') || '';
8998
+ const statusValue = findValue('status') || '';
8999
+ const statusReason = findValue('statusReason') || '';
9000
+ const createdBy = findValue('createdBy') || '';
9001
+ const createdAt = findValue('createdAt') || '';
9002
+ const lastApprovedBy = findValue('lastApprovedBy') || '';
9003
+ const lastApprovedAt = findValue('lastApprovedAt') || '';
9004
+ // ── Format options ────────────────────────────────────────────
9005
+ const format = (widgetConfig['widget-data-format'] || {});
9006
+ const imageSize = format.imageSize || 90;
9007
+ const nameColor = format.nameColor || '#ED7C22';
9008
+ const statusColors = {
9009
+ ...DEFAULT_STATUS_COLORS,
9010
+ ...(format.statusColors || {}),
9011
+ };
9012
+ // ── Value change helpers ──────────────────────────────────────
9013
+ const updateFieldValue = React.useCallback((fieldKey, newValue) => {
9014
+ const path = paths[fieldKey];
9015
+ if (!path)
9016
+ return;
9017
+ const updated = setValueByPath({ ...values }, path, newValue);
9018
+ dispatch(setValues(updated));
9019
+ }, [paths, values, dispatch]);
9020
+ // ── Status helpers ────────────────────────────────────────────
9021
+ const statusLabel = React.useMemo(() => {
9022
+ if (!statusValue)
9023
+ return '';
9024
+ const opt = statusOptions.find((o) => String(o.value).toLowerCase() === String(statusValue).toLowerCase());
9025
+ return opt ? opt.label : String(statusValue);
9026
+ }, [statusValue, statusOptions]);
9027
+ const statusColor = statusColors[String(statusValue).toLowerCase()] || '#6B7280';
9028
+ // ── Scoped class for CSS isolation ────────────────────────────
9029
+ const cls = `header-section-widget-${widgetConfig['widget-id']}`;
9030
+ // ── Indicator dot component ───────────────────────────────────
9031
+ const Dot = ({ color }) => (jsxRuntimeExports.jsx("span", { style: {
9032
+ display: 'inline-block',
9033
+ width: 8,
9034
+ height: 8,
9035
+ borderRadius: '50%',
9036
+ backgroundColor: color,
9037
+ flexShrink: 0,
9038
+ marginTop: 6,
9039
+ } }));
9040
+ // ── RENDER ────────────────────────────────────────────────────
9041
+ return (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("style", { children: `
9042
+ .${cls} {
9043
+ display: flex;
9044
+ flex-direction: row;
9045
+ gap: 1.5rem;
9046
+ width: 100%;
9047
+ font-family: Roboto, sans-serif;
9048
+ padding: 35px 0 16px 0;
9049
+ }
9050
+
9051
+ .${cls} .hdr-left {
9052
+ display: flex;
9053
+ flex-direction: row;
9054
+ align-items: flex-start;
9055
+ gap: 1rem;
9056
+ flex: 1 1 55%;
9057
+ min-width: 0;
9058
+ }
9059
+
9060
+ .${cls} .hdr-right {
9061
+ display: flex;
9062
+ flex-direction: column;
9063
+ gap: 0.5rem;
9064
+ flex: 0 0 auto;
9065
+ min-width: 220px;
9066
+ }
9067
+
9068
+ .${cls} .hdr-avatar {
9069
+ width: ${imageSize}px;
9070
+ height: ${imageSize}px;
9071
+ border-radius: 8px;
9072
+ object-fit: cover;
9073
+ background-color: #e5e7eb;
9074
+ border: 2px solid #d1d5db;
9075
+ flex-shrink: 0;
9076
+ }
9077
+
9078
+ .${cls} .hdr-avatar-placeholder {
9079
+ width: ${imageSize}px;
9080
+ height: ${imageSize}px;
9081
+ border-radius: 8px;
9082
+ background-color: #e5e7eb;
9083
+ border: 2px solid #d1d5db;
9084
+ display: flex;
9085
+ align-items: center;
9086
+ justify-content: center;
9087
+ flex-shrink: 0;
9088
+ overflow: hidden;
9089
+ }
9090
+
9091
+ .${cls} .hdr-avatar-placeholder img {
9092
+ width: 100%;
9093
+ height: 100%;
9094
+ object-fit: cover;
9095
+ border-radius: 8px;
9096
+ }
9097
+
9098
+ .${cls} .hdr-info {
9099
+ display: flex;
9100
+ flex-direction: column;
9101
+ gap: 0.35rem;
9102
+ min-width: 0;
9103
+ flex: 1;
9104
+ }
9105
+
9106
+ .${cls} .hdr-name {
9107
+ font-size: 1.25rem;
9108
+ font-weight: 600;
9109
+ color: ${nameColor};
9110
+ line-height: 1.4;
9111
+ word-wrap: break-word;
9112
+ }
9113
+
9114
+ .${cls} .hdr-field-row {
9115
+ display: flex;
9116
+ align-items: flex-start;
9117
+ gap: 0.5rem;
9118
+ font-size: 0.875rem;
9119
+ line-height: 1.6;
9120
+ }
9121
+
9122
+ .${cls} .hdr-field-label {
9123
+ color: #6b7280;
9124
+ font-weight: 500;
9125
+ white-space: nowrap;
9126
+ }
9127
+
9128
+ .${cls} .hdr-field-value {
9129
+ color: #111827;
9130
+ font-weight: 600;
9131
+ }
9132
+
9133
+ .${cls} .hdr-status-badge {
9134
+ display: inline-block;
9135
+ padding: 2px 12px;
9136
+ border-radius: 4px;
9137
+ font-size: 0.75rem;
9138
+ font-weight: 600;
9139
+ color: #fff;
9140
+ }
9141
+
9142
+ .${cls} .hdr-meta-row {
9143
+ display: flex;
9144
+ align-items: baseline;
9145
+ gap: 0.35rem;
9146
+ font-size: 0.875rem;
9147
+ line-height: 1.6;
9148
+ }
9149
+
9150
+ .${cls} .hdr-meta-label {
9151
+ color: #6b7280;
9152
+ font-weight: 400;
9153
+ }
9154
+
9155
+ .${cls} .hdr-meta-value {
9156
+ color: #111827;
9157
+ font-weight: 600;
9158
+ }
9159
+
9160
+ .${cls} .hdr-select {
9161
+ height: 32px;
9162
+ padding: 0 8px;
9163
+ border: 1px solid #d1d5db;
9164
+ border-radius: 6px;
9165
+ font-size: 0.875rem;
9166
+ font-family: Roboto, sans-serif;
9167
+ background: #fff;
9168
+ min-width: 140px;
9169
+ color: #374151;
9170
+ }
9171
+ .${cls} .hdr-select:focus {
9172
+ outline: none;
9173
+ border-color: #ED7C22;
9174
+ box-shadow: 0 0 0 2px rgba(237, 124, 34, 0.15);
9175
+ }
9176
+
9177
+ .${cls} .hdr-input {
9178
+ height: 32px;
9179
+ padding: 0 8px;
9180
+ border: 1px solid #d1d5db;
9181
+ border-radius: 6px;
9182
+ font-size: 0.875rem;
9183
+ font-family: Roboto, sans-serif;
9184
+ background: #fff;
9185
+ min-width: 140px;
9186
+ color: #374151;
9187
+ }
9188
+ .${cls} .hdr-input:focus {
9189
+ outline: none;
9190
+ border-color: #ED7C22;
9191
+ box-shadow: 0 0 0 2px rgba(237, 124, 34, 0.15);
9192
+ }
9193
+
9194
+ @media (max-width: 768px) {
9195
+ .${cls} {
9196
+ flex-direction: column;
9197
+ }
9198
+ .${cls} .hdr-right {
9199
+ min-width: 0;
9200
+ }
9201
+ }
9202
+ ` }), jsxRuntimeExports.jsxs("div", { className: cls, children: [jsxRuntimeExports.jsxs("div", { className: "hdr-left", children: [jsxRuntimeExports.jsxs("div", { children: [imageUrl ? (jsxRuntimeExports.jsx("img", { src: imageUrl, alt: displayName || 'Profile', className: "hdr-avatar", onError: (e) => {
9203
+ e.target.style.display = 'none';
9204
+ const placeholder = e.target
9205
+ .parentElement?.querySelector('.hdr-avatar-placeholder');
9206
+ if (placeholder)
9207
+ placeholder.style.display = 'flex';
9208
+ } })) : null, jsxRuntimeExports.jsx("div", { className: "hdr-avatar-placeholder", style: { display: imageUrl ? 'none' : 'flex' }, children: jsxRuntimeExports.jsx("img", { src: img$f, alt: "Profile Placeholder" }) })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-info", children: [displayName && jsxRuntimeExports.jsx("div", { className: "hdr-name", children: displayName }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsx(Dot, { color: "#9CA3AF" }), jsxRuntimeExports.jsxs("span", { className: "hdr-field-label", children: [translateConfig('Functional Record ID') || 'Functional Record ID', " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-field-value", children: functionalId || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsx(Dot, { color: isReadonly ? statusColor : '#F59E0B' }), jsxRuntimeExports.jsx("span", { className: "hdr-field-label", children: translateConfig('Record Status') || 'Record Status' }), isReadonly ? (statusLabel ? (jsxRuntimeExports.jsx("span", { className: "hdr-status-badge", style: { backgroundColor: statusColor }, children: statusLabel })) : (jsxRuntimeExports.jsx("span", { className: "hdr-field-value", children: "-" }))) : (jsxRuntimeExports.jsxs("select", { className: "hdr-select", value: statusValue, onChange: (e) => updateFieldValue('status', e.target.value), children: [jsxRuntimeExports.jsx("option", { value: "", children: translateConfig('Select') || 'Select' }), statusOptions.map((opt) => (jsxRuntimeExports.jsx("option", { value: opt.value, children: opt.label }, opt.value)))] }))] }), jsxRuntimeExports.jsxs("div", { className: "hdr-field-row", children: [jsxRuntimeExports.jsx(Dot, { color: isReadonly ? '#9CA3AF' : '#F59E0B' }), jsxRuntimeExports.jsxs("span", { className: "hdr-field-label", children: [translateConfig('Status Reason') || 'Status Reason', " :"] }), isReadonly ? (jsxRuntimeExports.jsx("span", { className: "hdr-field-value", children: statusReason || '-' })) : (jsxRuntimeExports.jsx("input", { type: "text", className: "hdr-input", value: statusReason, placeholder: translateConfig('Enter Reason') || 'Enter Reason', onChange: (e) => updateFieldValue('statusReason', e.target.value) }))] })] })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-right", children: [jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", children: [translateConfig('Created by') || 'Created by', " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", children: createdBy || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", children: [translateConfig('Created at') || 'Created at', " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", children: createdAt || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", children: [translateConfig('Last Approved by') || 'Last Approved by', " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", children: lastApprovedBy || '-' })] }), jsxRuntimeExports.jsxs("div", { className: "hdr-meta-row", children: [jsxRuntimeExports.jsxs("span", { className: "hdr-meta-label", children: [translateConfig('Last Approved at') || 'Last Approved at', " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", children: lastApprovedAt || '-' })] })] })] })] }));
9209
+ };
9210
+
8886
9211
  /**
8887
9212
  * Register all default/generic widgets
8888
9213
  * This is called automatically when the package is imported
@@ -8922,6 +9247,8 @@ const registerDefaultWidgets = () => {
8922
9247
  widgetRegistry.register({ widget: 'display', component: DisplayWidget });
8923
9248
  // Profile widget for displaying user identity (image, name, ID)
8924
9249
  widgetRegistry.register({ widget: 'profile', component: ProfileWidget });
9250
+ // Header section widget for full-width registry header with profile, status, and metadata
9251
+ widgetRegistry.register({ widget: 'header-section', component: HeaderSectionWidget });
8925
9252
  };
8926
9253
  // Auto-register on import
8927
9254
  registerDefaultWidgets();
@@ -9287,6 +9614,7 @@ exports.DateInputWidget = DateInputWidget;
9287
9614
  exports.DateTimeInputWidget = DateTimeInputWidget;
9288
9615
  exports.DisplayWidget = DisplayWidget;
9289
9616
  exports.FileInputWidget = FileInputWidget;
9617
+ exports.HeaderSectionWidget = HeaderSectionWidget;
9290
9618
  exports.IterableAccordionWidget = IterableAccordionWidget;
9291
9619
  exports.JSONEditorPanel = JSONEditorPanel;
9292
9620
  exports.NumberInputWidget = NumberInputWidget;