@burdenoff/microfe-workspaces 2026.530.2 → 2026.531.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/WorkspacesRoutes.js +19 -15
  2. package/dist/WorkspacesRoutes.js.map +1 -1
  3. package/dist/components/ProjectCard.js +2 -2
  4. package/dist/components/ProjectCard.js.map +1 -1
  5. package/dist/components/tags/ProjectTagsWidget.js +53 -57
  6. package/dist/components/tags/ProjectTagsWidget.js.map +1 -1
  7. package/dist/generated/wspace-operations.js +24 -9
  8. package/dist/generated/wspace-operations.js.map +1 -1
  9. package/dist/hooks/index.js +1 -0
  10. package/dist/hooks/useMemberMutations.js +2 -2
  11. package/dist/hooks/useMemberMutations.js.map +1 -1
  12. package/dist/hooks/usePendingInvitations.js +29 -0
  13. package/dist/hooks/usePendingInvitations.js.map +1 -0
  14. package/dist/hooks/useProjectMutations.js +107 -6
  15. package/dist/hooks/useProjectMutations.js.map +1 -1
  16. package/dist/hooks/useProjects.js +1 -1
  17. package/dist/hooks/useProjects.js.map +1 -1
  18. package/dist/hooks/useWorkspaceMembers.js +1 -1
  19. package/dist/hooks/useWorkspaceMembers.js.map +1 -1
  20. package/dist/index.js +20 -18
  21. package/dist/pages/InviteAcceptancePage.js +68 -44
  22. package/dist/pages/InviteAcceptancePage.js.map +1 -1
  23. package/dist/pages/PendingInvitesPage.js +254 -0
  24. package/dist/pages/PendingInvitesPage.js.map +1 -0
  25. package/dist/pages/ProjectDetailPage.js +187 -161
  26. package/dist/pages/ProjectDetailPage.js.map +1 -1
  27. package/dist/pages/ProjectsListPage.js +2 -2
  28. package/dist/pages/ProjectsListPage.js.map +1 -1
  29. package/dist/pages/WorkspacesListPage.js +118 -102
  30. package/dist/pages/WorkspacesListPage.js.map +1 -1
  31. package/dist/utils/projectTags.js +28 -2
  32. package/dist/utils/projectTags.js.map +1 -1
  33. package/package.json +1 -1
@@ -0,0 +1,254 @@
1
+ import { useWorkspacesContext as e } from "../providers/WorkspacesProvider.js";
2
+ import { useMyPendingInvitations as t } from "../hooks/usePendingInvitations.js";
3
+ import { tWithFallback as n } from "../utils/i18n.js";
4
+ import { useWorkspacesPageView as r } from "../hooks/useWorkspacesPageView.js";
5
+ import { useAcceptInvitation as i, useRejectInvitation as a } from "../hooks/useMemberMutations.js";
6
+ import { useMemo as o, useState as s } from "react";
7
+ import { AlertTriangle as c, CheckCircle as l, Home as u, Inbox as d, Mail as f, RefreshCw as p, XCircle as m } from "lucide-react";
8
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
9
+ import { Badge as _, Button as v, PageContainer as y, Skeleton as b } from "@burdenoff/fe-libs/ui";
10
+ import { useNavigate as x } from "react-router-dom";
11
+ import { useI18n as S } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
12
+ import { ActorIdentity as C, useActorProfiles as w } from "@burdenoff/fe-libs/shared";
13
+ //#region src/pages/PendingInvitesPage.tsx
14
+ function T(e) {
15
+ if (!e) return null;
16
+ let t = new Date(e);
17
+ return Number.isNaN(t.getTime()) ? null : t.toLocaleDateString(void 0, {
18
+ year: "numeric",
19
+ month: "short",
20
+ day: "numeric",
21
+ hour: "2-digit",
22
+ minute: "2-digit"
23
+ });
24
+ }
25
+ function E(e) {
26
+ if (e.workspaceName) return e.workspaceName;
27
+ let t = e.context?.metadata?.workspaceName;
28
+ return typeof t == "string" && t.trim().length > 0 ? t : e.workspaceId || "—";
29
+ }
30
+ function D(e) {
31
+ if (Array.isArray(e.roleNames) && e.roleNames.length > 0) return e.roleNames;
32
+ let t = e.context?.metadata?.roleNames;
33
+ return Array.isArray(t) && t.every((e) => typeof e == "string") ? t : e.roleIds;
34
+ }
35
+ function O() {
36
+ r("workspaces.pending-invites");
37
+ let { t: O } = S(), k = x(), { currentUser: A, authToken: j, apiGatewayUrl: M, organizationId: N, basePath: P = "/workspaces" } = e(), [F, I] = s(null), [L, R] = s(null), { data: z = [], isLoading: B, error: V } = t(A?.email), H = o(() => [...z].sort((e, t) => new Date(t.createdAt).getTime() - new Date(e.createdAt).getTime()), [z]), U = w(o(() => H.flatMap((e) => e.invitedById ? [{
38
+ actorId: e.invitedById,
39
+ actorType: "user"
40
+ }] : []), [H]), {
41
+ apiGatewayUrl: M,
42
+ authToken: j,
43
+ orgId: N
44
+ }), { mutate: W, isPending: G } = i(), { mutate: K, isPending: q } = a(), J = G || q, Y = () => {
45
+ k(`${P}/list`);
46
+ }, X = (e) => {
47
+ I(e), R(null), W(e, {
48
+ onSuccess: () => {
49
+ I(null);
50
+ },
51
+ onError: (e) => {
52
+ I(null), R(e instanceof Error ? e.message : n(O, "pendingInvites.acceptError", "Failed to accept invitation"));
53
+ }
54
+ });
55
+ }, Z = (e) => {
56
+ I(e), R(null), K(e, {
57
+ onSuccess: () => {
58
+ I(null);
59
+ },
60
+ onError: (e) => {
61
+ I(null), R(e instanceof Error ? e.message : n(O, "pendingInvites.rejectError", "Failed to reject invitation"));
62
+ }
63
+ });
64
+ };
65
+ return !j || !A?.email ? /* @__PURE__ */ h(y, {
66
+ padded: !1,
67
+ children: /* @__PURE__ */ h("div", {
68
+ className: "space-y-4 sm:space-y-6 p-3 sm:p-6",
69
+ children: /* @__PURE__ */ g("div", {
70
+ className: "rounded-lg border bg-card p-6 text-center",
71
+ children: [
72
+ /* @__PURE__ */ h(c, { className: "mx-auto mb-3 size-10 text-muted-foreground" }),
73
+ /* @__PURE__ */ h("h1", {
74
+ className: "text-2xl font-bold",
75
+ children: n(O, "pendingInvites.signInTitle", "Sign in to view your pending invites")
76
+ }),
77
+ /* @__PURE__ */ h("p", {
78
+ className: "mt-2 text-muted-foreground",
79
+ children: n(O, "pendingInvites.signInDescription", "You need an authenticated account before you can review or respond to workspace invitations.")
80
+ }),
81
+ /* @__PURE__ */ h("div", {
82
+ className: "mt-4 flex justify-center",
83
+ children: /* @__PURE__ */ g(v, {
84
+ onClick: Y,
85
+ variant: "outline",
86
+ children: [/* @__PURE__ */ h(u, { className: "mr-2 size-4" }), n(O, "pendingInvites.backToWorkspaces", "Back to Workspaces")]
87
+ })
88
+ })
89
+ ]
90
+ })
91
+ })
92
+ }) : /* @__PURE__ */ h(y, {
93
+ padded: !1,
94
+ children: /* @__PURE__ */ g("div", {
95
+ className: "space-y-4 sm:space-y-6 p-3 sm:p-6",
96
+ children: [
97
+ /* @__PURE__ */ g("div", {
98
+ className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between",
99
+ children: [/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("h1", {
100
+ className: "text-2xl sm:text-3xl font-bold",
101
+ children: n(O, "pendingInvites.pageTitle", "Pending Invites")
102
+ }), /* @__PURE__ */ h("p", {
103
+ className: "text-sm sm:text-base text-muted-foreground",
104
+ children: n(O, "pendingInvites.pageSubtitle", "Review and respond to workspace invitations sent to your account.")
105
+ })] }), /* @__PURE__ */ g("div", {
106
+ className: "flex items-center gap-2",
107
+ children: [/* @__PURE__ */ h(_, {
108
+ variant: "secondary",
109
+ children: H.length
110
+ }), /* @__PURE__ */ g(v, {
111
+ variant: "outline",
112
+ onClick: Y,
113
+ children: [/* @__PURE__ */ h(u, { className: "mr-2 size-4" }), n(O, "pendingInvites.viewWorkspaces", "View Workspaces")]
114
+ })]
115
+ })]
116
+ }),
117
+ L ? /* @__PURE__ */ g("div", {
118
+ className: "flex items-start gap-2 rounded-lg border border-destructive/30 bg-destructive/10 p-4 text-sm text-destructive",
119
+ children: [/* @__PURE__ */ h(c, { className: "mt-0.5 size-4 shrink-0" }), /* @__PURE__ */ h("span", { children: L })]
120
+ }) : null,
121
+ V ? /* @__PURE__ */ h("div", {
122
+ className: "rounded-lg border border-destructive/30 bg-destructive/10 p-4 text-sm text-destructive",
123
+ children: V instanceof Error ? V.message : n(O, "pendingInvites.loadError", "Failed to load pending invitations")
124
+ }) : null,
125
+ B ? /* @__PURE__ */ h("div", {
126
+ className: "space-y-4",
127
+ children: Array.from({ length: 3 }).map((e, t) => /* @__PURE__ */ g("div", {
128
+ className: "rounded-lg border bg-card p-5 space-y-3",
129
+ children: [
130
+ /* @__PURE__ */ h(b, { className: "h-5 w-40" }),
131
+ /* @__PURE__ */ h(b, { className: "h-4 w-64" }),
132
+ /* @__PURE__ */ h(b, { className: "h-4 w-52" }),
133
+ /* @__PURE__ */ g("div", {
134
+ className: "flex gap-2 pt-2",
135
+ children: [/* @__PURE__ */ h(b, { className: "h-10 w-36" }), /* @__PURE__ */ h(b, { className: "h-10 w-28" })]
136
+ })
137
+ ]
138
+ }, t))
139
+ }) : H.length === 0 ? /* @__PURE__ */ g("div", {
140
+ className: "rounded-lg border bg-card p-10 text-center",
141
+ children: [
142
+ /* @__PURE__ */ h(d, { className: "mx-auto mb-4 size-12 text-muted-foreground" }),
143
+ /* @__PURE__ */ h("h2", {
144
+ className: "text-xl font-semibold",
145
+ children: n(O, "pendingInvites.emptyTitle", "No pending invites")
146
+ }),
147
+ /* @__PURE__ */ h("p", {
148
+ className: "mt-2 text-muted-foreground",
149
+ children: n(O, "pendingInvites.emptyDescription", "When someone invites you to a workspace, it will appear here.")
150
+ })
151
+ ]
152
+ }) : /* @__PURE__ */ h("div", {
153
+ className: "space-y-4",
154
+ children: H.map((e) => {
155
+ let t = e.invitedById ? U[e.invitedById] : void 0, r = t?.displayName || t?.email || e.invitedById || "—", i = T(e.expiresAt), a = T(e.createdAt), o = E(e), s = D(e), c = F === e.id && J;
156
+ return /* @__PURE__ */ h("div", {
157
+ className: "rounded-lg border bg-card p-5",
158
+ children: /* @__PURE__ */ g("div", {
159
+ className: "flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between",
160
+ children: [/* @__PURE__ */ g("div", {
161
+ className: "space-y-3 min-w-0",
162
+ children: [
163
+ /* @__PURE__ */ g("div", {
164
+ className: "flex flex-wrap items-center gap-2",
165
+ children: [/* @__PURE__ */ g("div", {
166
+ className: "flex items-center gap-2 font-semibold text-lg",
167
+ children: [/* @__PURE__ */ h(f, { className: "size-4 text-primary" }), /* @__PURE__ */ h("span", { children: e.email })]
168
+ }), /* @__PURE__ */ h(_, {
169
+ variant: "secondary",
170
+ children: e.status
171
+ })]
172
+ }),
173
+ /* @__PURE__ */ g("dl", {
174
+ className: "grid gap-2 text-sm text-muted-foreground sm:grid-cols-2",
175
+ children: [
176
+ /* @__PURE__ */ g("div", { children: [
177
+ /* @__PURE__ */ h("dt", {
178
+ className: "font-medium text-foreground",
179
+ children: n(O, "pendingInvites.workspaceLabel", "Workspace")
180
+ }),
181
+ /* @__PURE__ */ h("dd", {
182
+ className: "break-words font-medium text-foreground",
183
+ children: o
184
+ }),
185
+ e.workspaceId ? /* @__PURE__ */ h("dd", {
186
+ className: "mt-1 break-all font-mono text-xs text-muted-foreground",
187
+ children: e.workspaceId
188
+ }) : null
189
+ ] }),
190
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("dt", {
191
+ className: "font-medium text-foreground",
192
+ children: n(O, "pendingInvites.invitedByLabel", "Invited by")
193
+ }), /* @__PURE__ */ h("dd", {
194
+ className: "break-all",
195
+ children: t ? /* @__PURE__ */ h(C, {
196
+ profile: t,
197
+ variant: "inline",
198
+ hideIdDrilldown: !0
199
+ }) : /* @__PURE__ */ h("span", {
200
+ className: "font-medium text-foreground",
201
+ children: r
202
+ })
203
+ })] }),
204
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("dt", {
205
+ className: "font-medium text-foreground",
206
+ children: n(O, "pendingInvites.receivedLabel", "Received")
207
+ }), /* @__PURE__ */ h("dd", { children: a ?? "—" })] }),
208
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("dt", {
209
+ className: "font-medium text-foreground",
210
+ children: n(O, "pendingInvites.expiresLabel", "Expires")
211
+ }), /* @__PURE__ */ h("dd", { children: i ?? "—" })] })
212
+ ]
213
+ }),
214
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("p", {
215
+ className: "text-sm font-medium text-foreground mb-2",
216
+ children: n(O, "pendingInvites.rolesLabel", "Roles")
217
+ }), /* @__PURE__ */ h("div", {
218
+ className: "flex flex-wrap gap-2",
219
+ children: s.length > 0 ? s.map((t, n) => /* @__PURE__ */ h(_, {
220
+ variant: "outline",
221
+ children: t
222
+ }, `${e.id}-role-${t}-${n}`)) : /* @__PURE__ */ h("span", {
223
+ className: "text-sm text-muted-foreground",
224
+ children: n(O, "pendingInvites.noRoles", "No explicit roles assigned")
225
+ })
226
+ })] })
227
+ ]
228
+ }), /* @__PURE__ */ g("div", {
229
+ className: "flex flex-col gap-2 sm:flex-row lg:flex-col lg:min-w-44",
230
+ children: [/* @__PURE__ */ g(v, {
231
+ onClick: () => X(e.id),
232
+ disabled: J,
233
+ className: "w-full",
234
+ children: [c && G ? /* @__PURE__ */ h(p, { className: "mr-2 size-4 animate-spin" }) : /* @__PURE__ */ h(l, { className: "mr-2 size-4" }), n(O, "pendingInvites.acceptButton", "Accept")]
235
+ }), /* @__PURE__ */ g(v, {
236
+ variant: "outline",
237
+ onClick: () => Z(e.id),
238
+ disabled: J,
239
+ className: "w-full",
240
+ children: [c && q ? /* @__PURE__ */ h(p, { className: "mr-2 size-4 animate-spin" }) : /* @__PURE__ */ h(m, { className: "mr-2 size-4" }), n(O, "pendingInvites.rejectButton", "Reject")]
241
+ })]
242
+ })]
243
+ })
244
+ }, e.id);
245
+ })
246
+ })
247
+ ]
248
+ })
249
+ });
250
+ }
251
+ //#endregion
252
+ export { O as PendingInvitesPage };
253
+
254
+ //# sourceMappingURL=PendingInvitesPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PendingInvitesPage.js","names":[],"sources":["../../src/pages/PendingInvitesPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { Mail, RefreshCw, CheckCircle, XCircle, Inbox, AlertTriangle, Home } from 'lucide-react';\nimport {\n ActorIdentity,\n useActorProfiles,\n type ActorInput,\n type ActorProfileMap,\n} from '@burdenoff/fe-libs/shared';\nimport { Badge, Button, PageContainer, Skeleton } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { useAcceptInvitation, useRejectInvitation } from '../hooks/useMemberMutations';\nimport { useMyPendingInvitations } from '../hooks/usePendingInvitations';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\n\nfunction formatDate(value?: string) {\n if (!value) return null;\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return null;\n\n return date.toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nfunction getWorkspaceDisplayName(invitation: {\n workspaceName?: string;\n workspaceId?: string;\n context?: { metadata?: Record<string, unknown> | null } | null;\n}) {\n if (invitation.workspaceName) {\n return invitation.workspaceName;\n }\n\n const metadataWorkspaceName = invitation.context?.metadata?.workspaceName;\n if (typeof metadataWorkspaceName === 'string' && metadataWorkspaceName.trim().length > 0) {\n return metadataWorkspaceName;\n }\n\n return invitation.workspaceId || '—';\n}\n\nfunction getRoleDisplayNames(invitation: {\n roleNames?: string[];\n roleIds: string[];\n context?: { metadata?: Record<string, unknown> | null } | null;\n}) {\n if (Array.isArray(invitation.roleNames) && invitation.roleNames.length > 0) {\n return invitation.roleNames;\n }\n\n const metadataRoleNames = invitation.context?.metadata?.roleNames;\n if (\n Array.isArray(metadataRoleNames) &&\n metadataRoleNames.every((value) => typeof value === 'string')\n ) {\n return metadataRoleNames;\n }\n\n return invitation.roleIds;\n}\n\nexport function PendingInvitesPage() {\n useWorkspacesPageView('workspaces.pending-invites');\n\n const { t } = useI18n();\n const navigate = useNavigate();\n const {\n currentUser,\n authToken,\n apiGatewayUrl,\n organizationId,\n basePath = '/workspaces',\n } = useWorkspacesContext();\n const [processingInviteId, setProcessingInviteId] = useState<string | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n\n const {\n data: pendingInvitations = [],\n isLoading,\n error,\n } = useMyPendingInvitations(currentUser?.email);\n\n const sortedInvitations = useMemo(\n () =>\n [...pendingInvitations].sort(\n (left, right) => new Date(right.createdAt).getTime() - new Date(left.createdAt).getTime()\n ),\n [pendingInvitations]\n );\n\n const inviterActors = useMemo<ActorInput[]>(\n () =>\n sortedInvitations.flatMap((invitation) =>\n invitation.invitedById\n ? [{ actorId: invitation.invitedById, actorType: 'user' as const }]\n : []\n ),\n [sortedInvitations]\n );\n\n const inviterProfiles: ActorProfileMap = useActorProfiles(inviterActors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n });\n\n const { mutate: acceptInvitation, isPending: isAccepting } = useAcceptInvitation();\n const { mutate: rejectInvitation, isPending: isRejecting } = useRejectInvitation();\n\n const isMutating = isAccepting || isRejecting;\n\n const handleBackToWorkspaces = () => {\n navigate(`${basePath}/list`);\n };\n\n const handleAccept = (invitationId: string) => {\n setProcessingInviteId(invitationId);\n setActionError(null);\n\n acceptInvitation(invitationId, {\n onSuccess: () => {\n setProcessingInviteId(null);\n },\n onError: (mutationError) => {\n setProcessingInviteId(null);\n setActionError(\n mutationError instanceof Error\n ? mutationError.message\n : tWithFallback(t, 'pendingInvites.acceptError', 'Failed to accept invitation')\n );\n },\n });\n };\n\n const handleReject = (invitationId: string) => {\n setProcessingInviteId(invitationId);\n setActionError(null);\n\n rejectInvitation(invitationId, {\n onSuccess: () => {\n setProcessingInviteId(null);\n },\n onError: (mutationError) => {\n setProcessingInviteId(null);\n setActionError(\n mutationError instanceof Error\n ? mutationError.message\n : tWithFallback(t, 'pendingInvites.rejectError', 'Failed to reject invitation')\n );\n },\n });\n };\n\n if (!authToken || !currentUser?.email) {\n return (\n <PageContainer padded={false}>\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n <div className=\"rounded-lg border bg-card p-6 text-center\">\n <AlertTriangle className=\"mx-auto mb-3 size-10 text-muted-foreground\" />\n <h1 className=\"text-2xl font-bold\">\n {tWithFallback(\n t,\n 'pendingInvites.signInTitle',\n 'Sign in to view your pending invites'\n )}\n </h1>\n <p className=\"mt-2 text-muted-foreground\">\n {tWithFallback(\n t,\n 'pendingInvites.signInDescription',\n 'You need an authenticated account before you can review or respond to workspace invitations.'\n )}\n </p>\n <div className=\"mt-4 flex justify-center\">\n <Button onClick={handleBackToWorkspaces} variant=\"outline\">\n <Home className=\"mr-2 size-4\" />\n {tWithFallback(t, 'pendingInvites.backToWorkspaces', 'Back to Workspaces')}\n </Button>\n </div>\n </div>\n </div>\n </PageContainer>\n );\n }\n\n return (\n <PageContainer padded={false}>\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">\n {tWithFallback(t, 'pendingInvites.pageTitle', 'Pending Invites')}\n </h1>\n <p className=\"text-sm sm:text-base text-muted-foreground\">\n {tWithFallback(\n t,\n 'pendingInvites.pageSubtitle',\n 'Review and respond to workspace invitations sent to your account.'\n )}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"secondary\">{sortedInvitations.length}</Badge>\n <Button variant=\"outline\" onClick={handleBackToWorkspaces}>\n <Home className=\"mr-2 size-4\" />\n {tWithFallback(t, 'pendingInvites.viewWorkspaces', 'View Workspaces')}\n </Button>\n </div>\n </div>\n\n {actionError ? (\n <div className=\"flex items-start gap-2 rounded-lg border border-destructive/30 bg-destructive/10 p-4 text-sm text-destructive\">\n <AlertTriangle className=\"mt-0.5 size-4 shrink-0\" />\n <span>{actionError}</span>\n </div>\n ) : null}\n\n {error ? (\n <div className=\"rounded-lg border border-destructive/30 bg-destructive/10 p-4 text-sm text-destructive\">\n {error instanceof Error\n ? error.message\n : tWithFallback(t, 'pendingInvites.loadError', 'Failed to load pending invitations')}\n </div>\n ) : null}\n\n {isLoading ? (\n <div className=\"space-y-4\">\n {Array.from({ length: 3 }).map((_, index) => (\n <div key={index} className=\"rounded-lg border bg-card p-5 space-y-3\">\n <Skeleton className=\"h-5 w-40\" />\n <Skeleton className=\"h-4 w-64\" />\n <Skeleton className=\"h-4 w-52\" />\n <div className=\"flex gap-2 pt-2\">\n <Skeleton className=\"h-10 w-36\" />\n <Skeleton className=\"h-10 w-28\" />\n </div>\n </div>\n ))}\n </div>\n ) : sortedInvitations.length === 0 ? (\n <div className=\"rounded-lg border bg-card p-10 text-center\">\n <Inbox className=\"mx-auto mb-4 size-12 text-muted-foreground\" />\n <h2 className=\"text-xl font-semibold\">\n {tWithFallback(t, 'pendingInvites.emptyTitle', 'No pending invites')}\n </h2>\n <p className=\"mt-2 text-muted-foreground\">\n {tWithFallback(\n t,\n 'pendingInvites.emptyDescription',\n 'When someone invites you to a workspace, it will appear here.'\n )}\n </p>\n </div>\n ) : (\n <div className=\"space-y-4\">\n {sortedInvitations.map((invitation) => {\n const inviterProfile = invitation.invitedById\n ? inviterProfiles[invitation.invitedById]\n : undefined;\n const inviterLabel =\n inviterProfile?.displayName ||\n inviterProfile?.email ||\n invitation.invitedById ||\n '—';\n const expiresAt = formatDate(invitation.expiresAt);\n const createdAt = formatDate(invitation.createdAt);\n const workspaceDisplayName = getWorkspaceDisplayName(invitation);\n const roleDisplayNames = getRoleDisplayNames(invitation);\n const isProcessingThisInvite = processingInviteId === invitation.id && isMutating;\n\n return (\n <div key={invitation.id} className=\"rounded-lg border bg-card p-5\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"space-y-3 min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <div className=\"flex items-center gap-2 font-semibold text-lg\">\n <Mail className=\"size-4 text-primary\" />\n <span>{invitation.email}</span>\n </div>\n <Badge variant=\"secondary\">{invitation.status}</Badge>\n </div>\n\n <dl className=\"grid gap-2 text-sm text-muted-foreground sm:grid-cols-2\">\n <div>\n <dt className=\"font-medium text-foreground\">\n {tWithFallback(t, 'pendingInvites.workspaceLabel', 'Workspace')}\n </dt>\n <dd className=\"break-words font-medium text-foreground\">\n {workspaceDisplayName}\n </dd>\n {invitation.workspaceId ? (\n <dd className=\"mt-1 break-all font-mono text-xs text-muted-foreground\">\n {invitation.workspaceId}\n </dd>\n ) : null}\n </div>\n <div>\n <dt className=\"font-medium text-foreground\">\n {tWithFallback(t, 'pendingInvites.invitedByLabel', 'Invited by')}\n </dt>\n <dd className=\"break-all\">\n {inviterProfile ? (\n <ActorIdentity\n profile={inviterProfile}\n variant=\"inline\"\n hideIdDrilldown\n />\n ) : (\n <span className=\"font-medium text-foreground\">{inviterLabel}</span>\n )}\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-foreground\">\n {tWithFallback(t, 'pendingInvites.receivedLabel', 'Received')}\n </dt>\n <dd>{createdAt ?? '—'}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-foreground\">\n {tWithFallback(t, 'pendingInvites.expiresLabel', 'Expires')}\n </dt>\n <dd>{expiresAt ?? '—'}</dd>\n </div>\n </dl>\n\n <div>\n <p className=\"text-sm font-medium text-foreground mb-2\">\n {tWithFallback(t, 'pendingInvites.rolesLabel', 'Roles')}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {roleDisplayNames.length > 0 ? (\n roleDisplayNames.map((roleName, index) => (\n <Badge\n key={`${invitation.id}-role-${roleName}-${index}`}\n variant=\"outline\"\n >\n {roleName}\n </Badge>\n ))\n ) : (\n <span className=\"text-sm text-muted-foreground\">\n {tWithFallback(\n t,\n 'pendingInvites.noRoles',\n 'No explicit roles assigned'\n )}\n </span>\n )}\n </div>\n </div>\n </div>\n\n <div className=\"flex flex-col gap-2 sm:flex-row lg:flex-col lg:min-w-44\">\n <Button\n onClick={() => handleAccept(invitation.id)}\n disabled={isMutating}\n className=\"w-full\"\n >\n {isProcessingThisInvite && isAccepting ? (\n <RefreshCw className=\"mr-2 size-4 animate-spin\" />\n ) : (\n <CheckCircle className=\"mr-2 size-4\" />\n )}\n {tWithFallback(t, 'pendingInvites.acceptButton', 'Accept')}\n </Button>\n <Button\n variant=\"outline\"\n onClick={() => handleReject(invitation.id)}\n disabled={isMutating}\n className=\"w-full\"\n >\n {isProcessingThisInvite && isRejecting ? (\n <RefreshCw className=\"mr-2 size-4 animate-spin\" />\n ) : (\n <XCircle className=\"mr-2 size-4\" />\n )}\n {tWithFallback(t, 'pendingInvites.rejectButton', 'Reject')}\n </Button>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n )}\n </div>\n </PageContainer>\n );\n}\n"],"mappings":"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"}