Skip to content

g1t/apps/web/app/components/teams.tsx

67 lines2,399 bytesCodeBlame
1/**
2 * Pieces of the teams pages: a team in a list, and its badges.
3 */
4import { EyeOff, UsersRound } from "lucide-react";
5import { Link, useOutletContext } from "react-router";
6
7import { type Team, teamHandle } from "@g1t/contracts";
8
9import { teamCounts, teamPath } from "../lib/teams";
10import { Badge } from "./ui/badge";
11
12/** What each page of a team gets from the team's layout. */
13export type TeamContext = { team: Team };
14
15/** The team whose page this is. */
16export function useTeam(): Team {
17 return useOutletContext<TeamContext>().team;
18}
19
20/** Secret, and the viewer's place in the team. */
21export function TeamBadges({ team }: { team: Pick<Team, "visibility" | "viewer_role"> }) {
22 return (
23 <>
24 {team.visibility === "secret" && (
25 <Badge>
26 <EyeOff size={10} />
27 Secret
28 </Badge>
29 )}
30 {team.viewer_role === "maintainer" && <Badge tone="accent">Maintainer</Badge>}
31 {team.viewer_role === "member" && <Badge>Member</Badge>}
32 </>
33 );
34}
35
36/** One team in a list: its name, handle, what it is for, and how big it is. */
37export function TeamRow({ team, children }: { team: Team; children?: React.ReactNode }) {
38 return (
39 <li className="flex flex-wrap items-start gap-3 px-4 py-3 transition-colors hover:bg-surface/60 sm:flex-nowrap">
40 <span className="mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-md bg-raised text-muted ring-1 ring-line">
41 <UsersRound size={14} />
42 </span>
43 <div className="min-w-0 grow basis-48">
44 <div className="flex flex-wrap items-center gap-x-2 gap-y-1">
45 <Link to={teamPath(team.workspace, team.slug)} prefetch="intent" className="font-medium hover:underline">
46 {team.name}
47 </Link>
48 <span className="font-mono text-xs text-faint">{teamHandle(team)}</span>
49 <TeamBadges team={team} />
50 </div>
51 {team.description && <p className="mt-0.5 line-clamp-2 text-sm text-muted">{team.description}</p>}
52 <p className="mt-1 text-xs text-faint">
53 {teamCounts(team)}
54 {team.parent && (
55 <>
56 {" · in "}
57 <Link to={teamPath(team.workspace, team.parent.slug)} className="hover:text-fg hover:underline">
58 {team.parent.name}
59 </Link>
60 </>
61 )}
62 </p>
63 </div>
64 {children}
65 </li>
66 );
67}