Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040) | 1 | import { Form } from "react-router"; |
| 2 | ||
| 3 | import type { WorkspaceInvitation } from "@g1t/contracts"; | |
| 4 | ||
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 5 | import { ErrorText, SubmitButton } from "./ui"; |
| 6 | import { Avatar } from "./ui/avatar"; | |
| 7 | import { Card } from "./ui/card"; | |
| Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040) | 8 | import { UserCard } from "./user-card"; |
| 9 | import { invitationLine } from "../lib/invitations"; | |
| 10 | ||
| 11 | /** | |
| 12 | * The workspace invitations waiting for the person's answer, each with | |
| 13 | * Accept and Decline. Accepting joins with the role it names; declining | |
| 14 | * tells whoever sent it. `next` is where accepting goes, when not to the | |
| 15 | * workspace itself. | |
| 16 | */ | |
| 17 | export function InvitationList({ | |
| 18 | invitations, | |
| 19 | error, | |
| 20 | errorFor, | |
| 21 | next, | |
| 22 | }: { | |
| 23 | invitations: WorkspaceInvitation[]; | |
| 24 | error?: string | null; | |
| 25 | errorFor?: string | null; | |
| 26 | next?: string | null; | |
| 27 | }) { | |
| 28 | return ( | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 29 | <Card asChild tone="plain" divided> |
| 30 | <ul> | |
| 31 | {invitations.map((invitation) => ( | |
| 32 | <li key={invitation.id} className="px-4 py-4"> | |
| 33 | <div className="flex flex-wrap items-center gap-3"> | |
| 34 | <Avatar name={invitation.workspace.slug} image={invitation.workspace.avatar} size={32} square /> | |
| 35 | <div className="min-w-0 grow basis-48"> | |
| 36 | <p className="text-sm font-medium"> | |
| 37 | {invitation.workspace.name}{" "} | |
| 38 | <span className="font-mono text-xs font-normal text-faint">{invitation.workspace.slug}</span> | |
| 39 | </p> | |
| 40 | <p className="mt-0.5 text-xs text-muted"> | |
| 41 | {invitation.invitedBy ? ( | |
| 42 | <> | |
| 43 | <UserCard username={invitation.invitedBy.username}> | |
| 44 | <span className="font-mono text-fg/90">@{invitation.invitedBy.username}</span> | |
| 45 | </UserCard>{" "} | |
| 46 | </> | |
| 47 | ) : ( | |
| 48 | "The g1t team " | |
| 49 | )} | |
| 50 | {invitationLine(invitation)} | |
| 51 | </p> | |
| 52 | </div> | |
| 53 | <div className="flex items-center gap-2"> | |
| 54 | <Form method="post"> | |
| 55 | <input type="hidden" name="intent" value="decline-invitation" /> | |
| 56 | <input type="hidden" name="id" value={invitation.id} /> | |
| 57 | <input type="hidden" name="workspace" value={invitation.workspace.name} /> | |
| 58 | <SubmitButton variant="outline" match={{ intent: "decline-invitation", id: invitation.id }} pending="Declining…"> | |
| 59 | Decline | |
| 60 | </SubmitButton> | |
| 61 | </Form> | |
| 62 | <Form method="post"> | |
| 63 | <input type="hidden" name="intent" value="accept-invitation" /> | |
| 64 | <input type="hidden" name="id" value={invitation.id} /> | |
| 65 | {next && <input type="hidden" name="next" value={next} />} | |
| 66 | <SubmitButton match={{ intent: "accept-invitation", id: invitation.id }} pending="Joining…"> | |
| 67 | Accept | |
| 68 | </SubmitButton> | |
| 69 | </Form> | |
| 70 | </div> | |
| Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040) | 71 | </div> |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 72 | {errorFor === invitation.id && ( |
| 73 | <div className="mt-2"> | |
| 74 | <ErrorText>{error}</ErrorText> | |
| 75 | </div> | |
| 76 | )} | |
| 77 | </li> | |
| 78 | ))} | |
| 79 | </ul> | |
| 80 | </Card> | |
| Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040) | 81 | ); |
| 82 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.