@elasticias/screens 1.0.41 → 1.0.42
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.
|
@@ -458,6 +458,13 @@ class AbstractScreenComponent extends AbstractComponent {
|
|
|
458
458
|
const v = raw;
|
|
459
459
|
if (!v || typeof v !== 'object' || !v.presetKey)
|
|
460
460
|
return null;
|
|
461
|
+
// An open range stores null bounds. `new Date(null)` is 1970, not
|
|
462
|
+
// NaN, so it must be handled before parsing, not after.
|
|
463
|
+
if (v.start == null && v.end == null) {
|
|
464
|
+
return { start: null, end: null, presetKey: v.presetKey, label: v.label ?? '', labelKey: v.labelKey };
|
|
465
|
+
}
|
|
466
|
+
if (v.start == null || v.end == null)
|
|
467
|
+
return null;
|
|
461
468
|
const start = new Date(v.start);
|
|
462
469
|
const end = new Date(v.end);
|
|
463
470
|
if (isNaN(start.getTime()) || isNaN(end.getTime()))
|
|
@@ -1075,14 +1082,27 @@ class AbstractSearchScreenV2 extends AbstractScreenComponent {
|
|
|
1075
1082
|
criteria = signal(new SearchEntity({}), ...(ngDevMode ? [{ debugName: "criteria" }] : /* istanbul ignore next */ []));
|
|
1076
1083
|
/**
|
|
1077
1084
|
* Active date-range filter shown in `ef-datepicker-advanced`'s
|
|
1078
|
-
* trigger
|
|
1079
|
-
*
|
|
1080
|
-
*
|
|
1085
|
+
* trigger, and the single source of the criteria's `start`/`end`.
|
|
1086
|
+
* The first search, `clear()` and a picked range all send exactly
|
|
1087
|
+
* what this holds, and a cache restore rebuilds it from the restored
|
|
1088
|
+
* dates, so the chip never reads one period while the list shows
|
|
1089
|
+
* another.
|
|
1081
1090
|
*
|
|
1082
|
-
*
|
|
1083
|
-
*
|
|
1091
|
+
* Defaults to `'all_time'` (no dates sent). Override
|
|
1092
|
+
* `buildDefaultDateRange()` per screen to open on a window instead.
|
|
1084
1093
|
*/
|
|
1085
1094
|
dateRange = signal(this.buildDefaultDateRange(), ...(ngDevMode ? [{ debugName: "dateRange" }] : /* istanbul ignore next */ []));
|
|
1095
|
+
/**
|
|
1096
|
+
* Whether this screen offers the date chip at all. Set it to `false`
|
|
1097
|
+
* (`override readonly showDateFilter = false;`) on a screen whose
|
|
1098
|
+
* backend query has no date field, and guard the template's
|
|
1099
|
+
* `<ef-datepicker-advanced>` with `@if (showDateFilter)` (or drop it).
|
|
1100
|
+
* The base then never sends `start`/`end`: not on first load, not
|
|
1101
|
+
* after `clear()`, not from criteria cached by an older session, and
|
|
1102
|
+
* not from `onDateRangeChange()` / `setDateRange()`, which become
|
|
1103
|
+
* no-ops, so a picker left in the template cannot filter either.
|
|
1104
|
+
*/
|
|
1105
|
+
showDateFilter = true;
|
|
1086
1106
|
/* ── Filter-bar UI state (DRY) ──────────────────────────────────
|
|
1087
1107
|
Identical across every list screen — lifted up so subclasses
|
|
1088
1108
|
don't re-declare. `clear()` resets all four signals. */
|
|
@@ -1196,14 +1216,16 @@ class AbstractSearchScreenV2 extends AbstractScreenComponent {
|
|
|
1196
1216
|
}
|
|
1197
1217
|
/* ── Date-range filter (DRY) ────────────────────────────────────
|
|
1198
1218
|
Wired to ef-datepicker-advanced. The default range is
|
|
1199
|
-
`
|
|
1200
|
-
if a
|
|
1219
|
+
`all_time`, which sends no dates. Override
|
|
1220
|
+
`buildDefaultDateRange()` per screen if a window is needed. */
|
|
1201
1221
|
/**
|
|
1202
1222
|
* Wired to `<ef-datepicker-advanced (rangeChange)>` — stores the
|
|
1203
1223
|
* range for trigger display and pushes it into the search criteria
|
|
1204
1224
|
* so the next `search()` filters by it.
|
|
1205
1225
|
*/
|
|
1206
1226
|
onDateRangeChange(range) {
|
|
1227
|
+
if (!this.showDateFilter)
|
|
1228
|
+
return;
|
|
1207
1229
|
this.dateRange.set(range);
|
|
1208
1230
|
this.setDateRange(range.start, range.end);
|
|
1209
1231
|
}
|
|
@@ -1216,19 +1238,52 @@ class AbstractSearchScreenV2 extends AbstractScreenComponent {
|
|
|
1216
1238
|
this.dateRange.set(this.buildDefaultDateRange());
|
|
1217
1239
|
}
|
|
1218
1240
|
/**
|
|
1219
|
-
* Build the default `EfDateRange`.
|
|
1220
|
-
*
|
|
1221
|
-
*
|
|
1241
|
+
* Build the default `EfDateRange`. Default: `all_time`, no bounds,
|
|
1242
|
+
* so a typed search finds an old record instead of silently missing
|
|
1243
|
+
* it behind a window nobody chose. Override per screen to open on a
|
|
1244
|
+
* window (e.g. `daysAgo(89)` to `startOfToday()` for `last_90_days`);
|
|
1245
|
+
* the first search and `clear()` then send that window.
|
|
1222
1246
|
*/
|
|
1223
1247
|
buildDefaultDateRange() {
|
|
1224
1248
|
return {
|
|
1225
|
-
start:
|
|
1226
|
-
end:
|
|
1227
|
-
presetKey: '
|
|
1228
|
-
labelKey: '
|
|
1229
|
-
label: '
|
|
1249
|
+
start: null,
|
|
1250
|
+
end: null,
|
|
1251
|
+
presetKey: 'all_time',
|
|
1252
|
+
labelKey: 'date_preset_all_time',
|
|
1253
|
+
label: '',
|
|
1230
1254
|
};
|
|
1231
1255
|
}
|
|
1256
|
+
/** Copy the chip's range onto fresh criteria, so what is shown is
|
|
1257
|
+
* what is sent. Sends nothing when the screen has no date chip. */
|
|
1258
|
+
seedDateCriteria(criteria) {
|
|
1259
|
+
const range = this.showDateFilter ? this.dateRange() : null;
|
|
1260
|
+
criteria.start = range?.start ?? null;
|
|
1261
|
+
criteria.end = range?.end ?? null;
|
|
1262
|
+
}
|
|
1263
|
+
/** Rebuild the chip from restored criteria: both dates are shown as
|
|
1264
|
+
* the custom range they filter on; anything less is `all_time`, and
|
|
1265
|
+
* `bootstrapInitialSearch()` drops a lone bound so nothing is sent.
|
|
1266
|
+
* No literal label: the picker formats a custom range itself. */
|
|
1267
|
+
dateRangeFromCriteria(startRaw, endRaw) {
|
|
1268
|
+
const start = this.toDateOrNull(startRaw);
|
|
1269
|
+
const end = this.toDateOrNull(endRaw);
|
|
1270
|
+
if (!start || !end) {
|
|
1271
|
+
return {
|
|
1272
|
+
start: null,
|
|
1273
|
+
end: null,
|
|
1274
|
+
presetKey: 'all_time',
|
|
1275
|
+
labelKey: 'date_preset_all_time',
|
|
1276
|
+
label: '',
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
return { start, end, presetKey: 'custom', label: '' };
|
|
1280
|
+
}
|
|
1281
|
+
toDateOrNull(raw) {
|
|
1282
|
+
if (raw == null || raw === '')
|
|
1283
|
+
return null;
|
|
1284
|
+
const d = raw instanceof Date ? raw : new Date(raw);
|
|
1285
|
+
return isNaN(d.getTime()) ? null : d;
|
|
1286
|
+
}
|
|
1232
1287
|
/** Today at 00:00 local time. */
|
|
1233
1288
|
startOfToday() {
|
|
1234
1289
|
const d = new Date();
|
|
@@ -1430,7 +1485,17 @@ class AbstractSearchScreenV2 extends AbstractScreenComponent {
|
|
|
1430
1485
|
bootstrapInitialSearch() {
|
|
1431
1486
|
const cached = this.cacheService.getCache(this.screenStateKey);
|
|
1432
1487
|
if (cached) {
|
|
1433
|
-
|
|
1488
|
+
const restored = new SearchEntity(cached);
|
|
1489
|
+
const bothBounds = this.toDateOrNull(cached['start']) && this.toDateOrNull(cached['end']);
|
|
1490
|
+
if (!this.showDateFilter || !bothBounds) {
|
|
1491
|
+
// Criteria cached while this screen still had a chip may carry
|
|
1492
|
+
// dates its backend ignores at best; and a lone bound would be
|
|
1493
|
+
// sent under a chip reading "all time". Drop them.
|
|
1494
|
+
restored.start = null;
|
|
1495
|
+
restored.end = null;
|
|
1496
|
+
restored.startEndDateRanges = null;
|
|
1497
|
+
}
|
|
1498
|
+
this.criteria.set(restored);
|
|
1434
1499
|
this.restoreFilterUiFromCriteria(cached);
|
|
1435
1500
|
}
|
|
1436
1501
|
else {
|
|
@@ -1448,6 +1513,7 @@ class AbstractSearchScreenV2 extends AbstractScreenComponent {
|
|
|
1448
1513
|
pageNumber: PaginationEnum.DEFAULT_PAGE,
|
|
1449
1514
|
pageSize: PaginationEnum.DEFAULT_PAGE_SIZE,
|
|
1450
1515
|
};
|
|
1516
|
+
this.seedDateCriteria(fresh);
|
|
1451
1517
|
this.criteria.set(fresh);
|
|
1452
1518
|
}
|
|
1453
1519
|
this.search();
|
|
@@ -1458,11 +1524,15 @@ class AbstractSearchScreenV2 extends AbstractScreenComponent {
|
|
|
1458
1524
|
* what the drawer / search box displays matches what the search will
|
|
1459
1525
|
* actually send (otherwise a stale criteria filter keeps applying
|
|
1460
1526
|
* while every control reads "Tous"). Base handles the free-text
|
|
1461
|
-
* input and the declared advanced filters; override
|
|
1462
|
-
* to sync screen-specific state
|
|
1527
|
+
* input, the date chip and the declared advanced filters; override
|
|
1528
|
+
* (calling super) to sync screen-specific state, such as a status pill
|
|
1529
|
+
* or custom switches.
|
|
1463
1530
|
*/
|
|
1464
1531
|
restoreFilterUiFromCriteria(cached) {
|
|
1465
1532
|
this.searchQuery.set(cached['searchText'] ?? '');
|
|
1533
|
+
if (this.showDateFilter) {
|
|
1534
|
+
this.dateRange.set(this.dateRangeFromCriteria(cached['start'], cached['end']));
|
|
1535
|
+
}
|
|
1466
1536
|
const next = {};
|
|
1467
1537
|
for (const f of this.advancedFilters) {
|
|
1468
1538
|
const value = cached[f.key];
|
|
@@ -1535,6 +1605,8 @@ class AbstractSearchScreenV2 extends AbstractScreenComponent {
|
|
|
1535
1605
|
this.search();
|
|
1536
1606
|
}
|
|
1537
1607
|
setDateRange(start, end) {
|
|
1608
|
+
if (!this.showDateFilter)
|
|
1609
|
+
return;
|
|
1538
1610
|
this.criteria.update((c) => this.cloneCriteria(c, { start, end, page: 1 }));
|
|
1539
1611
|
this.search();
|
|
1540
1612
|
}
|
|
@@ -1557,6 +1629,8 @@ class AbstractSearchScreenV2 extends AbstractScreenComponent {
|
|
|
1557
1629
|
},
|
|
1558
1630
|
];
|
|
1559
1631
|
}
|
|
1632
|
+
this.resetDateRange();
|
|
1633
|
+
this.seedDateCriteria(fresh);
|
|
1560
1634
|
this.criteria.set(fresh);
|
|
1561
1635
|
// Reset all the shared filter-bar / selection signals so
|
|
1562
1636
|
// subclasses don't have to track each one individually.
|
|
@@ -1566,7 +1640,6 @@ class AbstractSearchScreenV2 extends AbstractScreenComponent {
|
|
|
1566
1640
|
this.activeFilters.set([]);
|
|
1567
1641
|
this.advancedValues.set({});
|
|
1568
1642
|
this.selected.set(new Set());
|
|
1569
|
-
this.resetDateRange();
|
|
1570
1643
|
this.cacheService.setCache(this.screenStateKey, fresh);
|
|
1571
1644
|
this.search();
|
|
1572
1645
|
}
|
|
@@ -2401,7 +2474,7 @@ class AbstractReportScreenV2 extends AbstractScreenComponent {
|
|
|
2401
2474
|
this.reportsService = this.injector.get(this.getConfig().SERVICE);
|
|
2402
2475
|
// Reinstate the period the user last chose before anything loads, so the
|
|
2403
2476
|
// widgets fetch it once instead of fetching the default and refetching.
|
|
2404
|
-
this.dateRange.set(this.restorePersistedDateRange(this.dateRange(), key => this.rangeFromPreset(key)));
|
|
2477
|
+
this.dateRange.set(this.boundedOrDefault(this.restorePersistedDateRange(this.dateRange(), key => this.rangeFromPreset(key))));
|
|
2405
2478
|
const lists = this.getConfig()?.REPORT_STATIC_LISTS ?? [];
|
|
2406
2479
|
if (lists.length > 0) {
|
|
2407
2480
|
this.initializeStaticLists(lists);
|
|
@@ -2415,10 +2488,22 @@ class AbstractReportScreenV2 extends AbstractScreenComponent {
|
|
|
2415
2488
|
/* ── Period ─────────────────────────────────────────────────── */
|
|
2416
2489
|
/** Wired to `<ef-datepicker-advanced (rangeChange)>`. */
|
|
2417
2490
|
onDateRangeChange(range) {
|
|
2418
|
-
this.
|
|
2419
|
-
this.
|
|
2491
|
+
const bounded = this.boundedOrDefault(range);
|
|
2492
|
+
this.dateRange.set(bounded);
|
|
2493
|
+
this.persistDateRange(bounded);
|
|
2420
2494
|
this.loadAll();
|
|
2421
2495
|
}
|
|
2496
|
+
/**
|
|
2497
|
+
* Every widget query is a period, so an open range (`all_time`) has no
|
|
2498
|
+
* meaning here. The picker only offers it with `allowAllTime`, which a
|
|
2499
|
+
* report should not set; if one arrives anyway, fall back to the
|
|
2500
|
+
* default period rather than send a null bound.
|
|
2501
|
+
*/
|
|
2502
|
+
boundedOrDefault(range) {
|
|
2503
|
+
return range.start && range.end
|
|
2504
|
+
? range
|
|
2505
|
+
: this.buildDefaultDateRange();
|
|
2506
|
+
}
|
|
2422
2507
|
/** Default period from the config's `DEFAULT_PERIOD` preset. */
|
|
2423
2508
|
buildDefaultDateRange() {
|
|
2424
2509
|
return this.rangeFromPreset(this.getConfig()?.DEFAULT_PERIOD ?? 'last_30_days');
|