@elasticias/screens 1.0.27 → 1.0.29
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.
|
@@ -362,6 +362,15 @@ class AbstractScreenComponent extends AbstractComponent {
|
|
|
362
362
|
async invalidateReferences(keys) {
|
|
363
363
|
await this.refDataService.invalidateKeys(keys);
|
|
364
364
|
}
|
|
365
|
+
/** The record the user acted on carries no id — it was removed by someone
|
|
366
|
+
* else, the list is stale, or the route points at nothing. Say that,
|
|
367
|
+
* rather than the internal "Item [id] is undefined!" this used to ship
|
|
368
|
+
* to end users. Lives on the common base so every screen abstract
|
|
369
|
+
* (search, detail and sub in V1 and V2, plus report-v2) says the same
|
|
370
|
+
* thing — four copies of one string is how this file set drifts. */
|
|
371
|
+
recordNotFound() {
|
|
372
|
+
this.toastService.show({ severity: 'error', textKey: 'ef_error_record_not_found' });
|
|
373
|
+
}
|
|
365
374
|
handleErrors(errors) {
|
|
366
375
|
if (errors && errors.length > 0) {
|
|
367
376
|
errors.forEach(error => this.toastService.showError(error, 'Erreur de validation'));
|
|
@@ -637,24 +646,37 @@ class AbstractDetailScreenComponent extends AbstractScreenComponent {
|
|
|
637
646
|
print() {
|
|
638
647
|
// Override in subclass to implement print functionality
|
|
639
648
|
}
|
|
649
|
+
/**
|
|
650
|
+
* Name of the record being deleted, so the confirmation can say what
|
|
651
|
+
* it is about to destroy rather than asking about "this item".
|
|
652
|
+
* Override in the screen and return whatever a user recognises the
|
|
653
|
+
* record by — `return this.entity?.name;` is the usual body.
|
|
654
|
+
*/
|
|
655
|
+
getDeleteConfirmName() {
|
|
656
|
+
return undefined;
|
|
657
|
+
}
|
|
640
658
|
delete() {
|
|
641
|
-
if (!this.entityId)
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
659
|
+
if (!this.entityId) {
|
|
660
|
+
this.recordNotFound();
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
this.confirmDialogService.confirm({
|
|
664
|
+
intent: 'delete',
|
|
665
|
+
name: this.getDeleteConfirmName(),
|
|
666
|
+
accept: () => this.serviceInstance.delete(this.entityId).subscribe({
|
|
667
|
+
next: (result) => {
|
|
668
|
+
if (result && result.errors && result.errors.length > 0) {
|
|
669
|
+
this.handleErrors(result.errors);
|
|
670
|
+
}
|
|
671
|
+
else {
|
|
672
|
+
this.toastService.showSuccess();
|
|
673
|
+
this.navigateBack();
|
|
674
|
+
}
|
|
675
|
+
},
|
|
676
|
+
error: () => {
|
|
677
|
+
// Errors handled by global error handler
|
|
678
|
+
},
|
|
679
|
+
}),
|
|
658
680
|
});
|
|
659
681
|
}
|
|
660
682
|
navigateBack() {
|
|
@@ -893,34 +915,53 @@ class AbstractSearchScreenComponent extends AbstractScreenComponent {
|
|
|
893
915
|
}
|
|
894
916
|
edit(id) {
|
|
895
917
|
if (!id) {
|
|
896
|
-
this.
|
|
918
|
+
this.recordNotFound();
|
|
897
919
|
return;
|
|
898
920
|
}
|
|
899
921
|
this.router.navigate([this.currentUrl.concat('/details/'), id]);
|
|
900
922
|
}
|
|
923
|
+
/**
|
|
924
|
+
* Name of the row being deleted, so the confirmation can say what it
|
|
925
|
+
* is about to destroy rather than asking about "this item". Override
|
|
926
|
+
* in the screen — look the row up in the loaded results and return
|
|
927
|
+
* whatever a user would recognise it by.
|
|
928
|
+
*
|
|
929
|
+
* ```ts
|
|
930
|
+
* protected override getDeleteConfirmName(id: any) {
|
|
931
|
+
* return this.searchEntity.items?.find((r: any) => r.id === id)?.name;
|
|
932
|
+
* }
|
|
933
|
+
* ```
|
|
934
|
+
*/
|
|
935
|
+
getDeleteConfirmName(_id) {
|
|
936
|
+
return undefined;
|
|
937
|
+
}
|
|
901
938
|
delete(id) {
|
|
902
|
-
if (!id)
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
939
|
+
if (!id) {
|
|
940
|
+
this.recordNotFound();
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
this.confirmDialogService.confirm({
|
|
944
|
+
intent: 'delete',
|
|
945
|
+
name: this.getDeleteConfirmName(id),
|
|
946
|
+
accept: () => this.serviceInstance.delete(id).subscribe({
|
|
947
|
+
next: (result) => {
|
|
948
|
+
if (result && result.errors && result.errors.length > 0) {
|
|
949
|
+
this.handleErrors(result.errors);
|
|
950
|
+
}
|
|
951
|
+
else {
|
|
952
|
+
this.search();
|
|
953
|
+
this.toastService.showSuccess();
|
|
954
|
+
}
|
|
955
|
+
},
|
|
956
|
+
error: () => {
|
|
957
|
+
// Errors handled by global error handler
|
|
958
|
+
},
|
|
959
|
+
}),
|
|
919
960
|
});
|
|
920
961
|
}
|
|
921
962
|
duplicate(id) {
|
|
922
963
|
if (!id) {
|
|
923
|
-
this.
|
|
964
|
+
this.recordNotFound();
|
|
924
965
|
return;
|
|
925
966
|
}
|
|
926
967
|
this.router.navigate([this.currentUrl.concat('/details/'), id], { queryParams: { mode: 'duplicate' } });
|
|
@@ -1327,6 +1368,15 @@ class AbstractSearchScreenV2 extends AbstractScreenComponent {
|
|
|
1327
1368
|
const cfg = this.getConfig();
|
|
1328
1369
|
const hasDynamicRefs = (cfg?.SEARCH_REFERENTIALS_KEYS?.length ?? 0) > 0;
|
|
1329
1370
|
const hasStaticLists = (cfg?.SEARCH_STATIC_LISTS?.length ?? 0) > 0;
|
|
1371
|
+
// Busy from the first frame, not from the first request.
|
|
1372
|
+
//
|
|
1373
|
+
// A screen with referentials waits for them before it searches at all, and
|
|
1374
|
+
// `loading` used to stay false for that entire window — so the table spent
|
|
1375
|
+
// it showing "nothing here yet" to someone whose data was still being
|
|
1376
|
+
// fetched. Six of the twelve list screens take that branch, including the
|
|
1377
|
+
// one where this was measured at 910ms. `search()` sets the same flag
|
|
1378
|
+
// again when it finally runs, so setting it here is idempotent.
|
|
1379
|
+
this.loading.set(true);
|
|
1330
1380
|
if (hasDynamicRefs || hasStaticLists) {
|
|
1331
1381
|
if (hasDynamicRefs)
|
|
1332
1382
|
this.initializeReferenceKeys(cfg.SEARCH_REFERENTIALS_KEYS);
|
|
@@ -1648,35 +1698,54 @@ class AbstractSearchScreenV2 extends AbstractScreenComponent {
|
|
|
1648
1698
|
}
|
|
1649
1699
|
edit(id) {
|
|
1650
1700
|
if (!id) {
|
|
1651
|
-
this.
|
|
1701
|
+
this.recordNotFound();
|
|
1652
1702
|
return;
|
|
1653
1703
|
}
|
|
1654
1704
|
this.router.navigate([`${this.resolveListUrl()}/details`, id]);
|
|
1655
1705
|
}
|
|
1706
|
+
/**
|
|
1707
|
+
* Name of the row being deleted, so the confirmation can say what it
|
|
1708
|
+
* is about to destroy — "Delete ANDALOCY PARFUMS?" rather than
|
|
1709
|
+
* "Delete?". Override in the screen; returning `undefined` keeps the
|
|
1710
|
+
* generic message, which still reads correctly.
|
|
1711
|
+
*
|
|
1712
|
+
* ```ts
|
|
1713
|
+
* protected override getDeleteConfirmName(id: any) {
|
|
1714
|
+
* return this.items().find(r => r.id === id)?.name;
|
|
1715
|
+
* }
|
|
1716
|
+
* ```
|
|
1717
|
+
*/
|
|
1718
|
+
getDeleteConfirmName(_id) {
|
|
1719
|
+
return undefined;
|
|
1720
|
+
}
|
|
1656
1721
|
delete(id) {
|
|
1657
1722
|
if (!id) {
|
|
1658
|
-
this.
|
|
1723
|
+
this.recordNotFound();
|
|
1659
1724
|
return;
|
|
1660
1725
|
}
|
|
1661
|
-
this.confirmDialogService.confirm(
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1726
|
+
this.confirmDialogService.confirm({
|
|
1727
|
+
intent: 'delete',
|
|
1728
|
+
name: this.getDeleteConfirmName(id),
|
|
1729
|
+
accept: () => this.serviceInstance.delete(id).subscribe({
|
|
1730
|
+
next: (result) => {
|
|
1731
|
+
if (result?.errors?.length) {
|
|
1732
|
+
this.handleErrors(result.errors);
|
|
1733
|
+
}
|
|
1734
|
+
else {
|
|
1735
|
+
this.search();
|
|
1736
|
+
this.toastService.showSuccess();
|
|
1737
|
+
}
|
|
1738
|
+
},
|
|
1739
|
+
// The HTTP error interceptor surfaces the message (toast). Swallow
|
|
1740
|
+
// here so a rejected delete (e.g. a 422 business-rule violation)
|
|
1741
|
+
// doesn't bubble up as an unhandled error.
|
|
1742
|
+
error: () => undefined,
|
|
1743
|
+
}),
|
|
1744
|
+
});
|
|
1676
1745
|
}
|
|
1677
1746
|
duplicate(id) {
|
|
1678
1747
|
if (!id) {
|
|
1679
|
-
this.
|
|
1748
|
+
this.recordNotFound();
|
|
1680
1749
|
return;
|
|
1681
1750
|
}
|
|
1682
1751
|
this.router.navigate([`${this.resolveListUrl()}/details`, id], {
|
|
@@ -1954,27 +2023,46 @@ class AbstractDetailScreenV2 extends AbstractScreenComponent {
|
|
|
1954
2023
|
/**
|
|
1955
2024
|
* Soft-delete-then-navigate-back. Confirms with the user first.
|
|
1956
2025
|
*/
|
|
2026
|
+
/**
|
|
2027
|
+
* Name of the record being deleted, so the confirmation can say what
|
|
2028
|
+
* it is about to destroy — "Delete ANDALOCY PARFUMS?" rather than
|
|
2029
|
+
* "Delete?". Override in the screen; returning `undefined` keeps the
|
|
2030
|
+
* generic message, which still reads correctly.
|
|
2031
|
+
*
|
|
2032
|
+
* ```ts
|
|
2033
|
+
* protected override getDeleteConfirmName() {
|
|
2034
|
+
* return this.entity()?.name;
|
|
2035
|
+
* }
|
|
2036
|
+
* ```
|
|
2037
|
+
*/
|
|
2038
|
+
getDeleteConfirmName() {
|
|
2039
|
+
return undefined;
|
|
2040
|
+
}
|
|
1957
2041
|
delete() {
|
|
1958
2042
|
const id = this.entityId();
|
|
1959
2043
|
if (!id) {
|
|
1960
|
-
this.
|
|
2044
|
+
this.recordNotFound();
|
|
1961
2045
|
return;
|
|
1962
2046
|
}
|
|
1963
|
-
this.confirmDialogService.confirm(
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
2047
|
+
this.confirmDialogService.confirm({
|
|
2048
|
+
intent: 'delete',
|
|
2049
|
+
name: this.getDeleteConfirmName(),
|
|
2050
|
+
accept: () => this.serviceInstance.delete(id).subscribe({
|
|
2051
|
+
next: (result) => {
|
|
2052
|
+
if (result?.errors?.length) {
|
|
2053
|
+
this.handleErrors(result.errors);
|
|
2054
|
+
}
|
|
2055
|
+
else {
|
|
2056
|
+
this.toastService.showSuccess();
|
|
2057
|
+
this.navigateBack();
|
|
2058
|
+
}
|
|
2059
|
+
},
|
|
2060
|
+
// The HTTP error interceptor surfaces the message (toast).
|
|
2061
|
+
// Swallow here so a rejected delete (e.g. a 422 business-rule
|
|
2062
|
+
// violation) doesn't bubble up as an unhandled error.
|
|
2063
|
+
error: () => undefined,
|
|
2064
|
+
}),
|
|
2065
|
+
});
|
|
1978
2066
|
}
|
|
1979
2067
|
/** Navigate to `/details/:id?mode=duplicate` so the abstract can
|
|
1980
2068
|
* reload the source entity, treat it as a template, and persist
|