@elasticias/screens 1.0.48 → 1.0.50

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elasticias/screens",
3
- "version": "1.0.48",
3
+ "version": "1.0.50",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^21.0.0",
6
6
  "@angular/core": "^21.0.0",
@@ -5,7 +5,7 @@ import { Subject, Observable } from 'rxjs';
5
5
  import { Permissions } from '@elasticias/types';
6
6
  import { Router, ActivatedRoute } from '@angular/router';
7
7
  import { NgForm } from '@angular/forms';
8
- import { CacheService, ToastService, ConfirmDialogService } from '@elasticias/core';
8
+ import { CacheService, ToastService, ConfirmDialogService, EfConfirmOptions } from '@elasticias/core';
9
9
  import { Table, TableLazyLoadEvent } from 'primeng/table';
10
10
  import { Location } from '@angular/common';
11
11
 
@@ -185,6 +185,24 @@ declare abstract class ScreenConfig {
185
185
  * to opt out. Must match the backend IAuditable.AuditEntityType (e.g. 'Client').
186
186
  */
187
187
  AUDIT_ENTITY_TYPE?: string;
188
+ /**
189
+ * Detail screens: business-rule codes (422 `ruleCode`) meaning "the record
190
+ * changed since you loaded it", e.g. ERP `['BR_SO_0123']`. A failed update
191
+ * with one of them offers a "reload" confirm; accepting
192
+ * reloads the record and drops the unsaved edits. The app must define the
193
+ * i18n keys `ef_stale_reload` (button), `ef_stale_reload_confirm` (message,
194
+ * say that unsaved edits are lost) and `ef_stale_reload_header`.
195
+ */
196
+ STALE_RULE_CODES?: string[];
197
+ /**
198
+ * Detail screens: after a successful in-place update (not a create or
199
+ * duplicate) refresh the record, so server-owned fields such as a
200
+ * concurrency revision are current for the next save. Uses the update
201
+ * response when it is the saved record (an object whose `id` is the
202
+ * screen's entity id), otherwise one `get(id)`. Change history reloads
203
+ * either way. Default false: the record is left as edited.
204
+ */
205
+ RELOAD_AFTER_SAVE?: boolean;
188
206
  /**
189
207
  * Report screens (AbstractReportScreenV2): starting period preset for the
190
208
  * screen's ef-datepicker-advanced. Unset → 'last_30_days'.
@@ -538,12 +556,34 @@ interface EfDataCardColumn {
538
556
  * always excluded regardless.
539
557
  */
540
558
  hideable?: boolean;
559
+ /**
560
+ * Start the column hidden. The viewer can still show it from the
561
+ * Columns picker, and that choice is saved per `tableKey` through
562
+ * `EF_PREFERENCE_STORE` like any other hidden column, so it wins on
563
+ * the next visit. Use it for a column a wide table can do without at
564
+ * first sight (a sort position, an internal code).
565
+ *
566
+ * Ignored with `hideable: false` and on `select` / `actions`: a column
567
+ * the picker does not list could never be shown again.
568
+ *
569
+ * A table whose preferences were saved before the column became
570
+ * `defaultHidden` keeps the saved list, which shows it.
571
+ */
572
+ defaultHidden?: boolean;
541
573
  /**
542
574
  * Set `false` to keep this column out of a CSV export. Use it for
543
575
  * columns that only make sense on screen — a thumbnail, a rendered
544
576
  * badge with no underlying value. Defaults to exported.
545
577
  */
546
578
  exportable?: boolean;
579
+ /**
580
+ * The value this column writes to a CSV export, given the row. Use it
581
+ * where the screen shows a label composed from codes (a kind plus its
582
+ * rule), so the file says what the screen said. It wins over the
583
+ * built-in reference lookup and raw value; `null`/`undefined` become an
584
+ * empty cell.
585
+ */
586
+ exportValue?: (row: any) => unknown;
547
587
  /** Inline CSS width (e.g. `'40px'`, `'15%'`). */
548
588
  width?: string;
549
589
  /** Extra CSS classes to apply on the `<td>` (e.g. `'num'`). */
@@ -608,6 +648,13 @@ interface EfDataCardExportRequest {
608
648
  resolveCell: (row: unknown, col: EfDataCardColumn) => unknown;
609
649
  }
610
650
 
651
+ /**
652
+ * What a search screen may add to its delete confirmation: the copy, the
653
+ * name, the params. `accept` and `reject` stay with the base, so the
654
+ * delete call, the `errors` handling, the new search and the toast are
655
+ * the same on every screen.
656
+ */
657
+ type EfDeleteConfirmOptions = Partial<Omit<EfConfirmOptions, 'accept' | 'reject' | 'intent'>>;
611
658
  /**
612
659
  * Declarative definition of an advanced-search select filter rendered in
613
660
  * the filter drawer. One `<ef-select>` is rendered per entry; the screen
@@ -662,6 +709,15 @@ interface AdvancedSelectFilter {
662
709
  declare abstract class AbstractSearchScreenV2<TItem = any> extends AbstractScreenComponent implements OnInit {
663
710
  protected readonly screenState = ScreenStateEnum.SEARCH;
664
711
  protected readonly injector: Injector;
712
+ private readonly destroyRef;
713
+ /**
714
+ * A delete confirm is loading or open. A second click (or the row
715
+ * shortcut) meanwhile is ignored, so one gesture cannot start two impact
716
+ * reads or stack two confirms. Cleared when the confirm is answered
717
+ * (PrimeNG calls reject on close and Escape too) or when the read
718
+ * yields no confirm.
719
+ */
720
+ private deleteConfirmBusy;
665
721
  private serviceInstance;
666
722
  /**
667
723
  * Translate lazily, through the injector.
@@ -866,6 +922,13 @@ declare abstract class AbstractSearchScreenV2<TItem = any> extends AbstractScree
866
922
  * criteria to cache on success so revisits can restore.
867
923
  */
868
924
  search(): void;
925
+ /**
926
+ * Hook for a screen whose search returns more than a page of rows (counts,
927
+ * facets, a summary): called with the raw response after `items` and
928
+ * `totalCount` are set, for the latest issued search only. A stale or
929
+ * failed search never reaches it.
930
+ */
931
+ protected afterSearch(_result: unknown): void;
869
932
  setSearchText(text: string): void;
870
933
  setPage(pageNumber: number, pageSize?: number): void;
871
934
  setSort(field: string, direction?: string): void;
@@ -929,7 +992,33 @@ declare abstract class AbstractSearchScreenV2<TItem = any> extends AbstractScree
929
992
  * ```
930
993
  */
931
994
  protected getDeleteConfirmName(_id: any): string | undefined;
995
+ /**
996
+ * Async details for the delete confirmation, read before it opens. Use
997
+ * it when the confirm has to say what the delete will take with it, such
998
+ * as how many products lose a collection:
999
+ *
1000
+ * ```ts
1001
+ * protected override getDeleteConfirmOptions(id: any) {
1002
+ * return this.collectionsClient.deleteImpact(id).pipe(
1003
+ * map(impact => ({
1004
+ * messageKey: impact.onHome ? 'x_delete_message_home' : 'x_delete_message',
1005
+ * messageParams: { count: impact.productCount ?? 0 },
1006
+ * })),
1007
+ * );
1008
+ * }
1009
+ * ```
1010
+ *
1011
+ * The returned options are merged over `{ intent: 'delete', name:
1012
+ * getDeleteConfirmName(id) }`. Only the first value of an Observable is
1013
+ * used. If the read fails (the HTTP interceptor toasts it) or completes
1014
+ * without a value, no confirm opens and nothing is deleted. Return
1015
+ * `undefined`, the default, to open the confirm at once. While the read
1016
+ * is in flight or the confirm is open, another delete is ignored, and a
1017
+ * screen destroyed during the read opens nothing.
1018
+ */
1019
+ protected getDeleteConfirmOptions(_id: any): Observable<EfDeleteConfirmOptions> | Promise<EfDeleteConfirmOptions> | undefined;
932
1020
  delete(id: any): void;
1021
+ private openDeleteConfirm;
933
1022
  duplicate(id: any): void;
934
1023
  ngOnDestroy(): void;
935
1024
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AbstractSearchScreenV2<any>, never>;
@@ -977,6 +1066,9 @@ interface EfDetailToolbarAction {
977
1066
  visible?: boolean;
978
1067
  /** Disable without removing. */
979
1068
  disabled?: boolean;
1069
+ /** Translation key saying why the action is disabled; shown in the
1070
+ * phone overflow menu under the label and as its description. */
1071
+ disabledReasonKey?: string;
980
1072
  /**
981
1073
  * If set, the action is hidden unless the bound `ScreenContext`
982
1074
  * grants this permission. Actions with no `permission` always
@@ -991,6 +1083,14 @@ interface EfDetailToolbarAction {
991
1083
  * context with no write state (no `canWrite`) leaves the action shown.
992
1084
  */
993
1085
  requiresWrite?: boolean;
1086
+ /**
1087
+ * On a phone, when the actions overflow into the menu, this one stays
1088
+ * inline beside Save (a screen's next step, which should never be one
1089
+ * tap deeper). Filtering by `visible`, `permission` and `requiresWrite`
1090
+ * still applies first. Ignored on tablet and desktop, which never
1091
+ * overflow. Keep it to one action: each one kept takes row width.
1092
+ */
1093
+ keepInView?: boolean;
994
1094
  /** Click handler. */
995
1095
  command?: () => void;
996
1096
  }
@@ -1141,6 +1241,8 @@ declare abstract class AbstractDetailScreenV2<TItem extends object = any> extend
1141
1241
  patchEntity(patch: Partial<TItem>): void;
1142
1242
  /** Fetch the entity from the backend and populate `entity`. */
1143
1243
  loadData(): void;
1244
+ /** Install a fetched (or save-returned) record as the current entity. */
1245
+ private applyLoaded;
1144
1246
  /**
1145
1247
  * Persist the entity. Routes to `service.create()` when in new
1146
1248
  * or duplicate mode, `service.update(id, entity)` when editing.
@@ -1151,6 +1253,23 @@ declare abstract class AbstractDetailScreenV2<TItem extends object = any> extend
1151
1253
  * anyway.
1152
1254
  */
1153
1255
  save(): void;
1256
+ /**
1257
+ * True when a failed save means the record changed since it was loaded:
1258
+ * a refusal whose `ruleCode` is in the config's `STALE_RULE_CODES`. A bare
1259
+ * 409 is a duplicate (e.g. a slug already taken), not a stale record, so
1260
+ * it never offers a reload. Only a refusal
1261
+ * the app's interceptor already toasted counts, so the user is never
1262
+ * told twice.
1263
+ */
1264
+ protected isStaleSave(error: unknown): boolean;
1265
+ /**
1266
+ * Asks, in an accessible confirm dialog (focus trapped, Escape cancels),
1267
+ * whether to reload the record. Accepting discards the unsaved edits,
1268
+ * and the message says so. The refusal itself was already toasted.
1269
+ * App keys: `ef_stale_reload` (button), `ef_stale_reload_confirm`
1270
+ * (message) and optionally `ef_stale_reload_header`.
1271
+ */
1272
+ protected offerStaleReload(): void;
1154
1273
  /**
1155
1274
  * Soft-delete-then-navigate-back. Confirms with the user first.
1156
1275
  */
@@ -1427,4 +1546,4 @@ declare class ViewModelEntity extends AbstractEntity {
1427
1546
  type EfSearchToolbarAction = EfDetailToolbarAction;
1428
1547
 
1429
1548
  export { AUDIT_HISTORY_SERVICE, AbstractComponent, AbstractDetailScreenComponent, AbstractDetailScreenV2, AbstractEntity, AbstractReportScreenV2, AbstractScreenComponent, AbstractSearchScreenComponent, AbstractSearchScreenV2, AbstractSubScreenComponent, AbstractSubScreenV2, PaginationEnum, SCREEN_REF_DATA_SERVICE, ScreenConfig, ScreenContext, ScreenStateEnum, SearchEntity, SortDirectionEnum, StateUtilsEnum, ViewModelEntity };
1430
- export type { ActiveFilter, AdvancedSelectFilter, AuditHistoryService, DefaultSort, EfBoundedDateRange, EfDataCardColumn, EfDataCardColumnAlign, EfDataCardColumnMobileRole, EfDataCardColumnType, EfDataCardExportRequest, EfDataCardSort, EfDataCardSortDirection, EfDatePresetKey, EfDateRange, EfDetailToolbarAction, EfReferenceColumnOpts, EfSearchToolbarAction, LoadOptions, ReferenceDataProvider, ReportWidget, ReportWidgetStatus, ScreenDatatableColumn, ScreenReferenceDataService };
1549
+ export type { ActiveFilter, AdvancedSelectFilter, AuditHistoryService, DefaultSort, EfBoundedDateRange, EfDataCardColumn, EfDataCardColumnAlign, EfDataCardColumnMobileRole, EfDataCardColumnType, EfDataCardExportRequest, EfDataCardSort, EfDataCardSortDirection, EfDatePresetKey, EfDateRange, EfDeleteConfirmOptions, EfDetailToolbarAction, EfReferenceColumnOpts, EfSearchToolbarAction, LoadOptions, ReferenceDataProvider, ReportWidget, ReportWidgetStatus, ScreenDatatableColumn, ScreenReferenceDataService };