Skip to content

Commit 2622e15

Browse files
committed
feat_favorites_homepage_section
1 parent db36636 commit 2622e15

2 files changed

Lines changed: 118 additions & 0 deletions

File tree

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { useNavigate } from "@tanstack/react-router";
2+
import { ChevronLeft, ChevronRight, GitBranch, Play, Star, X } from "lucide-react";
3+
import { useState } from "react";
4+
5+
import { Button } from "@/components/ui/button";
6+
import { InlineStack } from "@/components/ui/layout";
7+
import { Paragraph, Text } from "@/components/ui/typography";
8+
import { type FavoriteItem, useFavorites } from "@/hooks/useFavorites";
9+
import { EDITOR_PATH, RUNS_BASE_PATH } from "@/routes/router";
10+
11+
const PAGE_SIZE = 10;
12+
13+
function getFavoriteUrl(item: FavoriteItem): string {
14+
if (item.type === "pipeline") return `${EDITOR_PATH}/${item.id}`;
15+
return `${RUNS_BASE_PATH}/${item.id}`;
16+
}
17+
18+
const FavoriteChip = ({ item }: { item: FavoriteItem }) => {
19+
const navigate = useNavigate();
20+
const { removeFavorite } = useFavorites();
21+
22+
const handleClick = () => {
23+
navigate({ to: getFavoriteUrl(item) });
24+
};
25+
26+
const handleRemove = (e: React.MouseEvent) => {
27+
e.stopPropagation();
28+
removeFavorite(item.type, item.id);
29+
};
30+
31+
return (
32+
<div
33+
onClick={handleClick}
34+
title={item.name}
35+
className={`group flex items-center gap-1.5 pl-2 pr-1 py-1 border rounded-md cursor-pointer min-w-0 ${
36+
item.type === "pipeline"
37+
? "bg-violet-50/50 hover:bg-violet-50 border-violet-100"
38+
: "bg-emerald-50/50 hover:bg-emerald-50 border-emerald-100"
39+
}`}
40+
>
41+
{item.type === "pipeline" ? (
42+
<GitBranch className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
43+
) : (
44+
<Play className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
45+
)}
46+
<span className="text-sm truncate">{item.name}</span>
47+
<button
48+
onClick={handleRemove}
49+
className="shrink-0 p-0.5 rounded opacity-0 group-hover:opacity-100 hover:bg-muted text-muted-foreground hover:text-foreground cursor-pointer"
50+
>
51+
<X className="h-3 w-3" />
52+
</button>
53+
</div>
54+
);
55+
};
56+
57+
export const FavoritesSection = () => {
58+
const { favorites } = useFavorites();
59+
const [page, setPage] = useState(0);
60+
61+
const totalPages = Math.ceil(favorites.length / PAGE_SIZE);
62+
// Reset to last valid page if favorites shrink (e.g. after removing items)
63+
const safePage = Math.min(page, Math.max(0, totalPages - 1));
64+
const paginated = favorites.slice(safePage * PAGE_SIZE, (safePage + 1) * PAGE_SIZE);
65+
66+
return (
67+
<div className="flex flex-col gap-2">
68+
<InlineStack blockAlign="center" gap="1">
69+
<Star className="h-4 w-4 text-warning" fill="oklch(79.5% 0.184 86.047)" />
70+
<Text as="h2" size="sm" weight="semibold">
71+
Favorites
72+
</Text>
73+
</InlineStack>
74+
75+
{favorites.length === 0 ? (
76+
<Paragraph tone="subdued" size="sm">
77+
No favorites yet. Star a pipeline or run to pin it here.
78+
</Paragraph>
79+
) : (
80+
<div className="flex flex-col gap-2">
81+
<div className="flex flex-wrap gap-2">
82+
{paginated.map((item) => (
83+
<FavoriteChip key={`${item.type}-${item.id}`} item={item} />
84+
))}
85+
</div>
86+
87+
{totalPages > 1 && (
88+
<InlineStack blockAlign="center" gap="2">
89+
<Button
90+
variant="ghost"
91+
size="icon"
92+
className="h-6 w-6"
93+
disabled={safePage === 0}
94+
onClick={() => setPage(safePage - 1)}
95+
>
96+
<ChevronLeft className="h-4 w-4" />
97+
</Button>
98+
<Paragraph tone="subdued" size="sm">
99+
{safePage + 1} / {totalPages}
100+
</Paragraph>
101+
<Button
102+
variant="ghost"
103+
size="icon"
104+
className="h-6 w-6"
105+
disabled={safePage >= totalPages - 1}
106+
onClick={() => setPage(safePage + 1)}
107+
>
108+
<ChevronRight className="h-4 w-4" />
109+
</Button>
110+
</InlineStack>
111+
)}
112+
</div>
113+
)}
114+
</div>
115+
);
116+
};

src/routes/Dashboard/Dashboard.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { FavoritesSection } from "@/components/Home/FavoritesSection/FavoritesSection";
12
import { BlockStack, InlineStack } from "@/components/ui/layout";
23
import { Text } from "@/components/ui/typography";
34

@@ -17,6 +18,7 @@ export const Dashboard = () => {
1718
Beta
1819
</Text>
1920
</InlineStack>
21+
<FavoritesSection />
2022
</BlockStack>
2123
);
2224
};

0 commit comments

Comments
 (0)