Skip to content
78 linesCodeBlameRaw
1import { Form } from "react-router";
2
3import type { WorkspaceInvitation } from "@g1t/contracts";
4
5import { Avatar, ErrorText, SubmitButton } from "./ui";
6import { UserCard } from "./user-card";
7import { invitationLine } from "../lib/invitations";
8
9/**
10 * The workspace invitations waiting for the person's answer, each with
11 * Accept and Decline. Accepting joins with the role it names; declining
12 * tells whoever sent it. `next` is where accepting goes, when not to the
13 * workspace itself.
14 */
15export function InvitationList({
16 invitations,
17 error,
18 errorFor,
19 next,
20}: {
21 invitations: WorkspaceInvitation[];
22 error?: string | null;
23 errorFor?: string | null;
24 next?: string | null;
25}) {
26 return (
27 <ul className="divide-y divide-line rounded-xl border border-line">
28 {invitations.map((invitation) => (
29 <li key={invitation.id} className="px-4 py-4">
30 <div className="flex flex-wrap items-center gap-3">
31 <Avatar name={invitation.workspace.slug} image={invitation.workspace.avatar} size={32} square />
32 <div className="min-w-0 grow basis-48">
33 <p className="text-sm font-medium">
34 {invitation.workspace.name}{" "}
35 <span className="font-mono text-xs font-normal text-faint">{invitation.workspace.slug}</span>
36 </p>
37 <p className="mt-0.5 text-xs text-muted">
38 {invitation.invitedBy ? (
39 <>
40 <UserCard username={invitation.invitedBy.username}>
41 <span className="font-mono text-fg/90">@{invitation.invitedBy.username}</span>
42 </UserCard>{" "}
43 </>
44 ) : (
45 "The g1t team "
46 )}
47 {invitationLine(invitation)}
48 </p>
49 </div>
50 <div className="flex items-center gap-2">
51 <Form method="post">
52 <input type="hidden" name="intent" value="decline-invitation" />
53 <input type="hidden" name="id" value={invitation.id} />
54 <input type="hidden" name="workspace" value={invitation.workspace.name} />
55 <SubmitButton variant="quiet" match={{ intent: "decline-invitation", id: invitation.id }} pending="Declining…">
56 Decline
57 </SubmitButton>
58 </Form>
59 <Form method="post">
60 <input type="hidden" name="intent" value="accept-invitation" />
61 <input type="hidden" name="id" value={invitation.id} />
62 {next && <input type="hidden" name="next" value={next} />}
63 <SubmitButton match={{ intent: "accept-invitation", id: invitation.id }} pending="Joining…">
64 Accept
65 </SubmitButton>
66 </Form>
67 </div>
68 </div>
69 {errorFor === invitation.id && (
70 <div className="mt-2">
71 <ErrorText>{error}</ErrorText>
72 </div>
73 )}
74 </li>
75 ))}
76 </ul>
77 );
78}