Skip to content

g1t/apps/web/app/routes/repo/releases.tsx

67 lines2,576 bytesCodeBlame
1import { Plus, Tag as TagIcon } from "lucide-react";
2import { Link } from "react-router";
3
4import type { Route } from "./+types/releases";
5import { ReleaseCard } from "../../components/releases";
6import { ButtonLink, EmptyState } from "../../components/ui";
7import { requireRepo } from "../../lib/access.server";
8import { page } from "../../lib/meta";
9import { repos } from "../../lib/services.server";
10import { unwrap } from "../../lib/session.server";
11
12export function meta({ params, ...args }: Route.MetaArgs) {
13 return page(args, { title: `Releases · ${params.owner}/${params.repo} · g1t` });
14}
15
16export async function loader({ params, context }: Route.LoaderArgs) {
17 const { viewer, access } = await requireRepo(context, params, "read");
18 const path = { namespace: params.owner, name: params.repo };
19 return { releases: unwrap(await repos.releases(path, viewer)), canPush: access.can.push };
20}
21
22export default function Releases({ loaderData, params }: Route.ComponentProps) {
23 const { releases, canPush } = loaderData;
24 const base = `/${params.owner}/${params.repo}`;
25 const repo = { namespace: params.owner, name: params.repo };
26 return (
27 <div className="space-y-6">
28 <div className="flex flex-wrap items-center justify-between gap-3">
29 <h2 className="text-lg font-semibold tracking-tight">
30 Releases <span className="font-normal text-faint">{releases.length}</span>
31 </h2>
32 <div className="flex items-center gap-2">
33 <Link to={`${base}/tags`} className="inline-flex items-center gap-1.5 text-sm text-muted hover:text-accent">
34 <TagIcon size={14} className="text-faint" />
35 Tags
36 </Link>
37 {canPush && (
38 <ButtonLink to={`${base}/releases/new`}>
39 <Plus size={15} />
40 Draft a new release
41 </ButtonLink>
42 )}
43 </div>
44 </div>
45 {releases.length === 0 ? (
46 <EmptyState title="No releases published">
47 A release is a tag with a title and notes, for people to see what changed and download it.
48 {canPush ? (
49 <>
50 {" "}
51 <Link to={`${base}/releases/new`} className="text-accent hover:underline">
52 Create a new release
53 </Link>
54 .
55 </>
56 ) : null}
57 </EmptyState>
58 ) : (
59 <div className="space-y-8">
60 {releases.map((release) => (
61 <ReleaseCard key={release.id} release={release} repo={repo} />
62 ))}
63 </div>
64 )}
65 </div>
66 );
67}