@gooddata/sdk-ui-dashboard 11.65.0-alpha.10 → 11.65.0-alpha.12
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/NOTICE +1 -39
- package/esm/__version.d.ts +1 -1
- package/esm/__version.js +1 -1
- package/esm/index.d.ts +2 -2
- package/esm/index.d.ts.map +1 -1
- package/esm/model/commandHandlers/dashboard/common/filterViews.d.ts +36 -3
- package/esm/model/commandHandlers/dashboard/common/filterViews.d.ts.map +1 -1
- package/esm/model/commandHandlers/dashboard/common/filterViews.js +138 -26
- package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/index.d.ts +15 -4
- package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/index.d.ts.map +1 -1
- package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/index.js +55 -11
- package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/preloadAttributeFiltersData.d.ts +6 -1
- package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/preloadAttributeFiltersData.d.ts.map +1 -1
- package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/preloadAttributeFiltersData.js +14 -6
- package/esm/model/commandHandlers/dashboard/resetDashboardHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/dashboard/resetDashboardHandler.js +21 -8
- package/esm/model/commandHandlers/filterContext/changeFilterContextSelectionHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/filterContext/changeFilterContextSelectionHandler.js +9 -3
- package/esm/model/commandHandlers/filterContext/filterViewHandler.d.ts +10 -0
- package/esm/model/commandHandlers/filterContext/filterViewHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/filterContext/filterViewHandler.js +79 -24
- package/esm/model/commandHandlers/filterContext/tabSync/writtenTargets.d.ts +17 -0
- package/esm/model/commandHandlers/filterContext/tabSync/writtenTargets.d.ts.map +1 -0
- package/esm/model/commandHandlers/filterContext/tabSync/writtenTargets.js +79 -0
- package/esm/model/commandHandlers/parameters/changeParameterValuesHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/parameters/changeParameterValuesHandler.js +7 -3
- package/esm/model/commandHandlers/tabs/switchDashboardTabHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/tabs/switchDashboardTabHandler.js +3 -1
- package/esm/model/commands/filters.d.ts +16 -1
- package/esm/model/commands/filters.d.ts.map +1 -1
- package/esm/model/commands/filters.js +2 -1
- package/esm/model/commands/parameters.d.ts +10 -1
- package/esm/model/commands/parameters.d.ts.map +1 -1
- package/esm/model/commands/parameters.js +2 -1
- package/esm/model/store/filterViews/filterViewsReducers.d.ts +8 -0
- package/esm/model/store/filterViews/filterViewsReducers.d.ts.map +1 -1
- package/esm/model/store/filterViews/filterViewsReducers.js +11 -0
- package/esm/model/store/filterViews/filterViewsReducersSelectors.d.ts +6 -0
- package/esm/model/store/filterViews/filterViewsReducersSelectors.d.ts.map +1 -1
- package/esm/model/store/filterViews/filterViewsReducersSelectors.js +6 -0
- package/esm/model/store/filterViews/filterViewsState.d.ts +6 -0
- package/esm/model/store/filterViews/filterViewsState.d.ts.map +1 -1
- package/esm/model/store/filterViews/index.d.ts +2 -0
- package/esm/model/store/filterViews/index.d.ts.map +1 -1
- package/esm/model/store/meta/metaSelectors.d.ts +7 -0
- package/esm/model/store/meta/metaSelectors.d.ts.map +1 -1
- package/esm/model/store/meta/metaSelectors.js +10 -0
- package/esm/model/store/tabs/filterContext/filterContextReducers.d.ts +24 -0
- package/esm/model/store/tabs/filterContext/filterContextReducers.d.ts.map +1 -1
- package/esm/model/store/tabs/filterContext/filterContextReducers.js +75 -0
- package/esm/model/store/tabs/index.d.ts +8 -0
- package/esm/model/store/tabs/index.d.ts.map +1 -1
- package/esm/model/store/tabs/parameters/parametersReducers.d.ts +16 -0
- package/esm/model/store/tabs/parameters/parametersReducers.d.ts.map +1 -1
- package/esm/model/store/tabs/parameters/parametersReducers.js +25 -0
- package/esm/presentation/insightEditor/InsightEditorContent.d.ts.map +1 -1
- package/esm/presentation/insightEditor/InsightEditorContent.js +24 -3
- package/esm/sdk-ui-dashboard.d.ts +74 -2
- package/package.json +23 -23
- package/styles/css/main.css +1 -0
- package/styles/css/main.css.map +1 -1
|
@@ -1,16 +1,27 @@
|
|
|
1
1
|
import { type BatchAction } from "redux-batched-actions";
|
|
2
2
|
import { type SagaIterator } from "redux-saga";
|
|
3
|
-
import { type
|
|
3
|
+
import { type IAttributeWithReferences } from "@gooddata/sdk-backend-spi";
|
|
4
|
+
import { type IDashboard, type IDateHierarchyTemplate, type ObjRef } from "@gooddata/sdk-model";
|
|
4
5
|
import { type InitializeDashboard } from "../../../commands/dashboard.js";
|
|
5
6
|
import { type DashboardContext } from "../../../types/commonTypes.js";
|
|
7
|
+
type IPreloadedAttributeFilters = {
|
|
8
|
+
refs: ObjRef[];
|
|
9
|
+
attributesWithReferences: IAttributeWithReferences[];
|
|
10
|
+
};
|
|
6
11
|
export declare function requestCatalog(ctx: DashboardContext, cmd: InitializeDashboard): Generator<import("redux-saga/effects").AllEffect<import("redux-saga/effects").CallEffect<IDateHierarchyTemplate[]> | import("redux-saga/effects").CallEffect<import("./loadCatalog.js").ILoadedCatalog>> | import("redux-saga/effects").PutEffect<{
|
|
7
12
|
payload: import("../../../../index.js").SetCatalogItemsPayload;
|
|
8
13
|
type: "catalog/setCatalogItems";
|
|
9
14
|
}> | import("redux-saga/effects").SelectEffect, void, boolean & [import("./loadCatalog.js").ILoadedCatalog, IDateHierarchyTemplate[]]>;
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Loads the attributes of the attribute filters, unless the initialization already loaded the attributes
|
|
17
|
+
* of all of them. Applying the default view can switch a filter to another display form, so the dashboard
|
|
18
|
+
* can need more attributes than the initialization loaded.
|
|
19
|
+
*/
|
|
20
|
+
export declare function preloadAttributeFiltersData(ctx: DashboardContext, dashboard: IDashboard, preloaded?: IPreloadedAttributeFilters): Generator<import("redux-saga/effects").CallEffect<IAttributeWithReferences[]> | import("redux-saga/effects").PutEffect<{
|
|
21
|
+
payload: IAttributeWithReferences[];
|
|
12
22
|
type: "tabs/setPreloadedAttributesWithReferences";
|
|
13
|
-
}> | import("redux-saga/effects").SelectEffect, void, import("@gooddata/sdk-model").IUnavailableReference[] &
|
|
23
|
+
}> | import("redux-saga/effects").SelectEffect, void, import("@gooddata/sdk-model").IUnavailableReference[] & IAttributeWithReferences[]>;
|
|
14
24
|
export declare function requestDashboardsList(ctx: DashboardContext): Generator<import("redux-saga/effects").CallEffect<import("@gooddata/sdk-model").IListedDashboard[]> | import("redux-saga/effects").PutEffect<BatchAction>, void, import("@gooddata/sdk-model").IListedDashboard[]>;
|
|
15
25
|
export declare function initializeDashboardHandler(ctx: DashboardContext, cmd: InitializeDashboard): SagaIterator;
|
|
26
|
+
export {};
|
|
16
27
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/model/commandHandlers/dashboard/initializeDashboardHandler/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,WAAW,EAAgB,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/model/commandHandlers/dashboard/initializeDashboardHandler/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,WAAW,EAAgB,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,OAAO,EACH,KAAK,wBAAwB,EAIhC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACH,KAAK,UAAU,EAKf,KAAK,sBAAsB,EAK3B,KAAK,MAAM,EAOd,MAAM,qBAAqB,CAAC;AAQ7B,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AA0B1E,OAAO,EACH,KAAK,gBAAgB,EAGxB,MAAM,+BAA+B,CAAC;AA+JvC,KAAK,0BAA0B,GAAG;IAC9B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,wBAAwB,EAAE,wBAAwB,EAAE,CAAC;CACxD,CAAC;AAuYF,wBAAiB,cAAc,CAAC,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE,mBAAmB;;;uIAyB9E;AAgED;;;;GAIG;AACH,wBAAiB,2BAA2B,CACxC,GAAG,EAAE,gBAAgB,EACrB,SAAS,EAAE,UAAU,EACrB,SAAS,CAAC,EAAE,0BAA0B;;;0IAiBzC;AAED,wBAAiB,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,sNAW3D;AAqCD,wBAAiB,0BAA0B,CAAC,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE,mBAAmB,GAAG,YAAY,CA0CzG"}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { uniqBy } from "lodash-es";
|
|
3
3
|
import { batchActions } from "redux-batched-actions";
|
|
4
4
|
import { all, call, put, select, spawn } from "redux-saga/effects";
|
|
5
|
-
import { walkLayout } from "@gooddata/sdk-backend-spi";
|
|
6
|
-
import { areObjRefsEqual, isDrillToInsight, isInsightWidget, isVisualizationSwitcherWidget, serializeObjRef, } from "@gooddata/sdk-model";
|
|
5
|
+
import { walkLayout, } from "@gooddata/sdk-backend-spi";
|
|
6
|
+
import { areObjRefsEqual, isDrillToInsight, isInsightWidget, isVisualizationSwitcherWidget, objRefToString, serializeObjRef, } from "@gooddata/sdk-model";
|
|
7
7
|
import { createDisplayFormMap, createDisplayFormMapFromCatalog, } from "../../../../_staging/catalog/displayFormMap.js";
|
|
8
8
|
import { deriveAbsoluteFormGranularitiesFromRelativeForm } from "../../../../_staging/dateFilterConfig/merge.js";
|
|
9
9
|
import { changeDashboardTimezoneOverride } from "../../../commands/timezone.js";
|
|
@@ -34,7 +34,7 @@ import { userActions } from "../../../store/user/index.js";
|
|
|
34
34
|
import { collectDashboardParameterRoots } from "../../parameters/dashboardParameterRoots.js";
|
|
35
35
|
import { loadParameterDependencies } from "../../parameters/loadParameterDependencies.js";
|
|
36
36
|
import { changeDashboardTimezoneOverrideHandler } from "../../timezone/changeDashboardTimezoneOverrideHandler.js";
|
|
37
|
-
import { applyDefaultFilterViewForRenderMode } from "../common/filterViews.js";
|
|
37
|
+
import { applyDefaultFilterViewForRenderMode, isDefaultFilterViewSharedAcrossTabs, } from "../common/filterViews.js";
|
|
38
38
|
import { actionsToInitializeExistingDashboard, actionsToInitializeNewDashboard, } from "../common/stateInitializers.js";
|
|
39
39
|
import { loadAutomationTimezone } from "./loadAutomationTimezone.js";
|
|
40
40
|
import { loadCatalog } from "./loadCatalog.js";
|
|
@@ -47,7 +47,7 @@ import { loadFilterViews } from "./loadFilterViews.js";
|
|
|
47
47
|
import { dashboardLoadReferenceTypes, loadUnavailableReferences } from "./loadUnavailableReferences.js";
|
|
48
48
|
import { loadUser } from "./loadUser.js";
|
|
49
49
|
import { mergeDateFilterConfigWithOverrides } from "./mergeDateFilterConfigs.js";
|
|
50
|
-
import {
|
|
50
|
+
import { attributeFilterDisplayFormRefs, loadAttributesWithReferences, } from "./preloadAttributeFiltersData.js";
|
|
51
51
|
import { resolveDashboardConfigAndFeatureFlagDependentCalls } from "./resolveDashboardConfig.js";
|
|
52
52
|
import { resolveEntitlements } from "./resolveEntitlements.js";
|
|
53
53
|
import { resolvePermissions } from "./resolvePermissions.js";
|
|
@@ -191,10 +191,18 @@ function* loadExistingDashboard(ctx, cmd, dashboardRef) {
|
|
|
191
191
|
// FF off => nothing is ever marked unavailable and behavior is unchanged
|
|
192
192
|
const unavailableReferences = yield call(loadUnavailableReferences, ctx, loadedDashboard, unavailable, Boolean(config.settings?.enableDashboardPartialRendering), !!cmd.payload.persistedDashboard);
|
|
193
193
|
yield put(unavailableObjectsActions.setUnavailableObjects(unavailableReferences));
|
|
194
|
-
const dashboardWithFilterView = applyDefaultFilterViewForRenderMode(loadedDashboard, filterViews, config.exportId, unavailableReferences, config.initialRenderMode);
|
|
195
194
|
// Resolve active tab: URL's initialTabId takes precedence, otherwise default to first tab
|
|
196
195
|
// Note: activeTabLocalIdentifier is app state only, NOT persisted to/read from dashboard MD
|
|
197
|
-
const resolvedActiveTabId = resolveActiveTabId(
|
|
196
|
+
const resolvedActiveTabId = resolveActiveTabId(loadedDashboard.tabs, cmd.payload.initialTabId, undefined);
|
|
197
|
+
// same rule as selectEnablePersistentFiltersAcrossTabs, which cannot be selected before init
|
|
198
|
+
const persistFiltersAcrossTabs = !!config.settings.enableDashboardPersistentFiltersAcrossTabs &&
|
|
199
|
+
loadedDashboard.disablePersistentFiltersAcrossTabs === false;
|
|
200
|
+
const filterViewOptions = { persistFiltersAcrossTabs, activeTabLocalIdentifier: resolvedActiveTabId };
|
|
201
|
+
const preloadedAttributeFilters = yield call(loadAttributesForSharedDefaultFilterView, ctx, loadedDashboard, filterViews, config.initialRenderMode, filterViewOptions, unavailableReferences);
|
|
202
|
+
const dashboardWithFilterView = applyDefaultFilterViewForRenderMode(loadedDashboard, filterViews, config.exportId, unavailableReferences, config.initialRenderMode, {
|
|
203
|
+
...filterViewOptions,
|
|
204
|
+
attributesWithReferences: preloadedAttributeFilters?.attributesWithReferences,
|
|
205
|
+
});
|
|
198
206
|
const dashboard = dashboardWithFilterView;
|
|
199
207
|
const enableParameters = config.settings?.enableParameters ?? false;
|
|
200
208
|
const [{ tabsAttributeFilterConfigs, tabsDateFilterConfig, tabsDateFilterConfigSource }, workspaceParameters, parameterDependencies,] = yield all([
|
|
@@ -225,6 +233,11 @@ function* loadExistingDashboard(ctx, cmd, dashboardRef) {
|
|
|
225
233
|
catalogActions.mergeCatalogParameterDependencies(parameterDependencies.byRoot),
|
|
226
234
|
catalogActions.markParameterDependenciesFailed(parameterDependencies.failedRoots),
|
|
227
235
|
...initActions,
|
|
236
|
+
...(preloadedAttributeFilters
|
|
237
|
+
? [
|
|
238
|
+
tabsActions.setPreloadedAttributesWithReferences(preloadedAttributeFilters.attributesWithReferences),
|
|
239
|
+
]
|
|
240
|
+
: []),
|
|
228
241
|
// NOTE: Tab configs (dateFilterConfig, dateFilterConfigs, attributeFilterConfigs, filterContext)
|
|
229
242
|
// are now initialized as part of the tabs state in initActions via setTabs action
|
|
230
243
|
uiActions.setMenuButtonItemsVisibility(config.menuButtonItemsVisibility),
|
|
@@ -238,6 +251,7 @@ function* loadExistingDashboard(ctx, cmd, dashboardRef) {
|
|
|
238
251
|
dashboard: ctx.dashboardRef, // should be defined as we are in existing dashboard load fn
|
|
239
252
|
filterViews,
|
|
240
253
|
}),
|
|
254
|
+
filterViewsActions.setDefaultFilterViewAppliedTabs(resolvedActiveTabId ? [resolvedActiveTabId] : []),
|
|
241
255
|
], "@@GDC.DASH/BATCH.INIT.EXISTING");
|
|
242
256
|
const event = dashboardInitialized(ctx, dashboard, insights, config, permissions, cmd.correlationId);
|
|
243
257
|
return {
|
|
@@ -245,6 +259,7 @@ function* loadExistingDashboard(ctx, cmd, dashboardRef) {
|
|
|
245
259
|
event,
|
|
246
260
|
// an export render takes its timezone from the export itself
|
|
247
261
|
automationTimezone: exportTimezone ? undefined : automationTimezone,
|
|
262
|
+
preloadedAttributeFilters,
|
|
248
263
|
};
|
|
249
264
|
}
|
|
250
265
|
function* initializeNewDashboard(ctx, cmd) {
|
|
@@ -361,9 +376,38 @@ function makeCatalogParametersPayload(result) {
|
|
|
361
376
|
}
|
|
362
377
|
return { status: "loaded", parameters: result };
|
|
363
378
|
}
|
|
364
|
-
|
|
379
|
+
/**
|
|
380
|
+
* The attributes of the attribute filters, which match the filters of the tabs when the default view of the
|
|
381
|
+
* active tab is written to the other tabs. The view is not shared, or the attributes fail to load: none,
|
|
382
|
+
* so filters match on equal display form refs.
|
|
383
|
+
*/
|
|
384
|
+
function* loadAttributesForSharedDefaultFilterView(ctx, dashboard, filterViews, renderMode, options, unavailableObjects) {
|
|
385
|
+
if (!isDefaultFilterViewSharedAcrossTabs(dashboard, filterViews, renderMode, options)) {
|
|
386
|
+
return undefined;
|
|
387
|
+
}
|
|
388
|
+
const refs = attributeFilterDisplayFormRefs(dashboard, unavailableObjects);
|
|
389
|
+
try {
|
|
390
|
+
const attributesWithReferences = yield call(loadAttributesWithReferences, ctx, refs);
|
|
391
|
+
return { refs, attributesWithReferences };
|
|
392
|
+
}
|
|
393
|
+
catch (error) {
|
|
394
|
+
console.warn("Loading the attributes of the dashboard filters failed", error);
|
|
395
|
+
return undefined;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Loads the attributes of the attribute filters, unless the initialization already loaded the attributes
|
|
400
|
+
* of all of them. Applying the default view can switch a filter to another display form, so the dashboard
|
|
401
|
+
* can need more attributes than the initialization loaded.
|
|
402
|
+
*/
|
|
403
|
+
export function* preloadAttributeFiltersData(ctx, dashboard, preloaded) {
|
|
365
404
|
const unavailableObjects = yield select(selectUnavailableObjects);
|
|
366
|
-
const
|
|
405
|
+
const refs = attributeFilterDisplayFormRefs(dashboard, unavailableObjects);
|
|
406
|
+
const preloadedRefs = new Set(preloaded?.refs.map(objRefToString));
|
|
407
|
+
if (preloaded && refs.every((ref) => preloadedRefs.has(objRefToString(ref)))) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
const attributesWithReferences = yield call(loadAttributesWithReferences, ctx, refs);
|
|
367
411
|
yield put(tabsActions.setPreloadedAttributesWithReferences(attributesWithReferences));
|
|
368
412
|
}
|
|
369
413
|
export function* requestDashboardsList(ctx) {
|
|
@@ -374,10 +418,10 @@ export function* requestDashboardsList(ctx) {
|
|
|
374
418
|
]);
|
|
375
419
|
yield put(dashboardsListActions);
|
|
376
420
|
}
|
|
377
|
-
function* advancedLoader(ctx, cmd, dashboard) {
|
|
421
|
+
function* advancedLoader(ctx, cmd, dashboard, preloadedAttributeFilters) {
|
|
378
422
|
yield all([
|
|
379
423
|
call(requestCatalog, ctx, cmd),
|
|
380
|
-
call(preloadAttributeFiltersData, ctx, dashboard),
|
|
424
|
+
call(preloadAttributeFiltersData, ctx, dashboard, preloadedAttributeFilters),
|
|
381
425
|
call(requestDashboardsList, ctx),
|
|
382
426
|
]);
|
|
383
427
|
}
|
|
@@ -418,7 +462,7 @@ export function* initializeDashboardHandler(ctx, cmd) {
|
|
|
418
462
|
if (dashboardRef) {
|
|
419
463
|
// let's run effects which are not essential for the existing
|
|
420
464
|
// dashboard to be rendered, such as catalog load
|
|
421
|
-
yield spawn(advancedLoader, ctx, cmd, result.event.payload.dashboard);
|
|
465
|
+
yield spawn(advancedLoader, ctx, cmd, result.event.payload.dashboard, result.preloadedAttributeFilters);
|
|
422
466
|
}
|
|
423
467
|
}
|
|
424
468
|
catch (e) {
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { type IAttributeWithReferences, type IUnavailableDashboardReference } from "@gooddata/sdk-backend-spi";
|
|
2
|
-
import { type IDashboard } from "@gooddata/sdk-model";
|
|
2
|
+
import { type IDashboard, type ObjRef } from "@gooddata/sdk-model";
|
|
3
3
|
import { type DashboardContext } from "../../../types/commonTypes.js";
|
|
4
4
|
export declare function preloadAttributeFiltersData(ctx: DashboardContext, dashboard?: IDashboard, unavailableObjects?: IUnavailableDashboardReference[]): Promise<IAttributeWithReferences[]>;
|
|
5
|
+
export declare function loadAttributesWithReferences(ctx: DashboardContext, refs: ObjRef[]): Promise<IAttributeWithReferences[]>;
|
|
6
|
+
/**
|
|
7
|
+
* The unique display forms of the attribute filters of the dashboard and its tabs that the user can read.
|
|
8
|
+
*/
|
|
9
|
+
export declare function attributeFilterDisplayFormRefs(dashboard?: IDashboard, unavailableObjects?: IUnavailableDashboardReference[]): ObjRef[];
|
|
5
10
|
//# sourceMappingURL=preloadAttributeFiltersData.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preloadAttributeFiltersData.d.ts","sourceRoot":"","sources":["../../../../../src/model/commandHandlers/dashboard/initializeDashboardHandler/preloadAttributeFiltersData.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,wBAAwB,EAC7B,KAAK,8BAA8B,EACtC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACH,KAAK,UAAU,
|
|
1
|
+
{"version":3,"file":"preloadAttributeFiltersData.d.ts","sourceRoot":"","sources":["../../../../../src/model/commandHandlers/dashboard/initializeDashboardHandler/preloadAttributeFiltersData.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,wBAAwB,EAC7B,KAAK,8BAA8B,EACtC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACH,KAAK,UAAU,EACf,KAAK,MAAM,EAGd,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEtE,wBAAgB,2BAA2B,CACvC,GAAG,EAAE,gBAAgB,EACrB,SAAS,CAAC,EAAE,UAAU,EACtB,kBAAkB,GAAE,8BAA8B,EAAO,GAC1D,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAErC;AAED,wBAAgB,4BAA4B,CACxC,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,MAAM,EAAE,GACf,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAOrC;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAC1C,SAAS,CAAC,EAAE,UAAU,EACtB,kBAAkB,GAAE,8BAA8B,EAAO,GAC1D,MAAM,EAAE,CAkBV"}
|
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
// (C) 2021-
|
|
1
|
+
// (C) 2021-2026 GoodData Corporation
|
|
2
2
|
import { uniqBy } from "lodash-es";
|
|
3
3
|
import { isDashboardAttributeFilter, objRefToString, } from "@gooddata/sdk-model";
|
|
4
4
|
import { isDashboardObjectRestricted } from "../../../store/filtering/restrictedFilterUtils.js";
|
|
5
5
|
export function preloadAttributeFiltersData(ctx, dashboard, unavailableObjects = []) {
|
|
6
|
+
return loadAttributesWithReferences(ctx, attributeFilterDisplayFormRefs(dashboard, unavailableObjects));
|
|
7
|
+
}
|
|
8
|
+
export function loadAttributesWithReferences(ctx, refs) {
|
|
9
|
+
if (!refs.length) {
|
|
10
|
+
return Promise.resolve([]);
|
|
11
|
+
}
|
|
6
12
|
const { backend, workspace } = ctx;
|
|
13
|
+
return backend.workspace(workspace).attributes().getAttributesWithReferences(refs);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The unique display forms of the attribute filters of the dashboard and its tabs that the user can read.
|
|
17
|
+
*/
|
|
18
|
+
export function attributeFilterDisplayFormRefs(dashboard, unavailableObjects = []) {
|
|
7
19
|
const dashboardAttributeFilterRefs = dashboard?.filterContext?.filters
|
|
8
20
|
.filter(isDashboardAttributeFilter)
|
|
9
21
|
.map((filter) => filter.attributeFilter.displayForm);
|
|
@@ -15,9 +27,5 @@ export function preloadAttributeFiltersData(ctx, dashboard, unavailableObjects =
|
|
|
15
27
|
}, []);
|
|
16
28
|
const allRefs = [...(dashboardAttributeFilterRefs ?? []), ...(tabsAttributeFilterRefs ?? [])].filter((ref) => !isDashboardObjectRestricted(ref, "displayForm", unavailableObjects));
|
|
17
29
|
// Remove duplicates by comparing ObjRef string representations
|
|
18
|
-
|
|
19
|
-
if (!uniqueRefs?.length) {
|
|
20
|
-
return Promise.resolve([]);
|
|
21
|
-
}
|
|
22
|
-
return backend.workspace(workspace).attributes().getAttributesWithReferences(uniqueRefs);
|
|
30
|
+
return uniqBy(allRefs, objRefToString);
|
|
23
31
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resetDashboardHandler.d.ts","sourceRoot":"","sources":["../../../../src/model/commandHandlers/dashboard/resetDashboardHandler.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAa/C,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,KAAK,kBAAkB,EAAqB,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"resetDashboardHandler.d.ts","sourceRoot":"","sources":["../../../../src/model/commandHandlers/dashboard/resetDashboardHandler.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAa/C,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,KAAK,kBAAkB,EAAqB,MAAM,2BAA2B,CAAC;AAsBvF,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAWnE,wBAAiB,qBAAqB,CAClC,GAAG,EAAE,gBAAgB,EACrB,GAAG,EAAE,eAAe,GACrB,YAAY,CAAC,kBAAkB,CAAC,CASlC;AAED;;GAEG;AACH,wBAAiB,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU;;;;;;;;;;;;;;;GAUzG"}
|
|
@@ -8,8 +8,10 @@ import { selectAllCatalogDisplayFormsMap, selectCatalogParameters, } from "../..
|
|
|
8
8
|
import { selectDateFilterConfig, selectEnableImmediateAttributeFilterDisplayAsLabelMigration, selectSettings, } from "../../store/config/configSelectors.js";
|
|
9
9
|
import { selectCrossFilteringFiltersLocalIdentifiers } from "../../store/drill/drillSelectors.js";
|
|
10
10
|
import { selectDashboardFilterViews } from "../../store/filterViews/filterViewsReducersSelectors.js";
|
|
11
|
-
import {
|
|
11
|
+
import { filterViewsActions } from "../../store/filterViews/index.js";
|
|
12
|
+
import { selectPersistedDashboard, selectPersistedEnablePersistentFiltersAcrossTabs, } from "../../store/meta/metaSelectors.js";
|
|
12
13
|
import { selectRenderMode } from "../../store/renderMode/renderModeSelectors.js";
|
|
14
|
+
import { selectPreloadedAttributesWithReferences } from "../../store/tabs/filterContext/filterContextSelectors.js";
|
|
13
15
|
import { selectActiveTabLocalIdentifier, selectTabs } from "../../store/tabs/tabsSelectors.js";
|
|
14
16
|
import { DEFAULT_TAB_ID } from "../../store/tabs/tabsState.js";
|
|
15
17
|
import { selectUnavailableObjects } from "../../store/unavailableObjects/unavailableObjectsSelectors.js";
|
|
@@ -119,12 +121,6 @@ function* resetDashboardFromPersisted(ctx, renderMode) {
|
|
|
119
121
|
// all tabs are rebuilt, so each needs its own default view, not only the active one
|
|
120
122
|
const filterViews = yield select(selectDashboardFilterViews);
|
|
121
123
|
const unavailableObjects = yield select(selectUnavailableObjects);
|
|
122
|
-
const dashboardWithUpdatedFilterContext = applyDefaultFilterViewForRenderMode(persistedDashboard, filterViews, ctx.config?.exportId, unavailableObjects, renderMode);
|
|
123
|
-
const insightRefsFromWidgets = insightReferencesFromDashboard(dashboardWithUpdatedFilterContext);
|
|
124
|
-
const uniqueInsightRefsFromWidgets = uniqWith(insightRefsFromWidgets, areObjRefsEqual);
|
|
125
|
-
const resolvedInsights = yield call(resolveInsights, ctx, uniqueInsightRefsFromWidgets);
|
|
126
|
-
const displayForms = yield select(selectAllCatalogDisplayFormsMap);
|
|
127
|
-
const resolvedInsightsValues = [...resolvedInsights.resolved.values()];
|
|
128
124
|
const currentActiveTabId = yield select(selectActiveTabLocalIdentifier);
|
|
129
125
|
// Validate that the current active tab exists in the persisted dashboard
|
|
130
126
|
// If not (e.g., user created a new tab but didn't save), fall back to first tab
|
|
@@ -132,8 +128,25 @@ function* resetDashboardFromPersisted(ctx, renderMode) {
|
|
|
132
128
|
const effectiveActiveTabId = activeTabExistsInPersisted
|
|
133
129
|
? currentActiveTabId
|
|
134
130
|
: persistedDashboard.tabs?.[0]?.localIdentifier;
|
|
131
|
+
// the state is rebuilt from the persisted dashboard, so an unsaved change of the setting does not apply
|
|
132
|
+
const persistFiltersAcrossTabs = yield select(selectPersistedEnablePersistentFiltersAcrossTabs);
|
|
133
|
+
const attributesWithReferences = yield select(selectPreloadedAttributesWithReferences);
|
|
134
|
+
const dashboardWithUpdatedFilterContext = applyDefaultFilterViewForRenderMode(persistedDashboard, filterViews, ctx.config?.exportId, unavailableObjects, renderMode, {
|
|
135
|
+
persistFiltersAcrossTabs,
|
|
136
|
+
activeTabLocalIdentifier: effectiveActiveTabId,
|
|
137
|
+
attributesWithReferences,
|
|
138
|
+
});
|
|
139
|
+
const insightRefsFromWidgets = insightReferencesFromDashboard(dashboardWithUpdatedFilterContext);
|
|
140
|
+
const uniqueInsightRefsFromWidgets = uniqWith(insightRefsFromWidgets, areObjRefsEqual);
|
|
141
|
+
const resolvedInsights = yield call(resolveInsights, ctx, uniqueInsightRefsFromWidgets);
|
|
142
|
+
const displayForms = yield select(selectAllCatalogDisplayFormsMap);
|
|
143
|
+
const resolvedInsightsValues = [...resolvedInsights.resolved.values()];
|
|
135
144
|
const workspaceParameters = yield select(selectCatalogParameters);
|
|
136
|
-
|
|
145
|
+
const initActions = yield call(actionsToInitializeExistingDashboard, ctx, dashboardWithUpdatedFilterContext, resolvedInsightsValues, settings, isImmediateAttributeFilterMigrationEnabled, effectiveOriginalFilterContext, migratedAttributeFilters, effectiveAttributeFilterConfigs, dateFilterConfig, tabsDateFilterConfigSource, displayForms, persistedDashboard, effectiveActiveTabId, workspaceParameters);
|
|
146
|
+
batch = [
|
|
147
|
+
...initActions,
|
|
148
|
+
filterViewsActions.setDefaultFilterViewAppliedTabs(effectiveActiveTabId ? [effectiveActiveTabId] : []),
|
|
149
|
+
];
|
|
137
150
|
}
|
|
138
151
|
else {
|
|
139
152
|
const settings = yield select(selectSettings);
|
package/esm/model/commandHandlers/filterContext/changeFilterContextSelectionHandler.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changeFilterContextSelectionHandler.d.ts","sourceRoot":"","sources":["../../../../src/model/commandHandlers/filterContext/changeFilterContextSelectionHandler.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAyC/C,OAAO,EAAE,KAAK,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AAuC9E,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"changeFilterContextSelectionHandler.d.ts","sourceRoot":"","sources":["../../../../src/model/commandHandlers/filterContext/changeFilterContextSelectionHandler.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAyC/C,OAAO,EAAE,KAAK,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AAuC9E,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAiOnE,wBAAiB,mCAAmC,CAChD,GAAG,EAAE,gBAAgB,EACrB,GAAG,EAAE,4BAA4B,GAClC,YAAY,CAAC,IAAI,CAAC,CA4OpB"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// (C) 2021-2026 GoodData Corporation
|
|
2
2
|
import { compact, partition, uniqBy } from "lodash-es";
|
|
3
|
-
import { all, call, select } from "redux-saga/effects";
|
|
3
|
+
import { all, call, put, select } from "redux-saga/effects";
|
|
4
4
|
import { NotSupported } from "@gooddata/sdk-backend-spi";
|
|
5
5
|
import { areObjRefsEqual, attributeElementsIsEmpty, dashboardAttributeFilterItemDisplayForm, dashboardAttributeFilterItemFilterElementsBy, dashboardAttributeFilterItemFilterElementsByDate, dashboardAttributeFilterItemLocalIdentifier, dashboardAttributeFilterItemValidateElementsBy, dashboardFilterLocalIdentifier, dashboardFilterObjRef, dashboardMeasureValueFilterLocalIdentifier, getAttributeElementsItems, isAllTimeDashboardDateFilter, isDashboardArbitraryAttributeFilter, isDashboardAttributeFilter, isDashboardAttributeFilterItem, isDashboardCommonDateFilter, isDashboardDateFilter, isDashboardMeasureValueFilter, isDashboardTextAttributeFilter, isSingleSelectionFilter, isUriRef, objRefToString, serializeObjRef, updateAttributeElementsItems, } from "@gooddata/sdk-model";
|
|
6
6
|
import { dashboardFilterToFilterContextItem } from "../../../_staging/dashboard/dashboardFilterContext.js";
|
|
@@ -20,6 +20,7 @@ import { resolveAndRegisterDisplayFormMetadata } from "./attributeFilter/resolve
|
|
|
20
20
|
import { computedAttributesDisabledMessage, findDisabledComputedAttributeRef, } from "./attributeFilter/validation/computedAttributesEnabledValidation.js";
|
|
21
21
|
import { canApplyDateFilter } from "./common.js";
|
|
22
22
|
import { beginTabSync, endTabSync, putTabSynced } from "./tabSync/tabSyncSaga.js";
|
|
23
|
+
import { writtenFiltersOf } from "./tabSync/writtenTargets.js";
|
|
23
24
|
function classifyRestore(type, restoresOriginal) {
|
|
24
25
|
// A computed key is not inferred as a single-property mapped type.
|
|
25
26
|
return { [type]: restoresOriginal };
|
|
@@ -121,7 +122,7 @@ function* selectAttributeFilterConfigsSelectionTypeMapTabAware(tabLocalIdentifie
|
|
|
121
122
|
return yield select(selectAttributeFilterConfigsSelectionTypeMap);
|
|
122
123
|
}
|
|
123
124
|
export function* changeFilterContextSelectionHandler(ctx, cmd) {
|
|
124
|
-
const { filters, resetOthers, attributeFilterConfigs = [], tabLocalIdentifier, matchByLocalIdentifier = false, restorePerTabOriginals = false, } = cmd.payload;
|
|
125
|
+
const { filters, resetOthers, attributeFilterConfigs = [], tabLocalIdentifier, matchByLocalIdentifier = false, restorePerTabOriginals = false, adoptAsOriginal = false, } = cmd.payload;
|
|
125
126
|
// Validate that the target tab exists if tabLocalIdentifier is provided
|
|
126
127
|
if (tabLocalIdentifier) {
|
|
127
128
|
const tabs = yield select(selectTabs);
|
|
@@ -233,13 +234,18 @@ export function* changeFilterContextSelectionHandler(ctx, cmd) {
|
|
|
233
234
|
}
|
|
234
235
|
}
|
|
235
236
|
}
|
|
236
|
-
yield call(putTabSynced, session, markRestoreTargetOriginal([
|
|
237
|
+
const dispatchedActions = yield call(putTabSynced, session, markRestoreTargetOriginal([
|
|
237
238
|
...attributeFilterResult.actions,
|
|
238
239
|
...commonDateFilterUpdateActions,
|
|
239
240
|
...dateFiltersUpdateActions,
|
|
240
241
|
...measureValueFilterUpdateActions,
|
|
241
242
|
], restorePerTabOriginals));
|
|
242
243
|
yield call(endTabSync, ctx, cmd, session, tabLocalIdentifier);
|
|
244
|
+
// A default filter view initializes the shared state. The values it wrote are that initial state,
|
|
245
|
+
// on this tab and on every tab they were synced to, so a reset restores them.
|
|
246
|
+
if (adoptAsOriginal) {
|
|
247
|
+
yield put(tabsActions.adoptAppliedFiltersAsOriginal(writtenFiltersOf(dispatchedActions)));
|
|
248
|
+
}
|
|
243
249
|
}
|
|
244
250
|
function* getDashboardFilterByAttributeMatching(filterRef, resolvedDisplayForms, ctx, tabLocalIdentifier, isFilterExcluded, areDisplayFormsEquivalent = areObjRefsEqual) {
|
|
245
251
|
if (isUriRef(filterRef) && !ctx.backend.capabilities.supportsObjectUris) {
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import { type SagaIterator } from "redux-saga";
|
|
2
2
|
import { type IApplyFilterView, type IDeleteFilterView, type ISaveFilterView, type ISetFilterViewAsDefault } from "../../commands/filters.js";
|
|
3
|
+
import { type ISwitchDashboardTab } from "../../commands/tabs.js";
|
|
3
4
|
import { type DashboardContext } from "../../types/commonTypes.js";
|
|
4
5
|
export declare function saveFilterViewHandler(ctx: DashboardContext, cmd: ISaveFilterView): SagaIterator<void>;
|
|
5
6
|
export declare function deleteFilterViewHandler(ctx: DashboardContext, cmd: IDeleteFilterView): SagaIterator<void>;
|
|
6
7
|
export declare function applyFilterViewHandler(ctx: DashboardContext, cmd: IApplyFilterView): SagaIterator<void>;
|
|
8
|
+
/**
|
|
9
|
+
* While filters persist across tabs, the dashboard opens with the default view of its active tab as
|
|
10
|
+
* the shared state. Every other tab applies its own default view, to all tabs, on its first visit in
|
|
11
|
+
* the session. Later visits keep the shared state. A drill to the tab sets the shared state itself, so
|
|
12
|
+
* it counts as the first visit without applying the view.
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
export declare function applyDefaultFilterViewOnFirstTabVisit(ctx: DashboardContext, cmd: ISwitchDashboardTab, tabLocalIdentifier: string): SagaIterator<void>;
|
|
7
17
|
export declare function setFilterViewAsDefaultHandler(ctx: DashboardContext, cmd: ISetFilterViewAsDefault): SagaIterator<void>;
|
|
8
18
|
export declare function reloadFilterViewsHandler(ctx: DashboardContext): SagaIterator<void>;
|
|
9
19
|
//# sourceMappingURL=filterViewHandler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filterViewHandler.d.ts","sourceRoot":"","sources":["../../../../src/model/commandHandlers/filterContext/filterViewHandler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"filterViewHandler.d.ts","sourceRoot":"","sources":["../../../../src/model/commandHandlers/filterContext/filterViewHandler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAe/C,OAAO,EACH,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAE/B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAoClE,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAiBnE,wBAAiB,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE,eAAe,GAAG,YAAY,CAAC,IAAI,CAAC,CAqEtG;AAMD,wBAAiB,uBAAuB,CAAC,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE,iBAAiB,GAAG,YAAY,CAAC,IAAI,CAAC,CAsB1G;AAOD,wBAAiB,sBAAsB,CAAC,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE,gBAAgB,GAAG,YAAY,CAAC,IAAI,CAAC,CAYxG;AA6DD;;;;;;;GAOG;AACH,wBAAiB,qCAAqC,CAClD,GAAG,EAAE,gBAAgB,EACrB,GAAG,EAAE,mBAAmB,EACxB,kBAAkB,EAAE,MAAM,GAC3B,YAAY,CAAC,IAAI,CAAC,CAyCpB;AAMD,wBAAiB,6BAA6B,CAC1C,GAAG,EAAE,gBAAgB,EACrB,GAAG,EAAE,uBAAuB,GAC7B,YAAY,CAAC,IAAI,CAAC,CAuBpB;AAED,wBAAiB,wBAAwB,CAAC,GAAG,EAAE,gBAAgB,GAAG,YAAY,CAAC,IAAI,CAAC,CAcnF"}
|
|
@@ -8,16 +8,20 @@ import { filterViewApplicationFailed, filterViewApplicationSucceeded, filterView
|
|
|
8
8
|
import { selectCatalogParameters } from "../../store/catalog/catalogSelectors.js";
|
|
9
9
|
import { selectIsApplyFiltersAllAtOnceEnabledAndSet } from "../../store/config/configSelectors.js";
|
|
10
10
|
import { selectCrossFilteringFiltersLocalIdentifiers } from "../../store/drill/drillSelectors.js";
|
|
11
|
-
import { selectFilterViews } from "../../store/filterViews/filterViewsReducersSelectors.js";
|
|
11
|
+
import { selectDashboardFilterViews, selectDefaultFilterViewAppliedTabs, selectFilterViews, } from "../../store/filterViews/filterViewsReducersSelectors.js";
|
|
12
12
|
import { filterViewsActions } from "../../store/filterViews/index.js";
|
|
13
|
+
import { selectEnablePersistentFiltersAcrossTabs } from "../../store/meta/metaSelectors.js";
|
|
14
|
+
import { selectIsInEditMode } from "../../store/renderMode/renderModeSelectors.js";
|
|
13
15
|
import { selectAttributeFilterConfigsOverrides } from "../../store/tabs/attributeFilterConfigs/attributeFilterConfigsSelectors.js";
|
|
14
16
|
import { selectFilterContextDefinition, selectWorkingFilterContextDefinition, } from "../../store/tabs/filterContext/filterContextSelectors.js";
|
|
15
17
|
import { tabsActions } from "../../store/tabs/index.js";
|
|
16
18
|
import { selectDashboardParameterEntries, selectFilterViewDisplayParameters, } from "../../store/tabs/parameters/parametersSelectors.js";
|
|
17
|
-
import { selectActiveTabLocalIdentifier } from "../../store/tabs/tabsSelectors.js";
|
|
19
|
+
import { selectActiveTabLocalIdentifier, selectFirstTabLocalIdentifier, } from "../../store/tabs/tabsSelectors.js";
|
|
20
|
+
import { findTabDefaultFilterView } from "../dashboard/common/filterViews.js";
|
|
18
21
|
import { resolveParameterValuesForFilterView } from "../dashboard/common/parameterHydration.js";
|
|
19
22
|
import { loadFilterViews } from "../dashboard/initializeDashboardHandler/loadFilterViews.js";
|
|
20
23
|
import { changeParameterValuesHandler } from "../parameters/changeParameterValuesHandler.js";
|
|
24
|
+
import { changeFilterContextSelectionHandler } from "./changeFilterContextSelectionHandler.js";
|
|
21
25
|
import { resetCrossFiltering } from "./common.js";
|
|
22
26
|
function createFilterView(ctx, filterView) {
|
|
23
27
|
return ctx.backend.workspace(ctx.workspace).dashboards().createFilterView(filterView);
|
|
@@ -93,35 +97,86 @@ function* findFilterView(ref) {
|
|
|
93
97
|
}
|
|
94
98
|
export function* applyFilterViewHandler(ctx, cmd) {
|
|
95
99
|
const filterView = yield call(findFilterView, cmd.payload.ref);
|
|
96
|
-
const attributeFilterConfigs = yield select(selectAttributeFilterConfigsOverrides);
|
|
97
100
|
if (filterView) {
|
|
98
|
-
yield call(
|
|
99
|
-
const isApplyAllAtOnceEnabledAndSet = yield select(selectIsApplyFiltersAllAtOnceEnabledAndSet);
|
|
100
|
-
if (isApplyAllAtOnceEnabledAndSet) {
|
|
101
|
-
yield put(tabsActions.resetWorkingSelection());
|
|
102
|
-
}
|
|
103
|
-
yield put(changeFilterContextSelectionByParams({
|
|
104
|
-
filters: filterView.filterContext.filters,
|
|
105
|
-
attributeFilterConfigs,
|
|
106
|
-
resetOthers: true,
|
|
107
|
-
correlationId: cmd.correlationId,
|
|
108
|
-
// a filter view snapshots this dashboard's own filter context, so its localIdentifiers
|
|
109
|
-
// are authoritative; display form matching alone cannot tell apart filters that share
|
|
110
|
-
// a display form
|
|
111
|
-
matchByLocalIdentifier: true,
|
|
112
|
-
}));
|
|
113
|
-
const parameterEntries = yield select(selectDashboardParameterEntries);
|
|
114
|
-
const workspaceParameters = yield select(selectCatalogParameters);
|
|
115
|
-
yield call(changeParameterValuesHandler, ctx, changeParameterValues({
|
|
116
|
-
parameters: resolveParameterValuesForFilterView(parameterEntries, filterView.parameters ?? [], workspaceParameters),
|
|
117
|
-
correlationId: cmd.correlationId,
|
|
118
|
-
}));
|
|
101
|
+
yield call(applyFilterViewSelection, ctx, cmd, filterView);
|
|
119
102
|
yield put(filterViewApplicationSucceeded(ctx, filterView, cmd.correlationId));
|
|
120
103
|
}
|
|
121
104
|
else {
|
|
122
105
|
yield put(filterViewApplicationFailed(ctx));
|
|
123
106
|
}
|
|
124
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Applies the filters and parameters of `filterView` to the active tab. While filters persist across
|
|
110
|
+
* tabs, the filter and parameter handlers mirror the values to every other tab, so the view sets the
|
|
111
|
+
* shared state.
|
|
112
|
+
*/
|
|
113
|
+
function* applyFilterViewSelection(ctx, cmd, filterView, options) {
|
|
114
|
+
const attributeFilterConfigs = yield select(selectAttributeFilterConfigsOverrides);
|
|
115
|
+
yield call(resetCrossFiltering, cmd);
|
|
116
|
+
const isApplyAllAtOnceEnabledAndSet = yield select(selectIsApplyFiltersAllAtOnceEnabledAndSet);
|
|
117
|
+
if (isApplyAllAtOnceEnabledAndSet) {
|
|
118
|
+
yield put(tabsActions.resetWorkingSelection());
|
|
119
|
+
}
|
|
120
|
+
const parameterEntries = yield select(selectDashboardParameterEntries);
|
|
121
|
+
const workspaceParameters = yield select(selectCatalogParameters);
|
|
122
|
+
yield call(changeParameterValuesHandler, ctx, changeParameterValues({
|
|
123
|
+
parameters: resolveParameterValuesForFilterView(parameterEntries, filterView.parameters ?? [], workspaceParameters),
|
|
124
|
+
correlationId: cmd.correlationId,
|
|
125
|
+
adoptAsOriginal: options?.adoptAsOriginal,
|
|
126
|
+
}));
|
|
127
|
+
// inline, so the view is applied before the caller continues: a queued command would run after
|
|
128
|
+
// the caller's own filter changes (e.g. automation filters after a tab switch) and overwrite them
|
|
129
|
+
yield call(changeFilterContextSelectionHandler, ctx, changeFilterContextSelectionByParams({
|
|
130
|
+
filters: filterView.filterContext.filters,
|
|
131
|
+
attributeFilterConfigs,
|
|
132
|
+
resetOthers: true,
|
|
133
|
+
correlationId: cmd.correlationId,
|
|
134
|
+
// a default view initializes the shared state, so the applied values are what a reset restores
|
|
135
|
+
adoptAsOriginal: options?.adoptAsOriginal,
|
|
136
|
+
// a filter view snapshots this dashboard's own filter context, so its localIdentifiers
|
|
137
|
+
// are authoritative; display form matching alone cannot tell apart filters that share
|
|
138
|
+
// a display form
|
|
139
|
+
matchByLocalIdentifier: true,
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* While filters persist across tabs, the dashboard opens with the default view of its active tab as
|
|
144
|
+
* the shared state. Every other tab applies its own default view, to all tabs, on its first visit in
|
|
145
|
+
* the session. Later visits keep the shared state. A drill to the tab sets the shared state itself, so
|
|
146
|
+
* it counts as the first visit without applying the view.
|
|
147
|
+
*
|
|
148
|
+
* @internal
|
|
149
|
+
*/
|
|
150
|
+
export function* applyDefaultFilterViewOnFirstTabVisit(ctx, cmd, tabLocalIdentifier) {
|
|
151
|
+
const persistFiltersAcrossTabs = yield select(selectEnablePersistentFiltersAcrossTabs);
|
|
152
|
+
// edit mode starts from the saved filters, same as on load
|
|
153
|
+
const isInEditMode = yield select(selectIsInEditMode);
|
|
154
|
+
if (!persistFiltersAcrossTabs || isInEditMode) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const appliedTabs = yield select(selectDefaultFilterViewAppliedTabs);
|
|
158
|
+
if (appliedTabs.includes(tabLocalIdentifier)) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (cmd.payload.source === "drillToSelf") {
|
|
162
|
+
yield put(filterViewsActions.markDefaultFilterViewApplied(tabLocalIdentifier));
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const filterViews = yield select(selectDashboardFilterViews);
|
|
166
|
+
const firstTabLocalIdentifier = yield select(selectFirstTabLocalIdentifier);
|
|
167
|
+
const defaultFilterView = findTabDefaultFilterView(filterViews, tabLocalIdentifier, tabLocalIdentifier === firstTabLocalIdentifier);
|
|
168
|
+
// A failed view must not fail the caller (the tab is already switched, a save already persisted).
|
|
169
|
+
// The tab stays unmarked, so its next visit retries the view.
|
|
170
|
+
try {
|
|
171
|
+
if (defaultFilterView) {
|
|
172
|
+
yield call(applyFilterViewSelection, ctx, cmd, defaultFilterView, { adoptAsOriginal: true });
|
|
173
|
+
}
|
|
174
|
+
yield put(filterViewsActions.markDefaultFilterViewApplied(tabLocalIdentifier));
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
defaultErrorHandler(error);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
125
180
|
function setFilterViewAsDefault(ctx, ref, isDefault) {
|
|
126
181
|
return ctx.backend.workspace(ctx.workspace).dashboards().setFilterViewAsDefault(ref, isDefault);
|
|
127
182
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Action } from "@reduxjs/toolkit";
|
|
2
|
+
import { type IWrittenTabFilters } from "../../../store/tabs/filterContext/filterContextReducers.js";
|
|
3
|
+
import { type IWrittenTabParameters } from "../../../store/tabs/parameters/parametersReducers.js";
|
|
4
|
+
/**
|
|
5
|
+
* The applied filters that `actions` write, per tab. Staged selection changes are not applied, so they
|
|
6
|
+
* are skipped.
|
|
7
|
+
*
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export declare function writtenFiltersOf(actions: readonly Action[]): IWrittenTabFilters[];
|
|
11
|
+
/**
|
|
12
|
+
* The parameters that `actions` write, per tab.
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
export declare function writtenParametersOf(actions: readonly Action[]): IWrittenTabParameters[];
|
|
17
|
+
//# sourceMappingURL=writtenTargets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writtenTargets.d.ts","sourceRoot":"","sources":["../../../../../src/model/commandHandlers/filterContext/tabSync/writtenTargets.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAK/C,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,4DAA4D,CAAC;AAErG,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,sDAAsD,CAAC;AAMlG;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,kBAAkB,EAAE,CA8CjF;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,qBAAqB,EAAE,CAgBvF"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// (C) 2026 GoodData Corporation
|
|
2
|
+
import { BATCH } from "redux-batched-actions";
|
|
3
|
+
import { areObjRefsEqual } from "@gooddata/sdk-model";
|
|
4
|
+
import { tabsActions } from "../../../store/tabs/index.js";
|
|
5
|
+
/**
|
|
6
|
+
* The applied filters that `actions` write, per tab. Staged selection changes are not applied, so they
|
|
7
|
+
* are skipped.
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
export function writtenFiltersOf(actions) {
|
|
12
|
+
const byTab = new Map();
|
|
13
|
+
const forTab = (tabLocalIdentifier) => {
|
|
14
|
+
let written = byTab.get(tabLocalIdentifier);
|
|
15
|
+
if (!written) {
|
|
16
|
+
written = { tabLocalIdentifier, filterLocalIds: [], dateDataSets: [], commonDateFilter: false };
|
|
17
|
+
byTab.set(tabLocalIdentifier, written);
|
|
18
|
+
}
|
|
19
|
+
return written;
|
|
20
|
+
};
|
|
21
|
+
const addFilters = (tabLocalIdentifier, localIdentifiers) => {
|
|
22
|
+
const written = forTab(tabLocalIdentifier);
|
|
23
|
+
written.filterLocalIds = [...new Set([...written.filterLocalIds, ...localIdentifiers])];
|
|
24
|
+
};
|
|
25
|
+
for (const action of actions.flatMap(unbatched)) {
|
|
26
|
+
if (tabsActions.updateAttributeFilterSelection.match(action) ||
|
|
27
|
+
tabsActions.replaceAttributeFilterItem.match(action) ||
|
|
28
|
+
tabsActions.changeAttributeDisplayForm.match(action)) {
|
|
29
|
+
if (!action.payload.isWorkingSelectionChange) {
|
|
30
|
+
addFilters(action.payload.tabLocalIdentifier, [action.payload.filterLocalId]);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else if (tabsActions.clearAttributeFiltersSelection.match(action)) {
|
|
34
|
+
addFilters(action.payload.tabLocalIdentifier, action.payload.filterLocalIds);
|
|
35
|
+
}
|
|
36
|
+
else if (tabsActions.changeMeasureValueFilterCondition.match(action) ||
|
|
37
|
+
tabsActions.setMeasureValueFilterDimensionality.match(action)) {
|
|
38
|
+
if (!("isWorkingSelectionChange" in action.payload && action.payload.isWorkingSelectionChange)) {
|
|
39
|
+
addFilters(action.payload.tabLocalIdentifier, [action.payload.localIdentifier]);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else if (tabsActions.upsertDateFilter.match(action) && !action.payload.isWorkingSelectionChange) {
|
|
43
|
+
const written = forTab(action.payload.tabLocalIdentifier);
|
|
44
|
+
const { dataSet } = action.payload;
|
|
45
|
+
if (!dataSet) {
|
|
46
|
+
written.commonDateFilter = true;
|
|
47
|
+
}
|
|
48
|
+
else if (!written.dateDataSets.some((writtenDataSet) => areObjRefsEqual(writtenDataSet, dataSet))) {
|
|
49
|
+
written.dateDataSets = [...written.dateDataSets, dataSet];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return [...byTab.values()];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The parameters that `actions` write, per tab.
|
|
57
|
+
*
|
|
58
|
+
* @internal
|
|
59
|
+
*/
|
|
60
|
+
export function writtenParametersOf(actions) {
|
|
61
|
+
const byTab = new Map();
|
|
62
|
+
for (const action of actions.flatMap(unbatched)) {
|
|
63
|
+
if (!tabsActions.setParameterRuntimeValues.match(action)) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const { tabLocalIdentifier, values } = action.payload;
|
|
67
|
+
const refs = byTab.get(tabLocalIdentifier) ?? [];
|
|
68
|
+
for (const { ref } of values) {
|
|
69
|
+
if (!refs.some((writtenRef) => areObjRefsEqual(writtenRef, ref))) {
|
|
70
|
+
refs.push(ref);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
byTab.set(tabLocalIdentifier, refs);
|
|
74
|
+
}
|
|
75
|
+
return [...byTab.entries()].map(([tabLocalIdentifier, refs]) => ({ tabLocalIdentifier, refs }));
|
|
76
|
+
}
|
|
77
|
+
function unbatched(action) {
|
|
78
|
+
return action.type === BATCH ? action.payload.flatMap(unbatched) : [action];
|
|
79
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changeParameterValuesHandler.d.ts","sourceRoot":"","sources":["../../../../src/model/commandHandlers/parameters/changeParameterValuesHandler.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,OAAO,EAAE,KAAK,sBAAsB,EAAmB,MAAM,qBAAqB,CAAC;AAEnF,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,KAAK,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAY3E,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"changeParameterValuesHandler.d.ts","sourceRoot":"","sources":["../../../../src/model/commandHandlers/parameters/changeParameterValuesHandler.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,OAAO,EAAE,KAAK,sBAAsB,EAAmB,MAAM,qBAAqB,CAAC;AAEnF,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,KAAK,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAY3E,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAInE;;;;;;;GAOG;AACH,wBAAiB,4BAA4B,CACzC,GAAG,EAAE,gBAAgB,EACrB,GAAG,EAAE,sBAAsB,GAC5B,YAAY,CAAC,IAAI,CAAC,CA2BpB;AAoCD;;;;;GAKG;AACH,wBAAiB,wCAAwC,CACrD,GAAG,EAAE,gBAAgB,EACrB,GAAG,EAAE,iBAAiB,EACtB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,EAAE,GAAG,SAAS,CAAC,EAClE,WAAW,EAAE,MAAM,GACpB,YAAY,CAAC,IAAI,CAAC,CAOpB;AAED;;;;;GAKG;AACH,wBAAiB,yBAAyB,CACtC,GAAG,EAAE,gBAAgB,EACrB,GAAG,EAAE,iBAAiB,EACtB,kBAAkB,EAAE,MAAM,EAC1B,YAAY,EAAE,sBAAsB,EAAE,GAAG,SAAS,GACnD,YAAY,CAAC,IAAI,CAAC,CASpB"}
|