diff --git a/admin/database/postgres/migrations/0045.sql b/admin/database/postgres/migrations/0045.sql
index 96af8687ac1..0692eda768d 100644
--- a/admin/database/postgres/migrations/0045.sql
+++ b/admin/database/postgres/migrations/0045.sql
@@ -9,4 +9,4 @@ CREATE TABLE billing_issues (
created_on TIMESTAMPTZ NOT NULL DEFAULT now(),
FOREIGN KEY (org_id) REFERENCES orgs (id) ON DELETE CASCADE,
CONSTRAINT billing_issues_org_id_type_unique UNIQUE (org_id, type)
-);
+);
\ No newline at end of file
diff --git a/admin/database/postgres/migrations/0046.sql b/admin/database/postgres/migrations/0046.sql
index a99789779c7..28dc2d34d97 100644
--- a/admin/database/postgres/migrations/0046.sql
+++ b/admin/database/postgres/migrations/0046.sql
@@ -1 +1 @@
-ALTER TABLE magic_auth_tokens ADD COLUMN title TEXT NOT NULL DEFAULT '';
\ No newline at end of file
+ALTER TABLE magic_auth_tokens ADD COLUMN title TEXT NOT NULL DEFAULT '';
diff --git a/admin/database/postgres/postgres.go b/admin/database/postgres/postgres.go
index be436647727..d8983eadf23 100644
--- a/admin/database/postgres/postgres.go
+++ b/admin/database/postgres/postgres.go
@@ -2163,8 +2163,8 @@ func (c *connection) decryptMap(m map[string]string, encKeyID string) (map[strin
// magicAuthTokenDTO wraps database.MagicAuthToken, using the pgtype package to handly types that pgx can't read directly into their native Go types.
type magicAuthTokenDTO struct {
*database.MagicAuthToken
- Attributes pgtype.JSON `db:"attributes"`
- MetricsViewFields pgtype.TextArray `db:"metrics_view_fields"`
+ Attributes pgtype.JSON `db:"attributes"`
+ Fields pgtype.TextArray `db:"fields"`
}
func (c *connection) magicAuthTokenFromDTO(dto *magicAuthTokenDTO, fetchSecret bool) (*database.MagicAuthToken, error) {
@@ -2172,7 +2172,7 @@ func (c *connection) magicAuthTokenFromDTO(dto *magicAuthTokenDTO, fetchSecret b
if err != nil {
return nil, err
}
- err = dto.MetricsViewFields.AssignTo(&dto.MagicAuthToken.Fields)
+ err = dto.Fields.AssignTo(&dto.MagicAuthToken.Fields)
if err != nil {
return nil, err
}
@@ -2193,8 +2193,8 @@ func (c *connection) magicAuthTokenFromDTO(dto *magicAuthTokenDTO, fetchSecret b
// magicAuthTokenWithUserDTO wraps database.MagicAuthTokenWithUser, using the pgtype package to handly types that pgx can't read directly into their native Go types.
type magicAuthTokenWithUserDTO struct {
*database.MagicAuthTokenWithUser
- Attributes pgtype.JSON `db:"attributes"`
- MetricsViewFields pgtype.TextArray `db:"metrics_view_fields"`
+ Attributes pgtype.JSON `db:"attributes"`
+ Fields pgtype.TextArray `db:"fields"`
}
func (c *connection) magicAuthTokenWithUserFromDTO(dto *magicAuthTokenWithUserDTO) (*database.MagicAuthTokenWithUser, error) {
@@ -2202,7 +2202,7 @@ func (c *connection) magicAuthTokenWithUserFromDTO(dto *magicAuthTokenWithUserDT
if err != nil {
return nil, err
}
- err = dto.MetricsViewFields.AssignTo(&dto.MagicAuthToken.Fields)
+ err = dto.Fields.AssignTo(&dto.MagicAuthToken.Fields)
if err != nil {
return nil, err
}
diff --git a/scripts/tsc-with-whitelist.sh b/scripts/tsc-with-whitelist.sh
index c085fed1fb8..e3b6a22c69b 100755
--- a/scripts/tsc-with-whitelist.sh
+++ b/scripts/tsc-with-whitelist.sh
@@ -17,8 +17,9 @@ web-admin/src/features/scheduled-reports/selectors.ts: error TS2345
web-admin/src/features/view-as-user/clearViewedAsUser.ts: error TS18047
web-admin/src/features/view-as-user/clearViewedAsUser.ts: error TS2322
web-admin/src/features/view-as-user/setViewedAsUser.ts: error TS2322
-web-admin/src/routes/[organization]/[project]/-/dashboards/+page.ts: error TS2307
web-admin/src/features/projects/github/GithubData.ts: error TS2769
+web-admin/src/routes/[organization]/[project]/[dashboard]/+page.ts: error TS2307
+web-admin/src/routes/[organization]/[project]/-/dashboards/+page.ts: error TS2307
web-common/src/components/button-group/ButtonGroup.spec.ts: error TS2345
web-common/src/components/data-graphic/actions/mouse-position-to-domain-action-factory.ts: error TS2322
web-common/src/components/data-graphic/actions/outline.ts: error TS18047
@@ -97,7 +98,7 @@ web-common/src/features/metrics-views/errors.ts: error TS2322
web-common/src/features/metrics-views/errors.ts: error TS2345
web-common/src/features/metrics-views/metrics-internal-store.ts: error TS18048
web-common/src/features/metrics-views/metrics-internal-store.ts: error TS2345
-web-common/src/features/metrics-views/workspace/editor/create-placeholder.ts: error TS2322
+web-common/src/features/metrics-views/editor/create-placeholder.ts: error TS2322
web-common/src/features/models/selectors.ts: error TS18048
web-common/src/features/models/selectors.ts: error TS2345
web-common/src/features/models/utils/embedded.ts: error TS18048
@@ -142,6 +143,7 @@ web-common/src/runtime-client/invalidation.ts: error TS18048
web-common/src/runtime-client/invalidation.ts: error TS2345
web-common/src/runtime-client/watch-request-client.ts: error TS2322
web-common/vite.config.ts: error TS2339
+web-local/src/routes//dashboard/[name]/+page.ts: error TS2307
"
# Run TypeScript compiler and find all distinct error per file
diff --git a/web-admin/src/features/alerts/CreateAlert.svelte b/web-admin/src/features/alerts/CreateAlert.svelte
index 299fabdfb24..3d428cf33bb 100644
--- a/web-admin/src/features/alerts/CreateAlert.svelte
+++ b/web-admin/src/features/alerts/CreateAlert.svelte
@@ -1,16 +1,16 @@
@@ -113,7 +114,7 @@
-
+
diff --git a/web-admin/src/features/bookmarks/BookmarkFiltersFormSection.svelte b/web-admin/src/features/bookmarks/BookmarkFiltersFormSection.svelte
index f8e7ec98d12..f995128825a 100644
--- a/web-admin/src/features/bookmarks/BookmarkFiltersFormSection.svelte
+++ b/web-admin/src/features/bookmarks/BookmarkFiltersFormSection.svelte
@@ -1,18 +1,18 @@
@@ -22,9 +22,9 @@
title="Filters"
>
diff --git a/web-admin/src/features/bookmarks/BookmarkTimeRangeSwitch.svelte b/web-admin/src/features/bookmarks/BookmarkTimeRangeSwitch.svelte
index bb755d86a04..86d2686f49e 100644
--- a/web-admin/src/features/bookmarks/BookmarkTimeRangeSwitch.svelte
+++ b/web-admin/src/features/bookmarks/BookmarkTimeRangeSwitch.svelte
@@ -6,6 +6,7 @@
import { runtime } from "@rilldata/web-common/runtime-client/runtime-store.js";
export let metricsViewName: string;
+ export let exploreName: string;
export let checked: boolean;
const queryClient = useQueryClient();
@@ -13,6 +14,7 @@
queryClient,
$runtime?.instanceId,
metricsViewName,
+ exploreName,
);
diff --git a/web-admin/src/features/bookmarks/Bookmarks.svelte b/web-admin/src/features/bookmarks/Bookmarks.svelte
index bf12d462ad5..37b78beded2 100644
--- a/web-admin/src/features/bookmarks/Bookmarks.svelte
+++ b/web-admin/src/features/bookmarks/Bookmarks.svelte
@@ -4,40 +4,42 @@
createAdminServiceRemoveBookmark,
getAdminServiceListBookmarksQueryKey,
} from "@rilldata/web-admin/client";
+ import BookmarkDialog from "@rilldata/web-admin/features/bookmarks/BookmarkDialog.svelte";
+ import BookmarksContent from "@rilldata/web-admin/features/bookmarks/BookmarksDropdownMenuContent.svelte";
+ import { createBookmarkApplier } from "@rilldata/web-admin/features/bookmarks/applyBookmark";
+ import { createHomeBookmarkModifier } from "@rilldata/web-admin/features/bookmarks/createOrUpdateHomeBookmark";
+ import { getBookmarkDataForDashboard } from "@rilldata/web-admin/features/bookmarks/getBookmarkDataForDashboard";
+ import type { BookmarkEntry } from "@rilldata/web-admin/features/bookmarks/selectors";
import { useProjectId } from "@rilldata/web-admin/features/projects/selectors";
import { Button } from "@rilldata/web-common/components/button";
import {
DropdownMenu,
DropdownMenuTrigger,
} from "@rilldata/web-common/components/dropdown-menu";
- import { createBookmarkApplier } from "@rilldata/web-admin/features/bookmarks/applyBookmark";
- import BookmarksContent from "@rilldata/web-admin/features/bookmarks/BookmarksDropdownMenuContent.svelte";
- import BookmarkDialog from "@rilldata/web-admin/features/bookmarks/BookmarkDialog.svelte";
- import { createHomeBookmarkModifier } from "@rilldata/web-admin/features/bookmarks/createOrUpdateHomeBookmark";
- import { getBookmarkDataForDashboard } from "@rilldata/web-admin/features/bookmarks/getBookmarkDataForDashboard";
- import type { BookmarkEntry } from "@rilldata/web-admin/features/bookmarks/selectors";
- import { useDashboardStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores";
+ import { useExploreStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores";
import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors";
+ import { eventBus } from "@rilldata/web-common/lib/event-bus/event-bus";
import { runtime } from "@rilldata/web-common/runtime-client/runtime-store";
import { useQueryClient } from "@tanstack/svelte-query";
import { BookmarkIcon } from "lucide-svelte";
- import { eventBus } from "@rilldata/web-common/lib/event-bus/event-bus";
export let metricsViewName: string;
+ export let exploreName: string;
let showDialog = false;
let bookmark: BookmarkEntry | null = null;
- $: bookmarkApplier = createBookmarkApplier(
- $runtime?.instanceId,
- metricsViewName,
- );
+ $: bookmarkApplier = createBookmarkApplier($runtime?.instanceId, exploreName);
- $: dashboardStore = useDashboardStore(metricsViewName);
+ $: exploreStore = useExploreStore(exploreName);
$: projectId = useProjectId($page.params.organization, $page.params.project);
const queryClient = useQueryClient();
- $: homeBookmarkModifier = createHomeBookmarkModifier($runtime?.instanceId);
+ $: homeBookmarkModifier = createHomeBookmarkModifier(
+ $runtime?.instanceId,
+ metricsViewName,
+ exploreName,
+ );
const bookmarkDeleter = createAdminServiceRemoveBookmark();
function selectBookmark(bookmark: BookmarkEntry) {
@@ -45,15 +47,15 @@
}
async function createHomeBookmark() {
- await homeBookmarkModifier(getBookmarkDataForDashboard($dashboardStore));
+ await homeBookmarkModifier(getBookmarkDataForDashboard($exploreStore));
eventBus.emit("notification", {
message: "Home bookmark created",
});
return queryClient.refetchQueries(
getAdminServiceListBookmarksQueryKey({
projectId: $projectId.data ?? "",
- resourceKind: ResourceKind.MetricsView,
- resourceName: metricsViewName,
+ resourceKind: ResourceKind.Explore,
+ resourceName: exploreName,
}),
);
}
@@ -98,6 +100,7 @@
}}
on:select={({ detail }) => selectBookmark(detail)}
{metricsViewName}
+ {exploreName}
/>
@@ -105,6 +108,7 @@
{
showDialog = false;
bookmark = null;
diff --git a/web-admin/src/features/bookmarks/BookmarksDropdownMenuContent.svelte b/web-admin/src/features/bookmarks/BookmarksDropdownMenuContent.svelte
index 1959e67d9c3..ffad6412388 100644
--- a/web-admin/src/features/bookmarks/BookmarksDropdownMenuContent.svelte
+++ b/web-admin/src/features/bookmarks/BookmarksDropdownMenuContent.svelte
@@ -21,6 +21,7 @@
import HomeBookmarkPlus from "@rilldata/web-common/components/icons/HomeBookmarkPlus.svelte";
export let metricsViewName: string;
+ export let exploreName: string;
const dispatch = createEventDispatcher();
const queryClient = useQueryClient();
@@ -36,6 +37,7 @@
organization,
project,
metricsViewName,
+ exploreName,
);
$: filteredBookmarks = searchBookmarks($bookmarks.data, searchText);
diff --git a/web-admin/src/features/bookmarks/applyBookmark.ts b/web-admin/src/features/bookmarks/applyBookmark.ts
index de23149485b..4f6cca66162 100644
--- a/web-admin/src/features/bookmarks/applyBookmark.ts
+++ b/web-admin/src/features/bookmarks/applyBookmark.ts
@@ -1,33 +1,32 @@
import type { V1Bookmark } from "@rilldata/web-admin/client";
-import { useMetricsView } from "@rilldata/web-common/features/dashboards/selectors";
import { metricsExplorerStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores";
+import { useExploreValidSpec } from "@rilldata/web-common/features/explores/selectors";
import { createQueryServiceMetricsViewSchema } from "@rilldata/web-common/runtime-client";
import { get } from "svelte/store";
-export function createBookmarkApplier(
- instanceId: string,
- metricsViewName: string,
-) {
- const metricsViewSpec = useMetricsView(instanceId, metricsViewName);
+export function createBookmarkApplier(instanceId: string, exploreName: string) {
+ const validExploreSpec = useExploreValidSpec(instanceId, exploreName);
const metricsSchema = createQueryServiceMetricsViewSchema(
instanceId,
- metricsViewName,
+ exploreName,
);
return (bookmark: V1Bookmark) => {
- const metricsViewSpecResp = get(metricsViewSpec);
+ const validExploreSpecResp = get(validExploreSpec);
const metricsSchemaResp = get(metricsSchema);
if (
!bookmark.data ||
- !metricsViewSpecResp.data ||
+ !validExploreSpecResp.data?.metricsView ||
+ !validExploreSpecResp.data?.explore ||
!metricsSchemaResp.data?.schema
) {
return;
}
metricsExplorerStore.syncFromUrl(
- metricsViewName,
+ exploreName,
decodeURIComponent(bookmark.data),
- metricsViewSpecResp.data,
+ validExploreSpecResp.data.metricsView,
+ validExploreSpecResp.data.explore,
metricsSchemaResp.data.schema,
);
};
diff --git a/web-admin/src/features/bookmarks/createOrUpdateHomeBookmark.ts b/web-admin/src/features/bookmarks/createOrUpdateHomeBookmark.ts
index 19b9842a4f6..8b6f1a6968c 100644
--- a/web-admin/src/features/bookmarks/createOrUpdateHomeBookmark.ts
+++ b/web-admin/src/features/bookmarks/createOrUpdateHomeBookmark.ts
@@ -9,7 +9,11 @@ import { ResourceKind } from "@rilldata/web-common/features/entity-management/re
import { useQueryClient } from "@tanstack/svelte-query";
import { get } from "svelte/store";
-export function createHomeBookmarkModifier(instanceId: string) {
+export function createHomeBookmarkModifier(
+ instanceId: string,
+ metricsViewName: string,
+ exploreName: string,
+) {
const bookmarkCreator = createAdminServiceCreateBookmark();
const bookmarkUpdater = createAdminServiceUpdateBookmark();
const projectIdRes = useProjectId(
@@ -21,7 +25,8 @@ export function createHomeBookmarkModifier(instanceId: string) {
instanceId,
get(page).params.organization,
get(page).params.project,
- get(page).params.dashboard,
+ metricsViewName,
+ exploreName,
);
return (data: string) => {
@@ -48,8 +53,8 @@ export function createHomeBookmarkModifier(instanceId: string) {
displayName: "Home",
description: "Main view For this dashboard",
projectId: projectId.data,
- resourceKind: ResourceKind.MetricsView,
- resourceName: get(page).params.dashboard,
+ resourceKind: ResourceKind.Explore,
+ resourceName: exploreName,
shared: true,
default: true,
data,
diff --git a/web-admin/src/features/bookmarks/selectors.ts b/web-admin/src/features/bookmarks/selectors.ts
index f0d372311b2..731bb7f65b9 100644
--- a/web-admin/src/features/bookmarks/selectors.ts
+++ b/web-admin/src/features/bookmarks/selectors.ts
@@ -6,17 +6,16 @@ import {
import { useProjectId } from "@rilldata/web-admin/features/projects/selectors";
import type { CompoundQueryResult } from "@rilldata/web-common/features/compound-query-result";
import { getDashboardStateFromUrl } from "@rilldata/web-common/features/dashboards/proto-state/fromProto";
-import {
- useMetricsView,
- useMetricsViewTimeRange,
-} from "@rilldata/web-common/features/dashboards/selectors";
-import { useDashboardStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores";
+import { useMetricsViewTimeRange } from "@rilldata/web-common/features/dashboards/selectors";
+import { useExploreStore } from "@rilldata/web-common/features/dashboards/stores/dashboard-stores";
import { timeControlStateSelector } from "@rilldata/web-common/features/dashboards/time-controls/time-control-store";
import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors";
+import { useExploreValidSpec } from "@rilldata/web-common/features/explores/selectors";
import { prettyFormatTimeRange } from "@rilldata/web-common/lib/time/ranges";
import { TimeRangePreset } from "@rilldata/web-common/lib/time/types";
import {
createQueryServiceMetricsViewSchema,
+ type V1ExploreSpec,
type V1MetricsViewSpec,
type V1StructType,
} from "@rilldata/web-common/runtime-client";
@@ -41,27 +40,29 @@ export function getBookmarks(
orgName: string,
projectName: string,
metricsViewName: string,
+ exploreName: string,
): CreateQueryResult {
return derived(
[
useProjectId(orgName, projectName),
- useMetricsView(instanceId, metricsViewName),
+ useExploreValidSpec(instanceId, exploreName),
createQueryServiceMetricsViewSchema(instanceId, metricsViewName),
createAdminServiceGetCurrentUser(),
],
- ([projectId, metricsViewResp, schemaResp, userResp], set) =>
+ ([projectId, validSpec, schemaResp, userResp], set) =>
createAdminServiceListBookmarks(
{
projectId: projectId.data,
- resourceKind: ResourceKind.MetricsView,
- resourceName: metricsViewName,
+ resourceKind: ResourceKind.Explore,
+ resourceName: exploreName,
},
{
query: {
enabled:
!!projectId?.data &&
!!metricsViewName &&
- !metricsViewResp.isFetching &&
+ !!exploreName &&
+ !validSpec.isFetching &&
!schemaResp.isFetching &&
userResp.isSuccess &&
!!userResp.data.user,
@@ -74,8 +75,9 @@ export function getBookmarks(
resp.bookmarks?.forEach((bookmarkResource) => {
const bookmark = parseBookmarkEntry(
bookmarkResource,
- metricsViewResp.data as V1MetricsViewSpec,
- schemaResp.data?.schema as V1StructType,
+ validSpec.data?.metricsView ?? {},
+ validSpec.data?.explore ?? {},
+ schemaResp.data?.schema ?? {},
);
if (bookmarkResource.default) {
bookmarks.home = bookmark;
@@ -116,6 +118,7 @@ export function getHomeBookmarkData(
orgName: string,
projectName: string,
metricsViewName: string,
+ exploreName: string,
): CompoundQueryResult {
return derived(
getBookmarks(
@@ -124,6 +127,7 @@ export function getHomeBookmarkData(
orgName,
projectName,
metricsViewName,
+ exploreName,
),
(bookmarks) => {
if (bookmarks.isFetching || !bookmarks.data) {
@@ -150,18 +154,20 @@ export function getPrettySelectedTimeRange(
queryClient: QueryClient,
instanceId: string,
metricsViewName: string,
+ exploreName: string,
): Readable {
return derived(
[
- useMetricsView(instanceId, metricsViewName),
+ useExploreValidSpec(instanceId, exploreName),
useMetricsViewTimeRange(instanceId, metricsViewName, {
query: { queryClient },
}),
- useDashboardStore(metricsViewName),
+ useExploreStore(metricsViewName),
],
- ([metricViewSpec, timeRangeSummary, metricsExplorerEntity]) => {
+ ([validSpec, timeRangeSummary, metricsExplorerEntity]) => {
const timeRangeState = timeControlStateSelector([
- metricViewSpec,
+ validSpec.data?.metricsView ?? {},
+ validSpec.data?.explore ?? {},
timeRangeSummary,
metricsExplorerEntity,
]);
@@ -179,11 +185,13 @@ export function getPrettySelectedTimeRange(
function parseBookmarkEntry(
bookmarkResource: V1Bookmark,
metricsViewSpec: V1MetricsViewSpec,
+ exploreSpec: V1ExploreSpec,
schema: V1StructType,
): BookmarkEntry {
const metricsEntity = getDashboardStateFromUrl(
bookmarkResource.data ?? "",
metricsViewSpec,
+ exploreSpec,
schema,
);
return {
diff --git a/web-admin/src/features/dashboards/DashboardBookmarksStateProvider.svelte b/web-admin/src/features/dashboards/DashboardBookmarksStateProvider.svelte
index e6670b124ae..b41c995f27e 100644
--- a/web-admin/src/features/dashboards/DashboardBookmarksStateProvider.svelte
+++ b/web-admin/src/features/dashboards/DashboardBookmarksStateProvider.svelte
@@ -5,20 +5,22 @@
import { getStateManagers } from "@rilldata/web-common/features/dashboards/state-managers/state-managers";
import { createDashboardStateSync } from "@rilldata/web-common/features/dashboards/stores/syncDashboardState";
import { initLocalUserPreferenceStore } from "@rilldata/web-common/features/dashboards/user-preferences";
- import { runtime } from "@rilldata/web-common/runtime-client/runtime-store";
import Spinner from "@rilldata/web-common/features/entity-management/Spinner.svelte";
import { EntityStatus } from "@rilldata/web-common/features/entity-management/types";
+ import { runtime } from "@rilldata/web-common/runtime-client/runtime-store";
- export let metricViewName: string;
+ export let metricsViewName: string;
+ export let exploreName: string;
- $: initLocalUserPreferenceStore(metricViewName);
+ $: initLocalUserPreferenceStore(exploreName);
const queryClient = useQueryClient();
const homeBookmark = getHomeBookmarkData(
queryClient,
$runtime?.instanceId,
$page.params.organization,
$page.params.project,
- metricViewName,
+ metricsViewName,
+ exploreName,
);
$: dashboardStoreReady = createDashboardStateSync(
diff --git a/web-admin/src/features/dashboards/listing/DashboardsTable.svelte b/web-admin/src/features/dashboards/listing/DashboardsTable.svelte
index bbd8bbc98dc..bb8c033059c 100644
--- a/web-admin/src/features/dashboards/listing/DashboardsTable.svelte
+++ b/web-admin/src/features/dashboards/listing/DashboardsTable.svelte
@@ -1,7 +1,7 @@
@@ -36,7 +36,7 @@
>
{#if isMetricsExplorer}
-
+
{:else}
{/if}
diff --git a/web-admin/src/features/dashboards/listing/DashboardsTableHeader.svelte b/web-admin/src/features/dashboards/listing/DashboardsTableHeader.svelte
index 7a40769f088..aca8a0b73ff 100644
--- a/web-admin/src/features/dashboards/listing/DashboardsTableHeader.svelte
+++ b/web-admin/src/features/dashboards/listing/DashboardsTableHeader.svelte
@@ -1,5 +1,5 @@
{
@@ -47,19 +48,36 @@ export interface DashboardResource {
function getDashboardRefreshedOn(
dashboard: V1Resource,
- allResources: V1Resource[],
+ allResources: Map
,
): string | undefined {
if (!dashboard) return undefined;
- const refName = dashboard.meta.refs[0];
- const refTable = allResources.find(
- (r) => r.meta?.name?.name === refName?.name,
+ const metricsViewRefName = dashboard.meta.refs[0];
+ const refTable = allResources.get(
+ `${metricsViewRefName?.kind}_${metricsViewRefName?.name}`,
);
return (
refTable?.model?.state.refreshedOn || refTable?.source?.state.refreshedOn
);
}
+function getExploreRefreshedOn(
+ explore: V1Resource,
+ allResources: Map,
+): string | undefined {
+ if (!explore) return undefined;
+
+ // 1st get the metrics view for the explore
+ const exploreRefName = explore.meta.refs[0];
+ const metricsView = allResources.get(
+ `${exploreRefName?.kind}_${exploreRefName?.name}`,
+ );
+ if (!metricsView) return undefined;
+
+ // next get the referenced table resource
+ return getDashboardRefreshedOn(metricsView, allResources);
+}
+
// This iteration of `useDashboards` returns the above `DashboardResource` type, which includes `refreshedOn`
export function useDashboardsV2(
instanceId: string,
@@ -67,15 +85,31 @@ export function useDashboardsV2(
return createRuntimeServiceListResources(instanceId, undefined, {
query: {
select: (data) => {
- // Filter for Metrics Explorers and Custom Dashboards
- const resources = data.resources.filter(
- (res) => res.metricsView || res.canvas,
+ // create a map since we are potentially looking up twice per explore
+ const allResources = getMapFromArray(
+ data.resources,
+ (r) => `${r.meta.name.kind}_${r.meta.name.name}`,
);
- // Add `refreshedOn` to each resource
- return resources.map((resource) => {
- const refreshedOn = getDashboardRefreshedOn(resource, data.resources);
- return { resource, refreshedOn };
- });
+ const allDashboards: DashboardResource[] = [];
+ // filter canvas dashboards
+ const canvasDashboards = data.resources.filter((res) => res.canvas);
+ allDashboards.push(
+ ...canvasDashboards.map((resource) => {
+ // Add `refreshedOn` to each resource
+ const refreshedOn = getDashboardRefreshedOn(resource, allResources);
+ return { resource, refreshedOn };
+ }),
+ );
+ // filter explores
+ const explores = data.resources.filter((res) => res.explore);
+ allDashboards.push(
+ ...explores.map((resource) => {
+ // Add `refreshedOn` to each resource
+ const refreshedOn = getExploreRefreshedOn(resource, allResources);
+ return { resource, refreshedOn };
+ }),
+ );
+ return allDashboards;
},
},
});
@@ -92,11 +126,22 @@ export function useDashboardV2(
select: (data) => {
if (!name) return;
- const dashboard = data.resources.find(
+ const resource = data.resources.find(
(res) => res.meta.name.name.toLowerCase() === name.toLowerCase(),
);
- const refreshedOn = getDashboardRefreshedOn(dashboard, data.resources);
- return { resource: dashboard, refreshedOn };
+ // create a map since we are potentially looking up twice per explore
+ const allResources = getMapFromArray(
+ data.resources,
+ (r) => `${r.meta.name.kind}_${r.meta.name.name}`,
+ );
+
+ if (resource.canvas) {
+ const refreshedOn = getDashboardRefreshedOn(resource, allResources);
+ return { resource, refreshedOn };
+ }
+
+ const refreshedOn = getExploreRefreshedOn(resource, allResources);
+ return { resource, refreshedOn };
},
},
});
diff --git a/web-admin/src/features/dashboards/query-mappers/getDashboardFromAggregationRequest.ts b/web-admin/src/features/dashboards/query-mappers/getDashboardFromAggregationRequest.ts
index e0f1d9c4352..c46174a728b 100644
--- a/web-admin/src/features/dashboards/query-mappers/getDashboardFromAggregationRequest.ts
+++ b/web-admin/src/features/dashboards/query-mappers/getDashboardFromAggregationRequest.ts
@@ -26,6 +26,7 @@ import { DashboardState_ActivePage } from "@rilldata/web-common/proto/gen/rill/u
import {
getQueryServiceMetricsViewSchemaQueryKey,
queryServiceMetricsViewSchema,
+ type V1ExploreSpec,
type V1Expression,
type V1MetricsViewAggregationRequest,
type V1MetricsViewSpec,
@@ -40,6 +41,7 @@ export async function getDashboardFromAggregationRequest({
timeRangeSummary,
executionTime,
metricsView,
+ explore,
annotations,
}: QueryMapperArgs) {
let loadedFromState = false;
@@ -49,6 +51,7 @@ export async function getDashboardFromAggregationRequest({
instanceId,
dashboard,
metricsView,
+ explore,
annotations["web_open_state"],
);
loadedFromState = true;
@@ -166,6 +169,7 @@ async function mergeDashboardFromUrlState(
instanceId: string,
dashboard: MetricsExplorerEntity,
metricsViewSpec: V1MetricsViewSpec,
+ exploreSpec: V1ExploreSpec,
urlState: string,
) {
const schemaResp = await queryClient.fetchQuery({
@@ -180,6 +184,7 @@ async function mergeDashboardFromUrlState(
const parsedDashboard = getDashboardStateFromUrl(
urlState,
metricsViewSpec,
+ exploreSpec,
schemaResp.schema,
);
for (const k in parsedDashboard) {
diff --git a/web-admin/src/features/dashboards/query-mappers/mapQueryToDashboard.ts b/web-admin/src/features/dashboards/query-mappers/mapQueryToDashboard.ts
index 16874fc7212..dc743b9f6e5 100644
--- a/web-admin/src/features/dashboards/query-mappers/mapQueryToDashboard.ts
+++ b/web-admin/src/features/dashboards/query-mappers/mapQueryToDashboard.ts
@@ -6,10 +6,10 @@ import type {
} from "@rilldata/web-admin/features/dashboards/query-mappers/types";
import type { CompoundQueryResult } from "@rilldata/web-common/features/compound-query-result";
import { getProtoFromDashboardState } from "@rilldata/web-common/features/dashboards/proto-state/toProto";
-import { useMetricsView } from "@rilldata/web-common/features/dashboards/selectors";
import { getDefaultMetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults";
import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity";
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";
import {
createQueryServiceMetricsViewTimeRange,
@@ -21,7 +21,7 @@ import { derived, get, readable } from "svelte/store";
type DashboardStateForQuery = {
state?: string;
- metricsView?: string;
+ exploreName?: string;
};
/**
@@ -29,6 +29,7 @@ type DashboardStateForQuery = {
* Used to show the relevant dashboard for a report/alert.
*/
export function mapQueryToDashboard(
+ exploreName: string,
queryName: string | undefined,
queryArgsJson: string | undefined,
executionTime: string | undefined,
@@ -76,12 +77,14 @@ export function mapQueryToDashboard(
"Failed to find metrics view name. Please check the format of the report.",
});
}
+ // backwards compatibility for older alerts created on metrics explore directly
+ if (!exploreName) exploreName = metricsViewName;
const instanceId = get(runtime).instanceId;
return derived(
[
- useMetricsView(instanceId, metricsViewName),
+ useExploreValidSpec(instanceId, exploreName),
// TODO: handle non-timestamp dashboards
createQueryServiceMetricsViewTimeRange(
get(runtime).instanceId,
@@ -89,8 +92,12 @@ export function mapQueryToDashboard(
{},
),
],
- ([metricsViewResource, timeRangeSummary], set) => {
- if (!metricsViewResource.data || !timeRangeSummary.data) {
+ ([validSpecResp, timeRangeSummary], set) => {
+ if (
+ !validSpecResp.data?.metricsView ||
+ !validSpecResp.data?.explore ||
+ !timeRangeSummary.data
+ ) {
set({
isFetching: true,
error: "",
@@ -98,21 +105,23 @@ export function mapQueryToDashboard(
return;
}
- if (metricsViewResource.error || timeRangeSummary.error) {
+ if (validSpecResp.error || timeRangeSummary.error) {
// error state
set({
isFetching: false,
error:
- metricsViewResource.error?.message ??
- timeRangeSummary.error?.message,
+ validSpecResp.error?.message ?? timeRangeSummary.error?.message,
});
return;
}
+ const { metricsView, explore } = validSpecResp.data;
+
initLocalUserPreferenceStore(metricsViewName);
const defaultDashboard = getDefaultMetricsExplorerEntity(
metricsViewName,
- metricsViewResource.data,
+ metricsView,
+ explore,
timeRangeSummary.data,
);
getDashboardState({
@@ -120,7 +129,8 @@ export function mapQueryToDashboard(
instanceId,
dashboard: defaultDashboard,
req,
- metricsView: metricsViewResource.data,
+ metricsView,
+ explore,
timeRangeSummary: timeRangeSummary.data.timeRangeSummary,
executionTime,
annotations,
@@ -131,7 +141,7 @@ export function mapQueryToDashboard(
error: "",
data: {
state: getProtoFromDashboardState(newDashboard),
- metricsView: metricsViewName,
+ exploreName,
},
});
})
diff --git a/web-admin/src/features/dashboards/query-mappers/types.ts b/web-admin/src/features/dashboards/query-mappers/types.ts
index 549c85cd662..4e1c329752b 100644
--- a/web-admin/src/features/dashboards/query-mappers/types.ts
+++ b/web-admin/src/features/dashboards/query-mappers/types.ts
@@ -1,5 +1,6 @@
import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity";
import type {
+ V1ExploreSpec,
V1MetricsViewAggregationRequest,
V1MetricsViewComparisonRequest,
V1MetricsViewRowsRequest,
@@ -23,6 +24,7 @@ export type QueryMapperArgs = {
dashboard: MetricsExplorerEntity;
req: R;
metricsView: V1MetricsViewSpec;
+ explore: V1ExploreSpec;
timeRangeSummary: V1TimeRangeSummary;
executionTime: string;
annotations: Record;
diff --git a/web-admin/src/features/dashboards/query-mappers/utils.ts b/web-admin/src/features/dashboards/query-mappers/utils.ts
index 8e497ae911b..861f607abac 100644
--- a/web-admin/src/features/dashboards/query-mappers/utils.ts
+++ b/web-admin/src/features/dashboards/query-mappers/utils.ts
@@ -201,3 +201,23 @@ export async function convertExprToToplist(
toplist.data.map((t) => t[dimensionName]),
);
}
+
+const ExploreNameRegex = /\/explore\/((?:\w|-)+)/;
+export function getExploreName(webOpenPath: string) {
+ const matches = ExploreNameRegex.exec(webOpenPath);
+ if (!matches || matches.length < 1) return "";
+ return matches[1];
+}
+
+export function getExplorePageUrl(
+ curPageUrl: URL,
+ organization: string,
+ project: string,
+ exploreName: string,
+ state: string,
+) {
+ const url = new URL(`${curPageUrl.protocol}//${curPageUrl.host}`);
+ url.pathname = `/${organization}/${project}/explore/${exploreName}`;
+ url.searchParams.set("state", state);
+ return url.toString();
+}
diff --git a/web-admin/src/features/embeds/CanvasDashboardEmbed.svelte b/web-admin/src/features/embeds/CanvasEmbed.svelte
similarity index 100%
rename from web-admin/src/features/embeds/CanvasDashboardEmbed.svelte
rename to web-admin/src/features/embeds/CanvasEmbed.svelte
diff --git a/web-admin/src/features/embeds/ExploreEmbed.svelte b/web-admin/src/features/embeds/ExploreEmbed.svelte
new file mode 100644
index 00000000000..a3a1a6d0578
--- /dev/null
+++ b/web-admin/src/features/embeds/ExploreEmbed.svelte
@@ -0,0 +1,51 @@
+
+
+{#if isSuccess}
+ {#if isExploreErrored}
+
Explore Error
+ {:else if data}
+ {#key exploreName}
+
+
+
+
+
+
+
+
+
+ {/key}
+ {/if}
+{/if}
diff --git a/web-admin/src/features/embeds/MetricsExplorerEmbed.svelte b/web-admin/src/features/embeds/MetricsExplorerEmbed.svelte
deleted file mode 100644
index 0954f5772b8..00000000000
--- a/web-admin/src/features/embeds/MetricsExplorerEmbed.svelte
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-{#if $dashboard.isSuccess}
- {#if isDashboardErrored}
-
Dashboard Error
- {:else}
- {#key dashboardName}
-
-
-
-
-
-
-
-
-
- {/key}
- {/if}
-{/if}
diff --git a/web-admin/src/features/embeds/UnsupportedKind.svelte b/web-admin/src/features/embeds/UnsupportedKind.svelte
new file mode 100644
index 00000000000..99d6dc869b1
--- /dev/null
+++ b/web-admin/src/features/embeds/UnsupportedKind.svelte
@@ -0,0 +1,7 @@
+
+
+ Embedding for this resource type is currently unavailable.
+
+
diff --git a/web-admin/src/features/navigation/TopNavigationBar.svelte b/web-admin/src/features/navigation/TopNavigationBar.svelte
index 4ba2d93ff80..674024a1742 100644
--- a/web-admin/src/features/navigation/TopNavigationBar.svelte
+++ b/web-admin/src/features/navigation/TopNavigationBar.svelte
@@ -7,8 +7,8 @@
import Breadcrumbs from "@rilldata/web-common/components/navigation/breadcrumbs/Breadcrumbs.svelte";
import type { PathOption } from "@rilldata/web-common/components/navigation/breadcrumbs/types";
import GlobalDimensionSearch from "@rilldata/web-common/features/dashboards/dimension-search/GlobalDimensionSearch.svelte";
- import { useDashboard } from "@rilldata/web-common/features/dashboards/selectors";
import StateManagersProvider from "@rilldata/web-common/features/dashboards/state-managers/StateManagersProvider.svelte";
+ import { useExplore } from "@rilldata/web-common/features/explores/selectors";
import { runtime } from "@rilldata/web-common/runtime-client/runtime-store";
import {
createAdminServiceGetCurrentUser,
@@ -25,7 +25,7 @@
import { useDashboardsV2 } from "../dashboards/listing/selectors";
import PageTitle from "../public-urls/PageTitle.svelte";
import { createAdminServiceGetMagicAuthToken } from "../public-urls/get-magic-auth-token";
- import { usePublicURLMetricsView } from "../public-urls/selectors";
+ import { usePublicURLExplore } from "../public-urls/selectors";
import { useReports } from "../scheduled-reports/selectors";
import {
isMetricsExplorerPage,
@@ -103,13 +103,13 @@
$: visualizationPaths = visualizations.reduce((map, { resource }) => {
const name = resource.meta.name.name;
- const isMetricsExplorer = !!resource?.metricsView;
+ const isMetricsExplorer = !!resource?.explore;
return map.set(name.toLowerCase(), {
label:
(isMetricsExplorer
- ? resource?.metricsView?.spec?.title
+ ? resource?.explore?.spec?.title
: resource?.canvas?.spec?.title) || name,
- section: isMetricsExplorer ? undefined : "-/dashboards",
+ section: isMetricsExplorer ? "explore" : "-/dashboards",
});
}, new Map());
@@ -136,25 +136,26 @@
report ? reportPaths : alert ? alertPaths : null,
];
- $: dashboardQuery = useDashboard(instanceId, dashboardParam, {
+ $: dashboardQuery = useExplore(instanceId, dashboardParam, {
enabled: !!instanceId && onMetricsExplorerPage,
});
- $: isDashboardValid = !!$dashboardQuery.data?.metricsView?.state?.validSpec;
+ $: exploreSpec = $dashboardQuery.data?.explore?.explore?.state?.validSpec;
+ $: isDashboardValid = !!exploreSpec;
- // Public URLs do not have the metrics view name in the URL. However, the magic token's metadata includes the metrics view name.
+ // Public URLs do not have the resource name in the URL. However, the magic token's metadata includes the resource name.
$: tokenQuery = createAdminServiceGetMagicAuthToken(token);
$: dashboard = onPublicURLPage
- ? $tokenQuery?.data?.token?.metricsView
+ ? $tokenQuery?.data?.token?.resourceName
: dashboardParam;
// If on a Public URL, get the dashboard title
- $: metricsViewQuery = usePublicURLMetricsView(
+ $: exploreQuery = usePublicURLExplore(
instanceId,
- $tokenQuery?.data?.token?.metricsView,
+ $tokenQuery?.data?.token?.resourceName,
onPublicURLPage,
);
$: publicURLDashboardTitle =
- $metricsViewQuery.data?.metricsView?.spec?.title ?? dashboard ?? "";
+ $exploreQuery.data?.explore?.spec?.title ?? dashboard ?? "";
$: currentPath = [organization, project, dashboard, report || alert];
@@ -185,17 +186,25 @@
{/if}
{#if (onMetricsExplorerPage && isDashboardValid) || onPublicURLPage}
- {#key dashboard}
-
-
-
- {#if $user.isSuccess && $user.data.user && !onPublicURLPage}
-
-
-
- {/if}
-
- {/key}
+ {#if exploreSpec}
+ {#key dashboard}
+
+
+
+ {#if $user.isSuccess && $user.data.user && !onPublicURLPage}
+
+
+
+ {/if}
+
+ {/key}
+ {/if}
{/if}
{#if $user.isSuccess}
{#if $user.data && $user.data.user}
diff --git a/web-admin/src/features/navigation/nav-utils.ts b/web-admin/src/features/navigation/nav-utils.ts
index 15dc524d067..715d8bb8b3e 100644
--- a/web-admin/src/features/navigation/nav-utils.ts
+++ b/web-admin/src/features/navigation/nav-utils.ts
@@ -27,7 +27,7 @@ export function withinProject(page: Page): boolean {
export function isMetricsExplorerPage(page: Page): boolean {
return (
- page.route.id === "/[organization]/[project]/[dashboard]" ||
+ page.route.id === "/[organization]/[project]/explore/[dashboard]" ||
page.route.id === "/-/embed"
);
}
diff --git a/web-admin/src/features/public-urls/CreatePublicURLForm.svelte b/web-admin/src/features/public-urls/CreatePublicURLForm.svelte
index 648be5f863e..507cb939648 100644
--- a/web-admin/src/features/public-urls/CreatePublicURLForm.svelte
+++ b/web-admin/src/features/public-urls/CreatePublicURLForm.svelte
@@ -4,53 +4,53 @@
createAdminServiceIssueMagicAuthToken,
getAdminServiceListMagicAuthTokensQueryKey,
} from "@rilldata/web-admin/client";
- import { Button } from "@rilldata/web-common/components/button";
+ import { Button, IconButton } from "@rilldata/web-common/components/button";
+ import Calendar from "@rilldata/web-common/components/date-picker/Calendar.svelte";
+ import Input from "@rilldata/web-common/components/forms/Input.svelte";
import Label from "@rilldata/web-common/components/forms/Label.svelte";
import Switch from "@rilldata/web-common/components/forms/Switch.svelte";
+ import { Divider } from "@rilldata/web-common/components/menu";
+ import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+ } from "@rilldata/web-common/components/popover";
import FilterChipsReadOnly from "@rilldata/web-common/features/dashboards/filters/FilterChipsReadOnly.svelte";
import { getStateManagers } from "@rilldata/web-common/features/dashboards/state-managers/state-managers";
+ import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors";
import { copyToClipboard } from "@rilldata/web-common/lib/actions/copy-to-clipboard";
import type { HTTPError } from "@rilldata/web-common/runtime-client/fetchWrapper";
+ import { useQueryClient } from "@tanstack/svelte-query";
+ import { Pencil } from "lucide-svelte";
+ import { DateTime } from "luxon";
import { defaults, superForm } from "sveltekit-superforms";
import { yup } from "sveltekit-superforms/adapters";
import { object, string } from "yup";
- import { useQueryClient } from "@tanstack/svelte-query";
import {
convertDateToMinutes,
- getMetricsViewFields,
+ getExploreFields,
getSanitizedDashboardStateParam,
hasDashboardDimensionThresholdFilter,
hasDashboardWhereFilter,
} from "./form-utils";
- import { Divider } from "@rilldata/web-common/components/menu";
- import Input from "@rilldata/web-common/components/forms/Input.svelte";
- import {
- Popover,
- PopoverContent,
- PopoverTrigger,
- } from "@rilldata/web-common/components/popover";
- import { Pencil } from "lucide-svelte";
- import { IconButton } from "@rilldata/web-common/components/button";
- import Calendar from "@rilldata/web-common/components/date-picker/Calendar.svelte";
- import { DateTime } from "luxon";
const queryClient = useQueryClient();
const StateManagers = getStateManagers();
const {
dashboardStore,
- metricsViewName,
+ exploreName,
selectors: {
measures: { visibleMeasures },
dimensions: { visibleDimensions },
},
} = StateManagers;
- $: ({ organization, project } = $page.params);
+ $: ({ organization, project, dashboard } = $page.params);
$: isTitleEmpty = $form.title.trim() === "";
- $: metricsViewFields = getMetricsViewFields(
+ $: exploreFields = getExploreFields(
$dashboardStore,
$visibleDimensions,
$visibleMeasures,
@@ -58,7 +58,7 @@
$: sanitizedState = getSanitizedDashboardStateParam(
$dashboardStore,
- metricsViewFields,
+ exploreFields,
);
let token: string;
@@ -92,11 +92,10 @@
organization,
project,
data: {
- metricsView: $metricsViewName,
- metricsViewFilter: hasWhereFilter
- ? $dashboardStore.whereFilter
- : undefined,
- metricsViewFields,
+ resourceType: ResourceKind.Explore as string,
+ resourceName: dashboard,
+ filter: hasWhereFilter ? $dashboardStore.whereFilter : undefined,
+ fields: exploreFields,
ttlMinutes: setExpiration
? convertDateToMinutes(values.expiresAt).toString()
: undefined,
@@ -232,7 +231,7 @@
data?.resource,
- enabled: !!instanceId && !!metricsViewName && enabled,
+ enabled: !!instanceId && !!exploreName && enabled,
},
},
);
diff --git a/web-admin/src/features/scheduled-reports/metadata/ReportMetadata.svelte b/web-admin/src/features/scheduled-reports/metadata/ReportMetadata.svelte
index ba703c40668..feec8947ed3 100644
--- a/web-admin/src/features/scheduled-reports/metadata/ReportMetadata.svelte
+++ b/web-admin/src/features/scheduled-reports/metadata/ReportMetadata.svelte
@@ -5,7 +5,7 @@
import IconButton from "@rilldata/web-common/components/button/IconButton.svelte";
import * as DropdownMenu from "@rilldata/web-common/components/dropdown-menu";
import ThreeDot from "@rilldata/web-common/components/icons/ThreeDot.svelte";
- import { useDashboard } from "@rilldata/web-common/features/dashboards/selectors";
+ import { useExploreValidSpec } from "@rilldata/web-common/features/explores/selectors";
import CreateScheduledReportDialog from "@rilldata/web-common/features/scheduled-reports/ScheduledReportDialog.svelte";
import { getRuntimeServiceListResourcesQueryKey } from "@rilldata/web-common/runtime-client";
import { runtime } from "@rilldata/web-common/runtime-client/runtime-store";
@@ -36,9 +36,8 @@
// Get dashboard
$: dashboardName = useReportDashboardName($runtime.instanceId, report);
- $: dashboard = useDashboard($runtime.instanceId, $dashboardName.data);
- $: dashboardTitle =
- $dashboard.data?.metricsView.spec.title || $dashboardName.data;
+ $: dashboard = useExploreValidSpec($runtime.instanceId, $dashboardName.data);
+ $: dashboardTitle = $dashboard.data?.explore?.title || $dashboardName.data;
// Get human-readable frequency
$: humanReadableFrequency =
@@ -140,7 +139,7 @@
Dashboard
- {dashboardTitle}
@@ -177,5 +176,6 @@
{/if}
diff --git a/web-admin/src/routes/-/embed/+page.svelte b/web-admin/src/routes/-/embed/+page.svelte
index 19cc6623286..63c98d031ab 100644
--- a/web-admin/src/routes/-/embed/+page.svelte
+++ b/web-admin/src/routes/-/embed/+page.svelte
@@ -2,15 +2,17 @@
import { page } from "$app/stores";
import ContentContainer from "@rilldata/web-admin/components/layout/ContentContainer.svelte";
import DashboardsTable from "@rilldata/web-admin/features/dashboards/listing/DashboardsTable.svelte";
- import CanvasDashboardEmbed from "@rilldata/web-admin/features/embeds/CanvasDashboardEmbed.svelte";
- import MetricsExplorerEmbed from "@rilldata/web-admin/features/embeds/MetricsExplorerEmbed.svelte";
+ import CanvasEmbed from "@rilldata/web-admin/features/embeds/CanvasEmbed.svelte";
+ import ExploreEmbed from "@rilldata/web-admin/features/embeds/ExploreEmbed.svelte";
import TopNavigationBarEmbed from "@rilldata/web-admin/features/embeds/TopNavigationBarEmbed.svelte";
+ import UnsupportedKind from "@rilldata/web-admin/features/embeds/UnsupportedKind.svelte";
import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors";
import type { V1ResourceName } from "@rilldata/web-common/runtime-client";
const instanceId = $page.url.searchParams.get("instance_id");
const initialResourceName = $page.url.searchParams.get("resource");
- const initialResourceKind = $page.url.searchParams.get("kind");
+ const initialResourceType =
+ $page.url.searchParams.get("type") ?? $page.url.searchParams.get("kind"); // "kind" is for backwards compatibility
const navigation = $page.url.searchParams.get("navigation");
// Ignoring state and theme params for now
// const state = $page.url.searchParams.get("state");
@@ -18,10 +20,10 @@
// Manage active resource
let activeResource: V1ResourceName | null = null;
- if (initialResourceName && initialResourceKind) {
+ if (initialResourceName && initialResourceType) {
activeResource = {
name: initialResourceName,
- kind: initialResourceKind,
+ kind: initialResourceType,
};
}
@@ -59,8 +61,10 @@
{/if}
{/if}
-{#if activeResource?.kind === ResourceKind.MetricsView.toString()}
-
+{#if activeResource?.kind === ResourceKind.Explore.toString()}
+
{:else if activeResource?.kind === ResourceKind.Canvas.toString()}
-
+
+{:else}
+
{/if}
diff --git a/web-admin/src/routes/[organization]/[project]/-/alerts/[alert]/open/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/alerts/[alert]/open/+page.svelte
index c38eb72797e..5b957edd53a 100644
--- a/web-admin/src/routes/[organization]/[project]/-/alerts/[alert]/open/+page.svelte
+++ b/web-admin/src/routes/[organization]/[project]/-/alerts/[alert]/open/+page.svelte
@@ -3,6 +3,10 @@
import { page } from "$app/stores";
import { useAlert } from "@rilldata/web-admin/features/alerts/selectors";
import { mapQueryToDashboard } from "@rilldata/web-admin/features/dashboards/query-mappers/mapQueryToDashboard";
+ import {
+ getExploreName,
+ getExplorePageUrl,
+ } from "@rilldata/web-admin/features/dashboards/query-mappers/utils.js";
import CtaButton from "@rilldata/web-common/components/calls-to-action/CTAButton.svelte";
import CtaContentContainer from "@rilldata/web-common/components/calls-to-action/CTAContentContainer.svelte";
import CtaLayoutContainer from "@rilldata/web-common/components/calls-to-action/CTALayoutContainer.svelte";
@@ -17,6 +21,9 @@
$: executionTime = $page.url.searchParams.get("execution_time");
$: alert = useAlert($runtime.instanceId, alertId);
+ $: exploreName = getExploreName(
+ $alert.data?.resource?.alert?.spec?.annotations?.web_open_path,
+ );
let dashboardStateForAlert: ReturnType
;
$: queryName =
@@ -28,6 +35,7 @@
$alert.data?.resource?.alert?.spec?.queryArgsJson ??
"";
$: dashboardStateForAlert = mapQueryToDashboard(
+ exploreName,
queryName,
queryArgsJson,
executionTime,
@@ -39,8 +47,14 @@
}
$: if ($dashboardStateForAlert.data) {
- goto(
- `/${organization}/${project}/${$dashboardStateForAlert.data.metricsView}?state=${encodeURIComponent($dashboardStateForAlert.data.state)}`,
+ void goto(
+ getExplorePageUrl(
+ $page.url,
+ organization,
+ project,
+ $dashboardStateForAlert.data.exploreName,
+ $dashboardStateForAlert.data.state,
+ ),
);
}
diff --git a/web-admin/src/routes/[organization]/[project]/-/reports/[report]/open/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/reports/[report]/open/+page.svelte
index a72a679082d..af8ab1f76b5 100644
--- a/web-admin/src/routes/[organization]/[project]/-/reports/[report]/open/+page.svelte
+++ b/web-admin/src/routes/[organization]/[project]/-/reports/[report]/open/+page.svelte
@@ -2,6 +2,10 @@
import { goto } from "$app/navigation";
import { page } from "$app/stores";
import { mapQueryToDashboard } from "@rilldata/web-admin/features/dashboards/query-mappers/mapQueryToDashboard";
+ import {
+ getExploreName,
+ getExplorePageUrl,
+ } from "@rilldata/web-admin/features/dashboards/query-mappers/utils";
import { useReport } from "@rilldata/web-admin/features/scheduled-reports/selectors";
import CtaButton from "@rilldata/web-common/components/calls-to-action/CTAButton.svelte";
import CtaContentContainer from "@rilldata/web-common/components/calls-to-action/CTAContentContainer.svelte";
@@ -17,9 +21,13 @@
$: executionTime = $page.url.searchParams.get("execution_time");
$: report = useReport($runtime.instanceId, reportId);
+ $: exploreName = getExploreName(
+ $report.data?.resource?.report?.spec?.annotations?.web_open_path,
+ );
let dashboardStateForReport: ReturnType;
$: dashboardStateForReport = mapQueryToDashboard(
+ exploreName,
$report.data?.resource?.report?.spec?.queryName,
$report.data?.resource?.report?.spec?.queryArgsJson,
executionTime,
@@ -27,8 +35,14 @@
);
$: if ($dashboardStateForReport.data) {
- goto(
- `/${organization}/${project}/${$dashboardStateForReport.data.metricsView}?state=${encodeURIComponent($dashboardStateForReport.data.state)}`,
+ void goto(
+ getExplorePageUrl(
+ $page.url,
+ organization,
+ project,
+ $dashboardStateForReport.data.exploreName,
+ $dashboardStateForReport.data.state,
+ ),
);
}
diff --git a/web-admin/src/routes/[organization]/[project]/-/settings/public-urls/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/settings/public-urls/+page.svelte
index d7ffff7dfe6..df767eb829b 100644
--- a/web-admin/src/routes/[organization]/[project]/-/settings/public-urls/+page.svelte
+++ b/web-admin/src/routes/[organization]/[project]/-/settings/public-urls/+page.svelte
@@ -1,18 +1,18 @@
-{#key metricsView}
-
-
-
-
-
-
-
-
-
+{#key resourceName}
+ {#if explore?.metricsView}
+
+
+
+
+
+
+
+
+
+ {/if}
{/key}
diff --git a/web-admin/src/routes/[organization]/[project]/[dashboard]/+page.ts b/web-admin/src/routes/[organization]/[project]/[dashboard]/+page.ts
new file mode 100644
index 00000000000..5fa55f18498
--- /dev/null
+++ b/web-admin/src/routes/[organization]/[project]/[dashboard]/+page.ts
@@ -0,0 +1,13 @@
+import { redirect } from "@sveltejs/kit";
+import type { PageLoad } from "./$types";
+
+/**
+ * Redirect `/[organization]/[project]/[dashboard]` to `/[organization]/[project]/explore/[dashboard]`.
+ * Maintains backwards compatibility with legacy URLs.
+ */
+export const load: PageLoad = ({ params }) => {
+ throw redirect(
+ 307,
+ `/${params.organization}/${params.project}/explore/${params.dashboard}`,
+ );
+};
diff --git a/web-admin/src/routes/[organization]/[project]/[dashboard]/+page.svelte b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte
similarity index 73%
rename from web-admin/src/routes/[organization]/[project]/[dashboard]/+page.svelte
rename to web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte
index deb8e73a131..c7fc94cad90 100644
--- a/web-admin/src/routes/[organization]/[project]/[dashboard]/+page.svelte
+++ b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.svelte
@@ -5,15 +5,15 @@
import DashboardBookmarksStateProvider from "@rilldata/web-admin/features/dashboards/DashboardBookmarksStateProvider.svelte";
import DashboardBuilding from "@rilldata/web-admin/features/dashboards/DashboardBuilding.svelte";
import DashboardErrored from "@rilldata/web-admin/features/dashboards/DashboardErrored.svelte";
+ import { errorStore } from "@rilldata/web-admin/features/errors/error-store";
import { viewAsUserStore } from "@rilldata/web-admin/features/view-as-user/viewAsUserStore";
import { Dashboard } from "@rilldata/web-common/features/dashboards";
import DashboardThemeProvider from "@rilldata/web-common/features/dashboards/DashboardThemeProvider.svelte";
import DashboardURLStateProvider from "@rilldata/web-common/features/dashboards/proto-state/DashboardURLStateProvider.svelte";
- import { useDashboard } from "@rilldata/web-common/features/dashboards/selectors";
import StateManagersProvider from "@rilldata/web-common/features/dashboards/state-managers/StateManagersProvider.svelte";
import DashboardStateProvider from "@rilldata/web-common/features/dashboards/stores/DashboardStateProvider.svelte";
+ import { useExplore } from "@rilldata/web-common/features/explores/selectors";
import { runtime } from "@rilldata/web-common/runtime-client/runtime-store";
- import { errorStore } from "../../../../features/errors/error-store";
const user = createAdminServiceGetCurrentUser();
@@ -26,10 +26,10 @@
$: ({
organization: orgName,
project: projectName,
- dashboard: dashboardName,
+ dashboard: exploreName,
} = $page.params);
- $: dashboard = useDashboard(instanceId, dashboardName, {
+ $: explore = useExplore(instanceId, exploreName, {
refetchInterval: () => {
if (isDashboardReconcilingForFirstTime) {
return PollIntervalWhenDashboardFirstReconciling;
@@ -42,19 +42,20 @@
});
$: isDashboardNotFound =
- !$dashboard.data &&
- $dashboard.isError &&
- $dashboard.error?.response?.status === 404;
+ !$explore.data &&
+ $explore.isError &&
+ $explore.error?.response?.status === 404;
+ // TODO: should these be checking metricsView or explore?
$: isDashboardReconcilingForFirstTime =
- $dashboard?.data?.metricsView?.state?.validSpec === null &&
- !$dashboard?.data?.meta?.reconcileError;
+ $explore?.data?.metricsView?.metricsView?.state?.validSpec === null &&
+ !$explore?.data?.metricsView?.meta?.reconcileError;
// We check for metricsView.state.validSpec instead of meta.reconcileError. validSpec persists
// from previous valid dashboards, allowing display even when the current dashboard spec is invalid
// and a meta.reconcileError exists.
$: isDashboardErrored =
- $dashboard?.data?.metricsView?.state?.validSpec === null &&
- !!$dashboard?.data?.meta?.reconcileError;
- $: metricViewName = $dashboard.data?.meta.name.name;
+ $explore?.data?.metricsView?.metricsView?.state?.validSpec === null &&
+ !!$explore?.data?.metricsView?.meta?.reconcileError;
+ $: metricsViewName = $explore.data?.metricsView?.meta?.name?.name;
// If no dashboard is found, show a 404 page
$: if (isDashboardNotFound) {
@@ -74,30 +75,30 @@
- {dashboardName} - Rill
+ {exploreName} - Rill
-{#if $dashboard.isSuccess}
+{#if $explore.isSuccess}
{#if isDashboardReconcilingForFirstTime}
{:else if isDashboardErrored}
- {:else if metricViewName}
- {#key metricViewName}
-
+ {:else if metricsViewName}
+ {#key metricsViewName}
+
{#if $user.isSuccess && $user.data.user}
-
-
+
+
-
+
{:else}
-
-
+
+
-
+
diff --git a/web-common/src/components/dropdown-menu/DropdownMenuItem.svelte b/web-common/src/components/dropdown-menu/DropdownMenuItem.svelte
index 220add1404e..bd1e7b0a161 100644
--- a/web-common/src/components/dropdown-menu/DropdownMenuItem.svelte
+++ b/web-common/src/components/dropdown-menu/DropdownMenuItem.svelte
@@ -13,19 +13,22 @@
let className: $$Props["class"] = undefined;
export { className as class };
+ export let href: $$Props["href"] = undefined;
export let inset: $$Props["inset"] = undefined;
export let type: $$Props["type"] = "default";
diff --git a/web-common/src/components/dropdown-menu/__stories__/DropdownMenu.stories.svelte b/web-common/src/components/dropdown-menu/__stories__/DropdownMenu.stories.svelte
index da5e40ef487..0c5aab4bfac 100644
--- a/web-common/src/components/dropdown-menu/__stories__/DropdownMenu.stories.svelte
+++ b/web-common/src/components/dropdown-menu/__stories__/DropdownMenu.stories.svelte
@@ -1,10 +1,9 @@
@@ -34,7 +33,7 @@
Option 1
-
+
Option 2
diff --git a/web-common/src/components/icons/EditIcon.svelte b/web-common/src/components/icons/EditIcon.svelte
index 65cf5869f68..ebbf65f515a 100644
--- a/web-common/src/components/icons/EditIcon.svelte
+++ b/web-common/src/components/icons/EditIcon.svelte
@@ -7,7 +7,7 @@