diff --git a/knip.json b/knip.json
index a5b3fc535..fbfe870ca 100644
--- a/knip.json
+++ b/knip.json
@@ -1,10 +1,8 @@
{
"$schema": "https://unpkg.com/knip@5/schema.json",
"ignore": [
- ".git/**",
"src/api/**",
"src/components/ui/**",
- "openapi-ts.config.ts",
"src/config/announcements.ts",
"src/components/shared/BetaFeatureWrapper/BetaFeatureWrapper.tsx"
],
diff --git a/src/components/Home/RecentlyViewedSection/RecentlyViewedSection.tsx b/src/components/Home/RecentlyViewedSection/RecentlyViewedSection.tsx
deleted file mode 100644
index 18bbc9d91..000000000
--- a/src/components/Home/RecentlyViewedSection/RecentlyViewedSection.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-import { useNavigate } from "@tanstack/react-router";
-import { Clock, GitBranch, Play } from "lucide-react";
-
-import { InlineStack } from "@/components/ui/layout";
-import {
- Tooltip,
- TooltipContent,
- TooltipTrigger,
-} from "@/components/ui/tooltip";
-import { Paragraph, Text } from "@/components/ui/typography";
-import {
- type RecentlyViewedItem,
- useRecentlyViewed,
-} from "@/hooks/useRecentlyViewed";
-import { EDITOR_PATH, RUNS_BASE_PATH } from "@/routes/router";
-
-function getRecentlyViewedUrl(item: RecentlyViewedItem): string {
- if (item.type === "pipeline") return `${EDITOR_PATH}/${item.id}`;
- if (item.type === "run") return `${RUNS_BASE_PATH}/${item.id}`;
- // component support to be added later
- return "/";
-}
-
-const RecentlyViewedChip = ({ item }: { item: RecentlyViewedItem }) => {
- const navigate = useNavigate();
-
- const tooltipContent =
- item.type === "run" ? `${item.name} #${item.id}` : item.name;
-
- return (
-
-
- navigate({ to: getRecentlyViewedUrl(item) })}
- className={`flex items-center gap-1.5 pl-2 pr-2 py-1 border rounded-md cursor-pointer w-48 ${
- item.type === "pipeline"
- ? "bg-violet-50/50 hover:bg-violet-50 border-violet-100"
- : "bg-emerald-50/50 hover:bg-emerald-50 border-emerald-100"
- }`}
- >
- {item.type === "pipeline" ? (
-
- ) : (
-
- )}
-
{item.name}
-
-
- {tooltipContent}
-
- );
-};
-
-export const RecentlyViewedSection = () => {
- const { recentlyViewed } = useRecentlyViewed();
-
- return (
-
-
-
-
- Recently Viewed
-
-
-
- {recentlyViewed.length === 0 ? (
-
- Nothing viewed yet. Open a pipeline or run to see it here.
-
- ) : (
-
- {recentlyViewed.map((item) => (
-
- ))}
-
- )}
-
- );
-};
diff --git a/src/hooks/useRecentlyViewed.ts b/src/hooks/useRecentlyViewed.ts
index 1ed77ebae..9d4714df1 100644
--- a/src/hooks/useRecentlyViewed.ts
+++ b/src/hooks/useRecentlyViewed.ts
@@ -5,9 +5,9 @@ import { getStorage } from "@/utils/typedStorage";
const RECENTLY_VIEWED_KEY = "Home/recently_viewed";
const MAX_ITEMS = 5;
-export type RecentlyViewedType = "pipeline" | "run" | "component";
+type RecentlyViewedType = "pipeline" | "run" | "component";
-export interface RecentlyViewedItem {
+interface RecentlyViewedItem {
type: RecentlyViewedType;
id: string;
name: string;
diff --git a/src/routes/Dashboard/Dashboard.tsx b/src/routes/Dashboard/Dashboard.tsx
deleted file mode 100644
index 313cfd7c2..000000000
--- a/src/routes/Dashboard/Dashboard.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { FavoritesSection } from "@/components/Home/FavoritesSection/FavoritesSection";
-import { RecentlyViewedSection } from "@/components/Home/RecentlyViewedSection/RecentlyViewedSection";
-import { BlockStack, InlineStack } from "@/components/ui/layout";
-import { Text } from "@/components/ui/typography";
-
-export const Dashboard = () => {
- return (
-
-
-
- Dashboard
-
-
- Beta
-
-
-
-
-
- );
-};
diff --git a/src/routes/Dashboard/DashboardLayout.tsx b/src/routes/Dashboard/DashboardLayout.tsx
new file mode 100644
index 000000000..774e7b71c
--- /dev/null
+++ b/src/routes/Dashboard/DashboardLayout.tsx
@@ -0,0 +1,72 @@
+import { Link, Outlet } from "@tanstack/react-router";
+
+import { Icon, type IconName } from "@/components/ui/icon";
+import { BlockStack, InlineStack } from "@/components/ui/layout";
+import { Heading, Text } from "@/components/ui/typography";
+import { cn } from "@/lib/utils";
+
+interface SidebarItem {
+ to: string;
+ label: string;
+ icon: IconName;
+}
+
+const SIDEBAR_ITEMS: SidebarItem[] = [
+ { to: "/dashboard/runs", label: "Runs", icon: "Play" },
+ { to: "/dashboard/pipelines", label: "Pipelines", icon: "GitBranch" },
+ { to: "/dashboard/components", label: "Components", icon: "Package" },
+ { to: "/dashboard/favorites", label: "Favorites", icon: "Star" },
+ { to: "/dashboard/recently-viewed", label: "Recently Viewed", icon: "Clock" },
+];
+
+export function DashboardLayout() {
+ return (
+
+
+
+ Dashboard
+
+ Beta
+
+
+
+
+
+ {SIDEBAR_ITEMS.map((item) => (
+
+ {({ isActive }) => (
+
+
+ {item.label}
+
+ )}
+
+ ))}
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/routes/router.ts b/src/routes/router.ts
index e7a91f528..d02e60d56 100644
--- a/src/routes/router.ts
+++ b/src/routes/router.ts
@@ -17,7 +17,7 @@ import { isFlagEnabled } from "@/components/shared/Settings/useFlags";
import { BASE_URL, IS_GITHUB_PAGES } from "@/utils/constants";
import RootLayout from "../components/layout/RootLayout";
-import { Dashboard } from "./Dashboard/Dashboard";
+import { DashboardLayout } from "./Dashboard/DashboardLayout";
import Editor from "./Editor";
import Home from "./Home";
import { ImportPage } from "./Import";
@@ -45,6 +45,11 @@ const DASHBOARD_PATH = "/dashboard";
export const APP_ROUTES = {
HOME: "/",
DASHBOARD: DASHBOARD_PATH,
+ DASHBOARD_RUNS: `${DASHBOARD_PATH}/runs`,
+ DASHBOARD_PIPELINES: `${DASHBOARD_PATH}/pipelines`,
+ DASHBOARD_COMPONENTS: `${DASHBOARD_PATH}/components`,
+ DASHBOARD_FAVORITES: `${DASHBOARD_PATH}/favorites`,
+ DASHBOARD_RECENTLY_VIEWED: `${DASHBOARD_PATH}/recently-viewed`,
QUICK_START: QUICK_START_PATH,
IMPORT: IMPORT_PATH,
PIPELINE_EDITOR: `${EDITOR_PATH}/$name`,
@@ -83,7 +88,7 @@ const indexRoute = createRoute({
const dashboardRoute = createRoute({
getParentRoute: () => mainLayout,
path: APP_ROUTES.DASHBOARD,
- component: Dashboard,
+ component: DashboardLayout,
beforeLoad: () => {
if (!isFlagEnabled("dashboard")) {
throw redirect({ to: APP_ROUTES.HOME });
@@ -91,6 +96,47 @@ const dashboardRoute = createRoute({
},
});
+const dashboardIndexRoute = createRoute({
+ getParentRoute: () => dashboardRoute,
+ path: "/",
+ beforeLoad: () => {
+ throw redirect({ to: APP_ROUTES.DASHBOARD_RUNS });
+ },
+});
+
+// Placeholder component — replaced in subsequent PRs
+const ComingSoon = () => null;
+
+const dashboardRunsRoute = createRoute({
+ getParentRoute: () => dashboardRoute,
+ path: "/runs",
+ component: ComingSoon,
+});
+
+const dashboardPipelinesRoute = createRoute({
+ getParentRoute: () => dashboardRoute,
+ path: "/pipelines",
+ component: ComingSoon,
+});
+
+const dashboardComponentsRoute = createRoute({
+ getParentRoute: () => dashboardRoute,
+ path: "/components",
+ component: ComingSoon,
+});
+
+const dashboardFavoritesRoute = createRoute({
+ getParentRoute: () => dashboardRoute,
+ path: "/favorites",
+ component: ComingSoon,
+});
+
+const dashboardRecentlyViewedRoute = createRoute({
+ getParentRoute: () => dashboardRoute,
+ path: "/recently-viewed",
+ component: ComingSoon,
+});
+
const quickStartRoute = createRoute({
getParentRoute: () => mainLayout,
path: APP_ROUTES.QUICK_START,
@@ -207,9 +253,18 @@ const settingsRouteTree = settingsLayoutRoute.addChildren([
secretsRouteTree,
]);
+const dashboardRouteTree = dashboardRoute.addChildren([
+ dashboardIndexRoute,
+ dashboardRunsRoute,
+ dashboardPipelinesRoute,
+ dashboardComponentsRoute,
+ dashboardFavoritesRoute,
+ dashboardRecentlyViewedRoute,
+]);
+
const appRouteTree = mainLayout.addChildren([
indexRoute,
- dashboardRoute,
+ dashboardRouteTree,
quickStartRoute,
settingsRouteTree,
importRoute,