@openg2p/registry-widgets 1.1.0-dev.0 → 1.1.0-dev.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.
package/README.md CHANGED
@@ -258,16 +258,16 @@ A full-width header card designed for registry record views. Displays a profile
258
258
 
259
259
  ### ScoresDisplay Widget
260
260
 
261
- A full-width, view-only widget for showing a single computed score record (e.g. PMT, Food Security Score). It fetches data via the host application's `dataSourceRequestHandler` and renders:
261
+ A full-width, view-only widget for showing **a list** of computed score records (e.g. PMT, Food Security Score). It renders, for each score:
262
262
 
263
263
  - **Score Type**
264
264
  - **Score**
265
265
  - **Computed at**
266
266
 
267
- **API request mapping**
267
+ **Data binding**
268
268
 
269
- - The widget reads `internal_record_id` from `widget-data-source.params.internal_record_id_path` (a dot-path into `schemaData` / store values).
270
- - It then calls `dataSourceRequestHandler(service, endpoint, method, params)` and passes `internal_record_id` in the request params.
269
+ - The widget reads an array of score records from `widget-data-path` (a dot-path into `schemaData` / store values).
270
+ - `widget-data-path` may point either directly to an array, or to an object containing `{ "scores": [...] }`.
271
271
 
272
272
  ```json
273
273
  {
@@ -275,37 +275,27 @@ A full-width, view-only widget for showing a single computed score record (e.g.
275
275
  "widget-type": "group",
276
276
  "widget-id": "record-scores",
277
277
  "widget-readonly": true,
278
- "widget-data-source": {
279
- "type": "api",
280
- "service": "staff-portal-api",
281
- "endpoint": "get_scores",
282
- "method": "POST",
283
- "params": {
284
- "internal_record_id_path": "internal_record_id"
285
- }
286
- }
278
+ "widget-data-path": "scores"
287
279
  }
288
280
  ```
289
281
 
290
- **Expected response shape (ideal)**
282
+ **Expected record shape (ideal)**
291
283
 
292
284
  ```json
293
- {
294
- "scores": [
295
- {
296
- "score_type": "PMT",
297
- "computed_score": 42,
298
- "computed_at": "2026-04-16T10:12:00Z",
299
- "triggered_by_cr_id": "CR-001"
300
- }
301
- ]
302
- }
285
+ [
286
+ {
287
+ "score_type": "PMT",
288
+ "computed_score": 42,
289
+ "computed_at": "2026-04-16T10:12:00Z",
290
+ "triggered_by_cr_id": "CR-001"
291
+ }
292
+ ]
303
293
  ```
304
294
 
305
295
  **Notes**
306
296
 
307
- - The widget displays a single score record. If multiple scores are returned, it selects the most recent by `computed_at` (falls back to the first item if dates are missing).
308
297
  - There is no edit mode for this widget.
298
+ - If multiple scores are provided, the widget sorts them by `computed_at` (most recent first). If dates are missing/invalid, original order is preserved for those items.
309
299
 
310
300
  ## Section Modes
311
301
 
package/dist/index.d.ts CHANGED
@@ -1454,33 +1454,20 @@ declare const HeaderSectionWidget: ({ config }: HeaderSectionWidgetProps) => rea
1454
1454
 
1455
1455
  interface ScoresDisplayWidgetProps {
1456
1456
  config: BaseWidgetConfig;
1457
- dataSourceRequestHandler?: DataSourceRequestHandler;
1458
1457
  schemaData?: Record<string, unknown>;
1459
1458
  }
1460
1459
  /**
1461
- * Scores Display Widget - full-width, view-only widget
1460
+ * Scores Display Widget - full-width, view-only widget (list)
1462
1461
  *
1463
1462
  * Expected config (reference):
1464
1463
  * {
1465
1464
  * "widget": "scores-display",
1466
1465
  * "widget-type": "group",
1467
1466
  * "widget-id": "record-scores",
1468
- * "widget-data-source": {
1469
- * "type": "api",
1470
- * "service": "staff-portal-api",
1471
- * "endpoint": "get_scores",
1472
- * "method": "POST",
1473
- * "params": { "internal_record_id_path": "internal_record_id" }
1474
- * }
1467
+ * "widget-data-path": "scores"
1475
1468
  * }
1476
- *
1477
- * The host's `dataSourceRequestHandler` is invoked with:
1478
- * - service: config.widget-data-source.service
1479
- * - endpoint: config.widget-data-source.endpoint
1480
- * - method: config.widget-data-source.method (default POST)
1481
- * - params: { internal_record_id: <resolved from internal_record_id_path> }
1482
1469
  */
1483
- declare const ScoresDisplayWidget: ({ config, dataSourceRequestHandler: propHandler, schemaData: propSchemaData, }: ScoresDisplayWidgetProps) => react_jsx_runtime.JSX.Element;
1470
+ declare const ScoresDisplayWidget: ({ config, schemaData: propSchemaData, }: ScoresDisplayWidgetProps) => react_jsx_runtime.JSX.Element;
1484
1471
 
1485
1472
  /**
1486
1473
  * Get value from object using dot notation path
package/dist/index.esm.js CHANGED
@@ -9526,6 +9526,13 @@ const HeaderSectionWidget = ({ config }) => {
9526
9526
  } })) : 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: [getLabel('functionalId'), " :"] }), 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: getLabel('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: getLabel('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: [getLabel('statusReason'), " :"] }), isReadonly ? (jsxRuntimeExports.jsx("span", { className: "hdr-field-value", children: statusReason || '-' })) : (jsxRuntimeExports.jsx("input", { type: "text", className: "hdr-input", value: statusReason, placeholder: getLabel('enterReason'), 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: [getLabel('createdBy'), " :"] }), 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: [getLabel('createdAt'), " :"] }), 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: [getLabel('lastApprovedBy'), " :"] }), 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: [getLabel('lastApprovedAt'), " :"] }), jsxRuntimeExports.jsx("span", { className: "hdr-meta-value", children: lastApprovedAt || '-' })] })] })] })] }));
9527
9527
  };
9528
9528
 
9529
+ function getValueByPathOrKey(obj, path) {
9530
+ if (!obj || !path)
9531
+ return undefined;
9532
+ if (Object.prototype.hasOwnProperty.call(obj, path))
9533
+ return obj[path];
9534
+ return getValueByPath(obj, path);
9535
+ }
9529
9536
  function tryFormatDateTime(value) {
9530
9537
  if (typeof value !== 'string' || !value)
9531
9538
  return value ? String(value) : '-';
@@ -9545,157 +9552,80 @@ function tryFormatDateTime(value) {
9545
9552
  return value;
9546
9553
  }
9547
9554
  }
9548
- function pickLatestScore(scores) {
9549
- if (!scores || scores.length === 0)
9550
- return null;
9555
+ function sortScores(scores) {
9551
9556
  const withTime = scores
9552
- .map((s) => {
9557
+ .map((s, idx) => {
9553
9558
  const t = typeof s?.computed_at === 'string' ? new Date(s.computed_at).getTime() : NaN;
9554
- return { s, t };
9559
+ return { s, t, idx };
9555
9560
  })
9556
- .filter((x) => !Number.isNaN(x.t));
9557
- if (withTime.length === 0)
9558
- return scores[0] || null;
9559
- withTime.sort((a, b) => b.t - a.t);
9560
- return withTime[0]?.s || null;
9561
+ .sort((a, b) => {
9562
+ const aHas = !Number.isNaN(a.t);
9563
+ const bHas = !Number.isNaN(b.t);
9564
+ if (aHas && bHas)
9565
+ return b.t - a.t;
9566
+ if (aHas)
9567
+ return -1;
9568
+ if (bHas)
9569
+ return 1;
9570
+ return a.idx - b.idx;
9571
+ });
9572
+ return withTime.map((x) => x.s);
9561
9573
  }
9562
9574
  /**
9563
- * Scores Display Widget - full-width, view-only widget
9575
+ * Scores Display Widget - full-width, view-only widget (list)
9564
9576
  *
9565
9577
  * Expected config (reference):
9566
9578
  * {
9567
9579
  * "widget": "scores-display",
9568
9580
  * "widget-type": "group",
9569
9581
  * "widget-id": "record-scores",
9570
- * "widget-data-source": {
9571
- * "type": "api",
9572
- * "service": "staff-portal-api",
9573
- * "endpoint": "get_scores",
9574
- * "method": "POST",
9575
- * "params": { "internal_record_id_path": "internal_record_id" }
9576
- * }
9582
+ * "widget-data-path": "scores"
9577
9583
  * }
9578
- *
9579
- * The host's `dataSourceRequestHandler` is invoked with:
9580
- * - service: config.widget-data-source.service
9581
- * - endpoint: config.widget-data-source.endpoint
9582
- * - method: config.widget-data-source.method (default POST)
9583
- * - params: { internal_record_id: <resolved from internal_record_id_path> }
9584
9584
  */
9585
- const ScoresDisplayWidget = ({ config, dataSourceRequestHandler: propHandler, schemaData: propSchemaData, }) => {
9586
- const { dataSourceRequestHandler: ctxHandler, schemaData: ctxSchemaData } = useWidgetContext();
9587
- const handler = propHandler || ctxHandler;
9588
- const schemaData = propSchemaData || ctxSchemaData || {};
9585
+ const ScoresDisplayWidget = ({ config, schemaData: propSchemaData, }) => {
9586
+ const { schemaData: ctxSchemaData } = useWidgetContext();
9587
+ const schemaData = (propSchemaData || ctxSchemaData || {});
9589
9588
  const values = useSelector((state) => state.widget.values);
9590
- const api = config['widget-data-source'];
9591
- const isApi = api?.type === 'api';
9592
- const apiDs = isApi ? api : null;
9593
- const internalIdPath = useMemo(() => {
9594
- if (!apiDs)
9595
- return undefined;
9596
- const p = apiDs.params || {};
9597
- const fromParams = p.internal_record_id_path || p.internalRecordIdPath;
9598
- const fromConfig = typeof config.internal_record_id_path === 'string'
9599
- ? config.internal_record_id_path
9600
- : undefined;
9601
- return fromParams || fromConfig;
9602
- }, [apiDs, config]);
9603
- const internalRecordId = useMemo(() => {
9604
- if (!internalIdPath)
9589
+ const dataPath = config['widget-data-path'];
9590
+ const rawScores = useMemo(() => {
9591
+ if (!dataPath || typeof dataPath !== 'string')
9605
9592
  return undefined;
9606
- const fromValues = getValueByPath(values || {}, internalIdPath);
9607
- if (fromValues !== undefined && fromValues !== null && String(fromValues).trim() !== '') {
9608
- return String(fromValues);
9609
- }
9610
- const fromSchema = getValueByPath(schemaData || {}, internalIdPath);
9611
- if (fromSchema !== undefined && fromSchema !== null && String(fromSchema).trim() !== '') {
9612
- return String(fromSchema);
9593
+ const valuesObj = values;
9594
+ const tryResolve = (path) => {
9595
+ const fromValues = getValueByPathOrKey(valuesObj, path);
9596
+ if (fromValues !== undefined)
9597
+ return fromValues;
9598
+ return getValueByPathOrKey(schemaData, path);
9599
+ };
9600
+ // 1) Try exact path (works when schema/store is already namespaced)
9601
+ const direct = tryResolve(dataPath);
9602
+ if (direct !== undefined)
9603
+ return direct;
9604
+ // 2) If section/widget config has been namespaced (e.g. "rv-section-0.scores"),
9605
+ // fall back to the original path ("scores") so examples still work even when
9606
+ // schemaData/store are not namespaced.
9607
+ if (dataPath.includes('.')) {
9608
+ const unNamespaced = dataPath.split('.').slice(1).join('.');
9609
+ const fallback = tryResolve(unNamespaced);
9610
+ if (fallback !== undefined)
9611
+ return fallback;
9613
9612
  }
9614
9613
  return undefined;
9615
- }, [internalIdPath, values, schemaData]);
9616
- const [loading, setLoading] = useState(false);
9617
- const [error, setError] = useState(null);
9618
- const [response, setResponse] = useState(null);
9619
- useEffect(() => {
9620
- let cancelled = false;
9621
- const load = async () => {
9622
- if (!apiDs) {
9623
- setError('Scores widget requires an API data source.');
9624
- setResponse(null);
9625
- return;
9626
- }
9627
- if (!handler) {
9628
- setError(null);
9629
- setResponse(null);
9630
- return;
9631
- }
9632
- const service = apiDs.service;
9633
- const endpoint = apiDs.endpoint;
9634
- const method = apiDs.method || 'POST';
9635
- if (!service || !endpoint) {
9636
- setError('Scores widget API data source is missing service/endpoint.');
9637
- setResponse(null);
9638
- return;
9639
- }
9640
- if (!internalRecordId) {
9641
- setError(null);
9642
- setResponse(null);
9643
- return;
9644
- }
9645
- try {
9646
- setLoading(true);
9647
- setError(null);
9648
- const rawParams = apiDs.params || {};
9649
- // Never pass the path helper through to the API.
9650
- const { internal_record_id_path, internalRecordIdPath, ...rest } = rawParams;
9651
- void internal_record_id_path;
9652
- void internalRecordIdPath;
9653
- const params = {
9654
- ...rest,
9655
- internal_record_id: internalRecordId,
9656
- };
9657
- const res = await handler(service, endpoint, method, params, {
9658
- headers: apiDs.headers,
9659
- });
9660
- if (cancelled)
9661
- return;
9662
- // Accept either direct payload or OpenG2P wrapper objects.
9663
- const envelope = res && typeof res === 'object' ? res : null;
9664
- const payload = envelope?.response_body?.response_payload ?? envelope?.data ?? res;
9665
- if (payload && typeof payload === 'object' && !Array.isArray(payload)) {
9666
- setResponse(payload);
9667
- }
9668
- else {
9669
- setResponse({ scores: Array.isArray(payload) ? payload : [] });
9670
- }
9671
- }
9672
- catch (e) {
9673
- if (cancelled)
9674
- return;
9675
- const maybeErr = e;
9676
- const msg = maybeErr && typeof maybeErr === 'object' && typeof maybeErr.message === 'string'
9677
- ? maybeErr.message
9678
- : 'Failed to load scores.';
9679
- setError(msg);
9680
- setResponse(null);
9681
- }
9682
- finally {
9683
- if (!cancelled)
9684
- setLoading(false);
9685
- }
9686
- };
9687
- load();
9688
- return () => {
9689
- cancelled = true;
9690
- };
9691
- }, [apiDs, handler, internalRecordId]);
9692
- const latest = useMemo(() => pickLatestScore(response?.scores), [response]);
9614
+ }, [dataPath, values, schemaData]);
9615
+ const scores = useMemo(() => {
9616
+ if (!rawScores)
9617
+ return [];
9618
+ if (Array.isArray(rawScores))
9619
+ return rawScores;
9620
+ if (typeof rawScores === 'object') {
9621
+ const maybe = rawScores.scores;
9622
+ if (Array.isArray(maybe))
9623
+ return maybe;
9624
+ }
9625
+ return [];
9626
+ }, [rawScores]);
9627
+ const sortedScores = useMemo(() => sortScores(scores), [scores]);
9693
9628
  const cls = `scores-display-widget-${config['widget-id']}`;
9694
- const scoreType = latest?.score_type ? String(latest.score_type) : '-';
9695
- const scoreValue = latest?.computed_score !== undefined && latest?.computed_score !== null && String(latest.computed_score) !== ''
9696
- ? String(latest.computed_score)
9697
- : '-';
9698
- const computedAt = tryFormatDateTime(latest?.computed_at);
9699
9629
  return (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("style", { children: `
9700
9630
  .${cls} {
9701
9631
  width: 100%;
@@ -9712,66 +9642,95 @@ const ScoresDisplayWidget = ({ config, dataSourceRequestHandler: propHandler, sc
9712
9642
  font-weight: 400;
9713
9643
  }
9714
9644
 
9715
- .${cls} .scores-card {
9645
+ .${cls} .scores-grid {
9716
9646
  width: 100%;
9717
- border: none;
9718
- border-radius: 0;
9719
- background: transparent;
9720
- padding: 0;
9721
9647
  display: grid;
9722
- grid-template-columns: 1fr 1fr 1fr;
9648
+ grid-template-columns: repeat(3, minmax(220px, 1fr));
9723
9649
  gap: 16px;
9724
- align-items: center;
9725
9650
  }
9726
9651
 
9727
- .${cls} .scores-col {
9728
- min-width: 0;
9652
+ .${cls} .scores-card {
9653
+ border: 1px solid var(--owt-color-border-light, #E4E4E4);
9654
+ border-radius: 10px;
9655
+ background: var(--owt-color-bg, #FFFFFF);
9656
+ padding: 14px 14px;
9729
9657
  display: flex;
9730
9658
  flex-direction: column;
9731
- gap: 6px;
9659
+ gap: 10px;
9660
+ min-width: 0;
9661
+ box-shadow: 0 1px 2px rgba(1, 22, 39, 0.06), 0 6px 16px rgba(1, 22, 39, 0.06);
9732
9662
  }
9733
9663
 
9734
- .${cls} .scores-label {
9735
- font-size: 12px;
9736
- color: var(--owt-color-text-muted, #727474);
9737
- font-weight: 600;
9738
- letter-spacing: 0.25px;
9739
- text-transform: uppercase;
9664
+ .${cls} .scores-type {
9665
+ font-size: 16px;
9666
+ font-weight: 800;
9667
+ color: var(--owt-color-primary-dark, #F07B1A);
9668
+ line-height: 1.2;
9669
+ word-break: break-word;
9740
9670
  }
9741
9671
 
9742
9672
  .${cls} .scores-value {
9743
- font-size: 16px;
9744
- font-weight: 600;
9673
+ font-size: 34px;
9674
+ font-weight: 800;
9745
9675
  color: var(--owt-color-text, #011627);
9746
- line-height: 1.25;
9747
- word-break: break-word;
9676
+ line-height: 1.05;
9677
+ letter-spacing: -0.25px;
9748
9678
  }
9749
9679
 
9750
- .${cls} .scores-value--highlight {
9751
- font-weight: 800;
9752
- color: var(--owt-color-primary-dark, #F07B1A);
9680
+ .${cls} .scores-separator {
9681
+ height: 1px;
9682
+ width: 100%;
9683
+ background-color: var(--owt-color-border-light, #E4E4E4);
9684
+ border: none;
9685
+ margin: 2px 0;
9753
9686
  }
9754
9687
 
9755
- .${cls} .scores-value-wrap {
9756
- display: inline-flex;
9757
- align-items: baseline;
9758
- gap: 10px;
9759
- flex-wrap: wrap;
9688
+ .${cls} .scores-value .scores-muted {
9689
+ font-size: 18px;
9690
+ font-weight: 600;
9691
+ color: var(--owt-color-text-muted, #727474);
9692
+ margin-left: 6px;
9760
9693
  }
9761
9694
 
9762
- .${cls} .scores-value-badge { display: inline; }
9695
+ .${cls} .scores-meta {
9696
+ display: flex;
9697
+ flex-direction: column;
9698
+ gap: 4px;
9699
+ }
9763
9700
 
9764
- .${cls} .scores-statusline {
9765
- grid-column: 1 / -1;
9766
- margin-top: 2px;
9701
+ .${cls} .scores-meta-line {
9702
+ font-size: 13px;
9703
+ color: var(--owt-color-text-muted, #727474);
9704
+ font-weight: 500;
9767
9705
  }
9768
9706
 
9769
- @media (max-width: 768px) {
9770
- .${cls} .scores-card {
9707
+ .${cls} .scores-meta-line strong {
9708
+ color: var(--owt-color-text, #011627);
9709
+ font-weight: 700;
9710
+ }
9711
+
9712
+ @media (max-width: 1024px) {
9713
+ .${cls} .scores-grid {
9714
+ grid-template-columns: repeat(2, minmax(220px, 1fr));
9715
+ }
9716
+ }
9717
+
9718
+ @media (max-width: 640px) {
9719
+ .${cls} .scores-grid {
9771
9720
  grid-template-columns: 1fr;
9772
9721
  }
9773
9722
  }
9774
- ` }), jsxRuntimeExports.jsx("div", { className: cls, children: jsxRuntimeExports.jsxs("div", { className: "scores-card", children: [jsxRuntimeExports.jsxs("div", { className: "scores-col", "aria-live": "polite", children: [jsxRuntimeExports.jsx("div", { className: "scores-label", children: "Score Type" }), jsxRuntimeExports.jsx("div", { className: "scores-value-wrap", children: jsxRuntimeExports.jsx("span", { className: "scores-value-badge", children: jsxRuntimeExports.jsx("span", { className: "scores-value scores-value--highlight", children: scoreType }) }) })] }), jsxRuntimeExports.jsxs("div", { className: "scores-col", children: [jsxRuntimeExports.jsx("div", { className: "scores-label", children: "Score" }), jsxRuntimeExports.jsx("div", { className: "scores-value-wrap", children: jsxRuntimeExports.jsx("span", { className: "scores-value-badge", children: jsxRuntimeExports.jsx("span", { className: "scores-value scores-value--highlight", children: scoreValue }) }) })] }), jsxRuntimeExports.jsxs("div", { className: "scores-col", children: [jsxRuntimeExports.jsx("div", { className: "scores-label", children: "Computed at" }), jsxRuntimeExports.jsx("div", { className: "scores-value", children: computedAt })] }), jsxRuntimeExports.jsx("div", { className: "scores-statusline", children: loading ? (jsxRuntimeExports.jsx("div", { className: "scores-subtle", children: "Loading scores\u2026" })) : error ? (jsxRuntimeExports.jsx("div", { className: "scores-subtle", style: { color: 'var(--owt-color-error, #B91C1C)' }, children: error })) : !latest ? (jsxRuntimeExports.jsx("div", { className: "scores-subtle", children: "No scores available." })) : null })] }) })] }));
9723
+ ` }), jsxRuntimeExports.jsx("div", { className: cls, children: sortedScores.length === 0 ? (jsxRuntimeExports.jsx("div", { className: "scores-subtle", children: "No scores available." })) : (jsxRuntimeExports.jsx("div", { className: "scores-grid", children: sortedScores.map((s, idx) => {
9724
+ const scoreType = s?.score_type ? String(s.score_type) : '-';
9725
+ const scoreValue = s?.computed_score !== undefined &&
9726
+ s?.computed_score !== null &&
9727
+ String(s.computed_score) !== ''
9728
+ ? String(s.computed_score)
9729
+ : '-';
9730
+ const computedAt = tryFormatDateTime(s?.computed_at);
9731
+ const key = `${scoreType}-${String(s?.computed_at || '')}-${idx}`;
9732
+ return (jsxRuntimeExports.jsxs("div", { className: "scores-card", "aria-live": idx === 0 ? 'polite' : undefined, children: [jsxRuntimeExports.jsx("div", { className: "scores-type", children: scoreType }), jsxRuntimeExports.jsx("div", { className: "scores-value", children: scoreValue }), jsxRuntimeExports.jsx("hr", { className: "scores-separator" }), jsxRuntimeExports.jsx("div", { className: "scores-meta", children: jsxRuntimeExports.jsxs("div", { className: "scores-meta-line", children: ["Computed at: ", jsxRuntimeExports.jsx("strong", { children: computedAt })] }) })] }, key));
9733
+ }) })) })] }));
9775
9734
  };
9776
9735
 
9777
9736
  /**