@burdenoff/microfe-workspaces 2026.510.50 → 2026.511.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/generated/wspace-operations.d.ts +91 -0
  2. package/dist/generated/wspace-operations.d.ts.map +1 -1
  3. package/dist/generated/wspace-operations.js +79 -51
  4. package/dist/generated/wspace-operations.js.map +1 -1
  5. package/dist/hooks/useMemberMutations.d.ts.map +1 -1
  6. package/dist/hooks/useMemberMutations.js +141 -60
  7. package/dist/hooks/useMemberMutations.js.map +1 -1
  8. package/dist/hooks/useProjectMutations.d.ts.map +1 -1
  9. package/dist/hooks/useProjectMutations.js +84 -44
  10. package/dist/hooks/useProjectMutations.js.map +1 -1
  11. package/dist/hooks/useWorkspaceMutations.d.ts.map +1 -1
  12. package/dist/hooks/useWorkspaceMutations.js +64 -29
  13. package/dist/hooks/useWorkspaceMutations.js.map +1 -1
  14. package/dist/hooks/useWorkspacesEventEmitter.d.ts +5 -1
  15. package/dist/hooks/useWorkspacesEventEmitter.d.ts.map +1 -1
  16. package/dist/hooks/useWorkspacesEventEmitter.js +8 -7
  17. package/dist/hooks/useWorkspacesEventEmitter.js.map +1 -1
  18. package/dist/hooks/useWorkspacesPageView.d.ts +27 -0
  19. package/dist/hooks/useWorkspacesPageView.d.ts.map +1 -0
  20. package/dist/hooks/useWorkspacesPageView.js +29 -0
  21. package/dist/hooks/useWorkspacesPageView.js.map +1 -0
  22. package/dist/pages/ActivityListPage.d.ts.map +1 -1
  23. package/dist/pages/ActivityListPage.js +59 -56
  24. package/dist/pages/ActivityListPage.js.map +1 -1
  25. package/dist/pages/CreateWorkspacePage.d.ts.map +1 -1
  26. package/dist/pages/CreateWorkspacePage.js +104 -104
  27. package/dist/pages/CreateWorkspacePage.js.map +1 -1
  28. package/dist/pages/InviteAcceptancePage.d.ts.map +1 -1
  29. package/dist/pages/InviteAcceptancePage.js +197 -216
  30. package/dist/pages/InviteAcceptancePage.js.map +1 -1
  31. package/dist/pages/MembersListPage.d.ts.map +1 -1
  32. package/dist/pages/MembersListPage.js +142 -138
  33. package/dist/pages/MembersListPage.js.map +1 -1
  34. package/dist/pages/ProjectDetailPage.d.ts.map +1 -1
  35. package/dist/pages/ProjectDetailPage.js +181 -176
  36. package/dist/pages/ProjectDetailPage.js.map +1 -1
  37. package/dist/pages/ProjectsListPage.d.ts.map +1 -1
  38. package/dist/pages/ProjectsListPage.js +175 -171
  39. package/dist/pages/ProjectsListPage.js.map +1 -1
  40. package/dist/pages/WorkspaceDetailPage.d.ts.map +1 -1
  41. package/dist/pages/WorkspaceDetailPage.js +104 -101
  42. package/dist/pages/WorkspaceDetailPage.js.map +1 -1
  43. package/dist/pages/WorkspaceSettingsPage.d.ts.map +1 -1
  44. package/dist/pages/WorkspaceSettingsPage.js +182 -137
  45. package/dist/pages/WorkspaceSettingsPage.js.map +1 -1
  46. package/dist/pages/WorkspacesListPage.d.ts.map +1 -1
  47. package/dist/pages/WorkspacesListPage.js +77 -77
  48. package/dist/pages/WorkspacesListPage.js.map +1 -1
  49. package/dist/pages/dashboard/DashboardCustomizer.d.ts.map +1 -1
  50. package/dist/pages/dashboard/DashboardCustomizer.js +53 -55
  51. package/dist/pages/dashboard/DashboardCustomizer.js.map +1 -1
  52. package/dist/pages/dashboard/DashboardGrid.d.ts.map +1 -1
  53. package/dist/pages/dashboard/DashboardGrid.js +33 -36
  54. package/dist/pages/dashboard/DashboardGrid.js.map +1 -1
  55. package/dist/pages/dashboard/DashboardPage.d.ts.map +1 -1
  56. package/dist/pages/dashboard/DashboardPage.js +67 -65
  57. package/dist/pages/dashboard/DashboardPage.js.map +1 -1
  58. package/dist/pages/dashboard/WidgetWrapper.d.ts.map +1 -1
  59. package/dist/pages/dashboard/WidgetWrapper.js +3 -6
  60. package/dist/pages/dashboard/WidgetWrapper.js.map +1 -1
  61. package/dist/utils/i18n.d.ts +13 -0
  62. package/dist/utils/i18n.d.ts.map +1 -0
  63. package/dist/utils/i18n.js +12 -0
  64. package/dist/utils/i18n.js.map +1 -0
  65. package/dist/utils/telemetryError.d.ts +2 -0
  66. package/dist/utils/telemetryError.d.ts.map +1 -0
  67. package/dist/utils/telemetryError.js +28 -0
  68. package/dist/utils/telemetryError.js.map +1 -0
  69. package/package.json +9 -4
@@ -4,23 +4,23 @@ import { useMyWorkspaces as n } from "../hooks/useWorkspaces.js";
4
4
  import { useScopedNavigation as r } from "../hooks/useScopedNavigation.js";
5
5
  import { WorkspaceCard as i } from "../components/WorkspaceCard.js";
6
6
  import { WORKSPACE_PERMISSIONS as a } from "../constants/permissions.js";
7
- import { useCallback as o, useEffect as s, useState as c } from "react";
8
- import { Grid3X3 as l, List as u, Plus as d, Search as f } from "lucide-react";
9
- import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
10
- import { Button as g, Input as _, Skeleton as v } from "@burdenoff/fe-libs/ui";
11
- import { useNavigate as y } from "react-router-dom";
12
- import { useDebounce as b } from "@burdenoff/fe-libs/shared/hooks";
13
- import { PermissionButton as x } from "@burdenoff/fe-libs/shared/components";
14
- import { useI18n as S } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
7
+ import { tWithFallback as o } from "../utils/i18n.js";
8
+ import { useWorkspacesPageView as s } from "../hooks/useWorkspacesPageView.js";
9
+ import { useEffect as c, useState as l } from "react";
10
+ import { Grid3X3 as u, List as d, Plus as f, Search as p } from "lucide-react";
11
+ import { Fragment as m, jsx as h, jsxs as g } from "react/jsx-runtime";
12
+ import { Button as _, Input as v, Skeleton as y } from "@burdenoff/fe-libs/ui";
13
+ import { useNavigate as b } from "react-router-dom";
14
+ import { useDebounce as x } from "@burdenoff/fe-libs/shared/hooks";
15
+ import { PermissionButton as S } from "@burdenoff/fe-libs/shared/components";
16
+ import { useI18n as C } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
15
17
  //#region src/pages/WorkspacesListPage.tsx
16
- function C() {
17
- let { t: C } = S(), w = o((e, t) => {
18
- let n = C(e);
19
- return n === e ? t : n;
20
- }, [C]), T = y(), { basePath: E = "/" } = e(), { withCurrentSearch: D } = r(), { viewMode: O, setViewMode: k, workspaceFilters: A, setWorkspaceFilters: j } = t(), M = () => {
18
+ function w() {
19
+ s("workspaces.list");
20
+ let { t: w } = C(), T = b(), { basePath: E = "/" } = e(), { withCurrentSearch: D } = r(), { viewMode: O, setViewMode: k, workspaceFilters: A, setWorkspaceFilters: j } = t(), M = () => {
21
21
  T(D(`${E}/new`));
22
- }, [N, P] = c(A.search || ""), F = b(N, 500);
23
- s(() => {
22
+ }, [N, P] = l(A.search || ""), F = x(N, 500);
23
+ c(() => {
24
24
  F !== A.search && j({ search: F });
25
25
  }, [
26
26
  F,
@@ -32,66 +32,66 @@ function C() {
32
32
  items: F ? I.items.filter((e) => e.name.toLowerCase().includes(F.toLowerCase())) : I.items,
33
33
  total: F ? I.items.filter((e) => e.name.toLowerCase().includes(F.toLowerCase())).length : I.total
34
34
  } : void 0;
35
- return R ? /* @__PURE__ */ m("div", {
35
+ return R ? /* @__PURE__ */ h("div", {
36
36
  className: "space-y-4 sm:space-y-6 p-3 sm:p-6",
37
- children: /* @__PURE__ */ h("div", {
37
+ children: /* @__PURE__ */ g("div", {
38
38
  className: "flex flex-col items-center justify-center py-12",
39
- children: [/* @__PURE__ */ m("p", {
39
+ children: [/* @__PURE__ */ h("p", {
40
40
  className: "text-destructive text-lg font-semibold",
41
- children: w("workspacesList.loadError", "Error loading workspaces")
42
- }), /* @__PURE__ */ m("p", {
41
+ children: o(w, "workspacesList.loadError", "Error loading workspaces")
42
+ }), /* @__PURE__ */ h("p", {
43
43
  className: "text-muted-foreground text-sm mt-2",
44
- children: R instanceof Error ? R.message : w("workspacesList.unknownError", "Unknown error")
44
+ children: R instanceof Error ? R.message : o(w, "workspacesList.unknownError", "Unknown error")
45
45
  })]
46
46
  })
47
- }) : /* @__PURE__ */ h("div", {
47
+ }) : /* @__PURE__ */ g("div", {
48
48
  className: "space-y-4 sm:space-y-6 p-3 sm:p-6",
49
49
  children: [
50
- /* @__PURE__ */ h("div", {
50
+ /* @__PURE__ */ g("div", {
51
51
  className: "flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3",
52
- children: [/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("h1", {
52
+ children: [/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("h1", {
53
53
  className: "text-2xl sm:text-3xl font-bold",
54
- children: w("workspacesList.title", "Workspaces")
55
- }), /* @__PURE__ */ m("p", {
54
+ children: o(w, "workspacesList.title", "Workspaces")
55
+ }), /* @__PURE__ */ h("p", {
56
56
  className: "text-sm sm:text-base text-muted-foreground",
57
- children: w("workspacesList.description", "Manage and organize your workspaces")
58
- })] }), /* @__PURE__ */ h("div", {
57
+ children: o(w, "workspacesList.description", "Manage and organize your workspaces")
58
+ })] }), /* @__PURE__ */ g("div", {
59
59
  className: "flex items-center gap-2",
60
60
  children: [
61
- /* @__PURE__ */ m(g, {
61
+ /* @__PURE__ */ h(_, {
62
62
  variant: O === "grid" ? "default" : "outline",
63
63
  size: "icon",
64
64
  onClick: () => k("grid"),
65
65
  className: "hidden sm:flex",
66
- "aria-label": w("workspacesList.gridViewAriaLabel", "Show workspaces in grid view"),
67
- title: w("workspacesList.gridViewTitle", "Grid view"),
68
- children: /* @__PURE__ */ m(l, { size: 20 })
66
+ "aria-label": o(w, "workspacesList.gridViewAriaLabel", "Show workspaces in grid view"),
67
+ title: o(w, "workspacesList.gridViewTitle", "Grid view"),
68
+ children: /* @__PURE__ */ h(u, { size: 20 })
69
69
  }),
70
- /* @__PURE__ */ m(g, {
70
+ /* @__PURE__ */ h(_, {
71
71
  variant: O === "list" ? "default" : "outline",
72
72
  size: "icon",
73
73
  onClick: () => k("list"),
74
74
  className: "hidden sm:flex",
75
- "aria-label": w("workspacesList.listViewAriaLabel", "Show workspaces in list view"),
76
- title: w("workspacesList.listViewTitle", "List view"),
77
- children: /* @__PURE__ */ m(u, { size: 20 })
75
+ "aria-label": o(w, "workspacesList.listViewAriaLabel", "Show workspaces in list view"),
76
+ title: o(w, "workspacesList.listViewTitle", "List view"),
77
+ children: /* @__PURE__ */ h(d, { size: 20 })
78
78
  }),
79
- /* @__PURE__ */ m(x, {
79
+ /* @__PURE__ */ h(S, {
80
80
  permission: a.WORKSPACE_CREATE,
81
81
  deniedBehavior: "disable",
82
- deniedTooltip: w("workspacesList.createDeniedTooltip", "You don't have permission to create workspaces"),
83
- children: /* @__PURE__ */ h(g, {
82
+ deniedTooltip: o(w, "workspacesList.createDeniedTooltip", "You don't have permission to create workspaces"),
83
+ children: /* @__PURE__ */ g(_, {
84
84
  onClick: M,
85
85
  className: "gap-2",
86
86
  children: [
87
- /* @__PURE__ */ m(d, { size: 20 }),
88
- /* @__PURE__ */ m("span", {
87
+ /* @__PURE__ */ h(f, { size: 20 }),
88
+ /* @__PURE__ */ h("span", {
89
89
  className: "hidden sm:inline",
90
- children: w("workspacesList.createButton", "Create Workspace")
90
+ children: o(w, "workspacesList.createButton", "Create Workspace")
91
91
  }),
92
- /* @__PURE__ */ m("span", {
92
+ /* @__PURE__ */ h("span", {
93
93
  className: "sm:hidden",
94
- children: w("workspacesList.createButtonShort", "Create")
94
+ children: o(w, "workspacesList.createButtonShort", "Create")
95
95
  })
96
96
  ]
97
97
  })
@@ -99,13 +99,13 @@ function C() {
99
99
  ]
100
100
  })]
101
101
  }),
102
- /* @__PURE__ */ h("div", {
102
+ /* @__PURE__ */ g("div", {
103
103
  className: "relative",
104
- children: [/* @__PURE__ */ m(f, {
104
+ children: [/* @__PURE__ */ h(p, {
105
105
  className: "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground",
106
106
  size: 20
107
- }), /* @__PURE__ */ m(_, {
108
- placeholder: w("workspacesList.searchPlaceholder", "Search workspaces..."),
107
+ }), /* @__PURE__ */ h(v, {
108
+ placeholder: o(w, "workspacesList.searchPlaceholder", "Search workspaces..."),
109
109
  value: N,
110
110
  onChange: (e) => {
111
111
  P(e.target.value);
@@ -113,74 +113,74 @@ function C() {
113
113
  className: "pl-[3.25rem]"
114
114
  })]
115
115
  }),
116
- L ? /* @__PURE__ */ m("div", {
116
+ L ? /* @__PURE__ */ h("div", {
117
117
  className: "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4",
118
- children: Array.from({ length: 6 }).map((e, t) => /* @__PURE__ */ h("div", {
118
+ children: Array.from({ length: 6 }).map((e, t) => /* @__PURE__ */ g("div", {
119
119
  className: "space-y-3 p-6 border rounded-lg",
120
- children: [/* @__PURE__ */ h("div", {
120
+ children: [/* @__PURE__ */ g("div", {
121
121
  className: "flex items-center gap-3",
122
- children: [/* @__PURE__ */ m(v, { className: "h-12 w-12 rounded-lg" }), /* @__PURE__ */ h("div", {
122
+ children: [/* @__PURE__ */ h(y, { className: "h-12 w-12 rounded-lg" }), /* @__PURE__ */ g("div", {
123
123
  className: "space-y-2 flex-1",
124
- children: [/* @__PURE__ */ m(v, { className: "h-4 w-3/4" }), /* @__PURE__ */ m(v, { className: "h-3 w-1/2" })]
124
+ children: [/* @__PURE__ */ h(y, { className: "h-4 w-3/4" }), /* @__PURE__ */ h(y, { className: "h-3 w-1/2" })]
125
125
  })]
126
- }), /* @__PURE__ */ m(v, { className: "h-3 w-1/3" })]
126
+ }), /* @__PURE__ */ h(y, { className: "h-3 w-1/3" })]
127
127
  }, t))
128
- }) : z?.items.length === 0 ? /* @__PURE__ */ h("div", {
128
+ }) : z?.items.length === 0 ? /* @__PURE__ */ g("div", {
129
129
  className: "flex flex-col items-center justify-center py-12 text-center",
130
130
  children: [
131
- /* @__PURE__ */ m("div", {
131
+ /* @__PURE__ */ h("div", {
132
132
  className: "rounded-full bg-muted p-6 mb-4",
133
- children: /* @__PURE__ */ m(l, {
133
+ children: /* @__PURE__ */ h(u, {
134
134
  size: 48,
135
135
  className: "text-muted-foreground"
136
136
  })
137
137
  }),
138
- /* @__PURE__ */ m("h3", {
138
+ /* @__PURE__ */ h("h3", {
139
139
  className: "text-lg font-semibold mb-2",
140
- children: w("workspacesList.emptyTitle", "No workspaces found")
140
+ children: o(w, "workspacesList.emptyTitle", "No workspaces found")
141
141
  }),
142
- /* @__PURE__ */ m("p", {
142
+ /* @__PURE__ */ h("p", {
143
143
  className: "text-muted-foreground mb-4 max-w-sm",
144
- children: A.search ? w("workspacesList.emptySearchHint", "Try adjusting your search criteria") : w("workspacesList.emptyCreateHint", "Get started by creating your first workspace")
144
+ children: A.search ? o(w, "workspacesList.emptySearchHint", "Try adjusting your search criteria") : o(w, "workspacesList.emptyCreateHint", "Get started by creating your first workspace")
145
145
  }),
146
- !A.search && /* @__PURE__ */ m(x, {
146
+ !A.search && /* @__PURE__ */ h(S, {
147
147
  permission: a.WORKSPACE_CREATE,
148
148
  deniedBehavior: "disable",
149
- deniedTooltip: w("workspacesList.createDeniedTooltip", "You don't have permission to create workspaces"),
150
- children: /* @__PURE__ */ h(g, {
149
+ deniedTooltip: o(w, "workspacesList.createDeniedTooltip", "You don't have permission to create workspaces"),
150
+ children: /* @__PURE__ */ g(_, {
151
151
  onClick: M,
152
152
  className: "gap-2",
153
- children: [/* @__PURE__ */ m(d, { size: 20 }), w("workspacesList.createFirstButton", "Create Your First Workspace")]
153
+ children: [/* @__PURE__ */ h(f, { size: 20 }), o(w, "workspacesList.createFirstButton", "Create Your First Workspace")]
154
154
  })
155
155
  })
156
156
  ]
157
- }) : /* @__PURE__ */ h(p, { children: [O === "list" ? /* @__PURE__ */ m("div", {
157
+ }) : /* @__PURE__ */ g(m, { children: [O === "list" ? /* @__PURE__ */ h("div", {
158
158
  className: "space-y-3",
159
- children: z?.items.map((e) => /* @__PURE__ */ m(i, { workspace: e }, e.id))
160
- }) : /* @__PURE__ */ m("div", {
159
+ children: z?.items.map((e) => /* @__PURE__ */ h(i, { workspace: e }, e.id))
160
+ }) : /* @__PURE__ */ h("div", {
161
161
  className: "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4",
162
- children: z?.items.map((e) => /* @__PURE__ */ m(i, { workspace: e }, e.id))
163
- }), z && z.total > 0 && /* @__PURE__ */ h("div", {
162
+ children: z?.items.map((e) => /* @__PURE__ */ h(i, { workspace: e }, e.id))
163
+ }), z && z.total > 0 && /* @__PURE__ */ g("div", {
164
164
  className: "flex items-center justify-between text-sm text-muted-foreground",
165
- children: [/* @__PURE__ */ h("p", { children: [
166
- w("workspacesList.showing", "Showing"),
165
+ children: [/* @__PURE__ */ g("p", { children: [
166
+ o(w, "workspacesList.showing", "Showing"),
167
167
  " ",
168
168
  z.items.length,
169
169
  " ",
170
- w("workspacesList.of", "of"),
170
+ o(w, "workspacesList.of", "of"),
171
171
  " ",
172
172
  z.total,
173
173
  " ",
174
- z.total === 1 ? w("workspacesList.workspace", "workspace") : w("workspacesList.workspaces", "workspaces")
175
- ] }), z.hasMore && /* @__PURE__ */ m("p", {
174
+ z.total === 1 ? o(w, "workspacesList.workspace", "workspace") : o(w, "workspacesList.workspaces", "workspaces")
175
+ ] }), z.hasMore && /* @__PURE__ */ h("p", {
176
176
  className: "text-xs",
177
- children: w("workspacesList.moreAvailable", "More workspaces available - pagination coming soon")
177
+ children: o(w, "workspacesList.moreAvailable", "More workspaces available - pagination coming soon")
178
178
  })]
179
179
  })] })
180
180
  ]
181
181
  });
182
182
  }
183
183
  //#endregion
184
- export { C as WorkspacesListPage };
184
+ export { w as WorkspacesListPage };
185
185
 
186
186
  //# sourceMappingURL=WorkspacesListPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"WorkspacesListPage.js","names":[],"sources":["../../src/pages/WorkspacesListPage.tsx"],"sourcesContent":["import { Plus, Grid3X3, List as ListIcon, Search } from 'lucide-react';\nimport { Button, Input, Skeleton } from '@burdenoff/fe-libs/ui';\nimport { useNavigate } from 'react-router-dom';\nimport { useWorkspacesStore } from '../store/workspacesStore';\nimport { useMyWorkspaces } from '../hooks/useWorkspaces';\nimport { WorkspaceCard } from '../components/WorkspaceCard';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { useDebounce } from '@burdenoff/fe-libs/shared/hooks';\nimport { PermissionButton } from '@burdenoff/fe-libs/shared/components';\nimport { WORKSPACE_PERMISSIONS } from '../constants/permissions';\nimport { useState, useEffect, useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\n\nexport function WorkspacesListPage() {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const navigate = useNavigate();\n const { basePath = '/' } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const { viewMode, setViewMode, workspaceFilters, setWorkspaceFilters } = useWorkspacesStore();\n\n // Navigate to unified workspace creation page\n const handleCreateWorkspace = () => {\n navigate(withCurrentSearch(`${basePath}/new`));\n };\n\n const [searchValue, setSearchValue] = useState(workspaceFilters.search || '');\n const debouncedSearch = useDebounce(searchValue, 500);\n\n // Update filters when debounced search changes\n useEffect(() => {\n if (debouncedSearch !== workspaceFilters.search) {\n setWorkspaceFilters({ search: debouncedSearch });\n }\n }, [debouncedSearch, workspaceFilters.search, setWorkspaceFilters]);\n\n const { data: rawData, isLoading, error } = useMyWorkspaces();\n\n // Client-side search filtering (myWorkspaces doesn't accept filter args)\n const data = rawData\n ? {\n ...rawData,\n items: debouncedSearch\n ? rawData.items.filter((ws) =>\n ws.name.toLowerCase().includes(debouncedSearch.toLowerCase())\n )\n : rawData.items,\n total: debouncedSearch\n ? rawData.items.filter((ws) =>\n ws.name.toLowerCase().includes(debouncedSearch.toLowerCase())\n ).length\n : rawData.total,\n }\n : undefined;\n\n const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n setSearchValue(e.target.value);\n };\n\n if (error) {\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n <div className=\"flex flex-col items-center justify-center py-12\">\n <p className=\"text-destructive text-lg font-semibold\">\n {tr('workspacesList.loadError', 'Error loading workspaces')}\n </p>\n <p className=\"text-muted-foreground text-sm mt-2\">\n {error instanceof Error\n ? error.message\n : tr('workspacesList.unknownError', 'Unknown error')}\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header with title + actions */}\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3\">\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">\n {tr('workspacesList.title', 'Workspaces')}\n </h1>\n <p className=\"text-sm sm:text-base text-muted-foreground\">\n {tr('workspacesList.description', 'Manage and organize your workspaces')}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n {/* View mode toggle */}\n <Button\n variant={viewMode === 'grid' ? 'default' : 'outline'}\n size=\"icon\"\n onClick={() => setViewMode('grid')}\n className=\"hidden sm:flex\"\n aria-label={tr('workspacesList.gridViewAriaLabel', 'Show workspaces in grid view')}\n title={tr('workspacesList.gridViewTitle', 'Grid view')}\n >\n <Grid3X3 size={20} />\n </Button>\n <Button\n variant={viewMode === 'list' ? 'default' : 'outline'}\n size=\"icon\"\n onClick={() => setViewMode('list')}\n className=\"hidden sm:flex\"\n aria-label={tr('workspacesList.listViewAriaLabel', 'Show workspaces in list view')}\n title={tr('workspacesList.listViewTitle', 'List view')}\n >\n <ListIcon size={20} />\n </Button>\n {/* Create button - requires workspace:create permission */}\n <PermissionButton\n permission={WORKSPACE_PERMISSIONS.WORKSPACE_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip={tr(\n 'workspacesList.createDeniedTooltip',\n \"You don't have permission to create workspaces\"\n )}\n >\n <Button onClick={handleCreateWorkspace} className=\"gap-2\">\n <Plus size={20} />\n <span className=\"hidden sm:inline\">\n {tr('workspacesList.createButton', 'Create Workspace')}\n </span>\n <span className=\"sm:hidden\">{tr('workspacesList.createButtonShort', 'Create')}</span>\n </Button>\n </PermissionButton>\n </div>\n </div>\n\n {/* Search */}\n <div className=\"relative\">\n <Search\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground\"\n size={20}\n />\n <Input\n placeholder={tr('workspacesList.searchPlaceholder', 'Search workspaces...')}\n value={searchValue}\n onChange={handleSearchChange}\n className=\"pl-[3.25rem]\"\n />\n </div>\n\n {/* Content */}\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 <div key={i} className=\"space-y-3 p-6 border rounded-lg\">\n <div className=\"flex items-center gap-3\">\n <Skeleton className=\"h-12 w-12 rounded-lg\" />\n <div className=\"space-y-2 flex-1\">\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-3 w-1/2\" />\n </div>\n </div>\n <Skeleton className=\"h-3 w-1/3\" />\n </div>\n ))}\n </div>\n ) : data?.items.length === 0 ? (\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 <Grid3X3 size={48} className=\"text-muted-foreground\" />\n </div>\n <h3 className=\"text-lg font-semibold mb-2\">\n {tr('workspacesList.emptyTitle', 'No workspaces found')}\n </h3>\n <p className=\"text-muted-foreground mb-4 max-w-sm\">\n {workspaceFilters.search\n ? tr('workspacesList.emptySearchHint', 'Try adjusting your search criteria')\n : tr(\n 'workspacesList.emptyCreateHint',\n 'Get started by creating your first workspace'\n )}\n </p>\n {!workspaceFilters.search && (\n <PermissionButton\n permission={WORKSPACE_PERMISSIONS.WORKSPACE_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip={tr(\n 'workspacesList.createDeniedTooltip',\n \"You don't have permission to create workspaces\"\n )}\n >\n <Button onClick={handleCreateWorkspace} className=\"gap-2\">\n <Plus size={20} />\n {tr('workspacesList.createFirstButton', 'Create Your First Workspace')}\n </Button>\n </PermissionButton>\n )}\n </div>\n ) : (\n <>\n {/* View */}\n {viewMode === 'list' ? (\n <div className=\"space-y-3\">\n {data?.items.map((workspace) => (\n <WorkspaceCard key={workspace.id} workspace={workspace} />\n ))}\n </div>\n ) : (\n <div className=\"grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4\">\n {data?.items.map((workspace) => (\n <WorkspaceCard key={workspace.id} workspace={workspace} />\n ))}\n </div>\n )}\n\n {/* Pagination info */}\n {data && data.total > 0 && (\n <div className=\"flex items-center justify-between text-sm text-muted-foreground\">\n <p>\n {tr('workspacesList.showing', 'Showing')} {data.items.length}{' '}\n {tr('workspacesList.of', 'of')} {data.total}{' '}\n {data.total !== 1\n ? tr('workspacesList.workspaces', 'workspaces')\n : tr('workspacesList.workspace', 'workspace')}\n </p>\n {data.hasMore && (\n <p className=\"text-xs\">\n {tr(\n 'workspacesList.moreAvailable',\n 'More workspaces available - pagination coming soon'\n )}\n </p>\n )}\n </div>\n )}\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"WorkspacesListPage.js","names":[],"sources":["../../src/pages/WorkspacesListPage.tsx"],"sourcesContent":["import { Plus, Grid3X3, List as ListIcon, Search } from 'lucide-react';\nimport { Button, Input, Skeleton } from '@burdenoff/fe-libs/ui';\nimport { useNavigate } from 'react-router-dom';\nimport { useWorkspacesStore } from '../store/workspacesStore';\nimport { useMyWorkspaces } from '../hooks/useWorkspaces';\nimport { WorkspaceCard } from '../components/WorkspaceCard';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { useDebounce } from '@burdenoff/fe-libs/shared/hooks';\nimport { PermissionButton } from '@burdenoff/fe-libs/shared/components';\nimport { WORKSPACE_PERMISSIONS } from '../constants/permissions';\nimport { useState, useEffect } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\n\nexport function WorkspacesListPage() {\n useWorkspacesPageView('workspaces.list');\n const { t } = useI18n();\n\n const navigate = useNavigate();\n const { basePath = '/' } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const { viewMode, setViewMode, workspaceFilters, setWorkspaceFilters } = useWorkspacesStore();\n\n // Navigate to unified workspace creation page\n const handleCreateWorkspace = () => {\n navigate(withCurrentSearch(`${basePath}/new`));\n };\n\n const [searchValue, setSearchValue] = useState(workspaceFilters.search || '');\n const debouncedSearch = useDebounce(searchValue, 500);\n\n // Update filters when debounced search changes\n useEffect(() => {\n if (debouncedSearch !== workspaceFilters.search) {\n setWorkspaceFilters({ search: debouncedSearch });\n }\n }, [debouncedSearch, workspaceFilters.search, setWorkspaceFilters]);\n\n const { data: rawData, isLoading, error } = useMyWorkspaces();\n\n // Client-side search filtering (myWorkspaces doesn't accept filter args)\n const data = rawData\n ? {\n ...rawData,\n items: debouncedSearch\n ? rawData.items.filter((ws) =>\n ws.name.toLowerCase().includes(debouncedSearch.toLowerCase())\n )\n : rawData.items,\n total: debouncedSearch\n ? rawData.items.filter((ws) =>\n ws.name.toLowerCase().includes(debouncedSearch.toLowerCase())\n ).length\n : rawData.total,\n }\n : undefined;\n\n const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n setSearchValue(e.target.value);\n };\n\n if (error) {\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n <div className=\"flex flex-col items-center justify-center py-12\">\n <p className=\"text-destructive text-lg font-semibold\">\n {tWithFallback(t, 'workspacesList.loadError', 'Error loading workspaces')}\n </p>\n <p className=\"text-muted-foreground text-sm mt-2\">\n {error instanceof Error\n ? error.message\n : tWithFallback(t, 'workspacesList.unknownError', 'Unknown error')}\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header with title + actions */}\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3\">\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">\n {tWithFallback(t, 'workspacesList.title', 'Workspaces')}\n </h1>\n <p className=\"text-sm sm:text-base text-muted-foreground\">\n {tWithFallback(t, 'workspacesList.description', 'Manage and organize your workspaces')}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n {/* View mode toggle */}\n <Button\n variant={viewMode === 'grid' ? 'default' : 'outline'}\n size=\"icon\"\n onClick={() => setViewMode('grid')}\n className=\"hidden sm:flex\"\n aria-label={tWithFallback(\n t,\n 'workspacesList.gridViewAriaLabel',\n 'Show workspaces in grid view'\n )}\n title={tWithFallback(t, 'workspacesList.gridViewTitle', 'Grid view')}\n >\n <Grid3X3 size={20} />\n </Button>\n <Button\n variant={viewMode === 'list' ? 'default' : 'outline'}\n size=\"icon\"\n onClick={() => setViewMode('list')}\n className=\"hidden sm:flex\"\n aria-label={tWithFallback(\n t,\n 'workspacesList.listViewAriaLabel',\n 'Show workspaces in list view'\n )}\n title={tWithFallback(t, 'workspacesList.listViewTitle', 'List view')}\n >\n <ListIcon size={20} />\n </Button>\n {/* Create button - requires workspace:create permission */}\n <PermissionButton\n permission={WORKSPACE_PERMISSIONS.WORKSPACE_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip={tWithFallback(\n t,\n 'workspacesList.createDeniedTooltip',\n \"You don't have permission to create workspaces\"\n )}\n >\n <Button onClick={handleCreateWorkspace} className=\"gap-2\">\n <Plus size={20} />\n <span className=\"hidden sm:inline\">\n {tWithFallback(t, 'workspacesList.createButton', 'Create Workspace')}\n </span>\n <span className=\"sm:hidden\">\n {tWithFallback(t, 'workspacesList.createButtonShort', 'Create')}\n </span>\n </Button>\n </PermissionButton>\n </div>\n </div>\n\n {/* Search */}\n <div className=\"relative\">\n <Search\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground\"\n size={20}\n />\n <Input\n placeholder={tWithFallback(t, 'workspacesList.searchPlaceholder', 'Search workspaces...')}\n value={searchValue}\n onChange={handleSearchChange}\n className=\"pl-[3.25rem]\"\n />\n </div>\n\n {/* Content */}\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 <div key={i} className=\"space-y-3 p-6 border rounded-lg\">\n <div className=\"flex items-center gap-3\">\n <Skeleton className=\"h-12 w-12 rounded-lg\" />\n <div className=\"space-y-2 flex-1\">\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-3 w-1/2\" />\n </div>\n </div>\n <Skeleton className=\"h-3 w-1/3\" />\n </div>\n ))}\n </div>\n ) : data?.items.length === 0 ? (\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 <Grid3X3 size={48} className=\"text-muted-foreground\" />\n </div>\n <h3 className=\"text-lg font-semibold mb-2\">\n {tWithFallback(t, 'workspacesList.emptyTitle', 'No workspaces found')}\n </h3>\n <p className=\"text-muted-foreground mb-4 max-w-sm\">\n {workspaceFilters.search\n ? tWithFallback(\n t,\n 'workspacesList.emptySearchHint',\n 'Try adjusting your search criteria'\n )\n : tWithFallback(\n t,\n 'workspacesList.emptyCreateHint',\n 'Get started by creating your first workspace'\n )}\n </p>\n {!workspaceFilters.search && (\n <PermissionButton\n permission={WORKSPACE_PERMISSIONS.WORKSPACE_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip={tWithFallback(\n t,\n 'workspacesList.createDeniedTooltip',\n \"You don't have permission to create workspaces\"\n )}\n >\n <Button onClick={handleCreateWorkspace} className=\"gap-2\">\n <Plus size={20} />\n {tWithFallback(\n t,\n 'workspacesList.createFirstButton',\n 'Create Your First Workspace'\n )}\n </Button>\n </PermissionButton>\n )}\n </div>\n ) : (\n <>\n {/* View */}\n {viewMode === 'list' ? (\n <div className=\"space-y-3\">\n {data?.items.map((workspace) => (\n <WorkspaceCard key={workspace.id} workspace={workspace} />\n ))}\n </div>\n ) : (\n <div className=\"grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4\">\n {data?.items.map((workspace) => (\n <WorkspaceCard key={workspace.id} workspace={workspace} />\n ))}\n </div>\n )}\n\n {/* Pagination info */}\n {data && data.total > 0 && (\n <div className=\"flex items-center justify-between text-sm text-muted-foreground\">\n <p>\n {tWithFallback(t, 'workspacesList.showing', 'Showing')} {data.items.length}{' '}\n {tWithFallback(t, 'workspacesList.of', 'of')} {data.total}{' '}\n {data.total !== 1\n ? tWithFallback(t, 'workspacesList.workspaces', 'workspaces')\n : tWithFallback(t, 'workspacesList.workspace', 'workspace')}\n </p>\n {data.hasMore && (\n <p className=\"text-xs\">\n {tWithFallback(\n t,\n 'workspacesList.moreAvailable',\n 'More workspaces available - pagination coming soon'\n )}\n </p>\n )}\n </div>\n )}\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardCustomizer.d.ts","sourceRoot":"","sources":["../../../src/pages/dashboard/DashboardCustomizer.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAI5C,UAAU,wBAAwB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,MAAM,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,OAAO,EACP,MAAM,EACN,QAAQ,GACT,EAAE,wBAAwB,2CA+J1B"}
1
+ {"version":3,"file":"DashboardCustomizer.d.ts","sourceRoot":"","sources":["../../../src/pages/dashboard/DashboardCustomizer.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAI5C,UAAU,wBAAwB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,MAAM,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,OAAO,EACP,MAAM,EACN,QAAQ,GACT,EAAE,wBAAwB,2CA2J1B"}
@@ -1,25 +1,23 @@
1
- import { DEFAULT_DASHBOARD_WIDGETS as e, WIDGET_DEFINITIONS as t } from "./config.js";
2
- import { nativeImpact as n } from "../../utils/nativeBridge.js";
3
- import { useCallback as r, useEffect as i, useState as a } from "react";
4
- import { ArrowDown as o, ArrowUp as s, GripVertical as c, Loader2 as l, RotateCcw as u, Save as d } from "lucide-react";
5
- import { jsx as f, jsxs as p } from "react/jsx-runtime";
6
- import { Badge as m, Button as h, Separator as g, Sheet as _, SheetContent as v, SheetDescription as y, SheetFooter as b, SheetHeader as x, SheetTitle as S, Switch as C } from "@burdenoff/fe-libs/ui";
7
- import { useI18n as w } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
1
+ import { tWithFallback as e } from "../../utils/i18n.js";
2
+ import { DEFAULT_DASHBOARD_WIDGETS as t, WIDGET_DEFINITIONS as n } from "./config.js";
3
+ import { nativeImpact as r } from "../../utils/nativeBridge.js";
4
+ import { useCallback as i, useEffect as a, useState as o } from "react";
5
+ import { ArrowDown as s, ArrowUp as c, GripVertical as l, Loader2 as u, RotateCcw as d, Save as f } from "lucide-react";
6
+ import { jsx as p, jsxs as m } from "react/jsx-runtime";
7
+ import { Badge as h, Button as g, Separator as _, Sheet as v, SheetContent as y, SheetDescription as b, SheetFooter as x, SheetHeader as S, SheetTitle as C, Switch as w } from "@burdenoff/fe-libs/ui";
8
+ import { useI18n as T } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
8
9
  //#region src/pages/dashboard/DashboardCustomizer.tsx
9
- function T({ open: T, onOpenChange: E, widgets: D, onSave: O, isSaving: k }) {
10
- let { t: A } = w(), j = r((e, t) => {
11
- let n = A(e);
12
- return n === e ? t : n;
13
- }, [A]), [M, N] = a(D), [P, F] = a(!1);
14
- i(() => {
15
- T && (N([...D]), F(!1));
16
- }, [T, D]);
17
- let I = r((e) => {
10
+ function E({ open: E, onOpenChange: D, widgets: O, onSave: k, isSaving: A }) {
11
+ let { t: j } = T(), [M, N] = o(O), [P, F] = o(!1);
12
+ a(() => {
13
+ E && (N([...O]), F(!1));
14
+ }, [E, O]);
15
+ let I = i((e) => {
18
16
  N((t) => t.map((t) => t.widgetId === e ? {
19
17
  ...t,
20
18
  enabled: !t.enabled
21
19
  } : t)), F(!0);
22
- }, []), L = r((e, t) => {
20
+ }, []), L = i((e, t) => {
23
21
  N((n) => {
24
22
  let r = [...n].sort((e, t) => e.order - t.order), i = r.findIndex((t) => t.widgetId === e);
25
23
  if (i < 0) return n;
@@ -34,71 +32,71 @@ function T({ open: T, onOpenChange: E, widgets: D, onSave: O, isSaving: k }) {
34
32
  order: o
35
33
  }, r;
36
34
  }), F(!0);
37
- }, []), R = r(() => {
38
- N([...e]), F(!0);
39
- }, []), z = r(async () => {
40
- await O(M), F(!1), E(!1);
35
+ }, []), R = i(() => {
36
+ N([...t]), F(!0);
37
+ }, []), z = i(async () => {
38
+ await k(M), F(!1), D(!1);
41
39
  }, [
42
40
  M,
43
- O,
44
- E
41
+ k,
42
+ D
45
43
  ]), B = [...M].sort((e, t) => e.order - t.order);
46
- return /* @__PURE__ */ f(_, {
47
- open: T,
48
- onOpenChange: E,
49
- children: /* @__PURE__ */ p(v, {
44
+ return /* @__PURE__ */ p(v, {
45
+ open: E,
46
+ onOpenChange: D,
47
+ children: /* @__PURE__ */ m(y, {
50
48
  className: "w-full sm:max-w-md overflow-y-auto",
51
49
  side: "right",
52
50
  children: [
53
- /* @__PURE__ */ p(x, { children: [/* @__PURE__ */ f(S, { children: j("dashboardCustomizer.title", "Customize Dashboard") }), /* @__PURE__ */ f(y, { children: j("dashboardCustomizer.description", "Toggle widgets on or off and reorder them to personalize your dashboard.") })] }),
54
- /* @__PURE__ */ f("div", {
51
+ /* @__PURE__ */ m(S, { children: [/* @__PURE__ */ p(C, { children: e(j, "dashboardCustomizer.title", "Customize Dashboard") }), /* @__PURE__ */ p(b, { children: e(j, "dashboardCustomizer.description", "Toggle widgets on or off and reorder them to personalize your dashboard.") })] }),
52
+ /* @__PURE__ */ p("div", {
55
53
  className: "py-4 space-y-1",
56
- children: B.map((e, r) => {
57
- let i = t[e.widgetId];
58
- return i ? /* @__PURE__ */ p("div", {
54
+ children: B.map((e, t) => {
55
+ let i = n[e.widgetId];
56
+ return i ? /* @__PURE__ */ m("div", {
59
57
  className: `flex items-center gap-3 p-3 rounded-lg border transition-colors ${e.enabled ? "border-border-default bg-bg-surface" : "border-transparent bg-muted/30 opacity-60"}`,
60
58
  children: [
61
- /* @__PURE__ */ f(c, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
62
- /* @__PURE__ */ p("div", {
59
+ /* @__PURE__ */ p(l, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
60
+ /* @__PURE__ */ m("div", {
63
61
  className: "flex-1 min-w-0",
64
- children: [/* @__PURE__ */ p("div", {
62
+ children: [/* @__PURE__ */ m("div", {
65
63
  className: "flex items-center gap-2",
66
- children: [/* @__PURE__ */ f("span", {
64
+ children: [/* @__PURE__ */ p("span", {
67
65
  className: "text-sm font-medium text-text-primary",
68
66
  children: i.title
69
- }), /* @__PURE__ */ f(m, {
67
+ }), /* @__PURE__ */ p(h, {
70
68
  variant: "outline",
71
69
  className: "text-[10px]",
72
70
  children: i.defaultSize
73
71
  })]
74
- }), /* @__PURE__ */ f("p", {
72
+ }), /* @__PURE__ */ p("p", {
75
73
  className: "text-xs text-muted-foreground truncate",
76
74
  children: i.description
77
75
  })]
78
76
  }),
79
- /* @__PURE__ */ p("div", {
77
+ /* @__PURE__ */ m("div", {
80
78
  className: "flex items-center gap-1 shrink-0",
81
79
  children: [
82
- /* @__PURE__ */ f(h, {
80
+ /* @__PURE__ */ p(g, {
83
81
  variant: "ghost",
84
82
  size: "sm",
85
83
  className: "h-7 w-7 p-0",
86
- disabled: r === 0,
84
+ disabled: t === 0,
87
85
  onClick: () => L(e.widgetId, "up"),
88
- children: /* @__PURE__ */ f(s, { className: "h-3 w-3" })
86
+ children: /* @__PURE__ */ p(c, { className: "h-3 w-3" })
89
87
  }),
90
- /* @__PURE__ */ f(h, {
88
+ /* @__PURE__ */ p(g, {
91
89
  variant: "ghost",
92
90
  size: "sm",
93
91
  className: "h-7 w-7 p-0",
94
- disabled: r === B.length - 1,
92
+ disabled: t === B.length - 1,
95
93
  onClick: () => L(e.widgetId, "down"),
96
- children: /* @__PURE__ */ f(o, { className: "h-3 w-3" })
94
+ children: /* @__PURE__ */ p(s, { className: "h-3 w-3" })
97
95
  }),
98
- /* @__PURE__ */ f(C, {
96
+ /* @__PURE__ */ p(w, {
99
97
  checked: e.enabled,
100
98
  onCheckedChange: () => {
101
- n("medium"), I(e.widgetId);
99
+ r("medium"), I(e.widgetId);
102
100
  },
103
101
  disabled: !i.removable,
104
102
  className: "ml-1"
@@ -109,21 +107,21 @@ function T({ open: T, onOpenChange: E, widgets: D, onSave: O, isSaving: k }) {
109
107
  }, e.widgetId) : null;
110
108
  })
111
109
  }),
112
- /* @__PURE__ */ f(g, {}),
113
- /* @__PURE__ */ p(b, {
110
+ /* @__PURE__ */ p(_, {}),
111
+ /* @__PURE__ */ m(x, {
114
112
  className: "flex-row gap-2 pt-4",
115
- children: [/* @__PURE__ */ p(h, {
113
+ children: [/* @__PURE__ */ m(g, {
116
114
  variant: "outline",
117
115
  size: "sm",
118
116
  onClick: R,
119
117
  className: "flex-1",
120
- children: [/* @__PURE__ */ f(u, { className: "h-3 w-3 mr-1.5" }), j("dashboardCustomizer.resetToDefaults", "Reset to defaults")]
121
- }), /* @__PURE__ */ p(h, {
118
+ children: [/* @__PURE__ */ p(d, { className: "h-3 w-3 mr-1.5" }), e(j, "dashboardCustomizer.resetToDefaults", "Reset to defaults")]
119
+ }), /* @__PURE__ */ m(g, {
122
120
  size: "sm",
123
121
  onClick: z,
124
- disabled: !P || k,
122
+ disabled: !P || A,
125
123
  className: "flex-1",
126
- children: [k ? /* @__PURE__ */ f(l, { className: "h-3 w-3 mr-1.5 animate-spin" }) : /* @__PURE__ */ f(d, { className: "h-3 w-3 mr-1.5" }), j("dashboardCustomizer.saveLayout", "Save layout")]
124
+ children: [A ? /* @__PURE__ */ p(u, { className: "h-3 w-3 mr-1.5 animate-spin" }) : /* @__PURE__ */ p(f, { className: "h-3 w-3 mr-1.5" }), e(j, "dashboardCustomizer.saveLayout", "Save layout")]
127
125
  })]
128
126
  })
129
127
  ]
@@ -131,6 +129,6 @@ function T({ open: T, onOpenChange: E, widgets: D, onSave: O, isSaving: k }) {
131
129
  });
132
130
  }
133
131
  //#endregion
134
- export { T as DashboardCustomizer };
132
+ export { E as DashboardCustomizer };
135
133
 
136
134
  //# sourceMappingURL=DashboardCustomizer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardCustomizer.js","names":[],"sources":["../../../src/pages/dashboard/DashboardCustomizer.tsx"],"sourcesContent":["/**\n * DashboardCustomizer — Sheet overlay for customizing dashboard widgets.\n *\n * Allows users to toggle widgets on/off, reorder them, and save their layout.\n */\nimport { useState, useCallback, useEffect } from 'react';\nimport {\n Sheet,\n SheetContent,\n SheetHeader,\n SheetTitle,\n SheetDescription,\n SheetFooter,\n Button,\n Switch,\n Badge,\n Separator,\n} from '@burdenoff/fe-libs/ui';\nimport { ArrowUp, ArrowDown, RotateCcw, Save, Loader2, GripVertical } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport type { WidgetConfig } from './types';\nimport { WIDGET_DEFINITIONS, DEFAULT_DASHBOARD_WIDGETS } from './config';\nimport { nativeImpact } from '../../utils/nativeBridge';\n\ninterface DashboardCustomizerProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n widgets: WidgetConfig[];\n onSave: (widgets: WidgetConfig[]) => Promise<void>;\n isSaving: boolean;\n}\n\nexport function DashboardCustomizer({\n open,\n onOpenChange,\n widgets,\n onSave,\n isSaving,\n}: DashboardCustomizerProps) {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const [localWidgets, setLocalWidgets] = useState<WidgetConfig[]>(widgets);\n const [hasChanges, setHasChanges] = useState(false);\n\n // Sync local state when sheet opens or widgets prop changes while open\n useEffect(() => {\n if (open) {\n setLocalWidgets([...widgets]);\n setHasChanges(false);\n }\n }, [open, widgets]);\n\n const toggleWidget = useCallback((widgetId: string) => {\n setLocalWidgets((prev) =>\n prev.map((w) => (w.widgetId === widgetId ? { ...w, enabled: !w.enabled } : w))\n );\n setHasChanges(true);\n }, []);\n\n const moveWidget = useCallback((widgetId: string, direction: 'up' | 'down') => {\n setLocalWidgets((prev) => {\n const sorted = [...prev].sort((a, b) => a.order - b.order);\n const idx = sorted.findIndex((w) => w.widgetId === widgetId);\n if (idx < 0) return prev;\n\n const swapIdx = direction === 'up' ? idx - 1 : idx + 1;\n if (swapIdx < 0 || swapIdx >= sorted.length) return prev;\n\n // Swap orders\n const tempOrder = sorted[idx].order;\n sorted[idx] = { ...sorted[idx], order: sorted[swapIdx].order };\n sorted[swapIdx] = { ...sorted[swapIdx], order: tempOrder };\n\n return sorted;\n });\n setHasChanges(true);\n }, []);\n\n const resetToDefaults = useCallback(() => {\n setLocalWidgets([...DEFAULT_DASHBOARD_WIDGETS]);\n setHasChanges(true);\n }, []);\n\n const handleSave = useCallback(async () => {\n await onSave(localWidgets);\n setHasChanges(false);\n onOpenChange(false);\n }, [localWidgets, onSave, onOpenChange]);\n\n const sorted = [...localWidgets].sort((a, b) => a.order - b.order);\n\n return (\n <Sheet open={open} onOpenChange={onOpenChange}>\n <SheetContent className=\"w-full sm:max-w-md overflow-y-auto\" side=\"right\">\n <SheetHeader>\n <SheetTitle>{tr('dashboardCustomizer.title', 'Customize Dashboard')}</SheetTitle>\n <SheetDescription>\n {tr(\n 'dashboardCustomizer.description',\n 'Toggle widgets on or off and reorder them to personalize your dashboard.'\n )}\n </SheetDescription>\n </SheetHeader>\n\n <div className=\"py-4 space-y-1\">\n {sorted.map((widget, idx) => {\n const definition = WIDGET_DEFINITIONS[widget.widgetId];\n if (!definition) return null;\n\n return (\n <div\n key={widget.widgetId}\n className={`flex items-center gap-3 p-3 rounded-lg border transition-colors ${\n widget.enabled\n ? 'border-border-default bg-bg-surface'\n : 'border-transparent bg-muted/30 opacity-60'\n }`}\n >\n <GripVertical className=\"h-4 w-4 text-muted-foreground shrink-0\" />\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium text-text-primary\">\n {definition.title}\n </span>\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {definition.defaultSize}\n </Badge>\n </div>\n <p className=\"text-xs text-muted-foreground truncate\">{definition.description}</p>\n </div>\n\n <div className=\"flex items-center gap-1 shrink-0\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-7 w-7 p-0\"\n disabled={idx === 0}\n onClick={() => moveWidget(widget.widgetId, 'up')}\n >\n <ArrowUp className=\"h-3 w-3\" />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-7 w-7 p-0\"\n disabled={idx === sorted.length - 1}\n onClick={() => moveWidget(widget.widgetId, 'down')}\n >\n <ArrowDown className=\"h-3 w-3\" />\n </Button>\n <Switch\n checked={widget.enabled}\n onCheckedChange={() => {\n void nativeImpact('medium');\n toggleWidget(widget.widgetId);\n }}\n disabled={!definition.removable}\n className=\"ml-1\"\n />\n </div>\n </div>\n );\n })}\n </div>\n\n <Separator />\n\n <SheetFooter className=\"flex-row gap-2 pt-4\">\n <Button variant=\"outline\" size=\"sm\" onClick={resetToDefaults} className=\"flex-1\">\n <RotateCcw className=\"h-3 w-3 mr-1.5\" />\n {tr('dashboardCustomizer.resetToDefaults', 'Reset to defaults')}\n </Button>\n <Button\n size=\"sm\"\n onClick={handleSave}\n disabled={!hasChanges || isSaving}\n className=\"flex-1\"\n >\n {isSaving ? (\n <Loader2 className=\"h-3 w-3 mr-1.5 animate-spin\" />\n ) : (\n <Save className=\"h-3 w-3 mr-1.5\" />\n )}\n {tr('dashboardCustomizer.saveLayout', 'Save layout')}\n </Button>\n </SheetFooter>\n </SheetContent>\n </Sheet>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DashboardCustomizer.js","names":[],"sources":["../../../src/pages/dashboard/DashboardCustomizer.tsx"],"sourcesContent":["/**\n * DashboardCustomizer — Sheet overlay for customizing dashboard widgets.\n *\n * Allows users to toggle widgets on/off, reorder them, and save their layout.\n */\nimport { useState, useCallback, useEffect } from 'react';\nimport {\n Sheet,\n SheetContent,\n SheetHeader,\n SheetTitle,\n SheetDescription,\n SheetFooter,\n Button,\n Switch,\n Badge,\n Separator,\n} from '@burdenoff/fe-libs/ui';\nimport { ArrowUp, ArrowDown, RotateCcw, Save, Loader2, GripVertical } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../../utils/i18n';\nimport type { WidgetConfig } from './types';\nimport { WIDGET_DEFINITIONS, DEFAULT_DASHBOARD_WIDGETS } from './config';\nimport { nativeImpact } from '../../utils/nativeBridge';\n\ninterface DashboardCustomizerProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n widgets: WidgetConfig[];\n onSave: (widgets: WidgetConfig[]) => Promise<void>;\n isSaving: boolean;\n}\n\nexport function DashboardCustomizer({\n open,\n onOpenChange,\n widgets,\n onSave,\n isSaving,\n}: DashboardCustomizerProps) {\n const { t } = useI18n();\n\n const [localWidgets, setLocalWidgets] = useState<WidgetConfig[]>(widgets);\n const [hasChanges, setHasChanges] = useState(false);\n\n // Sync local state when sheet opens or widgets prop changes while open\n useEffect(() => {\n if (open) {\n setLocalWidgets([...widgets]);\n setHasChanges(false);\n }\n }, [open, widgets]);\n\n const toggleWidget = useCallback((widgetId: string) => {\n setLocalWidgets((prev) =>\n prev.map((w) => (w.widgetId === widgetId ? { ...w, enabled: !w.enabled } : w))\n );\n setHasChanges(true);\n }, []);\n\n const moveWidget = useCallback((widgetId: string, direction: 'up' | 'down') => {\n setLocalWidgets((prev) => {\n const sorted = [...prev].sort((a, b) => a.order - b.order);\n const idx = sorted.findIndex((w) => w.widgetId === widgetId);\n if (idx < 0) return prev;\n\n const swapIdx = direction === 'up' ? idx - 1 : idx + 1;\n if (swapIdx < 0 || swapIdx >= sorted.length) return prev;\n\n // Swap orders\n const tempOrder = sorted[idx].order;\n sorted[idx] = { ...sorted[idx], order: sorted[swapIdx].order };\n sorted[swapIdx] = { ...sorted[swapIdx], order: tempOrder };\n\n return sorted;\n });\n setHasChanges(true);\n }, []);\n\n const resetToDefaults = useCallback(() => {\n setLocalWidgets([...DEFAULT_DASHBOARD_WIDGETS]);\n setHasChanges(true);\n }, []);\n\n const handleSave = useCallback(async () => {\n await onSave(localWidgets);\n setHasChanges(false);\n onOpenChange(false);\n }, [localWidgets, onSave, onOpenChange]);\n\n const sorted = [...localWidgets].sort((a, b) => a.order - b.order);\n\n return (\n <Sheet open={open} onOpenChange={onOpenChange}>\n <SheetContent className=\"w-full sm:max-w-md overflow-y-auto\" side=\"right\">\n <SheetHeader>\n <SheetTitle>\n {tWithFallback(t, 'dashboardCustomizer.title', 'Customize Dashboard')}\n </SheetTitle>\n <SheetDescription>\n {tWithFallback(\n t,\n 'dashboardCustomizer.description',\n 'Toggle widgets on or off and reorder them to personalize your dashboard.'\n )}\n </SheetDescription>\n </SheetHeader>\n\n <div className=\"py-4 space-y-1\">\n {sorted.map((widget, idx) => {\n const definition = WIDGET_DEFINITIONS[widget.widgetId];\n if (!definition) return null;\n\n return (\n <div\n key={widget.widgetId}\n className={`flex items-center gap-3 p-3 rounded-lg border transition-colors ${\n widget.enabled\n ? 'border-border-default bg-bg-surface'\n : 'border-transparent bg-muted/30 opacity-60'\n }`}\n >\n <GripVertical className=\"h-4 w-4 text-muted-foreground shrink-0\" />\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium text-text-primary\">\n {definition.title}\n </span>\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {definition.defaultSize}\n </Badge>\n </div>\n <p className=\"text-xs text-muted-foreground truncate\">{definition.description}</p>\n </div>\n\n <div className=\"flex items-center gap-1 shrink-0\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-7 w-7 p-0\"\n disabled={idx === 0}\n onClick={() => moveWidget(widget.widgetId, 'up')}\n >\n <ArrowUp className=\"h-3 w-3\" />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-7 w-7 p-0\"\n disabled={idx === sorted.length - 1}\n onClick={() => moveWidget(widget.widgetId, 'down')}\n >\n <ArrowDown className=\"h-3 w-3\" />\n </Button>\n <Switch\n checked={widget.enabled}\n onCheckedChange={() => {\n void nativeImpact('medium');\n toggleWidget(widget.widgetId);\n }}\n disabled={!definition.removable}\n className=\"ml-1\"\n />\n </div>\n </div>\n );\n })}\n </div>\n\n <Separator />\n\n <SheetFooter className=\"flex-row gap-2 pt-4\">\n <Button variant=\"outline\" size=\"sm\" onClick={resetToDefaults} className=\"flex-1\">\n <RotateCcw className=\"h-3 w-3 mr-1.5\" />\n {tWithFallback(t, 'dashboardCustomizer.resetToDefaults', 'Reset to defaults')}\n </Button>\n <Button\n size=\"sm\"\n onClick={handleSave}\n disabled={!hasChanges || isSaving}\n className=\"flex-1\"\n >\n {isSaving ? (\n <Loader2 className=\"h-3 w-3 mr-1.5 animate-spin\" />\n ) : (\n <Save className=\"h-3 w-3 mr-1.5\" />\n )}\n {tWithFallback(t, 'dashboardCustomizer.saveLayout', 'Save layout')}\n </Button>\n </SheetFooter>\n </SheetContent>\n </Sheet>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardGrid.d.ts","sourceRoot":"","sources":["../../../src/pages/dashboard/DashboardGrid.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAmCzD,UAAU,kBAAmB,SAAQ,WAAW;IAC9C,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,wBAAgB,aAAa,CAAC,EAAE,OAAO,EAAE,GAAG,WAAW,EAAE,EAAE,kBAAkB,2CAmC5E"}
1
+ {"version":3,"file":"DashboardGrid.d.ts","sourceRoot":"","sources":["../../../src/pages/dashboard/DashboardGrid.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAmCzD,UAAU,kBAAmB,SAAQ,WAAW;IAC9C,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,wBAAgB,aAAa,CAAC,EAAE,OAAO,EAAE,GAAG,WAAW,EAAE,EAAE,kBAAkB,2CAgC5E"}