diff --git a/src/components/Home/FavoritesSection/FavoritesSection.tsx b/src/components/Home/FavoritesSection/FavoritesSection.tsx new file mode 100644 index 000000000..75114d38c --- /dev/null +++ b/src/components/Home/FavoritesSection/FavoritesSection.tsx @@ -0,0 +1,170 @@ +import { useNavigate } from "@tanstack/react-router"; +import { + ChevronLeft, + ChevronRight, + GitBranch, + Play, + Star, + X, +} from "lucide-react"; +import { useState } from "react"; + +import { Button } from "@/components/ui/button"; +import { Icon } from "@/components/ui/icon"; +import { Input } from "@/components/ui/input"; +import { InlineStack } from "@/components/ui/layout"; +import { Paragraph, Text } from "@/components/ui/typography"; +import { type FavoriteItem, useFavorites } from "@/hooks/useFavorites"; +import { EDITOR_PATH, RUNS_BASE_PATH } from "@/routes/router"; + +const PAGE_SIZE = 10; + +function getFavoriteUrl(item: FavoriteItem): string { + if (item.type === "pipeline") return `${EDITOR_PATH}/${item.id}`; + return `${RUNS_BASE_PATH}/${item.id}`; +} + +const FavoriteChip = ({ item }: { item: FavoriteItem }) => { + const navigate = useNavigate(); + const { removeFavorite } = useFavorites(); + + const handleClick = () => { + navigate({ to: getFavoriteUrl(item) }); + }; + + const handleRemove = (e: React.MouseEvent) => { + e.stopPropagation(); + removeFavorite(item.type, item.id); + }; + + return ( +
+ {item.type === "pipeline" ? ( + + ) : ( + + )} + {item.name} + +
+ ); +}; + +export const FavoritesSection = () => { + const { favorites } = useFavorites(); + const [page, setPage] = useState(0); + const [query, setQuery] = useState(""); + + const filtered = query.trim() + ? favorites.filter((f) => { + const q = query.toLowerCase(); + return ( + f.id.toLowerCase().includes(q) || f.name.toLowerCase().includes(q) + ); + }) + : favorites; + + const totalPages = Math.ceil(filtered.length / PAGE_SIZE); + // Reset to last valid page if filtered results shrink + const safePage = Math.min(page, Math.max(0, totalPages - 1)); + const paginated = filtered.slice( + safePage * PAGE_SIZE, + (safePage + 1) * PAGE_SIZE, + ); + + return ( +
+ + + + Favorites + + + + {favorites.length === 0 ? ( + + No favorites yet. Star a pipeline or run to pin it here. + + ) : ( +
+
+ + { + setPage(0); + setQuery(e.target.value); + }} + className="pl-9 pr-8 w-full" + /> + {query && ( + + )} +
+
+ {paginated.map((item) => ( + + ))} +
+ + {totalPages > 1 && ( + + + + {safePage + 1} / {totalPages} + + + + )} +
+ )} +
+ ); +}; diff --git a/src/routes/Dashboard/DashboardFavoritesView.tsx b/src/routes/Dashboard/DashboardFavoritesView.tsx new file mode 100644 index 000000000..7a992312d --- /dev/null +++ b/src/routes/Dashboard/DashboardFavoritesView.tsx @@ -0,0 +1,5 @@ +import { FavoritesSection } from "@/components/Home/FavoritesSection/FavoritesSection"; + +export function DashboardFavoritesView() { + return ; +} diff --git a/src/routes/router.ts b/src/routes/router.ts index d02e60d56..6771b2eca 100644 --- a/src/routes/router.ts +++ b/src/routes/router.ts @@ -17,6 +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 { DashboardFavoritesView } from "./Dashboard/DashboardFavoritesView"; import { DashboardLayout } from "./Dashboard/DashboardLayout"; import Editor from "./Editor"; import Home from "./Home"; @@ -128,7 +129,7 @@ const dashboardComponentsRoute = createRoute({ const dashboardFavoritesRoute = createRoute({ getParentRoute: () => dashboardRoute, path: "/favorites", - component: ComingSoon, + component: DashboardFavoritesView, }); const dashboardRecentlyViewedRoute = createRoute({