@masterteam/work-center 0.0.61 → 0.0.62

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": "@masterteam/work-center",
3
- "version": "0.0.61",
3
+ "version": "0.0.62",
4
4
  "publishConfig": {
5
5
  "directory": "../../../dist/masterteam/work-center",
6
6
  "linkDirectory": false,
@@ -20,12 +20,12 @@
20
20
  "rxjs": "^7.8.2",
21
21
  "tailwindcss": "^4.2.2",
22
22
  "tailwindcss-primeui": "^0.6.1",
23
- "@masterteam/client-components": "^0.0.66",
24
- "@masterteam/components": "^0.0.238",
25
- "@masterteam/icons": "^0.0.17",
26
- "@masterteam/forms": "^0.0.126",
23
+ "@masterteam/client-components": "^0.0.67",
27
24
  "@masterteam/discussion": "^0.0.26",
28
- "@masterteam/structure-builder": "^0.0.61"
25
+ "@masterteam/structure-builder": "^0.0.61",
26
+ "@masterteam/forms": "^0.0.126",
27
+ "@masterteam/icons": "^0.0.17",
28
+ "@masterteam/components": "^0.0.239"
29
29
  },
30
30
  "sideEffects": false,
31
31
  "exports": {
@@ -1,5 +1,5 @@
1
1
  import * as _masterteam_components_table from '@masterteam/components/table';
2
- import { ColumnDef, TableFilters } from '@masterteam/components/table';
2
+ import { ColumnDef, TableFilters, TablePersistStateKey } from '@masterteam/components/table';
3
3
  import * as _masterteam_components_statistic_card from '@masterteam/components/statistic-card';
4
4
  import { StatisticCardData } from '@masterteam/components/statistic-card';
5
5
  import * as _masterteam_work_center from '@masterteam/work-center';
@@ -28,6 +28,22 @@ declare const WORK_CENTER_QUERY_VERSION = 1;
28
28
  declare const WORK_CENTER_MAX_PAGE_SIZE = 100;
29
29
  declare const WORK_CENTER_MAX_FILTERS = 20;
30
30
  declare const WORK_CENTER_MAX_SORT = 5;
31
+ /**
32
+ * Backend column key carrying the row's LevelData display label
33
+ * (`extra.levelDataName`). Grouping by it maps to a single backend sort on the
34
+ * same key — see `WORK_CENTER_LEVEL_GROUP_FIELD` for the grouping identity.
35
+ */
36
+ declare const WORK_CENTER_LEVEL_NAME_KEY = "levelDataName";
37
+ /**
38
+ * Synthetic per-row field holding the *identity* behind the Level Name column.
39
+ * The visible label is ambiguous (two LevelData records may share a name), so
40
+ * rows are bucketed by `context.levelDataId` instead — except rows with no
41
+ * visible label, which get an empty identity so they all collapse into the one
42
+ * shared "—" group the backend contract requires.
43
+ */
44
+ declare const WORK_CENTER_LEVEL_GROUP_FIELD = "__mtLevelGroup";
45
+ /** Rendered in place of a missing / restricted LevelData label. */
46
+ declare const WORK_CENTER_EMPTY_LABEL = "\u2014";
31
47
  interface ApiEnvelope<T> {
32
48
  endpoint: string | null;
33
49
  status: number;
@@ -208,6 +224,11 @@ interface WorkCenterAreaState {
208
224
  * Same `(field, op)` pair from this slice overrides a header rule.
209
225
  */
210
226
  columnFilters: WorkCenterFilterRule[];
227
+ /**
228
+ * Column key the table groups rows by. UI-only — never part of the runtime
229
+ * request; grouping is expressed to the backend as `context.sort`.
230
+ */
231
+ groupBy: string | null;
211
232
  menuItems: ClientPageMenuItem[];
212
233
  rows: Record<string, unknown>[];
213
234
  columns: ColumnDef[];
@@ -239,6 +260,8 @@ interface WorkCenterBootstrapInputs {
239
260
  levelDataId?: unknown;
240
261
  sort?: unknown;
241
262
  filters?: unknown;
263
+ /** Grouped column key, so a grouped view survives reload / deep-link. */
264
+ group?: unknown;
242
265
  }
243
266
  interface WorkCenterItemModalComponentInput {
244
267
  contextKey: WorkCenterItemContextKey;
@@ -267,7 +290,7 @@ interface WorkCenterClientFormModalOptions {
267
290
  buttonIcon?: string;
268
291
  lookups?: ClientLookup[];
269
292
  }
270
- type WorkCenterLoadReason = 'route-enter' | 'menu-change' | 'filters-change' | 'table-lazy' | 'refresh' | 'manual';
293
+ type WorkCenterLoadReason = 'route-enter' | 'menu-change' | 'filters-change' | 'group-change' | 'table-lazy' | 'refresh' | 'manual';
271
294
 
272
295
  declare class WorkCenterPage {
273
296
  private readonly facade;
@@ -289,6 +312,15 @@ declare class WorkCenterPage {
289
312
  readonly runtimeFiltersChanged: _angular_core.OutputEmitterRef<WorkCenterFilterRule[]>;
290
313
  readonly itemClicked: _angular_core.OutputEmitterRef<string>;
291
314
  readonly cardSelected: _angular_core.OutputEmitterRef<string>;
315
+ /**
316
+ * Emitted whenever the backend sort changes (including grouping, which is
317
+ * sent as a sort). Hosts should persist it — the runtime query is rebuilt
318
+ * from `bootstrapInputs`, so a sort that isn't round-tripped is dropped on
319
+ * the next navigation.
320
+ */
321
+ readonly sortChanged: _angular_core.OutputEmitterRef<WorkCenterSortRule[]>;
322
+ /** Emitted when the grouped column changes; `null` when grouping is cleared. */
323
+ readonly groupChanged: _angular_core.OutputEmitterRef<string | null>;
292
324
  protected readonly context: _angular_core.Signal<_masterteam_work_center.WorkCenterContext>;
293
325
  protected readonly headerFilters: _angular_core.Signal<WorkCenterFilterRule[]>;
294
326
  protected readonly tableFilters: _angular_core.WritableSignal<TableFilters>;
@@ -312,6 +344,14 @@ declare class WorkCenterPage {
312
344
  protected readonly totalCount: _angular_core.Signal<number>;
313
345
  protected readonly columnFilters: _angular_core.Signal<WorkCenterFilterRule[]>;
314
346
  protected readonly loading: _angular_core.Signal<boolean>;
347
+ protected readonly groupBy: _angular_core.Signal<string | null>;
348
+ /**
349
+ * Sort and grouping live in the runtime query (and the host's URL), not in
350
+ * table storage — persisting them locally would let a restored table claim a
351
+ * grouping the backend query no longer has. Stable reference: a fresh array
352
+ * per change detection would re-run the table's persistence effect.
353
+ */
354
+ protected readonly persistStateExclude: TablePersistStateKey[];
315
355
  protected readonly tableColumns: _angular_core.Signal<{
316
356
  sortable: boolean;
317
357
  key: string;
@@ -320,18 +360,28 @@ declare class WorkCenterPage {
320
360
  customCellTpl?: _angular_core.TemplateRef<any>;
321
361
  filterConfig?: _masterteam_components_table.FilterConfig;
322
362
  statusMap?: Record<string, _masterteam_components_table.TableStatusMapValue>;
363
+ groupable?: boolean;
364
+ groupKey?: string;
323
365
  readonly?: boolean;
324
366
  width?: string;
325
367
  filterOnly?: boolean;
326
368
  exportable?: boolean;
327
369
  exportValue?: (row: any) => string | number | boolean | null;
328
370
  }[]>;
371
+ /**
372
+ * Grouping is executed by the backend as a single sort, so the table's own
373
+ * sort state is driven from the query we actually sent instead of the
374
+ * table's internal memory of it.
375
+ */
376
+ protected readonly sortField: _angular_core.Signal<string>;
377
+ protected readonly sortDirection: _angular_core.Signal<"asc" | "desc" | null>;
329
378
  /**
330
379
  * Maps each status/group display label to its total count from the KPI
331
380
  * summary cards so the group header shows the real backend total rather than
332
- * the current-page row count.
381
+ * the current-page row count. Withheld for groups the backend doesn't
382
+ * aggregate — see `PAGE_LOCAL_GROUP_KEYS`.
333
383
  */
334
- protected readonly statusGroupCountMap: _angular_core.Signal<Map<string, number>>;
384
+ protected readonly groupCountMap: _angular_core.Signal<Map<string, number> | null>;
335
385
  protected readonly propertyFilterSchema: _angular_core.Signal<PropertyFilterBuilderSchema | null>;
336
386
  protected readonly showGeneralTaskCreateButton: _angular_core.Signal<boolean>;
337
387
  protected readonly generalTaskButtonLabel: _angular_core.Signal<string>;
@@ -349,14 +399,27 @@ declare class WorkCenterPage {
349
399
  * (handled by `applyColumnFiltersAndLoad`); when only paging/sort changed
350
400
  * we fall through to `applyTableLazyLoadAndLoad`.
351
401
  */
402
+ /**
403
+ * The table asks to group; the backend does the ordering. `setGroupByAndLoad`
404
+ * rewrites `context.sort` and resets paging, so the lazy event the table
405
+ * fires straight after carries the same sort and lands as a no-op.
406
+ */
407
+ onTableGroupByChange(key: string | null): void;
352
408
  onTableLazyLoad(event: any): void;
409
+ /**
410
+ * Announces the backend sort to the host only when it actually moved, so a
411
+ * host that mirrors it into the URL doesn't churn history on every page hit.
412
+ * Reads the snapshot, not the signal — this runs in the same tick as the
413
+ * dispatch that changed it.
414
+ */
415
+ private emitSortIfChanged;
353
416
  onGeneralTaskCreateClick(): void;
354
417
  onRowClick(row: Record<string, unknown>): void;
355
418
  protected readonly rowsClickable: _angular_core.Signal<boolean>;
356
419
  private getOpLabel;
357
420
  private resolveRowContextKey;
358
421
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<WorkCenterPage, never>;
359
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<WorkCenterPage, "mt-work-center-page", never, { "area": { "alias": "area"; "required": false; "isSignal": true; }; "pageTitle": { "alias": "pageTitle"; "required": false; "isSignal": true; }; "menuIcon": { "alias": "menuIcon"; "required": false; "isSignal": true; }; "lookups": { "alias": "lookups"; "required": false; "isSignal": true; }; "showSidebar": { "alias": "showSidebar"; "required": false; "isSignal": true; }; "bootstrapInputs": { "alias": "bootstrapInputs"; "required": false; "isSignal": true; }; "openItemDrawer": { "alias": "openItemDrawer"; "required": false; "isSignal": true; }; "itemReadOnly": { "alias": "itemReadOnly"; "required": false; "isSignal": true; }; "openItemsInModal": { "alias": "openItemsInModal"; "required": false; "isSignal": true; }; "itemModal": { "alias": "itemModal"; "required": false; "isSignal": true; }; "generalTaskModal": { "alias": "generalTaskModal"; "required": false; "isSignal": true; }; }, { "runtimeFiltersChanged": "runtimeFiltersChanged"; "itemClicked": "itemClicked"; "cardSelected": "cardSelected"; }, never, never, true, never>;
422
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<WorkCenterPage, "mt-work-center-page", never, { "area": { "alias": "area"; "required": false; "isSignal": true; }; "pageTitle": { "alias": "pageTitle"; "required": false; "isSignal": true; }; "menuIcon": { "alias": "menuIcon"; "required": false; "isSignal": true; }; "lookups": { "alias": "lookups"; "required": false; "isSignal": true; }; "showSidebar": { "alias": "showSidebar"; "required": false; "isSignal": true; }; "bootstrapInputs": { "alias": "bootstrapInputs"; "required": false; "isSignal": true; }; "openItemDrawer": { "alias": "openItemDrawer"; "required": false; "isSignal": true; }; "itemReadOnly": { "alias": "itemReadOnly"; "required": false; "isSignal": true; }; "openItemsInModal": { "alias": "openItemsInModal"; "required": false; "isSignal": true; }; "itemModal": { "alias": "itemModal"; "required": false; "isSignal": true; }; "generalTaskModal": { "alias": "generalTaskModal"; "required": false; "isSignal": true; }; }, { "runtimeFiltersChanged": "runtimeFiltersChanged"; "itemClicked": "itemClicked"; "cardSelected": "cardSelected"; "sortChanged": "sortChanged"; "groupChanged": "groupChanged"; }, never, never, true, never>;
360
423
  }
361
424
 
362
425
  declare class WorkCenterClientFormModal {
@@ -495,6 +558,18 @@ declare class ApplyColumnFilters {
495
558
  static readonly type = "[WorkCenter] Apply Column Filters";
496
559
  constructor(area: WorkCenterArea, filters: WorkCenterFilterRule[]);
497
560
  }
561
+ /**
562
+ * Sets the grouped column. Grouping is expressed to the backend as a single
563
+ * sort on the same field, so this also rewrites `context.sort` and resets
564
+ * `page` to 1 before reloading. `groupBy: null` clears both.
565
+ */
566
+ declare class SetGroupBy {
567
+ area: WorkCenterArea;
568
+ groupBy: string | null;
569
+ dir: 'asc' | 'desc';
570
+ static readonly type = "[WorkCenter] Set Group By";
571
+ constructor(area: WorkCenterArea, groupBy: string | null, dir?: 'asc' | 'desc');
572
+ }
498
573
  /**
499
574
  * Clears both the header filter slice and the column filter slice in one
500
575
  * state update, triggering a single BE reload.
@@ -508,7 +583,7 @@ declare class ClearAllFilters {
508
583
  declare class WorkCenterFacade {
509
584
  private readonly store;
510
585
  readonly activeArea: _angular_core.Signal<WorkCenterArea>;
511
- readonly context: _angular_core.Signal<_masterteam_work_center.WorkCenterContext>;
586
+ readonly context: _angular_core.Signal<WorkCenterContext>;
512
587
  readonly menuItems: _angular_core.Signal<_masterteam_components_types_masterteam_components_client_page_menu.ClientPageMenuItem[]>;
513
588
  readonly rows: _angular_core.Signal<Record<string, unknown>[]>;
514
589
  readonly columns: _angular_core.Signal<_masterteam_components_types_masterteam_components_table.ColumnDef[]>;
@@ -517,12 +592,20 @@ declare class WorkCenterFacade {
517
592
  readonly runtimeFilterSchema: _angular_core.Signal<_masterteam_work_center.WorkCenterRuntimeFilterSchema | null>;
518
593
  readonly headerFilters: _angular_core.Signal<WorkCenterFilterRule[]>;
519
594
  readonly columnFilters: _angular_core.Signal<WorkCenterFilterRule[]>;
595
+ readonly groupBy: _angular_core.Signal<string | null>;
520
596
  readonly totalCount: _angular_core.Signal<number>;
521
597
  readonly warnings: _angular_core.Signal<_masterteam_work_center.WorkCenterRuntimeWarningDto[]>;
522
598
  private readonly loadingActive;
523
599
  private readonly errors;
524
600
  readonly loading: _angular_core.Signal<boolean>;
525
601
  readonly error: _angular_core.Signal<string | null>;
602
+ /**
603
+ * Snapshot reads for callers that must observe state *immediately* after a
604
+ * dispatch — e.g. an output handler mirroring the runtime query into the URL.
605
+ * The `select()` signals above are the right choice everywhere else.
606
+ */
607
+ snapshotContext(): WorkCenterContext;
608
+ snapshotGroupBy(): string | null;
526
609
  enterArea(area: WorkCenterArea): rxjs.Observable<void>;
527
610
  hydrateContext(area: WorkCenterArea, patch: WorkCenterHydratePatch): rxjs.Observable<void>;
528
611
  setParams(area: WorkCenterArea, inputs: WorkCenterBootstrapInputs): rxjs.Observable<void>;
@@ -540,6 +623,13 @@ declare class WorkCenterFacade {
540
623
  * and reloads with merged `headerFilters` + `columnFilters`.
541
624
  */
542
625
  applyColumnFiltersAndLoad(area: WorkCenterArea, runtimeFilters: WorkCenterFilterRule[]): rxjs.Observable<void>;
626
+ /**
627
+ * Groups the table by `groupBy` (or clears grouping with `null`). Pushes the
628
+ * grouped column down as the single backend sort, resets to page 1 and
629
+ * reloads. Safe to call from the table's `groupByChange` — the lazy event
630
+ * that follows carries the same sort and is absorbed as a no-op.
631
+ */
632
+ setGroupByAndLoad(area: WorkCenterArea, groupBy: string | null, dir?: 'asc' | 'desc'): rxjs.Observable<void>;
543
633
  /**
544
634
  * Clears both header and column filter slices in one state update,
545
635
  * triggering a single BE reload.
@@ -547,6 +637,12 @@ declare class WorkCenterFacade {
547
637
  clearAllFiltersAndLoad(area: WorkCenterArea): rxjs.Observable<void>;
548
638
  loadRuntime(area: WorkCenterArea, reason?: WorkCenterLoadReason): rxjs.Observable<void>;
549
639
  private toPositiveInt;
640
+ /**
641
+ * `mt-table` always reports its sort on a lazy event, so a `null` field is an
642
+ * explicit "no sort" and must clear `context.sort` (the BE contract asks for
643
+ * `sort: []`). Only an event with no `sortField` key at all — nothing the
644
+ * current table emits — leaves the existing sort alone.
645
+ */
550
646
  private toSortFromLazyEvent;
551
647
  private isSameSort;
552
648
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<WorkCenterFacade, never>;
@@ -566,6 +662,7 @@ declare class WorkCenterState {
566
662
  static runtimeFilterSchema(snapshot: WorkCenterAreaState): WorkCenterRuntimeFilterSchema | null;
567
663
  static headerFilters(snapshot: WorkCenterAreaState): WorkCenterFilterRule[];
568
664
  static columnFilters(snapshot: WorkCenterAreaState): WorkCenterFilterRule[];
665
+ static groupBy(snapshot: WorkCenterAreaState): string | null;
569
666
  static totalCount(snapshot: WorkCenterAreaState): number;
570
667
  static warnings(snapshot: WorkCenterAreaState): _masterteam_work_center.WorkCenterRuntimeWarningDto[];
571
668
  static lookups(state: WorkCenterStateModel): _masterteam_work_center.WorkCenterLookups;
@@ -577,6 +674,12 @@ declare class WorkCenterState {
577
674
  setLookups(ctx: StateContext<WorkCenterStateModel>, action: SetLookups): void;
578
675
  applyHeaderFilters(ctx: StateContext<WorkCenterStateModel>, action: ApplyHeaderFilters): rxjs.Observable<void> | undefined;
579
676
  applyColumnFilters(ctx: StateContext<WorkCenterStateModel>, action: ApplyColumnFilters): rxjs.Observable<void> | undefined;
677
+ /**
678
+ * Grouping is a backend concern: the table only renders contiguous rows, so
679
+ * the grouped column is pushed down as the single `context.sort` entry and
680
+ * pagination restarts at page 1. Clearing the group sends `sort: []`.
681
+ */
682
+ setGroupBy(ctx: StateContext<WorkCenterStateModel>, action: SetGroupBy): rxjs.Observable<void> | undefined;
580
683
  clearAllFilters(ctx: StateContext<WorkCenterStateModel>, action: ClearAllFilters): rxjs.Observable<void> | undefined;
581
684
  private applyFilterSlice;
582
685
  loadRuntime(ctx: StateContext<WorkCenterStateModel>, action: LoadRuntime): rxjs.Observable<WorkCenterRuntimeResponseData>;
@@ -587,5 +690,5 @@ declare class WorkCenterState {
587
690
 
588
691
  declare const APP_STATES: (typeof WorkCenterState)[];
589
692
 
590
- export { APP_STATES, ApplyColumnFilters, ApplyHeaderFilters, ClearAllFilters, EnterArea, HydrateFromContext, LoadRuntime, SetLookups, SetParams, WORK_CENTER_MAX_FILTERS, WORK_CENTER_MAX_PAGE_SIZE, WORK_CENTER_MAX_SORT, WORK_CENTER_QUERY_VERSION, WorkCenterActionKey, WorkCenterClientFormModal, WorkCenterFacade, WorkCenterItemModal, WorkCenterItemModalRoute, WorkCenterPage, WorkCenterState, decodeWorkCenterRouteContextKey, encodeWorkCenterRouteContextKey };
693
+ export { APP_STATES, ApplyColumnFilters, ApplyHeaderFilters, ClearAllFilters, EnterArea, HydrateFromContext, LoadRuntime, SetGroupBy, SetLookups, SetParams, WORK_CENTER_EMPTY_LABEL, WORK_CENTER_LEVEL_GROUP_FIELD, WORK_CENTER_LEVEL_NAME_KEY, WORK_CENTER_MAX_FILTERS, WORK_CENTER_MAX_PAGE_SIZE, WORK_CENTER_MAX_SORT, WORK_CENTER_QUERY_VERSION, WorkCenterActionKey, WorkCenterClientFormModal, WorkCenterFacade, WorkCenterItemModal, WorkCenterItemModalRoute, WorkCenterPage, WorkCenterState, decodeWorkCenterRouteContextKey, encodeWorkCenterRouteContextKey };
591
694
  export type { ApiEnvelope, TranslatableDto, WorkCenterArea, WorkCenterAreaState, WorkCenterBootstrapInputs, WorkCenterClientFormModalData, WorkCenterClientFormModalOptions, WorkCenterColumnsConfig, WorkCenterContext, WorkCenterFilterOperator, WorkCenterFilterRule, WorkCenterHydratePatch, WorkCenterItemActionAfterSuccess, WorkCenterItemActionExecutionResult, WorkCenterItemActionsData, WorkCenterItemBasicInfoData, WorkCenterItemContextKey, WorkCenterItemModalComponentInput, WorkCenterItemModalOptions, WorkCenterLoadReason, WorkCenterLookups, WorkCenterPreviewLevelTarget, WorkCenterRuntimeCard, WorkCenterRuntimeFilterSchema, WorkCenterRuntimeItemAction, WorkCenterRuntimeOperatorOption, WorkCenterRuntimePagination, WorkCenterRuntimeProperty, WorkCenterRuntimeRequest, WorkCenterRuntimeResponseData, WorkCenterRuntimeSelectedCard, WorkCenterRuntimeStatsItem, WorkCenterRuntimeWarningDto, WorkCenterSortRule, WorkCenterStateModel };