@burdenoff/microfe-workspaces 2026.625.4 → 2026.626.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.
- package/dist/hooks/useMemberMutations.js +6 -6
- package/dist/hooks/useMemberMutations.js.map +1 -1
- package/dist/pages/ActivityListPage.js +94 -90
- package/dist/pages/ActivityListPage.js.map +1 -1
- package/dist/pages/CreateWorkspacePage.js +68 -63
- package/dist/pages/CreateWorkspacePage.js.map +1 -1
- package/dist/pages/InviteAcceptancePage.js +101 -96
- package/dist/pages/InviteAcceptancePage.js.map +1 -1
- package/dist/pages/MembersListPage.js +185 -184
- package/dist/pages/MembersListPage.js.map +1 -1
- package/dist/pages/PendingInvitesPage.js +70 -68
- package/dist/pages/PendingInvitesPage.js.map +1 -1
- package/dist/pages/ProjectDetailPage.js +119 -78
- package/dist/pages/ProjectDetailPage.js.map +1 -1
- package/dist/pages/ProjectMembersPage.js +105 -104
- package/dist/pages/ProjectMembersPage.js.map +1 -1
- package/dist/pages/ProjectsListPage.js +158 -157
- package/dist/pages/ProjectsListPage.js.map +1 -1
- package/dist/pages/WorkspaceDetailPage.js +236 -198
- package/dist/pages/WorkspaceDetailPage.js.map +1 -1
- package/dist/pages/WorkspaceInvitePage.js +341 -300
- package/dist/pages/WorkspaceInvitePage.js.map +1 -1
- package/dist/pages/WorkspaceSettingsPage.js +87 -85
- package/dist/pages/WorkspaceSettingsPage.js.map +1 -1
- package/dist/pages/WorkspacesListPage.js +122 -121
- package/dist/pages/WorkspacesListPage.js.map +1 -1
- package/dist/pages/dashboard/DashboardPage.js +55 -51
- package/dist/pages/dashboard/DashboardPage.js.map +1 -1
- package/dist/pages/dashboard/widgets/GroupsWidget.js +16 -17
- package/dist/pages/dashboard/widgets/GroupsWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/TagCloudWidget.js +18 -19
- package/dist/pages/dashboard/widgets/TagCloudWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/WorkflowStatsWidget.js +10 -11
- package/dist/pages/dashboard/widgets/WorkflowStatsWidget.js.map +1 -1
- package/package.json +2 -2
|
@@ -2,23 +2,23 @@ import { useWorkspacesContext as e } from "../providers/WorkspacesProvider.js";
|
|
|
2
2
|
import { useScopedNavigation as t } from "../hooks/useScopedNavigation.js";
|
|
3
3
|
import { tWithFallback as n } from "../utils/i18n.js";
|
|
4
4
|
import { useWorkspacesPageView as r } from "../hooks/useWorkspacesPageView.js";
|
|
5
|
-
import { nativeConfirm as i, nativeImpact as
|
|
6
|
-
import { useWorkspacePermissions as
|
|
7
|
-
import { useProject as
|
|
8
|
-
import { formatRelativeTime as
|
|
9
|
-
import { useRemoveProjectMember as
|
|
10
|
-
import { useInfiniteProjectMembers as
|
|
11
|
-
import { ProjectMemberPickerDialog as
|
|
12
|
-
import { useMemo as
|
|
13
|
-
import { ArrowLeft as
|
|
14
|
-
import { jsx as
|
|
15
|
-
import { Badge as
|
|
16
|
-
import { useNavigate as
|
|
17
|
-
import { useI18n as
|
|
18
|
-
import { ActorIdentity as
|
|
5
|
+
import { nativeConfirm as i, nativeImpact as a, nativeNotify as o } from "../utils/nativeBridge.js";
|
|
6
|
+
import { useWorkspacePermissions as s } from "../hooks/useWorkspacePermissions.js";
|
|
7
|
+
import { useProject as ee } from "../hooks/useProjects.js";
|
|
8
|
+
import { formatRelativeTime as te } from "../utils/formatters.js";
|
|
9
|
+
import { useRemoveProjectMember as c } from "../hooks/useProjectMemberMutations.js";
|
|
10
|
+
import { useInfiniteProjectMembers as l } from "../hooks/useProjectMembers.js";
|
|
11
|
+
import { ProjectMemberPickerDialog as u } from "../components/ProjectMemberPickerDialog.js";
|
|
12
|
+
import { useMemo as d, useState as f } from "react";
|
|
13
|
+
import { ArrowLeft as ne, Search as re, UserMinus as ie, UserPlus as p } from "lucide-react";
|
|
14
|
+
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
15
|
+
import { Badge as g, Button as _, IllustratedEmptyState as v, Input as y, PageContainer as b, PagePurpose as x, Skeleton as S } from "@burdenoff/fe-libs/ui";
|
|
16
|
+
import { useNavigate as ae, useParams as oe } from "react-router-dom";
|
|
17
|
+
import { useI18n as se } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
18
|
+
import { ActorIdentity as ce, useActorProfiles as le } from "@burdenoff/fe-libs/shared";
|
|
19
19
|
//#region src/pages/ProjectMembersPage.tsx
|
|
20
|
-
var
|
|
21
|
-
function
|
|
20
|
+
var ue = 100;
|
|
21
|
+
function C(e, t) {
|
|
22
22
|
let n = e[t.userId];
|
|
23
23
|
return n ? {
|
|
24
24
|
name: n.displayName,
|
|
@@ -28,25 +28,25 @@ function w(e, t) {
|
|
|
28
28
|
email: void 0
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
-
function
|
|
32
|
-
let { t:
|
|
31
|
+
function w() {
|
|
32
|
+
let { t: w } = se(), de = ae(), { workspaceId: T, projectId: E } = oe(), { basePath: fe = "/workspaces", apiGatewayUrl: D, authToken: O, organizationId: k } = e(), { withCurrentSearch: A } = t(), [j, M] = f(""), [N, P] = f(!1), [F, I] = f(null), [L, R] = f(null);
|
|
33
33
|
r("workspaces.project-members", {
|
|
34
|
-
workspaceId:
|
|
35
|
-
projectId:
|
|
34
|
+
workspaceId: T,
|
|
35
|
+
projectId: E,
|
|
36
36
|
requireParams: !0
|
|
37
37
|
});
|
|
38
|
-
let { data: z, isLoading: B, error: V } =
|
|
38
|
+
let { data: z, isLoading: B, error: V } = ee(E || "", T), { data: H, isLoading: pe, error: U, hasNextPage: W, fetchNextPage: G, isFetchingNextPage: K } = l(E || "", T, ue), { mutate: me, isPending: q } = c(T), { canViewProject: he, canListProjectMembers: ge, canAddProjectMember: J, canRemoveProjectMember: _e, isLoading: Y } = s(), X = d(() => H?.pages.flatMap((e) => e.items) ?? [], [H?.pages]), Z = H?.pages[0]?.total ?? X.length, Q = le(d(() => X.map((e) => ({
|
|
39
39
|
actorId: e.userId,
|
|
40
40
|
actorType: "user"
|
|
41
41
|
})), [X]), {
|
|
42
|
-
apiGatewayUrl:
|
|
42
|
+
apiGatewayUrl: D,
|
|
43
43
|
authToken: O,
|
|
44
44
|
orgId: k,
|
|
45
|
-
workspaceId:
|
|
46
|
-
}), $ =
|
|
45
|
+
workspaceId: T
|
|
46
|
+
}), $ = d(() => {
|
|
47
47
|
let e = j.trim().toLowerCase();
|
|
48
48
|
return e ? X.filter((t) => {
|
|
49
|
-
let n =
|
|
49
|
+
let n = C(Q, t);
|
|
50
50
|
return n.name.toLowerCase().includes(e) || n.email?.toLowerCase().includes(e) || t.userId.toLowerCase().includes(e);
|
|
51
51
|
}) : X;
|
|
52
52
|
}, [
|
|
@@ -54,171 +54,172 @@ function T() {
|
|
|
54
54
|
X,
|
|
55
55
|
j
|
|
56
56
|
]);
|
|
57
|
-
if (Y) return /* @__PURE__ */
|
|
57
|
+
if (Y) return /* @__PURE__ */ h("div", {
|
|
58
58
|
className: "space-y-4 p-6",
|
|
59
59
|
children: [
|
|
60
|
-
/* @__PURE__ */
|
|
61
|
-
/* @__PURE__ */
|
|
62
|
-
/* @__PURE__ */
|
|
60
|
+
/* @__PURE__ */ m(S, { className: "h-10 w-40" }),
|
|
61
|
+
/* @__PURE__ */ m(S, { className: "h-10 w-64" }),
|
|
62
|
+
/* @__PURE__ */ m(S, { className: "h-40 w-full" })
|
|
63
63
|
]
|
|
64
64
|
});
|
|
65
|
-
if (!
|
|
65
|
+
if (!he || !ge) return /* @__PURE__ */ m("div", {
|
|
66
66
|
className: "flex min-h-[400px] items-center justify-center",
|
|
67
|
-
children: /* @__PURE__ */
|
|
67
|
+
children: /* @__PURE__ */ h("div", {
|
|
68
68
|
className: "space-y-2 text-center",
|
|
69
|
-
children: [/* @__PURE__ */
|
|
69
|
+
children: [/* @__PURE__ */ m("h2", {
|
|
70
70
|
className: "text-lg font-semibold",
|
|
71
|
-
children: n(
|
|
72
|
-
}), /* @__PURE__ */
|
|
71
|
+
children: n(w, "projectMembers.accessDenied", "Access Denied")
|
|
72
|
+
}), /* @__PURE__ */ m("p", {
|
|
73
73
|
className: "text-sm text-text-secondary",
|
|
74
|
-
children: n(
|
|
74
|
+
children: n(w, "projectMembers.noPermission", "You don't have permission to manage project members.")
|
|
75
75
|
})]
|
|
76
76
|
})
|
|
77
77
|
});
|
|
78
|
-
let
|
|
79
|
-
|
|
80
|
-
},
|
|
81
|
-
let t =
|
|
82
|
-
|
|
83
|
-
let r = `${n(
|
|
84
|
-
title: n(
|
|
78
|
+
let ve = () => {
|
|
79
|
+
de(A(`${fe}/${T}/projects/${E}`));
|
|
80
|
+
}, ye = async (e) => {
|
|
81
|
+
let t = C(Q, e);
|
|
82
|
+
a("medium"), R(null);
|
|
83
|
+
let r = `${n(w, "projectMembers.removeConfirmPrefix", "Remove")} ${t.name} ${n(w, "projectMembers.removeConfirmSuffix", "from this project?")}`, s = await i({
|
|
84
|
+
title: n(w, "projectMembers.removeConfirmTitle", "Remove project member"),
|
|
85
85
|
message: r,
|
|
86
|
-
okButtonTitle: n(
|
|
87
|
-
cancelButtonTitle: n(
|
|
86
|
+
okButtonTitle: n(w, "projectMembers.removeAction", "Remove"),
|
|
87
|
+
cancelButtonTitle: n(w, "projectMembers.cancel", "Cancel")
|
|
88
88
|
});
|
|
89
|
-
(
|
|
89
|
+
(s === null ? window.confirm(r) : s) && (I(e.userId), me({
|
|
90
90
|
projectId: e.projectId,
|
|
91
91
|
userId: e.userId
|
|
92
92
|
}, {
|
|
93
93
|
onSuccess: () => {
|
|
94
|
-
|
|
94
|
+
o("success"), I(null);
|
|
95
95
|
},
|
|
96
96
|
onError: () => {
|
|
97
|
-
|
|
97
|
+
o("error"), R(n(w, "projectMembers.removeError", "Failed to remove project member. Please try again.")), I(null);
|
|
98
98
|
}
|
|
99
99
|
}));
|
|
100
100
|
};
|
|
101
|
-
return /* @__PURE__ */
|
|
101
|
+
return /* @__PURE__ */ m(b, {
|
|
102
102
|
padded: !1,
|
|
103
|
-
children: /* @__PURE__ */
|
|
103
|
+
children: /* @__PURE__ */ h("div", {
|
|
104
104
|
className: "space-y-4 p-3 sm:space-y-6 sm:p-6",
|
|
105
105
|
children: [
|
|
106
|
-
/* @__PURE__ */
|
|
106
|
+
/* @__PURE__ */ h("div", {
|
|
107
107
|
className: "flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between",
|
|
108
|
-
children: [/* @__PURE__ */
|
|
109
|
-
/* @__PURE__ */
|
|
108
|
+
children: [/* @__PURE__ */ h("div", { children: [
|
|
109
|
+
/* @__PURE__ */ h(_, {
|
|
110
110
|
variant: "ghost",
|
|
111
|
-
onClick:
|
|
111
|
+
onClick: ve,
|
|
112
112
|
className: "mb-2 -ml-2 gap-2",
|
|
113
|
-
children: [/* @__PURE__ */
|
|
113
|
+
children: [/* @__PURE__ */ m(ne, { size: 20 }), n(w, "projectMembers.backToProject", "Back to Project")]
|
|
114
114
|
}),
|
|
115
|
-
/* @__PURE__ */
|
|
115
|
+
/* @__PURE__ */ m("h1", {
|
|
116
116
|
className: "text-2xl font-bold sm:text-3xl",
|
|
117
|
-
children: n(
|
|
117
|
+
children: n(w, "projectMembers.pageTitle", "Project Members")
|
|
118
118
|
}),
|
|
119
|
-
/* @__PURE__ */
|
|
119
|
+
/* @__PURE__ */ m("p", {
|
|
120
120
|
className: "text-sm text-text-secondary sm:text-base",
|
|
121
|
-
children: z?.name ? `${z.name} · ${n(
|
|
121
|
+
children: z?.name ? `${z.name} · ${n(w, "projectMembers.pageSubtitle", "Only existing workspace members can be added to this project.")}` : n(w, "projectMembers.pageSubtitle", "Only existing workspace members can be added to this project.")
|
|
122
122
|
})
|
|
123
|
-
] }), /* @__PURE__ */
|
|
123
|
+
] }), /* @__PURE__ */ h("div", {
|
|
124
124
|
className: "flex items-center gap-2",
|
|
125
|
-
children: [/* @__PURE__ */
|
|
125
|
+
children: [/* @__PURE__ */ m(g, {
|
|
126
126
|
variant: "secondary",
|
|
127
127
|
children: Z
|
|
128
|
-
}), J ? /* @__PURE__ */
|
|
128
|
+
}), J ? /* @__PURE__ */ h(_, {
|
|
129
129
|
onClick: () => P(!0),
|
|
130
|
-
disabled: !
|
|
131
|
-
children: [/* @__PURE__ */
|
|
130
|
+
disabled: !T || !E,
|
|
131
|
+
children: [/* @__PURE__ */ m(p, { className: "mr-2 size-4" }), n(w, "projectMembers.addMember", "Add Member")]
|
|
132
132
|
}) : null]
|
|
133
133
|
})]
|
|
134
134
|
}),
|
|
135
|
-
/* @__PURE__ */
|
|
135
|
+
/* @__PURE__ */ m(x, { children: n(w, "projectMembers.purpose", "This is the subset of workspace members who can work on this specific project. Add people here to give them project-level access, search to find someone, or remove members who have moved on. Only existing workspace members can be added.") }),
|
|
136
|
+
/* @__PURE__ */ h("div", {
|
|
136
137
|
className: "relative",
|
|
137
|
-
children: [/* @__PURE__ */
|
|
138
|
+
children: [/* @__PURE__ */ m(re, { className: "absolute left-3 top-1/2 size-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ m(y, {
|
|
138
139
|
value: j,
|
|
139
140
|
onChange: (e) => M(e.target.value),
|
|
140
|
-
placeholder: n(
|
|
141
|
+
placeholder: n(w, "projectMembers.searchPlaceholder", "Search project members"),
|
|
141
142
|
className: "pl-9"
|
|
142
143
|
})]
|
|
143
144
|
}),
|
|
144
|
-
L ? /* @__PURE__ */
|
|
145
|
+
L ? /* @__PURE__ */ m("div", {
|
|
145
146
|
className: "rounded-lg border border-status-error-border/20 bg-status-error-bg/10 p-4 text-sm text-status-error-text",
|
|
146
147
|
children: L
|
|
147
148
|
}) : null,
|
|
148
|
-
B ||
|
|
149
|
+
B || pe ? /* @__PURE__ */ m("div", {
|
|
149
150
|
className: "space-y-4",
|
|
150
|
-
children: Array.from({ length: 4 }).map((e, t) => /* @__PURE__ */
|
|
151
|
+
children: Array.from({ length: 4 }).map((e, t) => /* @__PURE__ */ h("div", {
|
|
151
152
|
className: "rounded-lg border border-border-seam bg-bg-surface p-4",
|
|
152
|
-
children: [/* @__PURE__ */
|
|
153
|
+
children: [/* @__PURE__ */ m(S, { className: "h-5 w-40" }), /* @__PURE__ */ m(S, { className: "mt-2 h-4 w-56" })]
|
|
153
154
|
}, t))
|
|
154
|
-
}) : V || U ? /* @__PURE__ */
|
|
155
|
+
}) : V || U ? /* @__PURE__ */ m("div", {
|
|
155
156
|
className: "rounded-lg border border-status-error-border/20 bg-status-error-bg/10 p-4 text-sm text-status-error-text",
|
|
156
|
-
children: U instanceof Error && U.message || V instanceof Error && V.message || n(
|
|
157
|
-
}) : $.length === 0 ? /* @__PURE__ */
|
|
157
|
+
children: U instanceof Error && U.message || V instanceof Error && V.message || n(w, "projectMembers.loadError", "Failed to load project members")
|
|
158
|
+
}) : $.length === 0 ? /* @__PURE__ */ m(v, {
|
|
158
159
|
illustration: j ? "empty-search" : "empty-people",
|
|
159
|
-
title: j ? n(
|
|
160
|
-
description: j ? n(
|
|
161
|
-
action: W || !j && J ? /* @__PURE__ */
|
|
160
|
+
title: j ? n(w, "projectMembers.noResultsTitle", "No matching members found") : n(w, "projectMembers.emptyTitle", "No project members yet"),
|
|
161
|
+
description: j ? n(w, "projectMembers.noResultsSubtitle", "Try a different search to find a project member.") : n(w, "projectMembers.emptySubtitle", "Add workspace members to this project so they can collaborate here."),
|
|
162
|
+
action: W || !j && J ? /* @__PURE__ */ h("div", {
|
|
162
163
|
className: "flex flex-wrap items-center justify-center gap-2",
|
|
163
|
-
children: [W ? /* @__PURE__ */
|
|
164
|
+
children: [W ? /* @__PURE__ */ m(_, {
|
|
164
165
|
variant: "outline",
|
|
165
166
|
onClick: () => void G(),
|
|
166
167
|
disabled: K,
|
|
167
168
|
children: K ? "Loading more…" : "Load More Members"
|
|
168
|
-
}) : null, !j && J ? /* @__PURE__ */
|
|
169
|
+
}) : null, !j && J ? /* @__PURE__ */ h(_, {
|
|
169
170
|
onClick: () => P(!0),
|
|
170
|
-
children: [/* @__PURE__ */
|
|
171
|
+
children: [/* @__PURE__ */ m(p, { className: "mr-2 size-4" }), n(w, "projectMembers.addFirstMember", "Add First Member")]
|
|
171
172
|
}) : null]
|
|
172
173
|
}) : void 0
|
|
173
|
-
}) : /* @__PURE__ */
|
|
174
|
+
}) : /* @__PURE__ */ h("div", {
|
|
174
175
|
className: "space-y-3",
|
|
175
176
|
children: [$.map((e) => {
|
|
176
|
-
let t = Q[e.userId], r =
|
|
177
|
-
return /* @__PURE__ */
|
|
177
|
+
let t = Q[e.userId], r = C(Q, e), i = q && F === e.userId;
|
|
178
|
+
return /* @__PURE__ */ h("div", {
|
|
178
179
|
className: "flex items-center justify-between gap-4 rounded-card border border-border-seam bg-bg-surface p-4 shadow-elevation-1 transition-[box-shadow,transform] duration-200 ease-[var(--motion-easing-out)] hover:shadow-elevation-2 [@media(hover:none)]:",
|
|
179
|
-
children: [/* @__PURE__ */
|
|
180
|
+
children: [/* @__PURE__ */ h("div", {
|
|
180
181
|
className: "min-w-0 flex-1",
|
|
181
|
-
children: [t ? /* @__PURE__ */
|
|
182
|
+
children: [t ? /* @__PURE__ */ m(ce, {
|
|
182
183
|
profile: t,
|
|
183
184
|
variant: "cell"
|
|
184
|
-
}) : /* @__PURE__ */
|
|
185
|
+
}) : /* @__PURE__ */ h("div", {
|
|
185
186
|
className: "min-w-0",
|
|
186
|
-
children: [/* @__PURE__ */
|
|
187
|
+
children: [/* @__PURE__ */ m("p", {
|
|
187
188
|
className: "truncate font-medium",
|
|
188
189
|
children: r.name
|
|
189
|
-
}), r.email ? /* @__PURE__ */
|
|
190
|
+
}), r.email ? /* @__PURE__ */ m("p", {
|
|
190
191
|
className: "truncate text-sm text-text-secondary",
|
|
191
192
|
children: r.email
|
|
192
|
-
}) : /* @__PURE__ */
|
|
193
|
+
}) : /* @__PURE__ */ m("p", {
|
|
193
194
|
className: "truncate text-xs text-text-secondary",
|
|
194
195
|
children: e.userId
|
|
195
196
|
})]
|
|
196
|
-
}), /* @__PURE__ */
|
|
197
|
+
}), /* @__PURE__ */ h("div", {
|
|
197
198
|
className: "mt-1 flex flex-wrap items-center gap-2",
|
|
198
|
-
children: [/* @__PURE__ */
|
|
199
|
+
children: [/* @__PURE__ */ m(g, {
|
|
199
200
|
variant: "secondary",
|
|
200
201
|
className: "text-xs",
|
|
201
|
-
children: n(
|
|
202
|
-
}), /* @__PURE__ */
|
|
202
|
+
children: n(w, "projectMembers.memberBadge", "Project member")
|
|
203
|
+
}), /* @__PURE__ */ h("span", {
|
|
203
204
|
className: "text-xs text-text-secondary",
|
|
204
205
|
children: [
|
|
205
|
-
n(
|
|
206
|
+
n(w, "projectMembers.addedLabel", "Added"),
|
|
206
207
|
" ",
|
|
207
|
-
|
|
208
|
+
te(e.createdAt)
|
|
208
209
|
]
|
|
209
210
|
})]
|
|
210
211
|
})]
|
|
211
|
-
}),
|
|
212
|
+
}), _e ? /* @__PURE__ */ h(_, {
|
|
212
213
|
variant: "outline",
|
|
213
214
|
size: "sm",
|
|
214
|
-
onClick: () => void
|
|
215
|
+
onClick: () => void ye(e),
|
|
215
216
|
disabled: q,
|
|
216
|
-
children: [/* @__PURE__ */
|
|
217
|
+
children: [/* @__PURE__ */ m(ie, { className: "mr-2 size-4" }), i ? n(w, "projectMembers.removing", "Removing...") : n(w, "projectMembers.removeAction", "Remove")]
|
|
217
218
|
}) : null]
|
|
218
219
|
}, e.id);
|
|
219
|
-
}), W ? /* @__PURE__ */
|
|
220
|
+
}), W ? /* @__PURE__ */ h("div", {
|
|
220
221
|
className: "flex flex-col items-center gap-2 rounded-lg border border-dashed border-border-seam bg-bg-sunken/20 p-4 text-center",
|
|
221
|
-
children: [/* @__PURE__ */
|
|
222
|
+
children: [/* @__PURE__ */ h("p", {
|
|
222
223
|
className: "text-sm text-text-secondary",
|
|
223
224
|
children: [
|
|
224
225
|
"Showing ",
|
|
@@ -227,7 +228,7 @@ function T() {
|
|
|
227
228
|
Z,
|
|
228
229
|
" project members."
|
|
229
230
|
]
|
|
230
|
-
}), /* @__PURE__ */
|
|
231
|
+
}), /* @__PURE__ */ m(_, {
|
|
231
232
|
variant: "outline",
|
|
232
233
|
onClick: () => void G(),
|
|
233
234
|
disabled: K,
|
|
@@ -235,11 +236,11 @@ function T() {
|
|
|
235
236
|
})]
|
|
236
237
|
}) : null]
|
|
237
238
|
}),
|
|
238
|
-
|
|
239
|
+
T && E && N ? /* @__PURE__ */ m(u, {
|
|
239
240
|
open: N,
|
|
240
241
|
onOpenChange: P,
|
|
241
|
-
workspaceId:
|
|
242
|
-
projectId:
|
|
242
|
+
workspaceId: T,
|
|
243
|
+
projectId: E,
|
|
243
244
|
projectName: z?.name
|
|
244
245
|
}) : null
|
|
245
246
|
]
|
|
@@ -247,6 +248,6 @@ function T() {
|
|
|
247
248
|
});
|
|
248
249
|
}
|
|
249
250
|
//#endregion
|
|
250
|
-
export {
|
|
251
|
+
export { w as ProjectMembersPage };
|
|
251
252
|
|
|
252
253
|
//# sourceMappingURL=ProjectMembersPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectMembersPage.js","names":[],"sources":["../../src/pages/ProjectMembersPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport { ArrowLeft, Search, UserMinus, UserPlus } from 'lucide-react';\nimport {\n ActorIdentity,\n useActorProfiles,\n type ActorInput,\n type ActorProfileMap,\n} from '@burdenoff/fe-libs/shared';\nimport {\n Badge,\n Button,\n Input,\n PageContainer,\n Skeleton,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ProjectMemberPickerDialog } from '../components/ProjectMemberPickerDialog';\nimport { useInfiniteProjectMembers } from '../hooks/useProjectMembers';\nimport { useProject } from '../hooks/useProjects';\nimport { useRemoveProjectMember } from '../hooks/useProjectMemberMutations';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { formatRelativeTime } from '../utils/formatters';\nimport { tWithFallback } from '../utils/i18n';\nimport { nativeConfirm, nativeImpact, nativeNotify } from '../utils/nativeBridge';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport type { ProjectMember } from '../types';\n\nconst PROJECT_MEMBER_PAGE_SIZE = 100;\n\nfunction getMemberFallback(profileMap: ActorProfileMap, member: ProjectMember) {\n const profile = profileMap[member.userId];\n if (profile) {\n return {\n name: profile.displayName,\n email: profile.email ?? undefined,\n };\n }\n\n return {\n name: member.userId,\n email: undefined,\n };\n}\n\nexport function ProjectMembersPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { workspaceId, projectId } = useParams<{ workspaceId: string; projectId: string }>();\n const {\n basePath = '/workspaces',\n apiGatewayUrl,\n authToken,\n organizationId,\n } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const [searchValue, setSearchValue] = useState('');\n const [isAddDialogOpen, setIsAddDialogOpen] = useState(false);\n const [removingUserId, setRemovingUserId] = useState<string | null>(null);\n const [removeError, setRemoveError] = useState<string | null>(null);\n\n useWorkspacesPageView('workspaces.project-members', {\n workspaceId,\n projectId,\n requireParams: true,\n });\n\n const {\n data: project,\n isLoading: isLoadingProject,\n error: projectError,\n } = useProject(projectId || '', workspaceId);\n const {\n data: projectMembersPages,\n isLoading: isLoadingMembers,\n error: membersError,\n hasNextPage: hasMoreProjectMembers,\n fetchNextPage: fetchNextProjectMembersPage,\n isFetchingNextPage: isFetchingNextProjectMembersPage,\n } = useInfiniteProjectMembers(projectId || '', workspaceId, PROJECT_MEMBER_PAGE_SIZE);\n const { mutate: removeProjectMember, isPending: isRemovingProjectMember } =\n useRemoveProjectMember(workspaceId);\n\n const {\n canViewProject,\n canListProjectMembers,\n canAddProjectMember,\n canRemoveProjectMember,\n isLoading: permissionsLoading,\n } = useWorkspacePermissions();\n\n const projectMembers = useMemo(\n () => projectMembersPages?.pages.flatMap((page) => page.items) ?? [],\n [projectMembersPages?.pages]\n );\n const totalProjectMembers = projectMembersPages?.pages[0]?.total ?? projectMembers.length;\n const actors = useMemo<ActorInput[]>(\n () => projectMembers.map((member) => ({ actorId: member.userId, actorType: 'user' })),\n [projectMembers]\n );\n const profileMap: ActorProfileMap = useActorProfiles(actors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n workspaceId,\n });\n\n const filteredMembers = useMemo(() => {\n const needle = searchValue.trim().toLowerCase();\n if (!needle) {\n return projectMembers;\n }\n\n return projectMembers.filter((member) => {\n const fallback = getMemberFallback(profileMap, member);\n return (\n fallback.name.toLowerCase().includes(needle) ||\n fallback.email?.toLowerCase().includes(needle) ||\n member.userId.toLowerCase().includes(needle)\n );\n });\n }, [profileMap, projectMembers, searchValue]);\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 p-6\">\n <Skeleton className=\"h-10 w-40\" />\n <Skeleton className=\"h-10 w-64\" />\n <Skeleton className=\"h-40 w-full\" />\n </div>\n );\n }\n\n if (!canViewProject || !canListProjectMembers) {\n return (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <div className=\"space-y-2 text-center\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'projectMembers.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'projectMembers.noPermission',\n \"You don't have permission to manage project members.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n const handleBack = () => {\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/projects/${projectId}`));\n };\n\n const handleRemoveMember = async (member: ProjectMember) => {\n const fallback = getMemberFallback(profileMap, member);\n void nativeImpact('medium');\n setRemoveError(null);\n const message = `${tWithFallback(t, 'projectMembers.removeConfirmPrefix', 'Remove')} ${fallback.name} ${tWithFallback(t, 'projectMembers.removeConfirmSuffix', 'from this project?')}`;\n const native = await nativeConfirm({\n title: tWithFallback(t, 'projectMembers.removeConfirmTitle', 'Remove project member'),\n message,\n okButtonTitle: tWithFallback(t, 'projectMembers.removeAction', 'Remove'),\n cancelButtonTitle: tWithFallback(t, 'projectMembers.cancel', 'Cancel'),\n });\n const confirmed = native === null ? window.confirm(message) : native;\n if (!confirmed) {\n return;\n }\n\n setRemovingUserId(member.userId);\n removeProjectMember(\n { projectId: member.projectId, userId: member.userId },\n {\n onSuccess: () => {\n void nativeNotify('success');\n setRemovingUserId(null);\n },\n onError: () => {\n void nativeNotify('error');\n setRemoveError(\n tWithFallback(\n t,\n 'projectMembers.removeError',\n 'Failed to remove project member. Please try again.'\n )\n );\n setRemovingUserId(null);\n },\n }\n );\n };\n\n return (\n <PageContainer padded={false}>\n <div className=\"space-y-4 p-3 sm:space-y-6 sm:p-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div>\n <Button variant=\"ghost\" onClick={handleBack} className=\"mb-2 -ml-2 gap-2\">\n <ArrowLeft size={20} />\n {tWithFallback(t, 'projectMembers.backToProject', 'Back to Project')}\n </Button>\n <h1 className=\"text-2xl font-bold sm:text-3xl\">\n {tWithFallback(t, 'projectMembers.pageTitle', 'Project Members')}\n </h1>\n <p className=\"text-sm text-text-secondary sm:text-base\">\n {project?.name\n ? `${project.name} · ${tWithFallback(\n t,\n 'projectMembers.pageSubtitle',\n 'Only existing workspace members can be added to this project.'\n )}`\n : tWithFallback(\n t,\n 'projectMembers.pageSubtitle',\n 'Only existing workspace members can be added to this project.'\n )}\n </p>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"secondary\">{totalProjectMembers}</Badge>\n {canAddProjectMember ? (\n <Button\n onClick={() => setIsAddDialogOpen(true)}\n disabled={!workspaceId || !projectId}\n >\n <UserPlus className=\"mr-2 size-4\" />\n {tWithFallback(t, 'projectMembers.addMember', 'Add Member')}\n </Button>\n ) : null}\n </div>\n </div>\n\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 size-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n value={searchValue}\n onChange={(event) => setSearchValue(event.target.value)}\n placeholder={tWithFallback(\n t,\n 'projectMembers.searchPlaceholder',\n 'Search project members'\n )}\n className=\"pl-9\"\n />\n </div>\n\n {removeError ? (\n <div className=\"rounded-lg border border-status-error-border/20 bg-status-error-bg/10 p-4 text-sm text-status-error-text\">\n {removeError}\n </div>\n ) : null}\n\n {isLoadingProject || isLoadingMembers ? (\n <div className=\"space-y-4\">\n {Array.from({ length: 4 }).map((_, index) => (\n <div key={index} className=\"rounded-lg border border-border-seam bg-bg-surface p-4\">\n <Skeleton className=\"h-5 w-40\" />\n <Skeleton className=\"mt-2 h-4 w-56\" />\n </div>\n ))}\n </div>\n ) : projectError || membersError ? (\n <div className=\"rounded-lg border border-status-error-border/20 bg-status-error-bg/10 p-4 text-sm text-status-error-text\">\n {(membersError instanceof Error && membersError.message) ||\n (projectError instanceof Error && projectError.message) ||\n tWithFallback(t, 'projectMembers.loadError', 'Failed to load project members')}\n </div>\n ) : filteredMembers.length === 0 ? (\n <IllustratedEmptyState\n illustration={searchValue ? 'empty-search' : 'empty-people'}\n title={\n searchValue\n ? tWithFallback(t, 'projectMembers.noResultsTitle', 'No matching members found')\n : tWithFallback(t, 'projectMembers.emptyTitle', 'No project members yet')\n }\n description={\n searchValue\n ? tWithFallback(\n t,\n 'projectMembers.noResultsSubtitle',\n 'Try a different search to find a project member.'\n )\n : tWithFallback(\n t,\n 'projectMembers.emptySubtitle',\n 'Add workspace members to this project so they can collaborate here.'\n )\n }\n action={\n hasMoreProjectMembers || (!searchValue && canAddProjectMember) ? (\n <div className=\"flex flex-wrap items-center justify-center gap-2\">\n {hasMoreProjectMembers ? (\n <Button\n variant=\"outline\"\n onClick={() => void fetchNextProjectMembersPage()}\n disabled={isFetchingNextProjectMembersPage}\n >\n {isFetchingNextProjectMembersPage ? 'Loading more…' : 'Load More Members'}\n </Button>\n ) : null}\n {!searchValue && canAddProjectMember ? (\n <Button onClick={() => setIsAddDialogOpen(true)}>\n <UserPlus className=\"mr-2 size-4\" />\n {tWithFallback(t, 'projectMembers.addFirstMember', 'Add First Member')}\n </Button>\n ) : null}\n </div>\n ) : undefined\n }\n />\n ) : (\n <div className=\"space-y-3\">\n {filteredMembers.map((member) => {\n const profile = profileMap[member.userId];\n const fallback = getMemberFallback(profileMap, member);\n const isRemoving = isRemovingProjectMember && removingUserId === member.userId;\n\n return (\n <div\n key={member.id}\n className=\"flex items-center justify-between gap-4 rounded-card border border-border-seam bg-bg-surface p-4 shadow-elevation-1 transition-[box-shadow,transform] duration-200 ease-[var(--motion-easing-out)] hover:shadow-elevation-2 [@media(hover:none)]:\"\n >\n <div className=\"min-w-0 flex-1\">\n {profile ? (\n <ActorIdentity profile={profile} variant=\"cell\" />\n ) : (\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium\">{fallback.name}</p>\n {fallback.email ? (\n <p className=\"truncate text-sm text-text-secondary\">{fallback.email}</p>\n ) : (\n <p className=\"truncate text-xs text-text-secondary\">{member.userId}</p>\n )}\n </div>\n )}\n\n <div className=\"mt-1 flex flex-wrap items-center gap-2\">\n <Badge variant=\"secondary\" className=\"text-xs\">\n {tWithFallback(t, 'projectMembers.memberBadge', 'Project member')}\n </Badge>\n <span className=\"text-xs text-text-secondary\">\n {tWithFallback(t, 'projectMembers.addedLabel', 'Added')}{' '}\n {formatRelativeTime(member.createdAt)}\n </span>\n </div>\n </div>\n\n {canRemoveProjectMember ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => void handleRemoveMember(member)}\n disabled={isRemovingProjectMember}\n >\n <UserMinus className=\"mr-2 size-4\" />\n {isRemoving\n ? tWithFallback(t, 'projectMembers.removing', 'Removing...')\n : tWithFallback(t, 'projectMembers.removeAction', 'Remove')}\n </Button>\n ) : null}\n </div>\n );\n })}\n\n {hasMoreProjectMembers ? (\n <div className=\"flex flex-col items-center gap-2 rounded-lg border border-dashed border-border-seam bg-bg-sunken/20 p-4 text-center\">\n <p className=\"text-sm text-text-secondary\">\n Showing {projectMembers.length} of {totalProjectMembers} project members.\n </p>\n <Button\n variant=\"outline\"\n onClick={() => void fetchNextProjectMembersPage()}\n disabled={isFetchingNextProjectMembersPage}\n >\n {isFetchingNextProjectMembersPage ? 'Loading more…' : 'Load More Members'}\n </Button>\n </div>\n ) : null}\n </div>\n )}\n\n {workspaceId && projectId && isAddDialogOpen ? (\n <ProjectMemberPickerDialog\n open={isAddDialogOpen}\n onOpenChange={setIsAddDialogOpen}\n workspaceId={workspaceId}\n projectId={projectId}\n projectName={project?.name}\n />\n ) : null}\n </div>\n </PageContainer>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA+BA,IAAM,KAA2B;AAEjC,SAAS,EAAkB,GAA6B,GAAuB;CAC7E,IAAM,IAAU,EAAW,EAAO;AAQlC,QAPI,IACK;EACL,MAAM,EAAQ;EACd,OAAO,EAAQ,SAAS,KAAA;EACzB,GAGI;EACL,MAAM,EAAO;EACb,OAAO,KAAA;EACR;;AAGH,SAAgB,IAAqB;CACnC,IAAM,EAAE,SAAM,GAAS,EACjB,KAAW,GAAa,EACxB,EAAE,gBAAa,iBAAc,GAAuD,EACpF,EACJ,eAAW,eACX,mBACA,cACA,sBACE,GAAsB,EACpB,EAAE,yBAAsB,GAAqB,EAC7C,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAiB,KAAsB,EAAS,GAAM,EACvD,CAAC,GAAgB,KAAqB,EAAwB,KAAK,EACnE,CAAC,GAAa,KAAkB,EAAwB,KAAK;AAEnE,GAAsB,8BAA8B;EAClD;EACA;EACA,eAAe;EAChB,CAAC;CAEF,IAAM,EACJ,MAAM,GACN,WAAW,GACX,OAAO,MACL,GAAW,KAAa,IAAI,EAAY,EACtC,EACJ,MAAM,GACN,WAAW,IACX,OAAO,GACP,aAAa,GACb,eAAe,GACf,oBAAoB,MAClB,GAA0B,KAAa,IAAI,GAAa,GAAyB,EAC/E,EAAE,QAAQ,IAAqB,WAAW,MAC9C,GAAuB,EAAY,EAE/B,EACJ,oBACA,2BACA,wBACA,4BACA,WAAW,MACT,GAAyB,EAEvB,IAAiB,QACf,GAAqB,MAAM,SAAS,MAAS,EAAK,MAAM,IAAI,EAAE,EACpE,CAAC,GAAqB,MAAM,CAC7B,EACK,IAAsB,GAAqB,MAAM,IAAI,SAAS,EAAe,QAK7E,IAA8B,EAJrB,QACP,EAAe,KAAK,OAAY;EAAE,SAAS,EAAO;EAAQ,WAAW;EAAQ,EAAE,EACrF,CAAC,EAAe,CACjB,EAC4D;EAC3D;EACA;EACA,OAAO;EACP;EACD,CAAC,EAEI,IAAkB,QAAc;EACpC,IAAM,IAAS,EAAY,MAAM,CAAC,aAAa;AAK/C,SAJK,IAIE,EAAe,QAAQ,MAAW;GACvC,IAAM,IAAW,EAAkB,GAAY,EAAO;AACtD,UACE,EAAS,KAAK,aAAa,CAAC,SAAS,EAAO,IAC5C,EAAS,OAAO,aAAa,CAAC,SAAS,EAAO,IAC9C,EAAO,OAAO,aAAa,CAAC,SAAS,EAAO;IAE9C,GAVO;IAWR;EAAC;EAAY;EAAgB;EAAY,CAAC;AAE7C,KAAI,EACF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD,EAAU,WAAU,aAAc,CAAA;GAClC,kBAAC,GAAD,EAAU,WAAU,aAAc,CAAA;GAClC,kBAAC,GAAD,EAAU,WAAU,eAAgB,CAAA;GAChC;;AAIV,KAAI,CAAC,MAAkB,CAAC,GACtB,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,MAAD;IAAI,WAAU;cACX,EAAc,GAAG,+BAA+B,gBAAgB;IAC9D,CAAA,EACL,kBAAC,KAAD;IAAG,WAAU;cACV,EACC,GACA,+BACA,uDACD;IACC,CAAA,CACA;;EACF,CAAA;CAIV,IAAM,WAAmB;AACvB,KAAS,EAAkB,GAAG,GAAS,GAAG,EAAY,YAAY,IAAY,CAAC;IAG3E,KAAqB,OAAO,MAA0B;EAC1D,IAAM,IAAW,EAAkB,GAAY,EAAO;AAEtD,EADK,GAAa,SAAS,EAC3B,EAAe,KAAK;EACpB,IAAM,IAAU,GAAG,EAAc,GAAG,sCAAsC,SAAS,CAAC,GAAG,EAAS,KAAK,GAAG,EAAc,GAAG,sCAAsC,qBAAqB,IAC9K,IAAS,MAAM,EAAc;GACjC,OAAO,EAAc,GAAG,qCAAqC,wBAAwB;GACrF;GACA,eAAe,EAAc,GAAG,+BAA+B,SAAS;GACxE,mBAAmB,EAAc,GAAG,yBAAyB,SAAS;GACvE,CAAC;AACgB,SAAW,OAAO,OAAO,QAAQ,EAAQ,GAAG,OAK9D,EAAkB,EAAO,OAAO,EAChC,GACE;GAAE,WAAW,EAAO;GAAW,QAAQ,EAAO;GAAQ,EACtD;GACE,iBAAiB;AAEf,IADK,EAAa,UAAU,EAC5B,EAAkB,KAAK;;GAEzB,eAAe;AASb,IARK,EAAa,QAAQ,EAC1B,EACE,EACE,GACA,8BACA,qDACD,CACF,EACD,EAAkB,KAAK;;GAE1B,CACF;;AAGH,QACE,kBAAC,GAAD;EAAe,QAAQ;YACrB,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD,EAAA,UAAA;MACE,kBAAC,GAAD;OAAQ,SAAQ;OAAQ,SAAS;OAAY,WAAU;iBAAvD,CACE,kBAAC,IAAD,EAAW,MAAM,IAAM,CAAA,EACtB,EAAc,GAAG,gCAAgC,kBAAkB,CAC7D;;MACT,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAc,GAAG,4BAA4B,kBAAkB;OAC7D,CAAA;MACL,kBAAC,KAAD;OAAG,WAAU;iBACV,GAAS,OACN,GAAG,EAAQ,KAAK,KAAK,EACnB,GACA,+BACA,gEACD,KACD,EACE,GACA,+BACA,gEACD;OACH,CAAA;MACA,EAAA,CAAA,EAEN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OAAO,SAAQ;iBAAa;OAA4B,CAAA,EACvD,IACC,kBAAC,GAAD;OACE,eAAe,EAAmB,GAAK;OACvC,UAAU,CAAC,KAAe,CAAC;iBAF7B,CAIE,kBAAC,GAAD,EAAU,WAAU,eAAgB,CAAA,EACnC,EAAc,GAAG,4BAA4B,aAAa,CACpD;WACP,KACA;QACF;;IAEN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,IAAD,EAAQ,WAAU,uEAAwE,CAAA,EAC1F,kBAAC,GAAD;MACE,OAAO;MACP,WAAW,MAAU,EAAe,EAAM,OAAO,MAAM;MACvD,aAAa,EACX,GACA,oCACA,yBACD;MACD,WAAU;MACV,CAAA,CACE;;IAEL,IACC,kBAAC,OAAD;KAAK,WAAU;eACZ;KACG,CAAA,GACJ;IAEH,KAAoB,KACnB,kBAAC,OAAD;KAAK,WAAU;eACZ,MAAM,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,GAAG,MACjC,kBAAC,OAAD;MAAiB,WAAU;gBAA3B,CACE,kBAAC,GAAD,EAAU,WAAU,YAAa,CAAA,EACjC,kBAAC,GAAD,EAAU,WAAU,iBAAkB,CAAA,CAClC;QAHI,EAGJ,CACN;KACE,CAAA,GACJ,KAAgB,IAClB,kBAAC,OAAD;KAAK,WAAU;eACX,aAAwB,SAAS,EAAa,WAC7C,aAAwB,SAAS,EAAa,WAC/C,EAAc,GAAG,4BAA4B,iCAAiC;KAC5E,CAAA,GACJ,EAAgB,WAAW,IAC7B,kBAAC,GAAD;KACE,cAAc,IAAc,iBAAiB;KAC7C,OACE,IACI,EAAc,GAAG,iCAAiC,4BAA4B,GAC9E,EAAc,GAAG,6BAA6B,yBAAyB;KAE7E,aACE,IACI,EACE,GACA,oCACA,mDACD,GACD,EACE,GACA,gCACA,sEACD;KAEP,QACE,KAA0B,CAAC,KAAe,IACxC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACG,IACC,kBAAC,GAAD;OACE,SAAQ;OACR,eAAe,KAAK,GAA6B;OACjD,UAAU;iBAET,IAAmC,kBAAkB;OAC/C,CAAA,GACP,MACH,CAAC,KAAe,IACf,kBAAC,GAAD;OAAQ,eAAe,EAAmB,GAAK;iBAA/C,CACE,kBAAC,GAAD,EAAU,WAAU,eAAgB,CAAA,EACnC,EAAc,GAAG,iCAAiC,mBAAmB,CAC/D;WACP,KACA;UACJ,KAAA;KAEN,CAAA,GAEF,kBAAC,OAAD;KAAK,WAAU;eAAf,CACG,EAAgB,KAAK,MAAW;MAC/B,IAAM,IAAU,EAAW,EAAO,SAC5B,IAAW,EAAkB,GAAY,EAAO,EAChD,IAAa,KAA2B,MAAmB,EAAO;AAExE,aACE,kBAAC,OAAD;OAEE,WAAU;iBAFZ,CAIE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACG,IACC,kBAAC,GAAD;SAAwB;SAAS,SAAQ;SAAS,CAAA,GAElD,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBAAwB,EAAS;UAAS,CAAA,EACtD,EAAS,QACR,kBAAC,KAAD;UAAG,WAAU;oBAAwC,EAAS;UAAU,CAAA,GAExE,kBAAC,KAAD;UAAG,WAAU;oBAAwC,EAAO;UAAW,CAAA,CAErE;YAGR,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAO,SAAQ;UAAY,WAAU;oBAClC,EAAc,GAAG,8BAA8B,iBAAiB;UAC3D,CAAA,EACR,kBAAC,QAAD;UAAM,WAAU;oBAAhB;WACG,EAAc,GAAG,6BAA6B,QAAQ;WAAE;WACxD,EAAmB,EAAO,UAAU;WAChC;YACH;WACF;WAEL,KACC,kBAAC,GAAD;QACE,SAAQ;QACR,MAAK;QACL,eAAe,KAAK,GAAmB,EAAO;QAC9C,UAAU;kBAJZ,CAME,kBAAC,IAAD,EAAW,WAAU,eAAgB,CAAA,EACpC,IACG,EAAc,GAAG,2BAA2B,cAAc,GAC1D,EAAc,GAAG,+BAA+B,SAAS,CACtD;YACP,KACA;SAzCC,EAAO,GAyCR;OAER,EAED,IACC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBAAb;QAA2C;QAChC,EAAe;QAAO;QAAK;QAAoB;QACtD;UACJ,kBAAC,GAAD;OACE,SAAQ;OACR,eAAe,KAAK,GAA6B;OACjD,UAAU;iBAET,IAAmC,kBAAkB;OAC/C,CAAA,CACL;UACJ,KACA;;IAGP,KAAe,KAAa,IAC3B,kBAAC,IAAD;KACE,MAAM;KACN,cAAc;KACD;KACF;KACX,aAAa,GAAS;KACtB,CAAA,GACA;IACA;;EACQ,CAAA"}
|
|
1
|
+
{"version":3,"file":"ProjectMembersPage.js","names":[],"sources":["../../src/pages/ProjectMembersPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport { ArrowLeft, Search, UserMinus, UserPlus } from 'lucide-react';\nimport {\n ActorIdentity,\n useActorProfiles,\n type ActorInput,\n type ActorProfileMap,\n} from '@burdenoff/fe-libs/shared';\nimport {\n Badge,\n Button,\n Input,\n PageContainer,\n Skeleton,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ProjectMemberPickerDialog } from '../components/ProjectMemberPickerDialog';\nimport { useInfiniteProjectMembers } from '../hooks/useProjectMembers';\nimport { useProject } from '../hooks/useProjects';\nimport { useRemoveProjectMember } from '../hooks/useProjectMemberMutations';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { formatRelativeTime } from '../utils/formatters';\nimport { tWithFallback } from '../utils/i18n';\nimport { nativeConfirm, nativeImpact, nativeNotify } from '../utils/nativeBridge';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport type { ProjectMember } from '../types';\n\nconst PROJECT_MEMBER_PAGE_SIZE = 100;\n\nfunction getMemberFallback(profileMap: ActorProfileMap, member: ProjectMember) {\n const profile = profileMap[member.userId];\n if (profile) {\n return {\n name: profile.displayName,\n email: profile.email ?? undefined,\n };\n }\n\n return {\n name: member.userId,\n email: undefined,\n };\n}\n\nexport function ProjectMembersPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { workspaceId, projectId } = useParams<{ workspaceId: string; projectId: string }>();\n const {\n basePath = '/workspaces',\n apiGatewayUrl,\n authToken,\n organizationId,\n } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const [searchValue, setSearchValue] = useState('');\n const [isAddDialogOpen, setIsAddDialogOpen] = useState(false);\n const [removingUserId, setRemovingUserId] = useState<string | null>(null);\n const [removeError, setRemoveError] = useState<string | null>(null);\n\n useWorkspacesPageView('workspaces.project-members', {\n workspaceId,\n projectId,\n requireParams: true,\n });\n\n const {\n data: project,\n isLoading: isLoadingProject,\n error: projectError,\n } = useProject(projectId || '', workspaceId);\n const {\n data: projectMembersPages,\n isLoading: isLoadingMembers,\n error: membersError,\n hasNextPage: hasMoreProjectMembers,\n fetchNextPage: fetchNextProjectMembersPage,\n isFetchingNextPage: isFetchingNextProjectMembersPage,\n } = useInfiniteProjectMembers(projectId || '', workspaceId, PROJECT_MEMBER_PAGE_SIZE);\n const { mutate: removeProjectMember, isPending: isRemovingProjectMember } =\n useRemoveProjectMember(workspaceId);\n\n const {\n canViewProject,\n canListProjectMembers,\n canAddProjectMember,\n canRemoveProjectMember,\n isLoading: permissionsLoading,\n } = useWorkspacePermissions();\n\n const projectMembers = useMemo(\n () => projectMembersPages?.pages.flatMap((page) => page.items) ?? [],\n [projectMembersPages?.pages]\n );\n const totalProjectMembers = projectMembersPages?.pages[0]?.total ?? projectMembers.length;\n const actors = useMemo<ActorInput[]>(\n () => projectMembers.map((member) => ({ actorId: member.userId, actorType: 'user' })),\n [projectMembers]\n );\n const profileMap: ActorProfileMap = useActorProfiles(actors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n workspaceId,\n });\n\n const filteredMembers = useMemo(() => {\n const needle = searchValue.trim().toLowerCase();\n if (!needle) {\n return projectMembers;\n }\n\n return projectMembers.filter((member) => {\n const fallback = getMemberFallback(profileMap, member);\n return (\n fallback.name.toLowerCase().includes(needle) ||\n fallback.email?.toLowerCase().includes(needle) ||\n member.userId.toLowerCase().includes(needle)\n );\n });\n }, [profileMap, projectMembers, searchValue]);\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 p-6\">\n <Skeleton className=\"h-10 w-40\" />\n <Skeleton className=\"h-10 w-64\" />\n <Skeleton className=\"h-40 w-full\" />\n </div>\n );\n }\n\n if (!canViewProject || !canListProjectMembers) {\n return (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <div className=\"space-y-2 text-center\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'projectMembers.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'projectMembers.noPermission',\n \"You don't have permission to manage project members.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n const handleBack = () => {\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/projects/${projectId}`));\n };\n\n const handleRemoveMember = async (member: ProjectMember) => {\n const fallback = getMemberFallback(profileMap, member);\n void nativeImpact('medium');\n setRemoveError(null);\n const message = `${tWithFallback(t, 'projectMembers.removeConfirmPrefix', 'Remove')} ${fallback.name} ${tWithFallback(t, 'projectMembers.removeConfirmSuffix', 'from this project?')}`;\n const native = await nativeConfirm({\n title: tWithFallback(t, 'projectMembers.removeConfirmTitle', 'Remove project member'),\n message,\n okButtonTitle: tWithFallback(t, 'projectMembers.removeAction', 'Remove'),\n cancelButtonTitle: tWithFallback(t, 'projectMembers.cancel', 'Cancel'),\n });\n const confirmed = native === null ? window.confirm(message) : native;\n if (!confirmed) {\n return;\n }\n\n setRemovingUserId(member.userId);\n removeProjectMember(\n { projectId: member.projectId, userId: member.userId },\n {\n onSuccess: () => {\n void nativeNotify('success');\n setRemovingUserId(null);\n },\n onError: () => {\n void nativeNotify('error');\n setRemoveError(\n tWithFallback(\n t,\n 'projectMembers.removeError',\n 'Failed to remove project member. Please try again.'\n )\n );\n setRemovingUserId(null);\n },\n }\n );\n };\n\n return (\n <PageContainer padded={false}>\n <div className=\"space-y-4 p-3 sm:space-y-6 sm:p-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div>\n <Button variant=\"ghost\" onClick={handleBack} className=\"mb-2 -ml-2 gap-2\">\n <ArrowLeft size={20} />\n {tWithFallback(t, 'projectMembers.backToProject', 'Back to Project')}\n </Button>\n <h1 className=\"text-2xl font-bold sm:text-3xl\">\n {tWithFallback(t, 'projectMembers.pageTitle', 'Project Members')}\n </h1>\n <p className=\"text-sm text-text-secondary sm:text-base\">\n {project?.name\n ? `${project.name} · ${tWithFallback(\n t,\n 'projectMembers.pageSubtitle',\n 'Only existing workspace members can be added to this project.'\n )}`\n : tWithFallback(\n t,\n 'projectMembers.pageSubtitle',\n 'Only existing workspace members can be added to this project.'\n )}\n </p>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"secondary\">{totalProjectMembers}</Badge>\n {canAddProjectMember ? (\n <Button\n onClick={() => setIsAddDialogOpen(true)}\n disabled={!workspaceId || !projectId}\n >\n <UserPlus className=\"mr-2 size-4\" />\n {tWithFallback(t, 'projectMembers.addMember', 'Add Member')}\n </Button>\n ) : null}\n </div>\n </div>\n\n <PagePurpose>\n {tWithFallback(\n t,\n 'projectMembers.purpose',\n 'This is the subset of workspace members who can work on this specific project. Add people here to give them project-level access, search to find someone, or remove members who have moved on. Only existing workspace members can be added.'\n )}\n </PagePurpose>\n\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 size-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n value={searchValue}\n onChange={(event) => setSearchValue(event.target.value)}\n placeholder={tWithFallback(\n t,\n 'projectMembers.searchPlaceholder',\n 'Search project members'\n )}\n className=\"pl-9\"\n />\n </div>\n\n {removeError ? (\n <div className=\"rounded-lg border border-status-error-border/20 bg-status-error-bg/10 p-4 text-sm text-status-error-text\">\n {removeError}\n </div>\n ) : null}\n\n {isLoadingProject || isLoadingMembers ? (\n <div className=\"space-y-4\">\n {Array.from({ length: 4 }).map((_, index) => (\n <div key={index} className=\"rounded-lg border border-border-seam bg-bg-surface p-4\">\n <Skeleton className=\"h-5 w-40\" />\n <Skeleton className=\"mt-2 h-4 w-56\" />\n </div>\n ))}\n </div>\n ) : projectError || membersError ? (\n <div className=\"rounded-lg border border-status-error-border/20 bg-status-error-bg/10 p-4 text-sm text-status-error-text\">\n {(membersError instanceof Error && membersError.message) ||\n (projectError instanceof Error && projectError.message) ||\n tWithFallback(t, 'projectMembers.loadError', 'Failed to load project members')}\n </div>\n ) : filteredMembers.length === 0 ? (\n <IllustratedEmptyState\n illustration={searchValue ? 'empty-search' : 'empty-people'}\n title={\n searchValue\n ? tWithFallback(t, 'projectMembers.noResultsTitle', 'No matching members found')\n : tWithFallback(t, 'projectMembers.emptyTitle', 'No project members yet')\n }\n description={\n searchValue\n ? tWithFallback(\n t,\n 'projectMembers.noResultsSubtitle',\n 'Try a different search to find a project member.'\n )\n : tWithFallback(\n t,\n 'projectMembers.emptySubtitle',\n 'Add workspace members to this project so they can collaborate here.'\n )\n }\n action={\n hasMoreProjectMembers || (!searchValue && canAddProjectMember) ? (\n <div className=\"flex flex-wrap items-center justify-center gap-2\">\n {hasMoreProjectMembers ? (\n <Button\n variant=\"outline\"\n onClick={() => void fetchNextProjectMembersPage()}\n disabled={isFetchingNextProjectMembersPage}\n >\n {isFetchingNextProjectMembersPage ? 'Loading more…' : 'Load More Members'}\n </Button>\n ) : null}\n {!searchValue && canAddProjectMember ? (\n <Button onClick={() => setIsAddDialogOpen(true)}>\n <UserPlus className=\"mr-2 size-4\" />\n {tWithFallback(t, 'projectMembers.addFirstMember', 'Add First Member')}\n </Button>\n ) : null}\n </div>\n ) : undefined\n }\n />\n ) : (\n <div className=\"space-y-3\">\n {filteredMembers.map((member) => {\n const profile = profileMap[member.userId];\n const fallback = getMemberFallback(profileMap, member);\n const isRemoving = isRemovingProjectMember && removingUserId === member.userId;\n\n return (\n <div\n key={member.id}\n className=\"flex items-center justify-between gap-4 rounded-card border border-border-seam bg-bg-surface p-4 shadow-elevation-1 transition-[box-shadow,transform] duration-200 ease-[var(--motion-easing-out)] hover:shadow-elevation-2 [@media(hover:none)]:\"\n >\n <div className=\"min-w-0 flex-1\">\n {profile ? (\n <ActorIdentity profile={profile} variant=\"cell\" />\n ) : (\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium\">{fallback.name}</p>\n {fallback.email ? (\n <p className=\"truncate text-sm text-text-secondary\">{fallback.email}</p>\n ) : (\n <p className=\"truncate text-xs text-text-secondary\">{member.userId}</p>\n )}\n </div>\n )}\n\n <div className=\"mt-1 flex flex-wrap items-center gap-2\">\n <Badge variant=\"secondary\" className=\"text-xs\">\n {tWithFallback(t, 'projectMembers.memberBadge', 'Project member')}\n </Badge>\n <span className=\"text-xs text-text-secondary\">\n {tWithFallback(t, 'projectMembers.addedLabel', 'Added')}{' '}\n {formatRelativeTime(member.createdAt)}\n </span>\n </div>\n </div>\n\n {canRemoveProjectMember ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => void handleRemoveMember(member)}\n disabled={isRemovingProjectMember}\n >\n <UserMinus className=\"mr-2 size-4\" />\n {isRemoving\n ? tWithFallback(t, 'projectMembers.removing', 'Removing...')\n : tWithFallback(t, 'projectMembers.removeAction', 'Remove')}\n </Button>\n ) : null}\n </div>\n );\n })}\n\n {hasMoreProjectMembers ? (\n <div className=\"flex flex-col items-center gap-2 rounded-lg border border-dashed border-border-seam bg-bg-sunken/20 p-4 text-center\">\n <p className=\"text-sm text-text-secondary\">\n Showing {projectMembers.length} of {totalProjectMembers} project members.\n </p>\n <Button\n variant=\"outline\"\n onClick={() => void fetchNextProjectMembersPage()}\n disabled={isFetchingNextProjectMembersPage}\n >\n {isFetchingNextProjectMembersPage ? 'Loading more…' : 'Load More Members'}\n </Button>\n </div>\n ) : null}\n </div>\n )}\n\n {workspaceId && projectId && isAddDialogOpen ? (\n <ProjectMemberPickerDialog\n open={isAddDialogOpen}\n onOpenChange={setIsAddDialogOpen}\n workspaceId={workspaceId}\n projectId={projectId}\n projectName={project?.name}\n />\n ) : null}\n </div>\n </PageContainer>\n );\n}\n"],"mappings":"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"}
|