feat: add bookmarks page 6d5c4312
Steve Simkins · 2026-04-26 07:39 3 file(s) · +83 −0
packages/client/src/components/page/LinkCard.astro (added) +23 −0
1 +
---
2 +
type Link = {
3 +
	id: number;
4 +
	short_id: string;
5 +
	title: string;
6 +
	url: string;
7 +
	category_id: number;
8 +
	created_at: number;
9 +
};
10 +
11 +
const { link } = Astro.props as { link: Link };
12 +
const domain = new URL(link.url).hostname.replace(/^www\./, "");
13 +
---
14 +
15 +
<article class="flex flex-col py-3 border-b border-[#333]">
16 +
	<a
17 +
		href={link.url}
18 +
		target="_blank"
19 +
		rel="noopener noreferrer"
20 +
		class="text-base hover:opacity-70 transition-opacity"
21 +
	>{link.title}</a>
22 +
	<span class="text-xs opacity-50 mt-0.5">{domain}</span>
23 +
</article>
packages/client/src/data/constants.ts +4 −0
40 40
    path: "/library",
41 41
  },
42 42
  {
43 +
    title: "Bookmarks",
44 +
    path: "/bookmarks",
45 +
  },
46 +
  {
43 47
    title: "Git",
44 48
    path: "/git",
45 49
  },
packages/client/src/pages/bookmarks.astro (added) +56 −0
1 +
---
2 +
export const prerender = false;
3 +
4 +
import PageLayout from "@/layouts/Base.astro";
5 +
import LinkCard from "@/components/page/LinkCard.astro";
6 +
7 +
type Link = {
8 +
	id: number;
9 +
	short_id: string;
10 +
	title: string;
11 +
	url: string;
12 +
	category_id: number;
13 +
	created_at: number;
14 +
};
15 +
16 +
const meta = {
17 +
	title: "Bookmarks",
18 +
	description: "Links I've saved and want to read",
19 +
};
20 +
21 +
let categories: [string, Link[]][] = [];
22 +
let error: string | null = null;
23 +
try {
24 +
	const res = await fetch("https://bookmarks.stevedylan.dev/api/links");
25 +
	if (res.ok) {
26 +
		const data: Record<string, Link[]> = await res.json();
27 +
		categories = Object.entries(data).filter(([, links]) => links.length > 0);
28 +
	} else {
29 +
		error = `API returned ${res.status}`;
30 +
	}
31 +
} catch (e) {
32 +
	error = e instanceof Error ? e.message : "Failed to reach bookmarks API";
33 +
}
34 +
---
35 +
36 +
<PageLayout meta={meta}>
37 +
	<div class="flex min-h-screen flex-col items-start justify-start gap-6">
38 +
		<h1 class="title">Bookmarks</h1>
39 +
		{error ? (
40 +
			<p class="text-red-400 text-sm">Could not load bookmarks: {error}</p>
41 +
		) : categories.length === 0 ? (
42 +
			<p class="text-gray-400 text-sm">no bookmarks yet</p>
43 +
		) : (
44 +
			<div class="flex flex-col w-full gap-8">
45 +
				{categories.map(([name, links]) => (
46 +
					<div class="flex flex-col w-full">
47 +
						<h2 class="text-sm font-semibold uppercase tracking-widest opacity-50 mb-2">{name}</h2>
48 +
						{links.map((link) => (
49 +
							<LinkCard link={link} />
50 +
						))}
51 +
					</div>
52 +
				))}
53 +
			</div>
54 +
		)}
55 +
	</div>
56 +
</PageLayout>