@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
|
@@ -10,31 +10,31 @@ import { useArchiveProject as oe, useDeleteProject as se, useTagProject as ce, u
|
|
|
10
10
|
import { formatDate as i } from "../utils/formatters.js";
|
|
11
11
|
import { getProjectDisplayTags as fe, toProjectTag as a } from "../utils/projectTags.js";
|
|
12
12
|
import { useWorkspaceActivity as pe } from "../hooks/useWorkspaceActivity.js";
|
|
13
|
-
import { ProjectTagsWidget as
|
|
14
|
-
import { ProjectMemberPickerDialog as
|
|
15
|
-
import { useMemo as
|
|
16
|
-
import { Activity as
|
|
13
|
+
import { ProjectTagsWidget as me } from "../components/tags/ProjectTagsWidget.js";
|
|
14
|
+
import { ProjectMemberPickerDialog as o } from "../components/ProjectMemberPickerDialog.js";
|
|
15
|
+
import { useMemo as s, useState as c } from "react";
|
|
16
|
+
import { Activity as l, Archive as u, ArrowLeft as d, Clock as f, FolderKanban as p, MoreVertical as he, UserPlus as m, Users as h } from "lucide-react";
|
|
17
17
|
import { jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
18
|
-
import { AlertDialog as ge, AlertDialogAction as _e, AlertDialogCancel as ve, AlertDialogContent as ye, AlertDialogDescription as be, AlertDialogFooter as xe, AlertDialogHeader as Se, AlertDialogTitle as Ce, Badge as v, Button as y, CardContent as b, CardHeader as x, CardTitle as S, DropdownMenu as
|
|
19
|
-
import { useNavigate as
|
|
20
|
-
import { useI18n as
|
|
18
|
+
import { AlertDialog as ge, AlertDialogAction as _e, AlertDialogCancel as ve, AlertDialogContent as ye, AlertDialogDescription as be, AlertDialogFooter as xe, AlertDialogHeader as Se, AlertDialogTitle as Ce, Badge as v, Button as y, CardContent as b, CardHeader as x, CardTitle as S, DropdownMenu as we, DropdownMenuContent as C, DropdownMenuItem as w, DropdownMenuSeparator as T, DropdownMenuTrigger as E, EntityIdModal as D, GlassCard as O, IllustratedEmptyState as k, NextSteps as Te, PagePurpose as A, ShowMore as Ee, Skeleton as j, useEntityIdModal as De } from "@burdenoff/fe-libs/ui";
|
|
19
|
+
import { useNavigate as Oe, useParams as ke } from "react-router-dom";
|
|
20
|
+
import { useI18n as Ae } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
21
21
|
//#region src/pages/ProjectDetailPage.tsx
|
|
22
22
|
function M() {
|
|
23
|
-
let { t: M } =
|
|
23
|
+
let { t: M } = Ae(), N = De(), { workspaceId: P, projectId: F } = ke();
|
|
24
24
|
te("workspaces.project-detail", {
|
|
25
25
|
workspaceId: P,
|
|
26
26
|
projectId: F,
|
|
27
27
|
requireParams: !0
|
|
28
28
|
});
|
|
29
|
-
let I =
|
|
29
|
+
let I = Oe(), { basePath: L = "/workspaces" } = e(), { withCurrentSearch: R } = ee(), { data: z, isLoading: je, error: Me } = ie(F || "", P), [Ne, B] = c(!1), [V, H] = c(!1), { mutate: Pe, isPending: U } = oe(P), { mutate: Fe, isPending: W } = le(P), { mutate: Ie, isPending: G } = se(P), { mutateAsync: Le, isPending: Re } = ce(P), { mutateAsync: ze, isPending: Be } = ue(P), { mutateAsync: Ve, isPending: He } = de(P), { canViewProject: Ue, canDeleteProject: K, canArchiveProject: q, canListProjectMembers: J, canAddProjectMember: Y, isLoading: We } = re(), { data: Ge } = ae(F || "", P), X = s(() => fe({
|
|
30
30
|
tags: z?.tags ?? [],
|
|
31
31
|
tagAssignments: z?.tagAssignments
|
|
32
|
-
}), [z?.tags, z?.tagAssignments]), { data:
|
|
33
|
-
if (
|
|
32
|
+
}), [z?.tags, z?.tagAssignments]), { data: Ke } = pe(P), qe = (Ke ?? []).filter((e) => e.resourceId === F || e.resourceType?.toLowerCase() === "project").length, Je = X;
|
|
33
|
+
if (We) return /* @__PURE__ */ _("div", {
|
|
34
34
|
className: "space-y-4 p-6",
|
|
35
35
|
children: [/* @__PURE__ */ g(j, { className: "h-10 w-1/3" }), /* @__PURE__ */ g(j, { className: "h-64" })]
|
|
36
36
|
});
|
|
37
|
-
if (!
|
|
37
|
+
if (!Ue) return /* @__PURE__ */ g("div", {
|
|
38
38
|
className: "flex items-center justify-center min-h-[400px]",
|
|
39
39
|
children: /* @__PURE__ */ _("div", {
|
|
40
40
|
className: "text-center space-y-2",
|
|
@@ -49,14 +49,14 @@ function M() {
|
|
|
49
49
|
});
|
|
50
50
|
let Z = () => {
|
|
51
51
|
I(R(`${L}/${P}/projects`));
|
|
52
|
-
},
|
|
53
|
-
z && (z.archived ?
|
|
52
|
+
}, Ye = () => {
|
|
53
|
+
z && (z.archived ? Fe(z.id, { onError: (e) => {
|
|
54
54
|
console.error("Failed to unarchive project:", e);
|
|
55
|
-
} }) :
|
|
55
|
+
} }) : Pe(z.id, { onError: (e) => {
|
|
56
56
|
console.error("Failed to archive project:", e);
|
|
57
57
|
} }));
|
|
58
|
-
},
|
|
59
|
-
z && (ne("medium"),
|
|
58
|
+
}, Xe = () => {
|
|
59
|
+
z && (ne("medium"), Ie(z.id, {
|
|
60
60
|
onSuccess: () => {
|
|
61
61
|
r("success"), B(!1), I(R(`${L}/${P}/projects`));
|
|
62
62
|
},
|
|
@@ -70,15 +70,36 @@ function M() {
|
|
|
70
70
|
if (!z) return;
|
|
71
71
|
let t = e.map((e) => a(e));
|
|
72
72
|
try {
|
|
73
|
-
await
|
|
73
|
+
await Ve({
|
|
74
74
|
id: z.id,
|
|
75
75
|
input: { tags: t }
|
|
76
76
|
});
|
|
77
77
|
} catch (e) {
|
|
78
78
|
r("warning"), console.error("Failed to sync legacy project tag mirror after updating structured project tags:", e);
|
|
79
79
|
}
|
|
80
|
+
}, Ze = async (e) => {
|
|
81
|
+
if (z) try {
|
|
82
|
+
await Le({
|
|
83
|
+
entityId: z.id,
|
|
84
|
+
tagId: e.id
|
|
85
|
+
}), await $([...X, a(e)]);
|
|
86
|
+
} catch (e) {
|
|
87
|
+
throw r("error"), e;
|
|
88
|
+
}
|
|
89
|
+
}, Qe = async (e) => {
|
|
90
|
+
if (!z) return;
|
|
91
|
+
let t = z.tagAssignments?.find((t) => t.tagId === e)?.id;
|
|
92
|
+
try {
|
|
93
|
+
await ze({
|
|
94
|
+
entityId: z.id,
|
|
95
|
+
tagId: e,
|
|
96
|
+
assignmentId: t
|
|
97
|
+
}), await $(X.filter((t) => t.tagId !== e && t.id !== e));
|
|
98
|
+
} catch (e) {
|
|
99
|
+
throw r("error"), e;
|
|
100
|
+
}
|
|
80
101
|
};
|
|
81
|
-
return
|
|
102
|
+
return je ? /* @__PURE__ */ _("div", {
|
|
82
103
|
className: "space-y-4 sm:space-y-6 p-3 sm:p-6",
|
|
83
104
|
children: [
|
|
84
105
|
/* @__PURE__ */ g(j, { className: "h-10 w-32" }),
|
|
@@ -93,20 +114,20 @@ function M() {
|
|
|
93
114
|
}),
|
|
94
115
|
/* @__PURE__ */ g(j, { className: "h-48" })
|
|
95
116
|
]
|
|
96
|
-
}) :
|
|
117
|
+
}) : Me || !z ? /* @__PURE__ */ _("div", {
|
|
97
118
|
className: "space-y-4 p-6",
|
|
98
119
|
children: [/* @__PURE__ */ _(y, {
|
|
99
120
|
variant: "ghost",
|
|
100
121
|
onClick: Z,
|
|
101
122
|
className: "gap-2",
|
|
102
|
-
children: [/* @__PURE__ */ g(
|
|
103
|
-
}), /* @__PURE__ */ g(
|
|
123
|
+
children: [/* @__PURE__ */ g(d, { size: 20 }), n(M, "projectDetail.backToProjects", "Back to Projects")]
|
|
124
|
+
}), /* @__PURE__ */ g(k, {
|
|
104
125
|
illustration: "empty-search",
|
|
105
126
|
title: n(M, "projectDetail.projectNotFound", "Project not found"),
|
|
106
127
|
description: n(M, "projectDetail.projectNotFoundDescription", "The project you're looking for doesn't exist or you don't have permission to view it."),
|
|
107
128
|
action: /* @__PURE__ */ _(y, {
|
|
108
129
|
onClick: Z,
|
|
109
|
-
children: [/* @__PURE__ */ g(
|
|
130
|
+
children: [/* @__PURE__ */ g(d, {
|
|
110
131
|
size: 16,
|
|
111
132
|
className: "mr-2"
|
|
112
133
|
}), n(M, "projectDetail.backToProjects", "Back to Projects")]
|
|
@@ -122,13 +143,13 @@ function M() {
|
|
|
122
143
|
variant: "ghost",
|
|
123
144
|
onClick: Z,
|
|
124
145
|
className: "gap-2 mb-2 -ml-2",
|
|
125
|
-
children: [/* @__PURE__ */ g(
|
|
146
|
+
children: [/* @__PURE__ */ g(d, { size: 20 }), n(M, "projectDetail.backToProjects", "Back to Projects")]
|
|
126
147
|
}),
|
|
127
148
|
/* @__PURE__ */ _("div", {
|
|
128
149
|
className: "flex items-center gap-3",
|
|
129
150
|
children: [/* @__PURE__ */ g("div", {
|
|
130
151
|
className: "p-3 rounded-lg bg-action-primary-bg/10",
|
|
131
|
-
children: /* @__PURE__ */ g(
|
|
152
|
+
children: /* @__PURE__ */ g(p, {
|
|
132
153
|
className: "text-primary",
|
|
133
154
|
size: 28
|
|
134
155
|
})
|
|
@@ -148,7 +169,7 @@ function M() {
|
|
|
148
169
|
}), z.archived && /* @__PURE__ */ _(v, {
|
|
149
170
|
variant: "outline",
|
|
150
171
|
className: "border-workspace-archived-border text-workspace-archived-text",
|
|
151
|
-
children: [/* @__PURE__ */ g(
|
|
172
|
+
children: [/* @__PURE__ */ g(u, {
|
|
152
173
|
size: 12,
|
|
153
174
|
className: "mr-1"
|
|
154
175
|
}), n(M, "projectDetail.archived", "Archived")]
|
|
@@ -156,27 +177,27 @@ function M() {
|
|
|
156
177
|
})
|
|
157
178
|
] }), /* @__PURE__ */ g("div", {
|
|
158
179
|
className: "flex items-center gap-2",
|
|
159
|
-
children: /* @__PURE__ */ _(
|
|
180
|
+
children: /* @__PURE__ */ _(we, { children: [/* @__PURE__ */ g(E, {
|
|
160
181
|
asChild: !0,
|
|
161
182
|
children: /* @__PURE__ */ g(y, {
|
|
162
183
|
variant: "outline",
|
|
163
184
|
size: "icon",
|
|
164
185
|
"aria-label": n(M, "projectDetail.openProjectActionsAriaLabel", "Open project actions"),
|
|
165
|
-
children: /* @__PURE__ */ g(
|
|
186
|
+
children: /* @__PURE__ */ g(he, { size: 20 })
|
|
166
187
|
})
|
|
167
|
-
}), /* @__PURE__ */ _(
|
|
188
|
+
}), /* @__PURE__ */ _(C, {
|
|
168
189
|
align: "end",
|
|
169
190
|
children: [
|
|
170
|
-
q && /* @__PURE__ */ _(
|
|
171
|
-
onClick:
|
|
191
|
+
q && /* @__PURE__ */ _(w, {
|
|
192
|
+
onClick: Ye,
|
|
172
193
|
disabled: U || W,
|
|
173
|
-
children: [/* @__PURE__ */ g(
|
|
194
|
+
children: [/* @__PURE__ */ g(u, {
|
|
174
195
|
size: 16,
|
|
175
196
|
className: "mr-2"
|
|
176
197
|
}), U || W ? n(M, "projectDetail.processing", "Processing...") : z.archived ? n(M, "projectDetail.unarchive", "Unarchive") : n(M, "projectDetail.archive", "Archive")]
|
|
177
198
|
}),
|
|
178
|
-
q && K && /* @__PURE__ */ g(
|
|
179
|
-
K && /* @__PURE__ */ g(
|
|
199
|
+
q && K && /* @__PURE__ */ g(T, {}),
|
|
200
|
+
K && /* @__PURE__ */ g(w, {
|
|
180
201
|
onClick: () => B(!0),
|
|
181
202
|
className: "text-status-error-text",
|
|
182
203
|
children: n(M, "projectDetail.deleteProject", "Delete Project")
|
|
@@ -185,14 +206,54 @@ function M() {
|
|
|
185
206
|
})] })
|
|
186
207
|
})]
|
|
187
208
|
}),
|
|
209
|
+
/* @__PURE__ */ g(A, { children: n(M, "projectDetail.purpose", "This is a single project — a focused stream of work within its workspace. From here you can manage the people working on it, organise it with tags so it surfaces in filters and reports, and keep track of related activity. Use the menu in the top-right to archive or delete it when it wraps up.") }),
|
|
210
|
+
/* @__PURE__ */ g(Te, {
|
|
211
|
+
storageKey: "workspaces-project-detail",
|
|
212
|
+
steps: [
|
|
213
|
+
...Y ? [{
|
|
214
|
+
id: "add-member",
|
|
215
|
+
label: n(M, "projectDetail.stepAddMember", "Add people to the project"),
|
|
216
|
+
description: n(M, "projectDetail.stepAddMemberDesc", "Pull in the teammates who will actually work on this project."),
|
|
217
|
+
icon: /* @__PURE__ */ g(m, { size: 16 }),
|
|
218
|
+
onClick: () => H(!0)
|
|
219
|
+
}] : [],
|
|
220
|
+
{
|
|
221
|
+
id: "tag-project",
|
|
222
|
+
label: n(M, "projectDetail.stepTag", "Tag the project"),
|
|
223
|
+
description: n(M, "projectDetail.stepTagDesc", "Add tags so this project shows up in the right filters and views."),
|
|
224
|
+
icon: /* @__PURE__ */ g(p, { size: 16 }),
|
|
225
|
+
onClick: () => {
|
|
226
|
+
document.getElementById("project-tags-section")?.scrollIntoView({
|
|
227
|
+
behavior: "smooth",
|
|
228
|
+
block: "center"
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
...J ? [{
|
|
233
|
+
id: "manage-members",
|
|
234
|
+
label: n(M, "projectDetail.stepManageMembers", "Review project members"),
|
|
235
|
+
description: n(M, "projectDetail.stepManageMembersDesc", "See who has access and adjust their project roles."),
|
|
236
|
+
icon: /* @__PURE__ */ g(h, { size: 16 }),
|
|
237
|
+
onClick: Q
|
|
238
|
+
}] : [],
|
|
239
|
+
{
|
|
240
|
+
id: "review-activity",
|
|
241
|
+
label: n(M, "projectDetail.stepActivity", "Check recent activity"),
|
|
242
|
+
description: n(M, "projectDetail.stepActivityDesc", "Catch up on what has changed across the workspace."),
|
|
243
|
+
icon: /* @__PURE__ */ g(l, { size: 16 }),
|
|
244
|
+
onClick: () => I(R(`${L}/${P}/activity`))
|
|
245
|
+
}
|
|
246
|
+
]
|
|
247
|
+
}),
|
|
188
248
|
/* @__PURE__ */ _("div", {
|
|
189
249
|
className: "grid grid-cols-1 lg:grid-cols-3 gap-4 sm:gap-6",
|
|
190
250
|
children: [/* @__PURE__ */ _("div", {
|
|
191
251
|
className: "lg:col-span-2 space-y-4 sm:space-y-6",
|
|
192
252
|
children: [/* @__PURE__ */ _("div", {
|
|
193
253
|
className: "grid grid-cols-1 md:grid-cols-2 gap-4",
|
|
194
|
-
children: [/* @__PURE__ */ _(
|
|
254
|
+
children: [/* @__PURE__ */ _(O, {
|
|
195
255
|
treatment: "metric",
|
|
256
|
+
glow: !0,
|
|
196
257
|
children: [/* @__PURE__ */ g(x, {
|
|
197
258
|
className: "flex flex-row items-center justify-between pb-2",
|
|
198
259
|
children: /* @__PURE__ */ _(S, {
|
|
@@ -207,7 +268,7 @@ function M() {
|
|
|
207
268
|
children: [
|
|
208
269
|
/* @__PURE__ */ g("p", {
|
|
209
270
|
className: "text-3xl font-bold tabular-nums",
|
|
210
|
-
children:
|
|
271
|
+
children: Ge ?? 0
|
|
211
272
|
}),
|
|
212
273
|
/* @__PURE__ */ g("p", {
|
|
213
274
|
className: "text-sm text-text-muted",
|
|
@@ -218,7 +279,7 @@ function M() {
|
|
|
218
279
|
children: [Y ? /* @__PURE__ */ _(y, {
|
|
219
280
|
size: "sm",
|
|
220
281
|
onClick: () => H(!0),
|
|
221
|
-
children: [/* @__PURE__ */ g(
|
|
282
|
+
children: [/* @__PURE__ */ g(m, {
|
|
222
283
|
size: 16,
|
|
223
284
|
className: "mr-2"
|
|
224
285
|
}), n(M, "projectDetail.addProjectMember", "Add Member")]
|
|
@@ -234,68 +295,48 @@ function M() {
|
|
|
234
295
|
}) : null
|
|
235
296
|
]
|
|
236
297
|
})]
|
|
237
|
-
}), /* @__PURE__ */ _(
|
|
298
|
+
}), /* @__PURE__ */ _(O, {
|
|
238
299
|
treatment: "metric",
|
|
239
300
|
children: [/* @__PURE__ */ g(x, {
|
|
240
301
|
className: "flex flex-row items-center justify-between pb-2",
|
|
241
302
|
children: /* @__PURE__ */ _(S, {
|
|
242
303
|
className: "flex items-center gap-2 text-sm font-medium text-text-secondary",
|
|
243
|
-
children: [/* @__PURE__ */ g(
|
|
304
|
+
children: [/* @__PURE__ */ g(l, {
|
|
244
305
|
size: 18,
|
|
245
306
|
className: "text-text-muted"
|
|
246
307
|
}), n(M, "projectDetail.activityCardTitle", "Activity")]
|
|
247
308
|
})
|
|
248
309
|
}), /* @__PURE__ */ _(b, { children: [/* @__PURE__ */ g("p", {
|
|
249
310
|
className: "text-3xl font-bold tabular-nums",
|
|
250
|
-
children:
|
|
311
|
+
children: qe
|
|
251
312
|
}), /* @__PURE__ */ g("p", {
|
|
252
313
|
className: "text-sm text-text-muted",
|
|
253
314
|
children: n(M, "projectDetail.recentActivityItems", "Recent activity items")
|
|
254
315
|
})] })]
|
|
255
316
|
})]
|
|
256
|
-
}), /* @__PURE__ */ _(
|
|
317
|
+
}), /* @__PURE__ */ _(O, {
|
|
257
318
|
treatment: "plain",
|
|
319
|
+
id: "project-tags-section",
|
|
258
320
|
className: "border-border-seam",
|
|
259
321
|
children: [/* @__PURE__ */ g(x, { children: /* @__PURE__ */ g(S, { children: n(M, "projectDetail.tagsTitle", "Tags") }) }), /* @__PURE__ */ _(b, {
|
|
260
322
|
className: "space-y-3",
|
|
261
323
|
children: [/* @__PURE__ */ g("p", {
|
|
262
324
|
className: "text-sm text-text-muted",
|
|
263
325
|
children: n(M, "projectDetail.tagsDescription", "Use universal tags to classify and filter this project across the workspace.")
|
|
264
|
-
}), /* @__PURE__ */ g(
|
|
326
|
+
}), /* @__PURE__ */ g(me, {
|
|
265
327
|
entityType: "Project",
|
|
266
328
|
entityId: z.id,
|
|
267
329
|
workspaceId: P,
|
|
268
|
-
tags:
|
|
269
|
-
onAdd:
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
entityId: z.id,
|
|
273
|
-
tagId: e.id
|
|
274
|
-
}), await $([...X, a(e)]);
|
|
275
|
-
} catch (e) {
|
|
276
|
-
throw r("error"), e;
|
|
277
|
-
}
|
|
278
|
-
},
|
|
279
|
-
onRemove: async (e) => {
|
|
280
|
-
if (!z) return;
|
|
281
|
-
let t = z.tagAssignments?.find((t) => t.tagId === e)?.id;
|
|
282
|
-
try {
|
|
283
|
-
await Le({
|
|
284
|
-
entityId: z.id,
|
|
285
|
-
tagId: e,
|
|
286
|
-
assignmentId: t
|
|
287
|
-
}), await $(X.filter((t) => t.tagId !== e && t.id !== e));
|
|
288
|
-
} catch (e) {
|
|
289
|
-
throw r("error"), e;
|
|
290
|
-
}
|
|
291
|
-
},
|
|
292
|
-
className: Ie || Re || Be ? "pointer-events-none opacity-75" : void 0
|
|
330
|
+
tags: Je,
|
|
331
|
+
onAdd: Ze,
|
|
332
|
+
onRemove: Qe,
|
|
333
|
+
className: Re || Be || He ? "pointer-events-none opacity-75" : void 0
|
|
293
334
|
})]
|
|
294
335
|
})]
|
|
295
336
|
})]
|
|
296
337
|
}), /* @__PURE__ */ g("aside", {
|
|
297
338
|
className: "lg:sticky lg:top-6 lg:self-start",
|
|
298
|
-
children: /* @__PURE__ */ _(
|
|
339
|
+
children: /* @__PURE__ */ _(O, {
|
|
299
340
|
treatment: "plain",
|
|
300
341
|
className: "border-border-seam",
|
|
301
342
|
children: [/* @__PURE__ */ g(x, { children: /* @__PURE__ */ g(S, { children: n(M, "projectDetail.projectInformation", "Project Information") }) }), /* @__PURE__ */ _(b, {
|
|
@@ -308,7 +349,7 @@ function M() {
|
|
|
308
349
|
className: "space-y-1",
|
|
309
350
|
children: [/* @__PURE__ */ _("span", {
|
|
310
351
|
className: "text-text-muted flex items-center gap-2",
|
|
311
|
-
children: [/* @__PURE__ */ g(
|
|
352
|
+
children: [/* @__PURE__ */ g(p, { size: 14 }), n(M, "projectDetail.projectId", "Project ID")]
|
|
312
353
|
}), /* @__PURE__ */ g("button", {
|
|
313
354
|
type: "button",
|
|
314
355
|
onClick: () => N.show({
|
|
@@ -328,7 +369,7 @@ function M() {
|
|
|
328
369
|
className: "space-y-1",
|
|
329
370
|
children: [/* @__PURE__ */ _("span", {
|
|
330
371
|
className: "text-text-secondary flex items-center gap-2",
|
|
331
|
-
children: [/* @__PURE__ */ g(
|
|
372
|
+
children: [/* @__PURE__ */ g(p, { size: 14 }), n(M, "projectDetail.projectKey", "Project Key")]
|
|
332
373
|
}), /* @__PURE__ */ g("span", {
|
|
333
374
|
className: "font-medium",
|
|
334
375
|
children: z.key
|
|
@@ -338,14 +379,14 @@ function M() {
|
|
|
338
379
|
className: "space-y-1",
|
|
339
380
|
children: [/* @__PURE__ */ _("span", {
|
|
340
381
|
className: "text-text-secondary flex items-center gap-2",
|
|
341
|
-
children: [/* @__PURE__ */ g(
|
|
382
|
+
children: [/* @__PURE__ */ g(f, { size: 14 }), n(M, "projectDetail.created", "Created")]
|
|
342
383
|
}), /* @__PURE__ */ g("span", { children: i(z.createdAt) })]
|
|
343
384
|
}),
|
|
344
385
|
/* @__PURE__ */ _("div", {
|
|
345
386
|
className: "space-y-1",
|
|
346
387
|
children: [/* @__PURE__ */ _("span", {
|
|
347
388
|
className: "text-text-secondary flex items-center gap-2",
|
|
348
|
-
children: [/* @__PURE__ */ g(
|
|
389
|
+
children: [/* @__PURE__ */ g(f, { size: 14 }), n(M, "projectDetail.lastUpdated", "Last Updated")]
|
|
349
390
|
}), /* @__PURE__ */ g("span", { children: i(z.updatedAt) })]
|
|
350
391
|
}),
|
|
351
392
|
/* @__PURE__ */ _("div", {
|
|
@@ -380,7 +421,7 @@ function M() {
|
|
|
380
421
|
children: JSON.stringify(z.config, null, 2)
|
|
381
422
|
})]
|
|
382
423
|
}),
|
|
383
|
-
/* @__PURE__ */ g(
|
|
424
|
+
/* @__PURE__ */ g(Ee, {
|
|
384
425
|
collapsedHeight: 0,
|
|
385
426
|
label: n(M, "projectDetail.quickActions", "Quick Actions"),
|
|
386
427
|
children: /* @__PURE__ */ _("div", {
|
|
@@ -411,7 +452,7 @@ function M() {
|
|
|
411
452
|
size: "sm",
|
|
412
453
|
className: "justify-start",
|
|
413
454
|
onClick: () => I(R(`${L}/${P}/activity`)),
|
|
414
|
-
children: [/* @__PURE__ */ g(
|
|
455
|
+
children: [/* @__PURE__ */ g(l, {
|
|
415
456
|
size: 16,
|
|
416
457
|
className: "mr-2"
|
|
417
458
|
}), n(M, "projectDetail.workspaceActivity", "Workspace Activity")]
|
|
@@ -425,26 +466,26 @@ function M() {
|
|
|
425
466
|
})]
|
|
426
467
|
}),
|
|
427
468
|
/* @__PURE__ */ g(ge, {
|
|
428
|
-
open:
|
|
469
|
+
open: Ne,
|
|
429
470
|
onOpenChange: B,
|
|
430
471
|
children: /* @__PURE__ */ _(ye, { children: [/* @__PURE__ */ _(Se, { children: [/* @__PURE__ */ g(Ce, { children: n(M, "projectDetail.deleteDialogTitle", "Delete Project") }), /* @__PURE__ */ g(be, { children: n(M, "projectDetail.deleteConfirmPrefix", "Are you sure you want to delete \"") + z.name + n(M, "projectDetail.deleteConfirmSuffix", "\"? This action cannot be undone and all project data will be permanently removed.") })] }), /* @__PURE__ */ _(xe, { children: [/* @__PURE__ */ g(ve, {
|
|
431
472
|
disabled: G,
|
|
432
473
|
children: n(M, "projectDetail.cancel", "Cancel")
|
|
433
474
|
}), /* @__PURE__ */ g(_e, {
|
|
434
|
-
onClick:
|
|
475
|
+
onClick: Xe,
|
|
435
476
|
disabled: G,
|
|
436
477
|
className: "bg-status-error-bg text-status-error-text hover:bg-status-error-bg/90",
|
|
437
478
|
children: G ? n(M, "projectDetail.deleting", "Deleting...") : n(M, "projectDetail.deleteProject", "Delete Project")
|
|
438
479
|
})] })] })
|
|
439
480
|
}),
|
|
440
|
-
P && F && V ? /* @__PURE__ */ g(
|
|
481
|
+
P && F && V ? /* @__PURE__ */ g(o, {
|
|
441
482
|
open: V,
|
|
442
483
|
onOpenChange: H,
|
|
443
484
|
workspaceId: P,
|
|
444
485
|
projectId: F,
|
|
445
486
|
projectName: z.name
|
|
446
487
|
}) : null,
|
|
447
|
-
/* @__PURE__ */ g(
|
|
488
|
+
/* @__PURE__ */ g(D, { control: N })
|
|
448
489
|
]
|
|
449
490
|
});
|
|
450
491
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectDetailPage.js","names":[],"sources":["../../src/pages/ProjectDetailPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useParams, useNavigate } from 'react-router-dom';\nimport {\n ArrowLeft,\n FolderKanban,\n Users,\n UserPlus,\n Activity,\n Archive,\n MoreVertical,\n Clock,\n} from 'lucide-react';\nimport {\n Button,\n ShowMore,\n GlassCard,\n CardHeader,\n CardTitle,\n CardContent,\n Badge,\n Skeleton,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n DropdownMenuSeparator,\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n EntityIdModal,\n useEntityIdModal,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { type TagItem } from '@burdenoff/fe-libs/tag';\nimport { ProjectTagsWidget } from '../components/tags/ProjectTagsWidget';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport { useProject, useProjectMembersCount } from '../hooks/useProjects';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport {\n useArchiveProject,\n useUnarchiveProject,\n useDeleteProject,\n useTagProject,\n useUntagProject,\n useUpdateProject,\n} from '../hooks/useProjectMutations';\nimport { formatDate } from '../utils/formatters';\nimport { nativeImpact, nativeNotify } from '../utils/nativeBridge';\nimport { getProjectDisplayTags, toProjectTag } from '../utils/projectTags';\nimport { getProjectStatusClasses } from '../utils/tokenHelpers';\nimport { useWorkspaceActivity } from '../hooks/useWorkspaceActivity';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport { ProjectMemberPickerDialog } from '../components/ProjectMemberPickerDialog';\n\nexport function ProjectDetailPage() {\n const { t } = useI18n();\n const idModal = useEntityIdModal();\n\n const { workspaceId, projectId } = useParams<{ workspaceId: string; projectId: string }>();\n useWorkspacesPageView('workspaces.project-detail', {\n workspaceId,\n projectId,\n requireParams: true,\n });\n const navigate = useNavigate();\n const { basePath = '/workspaces' } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const { data: project, isLoading, error } = useProject(projectId || '', workspaceId);\n const [showDeleteDialog, setShowDeleteDialog] = useState(false);\n const [showAddMemberDialog, setShowAddMemberDialog] = useState(false);\n\n const { mutate: archiveProject, isPending: isArchiving } = useArchiveProject(workspaceId);\n const { mutate: unarchiveProject, isPending: isUnarchiving } = useUnarchiveProject(workspaceId);\n const { mutate: deleteProject, isPending: isDeleting } = useDeleteProject(workspaceId);\n const { mutateAsync: tagProject, isPending: isTaggingProject } = useTagProject(workspaceId);\n const { mutateAsync: untagProject, isPending: isUntaggingProject } = useUntagProject(workspaceId);\n const { mutateAsync: updateProject, isPending: isUpdatingProject } =\n useUpdateProject(workspaceId);\n const {\n canViewProject,\n canDeleteProject,\n canArchiveProject,\n canListProjectMembers,\n canAddProjectMember,\n isLoading: permissionsLoading,\n } = useWorkspacePermissions();\n const { data: projectMemberCount } = useProjectMembersCount(projectId || '', workspaceId);\n const displayTags = useMemo(\n () =>\n getProjectDisplayTags({\n tags: project?.tags ?? [],\n tagAssignments: project?.tagAssignments,\n }),\n [project?.tags, project?.tagAssignments]\n );\n const { data: workspaceActivity } = useWorkspaceActivity(workspaceId);\n const relatedActivityCount = (workspaceActivity ?? []).filter(\n (item) => item.resourceId === projectId || item.resourceType?.toLowerCase() === 'project'\n ).length;\n const widgetTags: TagItem[] = displayTags;\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 p-6\">\n <Skeleton className=\"h-10 w-1/3\" />\n <Skeleton className=\"h-64\" />\n </div>\n );\n }\n\n if (!canViewProject) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'projectDetail.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'projectDetail.noPermissionToViewProject',\n \"You don't have permission to view this project.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n const handleBack = () => {\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/projects`));\n };\n\n const handleArchive = () => {\n if (!project) return;\n if (project.archived) {\n unarchiveProject(project.id, {\n onError: (err) => {\n console.error('Failed to unarchive project:', err);\n },\n });\n } else {\n archiveProject(project.id, {\n onError: (err) => {\n console.error('Failed to archive project:', err);\n },\n });\n }\n };\n\n const handleDelete = () => {\n if (!project) return;\n void nativeImpact('medium');\n deleteProject(project.id, {\n onSuccess: () => {\n void nativeNotify('success');\n setShowDeleteDialog(false);\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/projects`));\n },\n onError: (err) => {\n void nativeNotify('error');\n console.error('Failed to delete project:', err);\n },\n });\n };\n\n const handleViewProjectMembers = () => {\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/projects/${projectId}/members`));\n };\n\n const syncLegacyProjectTags = async (nextTags: TagItem[]) => {\n if (!project) {\n return;\n }\n\n const normalizedNextTags = nextTags.map((tag) => toProjectTag(tag));\n\n try {\n await updateProject({\n id: project.id,\n input: {\n tags: normalizedNextTags,\n },\n });\n } catch (err) {\n void nativeNotify('warning');\n console.error(\n 'Failed to sync legacy project tag mirror after updating structured project tags:',\n err\n );\n }\n };\n\n const handleAddTag = async (tag: TagItem) => {\n if (!project) {\n return;\n }\n\n try {\n await tagProject({\n entityId: project.id,\n tagId: tag.id,\n });\n\n await syncLegacyProjectTags([...displayTags, toProjectTag(tag)]);\n } catch (err) {\n void nativeNotify('error');\n throw err;\n }\n };\n\n const handleRemoveTag = async (tagId: string) => {\n if (!project) {\n return;\n }\n\n const assignmentId = project.tagAssignments?.find(\n (assignment) => assignment.tagId === tagId\n )?.id;\n try {\n await untagProject({\n entityId: project.id,\n tagId,\n assignmentId,\n });\n\n await syncLegacyProjectTags(\n displayTags.filter((tag) => tag.tagId !== tagId && tag.id !== tagId)\n );\n } catch (err) {\n void nativeNotify('error');\n throw err;\n }\n };\n\n if (isLoading) {\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n <Skeleton className=\"h-10 w-32\" />\n <Skeleton className=\"h-10 w-1/3\" />\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n </div>\n <Skeleton className=\"h-48\" />\n </div>\n );\n }\n\n if (error || !project) {\n return (\n <div className=\"space-y-4 p-6\">\n <Button variant=\"ghost\" onClick={handleBack} className=\"gap-2\">\n <ArrowLeft size={20} />\n {tWithFallback(t, 'projectDetail.backToProjects', 'Back to Projects')}\n </Button>\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title={tWithFallback(t, 'projectDetail.projectNotFound', 'Project not found')}\n description={tWithFallback(\n t,\n 'projectDetail.projectNotFoundDescription',\n \"The project you're looking for doesn't exist or you don't have permission to view it.\"\n )}\n action={\n <Button onClick={handleBack}>\n <ArrowLeft size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.backToProjects', 'Back to Projects')}\n </Button>\n }\n />\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3\">\n <div>\n <Button variant=\"ghost\" onClick={handleBack} className=\"gap-2 mb-2 -ml-2\">\n <ArrowLeft size={20} />\n {tWithFallback(t, 'projectDetail.backToProjects', 'Back to Projects')}\n </Button>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-3 rounded-lg bg-action-primary-bg/10\">\n <FolderKanban className=\"text-primary\" size={28} />\n </div>\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">{project.name}</h1>\n <p className=\"text-sm text-text-secondary font-mono\">{project.key}</p>\n </div>\n </div>\n <div className=\"flex items-center gap-2 mt-3\">\n <Badge className={`border ${getProjectStatusClasses(project.status)}`}>\n {project.status}\n </Badge>\n {project.archived && (\n <Badge\n variant=\"outline\"\n className=\"border-workspace-archived-border text-workspace-archived-text\"\n >\n <Archive size={12} className=\"mr-1\" />\n {tWithFallback(t, 'projectDetail.archived', 'Archived')}\n </Badge>\n )}\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"icon\"\n aria-label={tWithFallback(\n t,\n 'projectDetail.openProjectActionsAriaLabel',\n 'Open project actions'\n )}\n >\n <MoreVertical size={20} />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {canArchiveProject && (\n <DropdownMenuItem onClick={handleArchive} disabled={isArchiving || isUnarchiving}>\n <Archive size={16} className=\"mr-2\" />\n {isArchiving || isUnarchiving\n ? tWithFallback(t, 'projectDetail.processing', 'Processing...')\n : project.archived\n ? tWithFallback(t, 'projectDetail.unarchive', 'Unarchive')\n : tWithFallback(t, 'projectDetail.archive', 'Archive')}\n </DropdownMenuItem>\n )}\n {canArchiveProject && canDeleteProject && <DropdownMenuSeparator />}\n {canDeleteProject && (\n <DropdownMenuItem\n onClick={() => setShowDeleteDialog(true)}\n className=\"text-status-error-text\"\n >\n {tWithFallback(t, 'projectDetail.deleteProject', 'Delete Project')}\n </DropdownMenuItem>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n\n {/* Main + detail rail */}\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-4 sm:gap-6\">\n {/* Main column */}\n <div className=\"lg:col-span-2 space-y-4 sm:space-y-6\">\n {/* Stat metrics */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <GlassCard treatment=\"metric\">\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <CardTitle className=\"flex items-center gap-2 text-sm font-medium text-text-secondary\">\n <Users size={18} className=\"text-text-muted\" />\n {tWithFallback(t, 'projectDetail.membersCardTitle', 'Members')}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <p className=\"text-3xl font-bold tabular-nums\">{projectMemberCount ?? 0}</p>\n <p className=\"text-sm text-text-muted\">\n {tWithFallback(t, 'projectDetail.projectMembers', 'Project members')}\n </p>\n\n {canAddProjectMember || canListProjectMembers ? (\n <div className=\"flex flex-wrap gap-2\">\n {canAddProjectMember ? (\n <Button size=\"sm\" onClick={() => setShowAddMemberDialog(true)}>\n <UserPlus size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.addProjectMember', 'Add Member')}\n </Button>\n ) : null}\n\n {canListProjectMembers ? (\n <Button variant=\"outline\" size=\"sm\" onClick={handleViewProjectMembers}>\n <Users size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.manageProjectMembers', 'Manage Members')}\n </Button>\n ) : null}\n </div>\n ) : null}\n </CardContent>\n </GlassCard>\n\n <GlassCard treatment=\"metric\">\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <CardTitle className=\"flex items-center gap-2 text-sm font-medium text-text-secondary\">\n <Activity size={18} className=\"text-text-muted\" />\n {tWithFallback(t, 'projectDetail.activityCardTitle', 'Activity')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-3xl font-bold tabular-nums\">{relatedActivityCount}</p>\n <p className=\"text-sm text-text-muted\">\n {tWithFallback(t, 'projectDetail.recentActivityItems', 'Recent activity items')}\n </p>\n </CardContent>\n </GlassCard>\n </div>\n\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <CardHeader>\n <CardTitle>{tWithFallback(t, 'projectDetail.tagsTitle', 'Tags')}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-text-muted\">\n {tWithFallback(\n t,\n 'projectDetail.tagsDescription',\n 'Use universal tags to classify and filter this project across the workspace.'\n )}\n </p>\n <ProjectTagsWidget\n entityType=\"Project\"\n entityId={project.id}\n workspaceId={workspaceId}\n tags={widgetTags}\n onAdd={handleAddTag}\n onRemove={handleRemoveTag}\n className={\n isTaggingProject || isUntaggingProject || isUpdatingProject\n ? 'pointer-events-none opacity-75'\n : undefined\n }\n />\n </CardContent>\n </GlassCard>\n </div>\n\n {/* Detail rail */}\n <aside className=\"lg:sticky lg:top-6 lg:self-start\">\n {/* Project Information */}\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <CardHeader>\n <CardTitle>\n {tWithFallback(t, 'projectDetail.projectInformation', 'Project Information')}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid grid-cols-1 gap-4 text-sm\">\n <div className=\"space-y-1\">\n <span className=\"text-text-muted flex items-center gap-2\">\n <FolderKanban size={14} />\n {tWithFallback(t, 'projectDetail.projectId', 'Project ID')}\n </span>\n <button\n type=\"button\"\n onClick={() =>\n idModal.show({\n id: project.id,\n name: project.name,\n type: 'Project',\n details: project.key ? [{ label: 'Key', value: project.key }] : undefined,\n })\n }\n className=\"inline-flex items-center gap-1.5 text-xs font-medium text-text-link hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] rounded\"\n >\n {tWithFallback(t, 'projectDetail.getId', 'Get ID')}\n </button>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-text-secondary flex items-center gap-2\">\n <FolderKanban size={14} />\n {tWithFallback(t, 'projectDetail.projectKey', 'Project Key')}\n </span>\n <span className=\"font-medium\">{project.key}</span>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-text-secondary flex items-center gap-2\">\n <Clock size={14} />\n {tWithFallback(t, 'projectDetail.created', 'Created')}\n </span>\n <span>{formatDate(project.createdAt)}</span>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-text-secondary flex items-center gap-2\">\n <Clock size={14} />\n {tWithFallback(t, 'projectDetail.lastUpdated', 'Last Updated')}\n </span>\n <span>{formatDate(project.updatedAt)}</span>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-text-secondary flex items-center gap-2\">\n <Users size={14} />\n {tWithFallback(t, 'projectDetail.workspaceId', 'Workspace ID')}\n </span>\n <span className=\"font-mono text-xs block\">{project.workspaceId}</span>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-text-secondary flex items-center gap-2\">\n <Users size={14} />\n {tWithFallback(t, 'projectDetail.creatorId', 'Creator ID')}\n </span>\n <span className=\"font-mono text-xs block\">{project.creatorId}</span>\n </div>\n </div>\n\n {project.config && Object.keys(project.config).length > 0 && (\n <div className=\"pt-4 border-t border-border-seam\">\n <h4 className=\"text-sm font-medium mb-2\">\n {tWithFallback(t, 'projectDetail.configuration', 'Configuration')}\n </h4>\n <pre className=\"text-xs bg-bg-sunken p-3 rounded-lg overflow-auto\">\n {JSON.stringify(project.config, null, 2)}\n </pre>\n </div>\n )}\n\n {/* Quick Actions — progressive disclosure (secondary navigation) */}\n <ShowMore\n collapsedHeight={0}\n label={tWithFallback(t, 'projectDetail.quickActions', 'Quick Actions')}\n >\n <div className=\"flex flex-col gap-2 pt-2\">\n {canListProjectMembers ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"justify-start\"\n onClick={handleViewProjectMembers}\n >\n <Users size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.projectMembersPage', 'Project Members')}\n </Button>\n ) : null}\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"justify-start\"\n onClick={() =>\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/members`))\n }\n >\n <Users size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.workspaceMembers', 'Workspace Members')}\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"justify-start\"\n onClick={() =>\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/activity`))\n }\n >\n <Activity size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.workspaceActivity', 'Workspace Activity')}\n </Button>\n </div>\n </ShowMore>\n </CardContent>\n </GlassCard>\n </aside>\n </div>\n\n {/* Delete Confirmation Dialog */}\n <AlertDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>\n {tWithFallback(t, 'projectDetail.deleteDialogTitle', 'Delete Project')}\n </AlertDialogTitle>\n <AlertDialogDescription>\n {tWithFallback(\n t,\n 'projectDetail.deleteConfirmPrefix',\n 'Are you sure you want to delete \"'\n ) +\n project.name +\n tWithFallback(\n t,\n 'projectDetail.deleteConfirmSuffix',\n '\"? This action cannot be undone and all project data will be permanently removed.'\n )}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel disabled={isDeleting}>\n {tWithFallback(t, 'projectDetail.cancel', 'Cancel')}\n </AlertDialogCancel>\n <AlertDialogAction\n onClick={handleDelete}\n disabled={isDeleting}\n className=\"bg-status-error-bg text-status-error-text hover:bg-status-error-bg/90\"\n >\n {isDeleting\n ? tWithFallback(t, 'projectDetail.deleting', 'Deleting...')\n : tWithFallback(t, 'projectDetail.deleteProject', 'Delete Project')}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n\n {workspaceId && projectId && showAddMemberDialog ? (\n <ProjectMemberPickerDialog\n open={showAddMemberDialog}\n onOpenChange={setShowAddMemberDialog}\n workspaceId={workspaceId}\n projectId={projectId}\n projectName={project.name}\n />\n ) : null}\n <EntityIdModal control={idModal} />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProjectDetailPage.js","names":[],"sources":["../../src/pages/ProjectDetailPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useParams, useNavigate } from 'react-router-dom';\nimport {\n ArrowLeft,\n FolderKanban,\n Users,\n UserPlus,\n Activity,\n Archive,\n MoreVertical,\n Clock,\n} from 'lucide-react';\nimport {\n Button,\n ShowMore,\n GlassCard,\n CardHeader,\n CardTitle,\n CardContent,\n Badge,\n Skeleton,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n DropdownMenuSeparator,\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n EntityIdModal,\n useEntityIdModal,\n IllustratedEmptyState,\n PagePurpose,\n NextSteps,\n} from '@burdenoff/fe-libs/ui';\nimport { type TagItem } from '@burdenoff/fe-libs/tag';\nimport { ProjectTagsWidget } from '../components/tags/ProjectTagsWidget';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport { useProject, useProjectMembersCount } from '../hooks/useProjects';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport {\n useArchiveProject,\n useUnarchiveProject,\n useDeleteProject,\n useTagProject,\n useUntagProject,\n useUpdateProject,\n} from '../hooks/useProjectMutations';\nimport { formatDate } from '../utils/formatters';\nimport { nativeImpact, nativeNotify } from '../utils/nativeBridge';\nimport { getProjectDisplayTags, toProjectTag } from '../utils/projectTags';\nimport { getProjectStatusClasses } from '../utils/tokenHelpers';\nimport { useWorkspaceActivity } from '../hooks/useWorkspaceActivity';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport { ProjectMemberPickerDialog } from '../components/ProjectMemberPickerDialog';\n\nexport function ProjectDetailPage() {\n const { t } = useI18n();\n const idModal = useEntityIdModal();\n\n const { workspaceId, projectId } = useParams<{ workspaceId: string; projectId: string }>();\n useWorkspacesPageView('workspaces.project-detail', {\n workspaceId,\n projectId,\n requireParams: true,\n });\n const navigate = useNavigate();\n const { basePath = '/workspaces' } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const { data: project, isLoading, error } = useProject(projectId || '', workspaceId);\n const [showDeleteDialog, setShowDeleteDialog] = useState(false);\n const [showAddMemberDialog, setShowAddMemberDialog] = useState(false);\n\n const { mutate: archiveProject, isPending: isArchiving } = useArchiveProject(workspaceId);\n const { mutate: unarchiveProject, isPending: isUnarchiving } = useUnarchiveProject(workspaceId);\n const { mutate: deleteProject, isPending: isDeleting } = useDeleteProject(workspaceId);\n const { mutateAsync: tagProject, isPending: isTaggingProject } = useTagProject(workspaceId);\n const { mutateAsync: untagProject, isPending: isUntaggingProject } = useUntagProject(workspaceId);\n const { mutateAsync: updateProject, isPending: isUpdatingProject } =\n useUpdateProject(workspaceId);\n const {\n canViewProject,\n canDeleteProject,\n canArchiveProject,\n canListProjectMembers,\n canAddProjectMember,\n isLoading: permissionsLoading,\n } = useWorkspacePermissions();\n const { data: projectMemberCount } = useProjectMembersCount(projectId || '', workspaceId);\n const displayTags = useMemo(\n () =>\n getProjectDisplayTags({\n tags: project?.tags ?? [],\n tagAssignments: project?.tagAssignments,\n }),\n [project?.tags, project?.tagAssignments]\n );\n const { data: workspaceActivity } = useWorkspaceActivity(workspaceId);\n const relatedActivityCount = (workspaceActivity ?? []).filter(\n (item) => item.resourceId === projectId || item.resourceType?.toLowerCase() === 'project'\n ).length;\n const widgetTags: TagItem[] = displayTags;\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 p-6\">\n <Skeleton className=\"h-10 w-1/3\" />\n <Skeleton className=\"h-64\" />\n </div>\n );\n }\n\n if (!canViewProject) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'projectDetail.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'projectDetail.noPermissionToViewProject',\n \"You don't have permission to view this project.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n const handleBack = () => {\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/projects`));\n };\n\n const handleArchive = () => {\n if (!project) return;\n if (project.archived) {\n unarchiveProject(project.id, {\n onError: (err) => {\n console.error('Failed to unarchive project:', err);\n },\n });\n } else {\n archiveProject(project.id, {\n onError: (err) => {\n console.error('Failed to archive project:', err);\n },\n });\n }\n };\n\n const handleDelete = () => {\n if (!project) return;\n void nativeImpact('medium');\n deleteProject(project.id, {\n onSuccess: () => {\n void nativeNotify('success');\n setShowDeleteDialog(false);\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/projects`));\n },\n onError: (err) => {\n void nativeNotify('error');\n console.error('Failed to delete project:', err);\n },\n });\n };\n\n const handleViewProjectMembers = () => {\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/projects/${projectId}/members`));\n };\n\n const syncLegacyProjectTags = async (nextTags: TagItem[]) => {\n if (!project) {\n return;\n }\n\n const normalizedNextTags = nextTags.map((tag) => toProjectTag(tag));\n\n try {\n await updateProject({\n id: project.id,\n input: {\n tags: normalizedNextTags,\n },\n });\n } catch (err) {\n void nativeNotify('warning');\n console.error(\n 'Failed to sync legacy project tag mirror after updating structured project tags:',\n err\n );\n }\n };\n\n const handleAddTag = async (tag: TagItem) => {\n if (!project) {\n return;\n }\n\n try {\n await tagProject({\n entityId: project.id,\n tagId: tag.id,\n });\n\n await syncLegacyProjectTags([...displayTags, toProjectTag(tag)]);\n } catch (err) {\n void nativeNotify('error');\n throw err;\n }\n };\n\n const handleRemoveTag = async (tagId: string) => {\n if (!project) {\n return;\n }\n\n const assignmentId = project.tagAssignments?.find(\n (assignment) => assignment.tagId === tagId\n )?.id;\n try {\n await untagProject({\n entityId: project.id,\n tagId,\n assignmentId,\n });\n\n await syncLegacyProjectTags(\n displayTags.filter((tag) => tag.tagId !== tagId && tag.id !== tagId)\n );\n } catch (err) {\n void nativeNotify('error');\n throw err;\n }\n };\n\n if (isLoading) {\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n <Skeleton className=\"h-10 w-32\" />\n <Skeleton className=\"h-10 w-1/3\" />\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n </div>\n <Skeleton className=\"h-48\" />\n </div>\n );\n }\n\n if (error || !project) {\n return (\n <div className=\"space-y-4 p-6\">\n <Button variant=\"ghost\" onClick={handleBack} className=\"gap-2\">\n <ArrowLeft size={20} />\n {tWithFallback(t, 'projectDetail.backToProjects', 'Back to Projects')}\n </Button>\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title={tWithFallback(t, 'projectDetail.projectNotFound', 'Project not found')}\n description={tWithFallback(\n t,\n 'projectDetail.projectNotFoundDescription',\n \"The project you're looking for doesn't exist or you don't have permission to view it.\"\n )}\n action={\n <Button onClick={handleBack}>\n <ArrowLeft size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.backToProjects', 'Back to Projects')}\n </Button>\n }\n />\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3\">\n <div>\n <Button variant=\"ghost\" onClick={handleBack} className=\"gap-2 mb-2 -ml-2\">\n <ArrowLeft size={20} />\n {tWithFallback(t, 'projectDetail.backToProjects', 'Back to Projects')}\n </Button>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-3 rounded-lg bg-action-primary-bg/10\">\n <FolderKanban className=\"text-primary\" size={28} />\n </div>\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">{project.name}</h1>\n <p className=\"text-sm text-text-secondary font-mono\">{project.key}</p>\n </div>\n </div>\n <div className=\"flex items-center gap-2 mt-3\">\n <Badge className={`border ${getProjectStatusClasses(project.status)}`}>\n {project.status}\n </Badge>\n {project.archived && (\n <Badge\n variant=\"outline\"\n className=\"border-workspace-archived-border text-workspace-archived-text\"\n >\n <Archive size={12} className=\"mr-1\" />\n {tWithFallback(t, 'projectDetail.archived', 'Archived')}\n </Badge>\n )}\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"icon\"\n aria-label={tWithFallback(\n t,\n 'projectDetail.openProjectActionsAriaLabel',\n 'Open project actions'\n )}\n >\n <MoreVertical size={20} />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {canArchiveProject && (\n <DropdownMenuItem onClick={handleArchive} disabled={isArchiving || isUnarchiving}>\n <Archive size={16} className=\"mr-2\" />\n {isArchiving || isUnarchiving\n ? tWithFallback(t, 'projectDetail.processing', 'Processing...')\n : project.archived\n ? tWithFallback(t, 'projectDetail.unarchive', 'Unarchive')\n : tWithFallback(t, 'projectDetail.archive', 'Archive')}\n </DropdownMenuItem>\n )}\n {canArchiveProject && canDeleteProject && <DropdownMenuSeparator />}\n {canDeleteProject && (\n <DropdownMenuItem\n onClick={() => setShowDeleteDialog(true)}\n className=\"text-status-error-text\"\n >\n {tWithFallback(t, 'projectDetail.deleteProject', 'Delete Project')}\n </DropdownMenuItem>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n\n <PagePurpose>\n {tWithFallback(\n t,\n 'projectDetail.purpose',\n 'This is a single project — a focused stream of work within its workspace. From here you can manage the people working on it, organise it with tags so it surfaces in filters and reports, and keep track of related activity. Use the menu in the top-right to archive or delete it when it wraps up.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"workspaces-project-detail\"\n steps={[\n ...(canAddProjectMember\n ? [\n {\n id: 'add-member',\n label: tWithFallback(\n t,\n 'projectDetail.stepAddMember',\n 'Add people to the project'\n ),\n description: tWithFallback(\n t,\n 'projectDetail.stepAddMemberDesc',\n 'Pull in the teammates who will actually work on this project.'\n ),\n icon: <UserPlus size={16} />,\n onClick: () => setShowAddMemberDialog(true),\n },\n ]\n : []),\n {\n id: 'tag-project',\n label: tWithFallback(t, 'projectDetail.stepTag', 'Tag the project'),\n description: tWithFallback(\n t,\n 'projectDetail.stepTagDesc',\n 'Add tags so this project shows up in the right filters and views.'\n ),\n icon: <FolderKanban size={16} />,\n onClick: () => {\n document\n .getElementById('project-tags-section')\n ?.scrollIntoView({ behavior: 'smooth', block: 'center' });\n },\n },\n ...(canListProjectMembers\n ? [\n {\n id: 'manage-members',\n label: tWithFallback(\n t,\n 'projectDetail.stepManageMembers',\n 'Review project members'\n ),\n description: tWithFallback(\n t,\n 'projectDetail.stepManageMembersDesc',\n 'See who has access and adjust their project roles.'\n ),\n icon: <Users size={16} />,\n onClick: handleViewProjectMembers,\n },\n ]\n : []),\n {\n id: 'review-activity',\n label: tWithFallback(t, 'projectDetail.stepActivity', 'Check recent activity'),\n description: tWithFallback(\n t,\n 'projectDetail.stepActivityDesc',\n 'Catch up on what has changed across the workspace.'\n ),\n icon: <Activity size={16} />,\n onClick: () => navigate(withCurrentSearch(`${basePath}/${workspaceId}/activity`)),\n },\n ]}\n />\n\n {/* Main + detail rail */}\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-4 sm:gap-6\">\n {/* Main column */}\n <div className=\"lg:col-span-2 space-y-4 sm:space-y-6\">\n {/* Stat metrics */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <GlassCard treatment=\"metric\" glow>\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <CardTitle className=\"flex items-center gap-2 text-sm font-medium text-text-secondary\">\n <Users size={18} className=\"text-text-muted\" />\n {tWithFallback(t, 'projectDetail.membersCardTitle', 'Members')}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <p className=\"text-3xl font-bold tabular-nums\">{projectMemberCount ?? 0}</p>\n <p className=\"text-sm text-text-muted\">\n {tWithFallback(t, 'projectDetail.projectMembers', 'Project members')}\n </p>\n\n {canAddProjectMember || canListProjectMembers ? (\n <div className=\"flex flex-wrap gap-2\">\n {canAddProjectMember ? (\n <Button size=\"sm\" onClick={() => setShowAddMemberDialog(true)}>\n <UserPlus size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.addProjectMember', 'Add Member')}\n </Button>\n ) : null}\n\n {canListProjectMembers ? (\n <Button variant=\"outline\" size=\"sm\" onClick={handleViewProjectMembers}>\n <Users size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.manageProjectMembers', 'Manage Members')}\n </Button>\n ) : null}\n </div>\n ) : null}\n </CardContent>\n </GlassCard>\n\n <GlassCard treatment=\"metric\">\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <CardTitle className=\"flex items-center gap-2 text-sm font-medium text-text-secondary\">\n <Activity size={18} className=\"text-text-muted\" />\n {tWithFallback(t, 'projectDetail.activityCardTitle', 'Activity')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-3xl font-bold tabular-nums\">{relatedActivityCount}</p>\n <p className=\"text-sm text-text-muted\">\n {tWithFallback(t, 'projectDetail.recentActivityItems', 'Recent activity items')}\n </p>\n </CardContent>\n </GlassCard>\n </div>\n\n <GlassCard treatment=\"plain\" id=\"project-tags-section\" className=\"border-border-seam\">\n <CardHeader>\n <CardTitle>{tWithFallback(t, 'projectDetail.tagsTitle', 'Tags')}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-text-muted\">\n {tWithFallback(\n t,\n 'projectDetail.tagsDescription',\n 'Use universal tags to classify and filter this project across the workspace.'\n )}\n </p>\n <ProjectTagsWidget\n entityType=\"Project\"\n entityId={project.id}\n workspaceId={workspaceId}\n tags={widgetTags}\n onAdd={handleAddTag}\n onRemove={handleRemoveTag}\n className={\n isTaggingProject || isUntaggingProject || isUpdatingProject\n ? 'pointer-events-none opacity-75'\n : undefined\n }\n />\n </CardContent>\n </GlassCard>\n </div>\n\n {/* Detail rail */}\n <aside className=\"lg:sticky lg:top-6 lg:self-start\">\n {/* Project Information */}\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <CardHeader>\n <CardTitle>\n {tWithFallback(t, 'projectDetail.projectInformation', 'Project Information')}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid grid-cols-1 gap-4 text-sm\">\n <div className=\"space-y-1\">\n <span className=\"text-text-muted flex items-center gap-2\">\n <FolderKanban size={14} />\n {tWithFallback(t, 'projectDetail.projectId', 'Project ID')}\n </span>\n <button\n type=\"button\"\n onClick={() =>\n idModal.show({\n id: project.id,\n name: project.name,\n type: 'Project',\n details: project.key ? [{ label: 'Key', value: project.key }] : undefined,\n })\n }\n className=\"inline-flex items-center gap-1.5 text-xs font-medium text-text-link hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] rounded\"\n >\n {tWithFallback(t, 'projectDetail.getId', 'Get ID')}\n </button>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-text-secondary flex items-center gap-2\">\n <FolderKanban size={14} />\n {tWithFallback(t, 'projectDetail.projectKey', 'Project Key')}\n </span>\n <span className=\"font-medium\">{project.key}</span>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-text-secondary flex items-center gap-2\">\n <Clock size={14} />\n {tWithFallback(t, 'projectDetail.created', 'Created')}\n </span>\n <span>{formatDate(project.createdAt)}</span>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-text-secondary flex items-center gap-2\">\n <Clock size={14} />\n {tWithFallback(t, 'projectDetail.lastUpdated', 'Last Updated')}\n </span>\n <span>{formatDate(project.updatedAt)}</span>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-text-secondary flex items-center gap-2\">\n <Users size={14} />\n {tWithFallback(t, 'projectDetail.workspaceId', 'Workspace ID')}\n </span>\n <span className=\"font-mono text-xs block\">{project.workspaceId}</span>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-text-secondary flex items-center gap-2\">\n <Users size={14} />\n {tWithFallback(t, 'projectDetail.creatorId', 'Creator ID')}\n </span>\n <span className=\"font-mono text-xs block\">{project.creatorId}</span>\n </div>\n </div>\n\n {project.config && Object.keys(project.config).length > 0 && (\n <div className=\"pt-4 border-t border-border-seam\">\n <h4 className=\"text-sm font-medium mb-2\">\n {tWithFallback(t, 'projectDetail.configuration', 'Configuration')}\n </h4>\n <pre className=\"text-xs bg-bg-sunken p-3 rounded-lg overflow-auto\">\n {JSON.stringify(project.config, null, 2)}\n </pre>\n </div>\n )}\n\n {/* Quick Actions — progressive disclosure (secondary navigation) */}\n <ShowMore\n collapsedHeight={0}\n label={tWithFallback(t, 'projectDetail.quickActions', 'Quick Actions')}\n >\n <div className=\"flex flex-col gap-2 pt-2\">\n {canListProjectMembers ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"justify-start\"\n onClick={handleViewProjectMembers}\n >\n <Users size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.projectMembersPage', 'Project Members')}\n </Button>\n ) : null}\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"justify-start\"\n onClick={() =>\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/members`))\n }\n >\n <Users size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.workspaceMembers', 'Workspace Members')}\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"justify-start\"\n onClick={() =>\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/activity`))\n }\n >\n <Activity size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.workspaceActivity', 'Workspace Activity')}\n </Button>\n </div>\n </ShowMore>\n </CardContent>\n </GlassCard>\n </aside>\n </div>\n\n {/* Delete Confirmation Dialog */}\n <AlertDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>\n {tWithFallback(t, 'projectDetail.deleteDialogTitle', 'Delete Project')}\n </AlertDialogTitle>\n <AlertDialogDescription>\n {tWithFallback(\n t,\n 'projectDetail.deleteConfirmPrefix',\n 'Are you sure you want to delete \"'\n ) +\n project.name +\n tWithFallback(\n t,\n 'projectDetail.deleteConfirmSuffix',\n '\"? This action cannot be undone and all project data will be permanently removed.'\n )}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel disabled={isDeleting}>\n {tWithFallback(t, 'projectDetail.cancel', 'Cancel')}\n </AlertDialogCancel>\n <AlertDialogAction\n onClick={handleDelete}\n disabled={isDeleting}\n className=\"bg-status-error-bg text-status-error-text hover:bg-status-error-bg/90\"\n >\n {isDeleting\n ? tWithFallback(t, 'projectDetail.deleting', 'Deleting...')\n : tWithFallback(t, 'projectDetail.deleteProject', 'Delete Project')}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n\n {workspaceId && projectId && showAddMemberDialog ? (\n <ProjectMemberPickerDialog\n open={showAddMemberDialog}\n onOpenChange={setShowAddMemberDialog}\n workspaceId={workspaceId}\n projectId={projectId}\n projectName={project.name}\n />\n ) : null}\n <EntityIdModal control={idModal} />\n </div>\n );\n}\n"],"mappings":"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"}
|