@burdenoff/microfe-workspaces 2026.513.2 → 2026.518.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.
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectsListPage.d.ts","sourceRoot":"","sources":["../../src/pages/ProjectsListPage.tsx"],"names":[],"mappings":"AAiCA,wBAAgB,gBAAgB,4CAmjB/B"}
1
+ {"version":3,"file":"ProjectsListPage.d.ts","sourceRoot":"","sources":["../../src/pages/ProjectsListPage.tsx"],"names":[],"mappings":"AAkCA,wBAAgB,gBAAgB,4CAymB/B"}
@@ -1,33 +1,34 @@
1
1
  import { useWorkspacesStore as e } from "../store/workspacesStore.js";
2
2
  import { tWithFallback as t } from "../utils/i18n.js";
3
3
  import { useWorkspacesPageView as n } from "../hooks/useWorkspacesPageView.js";
4
- import { nativeConfirm as ee, nativeImpact as r } from "../utils/nativeBridge.js";
5
- import { useProjects as i } from "../hooks/useProjects.js";
6
- import { useArchiveProject as te, useDeleteProject as ne, useUnarchiveProject as re } from "../hooks/useProjectMutations.js";
7
- import { ProjectCard as ie } from "../components/ProjectCard.js";
8
- import { CreateProjectDialog as ae } from "../components/CreateProjectDialog.js";
9
- import { useWorkspacePermissions as oe } from "../hooks/useWorkspacePermissions.js";
10
- import { useEffect as a, useMemo as o, useState as s } from "react";
11
- import { FolderKanban as c, Grid3x3 as l, List as u, Plus as d, Search as se } from "lucide-react";
12
- import { Fragment as ce, jsx as f, jsxs as p } from "react/jsx-runtime";
13
- import { Button as m, Card as h, CardContent as g, Input as le, Select as _, SelectContent as v, SelectItem as y, SelectTrigger as b, SelectValue as x, Skeleton as S } from "@burdenoff/fe-libs/ui";
14
- import { useParams as ue } from "react-router-dom";
15
- import { useDebounce as de } from "@burdenoff/fe-libs/shared/hooks";
16
- import { useI18n as fe } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
17
- import { TagBadge as pe } from "@burdenoff/fe-libs/tag";
4
+ import { nativeConfirm as r, nativeImpact as i } from "../utils/nativeBridge.js";
5
+ import { useMyProjects as a } from "../hooks/useProjects.js";
6
+ import { useArchiveProject as o, useDeleteProject as s, useUnarchiveProject as c } from "../hooks/useProjectMutations.js";
7
+ import { normalizeProjectTags as ee } from "../utils/projectTags.js";
8
+ import { ProjectCard as l } from "../components/ProjectCard.js";
9
+ import { CreateProjectDialog as u } from "../components/CreateProjectDialog.js";
10
+ import { useWorkspacePermissions as d } from "../hooks/useWorkspacePermissions.js";
11
+ import { useEffect as f, useMemo as p, useState as m } from "react";
12
+ import { FolderKanban as te, Grid3x3 as ne, List as re, Plus as h, Search as ie } from "lucide-react";
13
+ import { Fragment as ae, jsx as g, jsxs as _ } from "react/jsx-runtime";
14
+ import { Button as v, Card as y, CardContent as b, Input as oe, Select as x, SelectContent as S, SelectItem as C, SelectTrigger as w, SelectValue as T, Skeleton as E } from "@burdenoff/fe-libs/ui";
15
+ import { useParams as se } from "react-router-dom";
16
+ import { useDebounce as ce } from "@burdenoff/fe-libs/shared/hooks";
17
+ import { useI18n as le } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
18
+ import { TagBadge as ue } from "@burdenoff/fe-libs/tag";
18
19
  //#region src/pages/ProjectsListPage.tsx
19
- function C() {
20
- let { t: C } = fe(), { workspaceId: w } = ue();
20
+ function D() {
21
+ let { t: D } = le(), { workspaceId: O } = se();
21
22
  n("workspaces.projects", {
22
- workspaceId: w,
23
+ workspaceId: O,
23
24
  requireParams: !0
24
25
  });
25
- let { viewMode: T, setViewMode: E, projectFilters: D, setProjectFilters: O, clearProjectFilters: k, selectedProjectIds: A, toggleProjectSelection: j, clearProjectSelection: M, openCreateProjectDialog: N, isCreateProjectDialogOpen: P } = e(), [F, I] = s(D.search || ""), [L, R] = s([]), [z, B] = s(!0), V = de(F, 500);
26
- a(() => {
27
- O({ search: V });
28
- }, [V, O]), a(() => {
29
- if (L.length === 0) {
30
- O({
26
+ let { viewMode: k, setViewMode: A, projectFilters: j, setProjectFilters: M, clearProjectFilters: de, selectedProjectIds: N, toggleProjectSelection: P, clearProjectSelection: F, openCreateProjectDialog: fe, isCreateProjectDialogOpen: pe } = e(), [I, L] = m(j.search || ""), [R, z] = m([]), [B, V] = m(!0), H = ce(I, 500);
27
+ f(() => {
28
+ M({ search: H });
29
+ }, [H, M]), f(() => {
30
+ if (R.length === 0) {
31
+ M({
31
32
  tagIds: void 0,
32
33
  tagHierarchyPaths: void 0,
33
34
  matchAllTags: void 0,
@@ -35,193 +36,200 @@ function C() {
35
36
  });
36
37
  return;
37
38
  }
38
- O({
39
- tagIds: L.map((e) => e.tagId),
40
- tagHierarchyPaths: L.map((e) => e.hierarchyPath).filter((e) => typeof e == "string" && e.length > 0),
41
- matchAllTags: z,
39
+ M({
40
+ tagIds: R.map((e) => e.tagId),
41
+ tagHierarchyPaths: R.map((e) => e.hierarchyPath).filter((e) => typeof e == "string" && e.length > 0),
42
+ matchAllTags: B,
42
43
  includeDescendants: !0
43
44
  });
44
45
  }, [
45
- L,
46
- z,
46
+ R,
47
+ B,
48
+ M
49
+ ]), f(() => () => F(), [F]);
50
+ let { data: U, isLoading: W, error: G } = a({
51
+ limit: 500,
52
+ offset: 0
53
+ }), K = p(() => {
54
+ let e = U?.items ?? [], t = (j.search ?? "").trim().toLowerCase(), n = j.tagIds ?? [], r = j.matchAllTags ?? !0, i = (e) => !O || e.workspaceId === O, a = (e) => !t || e.name.toLowerCase().includes(t) || e.key.toLowerCase().includes(t), o = (e) => !j.status || e.status === j.status, s = (e) => j.archived === void 0 || e.archived === j.archived, c = (e) => {
55
+ if (n.length === 0) return !0;
56
+ let t = new Set(ee(e.tags).map((e) => e.tagId));
57
+ return r ? n.every((e) => t.has(e)) : n.some((e) => t.has(e));
58
+ }, l = e.filter((e) => i(e) && a(e) && o(e) && s(e) && c(e)), u = U?.total ?? l.length, d = U?.hasMore ?? !1;
59
+ return {
60
+ items: l,
61
+ total: O || n.length > 0 ? l.length : u,
62
+ hasMore: d
63
+ };
64
+ }, [
65
+ U?.items,
66
+ U?.total,
67
+ U?.hasMore,
68
+ j,
47
69
  O
48
- ]), a(() => () => M(), [M]);
49
- let { data: H, isLoading: U, error: W } = i(D, void 0, w), { mutateAsync: me } = te(w), { mutateAsync: he } = re(w), { mutateAsync: ge } = ne(w), G = o(() => H?.items.filter((e) => A.has(e.id)) ?? [], [H?.items, A]), K = o(() => G.filter((e) => !e.archived), [G]), q = o(() => G.filter((e) => e.archived), [G]), J = (H?.items.length ?? 0) > 0 && (H?.items ?? []).every((e) => A.has(e.id)), _e = (H?.items ?? []).some((e) => A.has(e.id)) && !J, { canListProjects: ve, canCreateProject: Y, canDeleteProject: X, canArchiveProject: Z, isLoading: ye } = oe();
50
- if (ye) return /* @__PURE__ */ p("div", {
70
+ ]), { mutateAsync: me } = o(O), { mutateAsync: he } = c(O), { mutateAsync: ge } = s(O), q = p(() => K?.items.filter((e) => N.has(e.id)) ?? [], [K?.items, N]), J = p(() => q.filter((e) => !e.archived), [q]), Y = p(() => q.filter((e) => e.archived), [q]), X = (K?.items.length ?? 0) > 0 && (K?.items ?? []).every((e) => N.has(e.id)), _e = (K?.items ?? []).some((e) => N.has(e.id)) && !X, { canCreateProject: ve, canDeleteProject: Z, canArchiveProject: Q, isLoading: ye } = d();
71
+ if (ye) return /* @__PURE__ */ _("div", {
51
72
  className: "space-y-4 p-6",
52
- children: [/* @__PURE__ */ f(S, { className: "h-10 w-1/3" }), /* @__PURE__ */ p("div", {
73
+ children: [/* @__PURE__ */ g(E, { className: "h-10 w-1/3" }), /* @__PURE__ */ _("div", {
53
74
  className: "grid grid-cols-1 md:grid-cols-3 gap-4",
54
75
  children: [
55
- /* @__PURE__ */ f(S, { className: "h-32" }),
56
- /* @__PURE__ */ f(S, { className: "h-32" }),
57
- /* @__PURE__ */ f(S, { className: "h-32" })
76
+ /* @__PURE__ */ g(E, { className: "h-32" }),
77
+ /* @__PURE__ */ g(E, { className: "h-32" }),
78
+ /* @__PURE__ */ g(E, { className: "h-32" })
58
79
  ]
59
80
  })]
60
81
  });
61
- if (!ve) return /* @__PURE__ */ f("div", {
62
- className: "flex items-center justify-center min-h-[400px]",
63
- children: /* @__PURE__ */ p("div", {
64
- className: "text-center space-y-2",
65
- children: [/* @__PURE__ */ f("h2", {
66
- className: "text-lg font-semibold",
67
- children: t(C, "projectsList.accessDenied", "Access Denied")
68
- }), /* @__PURE__ */ f("p", {
69
- className: "text-sm text-muted-foreground",
70
- children: t(C, "projectsList.noPermissionToViewProjects", "You don't have permission to view projects.")
71
- })]
72
- })
73
- });
74
- let Q = () => {
75
- N({});
76
- }, be = (e) => {
77
- if (e === "all") {
78
- let e = { ...D };
79
- delete e.status, O(e);
80
- } else O({ status: e });
82
+ let be = () => {
83
+ fe({});
81
84
  }, xe = (e) => {
82
85
  if (e === "all") {
83
- let e = { ...D };
84
- delete e.archived, O(e);
85
- } else O({ archived: e === "true" });
86
+ let e = { ...j };
87
+ delete e.status, M(e);
88
+ } else M({ status: e });
89
+ }, Se = (e) => {
90
+ if (e === "all") {
91
+ let e = { ...j };
92
+ delete e.archived, M(e);
93
+ } else M({ archived: e === "true" });
86
94
  }, $ = (e) => {
87
- R((t) => t.some((t) => t.tagId === e.tagId) ? t.filter((t) => t.tagId !== e.tagId) : [...t, e]);
88
- }, Se = () => {
89
- I(""), R([]), B(!0), k();
90
- }, Ce = (e) => {
91
- (H?.items ?? []).forEach((t) => {
92
- e !== A.has(t.id) && j(t.id);
95
+ z((t) => t.some((t) => t.tagId === e.tagId) ? t.filter((t) => t.tagId !== e.tagId) : [...t, e]);
96
+ }, Ce = () => {
97
+ L(""), z([]), V(!0), de();
98
+ }, we = (e) => {
99
+ (K?.items ?? []).forEach((t) => {
100
+ e !== N.has(t.id) && P(t.id);
93
101
  });
94
102
  };
95
- return /* @__PURE__ */ p("div", {
103
+ return /* @__PURE__ */ _("div", {
96
104
  className: "space-y-4 sm:space-y-6 p-3 sm:p-6",
97
105
  children: [
98
- /* @__PURE__ */ p("div", {
106
+ /* @__PURE__ */ _("div", {
99
107
  className: "flex flex-col sm:flex-row sm:items-center justify-between gap-4",
100
- children: [/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("h1", {
108
+ children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("h1", {
101
109
  className: "text-2xl sm:text-3xl font-bold",
102
- children: t(C, "projectsList.pageTitle", "Projects")
103
- }), /* @__PURE__ */ f("p", {
110
+ children: t(D, "projectsList.pageTitle", "Projects")
111
+ }), /* @__PURE__ */ g("p", {
104
112
  className: "text-muted-foreground",
105
- children: t(C, "projectsList.pageSubtitle", "Manage and organize your projects")
106
- })] }), /* @__PURE__ */ p("div", {
113
+ children: t(D, "projectsList.pageSubtitle", "Manage and organize your projects")
114
+ })] }), /* @__PURE__ */ _("div", {
107
115
  className: "flex items-center gap-2",
108
- children: [/* @__PURE__ */ p("div", {
116
+ children: [/* @__PURE__ */ _("div", {
109
117
  className: "flex items-center gap-1 border rounded-lg p-1",
110
- children: [/* @__PURE__ */ f(m, {
111
- variant: T === "grid" ? "secondary" : "ghost",
118
+ children: [/* @__PURE__ */ g(v, {
119
+ variant: k === "grid" ? "secondary" : "ghost",
112
120
  size: "sm",
113
- onClick: () => E("grid"),
121
+ onClick: () => A("grid"),
114
122
  className: "h-8 w-8 p-0",
115
- "aria-label": t(C, "projectsList.gridViewAriaLabel", "Show projects in grid view"),
116
- children: /* @__PURE__ */ f(l, { size: 16 })
117
- }), /* @__PURE__ */ f(m, {
118
- variant: T === "list" ? "secondary" : "ghost",
123
+ "aria-label": t(D, "projectsList.gridViewAriaLabel", "Show projects in grid view"),
124
+ children: /* @__PURE__ */ g(ne, { size: 16 })
125
+ }), /* @__PURE__ */ g(v, {
126
+ variant: k === "list" ? "secondary" : "ghost",
119
127
  size: "sm",
120
- onClick: () => E("list"),
128
+ onClick: () => A("list"),
121
129
  className: "h-8 w-8 p-0",
122
- "aria-label": t(C, "projectsList.listViewAriaLabel", "Show projects in list view"),
123
- children: /* @__PURE__ */ f(u, { size: 16 })
130
+ "aria-label": t(D, "projectsList.listViewAriaLabel", "Show projects in list view"),
131
+ children: /* @__PURE__ */ g(re, { size: 16 })
124
132
  })]
125
- }), Y && /* @__PURE__ */ p(m, {
126
- onClick: Q,
127
- children: [/* @__PURE__ */ f(d, { className: "h-4 w-4 sm:mr-2" }), /* @__PURE__ */ f("span", {
133
+ }), ve && /* @__PURE__ */ _(v, {
134
+ onClick: be,
135
+ children: [/* @__PURE__ */ g(h, { className: "h-4 w-4 sm:mr-2" }), /* @__PURE__ */ g("span", {
128
136
  className: "hidden sm:inline",
129
- children: t(C, "projectsList.createProject", "Create Project")
137
+ children: t(D, "projectsList.createProject", "Create Project")
130
138
  })]
131
139
  })]
132
140
  })]
133
141
  }),
134
- /* @__PURE__ */ p("div", {
142
+ /* @__PURE__ */ _("div", {
135
143
  className: "flex flex-col sm:flex-row gap-3",
136
144
  children: [
137
- /* @__PURE__ */ p("div", {
145
+ /* @__PURE__ */ _("div", {
138
146
  className: "relative flex-1",
139
- children: [/* @__PURE__ */ f(se, {
147
+ children: [/* @__PURE__ */ g(ie, {
140
148
  className: "absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground",
141
149
  size: 18
142
- }), /* @__PURE__ */ f(le, {
143
- placeholder: t(C, "projectsList.searchPlaceholder", "Search projects..."),
144
- value: F,
145
- onChange: (e) => I(e.target.value),
150
+ }), /* @__PURE__ */ g(oe, {
151
+ placeholder: t(D, "projectsList.searchPlaceholder", "Search projects..."),
152
+ value: I,
153
+ onChange: (e) => L(e.target.value),
146
154
  className: "pl-[3.25rem]"
147
155
  })]
148
156
  }),
149
- /* @__PURE__ */ p(_, {
150
- value: D.status || "all",
151
- onValueChange: be,
152
- children: [/* @__PURE__ */ f(b, {
157
+ /* @__PURE__ */ _(x, {
158
+ value: j.status || "all",
159
+ onValueChange: xe,
160
+ children: [/* @__PURE__ */ g(w, {
153
161
  className: "w-full sm:w-[180px]",
154
- children: /* @__PURE__ */ f(x, { placeholder: t(C, "projectsList.filterByStatus", "Filter by status") })
155
- }), /* @__PURE__ */ p(v, { children: [
156
- /* @__PURE__ */ f(y, {
162
+ children: /* @__PURE__ */ g(T, { placeholder: t(D, "projectsList.filterByStatus", "Filter by status") })
163
+ }), /* @__PURE__ */ _(S, { children: [
164
+ /* @__PURE__ */ g(C, {
157
165
  value: "all",
158
- children: t(C, "projectsList.statusAll", "All Statuses")
166
+ children: t(D, "projectsList.statusAll", "All Statuses")
159
167
  }),
160
- /* @__PURE__ */ f(y, {
168
+ /* @__PURE__ */ g(C, {
161
169
  value: "ACTIVE",
162
- children: t(C, "projectsList.statusActive", "Active")
170
+ children: t(D, "projectsList.statusActive", "Active")
163
171
  }),
164
- /* @__PURE__ */ f(y, {
172
+ /* @__PURE__ */ g(C, {
165
173
  value: "INACTIVE",
166
- children: t(C, "projectsList.statusInactive", "Inactive")
174
+ children: t(D, "projectsList.statusInactive", "Inactive")
167
175
  })
168
176
  ] })]
169
177
  }),
170
- /* @__PURE__ */ p(_, {
171
- value: D.archived === void 0 ? "all" : D.archived ? "true" : "false",
172
- onValueChange: xe,
173
- children: [/* @__PURE__ */ f(b, {
178
+ /* @__PURE__ */ _(x, {
179
+ value: j.archived === void 0 ? "all" : j.archived ? "true" : "false",
180
+ onValueChange: Se,
181
+ children: [/* @__PURE__ */ g(w, {
174
182
  className: "w-full sm:w-[180px]",
175
- children: /* @__PURE__ */ f(x, { placeholder: t(C, "projectsList.filterByArchive", "Filter by archive") })
176
- }), /* @__PURE__ */ p(v, { children: [
177
- /* @__PURE__ */ f(y, {
183
+ children: /* @__PURE__ */ g(T, { placeholder: t(D, "projectsList.filterByArchive", "Filter by archive") })
184
+ }), /* @__PURE__ */ _(S, { children: [
185
+ /* @__PURE__ */ g(C, {
178
186
  value: "all",
179
- children: t(C, "projectsList.archiveAll", "All Projects")
187
+ children: t(D, "projectsList.archiveAll", "All Projects")
180
188
  }),
181
- /* @__PURE__ */ f(y, {
189
+ /* @__PURE__ */ g(C, {
182
190
  value: "false",
183
- children: t(C, "projectsList.archiveActiveProjects", "Active Projects")
191
+ children: t(D, "projectsList.archiveActiveProjects", "Active Projects")
184
192
  }),
185
- /* @__PURE__ */ f(y, {
193
+ /* @__PURE__ */ g(C, {
186
194
  value: "true",
187
- children: t(C, "projectsList.archiveArchived", "Archived")
195
+ children: t(D, "projectsList.archiveArchived", "Archived")
188
196
  })
189
197
  ] })]
190
198
  }),
191
- (D.search || D.status || D.archived !== void 0 || L.length > 0) && /* @__PURE__ */ f(m, {
199
+ (j.search || j.status || j.archived !== void 0 || R.length > 0) && /* @__PURE__ */ g(v, {
192
200
  variant: "outline",
193
- onClick: Se,
194
- children: t(C, "projectsList.clearFilters", "Clear Filters")
201
+ onClick: Ce,
202
+ children: t(D, "projectsList.clearFilters", "Clear Filters")
195
203
  })
196
204
  ]
197
205
  }),
198
- L.length > 0 && /* @__PURE__ */ p("div", {
206
+ R.length > 0 && /* @__PURE__ */ _("div", {
199
207
  className: "flex flex-col gap-3 rounded-lg border border-border bg-card px-4 py-3",
200
- children: [/* @__PURE__ */ p("div", {
208
+ children: [/* @__PURE__ */ _("div", {
201
209
  className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",
202
- children: [/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("p", {
210
+ children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
203
211
  className: "text-sm font-medium",
204
- children: t(C, "projectsList.activeTagFilters", "Active tag filters")
205
- }), /* @__PURE__ */ f("p", {
212
+ children: t(D, "projectsList.activeTagFilters", "Active tag filters")
213
+ }), /* @__PURE__ */ g("p", {
206
214
  className: "text-xs text-muted-foreground",
207
- children: t(C, "projectsList.activeTagFiltersDescription", "Click a project tag to add or remove it from the current filter set.")
208
- })] }), /* @__PURE__ */ p(_, {
209
- value: z ? "all" : "any",
210
- onValueChange: (e) => B(e === "all"),
211
- children: [/* @__PURE__ */ f(b, {
215
+ children: t(D, "projectsList.activeTagFiltersDescription", "Click a project tag to add or remove it from the current filter set.")
216
+ })] }), /* @__PURE__ */ _(x, {
217
+ value: B ? "all" : "any",
218
+ onValueChange: (e) => V(e === "all"),
219
+ children: [/* @__PURE__ */ g(w, {
212
220
  className: "w-full sm:w-[180px]",
213
- children: /* @__PURE__ */ f(x, { placeholder: t(C, "projectsList.tagMatchMode", "Tag match mode") })
214
- }), /* @__PURE__ */ p(v, { children: [/* @__PURE__ */ f(y, {
221
+ children: /* @__PURE__ */ g(T, { placeholder: t(D, "projectsList.tagMatchMode", "Tag match mode") })
222
+ }), /* @__PURE__ */ _(S, { children: [/* @__PURE__ */ g(C, {
215
223
  value: "all",
216
- children: t(C, "projectsList.tagMatchAll", "Match all tags")
217
- }), /* @__PURE__ */ f(y, {
224
+ children: t(D, "projectsList.tagMatchAll", "Match all tags")
225
+ }), /* @__PURE__ */ g(C, {
218
226
  value: "any",
219
- children: t(C, "projectsList.tagMatchAny", "Match any tag")
227
+ children: t(D, "projectsList.tagMatchAny", "Match any tag")
220
228
  })] })]
221
229
  })]
222
- }), /* @__PURE__ */ f("div", {
230
+ }), /* @__PURE__ */ g("div", {
223
231
  className: "flex flex-wrap gap-2",
224
- children: L.map((e) => /* @__PURE__ */ f(pe, {
232
+ children: R.map((e) => /* @__PURE__ */ g(ue, {
225
233
  id: e.tagId,
226
234
  label: e.label,
227
235
  color: e.color ?? void 0,
@@ -233,170 +241,170 @@ function C() {
233
241
  }, e.tagId))
234
242
  })]
235
243
  }),
236
- W && /* @__PURE__ */ f(h, {
244
+ G && /* @__PURE__ */ g(y, {
237
245
  className: "border-destructive",
238
- children: /* @__PURE__ */ f(g, {
246
+ children: /* @__PURE__ */ g(b, {
239
247
  className: "pt-6",
240
- children: /* @__PURE__ */ p("div", {
248
+ children: /* @__PURE__ */ _("div", {
241
249
  className: "flex flex-col items-center justify-center py-8 text-center",
242
- children: [/* @__PURE__ */ f("p", {
250
+ children: [/* @__PURE__ */ g("p", {
243
251
  className: "text-destructive font-semibold mb-2",
244
- children: t(C, "projectsList.errorLoading", "Error loading projects")
245
- }), /* @__PURE__ */ f("p", {
252
+ children: t(D, "projectsList.errorLoading", "Error loading projects")
253
+ }), /* @__PURE__ */ g("p", {
246
254
  className: "text-sm text-muted-foreground",
247
- children: W instanceof Error ? W.message : t(C, "projectsList.unknownError", "An unknown error occurred")
255
+ children: G instanceof Error ? G.message : t(D, "projectsList.unknownError", "An unknown error occurred")
248
256
  })]
249
257
  })
250
258
  })
251
259
  }),
252
- U && /* @__PURE__ */ f("div", {
260
+ W && /* @__PURE__ */ g("div", {
253
261
  className: "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4",
254
- children: Array.from({ length: 6 }).map((e, t) => /* @__PURE__ */ f(h, { children: /* @__PURE__ */ f(g, {
262
+ children: Array.from({ length: 6 }).map((e, t) => /* @__PURE__ */ g(y, { children: /* @__PURE__ */ g(b, {
255
263
  className: "pt-6",
256
- children: /* @__PURE__ */ p("div", {
264
+ children: /* @__PURE__ */ _("div", {
257
265
  className: "space-y-3",
258
266
  children: [
259
- /* @__PURE__ */ f(S, { className: "h-12 w-12 rounded-lg" }),
260
- /* @__PURE__ */ f(S, { className: "h-4 w-3/4" }),
261
- /* @__PURE__ */ f(S, { className: "h-4 w-1/2" })
267
+ /* @__PURE__ */ g(E, { className: "h-12 w-12 rounded-lg" }),
268
+ /* @__PURE__ */ g(E, { className: "h-4 w-3/4" }),
269
+ /* @__PURE__ */ g(E, { className: "h-4 w-1/2" })
262
270
  ]
263
271
  })
264
272
  }) }, t))
265
273
  }),
266
- !U && !W && H?.items.length === 0 && /* @__PURE__ */ f(h, { children: /* @__PURE__ */ f(g, {
274
+ !W && !G && K?.items.length === 0 && /* @__PURE__ */ g(y, { children: /* @__PURE__ */ g(b, {
267
275
  className: "pt-6",
268
- children: /* @__PURE__ */ p("div", {
276
+ children: /* @__PURE__ */ _("div", {
269
277
  className: "flex flex-col items-center justify-center py-12 text-center",
270
278
  children: [
271
- /* @__PURE__ */ f("div", {
279
+ /* @__PURE__ */ g("div", {
272
280
  className: "rounded-full bg-muted p-6 mb-4",
273
- children: /* @__PURE__ */ f(c, {
281
+ children: /* @__PURE__ */ g(te, {
274
282
  size: 48,
275
283
  className: "text-muted-foreground"
276
284
  })
277
285
  }),
278
- /* @__PURE__ */ f("h3", {
286
+ /* @__PURE__ */ g("h3", {
279
287
  className: "text-lg font-semibold mb-2",
280
- children: t(C, "projectsList.noProjectsFound", "No projects found")
288
+ children: t(D, "projectsList.noProjectsFound", "No projects found")
281
289
  }),
282
- /* @__PURE__ */ f("p", {
290
+ /* @__PURE__ */ g("p", {
283
291
  className: "text-muted-foreground max-w-sm mb-4",
284
- children: D.search ? t(C, "projectsList.adjustSearchOrFilters", "Try adjusting your search or filters") : t(C, "projectsList.createFirstProject", "Create your first project to get started organizing your work")
292
+ children: j.search ? t(D, "projectsList.adjustSearchOrFilters", "Try adjusting your search or filters") : t(D, "projectsList.createFirstProject", "Create your first project to get started organizing your work")
285
293
  }),
286
- !D.search && Y && /* @__PURE__ */ p(m, {
287
- onClick: Q,
288
- children: [/* @__PURE__ */ f(d, {
294
+ !j.search && ve && /* @__PURE__ */ _(v, {
295
+ onClick: be,
296
+ children: [/* @__PURE__ */ g(h, {
289
297
  size: 16,
290
298
  className: "mr-2"
291
- }), t(C, "projectsList.createProject", "Create Project")]
299
+ }), t(D, "projectsList.createProject", "Create Project")]
292
300
  })
293
301
  ]
294
302
  })
295
303
  }) }),
296
- !U && !W && H && H.items.length > 0 && /* @__PURE__ */ p(ce, { children: [
297
- /* @__PURE__ */ p("div", {
304
+ !W && !G && K && K.items.length > 0 && /* @__PURE__ */ _(ae, { children: [
305
+ /* @__PURE__ */ _("div", {
298
306
  className: "flex flex-col gap-3 rounded-lg border bg-card p-4 sm:flex-row sm:items-center sm:justify-between",
299
- children: [/* @__PURE__ */ p("label", {
307
+ children: [/* @__PURE__ */ _("label", {
300
308
  className: "flex items-center gap-2 text-sm text-foreground",
301
- children: [/* @__PURE__ */ f("input", {
309
+ children: [/* @__PURE__ */ g("input", {
302
310
  type: "checkbox",
303
- checked: J,
311
+ checked: X,
304
312
  ref: (e) => {
305
- e && (e.indeterminate = _e && !J);
313
+ e && (e.indeterminate = _e && !X);
306
314
  },
307
- onChange: (e) => Ce(e.target.checked),
308
- "aria-label": t(C, "projectsList.selectAllAriaLabel", "Select all projects"),
315
+ onChange: (e) => we(e.target.checked),
316
+ "aria-label": t(D, "projectsList.selectAllAriaLabel", "Select all projects"),
309
317
  className: "h-4 w-4 rounded border-border text-primary"
310
- }), /* @__PURE__ */ f("span", { children: t(C, "projectsList.selectAllVisible", "Select all visible") })]
311
- }), /* @__PURE__ */ p("div", {
318
+ }), /* @__PURE__ */ g("span", { children: t(D, "projectsList.selectAllVisible", "Select all visible") })]
319
+ }), /* @__PURE__ */ _("div", {
312
320
  className: "flex flex-wrap items-center gap-2",
313
321
  children: [
314
- /* @__PURE__ */ p("span", {
322
+ /* @__PURE__ */ _("span", {
315
323
  className: "text-sm text-muted-foreground",
316
324
  children: [
317
- A.size,
325
+ N.size,
318
326
  " ",
319
- t(C, "projectsList.selected", "selected")
327
+ t(D, "projectsList.selected", "selected")
320
328
  ]
321
329
  }),
322
- A.size > 0 && /* @__PURE__ */ f(m, {
330
+ N.size > 0 && /* @__PURE__ */ g(v, {
323
331
  variant: "ghost",
324
332
  size: "sm",
325
- onClick: M,
326
- children: t(C, "projectsList.clearSelection", "Clear selection")
333
+ onClick: F,
334
+ children: t(D, "projectsList.clearSelection", "Clear selection")
327
335
  }),
328
- Z && /* @__PURE__ */ p(m, {
336
+ O && Q && /* @__PURE__ */ _(v, {
329
337
  variant: "outline",
330
338
  size: "sm",
331
339
  onClick: async () => {
332
- K.length !== 0 && (await Promise.allSettled(K.map((e) => me(e.id))), M());
340
+ J.length !== 0 && (await Promise.allSettled(J.map((e) => me(e.id))), F());
333
341
  },
334
- disabled: K.length === 0,
335
- children: [t(C, "projectsList.archiveSelected", "Archive Selected"), K.length > 0 ? ` (${K.length})` : ""]
342
+ disabled: J.length === 0,
343
+ children: [t(D, "projectsList.archiveSelected", "Archive Selected"), J.length > 0 ? ` (${J.length})` : ""]
336
344
  }),
337
- Z && /* @__PURE__ */ p(m, {
345
+ O && Q && /* @__PURE__ */ _(v, {
338
346
  variant: "outline",
339
347
  size: "sm",
340
348
  onClick: async () => {
341
- q.length !== 0 && (await Promise.allSettled(q.map((e) => he(e.id))), M());
349
+ Y.length !== 0 && (await Promise.allSettled(Y.map((e) => he(e.id))), F());
342
350
  },
343
- disabled: q.length === 0,
344
- children: [t(C, "projectsList.unarchiveSelected", "Unarchive Selected"), q.length > 0 ? ` (${q.length})` : ""]
351
+ disabled: Y.length === 0,
352
+ children: [t(D, "projectsList.unarchiveSelected", "Unarchive Selected"), Y.length > 0 ? ` (${Y.length})` : ""]
345
353
  }),
346
- X && /* @__PURE__ */ p(m, {
354
+ O && Z && /* @__PURE__ */ _(v, {
347
355
  variant: "outline",
348
356
  size: "sm",
349
357
  onClick: async () => {
350
- if (G.length === 0) return;
351
- r("medium");
352
- let e = t(C, "projectsList.bulkDeleteConfirmPrefix", "Delete ") + G.length + " " + t(C, "projectsList.bulkDeleteConfirmProject", "selected project") + (G.length === 1 ? "" : t(C, "projectsList.bulkDeleteConfirmPluralSuffix", "s")) + t(C, "projectsList.bulkDeleteConfirmSuffix", "? This cannot be undone."), n = await ee({
358
+ if (q.length === 0) return;
359
+ i("medium");
360
+ let e = t(D, "projectsList.bulkDeleteConfirmPrefix", "Delete ") + q.length + " " + t(D, "projectsList.bulkDeleteConfirmProject", "selected project") + (q.length === 1 ? "" : t(D, "projectsList.bulkDeleteConfirmPluralSuffix", "s")) + t(D, "projectsList.bulkDeleteConfirmSuffix", "? This cannot be undone."), n = await r({
353
361
  title: "Delete projects",
354
362
  message: e,
355
363
  okButtonTitle: "Delete",
356
364
  cancelButtonTitle: "Cancel"
357
365
  });
358
- (n === null ? window.confirm(e) : n) && (await Promise.allSettled(G.map((e) => ge(e.id))), M());
366
+ (n === null ? window.confirm(e) : n) && (await Promise.allSettled(q.map((e) => ge(e.id))), F());
359
367
  },
360
- disabled: G.length === 0,
368
+ disabled: q.length === 0,
361
369
  className: "text-destructive",
362
- children: [t(C, "projectsList.deleteSelected", "Delete Selected"), G.length > 0 ? ` (${G.length})` : ""]
370
+ children: [t(D, "projectsList.deleteSelected", "Delete Selected"), q.length > 0 ? ` (${q.length})` : ""]
363
371
  })
364
372
  ]
365
373
  })]
366
374
  }),
367
- /* @__PURE__ */ f("div", {
368
- className: T === "list" ? "space-y-3" : "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4",
369
- children: H.items.map((e) => /* @__PURE__ */ f(ie, {
375
+ /* @__PURE__ */ g("div", {
376
+ className: k === "list" ? "space-y-3" : "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4",
377
+ children: K.items.map((e) => /* @__PURE__ */ g(l, {
370
378
  project: e,
371
- selected: A.has(e.id),
372
- canArchive: Z,
373
- canDelete: X,
379
+ selected: N.has(e.id),
380
+ canArchive: Q,
381
+ canDelete: Z,
374
382
  onTagClick: $,
375
383
  onToggleSelect: (t) => {
376
- t !== A.has(e.id) && j(e.id);
384
+ t !== N.has(e.id) && P(e.id);
377
385
  }
378
386
  }, e.id))
379
387
  }),
380
- H.total > H.items.length && /* @__PURE__ */ p("div", {
388
+ K.total > K.items.length && /* @__PURE__ */ _("div", {
381
389
  className: "text-center text-sm text-muted-foreground",
382
390
  children: [
383
- t(C, "projectsList.showingPrefix", "Showing"),
391
+ t(D, "projectsList.showingPrefix", "Showing"),
384
392
  " ",
385
- H.items.length,
393
+ K.items.length,
386
394
  " ",
387
- t(C, "projectsList.showingOf", "of"),
395
+ t(D, "projectsList.showingOf", "of"),
388
396
  " ",
389
- H.total,
397
+ K.total,
390
398
  " ",
391
- t(C, "projectsList.showingProjects", "projects")
399
+ t(D, "projectsList.showingProjects", "projects")
392
400
  ]
393
401
  })
394
402
  ] }),
395
- P && /* @__PURE__ */ f(ae, { workspaceId: w })
403
+ pe && /* @__PURE__ */ g(u, { workspaceId: O })
396
404
  ]
397
405
  });
398
406
  }
399
407
  //#endregion
400
- export { C as ProjectsListPage };
408
+ export { D as ProjectsListPage };
401
409
 
402
410
  //# sourceMappingURL=ProjectsListPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectsListPage.js","names":[],"sources":["../../src/pages/ProjectsListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { FolderKanban, Plus, Grid3x3, List, Search } from 'lucide-react';\nimport { TagBadge } from '@burdenoff/fe-libs/tag';\nimport {\n Button,\n Input,\n Card,\n CardContent,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Skeleton,\n} from '@burdenoff/fe-libs/ui';\nimport { useDebounce } from '@burdenoff/fe-libs/shared/hooks';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useWorkspacesStore } from '../store/workspacesStore';\nimport { nativeConfirm, nativeImpact } from '../utils/nativeBridge';\nimport { useProjects } from '../hooks/useProjects';\nimport {\n useArchiveProject,\n useDeleteProject,\n useUnarchiveProject,\n} from '../hooks/useProjectMutations';\nimport { ProjectCard } from '../components/ProjectCard';\nimport { CreateProjectDialog } from '../components/CreateProjectDialog';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport { tWithFallback } from '../utils/i18n';\nimport type { ProjectTag, Workspace } from '../types';\n\nexport function ProjectsListPage() {\n const { t } = useI18n();\n\n const { workspaceId } = useParams<{ workspaceId: string }>();\n useWorkspacesPageView('workspaces.projects', { workspaceId, requireParams: true });\n const {\n viewMode,\n setViewMode,\n projectFilters,\n setProjectFilters,\n clearProjectFilters,\n selectedProjectIds,\n toggleProjectSelection,\n clearProjectSelection,\n openCreateProjectDialog,\n isCreateProjectDialogOpen,\n } = useWorkspacesStore();\n\n const [searchValue, setSearchValue] = useState(projectFilters.search || '');\n const [activeTagFilters, setActiveTagFilters] = useState<ProjectTag[]>([]);\n const [matchAllTagFilters, setMatchAllTagFilters] = useState(true);\n const debouncedSearch = useDebounce(searchValue, 500);\n\n // Update filters when debounced search changes\n useEffect(() => {\n setProjectFilters({ search: debouncedSearch });\n }, [debouncedSearch, setProjectFilters]);\n\n useEffect(() => {\n if (activeTagFilters.length === 0) {\n setProjectFilters({\n tagIds: undefined,\n tagHierarchyPaths: undefined,\n matchAllTags: undefined,\n includeDescendants: undefined,\n });\n return;\n }\n\n setProjectFilters({\n tagIds: activeTagFilters.map((tag) => tag.tagId),\n tagHierarchyPaths: activeTagFilters\n .map((tag) => tag.hierarchyPath)\n .filter((tagPath): tagPath is string => typeof tagPath === 'string' && tagPath.length > 0),\n matchAllTags: matchAllTagFilters,\n includeDescendants: true,\n });\n }, [activeTagFilters, matchAllTagFilters, setProjectFilters]);\n\n useEffect(() => {\n return () => clearProjectSelection();\n }, [clearProjectSelection]);\n\n const { data, isLoading, error } = useProjects(projectFilters, undefined, workspaceId);\n const { mutateAsync: archiveProject } = useArchiveProject(workspaceId);\n const { mutateAsync: unarchiveProject } = useUnarchiveProject(workspaceId);\n const { mutateAsync: deleteProject } = useDeleteProject(workspaceId);\n\n const selectedProjects = useMemo(\n () => data?.items.filter((project) => selectedProjectIds.has(project.id)) ?? [],\n [data?.items, selectedProjectIds]\n );\n\n const archivableProjects = useMemo(\n () => selectedProjects.filter((project) => !project.archived),\n [selectedProjects]\n );\n\n const unarchivableProjects = useMemo(\n () => selectedProjects.filter((project) => project.archived),\n [selectedProjects]\n );\n\n const allVisibleSelected =\n (data?.items.length ?? 0) > 0 &&\n (data?.items ?? []).every((project) => selectedProjectIds.has(project.id));\n const partiallySelected =\n (data?.items ?? []).some((project) => selectedProjectIds.has(project.id)) &&\n !allVisibleSelected;\n\n const {\n canListProjects,\n canCreateProject,\n canDeleteProject,\n canArchiveProject,\n isLoading: permissionsLoading,\n } = useWorkspacePermissions();\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 p-6\">\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 </div>\n );\n }\n\n if (!canListProjects) {\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, 'projectsList.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {tWithFallback(\n t,\n 'projectsList.noPermissionToViewProjects',\n \"You don't have permission to view projects.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n const handleCreateProject = () => {\n openCreateProjectDialog({} as Workspace);\n };\n\n const handleStatusFilter = (value: string) => {\n if (value === 'all') {\n const nextFilters = { ...projectFilters };\n delete nextFilters.status;\n setProjectFilters(nextFilters);\n } else {\n setProjectFilters({ status: value as 'ACTIVE' | 'INACTIVE' | 'ARCHIVED' });\n }\n };\n\n const handleArchivedFilter = (value: string) => {\n if (value === 'all') {\n const nextFilters = { ...projectFilters };\n delete nextFilters.archived;\n setProjectFilters(nextFilters);\n } else {\n setProjectFilters({ archived: value === 'true' });\n }\n };\n\n const toggleTagFilter = (tag: ProjectTag) => {\n setActiveTagFilters((current) => {\n const exists = current.some((entry) => entry.tagId === tag.tagId);\n if (exists) {\n return current.filter((entry) => entry.tagId !== tag.tagId);\n }\n\n return [...current, tag];\n });\n };\n\n const clearAllProjectFilters = () => {\n setSearchValue('');\n setActiveTagFilters([]);\n setMatchAllTagFilters(true);\n clearProjectFilters();\n };\n\n const toggleAllProjects = (checked: boolean) => {\n (data?.items ?? []).forEach((project) => {\n const isSelected = selectedProjectIds.has(project.id);\n if (checked !== isSelected) {\n toggleProjectSelection(project.id);\n }\n });\n };\n\n const handleBulkArchive = async () => {\n if (archivableProjects.length === 0) return;\n await Promise.allSettled(archivableProjects.map((project) => archiveProject(project.id)));\n clearProjectSelection();\n };\n\n const handleBulkUnarchive = async () => {\n if (unarchivableProjects.length === 0) return;\n await Promise.allSettled(unarchivableProjects.map((project) => unarchiveProject(project.id)));\n clearProjectSelection();\n };\n\n const handleBulkDelete = async () => {\n if (selectedProjects.length === 0) return;\n void nativeImpact('medium');\n const message =\n tWithFallback(t, 'projectsList.bulkDeleteConfirmPrefix', 'Delete ') +\n selectedProjects.length +\n ' ' +\n tWithFallback(t, 'projectsList.bulkDeleteConfirmProject', 'selected project') +\n (selectedProjects.length === 1\n ? ''\n : tWithFallback(t, 'projectsList.bulkDeleteConfirmPluralSuffix', 's')) +\n tWithFallback(t, 'projectsList.bulkDeleteConfirmSuffix', '? This cannot be undone.');\n const native = await nativeConfirm({\n title: 'Delete projects',\n message,\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n const confirmed = native === null ? window.confirm(message) : native;\n if (!confirmed) return;\n\n await Promise.allSettled(selectedProjects.map((project) => deleteProject(project.id)));\n clearProjectSelection();\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-center justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">\n {tWithFallback(t, 'projectsList.pageTitle', 'Projects')}\n </h1>\n <p className=\"text-muted-foreground\">\n {tWithFallback(t, 'projectsList.pageSubtitle', 'Manage and organize your projects')}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n {/* View mode toggle */}\n <div className=\"flex items-center gap-1 border rounded-lg p-1\">\n <Button\n variant={viewMode === 'grid' ? 'secondary' : 'ghost'}\n size=\"sm\"\n onClick={() => setViewMode('grid')}\n className=\"h-8 w-8 p-0\"\n aria-label={tWithFallback(\n t,\n 'projectsList.gridViewAriaLabel',\n 'Show projects in grid view'\n )}\n >\n <Grid3x3 size={16} />\n </Button>\n <Button\n variant={viewMode === 'list' ? 'secondary' : 'ghost'}\n size=\"sm\"\n onClick={() => setViewMode('list')}\n className=\"h-8 w-8 p-0\"\n aria-label={tWithFallback(\n t,\n 'projectsList.listViewAriaLabel',\n 'Show projects in list view'\n )}\n >\n <List size={16} />\n </Button>\n </div>\n {canCreateProject && (\n <Button onClick={handleCreateProject}>\n <Plus className=\"h-4 w-4 sm:mr-2\" />\n <span className=\"hidden sm:inline\">\n {tWithFallback(t, 'projectsList.createProject', 'Create Project')}\n </span>\n </Button>\n )}\n </div>\n </div>\n\n {/* Filters & Search */}\n <div className=\"flex flex-col sm:flex-row gap-3\">\n <div className=\"relative flex-1\">\n <Search\n className=\"absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground\"\n size={18}\n />\n <Input\n placeholder={tWithFallback(t, 'projectsList.searchPlaceholder', 'Search projects...')}\n value={searchValue}\n onChange={(e) => setSearchValue(e.target.value)}\n className=\"pl-[3.25rem]\"\n />\n </div>\n <Select value={projectFilters.status || 'all'} onValueChange={handleStatusFilter}>\n <SelectTrigger className=\"w-full sm:w-[180px]\">\n <SelectValue\n placeholder={tWithFallback(t, 'projectsList.filterByStatus', 'Filter by status')}\n />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {tWithFallback(t, 'projectsList.statusAll', 'All Statuses')}\n </SelectItem>\n <SelectItem value=\"ACTIVE\">\n {tWithFallback(t, 'projectsList.statusActive', 'Active')}\n </SelectItem>\n <SelectItem value=\"INACTIVE\">\n {tWithFallback(t, 'projectsList.statusInactive', 'Inactive')}\n </SelectItem>\n </SelectContent>\n </Select>\n <Select\n value={\n projectFilters.archived === undefined\n ? 'all'\n : projectFilters.archived\n ? 'true'\n : 'false'\n }\n onValueChange={handleArchivedFilter}\n >\n <SelectTrigger className=\"w-full sm:w-[180px]\">\n <SelectValue\n placeholder={tWithFallback(t, 'projectsList.filterByArchive', 'Filter by archive')}\n />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {tWithFallback(t, 'projectsList.archiveAll', 'All Projects')}\n </SelectItem>\n <SelectItem value=\"false\">\n {tWithFallback(t, 'projectsList.archiveActiveProjects', 'Active Projects')}\n </SelectItem>\n <SelectItem value=\"true\">\n {tWithFallback(t, 'projectsList.archiveArchived', 'Archived')}\n </SelectItem>\n </SelectContent>\n </Select>\n {(projectFilters.search ||\n projectFilters.status ||\n projectFilters.archived !== undefined ||\n activeTagFilters.length > 0) && (\n <Button variant=\"outline\" onClick={clearAllProjectFilters}>\n {tWithFallback(t, 'projectsList.clearFilters', 'Clear Filters')}\n </Button>\n )}\n </div>\n\n {activeTagFilters.length > 0 && (\n <div className=\"flex flex-col gap-3 rounded-lg border border-border bg-card px-4 py-3\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <p className=\"text-sm font-medium\">\n {tWithFallback(t, 'projectsList.activeTagFilters', 'Active tag filters')}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {tWithFallback(\n t,\n 'projectsList.activeTagFiltersDescription',\n 'Click a project tag to add or remove it from the current filter set.'\n )}\n </p>\n </div>\n <Select\n value={matchAllTagFilters ? 'all' : 'any'}\n onValueChange={(value) => setMatchAllTagFilters(value === 'all')}\n >\n <SelectTrigger className=\"w-full sm:w-[180px]\">\n <SelectValue\n placeholder={tWithFallback(t, 'projectsList.tagMatchMode', 'Tag match mode')}\n />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {tWithFallback(t, 'projectsList.tagMatchAll', 'Match all tags')}\n </SelectItem>\n <SelectItem value=\"any\">\n {tWithFallback(t, 'projectsList.tagMatchAny', 'Match any tag')}\n </SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex flex-wrap gap-2\">\n {activeTagFilters.map((tag) => (\n <TagBadge\n key={tag.tagId}\n id={tag.tagId}\n label={tag.label}\n color={tag.color ?? undefined}\n icon={tag.icon ?? undefined}\n description={tag.description ?? undefined}\n removable\n onRemove={() => toggleTagFilter(tag)}\n size=\"sm\"\n />\n ))}\n </div>\n </div>\n )}\n\n {/* Error State */}\n {error && (\n <Card className=\"border-destructive\">\n <CardContent className=\"pt-6\">\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <p className=\"text-destructive font-semibold mb-2\">\n {tWithFallback(t, 'projectsList.errorLoading', 'Error loading projects')}\n </p>\n <p className=\"text-sm text-muted-foreground\">\n {error instanceof Error\n ? error.message\n : tWithFallback(t, 'projectsList.unknownError', 'An unknown error occurred')}\n </p>\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Loading State */}\n {isLoading && (\n <div className=\"grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4\">\n {Array.from({ length: 6 }).map((_, i) => (\n <Card key={i}>\n <CardContent className=\"pt-6\">\n <div className=\"space-y-3\">\n <Skeleton className=\"h-12 w-12 rounded-lg\" />\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-4 w-1/2\" />\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n )}\n\n {/* Empty State */}\n {!isLoading && !error && data?.items.length === 0 && (\n <Card>\n <CardContent className=\"pt-6\">\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <div className=\"rounded-full bg-muted p-6 mb-4\">\n <FolderKanban size={48} className=\"text-muted-foreground\" />\n </div>\n <h3 className=\"text-lg font-semibold mb-2\">\n {tWithFallback(t, 'projectsList.noProjectsFound', 'No projects found')}\n </h3>\n <p className=\"text-muted-foreground max-w-sm mb-4\">\n {projectFilters.search\n ? tWithFallback(\n t,\n 'projectsList.adjustSearchOrFilters',\n 'Try adjusting your search or filters'\n )\n : tWithFallback(\n t,\n 'projectsList.createFirstProject',\n 'Create your first project to get started organizing your work'\n )}\n </p>\n {!projectFilters.search && canCreateProject && (\n <Button onClick={handleCreateProject}>\n <Plus size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectsList.createProject', 'Create Project')}\n </Button>\n )}\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Projects Grid */}\n {!isLoading && !error && data && data.items.length > 0 && (\n <>\n <div className=\"flex flex-col gap-3 rounded-lg border bg-card p-4 sm:flex-row sm:items-center sm:justify-between\">\n <label className=\"flex items-center gap-2 text-sm text-foreground\">\n <input\n type=\"checkbox\"\n checked={allVisibleSelected}\n ref={(input) => {\n if (input) {\n input.indeterminate = partiallySelected && !allVisibleSelected;\n }\n }}\n onChange={(event) => toggleAllProjects(event.target.checked)}\n aria-label={tWithFallback(\n t,\n 'projectsList.selectAllAriaLabel',\n 'Select all projects'\n )}\n className=\"h-4 w-4 rounded border-border text-primary\"\n />\n <span>{tWithFallback(t, 'projectsList.selectAllVisible', 'Select all visible')}</span>\n </label>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm text-muted-foreground\">\n {selectedProjectIds.size} {tWithFallback(t, 'projectsList.selected', 'selected')}\n </span>\n {selectedProjectIds.size > 0 && (\n <Button variant=\"ghost\" size=\"sm\" onClick={clearProjectSelection}>\n {tWithFallback(t, 'projectsList.clearSelection', 'Clear selection')}\n </Button>\n )}\n {canArchiveProject && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkArchive}\n disabled={archivableProjects.length === 0}\n >\n {tWithFallback(t, 'projectsList.archiveSelected', 'Archive Selected')}\n {archivableProjects.length > 0 ? ` (${archivableProjects.length})` : ''}\n </Button>\n )}\n {canArchiveProject && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkUnarchive}\n disabled={unarchivableProjects.length === 0}\n >\n {tWithFallback(t, 'projectsList.unarchiveSelected', 'Unarchive Selected')}\n {unarchivableProjects.length > 0 ? ` (${unarchivableProjects.length})` : ''}\n </Button>\n )}\n {canDeleteProject && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkDelete}\n disabled={selectedProjects.length === 0}\n className=\"text-destructive\"\n >\n {tWithFallback(t, 'projectsList.deleteSelected', 'Delete Selected')}\n {selectedProjects.length > 0 ? ` (${selectedProjects.length})` : ''}\n </Button>\n )}\n </div>\n </div>\n\n <div\n className={\n viewMode === 'list'\n ? 'space-y-3'\n : 'grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4'\n }\n >\n {data.items.map((project) => (\n <ProjectCard\n key={project.id}\n project={project}\n selected={selectedProjectIds.has(project.id)}\n canArchive={canArchiveProject}\n canDelete={canDeleteProject}\n onTagClick={toggleTagFilter}\n onToggleSelect={(checked) => {\n const isSelected = selectedProjectIds.has(project.id);\n if (checked !== isSelected) {\n toggleProjectSelection(project.id);\n }\n }}\n />\n ))}\n </div>\n\n {/* Pagination Info */}\n {data.total > data.items.length && (\n <div className=\"text-center text-sm text-muted-foreground\">\n {tWithFallback(t, 'projectsList.showingPrefix', 'Showing')} {data.items.length}{' '}\n {tWithFallback(t, 'projectsList.showingOf', 'of')} {data.total}{' '}\n {tWithFallback(t, 'projectsList.showingProjects', 'projects')}\n </div>\n )}\n </>\n )}\n\n {/* Create Project Dialog */}\n {isCreateProjectDialogOpen && <CreateProjectDialog workspaceId={workspaceId} />}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProjectsListPage.js","names":[],"sources":["../../src/pages/ProjectsListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { FolderKanban, Plus, Grid3x3, List, Search } from 'lucide-react';\nimport { TagBadge } from '@burdenoff/fe-libs/tag';\nimport {\n Button,\n Input,\n Card,\n CardContent,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Skeleton,\n} from '@burdenoff/fe-libs/ui';\nimport { useDebounce } from '@burdenoff/fe-libs/shared/hooks';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useWorkspacesStore } from '../store/workspacesStore';\nimport { nativeConfirm, nativeImpact } from '../utils/nativeBridge';\nimport { useMyProjects } from '../hooks/useProjects';\nimport {\n useArchiveProject,\n useDeleteProject,\n useUnarchiveProject,\n} from '../hooks/useProjectMutations';\nimport { ProjectCard } from '../components/ProjectCard';\nimport { CreateProjectDialog } from '../components/CreateProjectDialog';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport { tWithFallback } from '../utils/i18n';\nimport { normalizeProjectTags } from '../utils/projectTags';\nimport type { ProjectTag, Workspace } from '../types';\n\nexport function ProjectsListPage() {\n const { t } = useI18n();\n\n const { workspaceId } = useParams<{ workspaceId: string }>();\n useWorkspacesPageView('workspaces.projects', { workspaceId, requireParams: true });\n const {\n viewMode,\n setViewMode,\n projectFilters,\n setProjectFilters,\n clearProjectFilters,\n selectedProjectIds,\n toggleProjectSelection,\n clearProjectSelection,\n openCreateProjectDialog,\n isCreateProjectDialogOpen,\n } = useWorkspacesStore();\n\n const [searchValue, setSearchValue] = useState(projectFilters.search || '');\n const [activeTagFilters, setActiveTagFilters] = useState<ProjectTag[]>([]);\n const [matchAllTagFilters, setMatchAllTagFilters] = useState(true);\n const debouncedSearch = useDebounce(searchValue, 500);\n\n // Update filters when debounced search changes\n useEffect(() => {\n setProjectFilters({ search: debouncedSearch });\n }, [debouncedSearch, setProjectFilters]);\n\n useEffect(() => {\n if (activeTagFilters.length === 0) {\n setProjectFilters({\n tagIds: undefined,\n tagHierarchyPaths: undefined,\n matchAllTags: undefined,\n includeDescendants: undefined,\n });\n return;\n }\n\n setProjectFilters({\n tagIds: activeTagFilters.map((tag) => tag.tagId),\n tagHierarchyPaths: activeTagFilters\n .map((tag) => tag.hierarchyPath)\n .filter((tagPath): tagPath is string => typeof tagPath === 'string' && tagPath.length > 0),\n matchAllTags: matchAllTagFilters,\n includeDescendants: true,\n });\n }, [activeTagFilters, matchAllTagFilters, setProjectFilters]);\n\n useEffect(() => {\n return () => clearProjectSelection();\n }, [clearProjectSelection]);\n\n // Membership-scoped query: backend filters by actorId, so no client-side\n // permission gate is needed. Page is shared by `/workspaces/projects` and\n // `/workspaces/:workspaceId/projects`; the latter narrows the list below.\n const PROJECTS_PAGE_SIZE = 500;\n const {\n data: myData,\n isLoading,\n error,\n } = useMyProjects({\n limit: PROJECTS_PAGE_SIZE,\n offset: 0,\n });\n\n const data = useMemo(() => {\n const all = myData?.items ?? [];\n const q = (projectFilters.search ?? '').trim().toLowerCase();\n const tagIds = projectFilters.tagIds ?? [];\n const matchAllTags = projectFilters.matchAllTags ?? true;\n\n const matchesWorkspace = (project: (typeof all)[number]) =>\n !workspaceId || project.workspaceId === workspaceId;\n const matchesSearch = (project: (typeof all)[number]) =>\n !q || project.name.toLowerCase().includes(q) || project.key.toLowerCase().includes(q);\n const matchesStatus = (project: (typeof all)[number]) =>\n !projectFilters.status || project.status === projectFilters.status;\n const matchesArchived = (project: (typeof all)[number]) =>\n typeof projectFilters.archived === 'undefined' ||\n project.archived === projectFilters.archived;\n const matchesTags = (project: (typeof all)[number]) => {\n if (tagIds.length === 0) return true;\n const projectTagIds = new Set(normalizeProjectTags(project.tags).map((t) => t.tagId));\n return matchAllTags\n ? tagIds.every((id) => projectTagIds.has(id))\n : tagIds.some((id) => projectTagIds.has(id));\n };\n\n const filtered = all.filter(\n (project) =>\n matchesWorkspace(project) &&\n matchesSearch(project) &&\n matchesStatus(project) &&\n matchesArchived(project) &&\n matchesTags(project)\n );\n\n // Surface the real backend totals so the \"Showing X of Y\" footer can flag\n // truncation. For narrowed views (workspace/tag filter), the backend total\n // doesn't reflect the post-filter count, so we report the filtered size as\n // total. But we MUST preserve `hasMore` from upstream — if the upstream\n // query was capped (hasMore=true), matches outside the first page are\n // silently hidden, and the footer needs to keep flagging that.\n const apiTotal = myData?.total ?? filtered.length;\n const apiHasMore = myData?.hasMore ?? false;\n const isNarrowed = !!workspaceId || tagIds.length > 0;\n return {\n items: filtered,\n total: isNarrowed ? filtered.length : apiTotal,\n hasMore: apiHasMore,\n };\n }, [myData?.items, myData?.total, myData?.hasMore, projectFilters, workspaceId]);\n\n const { mutateAsync: archiveProject } = useArchiveProject(workspaceId);\n const { mutateAsync: unarchiveProject } = useUnarchiveProject(workspaceId);\n const { mutateAsync: deleteProject } = useDeleteProject(workspaceId);\n\n const selectedProjects = useMemo(\n () => data?.items.filter((project) => selectedProjectIds.has(project.id)) ?? [],\n [data?.items, selectedProjectIds]\n );\n\n const archivableProjects = useMemo(\n () => selectedProjects.filter((project) => !project.archived),\n [selectedProjects]\n );\n\n const unarchivableProjects = useMemo(\n () => selectedProjects.filter((project) => project.archived),\n [selectedProjects]\n );\n\n const allVisibleSelected =\n (data?.items.length ?? 0) > 0 &&\n (data?.items ?? []).every((project) => selectedProjectIds.has(project.id));\n const partiallySelected =\n (data?.items ?? []).some((project) => selectedProjectIds.has(project.id)) &&\n !allVisibleSelected;\n\n const {\n canCreateProject,\n canDeleteProject,\n canArchiveProject,\n isLoading: permissionsLoading,\n } = useWorkspacePermissions();\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 p-6\">\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 </div>\n );\n }\n\n // No client-side list gate: this page calls `myProjects` which is\n // membership-scoped — the resolver filters by actorId on the backend, so the\n // data set returned IS the access list. If the user is in zero projects, the\n // page renders the empty state below. Per-action checks (canCreateProject,\n // canDeleteProject, canArchiveProject) remain in place to hide buttons the\n // user isn't authorized for on their workspace.\n\n const handleCreateProject = () => {\n openCreateProjectDialog({} as Workspace);\n };\n\n const handleStatusFilter = (value: string) => {\n if (value === 'all') {\n const nextFilters = { ...projectFilters };\n delete nextFilters.status;\n setProjectFilters(nextFilters);\n } else {\n setProjectFilters({ status: value as 'ACTIVE' | 'INACTIVE' | 'ARCHIVED' });\n }\n };\n\n const handleArchivedFilter = (value: string) => {\n if (value === 'all') {\n const nextFilters = { ...projectFilters };\n delete nextFilters.archived;\n setProjectFilters(nextFilters);\n } else {\n setProjectFilters({ archived: value === 'true' });\n }\n };\n\n const toggleTagFilter = (tag: ProjectTag) => {\n setActiveTagFilters((current) => {\n const exists = current.some((entry) => entry.tagId === tag.tagId);\n if (exists) {\n return current.filter((entry) => entry.tagId !== tag.tagId);\n }\n\n return [...current, tag];\n });\n };\n\n const clearAllProjectFilters = () => {\n setSearchValue('');\n setActiveTagFilters([]);\n setMatchAllTagFilters(true);\n clearProjectFilters();\n };\n\n const toggleAllProjects = (checked: boolean) => {\n (data?.items ?? []).forEach((project) => {\n const isSelected = selectedProjectIds.has(project.id);\n if (checked !== isSelected) {\n toggleProjectSelection(project.id);\n }\n });\n };\n\n const handleBulkArchive = async () => {\n if (archivableProjects.length === 0) return;\n await Promise.allSettled(archivableProjects.map((project) => archiveProject(project.id)));\n clearProjectSelection();\n };\n\n const handleBulkUnarchive = async () => {\n if (unarchivableProjects.length === 0) return;\n await Promise.allSettled(unarchivableProjects.map((project) => unarchiveProject(project.id)));\n clearProjectSelection();\n };\n\n const handleBulkDelete = async () => {\n if (selectedProjects.length === 0) return;\n void nativeImpact('medium');\n const message =\n tWithFallback(t, 'projectsList.bulkDeleteConfirmPrefix', 'Delete ') +\n selectedProjects.length +\n ' ' +\n tWithFallback(t, 'projectsList.bulkDeleteConfirmProject', 'selected project') +\n (selectedProjects.length === 1\n ? ''\n : tWithFallback(t, 'projectsList.bulkDeleteConfirmPluralSuffix', 's')) +\n tWithFallback(t, 'projectsList.bulkDeleteConfirmSuffix', '? This cannot be undone.');\n const native = await nativeConfirm({\n title: 'Delete projects',\n message,\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n const confirmed = native === null ? window.confirm(message) : native;\n if (!confirmed) return;\n\n await Promise.allSettled(selectedProjects.map((project) => deleteProject(project.id)));\n clearProjectSelection();\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-center justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">\n {tWithFallback(t, 'projectsList.pageTitle', 'Projects')}\n </h1>\n <p className=\"text-muted-foreground\">\n {tWithFallback(t, 'projectsList.pageSubtitle', 'Manage and organize your projects')}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n {/* View mode toggle */}\n <div className=\"flex items-center gap-1 border rounded-lg p-1\">\n <Button\n variant={viewMode === 'grid' ? 'secondary' : 'ghost'}\n size=\"sm\"\n onClick={() => setViewMode('grid')}\n className=\"h-8 w-8 p-0\"\n aria-label={tWithFallback(\n t,\n 'projectsList.gridViewAriaLabel',\n 'Show projects in grid view'\n )}\n >\n <Grid3x3 size={16} />\n </Button>\n <Button\n variant={viewMode === 'list' ? 'secondary' : 'ghost'}\n size=\"sm\"\n onClick={() => setViewMode('list')}\n className=\"h-8 w-8 p-0\"\n aria-label={tWithFallback(\n t,\n 'projectsList.listViewAriaLabel',\n 'Show projects in list view'\n )}\n >\n <List size={16} />\n </Button>\n </div>\n {canCreateProject && (\n <Button onClick={handleCreateProject}>\n <Plus className=\"h-4 w-4 sm:mr-2\" />\n <span className=\"hidden sm:inline\">\n {tWithFallback(t, 'projectsList.createProject', 'Create Project')}\n </span>\n </Button>\n )}\n </div>\n </div>\n\n {/* Filters & Search */}\n <div className=\"flex flex-col sm:flex-row gap-3\">\n <div className=\"relative flex-1\">\n <Search\n className=\"absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground\"\n size={18}\n />\n <Input\n placeholder={tWithFallback(t, 'projectsList.searchPlaceholder', 'Search projects...')}\n value={searchValue}\n onChange={(e) => setSearchValue(e.target.value)}\n className=\"pl-[3.25rem]\"\n />\n </div>\n <Select value={projectFilters.status || 'all'} onValueChange={handleStatusFilter}>\n <SelectTrigger className=\"w-full sm:w-[180px]\">\n <SelectValue\n placeholder={tWithFallback(t, 'projectsList.filterByStatus', 'Filter by status')}\n />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {tWithFallback(t, 'projectsList.statusAll', 'All Statuses')}\n </SelectItem>\n <SelectItem value=\"ACTIVE\">\n {tWithFallback(t, 'projectsList.statusActive', 'Active')}\n </SelectItem>\n <SelectItem value=\"INACTIVE\">\n {tWithFallback(t, 'projectsList.statusInactive', 'Inactive')}\n </SelectItem>\n </SelectContent>\n </Select>\n <Select\n value={\n projectFilters.archived === undefined\n ? 'all'\n : projectFilters.archived\n ? 'true'\n : 'false'\n }\n onValueChange={handleArchivedFilter}\n >\n <SelectTrigger className=\"w-full sm:w-[180px]\">\n <SelectValue\n placeholder={tWithFallback(t, 'projectsList.filterByArchive', 'Filter by archive')}\n />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {tWithFallback(t, 'projectsList.archiveAll', 'All Projects')}\n </SelectItem>\n <SelectItem value=\"false\">\n {tWithFallback(t, 'projectsList.archiveActiveProjects', 'Active Projects')}\n </SelectItem>\n <SelectItem value=\"true\">\n {tWithFallback(t, 'projectsList.archiveArchived', 'Archived')}\n </SelectItem>\n </SelectContent>\n </Select>\n {(projectFilters.search ||\n projectFilters.status ||\n projectFilters.archived !== undefined ||\n activeTagFilters.length > 0) && (\n <Button variant=\"outline\" onClick={clearAllProjectFilters}>\n {tWithFallback(t, 'projectsList.clearFilters', 'Clear Filters')}\n </Button>\n )}\n </div>\n\n {activeTagFilters.length > 0 && (\n <div className=\"flex flex-col gap-3 rounded-lg border border-border bg-card px-4 py-3\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <p className=\"text-sm font-medium\">\n {tWithFallback(t, 'projectsList.activeTagFilters', 'Active tag filters')}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {tWithFallback(\n t,\n 'projectsList.activeTagFiltersDescription',\n 'Click a project tag to add or remove it from the current filter set.'\n )}\n </p>\n </div>\n <Select\n value={matchAllTagFilters ? 'all' : 'any'}\n onValueChange={(value) => setMatchAllTagFilters(value === 'all')}\n >\n <SelectTrigger className=\"w-full sm:w-[180px]\">\n <SelectValue\n placeholder={tWithFallback(t, 'projectsList.tagMatchMode', 'Tag match mode')}\n />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {tWithFallback(t, 'projectsList.tagMatchAll', 'Match all tags')}\n </SelectItem>\n <SelectItem value=\"any\">\n {tWithFallback(t, 'projectsList.tagMatchAny', 'Match any tag')}\n </SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex flex-wrap gap-2\">\n {activeTagFilters.map((tag) => (\n <TagBadge\n key={tag.tagId}\n id={tag.tagId}\n label={tag.label}\n color={tag.color ?? undefined}\n icon={tag.icon ?? undefined}\n description={tag.description ?? undefined}\n removable\n onRemove={() => toggleTagFilter(tag)}\n size=\"sm\"\n />\n ))}\n </div>\n </div>\n )}\n\n {/* Error State */}\n {error && (\n <Card className=\"border-destructive\">\n <CardContent className=\"pt-6\">\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <p className=\"text-destructive font-semibold mb-2\">\n {tWithFallback(t, 'projectsList.errorLoading', 'Error loading projects')}\n </p>\n <p className=\"text-sm text-muted-foreground\">\n {error instanceof Error\n ? error.message\n : tWithFallback(t, 'projectsList.unknownError', 'An unknown error occurred')}\n </p>\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Loading State */}\n {isLoading && (\n <div className=\"grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4\">\n {Array.from({ length: 6 }).map((_, i) => (\n <Card key={i}>\n <CardContent className=\"pt-6\">\n <div className=\"space-y-3\">\n <Skeleton className=\"h-12 w-12 rounded-lg\" />\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-4 w-1/2\" />\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n )}\n\n {/* Empty State */}\n {!isLoading && !error && data?.items.length === 0 && (\n <Card>\n <CardContent className=\"pt-6\">\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <div className=\"rounded-full bg-muted p-6 mb-4\">\n <FolderKanban size={48} className=\"text-muted-foreground\" />\n </div>\n <h3 className=\"text-lg font-semibold mb-2\">\n {tWithFallback(t, 'projectsList.noProjectsFound', 'No projects found')}\n </h3>\n <p className=\"text-muted-foreground max-w-sm mb-4\">\n {projectFilters.search\n ? tWithFallback(\n t,\n 'projectsList.adjustSearchOrFilters',\n 'Try adjusting your search or filters'\n )\n : tWithFallback(\n t,\n 'projectsList.createFirstProject',\n 'Create your first project to get started organizing your work'\n )}\n </p>\n {!projectFilters.search && canCreateProject && (\n <Button onClick={handleCreateProject}>\n <Plus size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectsList.createProject', 'Create Project')}\n </Button>\n )}\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Projects Grid */}\n {!isLoading && !error && data && data.items.length > 0 && (\n <>\n <div className=\"flex flex-col gap-3 rounded-lg border bg-card p-4 sm:flex-row sm:items-center sm:justify-between\">\n <label className=\"flex items-center gap-2 text-sm text-foreground\">\n <input\n type=\"checkbox\"\n checked={allVisibleSelected}\n ref={(input) => {\n if (input) {\n input.indeterminate = partiallySelected && !allVisibleSelected;\n }\n }}\n onChange={(event) => toggleAllProjects(event.target.checked)}\n aria-label={tWithFallback(\n t,\n 'projectsList.selectAllAriaLabel',\n 'Select all projects'\n )}\n className=\"h-4 w-4 rounded border-border text-primary\"\n />\n <span>{tWithFallback(t, 'projectsList.selectAllVisible', 'Select all visible')}</span>\n </label>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm text-muted-foreground\">\n {selectedProjectIds.size} {tWithFallback(t, 'projectsList.selected', 'selected')}\n </span>\n {selectedProjectIds.size > 0 && (\n <Button variant=\"ghost\" size=\"sm\" onClick={clearProjectSelection}>\n {tWithFallback(t, 'projectsList.clearSelection', 'Clear selection')}\n </Button>\n )}\n {/* Bulk mutations are bound to a single workspaceId via the\n archive/unarchive/delete hooks. On the cross-workspace\n /workspaces/projects route (workspaceId is undefined),\n hide them — selections can span workspaces and the bound\n hook would fail authorization for projects outside the\n caller's context. The per-card actions in ProjectCard\n still work because they resolve per-project. */}\n {workspaceId && canArchiveProject && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkArchive}\n disabled={archivableProjects.length === 0}\n >\n {tWithFallback(t, 'projectsList.archiveSelected', 'Archive Selected')}\n {archivableProjects.length > 0 ? ` (${archivableProjects.length})` : ''}\n </Button>\n )}\n {workspaceId && canArchiveProject && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkUnarchive}\n disabled={unarchivableProjects.length === 0}\n >\n {tWithFallback(t, 'projectsList.unarchiveSelected', 'Unarchive Selected')}\n {unarchivableProjects.length > 0 ? ` (${unarchivableProjects.length})` : ''}\n </Button>\n )}\n {workspaceId && canDeleteProject && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkDelete}\n disabled={selectedProjects.length === 0}\n className=\"text-destructive\"\n >\n {tWithFallback(t, 'projectsList.deleteSelected', 'Delete Selected')}\n {selectedProjects.length > 0 ? ` (${selectedProjects.length})` : ''}\n </Button>\n )}\n </div>\n </div>\n\n <div\n className={\n viewMode === 'list'\n ? 'space-y-3'\n : 'grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4'\n }\n >\n {data.items.map((project) => (\n <ProjectCard\n key={project.id}\n project={project}\n selected={selectedProjectIds.has(project.id)}\n canArchive={canArchiveProject}\n canDelete={canDeleteProject}\n onTagClick={toggleTagFilter}\n onToggleSelect={(checked) => {\n const isSelected = selectedProjectIds.has(project.id);\n if (checked !== isSelected) {\n toggleProjectSelection(project.id);\n }\n }}\n />\n ))}\n </div>\n\n {/* Pagination Info */}\n {data.total > data.items.length && (\n <div className=\"text-center text-sm text-muted-foreground\">\n {tWithFallback(t, 'projectsList.showingPrefix', 'Showing')} {data.items.length}{' '}\n {tWithFallback(t, 'projectsList.showingOf', 'of')} {data.total}{' '}\n {tWithFallback(t, 'projectsList.showingProjects', 'projects')}\n </div>\n )}\n </>\n )}\n\n {/* Create Project Dialog */}\n {isCreateProjectDialogOpen && <CreateProjectDialog workspaceId={workspaceId} />}\n </div>\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-workspaces",
3
- "version": "2026.513.2",
3
+ "version": "2026.518.1",
4
4
  "description": "Workspaces microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [