@masterteam/work-center 0.0.62 → 0.0.64

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.
@@ -168,6 +168,22 @@ class ApplyColumnFilters {
168
168
  this.filters = filters;
169
169
  }
170
170
  }
171
+ /**
172
+ * Switches the selected card and drops everything that was scoped to the
173
+ * previous one: advanced filters, in-table column filters, grouping and the
174
+ * sort that grouping implied. Each card brings its own columns and filter
175
+ * schema, so carrying those over would filter on fields the new card doesn't
176
+ * have and group by a column it doesn't show.
177
+ */
178
+ class SelectCard {
179
+ area;
180
+ selectedCardKey;
181
+ static type = '[WorkCenter] Select Card';
182
+ constructor(area, selectedCardKey) {
183
+ this.area = area;
184
+ this.selectedCardKey = selectedCardKey;
185
+ }
186
+ }
171
187
  /**
172
188
  * Sets the grouped column. Grouping is expressed to the backend as a single
173
189
  * sort on the same field, so this also rewrites `context.sort` and resets
@@ -220,6 +236,13 @@ const WORK_CENTER_LEVEL_NAME_KEY = 'levelDataName';
220
236
  const WORK_CENTER_LEVEL_GROUP_FIELD = '__mtLevelGroup';
221
237
  /** Rendered in place of a missing / restricted LevelData label. */
222
238
  const WORK_CENTER_EMPTY_LABEL = '—';
239
+ /**
240
+ * Bootstrap `group` value meaning "the user turned grouping off". Needed
241
+ * because an absent `group` means "not chosen yet" and falls back to the
242
+ * default grouping — without a distinct value, clearing the group would be
243
+ * undone by the next navigation.
244
+ */
245
+ const WORK_CENTER_GROUP_NONE = 'none';
223
246
 
224
247
  const AREA_COLORS = ['violet', 'blue', 'emerald', 'amber', 'teal'];
225
248
  /** Fallback accent rotation used only when the backend sends no stat color. */
@@ -306,15 +329,30 @@ function parseSortInput(value) {
306
329
  : [];
307
330
  }
308
331
  /**
309
- * Resolves the grouped column key from bootstrap inputs. Mirrors the `sort`
310
- * rules: a present value wins, an explicit `null` clears, `undefined` keeps
311
- * whatever is already in state.
332
+ * Resolves the grouped column key from bootstrap inputs.
333
+ *
334
+ * A missing `group` keeps whatever state already has, so an unrelated
335
+ * navigation doesn't drop the user's grouping. `WORK_CENTER_GROUP_NONE` is the
336
+ * only way to express "turn it off" — a bare `null` can't, because it is also
337
+ * what a host passes for an absent query param.
312
338
  */
313
339
  function resolveGroupByFromInputs(current, group) {
314
- if (hasParamValue(group)) {
315
- return typeof group === 'string' ? group.trim() || null : null;
340
+ if (!hasParamValue(group) || typeof group !== 'string') {
341
+ return current;
342
+ }
343
+ const parsed = group.trim();
344
+ if (!parsed || parsed === WORK_CENTER_GROUP_NONE) {
345
+ return null;
316
346
  }
317
- return group === null ? null : current;
347
+ return parsed;
348
+ }
349
+ /**
350
+ * The card these inputs resolve to — the same rule `buildContextFromInputs`
351
+ * applies, exposed so callers can detect a card switch before building the
352
+ * context (what survives the switch depends on it).
353
+ */
354
+ function resolveSelectedCardKeyFromInputs(currentContext, inputs = {}) {
355
+ return toSelectedCardKey(inputs.card) ?? currentContext.selectedCardKey;
318
356
  }
319
357
  function parseFiltersInput(value) {
320
358
  const parsed = parseJson(value);
@@ -543,6 +581,10 @@ function buildKpis(stats, fallbackIcon) {
543
581
  color: stat.color?.trim() || KPI_COLORS[index % KPI_COLORS.length],
544
582
  }));
545
583
  }
584
+ /** The backend sort that renders a grouped column as contiguous rows. */
585
+ function sortForGroup(groupBy) {
586
+ return groupBy ? [{ field: groupBy, dir: 'asc' }] : [];
587
+ }
546
588
  function createDefaultContext(area) {
547
589
  return {
548
590
  area,
@@ -611,7 +653,14 @@ function normalizeContext(context) {
611
653
  runtimeFilters: sanitizeFilters(context.runtimeFilters),
612
654
  };
613
655
  }
614
- function buildContextFromInputs(currentContext, area, inputs = {}) {
656
+ function buildContextFromInputs(currentContext, area, inputs = {},
657
+ /**
658
+ * Already-resolved grouped column. Grouping is executed as a backend sort,
659
+ * so when the host supplies no explicit `sort` the grouped column defines
660
+ * it — that is what makes the default grouping reach the very first request
661
+ * instead of costing a second round trip.
662
+ */
663
+ groupBy = null) {
615
664
  const parsedCardKey = toSelectedCardKey(inputs.card);
616
665
  const parsedTemplateId = toOptionalInt(inputs.templateId);
617
666
  const parsedLevelDataId = toOptionalInt(inputs.levelDataId);
@@ -628,9 +677,11 @@ function buildContextFromInputs(currentContext, area, inputs = {}) {
628
677
  levelDataId: parsedLevelDataId ?? currentContext.levelDataId,
629
678
  sort: hasParamValue(inputs.sort)
630
679
  ? parseSortInput(inputs.sort)
631
- : inputs.sort === null
632
- ? []
633
- : currentContext.sort,
680
+ : groupBy
681
+ ? sortForGroup(groupBy)
682
+ : inputs.sort === null
683
+ ? []
684
+ : currentContext.sort,
634
685
  runtimeFilters: hasParamValue(inputs.filters)
635
686
  ? parseFiltersInput(inputs.filters)
636
687
  : inputs.filters === null
@@ -664,8 +715,20 @@ function mapRuntimeResponse(current, response) {
664
715
  const columns = buildColumns(selectedCard?.columnsConfig ?? selectedCardFromList?.columnsConfig);
665
716
  const rows = toDisplayRows(selectedCard?.items ?? [], columns);
666
717
  const kpis = buildKpis(selectedCard?.stats ?? [], selectedMenuItem?.icon ?? null);
718
+ // A card that doesn't advertise the grouped column can't be grouped by it —
719
+ // drop it here rather than letting the table discover the stale column and
720
+ // bounce a second request. The sort goes with it when it was the one the
721
+ // grouping asked for.
722
+ const groupBy = current.groupBy && columns.some((column) => column.key === current.groupBy)
723
+ ? current.groupBy
724
+ : null;
725
+ const droppedGroup = !groupBy && current.groupBy;
726
+ const sort = droppedGroup && isSortForGroup(current.context.sort, current.groupBy)
727
+ ? []
728
+ : current.context.sort;
667
729
  return {
668
730
  ...current,
731
+ groupBy,
669
732
  menuItems,
670
733
  rows,
671
734
  columns,
@@ -679,11 +742,16 @@ function mapRuntimeResponse(current, response) {
679
742
  context: {
680
743
  ...current.context,
681
744
  selectedCardKey,
745
+ sort,
682
746
  page: selectedCard?.pagination.page ?? current.context.page,
683
747
  pageSize: selectedCard?.pagination.pageSize ?? current.context.pageSize,
684
748
  },
685
749
  };
686
750
  }
751
+ /** True when `sort` is exactly the single rule a grouping would produce. */
752
+ function isSortForGroup(sort, groupBy) {
753
+ return !!groupBy && sort.length === 1 && sort[0].field === groupBy;
754
+ }
687
755
 
688
756
  var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
689
757
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -784,8 +852,19 @@ let WorkCenterState = class WorkCenterState {
784
852
  setParams(ctx, action) {
785
853
  const state = ctx.getState();
786
854
  const current = state.byArea[action.area];
787
- const nextContext = buildContextFromInputs(current.context, action.area, action.inputs);
788
- const nextGroupBy = resolveGroupByFromInputs(current.groupBy, action.inputs?.group);
855
+ // A card switch invalidates everything scoped to the previous card. The
856
+ // host usually drops those query params too, but a stale URL must not be
857
+ // able to re-apply a filter on a field the new card doesn't have.
858
+ const cardChanged = resolveSelectedCardKeyFromInputs(current.context, action.inputs) !==
859
+ current.context.selectedCardKey;
860
+ // Resolve grouping first: with no explicit `sort` input it is what defines
861
+ // the backend sort.
862
+ const nextGroupBy = resolveGroupByFromInputs(cardChanged ? null : current.groupBy, action.inputs?.group);
863
+ const builtContext = buildContextFromInputs(current.context, action.area, action.inputs, nextGroupBy);
864
+ const filtersFromInputs = action.inputs?.filters !== undefined;
865
+ const nextContext = cardChanged && !filtersFromInputs
866
+ ? { ...builtContext, runtimeFilters: [] }
867
+ : builtContext;
789
868
  const areaChanged = state.activeArea !== action.area;
790
869
  const contextChanged = !isSameContext(current.context, nextContext);
791
870
  const groupChanged = nextGroupBy !== current.groupBy;
@@ -795,11 +874,12 @@ let WorkCenterState = class WorkCenterState {
795
874
  // URL-derived filters land on the header slice. Anything previously
796
875
  // applied via the in-table column filters is dropped so the BE payload
797
876
  // matches what the user sees on the page header.
798
- const filtersFromInputs = action.inputs?.filters !== undefined;
799
877
  const nextHeaderFilters = filtersFromInputs
800
878
  ? nextContext.runtimeFilters
801
- : current.headerFilters;
802
- const nextColumnFilters = filtersFromInputs ? [] : current.columnFilters;
879
+ : cardChanged
880
+ ? []
881
+ : current.headerFilters;
882
+ const nextColumnFilters = filtersFromInputs || cardChanged ? [] : current.columnFilters;
803
883
  ctx.patchState({
804
884
  activeArea: action.area,
805
885
  byArea: {
@@ -836,6 +916,33 @@ let WorkCenterState = class WorkCenterState {
836
916
  columnFilters: action.filters,
837
917
  });
838
918
  }
919
+ selectCard(ctx, action) {
920
+ const state = ctx.getState();
921
+ const current = state.byArea[action.area];
922
+ if (current.context.selectedCardKey === action.selectedCardKey) {
923
+ return;
924
+ }
925
+ ctx.patchState({
926
+ activeArea: action.area,
927
+ byArea: {
928
+ ...state.byArea,
929
+ [action.area]: {
930
+ ...current,
931
+ headerFilters: [],
932
+ columnFilters: [],
933
+ groupBy: null,
934
+ context: {
935
+ ...current.context,
936
+ selectedCardKey: action.selectedCardKey,
937
+ runtimeFilters: [],
938
+ sort: [],
939
+ page: 1,
940
+ },
941
+ },
942
+ },
943
+ });
944
+ return ctx.dispatch(new LoadRuntime(action.area, 'menu-change'));
945
+ }
839
946
  /**
840
947
  * Grouping is a backend concern: the table only renders contiguous rows, so
841
948
  * the grouped column is pushed down as the single `context.sort` entry and
@@ -974,6 +1081,9 @@ __decorate([
974
1081
  __decorate([
975
1082
  Action(ApplyColumnFilters)
976
1083
  ], WorkCenterState.prototype, "applyColumnFilters", null);
1084
+ __decorate([
1085
+ Action(SelectCard)
1086
+ ], WorkCenterState.prototype, "selectCard", null);
977
1087
  __decorate([
978
1088
  Action(SetGroupBy)
979
1089
  ], WorkCenterState.prototype, "setGroupBy", null);
@@ -1042,7 +1152,7 @@ WorkCenterState = __decorate([
1042
1152
  ], WorkCenterState);
1043
1153
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkCenterState, decorators: [{
1044
1154
  type: Injectable
1045
- }], propDecorators: { enterArea: [], hydrateFromContext: [], setParams: [], setLookups: [], applyHeaderFilters: [], applyColumnFilters: [], setGroupBy: [], clearAllFilters: [], loadRuntime: [] } });
1155
+ }], propDecorators: { enterArea: [], hydrateFromContext: [], setParams: [], setLookups: [], applyHeaderFilters: [], applyColumnFilters: [], selectCard: [], setGroupBy: [], clearAllFilters: [], loadRuntime: [] } });
1046
1156
 
1047
1157
  class WorkCenterFacade {
1048
1158
  store = inject(Store);
@@ -1086,11 +1196,12 @@ class WorkCenterFacade {
1086
1196
  setLookups(lookups) {
1087
1197
  return this.store.dispatch(new SetLookups(lookups));
1088
1198
  }
1199
+ /**
1200
+ * Switches card and drops the previous card's filters, grouping and sort —
1201
+ * each card has its own columns and filter schema.
1202
+ */
1089
1203
  selectCardAndLoad(area, selectedCardKey) {
1090
- return this.store.dispatch([
1091
- new HydrateFromContext(area, { selectedCardKey, page: 1 }),
1092
- new LoadRuntime(area, 'menu-change'),
1093
- ]);
1204
+ return this.store.dispatch(new SelectCard(area, selectedCardKey));
1094
1205
  }
1095
1206
  applyTableLazyLoadAndLoad(area, event) {
1096
1207
  const context = this.store.selectSnapshot(WorkCenterState.context);
@@ -3012,8 +3123,13 @@ class WorkCenterPage {
3012
3123
  if (item.key === this.context().selectedCardKey) {
3013
3124
  return;
3014
3125
  }
3126
+ // The store drops the previous card's filters and grouping; the table's
3127
+ // own filter chips live here, so they have to be cleared alongside.
3128
+ this.tableFilters.set({});
3015
3129
  this.facade.selectCardAndLoad(this.area(), item.key);
3016
3130
  this.cardSelected.emit(item.key);
3131
+ this.groupChanged.emit(null);
3132
+ this.sortChanged.emit([]);
3017
3133
  }
3018
3134
  onRuntimeFiltersApplied(filters) {
3019
3135
  const mappedFilters = filters.map((filter) => ({
@@ -3189,5 +3305,5 @@ const APP_STATES = [WorkCenterState];
3189
3305
  * Generated bundle index. Do not edit.
3190
3306
  */
3191
3307
 
3192
- 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 };
3308
+ export { APP_STATES, ApplyColumnFilters, ApplyHeaderFilters, ClearAllFilters, EnterArea, HydrateFromContext, LoadRuntime, SelectCard, SetGroupBy, SetLookups, SetParams, WORK_CENTER_EMPTY_LABEL, WORK_CENTER_GROUP_NONE, 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 };
3193
3309
  //# sourceMappingURL=masterteam-work-center.mjs.map