pr_01m47d15m3e54sn21z27rpy5n9/apps/web/app/routes/profile.tsx
| 1 | import { data } from "react-router"; |
| 2 | |
| 3 | import type { Route } from "./+types/profile"; |
| 4 | import { RepoList } from "../components/repo-list"; |
| 5 | import { Avatar } from "../components/ui"; |
| 6 | import { identity, repos as reposApi } from "../lib/services.server"; |
| 7 | import { getViewer } from "../lib/session.server"; |
| 8 | |
| 9 | export function meta({ params }: Route.MetaArgs) { |
| 10 | return [{ title: `${params.owner} · g1t` }]; |
| 11 | } |
| 12 | |
| 13 | export async function loader({ params, context }: Route.LoaderArgs) { |
| 14 | const [user, repos] = await Promise.all([ |
| 15 | identity.userByUsername(params.owner), |
| 16 | reposApi.list(getViewer(context), { namespace: params.owner }), |
| 17 | ]); |
| 18 | if (!user) throw data(null, { status: 404 }); |
| 19 | return { user, repos }; |
| 20 | } |
| 21 | |
| 22 | export default function Profile({ loaderData }: Route.ComponentProps) { |
| 23 | const { user, repos } = loaderData; |
| 24 | return ( |
| 25 | <main className="mx-auto max-w-4xl px-4 py-10"> |
| 26 | <div className="flex items-center gap-4"> |
| 27 | <Avatar name={user.username} size={56} /> |
| 28 | <div> |
| 29 | <h1 className="font-mono text-2xl font-semibold tracking-tight"> |
| 30 | {user.username} |
| 31 | </h1> |
| 32 | <p className="text-sm text-muted"> |
| 33 | {repos.length} {repos.length === 1 ? "repository" : "repositories"} |
| 34 | </p> |
| 35 | </div> |
| 36 | </div> |
| 37 | <h2 className="mt-10 text-sm font-medium text-muted">Repositories</h2> |
| 38 | <RepoList repos={repos} /> |
| 39 | </main> |
| 40 | ); |
| 41 | } |