Skip to content

Commit

Permalink
Embed: Add ability to navigate a project's dashboards (#4367)
Browse files Browse the repository at this point in the history
* Refactor `/-/embed` to handle different resource kinds

* Delete unused component

* Add `click-row` event to Table component

* Edit `GetIFrame` handler to allow `navigation` and/or `resource` params

* Refactor `DashboardsTable` to account for embedded context

* Refactor `LastRefreshedDate` to account for embedded context

* Add navigation to `/-/embed`

* Fix lint
  • Loading branch information
ericpgreen2 authored and nishantmonu51 committed Mar 19, 2024
1 parent 2b09f14 commit 385c5cf
Show file tree
Hide file tree
Showing 11 changed files with 218 additions and 155 deletions.
4 changes: 2 additions & 2 deletions admin/server/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,8 @@ func (s *Server) GetIFrame(ctx context.Context, req *adminv1.GetIFrameRequest) (
attribute.String("args.state", req.State),
)

if req.Resource == "" {
return nil, status.Error(codes.InvalidArgument, "resource must be specified")
if !req.Navigation && req.Resource == "" {
return nil, status.Error(codes.InvalidArgument, "resource must be provided if navigation is not enabled")
}

proj, err := s.admin.DB.FindProjectByName(ctx, req.Organization, req.Project)
Expand Down
11 changes: 9 additions & 2 deletions web-admin/src/components/table/Table.svelte
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
<script lang="ts">
import {
Row,
createSvelteTable,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getSortedRowModel,
} from "@tanstack/svelte-table";
import type { ColumnDef, TableOptions } from "@tanstack/table-core/src/types";
import { setContext } from "svelte";
import { createEventDispatcher, setContext } from "svelte";
import { writable } from "svelte/store";
export let data: unknown[] = [];
export let columns: ColumnDef<unknown, unknown>[] = [];
export let columnVisibility: Record<string, boolean> = {};
export let maxWidthOverride: string | null = null;
const dispatch = createEventDispatcher();
let maxWidth = maxWidthOverride ?? "max-w-[800px]";
let sorting = [];
Expand Down Expand Up @@ -55,6 +58,10 @@
// Expose the table API to the children components via Context
setContext("table", table);
function handleClickRow(row: Row<unknown>) {
dispatch("click-row", row);
}
function rerender() {
options.update((options) => ({
...options,
Expand All @@ -77,7 +84,7 @@
</tr>
{:else}
{#each $table.getRowModel().rows as row}
<tr>
<tr on:click={() => handleClickRow(row)}>
{#each row.getVisibleCells() as cell}
<td class="hover:bg-slate-50">
<svelte:component
Expand Down
86 changes: 0 additions & 86 deletions web-admin/src/features/dashboards/listing/DashboardList.svelte

This file was deleted.

40 changes: 26 additions & 14 deletions web-admin/src/features/dashboards/listing/DashboardsTable.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
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";
import { flexRender } from "@tanstack/svelte-table";
import { Row, flexRender } from "@tanstack/svelte-table";
import { createEventDispatcher } from "svelte";
import Table from "../../../components/table/Table.svelte";
import DashboardsError from "./DashboardsError.svelte";
import DashboardsTableCompositeCell from "./DashboardsTableCompositeCell.svelte";
Expand All @@ -11,8 +12,7 @@
import NoDashboardsCTA from "./NoDashboardsCTA.svelte";
import { DashboardResource, useDashboardsV2 } from "./selectors";
export let organization: string;
export let project: string;
export let isEmbedded = false;
$: dashboards = useDashboardsV2($runtime.instanceId);
Expand All @@ -29,16 +29,17 @@
const columns = [
{
id: "composite",
cell: ({ row }) =>
flexRender(DashboardsTableCompositeCell, {
organization: organization,
project: project,
name: row.original.resource.meta.name.name,
title: row.original.resource.metricsView.spec.title,
lastRefreshed: row.original.refreshedOn,
description: row.original.resource.metricsView.spec.description,
error: row.original.resource.meta.reconcileError,
}),
cell: ({ row }) => {
const dashboard = row.original as DashboardResource;
return flexRender(DashboardsTableCompositeCell, {
name: dashboard.resource.meta.name.name,
title: dashboard.resource.metricsView.spec.title,
lastRefreshed: dashboard.refreshedOn,
description: dashboard.resource.metricsView.spec.description,
error: dashboard.resource.meta.reconcileError,
isEmbedded,
});
},
},
{
id: "title",
Expand Down Expand Up @@ -66,6 +67,12 @@
lastRefreshed: false,
description: false,
};
const dispatch = createEventDispatcher();
function handleClickRow(e: CustomEvent<Row<DashboardResource>>) {
dispatch("select-dashboard", e.detail.original.resource.meta.name.name);
}
</script>

{#if $dashboards.isLoading}
Expand All @@ -78,7 +85,12 @@
{#if $dashboards.data.length === 0}
<NoDashboardsCTA />
{:else}
<Table data={$dashboards?.data} {columns} {columnVisibility}>
<Table
data={$dashboards?.data}
{columns}
{columnVisibility}
on:click-row={handleClickRow}
>
<DashboardsTableHeader slot="header" />
<DashboardsTableEmpty slot="empty" />
</Table>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
<script lang="ts">
import { page } from "$app/stores";
import DashboardIcon from "@rilldata/web-common/components/icons/DashboardIcon.svelte";
import Tag from "@rilldata/web-common/components/tag/Tag.svelte";
import Tooltip from "@rilldata/web-common/components/tooltip/Tooltip.svelte";
import TooltipContent from "@rilldata/web-common/components/tooltip/TooltipContent.svelte";
import { timeAgo } from "./utils";
export let organization: string;
export let project: string;
export let name: string;
export let title: string;
export let lastRefreshed: string;
export let description: string;
export let error: string;
export let isEmbedded: boolean;
$: organization = $page.params.organization;
$: project = $page.params.project;
$: lastRefreshedDate = new Date(lastRefreshed);
$: isValidLastRefreshedDate = !isNaN(lastRefreshedDate.getTime());
</script>

<a
href={`/${organization}/${project}/${name}`}
class="flex flex-col gap-y-0.5 group px-4 py-[5px]"
<svelte:element
this={isEmbedded ? "button" : "a"}
class="flex flex-col gap-y-0.5 group px-4 py-[5px] w-full"
href={isEmbedded ? undefined : `/${organization}/${project}/${name}`}
role={isEmbedded ? "button" : "link"}
>
<div class="flex gap-x-2 items-center">
<DashboardIcon size={"14px"} className="text-slate-500" />
Expand Down Expand Up @@ -49,4 +54,4 @@
<span class="line-clamp-1">{description}</span>
{/if}
</div>
</a>
</svelte:element>
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<script lang="ts">
import { page } from "$app/stores";
import Tooltip from "@rilldata/web-common/components/tooltip/Tooltip.svelte";
import TooltipContent from "@rilldata/web-common/components/tooltip/TooltipContent.svelte";
import { runtime } from "@rilldata/web-common/runtime-client/runtime-store";
import { useDashboardV2 } from "./selectors";
import { timeAgo } from "./utils";
$: dashboardName = $page.params.dashboard;
export let dashboard: string;
$: dashboard = useDashboardV2($runtime?.instanceId, dashboardName);
$: dashboardQuery = useDashboardV2($runtime?.instanceId, dashboard);
$: lastRefreshedDate =
$dashboard?.data?.refreshedOn && new Date($dashboard.data.refreshedOn);
$dashboardQuery?.data?.refreshedOn &&
new Date($dashboardQuery.data.refreshedOn);
</script>

{#if lastRefreshedDate}
Expand Down
47 changes: 47 additions & 0 deletions web-admin/src/features/embeds/DashboardEmbed.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<script lang="ts">
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 { errorStore } from "../errors/error-store";
export let instanceId: string;
export let dashboardName: string;
$: dashboard = useDashboard(instanceId, dashboardName);
$: isDashboardNotFound =
$dashboard.isError && $dashboard.error?.response?.status === 404;
// 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;
// If no dashboard is found, show a 404 page
$: if (isDashboardNotFound) {
errorStore.set({
statusCode: 404,
header: "Dashboard not found",
body: `The dashboard you requested could not be found. Please check that you provided the name of a working dashboard.`,
});
}
</script>

{#if $dashboard.isSuccess}
{#if isDashboardErrored}
<br /> Dashboard Error <br />
{:else}
{#key dashboardName}
<StateManagersProvider metricsViewName={dashboardName}>
<DashboardStateProvider metricViewName={dashboardName}>
<DashboardURLStateProvider metricViewName={dashboardName}>
<DashboardThemeProvider>
<Dashboard metricViewName={dashboardName} leftMargin={"48px"} />
</DashboardThemeProvider>
</DashboardURLStateProvider>
</DashboardStateProvider>
</StateManagersProvider>
{/key}
{/if}
{/if}
Loading

0 comments on commit 385c5cf

Please sign in to comment.