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