@burdenoff/microfe-workspaces 2026.802.1 → 2026.829.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 (55) hide show
  1. package/dist/generated/global-operations.js +44 -1
  2. package/dist/generated/global-operations.js.map +1 -1
  3. package/dist/generated/wspace-operations.js +18 -18
  4. package/dist/generated/wspace-operations.js.map +1 -1
  5. package/dist/hooks/useWorkspaceMutations.js +84 -61
  6. package/dist/hooks/useWorkspaceMutations.js.map +1 -1
  7. package/dist/pages/MembersListPage.js +3 -3
  8. package/dist/pages/MembersListPage.js.map +1 -1
  9. package/dist/pages/ProjectsListPage.js +3 -3
  10. package/dist/pages/ProjectsListPage.js.map +1 -1
  11. package/dist/pages/WorkspaceDetailPage.js +152 -114
  12. package/dist/pages/WorkspaceDetailPage.js.map +1 -1
  13. package/dist/pages/WorkspaceInvitePage.js +2 -2
  14. package/dist/pages/WorkspaceInvitePage.js.map +1 -1
  15. package/dist/pages/dashboard/DashboardCustomizer.js +62 -62
  16. package/dist/pages/dashboard/DashboardCustomizer.js.map +1 -1
  17. package/dist/pages/dashboard/DashboardPage.js +88 -69
  18. package/dist/pages/dashboard/DashboardPage.js.map +1 -1
  19. package/dist/pages/dashboard/config.js +77 -1
  20. package/dist/pages/dashboard/config.js.map +1 -1
  21. package/dist/pages/dashboard/widgets/AgentsWidget.js +56 -54
  22. package/dist/pages/dashboard/widgets/AgentsWidget.js.map +1 -1
  23. package/dist/pages/dashboard/widgets/GroupsWidget.js +29 -31
  24. package/dist/pages/dashboard/widgets/GroupsWidget.js.map +1 -1
  25. package/dist/pages/dashboard/widgets/MembersWidget.js +33 -33
  26. package/dist/pages/dashboard/widgets/MembersWidget.js.map +1 -1
  27. package/dist/pages/dashboard/widgets/ProjectsWidget.js +42 -44
  28. package/dist/pages/dashboard/widgets/ProjectsWidget.js.map +1 -1
  29. package/dist/pages/dashboard/widgets/QuickActionsWidget.js +171 -161
  30. package/dist/pages/dashboard/widgets/QuickActionsWidget.js.map +1 -1
  31. package/dist/pages/dashboard/widgets/RecentActivityWidget.js +54 -44
  32. package/dist/pages/dashboard/widgets/RecentActivityWidget.js.map +1 -1
  33. package/dist/pages/dashboard/widgets/SessionsWidget.js +57 -41
  34. package/dist/pages/dashboard/widgets/SessionsWidget.js.map +1 -1
  35. package/dist/pages/dashboard/widgets/StorageWidget.js +52 -54
  36. package/dist/pages/dashboard/widgets/StorageWidget.js.map +1 -1
  37. package/dist/pages/dashboard/widgets/TagCloudWidget.js +28 -30
  38. package/dist/pages/dashboard/widgets/TagCloudWidget.js.map +1 -1
  39. package/dist/pages/dashboard/widgets/TipsWidget.js +141 -131
  40. package/dist/pages/dashboard/widgets/TipsWidget.js.map +1 -1
  41. package/dist/pages/dashboard/widgets/UpcomingEventsWidget.js +46 -40
  42. package/dist/pages/dashboard/widgets/UpcomingEventsWidget.js.map +1 -1
  43. package/dist/pages/dashboard/widgets/VibesWidget.js +44 -34
  44. package/dist/pages/dashboard/widgets/VibesWidget.js.map +1 -1
  45. package/dist/pages/dashboard/widgets/WelcomeWidget.js +43 -41
  46. package/dist/pages/dashboard/widgets/WelcomeWidget.js.map +1 -1
  47. package/dist/pages/dashboard/widgets/WorkflowRunsWidget.js +68 -62
  48. package/dist/pages/dashboard/widgets/WorkflowRunsWidget.js.map +1 -1
  49. package/dist/pages/dashboard/widgets/WorkflowStatsWidget.js +50 -44
  50. package/dist/pages/dashboard/widgets/WorkflowStatsWidget.js.map +1 -1
  51. package/dist/pages/dashboard/widgets/WorkflowsOverviewWidget.js +59 -49
  52. package/dist/pages/dashboard/widgets/WorkflowsOverviewWidget.js.map +1 -1
  53. package/dist/pages/dashboard/widgets/WorkspacesWidget.js +35 -33
  54. package/dist/pages/dashboard/widgets/WorkspacesWidget.js.map +1 -1
  55. package/package.json +4 -4
@@ -1,108 +1,110 @@
1
- import { WidgetWrapper as e } from "../WidgetWrapper.js";
2
- import { AlertTriangle as t, ArrowRight as n, Bot as r, Wifi as i, WifiOff as a } from "lucide-react";
3
- import { jsx as o, jsxs as s } from "react/jsx-runtime";
4
- import { Button as c, IllustratedEmptyState as l } from "@burdenoff/fe-libs/ui";
1
+ import { tWithFallback as e } from "../../../utils/i18n.js";
2
+ import { WidgetWrapper as t } from "../WidgetWrapper.js";
3
+ import { AlertTriangle as n, ArrowRight as r, Bot as i, Wifi as a, WifiOff as o } from "lucide-react";
4
+ import { jsx as s, jsxs as c } from "react/jsx-runtime";
5
+ import { Button as l, IllustratedEmptyState as u } from "@burdenoff/fe-libs/ui";
6
+ import { useI18n as d } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
5
7
  //#region src/pages/dashboard/widgets/AgentsWidget.tsx
6
- function u({ workspaceData: u, isLoading: d, navigate: f }) {
7
- let p = u?.agentFleet ?? null, m = p?.total ?? 0;
8
- return /* @__PURE__ */ o(e, {
9
- title: "Agent Fleet",
10
- icon: /* @__PURE__ */ o(r, { className: "size-4" }),
8
+ function f({ workspaceData: f, isLoading: p, navigate: m }) {
9
+ let { t: h } = d(), g = f?.agentFleet ?? null, _ = g?.total ?? 0;
10
+ return /* @__PURE__ */ s(t, {
11
+ title: e(h, "dashboard.agents.title", "Agent Fleet"),
12
+ icon: /* @__PURE__ */ s(i, { className: "size-4" }),
11
13
  size: "small",
12
- isLoading: d,
13
- unavailable: !u,
14
- headerAction: /* @__PURE__ */ s(c, {
14
+ isLoading: p,
15
+ unavailable: !f,
16
+ headerAction: /* @__PURE__ */ c(l, {
15
17
  variant: "ghost",
16
18
  size: "sm",
17
19
  className: "text-xs",
18
- onClick: () => f("/vibecontrols/agents"),
19
- children: ["Open ", /* @__PURE__ */ o(n, { className: "ml-1 size-3" })]
20
+ onClick: () => m("/vibecontrols/agents"),
21
+ children: [
22
+ e(h, "dashboard.agents.open", "Open"),
23
+ " ",
24
+ /* @__PURE__ */ s(r, { className: "ml-1 size-3" })
25
+ ]
20
26
  }),
21
- children: !p || m === 0 ? /* @__PURE__ */ o(l, {
27
+ children: !g || _ === 0 ? /* @__PURE__ */ s(u, {
22
28
  illustration: "empty-agents",
23
- title: "No agents connected",
29
+ title: e(h, "dashboard.agents.emptyTitle", "No agents connected"),
24
30
  className: "py-4",
25
- action: /* @__PURE__ */ o(c, {
31
+ action: /* @__PURE__ */ s(l, {
26
32
  variant: "outline",
27
33
  size: "sm",
28
- onClick: () => f("/vibecontrols/agents/new"),
29
- children: "Connect agent"
34
+ onClick: () => m("/vibecontrols/agents/new"),
35
+ children: e(h, "dashboard.agents.connectAgent", "Connect agent")
30
36
  })
31
- }) : /* @__PURE__ */ s("div", {
37
+ }) : /* @__PURE__ */ c("div", {
32
38
  className: "space-y-3",
33
39
  children: [
34
- /* @__PURE__ */ s("p", {
40
+ /* @__PURE__ */ c("p", {
35
41
  className: "text-2xl font-semibold text-text-primary",
36
- children: [m, /* @__PURE__ */ o("span", {
42
+ children: [_, /* @__PURE__ */ s("span", {
37
43
  className: "ml-1 text-xs font-normal text-text-secondary",
38
- children: "total"
44
+ children: e(h, "dashboard.agents.total", "total")
39
45
  })]
40
46
  }),
41
- /* @__PURE__ */ s("div", {
47
+ /* @__PURE__ */ c("div", {
42
48
  className: "grid grid-cols-3 gap-2 text-center",
43
49
  children: [
44
- /* @__PURE__ */ s("div", {
50
+ /* @__PURE__ */ c("div", {
45
51
  className: "rounded-md bg-status-success-bg/30 p-2",
46
52
  children: [
47
- /* @__PURE__ */ o(i, { className: `mx-auto size-4 text-status-success-text ${p.online > 0 ? "motion-safe:animate-vc-glow-pulse" : ""}` }),
48
- /* @__PURE__ */ o("p", {
53
+ /* @__PURE__ */ s(a, { className: `mx-auto size-4 text-status-success-text ${g.online > 0 ? "motion-safe:animate-vc-glow-pulse" : ""}` }),
54
+ /* @__PURE__ */ s("p", {
49
55
  className: "mt-1 text-sm font-semibold text-text-primary",
50
- children: p.online
56
+ children: g.online
51
57
  }),
52
- /* @__PURE__ */ o("p", {
58
+ /* @__PURE__ */ s("p", {
53
59
  className: "text-[10px] text-text-secondary",
54
- children: "online"
60
+ children: e(h, "dashboard.agents.online", "online")
55
61
  })
56
62
  ]
57
63
  }),
58
- /* @__PURE__ */ s("div", {
64
+ /* @__PURE__ */ c("div", {
59
65
  className: "rounded-md bg-status-warning-bg/30 p-2",
60
66
  children: [
61
- /* @__PURE__ */ o(t, { className: "mx-auto size-4 text-status-warning-text" }),
62
- /* @__PURE__ */ o("p", {
67
+ /* @__PURE__ */ s(n, { className: "mx-auto size-4 text-status-warning-text" }),
68
+ /* @__PURE__ */ s("p", {
63
69
  className: "mt-1 text-sm font-semibold text-text-primary",
64
- children: p.degraded
70
+ children: g.degraded
65
71
  }),
66
- /* @__PURE__ */ o("p", {
72
+ /* @__PURE__ */ s("p", {
67
73
  className: "text-[10px] text-text-secondary",
68
- children: "degraded"
74
+ children: e(h, "dashboard.agents.degraded", "degraded")
69
75
  })
70
76
  ]
71
77
  }),
72
- /* @__PURE__ */ s("div", {
78
+ /* @__PURE__ */ c("div", {
73
79
  className: "rounded-md bg-text-tertiary/10 p-2",
74
80
  children: [
75
- /* @__PURE__ */ o(a, { className: "mx-auto size-4 text-text-tertiary" }),
76
- /* @__PURE__ */ o("p", {
81
+ /* @__PURE__ */ s(o, { className: "mx-auto size-4 text-text-tertiary" }),
82
+ /* @__PURE__ */ s("p", {
77
83
  className: "mt-1 text-sm font-semibold text-text-primary",
78
- children: p.offline
84
+ children: g.offline
79
85
  }),
80
- /* @__PURE__ */ o("p", {
86
+ /* @__PURE__ */ s("p", {
81
87
  className: "text-[10px] text-text-secondary",
82
- children: "offline"
88
+ children: e(h, "dashboard.agents.offline", "offline")
83
89
  })
84
90
  ]
85
91
  })
86
92
  ]
87
93
  }),
88
- p.lastSeenAt ? /* @__PURE__ */ s("p", {
94
+ g.lastSeenAt ? /* @__PURE__ */ s("p", {
89
95
  className: "text-[11px] text-text-secondary text-center",
90
- children: [
91
- "Last seen",
92
- " ",
93
- new Date(p.lastSeenAt).toLocaleString(void 0, {
94
- hour: "2-digit",
95
- minute: "2-digit",
96
- month: "short",
97
- day: "numeric"
98
- })
99
- ]
96
+ children: e(h, "dashboard.agents.lastSeen", "Last seen {time}", { time: new Date(g.lastSeenAt).toLocaleString(void 0, {
97
+ hour: "2-digit",
98
+ minute: "2-digit",
99
+ month: "short",
100
+ day: "numeric"
101
+ }) })
100
102
  }) : null
101
103
  ]
102
104
  })
103
105
  });
104
106
  }
105
107
  //#endregion
106
- export { u as AgentsWidget };
108
+ export { f as AgentsWidget };
107
109
 
108
110
  //# sourceMappingURL=AgentsWidget.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AgentsWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/AgentsWidget.tsx"],"sourcesContent":["import { Bot, ArrowRight, Wifi, WifiOff, AlertTriangle } from 'lucide-react';\nimport { Button, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport type { WidgetProps } from '../types';\n\nexport function AgentsWidget({ workspaceData, isLoading, navigate }: WidgetProps) {\n const fleet = workspaceData?.agentFleet ?? null;\n const total = fleet?.total ?? 0;\n\n return (\n <WidgetWrapper\n title=\"Agent Fleet\"\n icon={<Bot className=\"size-4\" />}\n size=\"small\"\n isLoading={isLoading}\n unavailable={!workspaceData}\n headerAction={\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-xs\"\n onClick={() => navigate('/vibecontrols/agents')}\n >\n Open <ArrowRight className=\"ml-1 size-3\" />\n </Button>\n }\n >\n {!fleet || total === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-agents\"\n title=\"No agents connected\"\n className=\"py-4\"\n action={\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => navigate('/vibecontrols/agents/new')}\n >\n Connect agent\n </Button>\n }\n />\n ) : (\n <div className=\"space-y-3\">\n <p className=\"text-2xl font-semibold text-text-primary\">\n {total}\n <span className=\"ml-1 text-xs font-normal text-text-secondary\">total</span>\n </p>\n <div className=\"grid grid-cols-3 gap-2 text-center\">\n <div className=\"rounded-md bg-status-success-bg/30 p-2\">\n <Wifi\n className={`mx-auto size-4 text-status-success-text ${\n fleet.online > 0 ? 'motion-safe:animate-vc-glow-pulse' : ''\n }`}\n />\n <p className=\"mt-1 text-sm font-semibold text-text-primary\">{fleet.online}</p>\n <p className=\"text-[10px] text-text-secondary\">online</p>\n </div>\n <div className=\"rounded-md bg-status-warning-bg/30 p-2\">\n <AlertTriangle className=\"mx-auto size-4 text-status-warning-text\" />\n <p className=\"mt-1 text-sm font-semibold text-text-primary\">{fleet.degraded}</p>\n <p className=\"text-[10px] text-text-secondary\">degraded</p>\n </div>\n <div className=\"rounded-md bg-text-tertiary/10 p-2\">\n <WifiOff className=\"mx-auto size-4 text-text-tertiary\" />\n <p className=\"mt-1 text-sm font-semibold text-text-primary\">{fleet.offline}</p>\n <p className=\"text-[10px] text-text-secondary\">offline</p>\n </div>\n </div>\n {fleet.lastSeenAt ? (\n <p className=\"text-[11px] text-text-secondary text-center\">\n Last seen{' '}\n {new Date(fleet.lastSeenAt).toLocaleString(undefined, {\n hour: '2-digit',\n minute: '2-digit',\n month: 'short',\n day: 'numeric',\n })}\n </p>\n ) : null}\n </div>\n )}\n </WidgetWrapper>\n );\n}\n"],"mappings":";;;;;AAKA,SAAgB,EAAa,EAAE,kBAAe,cAAW,eAAyB;CAChF,IAAM,IAAQ,GAAe,cAAc,MACrC,IAAQ,GAAO,SAAS;AAE9B,QACE,kBAAC,GAAD;EACE,OAAM;EACN,MAAM,kBAAC,GAAD,EAAK,WAAU,UAAW,CAAA;EAChC,MAAK;EACM;EACX,aAAa,CAAC;EACd,cACE,kBAAC,GAAD;GACE,SAAQ;GACR,MAAK;GACL,WAAU;GACV,eAAe,EAAS,uBAAuB;aAJjD,CAKC,SACM,kBAAC,GAAD,EAAY,WAAU,eAAgB,CAAA,CACpC;;YAGV,CAAC,KAAS,MAAU,IACnB,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,WAAU;GACV,QACE,kBAAC,GAAD;IACE,SAAQ;IACR,MAAK;IACL,eAAe,EAAS,2BAA2B;cACpD;IAEQ,CAAA;GAEX,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,KAAD;KAAG,WAAU;eAAb,CACG,GACD,kBAAC,QAAD;MAAM,WAAU;gBAA+C;MAAY,CAAA,CACzE;;IACJ,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EACE,WAAW,2CACT,EAAM,SAAS,IAAI,sCAAsC,MAE3D,CAAA;QACF,kBAAC,KAAD;SAAG,WAAU;mBAAgD,EAAM;SAAW,CAAA;QAC9E,kBAAC,KAAD;SAAG,WAAU;mBAAkC;SAAU,CAAA;QACrD;;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EAAe,WAAU,2CAA4C,CAAA;QACrE,kBAAC,KAAD;SAAG,WAAU;mBAAgD,EAAM;SAAa,CAAA;QAChF,kBAAC,KAAD;SAAG,WAAU;mBAAkC;SAAY,CAAA;QACvD;;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EAAS,WAAU,qCAAsC,CAAA;QACzD,kBAAC,KAAD;SAAG,WAAU;mBAAgD,EAAM;SAAY,CAAA;QAC/E,kBAAC,KAAD;SAAG,WAAU;mBAAkC;SAAW,CAAA;QACtD;;MACF;;IACL,EAAM,aACL,kBAAC,KAAD;KAAG,WAAU;eAAb;MAA2D;MAC/C;MACT,IAAI,KAAK,EAAM,WAAW,CAAC,eAAe,KAAA,GAAW;OACpD,MAAM;OACN,QAAQ;OACR,OAAO;OACP,KAAK;OACN,CAAC;MACA;SACF;IACA;;EAEM,CAAA"}
1
+ {"version":3,"file":"AgentsWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/AgentsWidget.tsx"],"sourcesContent":["import { Bot, ArrowRight, Wifi, WifiOff, AlertTriangle } from 'lucide-react';\nimport { Button, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport { tWithFallback } from '../../../utils/i18n';\nimport type { WidgetProps } from '../types';\n\nexport function AgentsWidget({ workspaceData, isLoading, navigate }: WidgetProps) {\n const { t } = useI18n();\n const fleet = workspaceData?.agentFleet ?? null;\n const total = fleet?.total ?? 0;\n\n return (\n <WidgetWrapper\n title={tWithFallback(t, 'dashboard.agents.title', 'Agent Fleet')}\n icon={<Bot className=\"size-4\" />}\n size=\"small\"\n isLoading={isLoading}\n unavailable={!workspaceData}\n headerAction={\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-xs\"\n onClick={() => navigate('/vibecontrols/agents')}\n >\n {tWithFallback(t, 'dashboard.agents.open', 'Open')} <ArrowRight className=\"ml-1 size-3\" />\n </Button>\n }\n >\n {!fleet || total === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-agents\"\n title={tWithFallback(t, 'dashboard.agents.emptyTitle', 'No agents connected')}\n className=\"py-4\"\n action={\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => navigate('/vibecontrols/agents/new')}\n >\n {tWithFallback(t, 'dashboard.agents.connectAgent', 'Connect agent')}\n </Button>\n }\n />\n ) : (\n <div className=\"space-y-3\">\n <p className=\"text-2xl font-semibold text-text-primary\">\n {total}\n <span className=\"ml-1 text-xs font-normal text-text-secondary\">\n {tWithFallback(t, 'dashboard.agents.total', 'total')}\n </span>\n </p>\n <div className=\"grid grid-cols-3 gap-2 text-center\">\n <div className=\"rounded-md bg-status-success-bg/30 p-2\">\n <Wifi\n className={`mx-auto size-4 text-status-success-text ${\n fleet.online > 0 ? 'motion-safe:animate-vc-glow-pulse' : ''\n }`}\n />\n <p className=\"mt-1 text-sm font-semibold text-text-primary\">{fleet.online}</p>\n <p className=\"text-[10px] text-text-secondary\">\n {tWithFallback(t, 'dashboard.agents.online', 'online')}\n </p>\n </div>\n <div className=\"rounded-md bg-status-warning-bg/30 p-2\">\n <AlertTriangle className=\"mx-auto size-4 text-status-warning-text\" />\n <p className=\"mt-1 text-sm font-semibold text-text-primary\">{fleet.degraded}</p>\n <p className=\"text-[10px] text-text-secondary\">\n {tWithFallback(t, 'dashboard.agents.degraded', 'degraded')}\n </p>\n </div>\n <div className=\"rounded-md bg-text-tertiary/10 p-2\">\n <WifiOff className=\"mx-auto size-4 text-text-tertiary\" />\n <p className=\"mt-1 text-sm font-semibold text-text-primary\">{fleet.offline}</p>\n <p className=\"text-[10px] text-text-secondary\">\n {tWithFallback(t, 'dashboard.agents.offline', 'offline')}\n </p>\n </div>\n </div>\n {fleet.lastSeenAt ? (\n <p className=\"text-[11px] text-text-secondary text-center\">\n {tWithFallback(t, 'dashboard.agents.lastSeen', 'Last seen {time}', {\n time: new Date(fleet.lastSeenAt).toLocaleString(undefined, {\n hour: '2-digit',\n minute: '2-digit',\n month: 'short',\n day: 'numeric',\n }),\n })}\n </p>\n ) : null}\n </div>\n )}\n </WidgetWrapper>\n );\n}\n"],"mappings":";;;;;;;AAOA,SAAgB,EAAa,EAAE,kBAAe,cAAW,eAAyB;CAChF,IAAM,EAAE,SAAM,GAAS,EACjB,IAAQ,GAAe,cAAc,MACrC,IAAQ,GAAO,SAAS;AAE9B,QACE,kBAAC,GAAD;EACE,OAAO,EAAc,GAAG,0BAA0B,cAAc;EAChE,MAAM,kBAAC,GAAD,EAAK,WAAU,UAAW,CAAA;EAChC,MAAK;EACM;EACX,aAAa,CAAC;EACd,cACE,kBAAC,GAAD;GACE,SAAQ;GACR,MAAK;GACL,WAAU;GACV,eAAe,EAAS,uBAAuB;aAJjD;IAMG,EAAc,GAAG,yBAAyB,OAAO;IAAC;IAAC,kBAAC,GAAD,EAAY,WAAU,eAAgB,CAAA;IACnF;;YAGV,CAAC,KAAS,MAAU,IACnB,kBAAC,GAAD;GACE,cAAa;GACb,OAAO,EAAc,GAAG,+BAA+B,sBAAsB;GAC7E,WAAU;GACV,QACE,kBAAC,GAAD;IACE,SAAQ;IACR,MAAK;IACL,eAAe,EAAS,2BAA2B;cAElD,EAAc,GAAG,iCAAiC,gBAAgB;IAC5D,CAAA;GAEX,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,KAAD;KAAG,WAAU;eAAb,CACG,GACD,kBAAC,QAAD;MAAM,WAAU;gBACb,EAAc,GAAG,0BAA0B,QAAQ;MAC/C,CAAA,CACL;;IACJ,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EACE,WAAW,2CACT,EAAM,SAAS,IAAI,sCAAsC,MAE3D,CAAA;QACF,kBAAC,KAAD;SAAG,WAAU;mBAAgD,EAAM;SAAW,CAAA;QAC9E,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAc,GAAG,2BAA2B,SAAS;SACpD,CAAA;QACA;;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EAAe,WAAU,2CAA4C,CAAA;QACrE,kBAAC,KAAD;SAAG,WAAU;mBAAgD,EAAM;SAAa,CAAA;QAChF,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAc,GAAG,6BAA6B,WAAW;SACxD,CAAA;QACA;;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EAAS,WAAU,qCAAsC,CAAA;QACzD,kBAAC,KAAD;SAAG,WAAU;mBAAgD,EAAM;SAAY,CAAA;QAC/E,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAc,GAAG,4BAA4B,UAAU;SACtD,CAAA;QACA;;MACF;;IACL,EAAM,aACL,kBAAC,KAAD;KAAG,WAAU;eACV,EAAc,GAAG,6BAA6B,oBAAoB,EACjE,MAAM,IAAI,KAAK,EAAM,WAAW,CAAC,eAAe,KAAA,GAAW;MACzD,MAAM;MACN,QAAQ;MACR,OAAO;MACP,KAAK;MACN,CAAC,EACH,CAAC;KACA,CAAA,GACF;IACA;;EAEM,CAAA"}
@@ -1,59 +1,57 @@
1
- import { WidgetWrapper as e } from "../WidgetWrapper.js";
2
- import { ArrowRight as t, Users as n } from "lucide-react";
3
- import { jsx as r, jsxs as i } from "react/jsx-runtime";
4
- import { Button as a, IllustratedEmptyState as o } from "@burdenoff/fe-libs/ui";
1
+ import { tWithFallback as e } from "../../../utils/i18n.js";
2
+ import { WidgetWrapper as t } from "../WidgetWrapper.js";
3
+ import { ArrowRight as n, Users as r } from "lucide-react";
4
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
5
+ import { Button as o, IllustratedEmptyState as s } from "@burdenoff/fe-libs/ui";
6
+ import { useI18n as c } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
5
7
  //#region src/pages/dashboard/widgets/GroupsWidget.tsx
6
- function s({ workspaceData: s, isLoading: c, navigate: l }) {
7
- let u = s?.groups.items ?? [], d = s?.groups.totalCount ?? 0;
8
- return /* @__PURE__ */ r(e, {
9
- title: "Groups",
10
- icon: /* @__PURE__ */ r(n, { className: "size-4" }),
8
+ function l({ workspaceData: l, isLoading: u, navigate: d }) {
9
+ let { t: f } = c(), p = l?.groups.items ?? [], m = l?.groups.totalCount ?? 0;
10
+ return /* @__PURE__ */ i(t, {
11
+ title: e(f, "dashboard.groups.title", "Groups"),
12
+ icon: /* @__PURE__ */ i(r, { className: "size-4" }),
11
13
  size: "small",
12
- isLoading: c,
13
- unavailable: !s,
14
- headerAction: /* @__PURE__ */ r(a, {
14
+ isLoading: u,
15
+ unavailable: !l,
16
+ headerAction: /* @__PURE__ */ i(o, {
15
17
  variant: "ghost",
16
18
  size: "sm",
17
19
  className: "text-xs",
18
- onClick: () => l("/groups"),
19
- children: /* @__PURE__ */ r(t, { className: "size-3" })
20
+ onClick: () => d("/groups"),
21
+ children: /* @__PURE__ */ i(n, { className: "size-3" })
20
22
  }),
21
- children: u.length === 0 ? /* @__PURE__ */ r(o, {
23
+ children: p.length === 0 ? /* @__PURE__ */ i(s, {
22
24
  illustration: "empty-people",
23
- title: "No groups yet",
24
- description: "Groups bundle members together so you can manage and mention them as one.",
25
+ title: e(f, "dashboard.groups.emptyTitle", "No groups yet"),
26
+ description: e(f, "dashboard.groups.emptyDescription", "Groups bundle members together so you can manage and mention them as one."),
25
27
  className: "py-4"
26
- }) : /* @__PURE__ */ i("div", {
28
+ }) : /* @__PURE__ */ a("div", {
27
29
  className: "space-y-1.5",
28
- children: [u.slice(0, 4).map((e) => /* @__PURE__ */ i("button", {
30
+ children: [p.slice(0, 4).map((e) => /* @__PURE__ */ a("button", {
29
31
  type: "button",
30
32
  className: "w-full flex items-center gap-2 p-1.5 rounded hover:bg-accent/5 cursor-pointer transition-colors text-left",
31
- onClick: () => l("/groups"),
32
- children: [/* @__PURE__ */ r("div", {
33
+ onClick: () => d("/groups"),
34
+ children: [/* @__PURE__ */ i("div", {
33
35
  className: "size-7 rounded-full bg-accent-purple-subtle text-accent-purple flex items-center justify-center text-xs font-bold shrink-0",
34
36
  children: e.name.charAt(0).toUpperCase()
35
- }), /* @__PURE__ */ i("div", {
37
+ }), /* @__PURE__ */ a("div", {
36
38
  className: "min-w-0",
37
- children: [/* @__PURE__ */ r("p", {
39
+ children: [/* @__PURE__ */ i("p", {
38
40
  className: "text-sm font-medium text-text-primary truncate",
39
41
  children: e.name
40
- }), e.description && /* @__PURE__ */ r("p", {
42
+ }), e.description && /* @__PURE__ */ i("p", {
41
43
  className: "text-[11px] text-text-secondary truncate",
42
44
  children: e.description
43
45
  })]
44
46
  })]
45
- }, e.id)), d > 4 && /* @__PURE__ */ i("p", {
47
+ }, e.id)), m > 4 && /* @__PURE__ */ i("p", {
46
48
  className: "text-[11px] text-text-secondary text-center",
47
- children: [
48
- "+",
49
- d - 4,
50
- " more groups"
51
- ]
49
+ children: e(f, "dashboard.groups.more", "+{count} more groups", { count: m - 4 })
52
50
  })]
53
51
  })
54
52
  });
55
53
  }
56
54
  //#endregion
57
- export { s as GroupsWidget };
55
+ export { l as GroupsWidget };
58
56
 
59
57
  //# sourceMappingURL=GroupsWidget.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"GroupsWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/GroupsWidget.tsx"],"sourcesContent":["import { Users, ArrowRight } from 'lucide-react';\nimport { Button, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport type { WidgetProps } from '../types';\n\nexport function GroupsWidget({ workspaceData, isLoading, navigate }: WidgetProps) {\n const groups = workspaceData?.groups.items ?? [];\n const total = workspaceData?.groups.totalCount ?? 0;\n\n return (\n <WidgetWrapper\n title=\"Groups\"\n icon={<Users className=\"size-4\" />}\n size=\"small\"\n isLoading={isLoading}\n unavailable={!workspaceData}\n headerAction={\n <Button variant=\"ghost\" size=\"sm\" className=\"text-xs\" onClick={() => navigate('/groups')}>\n <ArrowRight className=\"size-3\" />\n </Button>\n }\n >\n {groups.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title=\"No groups yet\"\n description=\"Groups bundle members together so you can manage and mention them as one.\"\n className=\"py-4\"\n />\n ) : (\n <div className=\"space-y-1.5\">\n {groups.slice(0, 4).map((group) => (\n <button\n key={group.id}\n type=\"button\"\n className=\"w-full flex items-center gap-2 p-1.5 rounded hover:bg-accent/5 cursor-pointer transition-colors text-left\"\n onClick={() => navigate('/groups')}\n >\n <div className=\"size-7 rounded-full bg-accent-purple-subtle text-accent-purple flex items-center justify-center text-xs font-bold shrink-0\">\n {group.name.charAt(0).toUpperCase()}\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">{group.name}</p>\n {group.description && (\n <p className=\"text-[11px] text-text-secondary truncate\">{group.description}</p>\n )}\n </div>\n </button>\n ))}\n {total > 4 && (\n <p className=\"text-[11px] text-text-secondary text-center\">+{total - 4} more groups</p>\n )}\n </div>\n )}\n </WidgetWrapper>\n );\n}\n"],"mappings":";;;;;AAKA,SAAgB,EAAa,EAAE,kBAAe,cAAW,eAAyB;CAChF,IAAM,IAAS,GAAe,OAAO,SAAS,EAAE,EAC1C,IAAQ,GAAe,OAAO,cAAc;AAElD,QACE,kBAAC,GAAD;EACE,OAAM;EACN,MAAM,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;EAClC,MAAK;EACM;EACX,aAAa,CAAC;EACd,cACE,kBAAC,GAAD;GAAQ,SAAQ;GAAQ,MAAK;GAAK,WAAU;GAAU,eAAe,EAAS,UAAU;aACtF,kBAAC,GAAD,EAAY,WAAU,UAAW,CAAA;GAC1B,CAAA;YAGV,EAAO,WAAW,IACjB,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,aAAY;GACZ,WAAU;GACV,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,EAAO,MAAM,GAAG,EAAE,CAAC,KAAK,MACvB,kBAAC,UAAD;IAEE,MAAK;IACL,WAAU;IACV,eAAe,EAAS,UAAU;cAJpC,CAME,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAM,KAAK,OAAO,EAAE,CAAC,aAAa;KAC/B,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAkD,EAAM;MAAS,CAAA,EAC7E,EAAM,eACL,kBAAC,KAAD;MAAG,WAAU;gBAA4C,EAAM;MAAgB,CAAA,CAE7E;OACC;MAdF,EAAM,GAcJ,CACT,EACD,IAAQ,KACP,kBAAC,KAAD;IAAG,WAAU;cAAb;KAA2D;KAAE,IAAQ;KAAE;KAAgB;MAErF;;EAEM,CAAA"}
1
+ {"version":3,"file":"GroupsWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/GroupsWidget.tsx"],"sourcesContent":["import { Users, ArrowRight } from 'lucide-react';\nimport { Button, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport { tWithFallback } from '../../../utils/i18n';\nimport type { WidgetProps } from '../types';\n\nexport function GroupsWidget({ workspaceData, isLoading, navigate }: WidgetProps) {\n const { t } = useI18n();\n const groups = workspaceData?.groups.items ?? [];\n const total = workspaceData?.groups.totalCount ?? 0;\n\n return (\n <WidgetWrapper\n title={tWithFallback(t, 'dashboard.groups.title', 'Groups')}\n icon={<Users className=\"size-4\" />}\n size=\"small\"\n isLoading={isLoading}\n unavailable={!workspaceData}\n headerAction={\n <Button variant=\"ghost\" size=\"sm\" className=\"text-xs\" onClick={() => navigate('/groups')}>\n <ArrowRight className=\"size-3\" />\n </Button>\n }\n >\n {groups.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title={tWithFallback(t, 'dashboard.groups.emptyTitle', 'No groups yet')}\n description={tWithFallback(\n t,\n 'dashboard.groups.emptyDescription',\n 'Groups bundle members together so you can manage and mention them as one.'\n )}\n className=\"py-4\"\n />\n ) : (\n <div className=\"space-y-1.5\">\n {groups.slice(0, 4).map((group) => (\n <button\n key={group.id}\n type=\"button\"\n className=\"w-full flex items-center gap-2 p-1.5 rounded hover:bg-accent/5 cursor-pointer transition-colors text-left\"\n onClick={() => navigate('/groups')}\n >\n <div className=\"size-7 rounded-full bg-accent-purple-subtle text-accent-purple flex items-center justify-center text-xs font-bold shrink-0\">\n {group.name.charAt(0).toUpperCase()}\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">{group.name}</p>\n {group.description && (\n <p className=\"text-[11px] text-text-secondary truncate\">{group.description}</p>\n )}\n </div>\n </button>\n ))}\n {total > 4 && (\n <p className=\"text-[11px] text-text-secondary text-center\">\n {tWithFallback(t, 'dashboard.groups.more', '+{count} more groups', {\n count: total - 4,\n })}\n </p>\n )}\n </div>\n )}\n </WidgetWrapper>\n );\n}\n"],"mappings":";;;;;;;AAOA,SAAgB,EAAa,EAAE,kBAAe,cAAW,eAAyB;CAChF,IAAM,EAAE,SAAM,GAAS,EACjB,IAAS,GAAe,OAAO,SAAS,EAAE,EAC1C,IAAQ,GAAe,OAAO,cAAc;AAElD,QACE,kBAAC,GAAD;EACE,OAAO,EAAc,GAAG,0BAA0B,SAAS;EAC3D,MAAM,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;EAClC,MAAK;EACM;EACX,aAAa,CAAC;EACd,cACE,kBAAC,GAAD;GAAQ,SAAQ;GAAQ,MAAK;GAAK,WAAU;GAAU,eAAe,EAAS,UAAU;aACtF,kBAAC,GAAD,EAAY,WAAU,UAAW,CAAA;GAC1B,CAAA;YAGV,EAAO,WAAW,IACjB,kBAAC,GAAD;GACE,cAAa;GACb,OAAO,EAAc,GAAG,+BAA+B,gBAAgB;GACvE,aAAa,EACX,GACA,qCACA,4EACD;GACD,WAAU;GACV,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,EAAO,MAAM,GAAG,EAAE,CAAC,KAAK,MACvB,kBAAC,UAAD;IAEE,MAAK;IACL,WAAU;IACV,eAAe,EAAS,UAAU;cAJpC,CAME,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAM,KAAK,OAAO,EAAE,CAAC,aAAa;KAC/B,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAkD,EAAM;MAAS,CAAA,EAC7E,EAAM,eACL,kBAAC,KAAD;MAAG,WAAU;gBAA4C,EAAM;MAAgB,CAAA,CAE7E;OACC;MAdF,EAAM,GAcJ,CACT,EACD,IAAQ,KACP,kBAAC,KAAD;IAAG,WAAU;cACV,EAAc,GAAG,yBAAyB,wBAAwB,EACjE,OAAO,IAAQ,GAChB,CAAC;IACA,CAAA,CAEF;;EAEM,CAAA"}
@@ -1,58 +1,58 @@
1
- import { WidgetWrapper as e } from "../WidgetWrapper.js";
2
- import { ArrowRight as t, UserPlus as n, Users as r } from "lucide-react";
3
- import { jsx as i, jsxs as a } from "react/jsx-runtime";
4
- import { Button as o } from "@burdenoff/fe-libs/ui";
1
+ import { tWithFallback as e } from "../../../utils/i18n.js";
2
+ import { WidgetWrapper as t } from "../WidgetWrapper.js";
3
+ import { ArrowRight as n, UserPlus as r, Users as i } from "lucide-react";
4
+ import { jsx as a, jsxs as o } from "react/jsx-runtime";
5
+ import { Button as s } from "@burdenoff/fe-libs/ui";
6
+ import { useI18n as c } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
5
7
  //#region src/pages/dashboard/widgets/MembersWidget.tsx
6
- function s({ workspaceData: s, globalData: c, isLoading: l, navigate: u }) {
7
- let d = c?.workspaces?.items ?? s?.workspaces.items ?? [], f = d.reduce((e, t) => e + (t.memberCount ?? 0), 0), p = c?.workspaces?.total ?? s?.workspaces.total ?? 0;
8
- return /* @__PURE__ */ i(e, {
9
- title: "Members",
10
- icon: /* @__PURE__ */ i(r, { className: "size-4" }),
8
+ function l({ workspaceData: l, globalData: u, isLoading: d, navigate: f }) {
9
+ let { t: p } = c(), m = u?.workspaces?.items ?? l?.workspaces.items ?? [], h = m.reduce((e, t) => e + (t.memberCount ?? 0), 0), g = u?.workspaces?.total ?? l?.workspaces.total ?? 0;
10
+ return /* @__PURE__ */ a(t, {
11
+ title: e(p, "dashboard.members.title", "Members"),
12
+ icon: /* @__PURE__ */ a(i, { className: "size-4" }),
11
13
  size: "small",
12
- isLoading: l,
13
- unavailable: !s && !c,
14
- headerAction: /* @__PURE__ */ i(o, {
14
+ isLoading: d,
15
+ unavailable: !l && !u,
16
+ headerAction: /* @__PURE__ */ a(s, {
15
17
  variant: "ghost",
16
18
  size: "sm",
17
19
  className: "text-xs",
18
- onClick: () => u("/workspaces/members"),
19
- children: /* @__PURE__ */ i(t, { className: "size-3" })
20
+ onClick: () => f("/workspaces/members"),
21
+ children: /* @__PURE__ */ a(n, { className: "size-3" })
20
22
  }),
21
- children: /* @__PURE__ */ a("div", {
23
+ children: /* @__PURE__ */ o("div", {
22
24
  className: "space-y-3",
23
25
  children: [
24
- /* @__PURE__ */ a("div", {
26
+ /* @__PURE__ */ o("div", {
25
27
  className: "text-center",
26
- children: [/* @__PURE__ */ i("p", {
28
+ children: [/* @__PURE__ */ a("p", {
27
29
  className: "text-3xl font-bold text-text-primary tabular-nums",
28
- children: f
30
+ children: h
29
31
  }), /* @__PURE__ */ a("p", {
30
32
  className: "text-xs text-text-secondary",
31
- children: [
32
- f === 1 ? "Member" : "Members",
33
- " across ",
34
- p,
35
- " ",
36
- p === 1 ? "workspace" : "workspaces"
37
- ]
33
+ children: e(p, "dashboard.members.summary", "{memberLabel} across {count} {workspaceLabel}", {
34
+ memberLabel: h === 1 ? e(p, "dashboard.members.memberSingular", "Member") : e(p, "dashboard.members.memberPlural", "Members"),
35
+ count: g,
36
+ workspaceLabel: g === 1 ? e(p, "dashboard.members.workspaceSingular", "workspace") : e(p, "dashboard.members.workspacePlural", "workspaces")
37
+ })
38
38
  })]
39
39
  }),
40
- /* @__PURE__ */ a(o, {
40
+ /* @__PURE__ */ o(s, {
41
41
  variant: "outline",
42
42
  size: "sm",
43
43
  className: "w-full text-xs",
44
- onClick: () => u("/workspaces/members?invite=true"),
45
- children: [/* @__PURE__ */ i(n, { className: "size-3 mr-1.5" }), "Invite a member"]
44
+ onClick: () => f("/workspaces/members?invite=true"),
45
+ children: [/* @__PURE__ */ a(r, { className: "size-3 mr-1.5" }), e(p, "dashboard.members.invite", "Invite a member")]
46
46
  }),
47
- d.length > 0 && /* @__PURE__ */ a("div", {
47
+ m.length > 0 && /* @__PURE__ */ o("div", {
48
48
  className: "flex -space-x-2 justify-center",
49
- children: [d.slice(0, 5).map((e) => /* @__PURE__ */ i("div", {
49
+ children: [m.slice(0, 5).map((e) => /* @__PURE__ */ a("div", {
50
50
  className: "size-8 rounded-full bg-action-primary-bg/10 text-primary flex items-center justify-center text-xs font-bold border-2 border-bg-surface",
51
51
  title: e.name,
52
52
  children: e.name.charAt(0).toUpperCase()
53
- }, e.id)), p > 5 && /* @__PURE__ */ a("div", {
53
+ }, e.id)), g > 5 && /* @__PURE__ */ o("div", {
54
54
  className: "size-8 rounded-full bg-bg-sunken text-text-secondary flex items-center justify-center text-xs font-bold border-2 border-bg-surface",
55
- children: ["+", p - 5]
55
+ children: ["+", g - 5]
56
56
  })]
57
57
  })
58
58
  ]
@@ -60,6 +60,6 @@ function s({ workspaceData: s, globalData: c, isLoading: l, navigate: u }) {
60
60
  });
61
61
  }
62
62
  //#endregion
63
- export { s as MembersWidget };
63
+ export { l as MembersWidget };
64
64
 
65
65
  //# sourceMappingURL=MembersWidget.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MembersWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/MembersWidget.tsx"],"sourcesContent":["import { Users, UserPlus, ArrowRight } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport type { WidgetProps } from '../types';\n\nexport function MembersWidget({ workspaceData, globalData, isLoading, navigate }: WidgetProps) {\n // Member count comes from globalData.workspaces (each workspace has memberCount)\n const workspaces = globalData?.workspaces?.items ?? workspaceData?.workspaces.items ?? [];\n const totalMembers = workspaces.reduce(\n (sum, ws) => sum + ((ws as { memberCount?: number }).memberCount ?? 0),\n 0\n );\n const totalWorkspaces = globalData?.workspaces?.total ?? workspaceData?.workspaces.total ?? 0;\n\n return (\n <WidgetWrapper\n title=\"Members\"\n icon={<Users className=\"size-4\" />}\n size=\"small\"\n isLoading={isLoading}\n unavailable={!workspaceData && !globalData}\n headerAction={\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-xs\"\n onClick={() => navigate('/workspaces/members')}\n >\n <ArrowRight className=\"size-3\" />\n </Button>\n }\n >\n <div className=\"space-y-3\">\n <div className=\"text-center\">\n <p className=\"text-3xl font-bold text-text-primary tabular-nums\">{totalMembers}</p>\n <p className=\"text-xs text-text-secondary\">\n {totalMembers === 1 ? 'Member' : 'Members'} across {totalWorkspaces}{' '}\n {totalWorkspaces === 1 ? 'workspace' : 'workspaces'}\n </p>\n </div>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"w-full text-xs\"\n onClick={() => navigate('/workspaces/members?invite=true')}\n >\n <UserPlus className=\"size-3 mr-1.5\" />\n Invite a member\n </Button>\n {workspaces.length > 0 && (\n <div className=\"flex -space-x-2 justify-center\">\n {workspaces.slice(0, 5).map((ws) => (\n <div\n key={ws.id}\n className=\"size-8 rounded-full bg-action-primary-bg/10 text-primary flex items-center justify-center text-xs font-bold border-2 border-bg-surface\"\n title={ws.name}\n >\n {ws.name.charAt(0).toUpperCase()}\n </div>\n ))}\n {totalWorkspaces > 5 && (\n <div className=\"size-8 rounded-full bg-bg-sunken text-text-secondary flex items-center justify-center text-xs font-bold border-2 border-bg-surface\">\n +{totalWorkspaces - 5}\n </div>\n )}\n </div>\n )}\n </div>\n </WidgetWrapper>\n );\n}\n"],"mappings":";;;;;AAKA,SAAgB,EAAc,EAAE,kBAAe,eAAY,cAAW,eAAyB;CAE7F,IAAM,IAAa,GAAY,YAAY,SAAS,GAAe,WAAW,SAAS,EAAE,EACnF,IAAe,EAAW,QAC7B,GAAK,MAAO,KAAQ,EAAgC,eAAe,IACpE,EACD,EACK,IAAkB,GAAY,YAAY,SAAS,GAAe,WAAW,SAAS;AAE5F,QACE,kBAAC,GAAD;EACE,OAAM;EACN,MAAM,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;EAClC,MAAK;EACM;EACX,aAAa,CAAC,KAAiB,CAAC;EAChC,cACE,kBAAC,GAAD;GACE,SAAQ;GACR,MAAK;GACL,WAAU;GACV,eAAe,EAAS,sBAAsB;aAE9C,kBAAC,GAAD,EAAY,WAAU,UAAW,CAAA;GAC1B,CAAA;YAGX,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAqD;MAAiB,CAAA,EACnF,kBAAC,KAAD;MAAG,WAAU;gBAAb;OACG,MAAiB,IAAI,WAAW;OAAU;OAAS;OAAiB;OACpE,MAAoB,IAAI,cAAc;OACrC;QACA;;IACN,kBAAC,GAAD;KACE,SAAQ;KACR,MAAK;KACL,WAAU;KACV,eAAe,EAAS,kCAAkC;eAJ5D,CAME,kBAAC,GAAD,EAAU,WAAU,iBAAkB,CAAA,EAAA,kBAE/B;;IACR,EAAW,SAAS,KACnB,kBAAC,OAAD;KAAK,WAAU;eAAf,CACG,EAAW,MAAM,GAAG,EAAE,CAAC,KAAK,MAC3B,kBAAC,OAAD;MAEE,WAAU;MACV,OAAO,EAAG;gBAET,EAAG,KAAK,OAAO,EAAE,CAAC,aAAa;MAC5B,EALC,EAAG,GAKJ,CACN,EACD,IAAkB,KACjB,kBAAC,OAAD;MAAK,WAAU;gBAAf,CAAoJ,KAChJ,IAAkB,EAChB;QAEJ;;IAEJ;;EACQ,CAAA"}
1
+ {"version":3,"file":"MembersWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/MembersWidget.tsx"],"sourcesContent":["import { Users, UserPlus, ArrowRight } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport { tWithFallback } from '../../../utils/i18n';\nimport type { WidgetProps } from '../types';\n\nexport function MembersWidget({ workspaceData, globalData, isLoading, navigate }: WidgetProps) {\n const { t } = useI18n();\n\n // Member count comes from globalData.workspaces (each workspace has memberCount)\n const workspaces = globalData?.workspaces?.items ?? workspaceData?.workspaces.items ?? [];\n const totalMembers = workspaces.reduce(\n (sum, ws) => sum + ((ws as { memberCount?: number }).memberCount ?? 0),\n 0\n );\n const totalWorkspaces = globalData?.workspaces?.total ?? workspaceData?.workspaces.total ?? 0;\n\n return (\n <WidgetWrapper\n title={tWithFallback(t, 'dashboard.members.title', 'Members')}\n icon={<Users className=\"size-4\" />}\n size=\"small\"\n isLoading={isLoading}\n unavailable={!workspaceData && !globalData}\n headerAction={\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-xs\"\n onClick={() => navigate('/workspaces/members')}\n >\n <ArrowRight className=\"size-3\" />\n </Button>\n }\n >\n <div className=\"space-y-3\">\n <div className=\"text-center\">\n <p className=\"text-3xl font-bold text-text-primary tabular-nums\">{totalMembers}</p>\n <p className=\"text-xs text-text-secondary\">\n {tWithFallback(\n t,\n 'dashboard.members.summary',\n '{memberLabel} across {count} {workspaceLabel}',\n {\n memberLabel:\n totalMembers === 1\n ? tWithFallback(t, 'dashboard.members.memberSingular', 'Member')\n : tWithFallback(t, 'dashboard.members.memberPlural', 'Members'),\n count: totalWorkspaces,\n workspaceLabel:\n totalWorkspaces === 1\n ? tWithFallback(t, 'dashboard.members.workspaceSingular', 'workspace')\n : tWithFallback(t, 'dashboard.members.workspacePlural', 'workspaces'),\n }\n )}\n </p>\n </div>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"w-full text-xs\"\n onClick={() => navigate('/workspaces/members?invite=true')}\n >\n <UserPlus className=\"size-3 mr-1.5\" />\n {tWithFallback(t, 'dashboard.members.invite', 'Invite a member')}\n </Button>\n {workspaces.length > 0 && (\n <div className=\"flex -space-x-2 justify-center\">\n {workspaces.slice(0, 5).map((ws) => (\n <div\n key={ws.id}\n className=\"size-8 rounded-full bg-action-primary-bg/10 text-primary flex items-center justify-center text-xs font-bold border-2 border-bg-surface\"\n title={ws.name}\n >\n {ws.name.charAt(0).toUpperCase()}\n </div>\n ))}\n {totalWorkspaces > 5 && (\n <div className=\"size-8 rounded-full bg-bg-sunken text-text-secondary flex items-center justify-center text-xs font-bold border-2 border-bg-surface\">\n +{totalWorkspaces - 5}\n </div>\n )}\n </div>\n )}\n </div>\n </WidgetWrapper>\n );\n}\n"],"mappings":";;;;;;;AAOA,SAAgB,EAAc,EAAE,kBAAe,eAAY,cAAW,eAAyB;CAC7F,IAAM,EAAE,SAAM,GAAS,EAGjB,IAAa,GAAY,YAAY,SAAS,GAAe,WAAW,SAAS,EAAE,EACnF,IAAe,EAAW,QAC7B,GAAK,MAAO,KAAQ,EAAgC,eAAe,IACpE,EACD,EACK,IAAkB,GAAY,YAAY,SAAS,GAAe,WAAW,SAAS;AAE5F,QACE,kBAAC,GAAD;EACE,OAAO,EAAc,GAAG,2BAA2B,UAAU;EAC7D,MAAM,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;EAClC,MAAK;EACM;EACX,aAAa,CAAC,KAAiB,CAAC;EAChC,cACE,kBAAC,GAAD;GACE,SAAQ;GACR,MAAK;GACL,WAAU;GACV,eAAe,EAAS,sBAAsB;aAE9C,kBAAC,GAAD,EAAY,WAAU,UAAW,CAAA;GAC1B,CAAA;YAGX,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAqD;MAAiB,CAAA,EACnF,kBAAC,KAAD;MAAG,WAAU;gBACV,EACC,GACA,6BACA,iDACA;OACE,aACE,MAAiB,IACb,EAAc,GAAG,oCAAoC,SAAS,GAC9D,EAAc,GAAG,kCAAkC,UAAU;OACnE,OAAO;OACP,gBACE,MAAoB,IAChB,EAAc,GAAG,uCAAuC,YAAY,GACpE,EAAc,GAAG,qCAAqC,aAAa;OAC1E,CACF;MACC,CAAA,CACA;;IACN,kBAAC,GAAD;KACE,SAAQ;KACR,MAAK;KACL,WAAU;KACV,eAAe,EAAS,kCAAkC;eAJ5D,CAME,kBAAC,GAAD,EAAU,WAAU,iBAAkB,CAAA,EACrC,EAAc,GAAG,4BAA4B,kBAAkB,CACzD;;IACR,EAAW,SAAS,KACnB,kBAAC,OAAD;KAAK,WAAU;eAAf,CACG,EAAW,MAAM,GAAG,EAAE,CAAC,KAAK,MAC3B,kBAAC,OAAD;MAEE,WAAU;MACV,OAAO,EAAG;gBAET,EAAG,KAAK,OAAO,EAAE,CAAC,aAAa;MAC5B,EALC,EAAG,GAKJ,CACN,EACD,IAAkB,KACjB,kBAAC,OAAD;MAAK,WAAU;gBAAf,CAAoJ,KAChJ,IAAkB,EAChB;QAEJ;;IAEJ;;EACQ,CAAA"}
@@ -1,82 +1,80 @@
1
- import { WidgetWrapper as e } from "../WidgetWrapper.js";
2
- import { ArrowRight as t, Circle as n, FolderKanban as r } from "lucide-react";
3
- import { jsx as i, jsxs as a } from "react/jsx-runtime";
4
- import { Badge as o, Button as s, IllustratedEmptyState as c } from "@burdenoff/fe-libs/ui";
1
+ import { tWithFallback as e } from "../../../utils/i18n.js";
2
+ import { WidgetWrapper as t } from "../WidgetWrapper.js";
3
+ import { ArrowRight as n, Circle as r, FolderKanban as i } from "lucide-react";
4
+ import { jsx as a, jsxs as o } from "react/jsx-runtime";
5
+ import { Badge as s, Button as c, IllustratedEmptyState as l } from "@burdenoff/fe-libs/ui";
6
+ import { useI18n as u } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
5
7
  //#region src/pages/dashboard/widgets/ProjectsWidget.tsx
6
- var l = {
8
+ var d = {
7
9
  ACTIVE: "bg-status-success-bg",
8
10
  INACTIVE: "bg-text-tertiary",
9
11
  ARCHIVED: "bg-status-warning-bg"
10
- }, u = {
12
+ }, f = {
11
13
  ACTIVE: "default",
12
14
  INACTIVE: "secondary",
13
15
  ARCHIVED: "outline"
14
16
  };
15
- function d({ workspaceData: d, isLoading: f, navigate: p }) {
16
- let m = d?.projects.items ?? [], h = d?.projects.total ?? 0, g = m.filter((e) => e.status === "ACTIVE").length, _ = m.filter((e) => e.status === "INACTIVE").length;
17
- return /* @__PURE__ */ i(e, {
18
- title: "Projects",
19
- icon: /* @__PURE__ */ i(r, { className: "size-4" }),
17
+ function p({ workspaceData: p, isLoading: m, navigate: h }) {
18
+ let { t: g } = u(), _ = p?.projects.items ?? [], v = p?.projects.total ?? 0, y = _.filter((e) => e.status === "ACTIVE").length, b = _.filter((e) => e.status === "INACTIVE").length;
19
+ return /* @__PURE__ */ a(t, {
20
+ title: e(g, "dashboard.projects.title", "Projects"),
21
+ icon: /* @__PURE__ */ a(i, { className: "size-4" }),
20
22
  size: "medium",
21
- isLoading: f,
22
- unavailable: !d,
23
- headerAction: /* @__PURE__ */ a(s, {
23
+ isLoading: m,
24
+ unavailable: !p,
25
+ headerAction: /* @__PURE__ */ o(c, {
24
26
  variant: "ghost",
25
27
  size: "sm",
26
28
  className: "text-xs",
27
- onClick: () => p("/workspaces/projects"),
28
- children: ["View all ", /* @__PURE__ */ i(t, { className: "ml-1 size-3" })]
29
+ onClick: () => h("/workspaces/projects"),
30
+ children: [
31
+ e(g, "dashboard.projects.viewAll", "View all"),
32
+ " ",
33
+ /* @__PURE__ */ a(n, { className: "ml-1 size-3" })
34
+ ]
29
35
  }),
30
- children: m.length === 0 ? /* @__PURE__ */ i(c, {
36
+ children: _.length === 0 ? /* @__PURE__ */ a(l, {
31
37
  illustration: "empty-data",
32
- title: "No projects yet",
38
+ title: e(g, "dashboard.projects.emptyTitle", "No projects yet"),
33
39
  className: "py-4",
34
- action: /* @__PURE__ */ i(s, {
40
+ action: /* @__PURE__ */ a(c, {
35
41
  variant: "outline",
36
42
  size: "sm",
37
- onClick: () => p("/workspaces/projects"),
38
- children: "Create a project"
43
+ onClick: () => h("/workspaces/projects"),
44
+ children: e(g, "dashboard.projects.createProject", "Create a project")
39
45
  })
40
- }) : /* @__PURE__ */ a("div", {
46
+ }) : /* @__PURE__ */ o("div", {
41
47
  className: "space-y-3",
42
- children: [h > 0 && /* @__PURE__ */ a("div", {
48
+ children: [v > 0 && /* @__PURE__ */ o("div", {
43
49
  className: "flex items-center gap-3 text-xs text-text-secondary",
44
50
  children: [
45
- /* @__PURE__ */ a("span", {
51
+ /* @__PURE__ */ o("span", {
46
52
  className: "flex items-center gap-1",
47
- children: [
48
- /* @__PURE__ */ i(n, { className: `size-2 fill-status-success-bg text-status-success-bg ${g > 0 ? "motion-safe:animate-vc-glow-pulse" : ""}` }),
49
- g,
50
- " active"
51
- ]
53
+ children: [/* @__PURE__ */ a(r, { className: `size-2 fill-status-success-bg text-status-success-bg ${y > 0 ? "motion-safe:animate-vc-glow-pulse" : ""}` }), e(g, "dashboard.projects.activeCount", "{count} active", { count: y })]
52
54
  }),
53
- _ > 0 && /* @__PURE__ */ a("span", {
55
+ b > 0 && /* @__PURE__ */ o("span", {
54
56
  className: "flex items-center gap-1",
55
- children: [
56
- /* @__PURE__ */ i(n, { className: "size-2 fill-text-tertiary text-text-tertiary" }),
57
- _,
58
- " inactive"
59
- ]
57
+ children: [/* @__PURE__ */ a(r, { className: "size-2 fill-text-tertiary text-text-tertiary" }), e(g, "dashboard.projects.inactiveCount", "{count} inactive", { count: b })]
60
58
  }),
61
59
  /* @__PURE__ */ a("span", {
62
60
  className: "ml-auto font-medium",
63
- children: [h, " total"]
61
+ children: e(g, "dashboard.projects.totalCount", "{count} total", { count: v })
64
62
  })
65
63
  ]
66
- }), /* @__PURE__ */ i("div", {
64
+ }), /* @__PURE__ */ a("div", {
67
65
  className: "space-y-2",
68
- children: m.slice(0, 4).map((e) => /* @__PURE__ */ a("button", {
66
+ children: _.slice(0, 4).map((e) => /* @__PURE__ */ o("button", {
69
67
  type: "button",
70
68
  className: "w-full flex items-center justify-between p-2 rounded-md hover:bg-accent/5 cursor-pointer transition-colors text-left",
71
- onClick: () => p(`/workspaces/${e.workspaceId}/projects`),
72
- children: [/* @__PURE__ */ a("div", {
69
+ onClick: () => h(`/workspaces/${e.workspaceId}/projects`),
70
+ children: [/* @__PURE__ */ o("div", {
73
71
  className: "flex items-center gap-2 min-w-0",
74
- children: [/* @__PURE__ */ i(n, { className: `size-2 ${l[e.status] || "bg-text-tertiary"} fill-current ${e.status === "ACTIVE" ? "motion-safe:animate-vc-glow-pulse" : ""}` }), /* @__PURE__ */ i("span", {
72
+ children: [/* @__PURE__ */ a(r, { className: `size-2 ${d[e.status] || "bg-text-tertiary"} fill-current ${e.status === "ACTIVE" ? "motion-safe:animate-vc-glow-pulse" : ""}` }), /* @__PURE__ */ a("span", {
75
73
  className: "text-sm font-medium text-text-primary truncate",
76
74
  children: e.name
77
75
  })]
78
- }), /* @__PURE__ */ i(o, {
79
- variant: u[e.status] || "outline",
76
+ }), /* @__PURE__ */ a(s, {
77
+ variant: f[e.status] || "outline",
80
78
  className: "text-[10px] shrink-0",
81
79
  children: e.key
82
80
  })]
@@ -86,6 +84,6 @@ function d({ workspaceData: d, isLoading: f, navigate: p }) {
86
84
  });
87
85
  }
88
86
  //#endregion
89
- export { d as ProjectsWidget };
87
+ export { p as ProjectsWidget };
90
88
 
91
89
  //# sourceMappingURL=ProjectsWidget.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectsWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/ProjectsWidget.tsx"],"sourcesContent":["import { FolderKanban, ArrowRight, Circle } from 'lucide-react';\nimport { Badge, Button, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport type { WidgetProps } from '../types';\n\nconst statusColors: Record<string, string> = {\n ACTIVE: 'bg-status-success-bg',\n INACTIVE: 'bg-text-tertiary',\n ARCHIVED: 'bg-status-warning-bg',\n};\n\nconst statusVariant: Record<string, 'default' | 'secondary' | 'outline'> = {\n ACTIVE: 'default',\n INACTIVE: 'secondary',\n ARCHIVED: 'outline',\n};\n\nexport function ProjectsWidget({ workspaceData, isLoading, navigate }: WidgetProps) {\n const projects = workspaceData?.projects.items ?? [];\n const total = workspaceData?.projects.total ?? 0;\n\n // Status breakdown\n const activeCount = projects.filter((p) => p.status === 'ACTIVE').length;\n const inactiveCount = projects.filter((p) => p.status === 'INACTIVE').length;\n\n return (\n <WidgetWrapper\n title=\"Projects\"\n icon={<FolderKanban className=\"size-4\" />}\n size=\"medium\"\n isLoading={isLoading}\n unavailable={!workspaceData}\n headerAction={\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-xs\"\n onClick={() => navigate('/workspaces/projects')}\n >\n View all <ArrowRight className=\"ml-1 size-3\" />\n </Button>\n }\n >\n {projects.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No projects yet\"\n className=\"py-4\"\n action={\n <Button variant=\"outline\" size=\"sm\" onClick={() => navigate('/workspaces/projects')}>\n Create a project\n </Button>\n }\n />\n ) : (\n <div className=\"space-y-3\">\n {total > 0 && (\n <div className=\"flex items-center gap-3 text-xs text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <Circle\n className={`size-2 fill-status-success-bg text-status-success-bg ${\n activeCount > 0 ? 'motion-safe:animate-vc-glow-pulse' : ''\n }`}\n />\n {activeCount} active\n </span>\n {inactiveCount > 0 && (\n <span className=\"flex items-center gap-1\">\n <Circle className=\"size-2 fill-text-tertiary text-text-tertiary\" />\n {inactiveCount} inactive\n </span>\n )}\n <span className=\"ml-auto font-medium\">{total} total</span>\n </div>\n )}\n <div className=\"space-y-2\">\n {projects.slice(0, 4).map((proj) => (\n <button\n key={proj.id}\n type=\"button\"\n className=\"w-full flex items-center justify-between p-2 rounded-md hover:bg-accent/5 cursor-pointer transition-colors text-left\"\n onClick={() => navigate(`/workspaces/${proj.workspaceId}/projects`)}\n >\n <div className=\"flex items-center gap-2 min-w-0\">\n <Circle\n className={`size-2 ${statusColors[proj.status] || 'bg-text-tertiary'} fill-current ${\n proj.status === 'ACTIVE' ? 'motion-safe:animate-vc-glow-pulse' : ''\n }`}\n />\n <span className=\"text-sm font-medium text-text-primary truncate\">\n {proj.name}\n </span>\n </div>\n <Badge\n variant={statusVariant[proj.status] || 'outline'}\n className=\"text-[10px] shrink-0\"\n >\n {proj.key}\n </Badge>\n </button>\n ))}\n </div>\n </div>\n )}\n </WidgetWrapper>\n );\n}\n"],"mappings":";;;;;AAKA,IAAM,IAAuC;CAC3C,QAAQ;CACR,UAAU;CACV,UAAU;CACX,EAEK,IAAqE;CACzE,QAAQ;CACR,UAAU;CACV,UAAU;CACX;AAED,SAAgB,EAAe,EAAE,kBAAe,cAAW,eAAyB;CAClF,IAAM,IAAW,GAAe,SAAS,SAAS,EAAE,EAC9C,IAAQ,GAAe,SAAS,SAAS,GAGzC,IAAc,EAAS,QAAQ,MAAM,EAAE,WAAW,SAAS,CAAC,QAC5D,IAAgB,EAAS,QAAQ,MAAM,EAAE,WAAW,WAAW,CAAC;AAEtE,QACE,kBAAC,GAAD;EACE,OAAM;EACN,MAAM,kBAAC,GAAD,EAAc,WAAU,UAAW,CAAA;EACzC,MAAK;EACM;EACX,aAAa,CAAC;EACd,cACE,kBAAC,GAAD;GACE,SAAQ;GACR,MAAK;GACL,WAAU;GACV,eAAe,EAAS,uBAAuB;aAJjD,CAKC,aACU,kBAAC,GAAD,EAAY,WAAU,eAAgB,CAAA,CACxC;;YAGV,EAAS,WAAW,IACnB,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,WAAU;GACV,QACE,kBAAC,GAAD;IAAQ,SAAQ;IAAU,MAAK;IAAK,eAAe,EAAS,uBAAuB;cAAE;IAE5E,CAAA;GAEX,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,IAAQ,KACP,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACE,kBAAC,GAAD,EACE,WAAW,wDACT,IAAc,IAAI,sCAAsC,MAE1D,CAAA;OACD;OAAY;OACR;;KACN,IAAgB,KACf,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACE,kBAAC,GAAD,EAAQ,WAAU,gDAAiD,CAAA;OAClE;OAAc;OACV;;KAET,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CAAuC,GAAM,SAAa;;KACtD;OAER,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAS,MAAM,GAAG,EAAE,CAAC,KAAK,MACzB,kBAAC,UAAD;KAEE,MAAK;KACL,WAAU;KACV,eAAe,EAAS,eAAe,EAAK,YAAY,WAAW;eAJrE,CAME,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EACE,WAAW,UAAU,EAAa,EAAK,WAAW,mBAAmB,gBACnE,EAAK,WAAW,WAAW,sCAAsC,MAEnE,CAAA,EACF,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAK;OACD,CAAA,CACH;SACN,kBAAC,GAAD;MACE,SAAS,EAAc,EAAK,WAAW;MACvC,WAAU;gBAET,EAAK;MACA,CAAA,CACD;OArBF,EAAK,GAqBH,CACT;IACE,CAAA,CACF;;EAEM,CAAA"}
1
+ {"version":3,"file":"ProjectsWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/ProjectsWidget.tsx"],"sourcesContent":["import { FolderKanban, ArrowRight, Circle } from 'lucide-react';\nimport { Badge, Button, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport { tWithFallback } from '../../../utils/i18n';\nimport type { WidgetProps } from '../types';\n\nconst statusColors: Record<string, string> = {\n ACTIVE: 'bg-status-success-bg',\n INACTIVE: 'bg-text-tertiary',\n ARCHIVED: 'bg-status-warning-bg',\n};\n\nconst statusVariant: Record<string, 'default' | 'secondary' | 'outline'> = {\n ACTIVE: 'default',\n INACTIVE: 'secondary',\n ARCHIVED: 'outline',\n};\n\nexport function ProjectsWidget({ workspaceData, isLoading, navigate }: WidgetProps) {\n const { t } = useI18n();\n const projects = workspaceData?.projects.items ?? [];\n const total = workspaceData?.projects.total ?? 0;\n\n // Status breakdown\n const activeCount = projects.filter((p) => p.status === 'ACTIVE').length;\n const inactiveCount = projects.filter((p) => p.status === 'INACTIVE').length;\n\n return (\n <WidgetWrapper\n title={tWithFallback(t, 'dashboard.projects.title', 'Projects')}\n icon={<FolderKanban className=\"size-4\" />}\n size=\"medium\"\n isLoading={isLoading}\n unavailable={!workspaceData}\n headerAction={\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-xs\"\n onClick={() => navigate('/workspaces/projects')}\n >\n {tWithFallback(t, 'dashboard.projects.viewAll', 'View all')}{' '}\n <ArrowRight className=\"ml-1 size-3\" />\n </Button>\n }\n >\n {projects.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={tWithFallback(t, 'dashboard.projects.emptyTitle', 'No projects yet')}\n className=\"py-4\"\n action={\n <Button variant=\"outline\" size=\"sm\" onClick={() => navigate('/workspaces/projects')}>\n {tWithFallback(t, 'dashboard.projects.createProject', 'Create a project')}\n </Button>\n }\n />\n ) : (\n <div className=\"space-y-3\">\n {total > 0 && (\n <div className=\"flex items-center gap-3 text-xs text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <Circle\n className={`size-2 fill-status-success-bg text-status-success-bg ${\n activeCount > 0 ? 'motion-safe:animate-vc-glow-pulse' : ''\n }`}\n />\n {tWithFallback(t, 'dashboard.projects.activeCount', '{count} active', {\n count: activeCount,\n })}\n </span>\n {inactiveCount > 0 && (\n <span className=\"flex items-center gap-1\">\n <Circle className=\"size-2 fill-text-tertiary text-text-tertiary\" />\n {tWithFallback(t, 'dashboard.projects.inactiveCount', '{count} inactive', {\n count: inactiveCount,\n })}\n </span>\n )}\n <span className=\"ml-auto font-medium\">\n {tWithFallback(t, 'dashboard.projects.totalCount', '{count} total', {\n count: total,\n })}\n </span>\n </div>\n )}\n <div className=\"space-y-2\">\n {projects.slice(0, 4).map((proj) => (\n <button\n key={proj.id}\n type=\"button\"\n className=\"w-full flex items-center justify-between p-2 rounded-md hover:bg-accent/5 cursor-pointer transition-colors text-left\"\n onClick={() => navigate(`/workspaces/${proj.workspaceId}/projects`)}\n >\n <div className=\"flex items-center gap-2 min-w-0\">\n <Circle\n className={`size-2 ${statusColors[proj.status] || 'bg-text-tertiary'} fill-current ${\n proj.status === 'ACTIVE' ? 'motion-safe:animate-vc-glow-pulse' : ''\n }`}\n />\n <span className=\"text-sm font-medium text-text-primary truncate\">\n {proj.name}\n </span>\n </div>\n <Badge\n variant={statusVariant[proj.status] || 'outline'}\n className=\"text-[10px] shrink-0\"\n >\n {proj.key}\n </Badge>\n </button>\n ))}\n </div>\n </div>\n )}\n </WidgetWrapper>\n );\n}\n"],"mappings":";;;;;;;AAOA,IAAM,IAAuC;CAC3C,QAAQ;CACR,UAAU;CACV,UAAU;CACX,EAEK,IAAqE;CACzE,QAAQ;CACR,UAAU;CACV,UAAU;CACX;AAED,SAAgB,EAAe,EAAE,kBAAe,cAAW,eAAyB;CAClF,IAAM,EAAE,SAAM,GAAS,EACjB,IAAW,GAAe,SAAS,SAAS,EAAE,EAC9C,IAAQ,GAAe,SAAS,SAAS,GAGzC,IAAc,EAAS,QAAQ,MAAM,EAAE,WAAW,SAAS,CAAC,QAC5D,IAAgB,EAAS,QAAQ,MAAM,EAAE,WAAW,WAAW,CAAC;AAEtE,QACE,kBAAC,GAAD;EACE,OAAO,EAAc,GAAG,4BAA4B,WAAW;EAC/D,MAAM,kBAAC,GAAD,EAAc,WAAU,UAAW,CAAA;EACzC,MAAK;EACM;EACX,aAAa,CAAC;EACd,cACE,kBAAC,GAAD;GACE,SAAQ;GACR,MAAK;GACL,WAAU;GACV,eAAe,EAAS,uBAAuB;aAJjD;IAMG,EAAc,GAAG,8BAA8B,WAAW;IAAE;IAC7D,kBAAC,GAAD,EAAY,WAAU,eAAgB,CAAA;IAC/B;;YAGV,EAAS,WAAW,IACnB,kBAAC,GAAD;GACE,cAAa;GACb,OAAO,EAAc,GAAG,iCAAiC,kBAAkB;GAC3E,WAAU;GACV,QACE,kBAAC,GAAD;IAAQ,SAAQ;IAAU,MAAK;IAAK,eAAe,EAAS,uBAAuB;cAChF,EAAc,GAAG,oCAAoC,mBAAmB;IAClE,CAAA;GAEX,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,IAAQ,KACP,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CACE,kBAAC,GAAD,EACE,WAAW,wDACT,IAAc,IAAI,sCAAsC,MAE1D,CAAA,EACD,EAAc,GAAG,kCAAkC,kBAAkB,EACpE,OAAO,GACR,CAAC,CACG;;KACN,IAAgB,KACf,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CACE,kBAAC,GAAD,EAAQ,WAAU,gDAAiD,CAAA,EAClE,EAAc,GAAG,oCAAoC,oBAAoB,EACxE,OAAO,GACR,CAAC,CACG;;KAET,kBAAC,QAAD;MAAM,WAAU;gBACb,EAAc,GAAG,iCAAiC,iBAAiB,EAClE,OAAO,GACR,CAAC;MACG,CAAA;KACH;OAER,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAS,MAAM,GAAG,EAAE,CAAC,KAAK,MACzB,kBAAC,UAAD;KAEE,MAAK;KACL,WAAU;KACV,eAAe,EAAS,eAAe,EAAK,YAAY,WAAW;eAJrE,CAME,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EACE,WAAW,UAAU,EAAa,EAAK,WAAW,mBAAmB,gBACnE,EAAK,WAAW,WAAW,sCAAsC,MAEnE,CAAA,EACF,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAK;OACD,CAAA,CACH;SACN,kBAAC,GAAD;MACE,SAAS,EAAc,EAAK,WAAW;MACvC,WAAU;gBAET,EAAK;MACA,CAAA,CACD;OArBF,EAAK,GAqBH,CACT;IACE,CAAA,CACF;;EAEM,CAAA"}