diff --git a/web-admin/src/features/bookmarks/BookmarksDropdownMenuContent.svelte b/web-admin/src/features/bookmarks/BookmarksDropdownMenuContent.svelte index 660e7e26ccd..e0cfbe739a5 100644 --- a/web-admin/src/features/bookmarks/BookmarksDropdownMenuContent.svelte +++ b/web-admin/src/features/bookmarks/BookmarksDropdownMenuContent.svelte @@ -27,6 +27,7 @@ import { getDefaultExplorePreset } from "@rilldata/web-common/features/dashboards/url-state/getDefaultExplorePreset"; import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors"; import { useExploreValidSpec } from "@rilldata/web-common/features/explores/selectors"; + import { createQueryServiceMetricsViewSchema } from "@rilldata/web-common/runtime-client"; import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; import { BookmarkPlusIcon } from "lucide-svelte"; import { createEventDispatcher } from "svelte"; @@ -51,6 +52,10 @@ exploreSpec, $metricsViewTimeRange.data, ); + $: schemaResp = createQueryServiceMetricsViewSchema( + $runtime.instanceId, + metricsViewName, + ); $: projectIdResp = useProjectId(organization, project); const userResp = createAdminServiceGetCurrentUser(); @@ -72,7 +77,7 @@ $bookamrksResp.data?.bookmarks ?? [], metricsViewSpec, exploreSpec, - {}, + $schemaResp.data?.schema, $exploreState, defaultExplorePreset, ); diff --git a/web-admin/src/features/bookmarks/selectors.ts b/web-admin/src/features/bookmarks/selectors.ts index 58e764722fb..d870486ad62 100644 --- a/web-admin/src/features/bookmarks/selectors.ts +++ b/web-admin/src/features/bookmarks/selectors.ts @@ -67,6 +67,7 @@ export function categorizeBookmarks( personal: [], shared: [], }; + if (!exploreState) return bookmarks; bookmarkResp?.forEach((bookmarkResource) => { const bookmark = parseBookmark( bookmarkResource, diff --git a/web-admin/src/features/dashboards/query-mappers/mapQueryToDashboard.ts b/web-admin/src/features/dashboards/query-mappers/mapQueryToDashboard.ts index 4a65ce3d724..628e6e9e5b6 100644 --- a/web-admin/src/features/dashboards/query-mappers/mapQueryToDashboard.ts +++ b/web-admin/src/features/dashboards/query-mappers/mapQueryToDashboard.ts @@ -5,8 +5,10 @@ import type { QueryRequests, } from "@rilldata/web-admin/features/dashboards/query-mappers/types"; import type { CompoundQueryResult } from "@rilldata/web-common/features/compound-query-result"; -import { getDefaultExploreState } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; +import { getFullInitExploreState } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { convertPresetToExploreState } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToExploreState"; +import { getDefaultExplorePreset } from "@rilldata/web-common/features/dashboards/url-state/getDefaultExplorePreset"; import { initLocalUserPreferenceStore } from "@rilldata/web-common/features/dashboards/user-preferences"; import { useExploreValidSpec } from "@rilldata/web-common/features/explores/selectors"; import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient"; @@ -117,12 +119,19 @@ export function mapQueryToDashboard( const { metricsView, explore } = validSpecResp.data; initLocalUserPreferenceStore(metricsViewName); - const defaultExploreState = getDefaultExploreState( - metricsViewName, - metricsView, - explore, + const defaultExplorePreset = getDefaultExplorePreset( + validSpecResp.data.explore, timeRangeSummary.data, ); + const { partialExploreState } = convertPresetToExploreState( + validSpecResp.data.metricsView, + validSpecResp.data.explore, + defaultExplorePreset, + ); + const defaultExploreState = getFullInitExploreState( + metricsViewName, + partialExploreState, + ); getDashboardState({ queryClient, instanceId, diff --git a/web-admin/src/routes/[organization]/[project]/-/share/[token]/+layout.ts b/web-admin/src/routes/[organization]/[project]/-/share/[token]/+layout.ts index 86058fe4b0f..d350f688cca 100644 --- a/web-admin/src/routes/[organization]/[project]/-/share/[token]/+layout.ts +++ b/web-admin/src/routes/[organization]/[project]/-/share/[token]/+layout.ts @@ -1,5 +1,10 @@ import { fetchMagicAuthToken } from "@rilldata/web-admin/features/projects/selectors"; -import { fetchExploreSpec } from "@rilldata/web-common/features/explores/selectors"; +import { getDashboardStateFromUrl } from "@rilldata/web-common/features/dashboards/proto-state/fromProto"; +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { + fetchExploreSpec, + fetchMetricsViewSchema, +} from "@rilldata/web-common/features/explores/selectors"; import { error } from "@sveltejs/kit"; export const load = async ({ params: { token }, parent }) => { @@ -12,16 +17,38 @@ export const load = async ({ params: { token }, parent }) => { throw new Error("Token does not have an associated resource name"); } - const { explore, metricsView, defaultExplorePreset } = - await fetchExploreSpec( - runtime.instanceId as string, - tokenData.token.resourceName, + const exploreName = tokenData.token?.resourceName; + + const { + explore, + metricsView, + defaultExplorePreset, + exploreStateFromYAMLConfig, + } = await fetchExploreSpec(runtime?.instanceId, exploreName); + const metricsViewSpec = metricsView.metricsView?.state?.validSpec ?? {}; + const exploreSpec = explore.explore?.state?.validSpec ?? {}; + + let tokenExploreState: Partial | undefined = + undefined; + if (tokenData.token?.state) { + const schema = await fetchMetricsViewSchema( + runtime?.instanceId, + exploreSpec.metricsView ?? "", + ); + tokenExploreState = getDashboardStateFromUrl( + tokenData.token?.state, + metricsViewSpec, + exploreSpec, + schema, ); + } return { explore, metricsView, defaultExplorePreset, + exploreStateFromYAMLConfig, + tokenExploreState, token: tokenData?.token, }; } catch (e) { diff --git a/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.svelte index d84646ce3c7..13b33795ba2 100644 --- a/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.svelte @@ -15,8 +15,11 @@ $: ({ defaultExplorePreset, - partialExploreState, - token: { resourceName }, + tokenExploreState, + exploreStateFromYAMLConfig, + partialExploreStateFromUrl, + exploreStateFromSessionStorage, + token: { resourceName, id: tokenId }, } = data); $: ({ organization, project } = $page.params); @@ -55,7 +58,16 @@ metricsViewName={explore.metricsView.meta.name.name} exploreName={resourceName} > - + { const { explore, metricsView, defaultExplorePreset, token } = await parent(); - const exploreName = token.resourceName; + const exploreName = token?.resourceName; const metricsViewSpec = metricsView.metricsView?.state?.validSpec; const exploreSpec = explore.explore?.state?.validSpec; - // On the first dashboard load, if there are no URL params, append the token's state (in human-readable format) to the URL. - if ( - token.state && - ![...url.searchParams.keys()].length && - !(exploreName in get(metricsExplorerStore).entities) - ) { - const exploreState = getDashboardStateFromUrl( - token.state, - metricsViewSpec, - exploreSpec, - {}, // TODO - ); - const newUrl = new URL(url); - newUrl.search = convertExploreStateToURLSearchParams( - exploreState as MetricsExplorerEntity, - exploreSpec, - defaultExplorePreset, - ); - throw redirect(307, `${newUrl.pathname}${newUrl.search}`); - } - - // Get Explore state from URL params - let partialExploreState: Partial = {}; - const errors: Error[] = []; - if (metricsViewSpec && exploreSpec) { - const { - partialExploreState: partialExploreStateFromUrl, - errors: errorsFromConvert, - } = convertURLToExploreState( - url.searchParams, - metricsViewSpec, - exploreSpec, - defaultExplorePreset, - ); - partialExploreState = partialExploreStateFromUrl; - errors.push(...errorsFromConvert); - } - - return { - partialExploreState, - errors, - }; + return getExploreStates( + exploreName, + `${token.id}__`, + url.searchParams, + metricsViewSpec, + exploreSpec, + defaultExplorePreset, + ); }; diff --git a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+layout.ts b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+layout.ts index 914f498f0b8..b905022109c 100644 --- a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+layout.ts +++ b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+layout.ts @@ -1,8 +1,14 @@ +import type { V1Bookmark } from "@rilldata/web-admin/client"; import { fetchBookmarks, isHomeBookmark, } from "@rilldata/web-admin/features/bookmarks/selectors"; -import { fetchExploreSpec } from "@rilldata/web-common/features/explores/selectors"; +import { getDashboardStateFromUrl } from "@rilldata/web-common/features/dashboards/proto-state/fromProto"; +import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { + fetchExploreSpec, + fetchMetricsViewSchema, +} from "@rilldata/web-common/features/explores/selectors"; import { type V1ExplorePreset, type V1Resource, @@ -11,23 +17,29 @@ import { export const load = async ({ params, depends, parent }) => { const { project, runtime } = await parent(); - const exploreName = params.dashboard; + const { dashboard: exploreName } = params; depends(exploreName, "explore"); - try { - const { explore, metricsView, defaultExplorePreset } = - await fetchExploreSpec(runtime?.instanceId, exploreName); - - // used to merge home bookmark to url state - const bookmarks = await fetchBookmarks(project.id, exploreName); + let explore: V1Resource | undefined; + let metricsView: V1Resource | undefined; + let defaultExplorePreset: V1ExplorePreset | undefined; + let exploreStateFromYAMLConfig: Partial = {}; + let bookmarks: V1Bookmark[] | undefined; - return { - explore, - metricsView, - defaultExplorePreset, - homeBookmark: bookmarks.find(isHomeBookmark), - }; + try { + [ + { + explore, + metricsView, + defaultExplorePreset, + exploreStateFromYAMLConfig, + }, + bookmarks, + ] = await Promise.all([ + fetchExploreSpec(runtime?.instanceId, exploreName), + fetchBookmarks(project.id, exploreName), + ]); } catch { // error handled in +page.svelte for now // TODO: move it here @@ -35,7 +47,39 @@ export const load = async ({ params, depends, parent }) => { explore: {}, metricsView: {}, defaultExplorePreset: {}, - homeBookmark: undefined, + exploreStateFromYAMLConfig, }; } + + const metricsViewSpec = metricsView.metricsView?.state?.validSpec ?? {}; + const exploreSpec = explore.explore?.state?.validSpec ?? {}; + + let homeBookmarkExploreState: Partial | undefined = + undefined; + try { + const homeBookmark = bookmarks.find(isHomeBookmark); + const schema = await fetchMetricsViewSchema( + runtime?.instanceId, + exploreSpec.metricsView ?? "", + ); + + if (homeBookmark) { + homeBookmarkExploreState = getDashboardStateFromUrl( + homeBookmark.data ?? "", + metricsViewSpec, + exploreSpec, + schema, + ); + } + } catch { + // TODO + } + + return { + explore, + metricsView, + defaultExplorePreset, + exploreStateFromYAMLConfig, + homeBookmarkExploreState, + }; }; diff --git a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte index 93f22dd32c6..0ff39204ae9 100644 --- a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte @@ -19,12 +19,21 @@ // const PollIntervalWhenDashboardOk = 60000; // This triggers a layout shift, so removing for now export let data: PageData; - $: ({ defaultExplorePreset, partialExploreState, errors } = data); + $: ({ + defaultExplorePreset, + homeBookmarkExploreState, + exploreStateFromYAMLConfig, + partialExploreStateFromUrl, + exploreStateFromSessionStorage, + errors, + exploreName, + } = data); $: if (errors?.length) { + const _errs = errors; setTimeout(() => { eventBus.emit("notification", { type: "error", - message: errors[0].message, + message: _errs[0].message, options: { persisted: true, }, @@ -33,11 +42,7 @@ } $: instanceId = $runtime?.instanceId; - $: ({ - organization: orgName, - project: projectName, - dashboard: exploreName, - } = $page.params); + $: ({ organization: orgName, project: projectName } = $page.params); $: explore = useExplore(instanceId, exploreName, { refetchInterval: () => { @@ -98,7 +103,16 @@ {:else if metricsViewName} {#key exploreName} - + diff --git a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.ts b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.ts index fecf0f6d21a..9794c8dfadc 100644 --- a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.ts +++ b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.ts @@ -1,48 +1,20 @@ -import { convertBookmarkToUrlSearchParams } from "@rilldata/web-admin/features/bookmarks/selectors"; -import { metricsExplorerStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; -import { getPartialExploreStateOrRedirect } from "@rilldata/web-common/features/explores/selectors"; -import { redirect } from "@sveltejs/kit"; -import { get } from "svelte/store"; +import { getExploreStates } from "@rilldata/web-common/features/explores/selectors"; export const load = async ({ url, parent, params }) => { - const { explore, metricsView, defaultExplorePreset, homeBookmark } = - await parent(); + const { explore, metricsView, defaultExplorePreset } = await parent(); const { organization, project, dashboard: exploreName } = params; const metricsViewSpec = metricsView.metricsView?.state?.validSpec; const exploreSpec = explore.explore?.state?.validSpec; - // On the first dashboard load, if there are no URL params, redirect to the "Home" bookmark. - if ( - homeBookmark && - ![...url.searchParams.keys()].length && - !(exploreName in get(metricsExplorerStore).entities) - ) { - const newUrl = new URL(url); - newUrl.search = convertBookmarkToUrlSearchParams( - homeBookmark, + return { + exploreName, + ...getExploreStates( + exploreName, + `${organization}__${project}__`, + url.searchParams, metricsViewSpec, exploreSpec, - {}, // TODO - undefined, defaultExplorePreset, - ); - - if (newUrl.search !== url.search) { - throw redirect(307, `${newUrl.pathname}${newUrl.search}`); - } - } - - const { partialExploreState, errors } = getPartialExploreStateOrRedirect( - exploreName, - metricsViewSpec, - exploreSpec, - defaultExplorePreset, - `__${organization}__${project}`, - url, - ); - - return { - partialExploreState, - errors, + ), }; }; diff --git a/web-common/src/features/dashboards/big-number/MeasureBigNumber.svelte b/web-common/src/features/dashboards/big-number/MeasureBigNumber.svelte index ca5fea0151b..c13d0800582 100644 --- a/web-common/src/features/dashboards/big-number/MeasureBigNumber.svelte +++ b/web-common/src/features/dashboards/big-number/MeasureBigNumber.svelte @@ -1,10 +1,7 @@ shiftClickHandler(copyValue), click: () => { diff --git a/web-common/src/features/dashboards/proto-state/fromProto.ts b/web-common/src/features/dashboards/proto-state/fromProto.ts index 3084b27fb22..d52d0f76a9f 100644 --- a/web-common/src/features/dashboards/proto-state/fromProto.ts +++ b/web-common/src/features/dashboards/proto-state/fromProto.ts @@ -214,7 +214,9 @@ export function getDashboardStateFromProto( entity.dashboardSortType = dashboard.leaderboardSortType; } - entity.pivot = fromPivotProto(dashboard, metricsView); + if (dashboard.pivotIsActive !== undefined) { + entity.pivot = fromPivotProto(dashboard, metricsView); + } Object.assign(entity, fromActivePageProto(dashboard)); diff --git a/web-common/src/features/dashboards/proto-state/proto.spec.ts b/web-common/src/features/dashboards/proto-state/proto.spec.ts index e8afc3f26e6..6f4ed6190ea 100644 --- a/web-common/src/features/dashboards/proto-state/proto.spec.ts +++ b/web-common/src/features/dashboards/proto-state/proto.spec.ts @@ -1,7 +1,7 @@ import { protoBase64, Value } from "@bufbuild/protobuf"; import { getDashboardStateFromUrl } from "@rilldata/web-common/features/dashboards/proto-state/fromProto"; import { getProtoFromDashboardState } from "@rilldata/web-common/features/dashboards/proto-state/toProto"; -import { getDefaultExploreState } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; +import { getFullInitExploreState } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; import { createAndExpression, createInExpression, @@ -17,6 +17,7 @@ import { AD_BIDS_SCHEMA, TestTimeConstants, } from "@rilldata/web-common/features/dashboards/stores/test-data/data"; +import { getInitExploreStateForTest } from "@rilldata/web-common/features/dashboards/stores/test-data/helpers"; import { getLocalUserPreferences, initLocalUserPreferenceStore, @@ -39,17 +40,19 @@ describe("toProto/fromProto", () => { }); it("backwards compatibility for time controls", () => { - const metricsExplorer = getDefaultExploreState( + const metricsExplorer = getFullInitExploreState( AD_BIDS_NAME, - AD_BIDS_METRICS_INIT_WITH_TIME, - AD_BIDS_EXPLORE_INIT, - { - timeRangeSummary: { - min: TestTimeConstants.LAST_DAY.toISOString(), - max: TestTimeConstants.NOW.toISOString(), - interval: V1TimeGrain.TIME_GRAIN_MINUTE as any, + getInitExploreStateForTest( + AD_BIDS_METRICS_INIT_WITH_TIME, + AD_BIDS_EXPLORE_INIT, + { + timeRangeSummary: { + min: TestTimeConstants.LAST_DAY.toISOString(), + max: TestTimeConstants.NOW.toISOString(), + interval: V1TimeGrain.TIME_GRAIN_MINUTE as any, + }, }, - }, + ), ); metricsExplorer.selectedTimeRange = { name: "LAST_SIX_HOURS", diff --git a/web-common/src/features/dashboards/proto-state/sparse-proto.spec.ts b/web-common/src/features/dashboards/proto-state/sparse-proto.spec.ts index 45bc03e7ee4..3b709c2b45a 100644 --- a/web-common/src/features/dashboards/proto-state/sparse-proto.spec.ts +++ b/web-common/src/features/dashboards/proto-state/sparse-proto.spec.ts @@ -1,5 +1,5 @@ import { getProtoFromDashboardState } from "@rilldata/web-common/features/dashboards/proto-state/toProto"; -import { getDefaultExploreState } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; +import { getFullInitExploreState } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; import { metricsExplorerStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { @@ -10,6 +10,7 @@ import { AD_BIDS_TIME_RANGE_SUMMARY, } from "@rilldata/web-common/features/dashboards/stores/test-data/data"; import { + getInitExploreStateForTest, getPartialDashboard, resetDashboardStore, } from "@rilldata/web-common/features/dashboards/stores/test-data/helpers"; @@ -94,11 +95,13 @@ describe("sparse proto", () => { describe("should reset dashboard store", () => { for (const { title, mutations } of TestCases) { it(`from ${title}`, () => { - const dashboard = getDefaultExploreState( + const dashboard = getFullInitExploreState( AD_BIDS_EXPLORE_NAME, - AD_BIDS_METRICS_INIT, - AD_BIDS_EXPLORE_INIT, - AD_BIDS_TIME_RANGE_SUMMARY, + getInitExploreStateForTest( + AD_BIDS_METRICS_INIT, + AD_BIDS_EXPLORE_INIT, + AD_BIDS_TIME_RANGE_SUMMARY, + ), ); const defaultProto = getProtoFromDashboardState(dashboard); diff --git a/web-common/src/features/dashboards/state-managers/StateManagersProvider.svelte b/web-common/src/features/dashboards/state-managers/StateManagersProvider.svelte index 2fe6884fe3c..760a05887bf 100644 --- a/web-common/src/features/dashboards/state-managers/StateManagersProvider.svelte +++ b/web-common/src/features/dashboards/state-managers/StateManagersProvider.svelte @@ -17,7 +17,7 @@ metricsViewName, exploreName, extraKeyPrefix: - orgName && projectName ? `__${orgName}__${projectName}` : "", + orgName && projectName ? `${orgName}__${projectName}__` : "", }); setContext(DEFAULT_STORE_KEY, stateManagers); diff --git a/web-common/src/features/dashboards/state-managers/state-managers.ts b/web-common/src/features/dashboards/state-managers/state-managers.ts index da823ec7a74..33e23aecf6e 100644 --- a/web-common/src/features/dashboards/state-managers/state-managers.ts +++ b/web-common/src/features/dashboards/state-managers/state-managers.ts @@ -3,11 +3,7 @@ import { type MetricsExplorerEntity, contextColWidthDefaults, } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; -import { - getPersistentDashboardStore, - initPersistentDashboardStore, -} from "@rilldata/web-common/features/dashboards/stores/persistent-dashboard-state"; -import { updateExploreSessionStore } from "@rilldata/web-common/features/dashboards/url-state/explore-web-view-store"; +import { createPersistentDashboardStore } from "@rilldata/web-common/features/dashboards/stores/persistent-dashboard-state"; import { getDefaultExplorePreset } from "@rilldata/web-common/features/dashboards/url-state/getDefaultExplorePreset"; import { initLocalUserPreferenceStore } from "@rilldata/web-common/features/dashboards/user-preferences"; import { @@ -125,6 +121,8 @@ export function createStateManagers({ query: { queryClient, enabled: !!validSpec?.data?.metricsView?.timeDimension, + staleTime: Infinity, + cacheTime: Infinity, }, }, ).subscribe(set), @@ -154,21 +152,11 @@ export function createStateManagers({ ); }, ); - dashboardStore.subscribe((dashState) => { - const exploreState = get(validSpecStore).data?.explore; - if (!dashState || !exploreState) return; - updateExploreSessionStore( - exploreName, - extraKeyPrefix, - dashState, - exploreState, - ); - }); - // TODO: once we move everything from dashboard-stores to here, we can get rid of the global - initPersistentDashboardStore((extraKeyPrefix || "") + exploreName); + const persistentDashboardStore = createPersistentDashboardStore( + (extraKeyPrefix || "") + exploreName, + ); initLocalUserPreferenceStore(exploreName); - const persistentDashboardStore = getPersistentDashboardStore(); return { runtime: runtime, diff --git a/web-common/src/features/dashboards/stores/AdvancedMeasureCorrector.spec.ts b/web-common/src/features/dashboards/stores/AdvancedMeasureCorrector.spec.ts index 566c8273cb9..a0d9019f789 100644 --- a/web-common/src/features/dashboards/stores/AdvancedMeasureCorrector.spec.ts +++ b/web-common/src/features/dashboards/stores/AdvancedMeasureCorrector.spec.ts @@ -3,7 +3,8 @@ import { MeasureFilterType, } from "@rilldata/web-common/features/dashboards/filters/measure-filters/measure-filter-options"; import { AdvancedMeasureCorrector } from "@rilldata/web-common/features/dashboards/stores/AdvancedMeasureCorrector"; -import { getDefaultExploreState } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; +import { getFullInitExploreState } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; +import { getInitExploreStateForTest } from "@rilldata/web-common/features/dashboards/stores/test-data/helpers"; import type { DashboardTimeControls } from "@rilldata/web-common/lib/time/types"; import { V1TimeGrain, @@ -34,11 +35,9 @@ describe("AdvancedMeasureCorrector", () => { } as V1ExploreSpec; it("changing grain while in TDD for measure based on timestamp", () => { - const dashboard = getDefaultExploreState( + const dashboard = getFullInitExploreState( "AdBids", - MetricsView, - Explore, - undefined, + getInitExploreStateForTest(MetricsView, Explore), ); dashboard.tdd.expandedMeasureName = AD_BIDS_IMPRESSIONS_MEASURE_NO_GRAIN; @@ -71,11 +70,9 @@ describe("AdvancedMeasureCorrector", () => { }); it("metrics view spec changed converting a measure to an advanced measure", () => { - const dashboard = getDefaultExploreState( + const dashboard = getFullInitExploreState( "AdBids", - MetricsView, - Explore, - undefined, + getInitExploreStateForTest(MetricsView, Explore), ); dashboard.leaderboardMeasureName = AD_BIDS_IMPRESSIONS_MEASURE; dashboard.dimensionThresholdFilters = [ diff --git a/web-common/src/features/dashboards/stores/DashboardStateProvider.svelte b/web-common/src/features/dashboards/stores/DashboardStateProvider.svelte deleted file mode 100644 index d7dffbe72ab..00000000000 --- a/web-common/src/features/dashboards/stores/DashboardStateProvider.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - -{#if $dashboardStoreReady.isFetching} -
- -
-{:else} - -{/if} diff --git a/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts b/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts index ce0203be25a..d6e6d9608ea 100644 --- a/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts +++ b/web-common/src/features/dashboards/stores/dashboard-store-defaults.ts @@ -3,28 +3,12 @@ import { contextColWidthDefaults, type MetricsExplorerEntity, } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; -import { getPersistentDashboardState } from "@rilldata/web-common/features/dashboards/stores/persistent-dashboard-state"; -import { convertPresetToExploreState } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToExploreState"; -import { getDefaultExplorePreset } from "@rilldata/web-common/features/dashboards/url-state/getDefaultExplorePreset"; -import type { - V1ExploreSpec, - V1MetricsViewSpec, - V1MetricsViewTimeRangeResponse, -} from "@rilldata/web-common/runtime-client"; -// TODO: Remove this in favour of just `getBasePreset` -export function getDefaultExploreState( +// TODO: Remove this in favour of just `getDefaultExplorePreset` +export function getFullInitExploreState( name: string, - metricsView: V1MetricsViewSpec, - explore: V1ExploreSpec, - fullTimeRange: V1MetricsViewTimeRangeResponse | undefined, - defaultExplorePreset = getDefaultExplorePreset(explore, fullTimeRange), + partialInitState: Partial, ): MetricsExplorerEntity { - const { partialExploreState } = convertPresetToExploreState( - metricsView, - explore, - defaultExplorePreset, - ); return { // fields filled here are the ones that are not stored and loaded to/from URL name, @@ -36,42 +20,6 @@ export function getDefaultExploreState( lastDefinedScrubRange: undefined, - ...partialExploreState, + ...partialInitState, } as MetricsExplorerEntity; } - -// TODO: move this to the same place where we load from session store. -// also move the type to V1ExplorePreset similar to session store. -export function restorePersistedDashboardState( - metricsExplorer: MetricsExplorerEntity, -) { - const persistedState = getPersistentDashboardState(); - if (persistedState.visibleMeasures) { - metricsExplorer.allMeasuresVisible = - persistedState.visibleMeasures.length === - metricsExplorer.visibleMeasureKeys.size; // TODO: check values - metricsExplorer.visibleMeasureKeys = new Set( - persistedState.visibleMeasures, - ); - } - if (persistedState.visibleDimensions) { - metricsExplorer.allDimensionsVisible = - persistedState.visibleDimensions.length === - metricsExplorer.visibleDimensionKeys.size; // TODO: check values - metricsExplorer.visibleDimensionKeys = new Set( - persistedState.visibleDimensions, - ); - } - if (persistedState.leaderboardMeasureName) { - metricsExplorer.leaderboardMeasureName = - persistedState.leaderboardMeasureName; - } - if (persistedState.dashboardSortType) { - metricsExplorer.dashboardSortType = persistedState.dashboardSortType; - } - if (persistedState.sortDirection) { - metricsExplorer.sortDirection = persistedState.sortDirection; - } - - return metricsExplorer; -} diff --git a/web-common/src/features/dashboards/stores/dashboard-stores.spec.ts b/web-common/src/features/dashboards/stores/dashboard-stores.spec.ts index e9cc1126092..4e987bf03b7 100644 --- a/web-common/src/features/dashboards/stores/dashboard-stores.spec.ts +++ b/web-common/src/features/dashboards/stores/dashboard-stores.spec.ts @@ -3,10 +3,6 @@ import { createAndExpression, createInExpression, } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; -import { - getPersistentDashboardStore, - initPersistentDashboardStore, -} from "@rilldata/web-common/features/dashboards/stores/persistent-dashboard-state"; import { AD_BIDS_BASE_FILTER, AD_BIDS_BID_PRICE_MEASURE, @@ -30,6 +26,7 @@ import { assertMetricsView, assertMetricsViewRaw, createAdBidsMirrorInStore, + getInitExploreStateForTest, initStateManagers, resetDashboardStore, } from "@rilldata/web-common/features/dashboards/stores/test-data/helpers"; @@ -46,7 +43,6 @@ import { beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("dashboard-stores", () => { beforeAll(() => { initLocalUserPreferenceStore(AD_BIDS_EXPLORE_NAME); - initPersistentDashboardStore(AD_BIDS_EXPLORE_NAME); runtime.set({ instanceId: "", host: "", @@ -54,7 +50,6 @@ describe("dashboard-stores", () => { }); beforeEach(() => { - getPersistentDashboardStore().reset(); resetDashboardStore(); }); @@ -200,12 +195,10 @@ describe("dashboard-stores", () => { metricsExplorerStore.init( AD_BIDS_EXPLORE_NO_TIMESTAMP_NAME, - AD_BIDS_METRICS_INIT, - { + getInitExploreStateForTest(AD_BIDS_METRICS_INIT, { metricsView: AD_BIDS_NO_TIMESTAMP_NAME, ...AD_BIDS_EXPLORE_INIT, - }, - undefined, + }), ); assertMetricsViewRaw( AD_BIDS_EXPLORE_NO_TIMESTAMP_NAME, @@ -230,22 +223,24 @@ describe("dashboard-stores", () => { metricsExplorerStore.remove(AD_BIDS_EXPLORE_NAME); metricsExplorerStore.init( AD_BIDS_EXPLORE_NAME, - AD_BIDS_METRICS_INIT, - { - ...AD_BIDS_EXPLORE_INIT, - defaultPreset: { - timeRange: "PT6H", - comparisonMode: - V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_UNSPECIFIED, + getInitExploreStateForTest( + AD_BIDS_METRICS_INIT, + { + ...AD_BIDS_EXPLORE_INIT, + defaultPreset: { + timeRange: "PT6H", + comparisonMode: + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_UNSPECIFIED, + }, }, - }, - { - timeRangeSummary: { - min: TestTimeConstants.LAST_DAY.toISOString(), - max: TestTimeConstants.NOW.toISOString(), - interval: V1TimeGrain.TIME_GRAIN_MINUTE as any, + { + timeRangeSummary: { + min: TestTimeConstants.LAST_DAY.toISOString(), + max: TestTimeConstants.NOW.toISOString(), + interval: V1TimeGrain.TIME_GRAIN_MINUTE as any, + }, }, - }, + ), ); let metrics = get(metricsExplorerStore).entities[AD_BIDS_EXPLORE_NAME]; @@ -257,22 +252,24 @@ describe("dashboard-stores", () => { metricsExplorerStore.remove(AD_BIDS_EXPLORE_NAME); metricsExplorerStore.init( AD_BIDS_EXPLORE_NAME, - AD_BIDS_METRICS_INIT, - { - ...AD_BIDS_EXPLORE_INIT, - defaultPreset: { - timeRange: "PT6H", - comparisonMode: - V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_DIMENSION, + getInitExploreStateForTest( + AD_BIDS_METRICS_INIT, + { + ...AD_BIDS_EXPLORE_INIT, + defaultPreset: { + timeRange: "PT6H", + comparisonMode: + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_DIMENSION, + }, }, - }, - { - timeRangeSummary: { - min: TestTimeConstants.LAST_DAY.toISOString(), - max: TestTimeConstants.NOW.toISOString(), - interval: V1TimeGrain.TIME_GRAIN_MINUTE as any, + { + timeRangeSummary: { + min: TestTimeConstants.LAST_DAY.toISOString(), + max: TestTimeConstants.NOW.toISOString(), + interval: V1TimeGrain.TIME_GRAIN_MINUTE as any, + }, }, - }, + ), ); metrics = get(metricsExplorerStore).entities[AD_BIDS_EXPLORE_NAME]; expect(metrics.showTimeComparison).toBeFalsy(); @@ -284,23 +281,25 @@ describe("dashboard-stores", () => { metricsExplorerStore.remove(AD_BIDS_EXPLORE_NAME); metricsExplorerStore.init( AD_BIDS_EXPLORE_NAME, - AD_BIDS_METRICS_INIT, - { - ...AD_BIDS_EXPLORE_INIT, - defaultPreset: { - timeRange: "PT6H", - comparisonMode: - V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_DIMENSION, - comparisonDimension: AD_BIDS_DOMAIN_DIMENSION, + getInitExploreStateForTest( + AD_BIDS_METRICS_INIT, + { + ...AD_BIDS_EXPLORE_INIT, + defaultPreset: { + timeRange: "PT6H", + comparisonMode: + V1ExploreComparisonMode.EXPLORE_COMPARISON_MODE_DIMENSION, + comparisonDimension: AD_BIDS_DOMAIN_DIMENSION, + }, }, - }, - { - timeRangeSummary: { - min: TestTimeConstants.LAST_DAY.toISOString(), - max: TestTimeConstants.NOW.toISOString(), - interval: V1TimeGrain.TIME_GRAIN_MINUTE as any, + { + timeRangeSummary: { + min: TestTimeConstants.LAST_DAY.toISOString(), + max: TestTimeConstants.NOW.toISOString(), + interval: V1TimeGrain.TIME_GRAIN_MINUTE as any, + }, }, - }, + ), ); metrics = get(metricsExplorerStore).entities[AD_BIDS_EXPLORE_NAME]; expect(metrics.selectedComparisonDimension).toBe(AD_BIDS_DOMAIN_DIMENSION); diff --git a/web-common/src/features/dashboards/stores/dashboard-stores.ts b/web-common/src/features/dashboards/stores/dashboard-stores.ts index d6b43764bfb..57a4b78f06b 100644 --- a/web-common/src/features/dashboards/stores/dashboard-stores.ts +++ b/web-common/src/features/dashboards/stores/dashboard-stores.ts @@ -3,16 +3,13 @@ import { getDashboardStateFromUrl } from "@rilldata/web-common/features/dashboar import { getProtoFromDashboardState } from "@rilldata/web-common/features/dashboards/proto-state/toProto"; import { getWhereFilterExpressionIndex } from "@rilldata/web-common/features/dashboards/state-managers/selectors/dimension-filters"; import { AdvancedMeasureCorrector } from "@rilldata/web-common/features/dashboards/stores/AdvancedMeasureCorrector"; -import { - getDefaultExploreState, - restorePersistedDashboardState, -} from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; +import { getFullInitExploreState } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; import { createAndExpression, filterExpressions, forEachIdentifier, } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; -import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { type MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; import { TimeRangePreset, @@ -25,7 +22,6 @@ import type { V1ExploreSpec, V1Expression, V1MetricsViewSpec, - V1MetricsViewTimeRangeResponse, V1TimeGrain, } from "@rilldata/web-common/runtime-client"; import { @@ -170,23 +166,11 @@ function syncDimensions( } const metricsViewReducers = { - init( - name: string, - metricsView: V1MetricsViewSpec, - explore: V1ExploreSpec, - fullTimeRange: V1MetricsViewTimeRangeResponse | undefined, - initState: Partial = {}, - ) { + init(name: string, initState: Partial = {}) { update((state) => { if (state.entities[name]) return state; - state.entities[name] = { - ...getDefaultExploreState(name, metricsView, explore, fullTimeRange), - ...initState, - }; - state.entities[name] = restorePersistedDashboardState( - state.entities[name], - ); + state.entities[name] = getFullInitExploreState(name, initState); updateMetricsExplorerProto(state.entities[name]); diff --git a/web-common/src/features/dashboards/stores/persistent-dashboard-state.ts b/web-common/src/features/dashboards/stores/persistent-dashboard-state.ts index 41c6c01ceb8..66a6096ed82 100644 --- a/web-common/src/features/dashboards/stores/persistent-dashboard-state.ts +++ b/web-common/src/features/dashboards/stores/persistent-dashboard-state.ts @@ -3,7 +3,7 @@ import type { SortType, } from "@rilldata/web-common/features/dashboards/proto-state/derived-types"; import { localStorageStore } from "@rilldata/web-common/lib/store-utils"; -import { get, type Readable, type Updater } from "svelte/store"; +import { type Readable, type Updater } from "svelte/store"; /** * Partial state of the dashboard that is stored in local storage. @@ -54,7 +54,7 @@ export type PersistentDashboardStore = Readable & ReturnType; export function createPersistentDashboardStore(storeKey: string) { const { subscribe, update } = localStorageStore( - `${storeKey}-persistentDashboardStore`, + `${storeKey.toLowerCase()}-persistentDashboardStore`, {}, ); return { @@ -63,22 +63,12 @@ export function createPersistentDashboardStore(storeKey: string) { }; } -// TODO: once we move everything to state-managers we wont need this -let persistentDashboardStore: PersistentDashboardStore; -export function initPersistentDashboardStore(storeKey: string) { - persistentDashboardStore = createPersistentDashboardStore(storeKey); -} - -export function getPersistentDashboardStore() { - return persistentDashboardStore; -} - -export function getPersistentDashboardState(): PersistentDashboardState { - if (!persistentDashboardStore) return {}; - return get(persistentDashboardStore); -} - -export function hasPersistentDashboardData() { - if (!persistentDashboardStore) return false; - return Object.keys(get(persistentDashboardStore)).length > 0; +export function getPersistentDashboardStateForKey( + key: string, +): PersistentDashboardState | undefined { + const dataRaw = localStorage.getItem( + `${key.toLowerCase()}-persistentDashboardStore`, + ); + if (!dataRaw) return undefined; + return JSON.parse(dataRaw) as PersistentDashboardState; } diff --git a/web-common/src/features/dashboards/stores/syncDashboardState.ts b/web-common/src/features/dashboards/stores/syncDashboardState.ts deleted file mode 100644 index b2febc4adc9..00000000000 --- a/web-common/src/features/dashboards/stores/syncDashboardState.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { page } from "$app/stores"; -import type { CompoundQueryResult } from "@rilldata/web-common/features/compound-query-result"; -import { - createMetricsViewSchema, - createTimeRangeSummary, -} from "@rilldata/web-common/features/dashboards/selectors/index"; -import type { StateManagers } from "@rilldata/web-common/features/dashboards/state-managers/state-managers"; -import { metricsExplorerStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; -import { derived, get } from "svelte/store"; - -/** - * createDashboardStateSync creates a store that keeps the dashboard state in sync with metrics config. - * It derives from metrics view spec, time range summary and metrics view schema. - * - * For the 1st time it is run it will call `metricsExplorerStore.init` to initialise the dashboard store. - * Optionally loads an initial url state. - * - * For successive runs it will call `metricsExplorerStore.sync` to keep the store in sync with metrics config. - * `sync` will make sure any removed measures and dimensions are not selected in anything in the dashboard. - * - * Note that this returns a readable so that the body of the `subscribe` is executed. - * - * @param ctx - * @param initialUrlStateStore Initial url state to load when the dashboard store is initialised for the 1st time. - * @returns A boolean readable that is true once the dashbaord store is created. - */ -export function createDashboardStateSync( - ctx: StateManagers, - initialUrlStateStore?: CompoundQueryResult, -) { - return derived( - [ - ctx.validSpecStore, - createTimeRangeSummary(ctx), - createMetricsViewSchema(ctx), - ...(initialUrlStateStore ? [initialUrlStateStore] : []), - ], - ([ - validSpecRes, - timeRangeRes, - metricsViewSchemaRes, - initialUrlStateRes, - ]) => { - if ( - // still fetching - validSpecRes.isFetching || - timeRangeRes.isFetching || - metricsViewSchemaRes.isFetching || - initialUrlStateRes?.isFetching - ) { - return { isFetching: true, error: false }; - } - - if ( - !validSpecRes.data?.metricsView || - (!!validSpecRes.data.metricsView?.timeDimension && - !timeRangeRes.data) || - !validSpecRes.data?.explore || - !metricsViewSchemaRes.data?.schema - ) { - return { isFetching: false, error: true }; - } - - const { metricsView, explore } = validSpecRes.data; - - const exploreName = get(ctx.exploreName); - if (exploreName in get(metricsExplorerStore).entities) { - // Successive syncs with metrics view spec - // metricsExplorerStore.sync(exploreName, explore); - } else { - // Running for the 1st time. Initialise the dashboard store. - metricsExplorerStore.init( - exploreName, - metricsView, - explore, - timeRangeRes.data, - ); - const initialUrlState = - get(page).url.searchParams.get("state") ?? initialUrlStateRes?.data; - if (initialUrlState) { - // If there is data to be loaded, load it during the init - // metricsExplorerStore.syncFromUrl( - // exploreName, - // initialUrlState, - // metricsView, - // explore, - // metricsViewSchemaRes.data.schema, - // ); - // Call sync to make sure changes in dashboard are honoured - metricsExplorerStore.sync(exploreName, explore); - } - } - return { isFetching: false, error: false }; - }, - ); -} diff --git a/web-common/src/features/dashboards/stores/test-data/helpers.ts b/web-common/src/features/dashboards/stores/test-data/helpers.ts index d382dcca79a..b7fab18b601 100644 --- a/web-common/src/features/dashboards/stores/test-data/helpers.ts +++ b/web-common/src/features/dashboards/stores/test-data/helpers.ts @@ -1,6 +1,5 @@ import { QueryClient } from "@rilldata/svelte-query"; import { createStateManagers } from "@rilldata/web-common/features/dashboards/state-managers/state-managers"; -import { getDefaultExploreState } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; import { metricsExplorerStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores"; import { createAndExpression } from "@rilldata/web-common/features/dashboards/stores/filter-utils"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; @@ -15,12 +14,15 @@ import { AD_BIDS_SCHEMA, AD_BIDS_TIME_RANGE_SUMMARY, } from "@rilldata/web-common/features/dashboards/stores/test-data/data"; +import { convertPresetToExploreState } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToExploreState"; +import { getDefaultExplorePreset } from "@rilldata/web-common/features/dashboards/url-state/getDefaultExplorePreset"; import type { ExploreValidSpecResponse } from "@rilldata/web-common/features/explores/selectors"; import type { DashboardTimeControls } from "@rilldata/web-common/lib/time/types"; import { type V1ExploreSpec, type V1Expression, type V1MetricsViewSpec, + type V1MetricsViewTimeRangeResponse, } from "@rilldata/web-common/runtime-client"; import { deepClone } from "@vitest/utils"; import { get } from "svelte/store"; @@ -36,32 +38,40 @@ export function resetDashboardStore() { export function initAdBidsInStore() { metricsExplorerStore.init( AD_BIDS_EXPLORE_NAME, - AD_BIDS_METRICS_INIT, - AD_BIDS_EXPLORE_INIT, - AD_BIDS_TIME_RANGE_SUMMARY, + getInitExploreStateForTest( + AD_BIDS_METRICS_INIT, + AD_BIDS_EXPLORE_INIT, + AD_BIDS_TIME_RANGE_SUMMARY, + ), ); } export function initAdBidsMirrorInStore() { metricsExplorerStore.init( AD_BIDS_MIRROR_NAME, - AD_BIDS_METRICS_INIT, - AD_BIDS_EXPLORE_INIT, - AD_BIDS_TIME_RANGE_SUMMARY, + getInitExploreStateForTest( + AD_BIDS_METRICS_INIT, + AD_BIDS_EXPLORE_INIT, + AD_BIDS_TIME_RANGE_SUMMARY, + ), ); } -export function createDashboardState( - name: string, - metrics: V1MetricsViewSpec, - explore: V1ExploreSpec, - whereFilter: V1Expression = createAndExpression([]), - timeRange: DashboardTimeControls = AD_BIDS_DEFAULT_TIME_RANGE, -): MetricsExplorerEntity { - const explorer = getDefaultExploreState(name, metrics, explore, undefined); - explorer.whereFilter = whereFilter; - explorer.selectedTimeRange = timeRange; - return explorer; +export function getInitExploreStateForTest( + metricsViewSpec: V1MetricsViewSpec, + exploreSpec: V1ExploreSpec, + timeRangeSummary: V1MetricsViewTimeRangeResponse | undefined = undefined, +) { + const defaultExplorePreset = getDefaultExplorePreset( + exploreSpec, + timeRangeSummary, + ); + const { partialExploreState } = convertPresetToExploreState( + metricsViewSpec, + exploreSpec, + defaultExplorePreset, + ); + return partialExploreState; } export function createAdBidsMirrorInStore({ diff --git a/web-common/src/features/dashboards/tab-bar/TabBar.svelte b/web-common/src/features/dashboards/tab-bar/TabBar.svelte index be5314c0ba1..97dd959a045 100644 --- a/web-common/src/features/dashboards/tab-bar/TabBar.svelte +++ b/web-common/src/features/dashboards/tab-bar/TabBar.svelte @@ -2,6 +2,7 @@ import Chart from "@rilldata/web-common/components/icons/Chart.svelte"; import Pivot from "@rilldata/web-common/components/icons/Pivot.svelte"; import Tag from "@rilldata/web-common/components/tag/Tag.svelte"; + import { ExploreStateURLParams } from "@rilldata/web-common/features/dashboards/url-state/url-params"; import { behaviourEvent } from "@rilldata/web-common/metrics/initMetrics"; import { BehaviourEventMedium } from "@rilldata/web-common/metrics/service/BehaviourEventTypes"; import { @@ -58,7 +59,7 @@ {@const selected = tab === currentTab} handleTabChange(tab)} > diff --git a/web-common/src/features/dashboards/time-series/BackToExplore.svelte b/web-common/src/features/dashboards/time-series/BackToExplore.svelte index 7a0c7c55e5e..f0e04ac70b9 100644 --- a/web-common/src/features/dashboards/time-series/BackToExplore.svelte +++ b/web-common/src/features/dashboards/time-series/BackToExplore.svelte @@ -1,21 +1,10 @@ - +