@burdenoff/microfe-workspaces 2026.802.1 → 2026.825.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 (49) 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/WorkspaceDetailPage.js +152 -114
  8. package/dist/pages/WorkspaceDetailPage.js.map +1 -1
  9. package/dist/pages/dashboard/DashboardCustomizer.js +62 -62
  10. package/dist/pages/dashboard/DashboardCustomizer.js.map +1 -1
  11. package/dist/pages/dashboard/DashboardPage.js +88 -69
  12. package/dist/pages/dashboard/DashboardPage.js.map +1 -1
  13. package/dist/pages/dashboard/config.js +77 -1
  14. package/dist/pages/dashboard/config.js.map +1 -1
  15. package/dist/pages/dashboard/widgets/AgentsWidget.js +56 -54
  16. package/dist/pages/dashboard/widgets/AgentsWidget.js.map +1 -1
  17. package/dist/pages/dashboard/widgets/GroupsWidget.js +29 -31
  18. package/dist/pages/dashboard/widgets/GroupsWidget.js.map +1 -1
  19. package/dist/pages/dashboard/widgets/MembersWidget.js +33 -33
  20. package/dist/pages/dashboard/widgets/MembersWidget.js.map +1 -1
  21. package/dist/pages/dashboard/widgets/ProjectsWidget.js +42 -44
  22. package/dist/pages/dashboard/widgets/ProjectsWidget.js.map +1 -1
  23. package/dist/pages/dashboard/widgets/QuickActionsWidget.js +171 -161
  24. package/dist/pages/dashboard/widgets/QuickActionsWidget.js.map +1 -1
  25. package/dist/pages/dashboard/widgets/RecentActivityWidget.js +54 -44
  26. package/dist/pages/dashboard/widgets/RecentActivityWidget.js.map +1 -1
  27. package/dist/pages/dashboard/widgets/SessionsWidget.js +57 -41
  28. package/dist/pages/dashboard/widgets/SessionsWidget.js.map +1 -1
  29. package/dist/pages/dashboard/widgets/StorageWidget.js +52 -54
  30. package/dist/pages/dashboard/widgets/StorageWidget.js.map +1 -1
  31. package/dist/pages/dashboard/widgets/TagCloudWidget.js +28 -30
  32. package/dist/pages/dashboard/widgets/TagCloudWidget.js.map +1 -1
  33. package/dist/pages/dashboard/widgets/TipsWidget.js +141 -131
  34. package/dist/pages/dashboard/widgets/TipsWidget.js.map +1 -1
  35. package/dist/pages/dashboard/widgets/UpcomingEventsWidget.js +46 -40
  36. package/dist/pages/dashboard/widgets/UpcomingEventsWidget.js.map +1 -1
  37. package/dist/pages/dashboard/widgets/VibesWidget.js +44 -34
  38. package/dist/pages/dashboard/widgets/VibesWidget.js.map +1 -1
  39. package/dist/pages/dashboard/widgets/WelcomeWidget.js +43 -41
  40. package/dist/pages/dashboard/widgets/WelcomeWidget.js.map +1 -1
  41. package/dist/pages/dashboard/widgets/WorkflowRunsWidget.js +68 -62
  42. package/dist/pages/dashboard/widgets/WorkflowRunsWidget.js.map +1 -1
  43. package/dist/pages/dashboard/widgets/WorkflowStatsWidget.js +50 -44
  44. package/dist/pages/dashboard/widgets/WorkflowStatsWidget.js.map +1 -1
  45. package/dist/pages/dashboard/widgets/WorkflowsOverviewWidget.js +59 -49
  46. package/dist/pages/dashboard/widgets/WorkflowsOverviewWidget.js.map +1 -1
  47. package/dist/pages/dashboard/widgets/WorkspacesWidget.js +35 -33
  48. package/dist/pages/dashboard/widgets/WorkspacesWidget.js.map +1 -1
  49. package/package.json +2 -2
@@ -1,24 +1,30 @@
1
- import { WidgetWrapper as e } from "../WidgetWrapper.js";
2
- import { Activity as t, ArrowRight 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";
5
- import { formatRelativeTime as s } from "@burdenoff/fe-libs/shared/utils";
1
+ import { tWithFallback as e } from "../../../utils/i18n.js";
2
+ import { WidgetWrapper as t } from "../WidgetWrapper.js";
3
+ import { Activity as n, ArrowRight 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 { formatRelativeTime as c } from "@burdenoff/fe-libs/shared/utils";
7
+ import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
6
8
  //#region src/pages/dashboard/widgets/RecentActivityWidget.tsx
7
- var c = {
8
- "workspace.created": "Created workspace",
9
- "workspace.updated": "Updated workspace",
10
- "project.created": "Created project",
11
- "project.updated": "Updated project",
12
- "member.added": "Added member",
13
- "member.removed": "Removed member",
14
- "file.uploaded": "Uploaded file",
15
- "tag.created": "Created tag",
16
- "tag.assigned": "Assigned tag",
17
- "workflow.started": "Started workflow",
18
- "workflow.completed": "Completed workflow",
19
- "event.created": "Created event",
20
- "comment.added": "Added comment"
21
- }, l = {
9
+ function u(t, n) {
10
+ switch (n) {
11
+ case "workspace.created": return e(t, "dashboard.recentActivity.workspaceCreated", "Created workspace");
12
+ case "workspace.updated": return e(t, "dashboard.recentActivity.workspaceUpdated", "Updated workspace");
13
+ case "project.created": return e(t, "dashboard.recentActivity.projectCreated", "Created project");
14
+ case "project.updated": return e(t, "dashboard.recentActivity.projectUpdated", "Updated project");
15
+ case "member.added": return e(t, "dashboard.recentActivity.memberAdded", "Added member");
16
+ case "member.removed": return e(t, "dashboard.recentActivity.memberRemoved", "Removed member");
17
+ case "file.uploaded": return e(t, "dashboard.recentActivity.fileUploaded", "Uploaded file");
18
+ case "tag.created": return e(t, "dashboard.recentActivity.tagCreated", "Created tag");
19
+ case "tag.assigned": return e(t, "dashboard.recentActivity.tagAssigned", "Assigned tag");
20
+ case "workflow.started": return e(t, "dashboard.recentActivity.workflowStarted", "Started workflow");
21
+ case "workflow.completed": return e(t, "dashboard.recentActivity.workflowCompleted", "Completed workflow");
22
+ case "event.created": return e(t, "dashboard.recentActivity.eventCreated", "Created event");
23
+ case "comment.added": return e(t, "dashboard.recentActivity.commentAdded", "Added comment");
24
+ default: return;
25
+ }
26
+ }
27
+ var d = {
22
28
  workspace: "bg-status-info-bg-subtle text-status-info-text",
23
29
  project: "bg-status-success-bg-subtle text-status-success-text",
24
30
  member: "bg-accent-purple-subtle text-accent-purple",
@@ -27,41 +33,45 @@ var c = {
27
33
  workflow: "bg-accent-orange-subtle text-accent-orange",
28
34
  calendar: "bg-accent-pink-subtle text-accent-pink"
29
35
  };
30
- function u({ workspaceData: u, isLoading: d, navigate: f }) {
31
- let p = u?.recentActivities ?? [];
32
- return /* @__PURE__ */ r(e, {
33
- title: "Recent Activity",
34
- icon: /* @__PURE__ */ r(t, { className: "size-4" }),
36
+ function f({ workspaceData: f, isLoading: p, navigate: m }) {
37
+ let { t: h } = l(), g = f?.recentActivities ?? [];
38
+ return /* @__PURE__ */ i(t, {
39
+ title: e(h, "dashboard.recentActivity.title", "Recent Activity"),
40
+ icon: /* @__PURE__ */ i(n, { className: "size-4" }),
35
41
  size: "large",
36
- isLoading: d,
37
- unavailable: !u,
38
- headerAction: /* @__PURE__ */ i(a, {
42
+ isLoading: p,
43
+ unavailable: !f,
44
+ headerAction: /* @__PURE__ */ a(o, {
39
45
  variant: "ghost",
40
46
  size: "sm",
41
47
  className: "text-xs",
42
- onClick: () => f("/workspaces/activity"),
43
- children: ["View all ", /* @__PURE__ */ r(n, { className: "ml-1 size-3" })]
48
+ onClick: () => m("/workspaces/activity"),
49
+ children: [
50
+ e(h, "dashboard.recentActivity.viewAll", "View all"),
51
+ " ",
52
+ /* @__PURE__ */ i(r, { className: "ml-1 size-3" })
53
+ ]
44
54
  }),
45
- children: p.length === 0 ? /* @__PURE__ */ r(o, {
55
+ children: g.length === 0 ? /* @__PURE__ */ i(s, {
46
56
  illustration: "empty-data",
47
- title: "No recent activity",
48
- description: "Activity will appear here as you use the platform",
57
+ title: e(h, "dashboard.recentActivity.emptyTitle", "No recent activity"),
58
+ description: e(h, "dashboard.recentActivity.emptyDescription", "Activity will appear here as you use the platform"),
49
59
  className: "py-6"
50
- }) : /* @__PURE__ */ r("div", {
60
+ }) : /* @__PURE__ */ i("div", {
51
61
  className: "space-y-1",
52
- children: p.map((e, t) => {
53
- let n = e.resourceType || "workspace", a = l[n] || "bg-bg-sunken text-text-secondary", o = s(new Date(e.timestamp));
54
- return /* @__PURE__ */ i("div", {
62
+ children: g.map((e, t) => {
63
+ let n = e.resourceType || "workspace", r = d[n] || "bg-bg-sunken text-text-secondary", o = c(new Date(e.timestamp)), s = u(h, e.action);
64
+ return /* @__PURE__ */ a("div", {
55
65
  className: "flex items-start gap-3 p-2 rounded-md hover:bg-accent/5 transition-colors",
56
- children: [/* @__PURE__ */ r("div", {
57
- className: `rounded-full size-7 flex items-center justify-center text-xs font-bold shrink-0 ${a}`,
66
+ children: [/* @__PURE__ */ i("div", {
67
+ className: `rounded-full size-7 flex items-center justify-center text-xs font-bold shrink-0 ${r}`,
58
68
  children: n.charAt(0).toUpperCase()
59
- }), /* @__PURE__ */ i("div", {
69
+ }), /* @__PURE__ */ a("div", {
60
70
  className: "min-w-0 flex-1",
61
- children: [/* @__PURE__ */ r("p", {
71
+ children: [/* @__PURE__ */ i("p", {
62
72
  className: "text-sm text-text-primary truncate",
63
- children: e.description || c[e.action] || e.action
64
- }), /* @__PURE__ */ r("p", {
73
+ children: e.description || s || e.action
74
+ }), /* @__PURE__ */ i("p", {
65
75
  className: "text-xs text-text-secondary",
66
76
  children: o
67
77
  })]
@@ -72,6 +82,6 @@ function u({ workspaceData: u, isLoading: d, navigate: f }) {
72
82
  });
73
83
  }
74
84
  //#endregion
75
- export { u as RecentActivityWidget };
85
+ export { f as RecentActivityWidget };
76
86
 
77
87
  //# sourceMappingURL=RecentActivityWidget.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RecentActivityWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/RecentActivityWidget.tsx"],"sourcesContent":["import { Activity, ArrowRight } from 'lucide-react';\nimport { Button, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport type { WidgetProps } from '../types';\nimport { formatRelativeTime } from '@burdenoff/fe-libs/shared/utils';\n\nconst actionLabels: Record<string, string> = {\n 'workspace.created': 'Created workspace',\n 'workspace.updated': 'Updated workspace',\n 'project.created': 'Created project',\n 'project.updated': 'Updated project',\n 'member.added': 'Added member',\n 'member.removed': 'Removed member',\n 'file.uploaded': 'Uploaded file',\n 'tag.created': 'Created tag',\n 'tag.assigned': 'Assigned tag',\n 'workflow.started': 'Started workflow',\n 'workflow.completed': 'Completed workflow',\n 'event.created': 'Created event',\n 'comment.added': 'Added comment',\n};\n\nconst entityIcons: Record<string, string> = {\n workspace: 'bg-status-info-bg-subtle text-status-info-text',\n project: 'bg-status-success-bg-subtle text-status-success-text',\n member: 'bg-accent-purple-subtle text-accent-purple',\n file: 'bg-status-warning-bg-subtle text-status-warning-text',\n tag: 'bg-accent-cyan-subtle text-accent-cyan',\n workflow: 'bg-accent-orange-subtle text-accent-orange',\n calendar: 'bg-accent-pink-subtle text-accent-pink',\n};\n\nexport function RecentActivityWidget({ workspaceData, isLoading, navigate }: WidgetProps) {\n const activities = workspaceData?.recentActivities ?? [];\n\n return (\n <WidgetWrapper\n title=\"Recent Activity\"\n icon={<Activity className=\"size-4\" />}\n size=\"large\"\n isLoading={isLoading}\n unavailable={!workspaceData}\n headerAction={\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-xs\"\n onClick={() => navigate('/workspaces/activity')}\n >\n View all <ArrowRight className=\"ml-1 size-3\" />\n </Button>\n }\n >\n {activities.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No recent activity\"\n description=\"Activity will appear here as you use the platform\"\n className=\"py-6\"\n />\n ) : (\n <div className=\"space-y-1\">\n {activities.map((activity, idx) => {\n const resourceType = activity.resourceType || 'workspace';\n const colorClass = entityIcons[resourceType] || 'bg-bg-sunken text-text-secondary';\n const timeAgo = formatRelativeTime(new Date(activity.timestamp));\n return (\n <div\n key={activity.id || idx}\n className=\"flex items-start gap-3 p-2 rounded-md hover:bg-accent/5 transition-colors\"\n >\n <div\n className={`rounded-full size-7 flex items-center justify-center text-xs font-bold shrink-0 ${colorClass}`}\n >\n {resourceType.charAt(0).toUpperCase()}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm text-text-primary truncate\">\n {activity.description || actionLabels[activity.action] || activity.action}\n </p>\n <p className=\"text-xs text-text-secondary\">{timeAgo}</p>\n </div>\n </div>\n );\n })}\n </div>\n )}\n </WidgetWrapper>\n );\n}\n"],"mappings":";;;;;;AAMA,IAAM,IAAuC;CAC3C,qBAAqB;CACrB,qBAAqB;CACrB,mBAAmB;CACnB,mBAAmB;CACnB,gBAAgB;CAChB,kBAAkB;CAClB,iBAAiB;CACjB,eAAe;CACf,gBAAgB;CAChB,oBAAoB;CACpB,sBAAsB;CACtB,iBAAiB;CACjB,iBAAiB;CAClB,EAEK,IAAsC;CAC1C,WAAW;CACX,SAAS;CACT,QAAQ;CACR,MAAM;CACN,KAAK;CACL,UAAU;CACV,UAAU;CACX;AAED,SAAgB,EAAqB,EAAE,kBAAe,cAAW,eAAyB;CACxF,IAAM,IAAa,GAAe,oBAAoB,EAAE;AAExD,QACE,kBAAC,GAAD;EACE,OAAM;EACN,MAAM,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA;EACrC,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,EAAW,WAAW,IACrB,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,aAAY;GACZ,WAAU;GACV,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAW,KAAK,GAAU,MAAQ;IACjC,IAAM,IAAe,EAAS,gBAAgB,aACxC,IAAa,EAAY,MAAiB,oCAC1C,IAAU,EAAmB,IAAI,KAAK,EAAS,UAAU,CAAC;AAChE,WACE,kBAAC,OAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,OAAD;MACE,WAAW,mFAAmF;gBAE7F,EAAa,OAAO,EAAE,CAAC,aAAa;MACjC,CAAA,EACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAS,eAAe,EAAa,EAAS,WAAW,EAAS;OACjE,CAAA,EACJ,kBAAC,KAAD;OAAG,WAAU;iBAA+B;OAAY,CAAA,CACpD;QACF;OAdC,EAAS,MAAM,EAchB;KAER;GACE,CAAA;EAEM,CAAA"}
1
+ {"version":3,"file":"RecentActivityWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/RecentActivityWidget.tsx"],"sourcesContent":["import { Activity, ArrowRight } from 'lucide-react';\nimport { Button, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport type { I18nContextValue } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport { tWithFallback } from '../../../utils/i18n';\nimport type { WidgetProps } from '../types';\nimport { formatRelativeTime } from '@burdenoff/fe-libs/shared/utils';\n\n/**\n * Human-readable label for an audit action, resolved through i18n with\n * **literal** keys.\n *\n * This used to be a `Record<string, string>` of English labels. A record\n * lookup can only ever produce a *computed* key, and the key extractor\n * (fluidgrids-app/scripts/extract-i18n-keys.ts) collects string literals only\n * — so those labels could never be extracted or seeded, and would render in\n * English in every locale forever. Spelling each action out gives the\n * extractor something to find. Unknown actions return `undefined`, exactly as\n * the record lookup did, so the caller still falls back to the raw action id.\n * [BOFF-5997]\n */\nfunction resolveActionLabel(t: I18nContextValue['t'], action: string): string | undefined {\n switch (action) {\n case 'workspace.created':\n return tWithFallback(t, 'dashboard.recentActivity.workspaceCreated', 'Created workspace');\n case 'workspace.updated':\n return tWithFallback(t, 'dashboard.recentActivity.workspaceUpdated', 'Updated workspace');\n case 'project.created':\n return tWithFallback(t, 'dashboard.recentActivity.projectCreated', 'Created project');\n case 'project.updated':\n return tWithFallback(t, 'dashboard.recentActivity.projectUpdated', 'Updated project');\n case 'member.added':\n return tWithFallback(t, 'dashboard.recentActivity.memberAdded', 'Added member');\n case 'member.removed':\n return tWithFallback(t, 'dashboard.recentActivity.memberRemoved', 'Removed member');\n case 'file.uploaded':\n return tWithFallback(t, 'dashboard.recentActivity.fileUploaded', 'Uploaded file');\n case 'tag.created':\n return tWithFallback(t, 'dashboard.recentActivity.tagCreated', 'Created tag');\n case 'tag.assigned':\n return tWithFallback(t, 'dashboard.recentActivity.tagAssigned', 'Assigned tag');\n case 'workflow.started':\n return tWithFallback(t, 'dashboard.recentActivity.workflowStarted', 'Started workflow');\n case 'workflow.completed':\n return tWithFallback(t, 'dashboard.recentActivity.workflowCompleted', 'Completed workflow');\n case 'event.created':\n return tWithFallback(t, 'dashboard.recentActivity.eventCreated', 'Created event');\n case 'comment.added':\n return tWithFallback(t, 'dashboard.recentActivity.commentAdded', 'Added comment');\n default:\n return undefined;\n }\n}\n\nconst entityIcons: Record<string, string> = {\n workspace: 'bg-status-info-bg-subtle text-status-info-text',\n project: 'bg-status-success-bg-subtle text-status-success-text',\n member: 'bg-accent-purple-subtle text-accent-purple',\n file: 'bg-status-warning-bg-subtle text-status-warning-text',\n tag: 'bg-accent-cyan-subtle text-accent-cyan',\n workflow: 'bg-accent-orange-subtle text-accent-orange',\n calendar: 'bg-accent-pink-subtle text-accent-pink',\n};\n\nexport function RecentActivityWidget({ workspaceData, isLoading, navigate }: WidgetProps) {\n const { t } = useI18n();\n const activities = workspaceData?.recentActivities ?? [];\n\n return (\n <WidgetWrapper\n title={tWithFallback(t, 'dashboard.recentActivity.title', 'Recent Activity')}\n icon={<Activity className=\"size-4\" />}\n size=\"large\"\n isLoading={isLoading}\n unavailable={!workspaceData}\n headerAction={\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-xs\"\n onClick={() => navigate('/workspaces/activity')}\n >\n {tWithFallback(t, 'dashboard.recentActivity.viewAll', 'View all')}{' '}\n <ArrowRight className=\"ml-1 size-3\" />\n </Button>\n }\n >\n {activities.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={tWithFallback(t, 'dashboard.recentActivity.emptyTitle', 'No recent activity')}\n description={tWithFallback(\n t,\n 'dashboard.recentActivity.emptyDescription',\n 'Activity will appear here as you use the platform'\n )}\n className=\"py-6\"\n />\n ) : (\n <div className=\"space-y-1\">\n {activities.map((activity, idx) => {\n const resourceType = activity.resourceType || 'workspace';\n const colorClass = entityIcons[resourceType] || 'bg-bg-sunken text-text-secondary';\n const timeAgo = formatRelativeTime(new Date(activity.timestamp));\n const actionLabel = resolveActionLabel(t, activity.action);\n return (\n <div\n key={activity.id || idx}\n className=\"flex items-start gap-3 p-2 rounded-md hover:bg-accent/5 transition-colors\"\n >\n <div\n className={`rounded-full size-7 flex items-center justify-center text-xs font-bold shrink-0 ${colorClass}`}\n >\n {resourceType.charAt(0).toUpperCase()}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm text-text-primary truncate\">\n {activity.description || actionLabel || activity.action}\n </p>\n <p className=\"text-xs text-text-secondary\">{timeAgo}</p>\n </div>\n </div>\n );\n })}\n </div>\n )}\n </WidgetWrapper>\n );\n}\n"],"mappings":";;;;;;;;AAsBA,SAAS,EAAmB,GAA0B,GAAoC;AACxF,SAAQ,GAAR;EACE,KAAK,oBACH,QAAO,EAAc,GAAG,6CAA6C,oBAAoB;EAC3F,KAAK,oBACH,QAAO,EAAc,GAAG,6CAA6C,oBAAoB;EAC3F,KAAK,kBACH,QAAO,EAAc,GAAG,2CAA2C,kBAAkB;EACvF,KAAK,kBACH,QAAO,EAAc,GAAG,2CAA2C,kBAAkB;EACvF,KAAK,eACH,QAAO,EAAc,GAAG,wCAAwC,eAAe;EACjF,KAAK,iBACH,QAAO,EAAc,GAAG,0CAA0C,iBAAiB;EACrF,KAAK,gBACH,QAAO,EAAc,GAAG,yCAAyC,gBAAgB;EACnF,KAAK,cACH,QAAO,EAAc,GAAG,uCAAuC,cAAc;EAC/E,KAAK,eACH,QAAO,EAAc,GAAG,wCAAwC,eAAe;EACjF,KAAK,mBACH,QAAO,EAAc,GAAG,4CAA4C,mBAAmB;EACzF,KAAK,qBACH,QAAO,EAAc,GAAG,8CAA8C,qBAAqB;EAC7F,KAAK,gBACH,QAAO,EAAc,GAAG,yCAAyC,gBAAgB;EACnF,KAAK,gBACH,QAAO,EAAc,GAAG,yCAAyC,gBAAgB;EACnF,QACE;;;AAIN,IAAM,IAAsC;CAC1C,WAAW;CACX,SAAS;CACT,QAAQ;CACR,MAAM;CACN,KAAK;CACL,UAAU;CACV,UAAU;CACX;AAED,SAAgB,EAAqB,EAAE,kBAAe,cAAW,eAAyB;CACxF,IAAM,EAAE,SAAM,GAAS,EACjB,IAAa,GAAe,oBAAoB,EAAE;AAExD,QACE,kBAAC,GAAD;EACE,OAAO,EAAc,GAAG,kCAAkC,kBAAkB;EAC5E,MAAM,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA;EACrC,MAAK;EACM;EACX,aAAa,CAAC;EACd,cACE,kBAAC,GAAD;GACE,SAAQ;GACR,MAAK;GACL,WAAU;GACV,eAAe,EAAS,uBAAuB;aAJjD;IAMG,EAAc,GAAG,oCAAoC,WAAW;IAAE;IACnE,kBAAC,GAAD,EAAY,WAAU,eAAgB,CAAA;IAC/B;;YAGV,EAAW,WAAW,IACrB,kBAAC,GAAD;GACE,cAAa;GACb,OAAO,EAAc,GAAG,uCAAuC,qBAAqB;GACpF,aAAa,EACX,GACA,6CACA,oDACD;GACD,WAAU;GACV,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAW,KAAK,GAAU,MAAQ;IACjC,IAAM,IAAe,EAAS,gBAAgB,aACxC,IAAa,EAAY,MAAiB,oCAC1C,IAAU,EAAmB,IAAI,KAAK,EAAS,UAAU,CAAC,EAC1D,IAAc,EAAmB,GAAG,EAAS,OAAO;AAC1D,WACE,kBAAC,OAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,OAAD;MACE,WAAW,mFAAmF;gBAE7F,EAAa,OAAO,EAAE,CAAC,aAAa;MACjC,CAAA,EACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAS,eAAe,KAAe,EAAS;OAC/C,CAAA,EACJ,kBAAC,KAAD;OAAG,WAAU;iBAA+B;OAAY,CAAA,CACpD;QACF;OAdC,EAAS,MAAM,EAchB;KAER;GACE,CAAA;EAEM,CAAA"}
@@ -1,76 +1,92 @@
1
- import { WidgetWrapper as e } from "../WidgetWrapper.js";
2
- import { ArrowRight as t, Terminal as n } from "lucide-react";
3
- import { jsx as r, jsxs as i } from "react/jsx-runtime";
4
- import { Badge as a, Button as o, IllustratedEmptyState as s } 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, Terminal as r } from "lucide-react";
4
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
5
+ import { Badge as o, Button as s, IllustratedEmptyState as c } from "@burdenoff/fe-libs/ui";
6
+ import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
5
7
  //#region src/pages/dashboard/widgets/SessionsWidget.tsx
6
- var c = {
8
+ var u = {
7
9
  RUNNING: "bg-status-success-bg text-status-success-text",
8
10
  ACTIVE: "bg-status-success-bg text-status-success-text",
9
11
  STARTING: "bg-status-warning-bg text-status-warning-text",
10
12
  STOPPED: "bg-text-tertiary/20 text-text-tertiary",
11
13
  ERROR: "bg-status-error-bg text-status-error-text"
12
- }, l = new Set(["RUNNING", "ACTIVE"]);
13
- function u(e) {
14
- if (!e) return "";
15
- let t = Date.now() - new Date(e).getTime(), n = Math.floor(t / 6e4);
16
- if (n < 1) return "just now";
17
- if (n < 60) return `${n}m`;
18
- let r = Math.floor(n / 60);
19
- return r < 24 ? `${r}h` : new Date(e).toLocaleDateString(void 0, {
14
+ }, d = new Set(["RUNNING", "ACTIVE"]);
15
+ function f(t, n) {
16
+ switch (n) {
17
+ case "RUNNING": return e(t, "dashboard.sessions.statusRunning", "running");
18
+ case "ACTIVE": return e(t, "dashboard.sessions.statusActive", "active");
19
+ case "STARTING": return e(t, "dashboard.sessions.statusStarting", "starting");
20
+ case "STOPPED": return e(t, "dashboard.sessions.statusStopped", "stopped");
21
+ case "ERROR": return e(t, "dashboard.sessions.statusError", "error");
22
+ default: return n.toLowerCase();
23
+ }
24
+ }
25
+ function p(t, n) {
26
+ if (!n) return "";
27
+ let r = Date.now() - new Date(n).getTime(), i = Math.floor(r / 6e4);
28
+ if (i < 1) return e(t, "dashboard.sessions.justNow", "just now");
29
+ if (i < 60) return e(t, "dashboard.sessions.minutesAgo", "{count}m", { count: i });
30
+ let a = Math.floor(i / 60);
31
+ return a < 24 ? e(t, "dashboard.sessions.hoursAgo", "{count}h", { count: a }) : new Date(n).toLocaleDateString(void 0, {
20
32
  month: "short",
21
33
  day: "numeric"
22
34
  });
23
35
  }
24
- function d({ workspaceData: d, isLoading: f, navigate: p }) {
25
- let m = d?.activeSessions ?? [];
26
- return /* @__PURE__ */ r(e, {
27
- title: "Active Sessions",
28
- icon: /* @__PURE__ */ r(n, { className: "size-4" }),
36
+ function m({ workspaceData: m, isLoading: h, navigate: g }) {
37
+ let { t: _ } = l(), v = m?.activeSessions ?? [];
38
+ return /* @__PURE__ */ i(t, {
39
+ title: e(_, "dashboard.sessions.title", "Active Sessions"),
40
+ icon: /* @__PURE__ */ i(r, { className: "size-4" }),
29
41
  size: "medium",
30
- isLoading: f,
31
- unavailable: !d,
32
- headerAction: /* @__PURE__ */ i(o, {
42
+ isLoading: h,
43
+ unavailable: !m,
44
+ headerAction: /* @__PURE__ */ a(s, {
33
45
  variant: "ghost",
34
46
  size: "sm",
35
47
  className: "text-xs",
36
- onClick: () => p("/vibecontrols/sessions"),
37
- children: ["View all ", /* @__PURE__ */ r(t, { className: "ml-1 size-3" })]
48
+ onClick: () => g("/vibecontrols/sessions"),
49
+ children: [
50
+ e(_, "dashboard.sessions.viewAll", "View all"),
51
+ " ",
52
+ /* @__PURE__ */ i(n, { className: "ml-1 size-3" })
53
+ ]
38
54
  }),
39
- children: m.length === 0 ? /* @__PURE__ */ r(s, {
55
+ children: v.length === 0 ? /* @__PURE__ */ i(c, {
40
56
  illustration: "empty-sessions",
41
- title: "No active sessions",
57
+ title: e(_, "dashboard.sessions.emptyTitle", "No active sessions"),
42
58
  className: "py-6"
43
- }) : /* @__PURE__ */ r("ul", {
59
+ }) : /* @__PURE__ */ i("ul", {
44
60
  className: "space-y-1.5",
45
- children: m.slice(0, 5).map((e) => /* @__PURE__ */ r("li", { children: /* @__PURE__ */ i("button", {
61
+ children: v.slice(0, 5).map((t) => /* @__PURE__ */ i("li", { children: /* @__PURE__ */ a("button", {
46
62
  type: "button",
47
- onClick: () => p(`/vibecontrols/sessions/${e.id}`),
63
+ onClick: () => g(`/vibecontrols/sessions/${t.id}`),
48
64
  className: "flex w-full items-center justify-between gap-2 rounded-md p-2 text-left transition-colors hover:bg-accent/5",
49
- children: [/* @__PURE__ */ i("div", {
65
+ children: [/* @__PURE__ */ a("div", {
50
66
  className: "min-w-0 flex-1",
51
- children: [/* @__PURE__ */ r("p", {
67
+ children: [/* @__PURE__ */ i("p", {
52
68
  className: "truncate text-sm font-medium text-text-primary",
53
- children: e.name || `Session ${e.id.slice(0, 8)}`
54
- }), e.agent?.name ? /* @__PURE__ */ r("p", {
69
+ children: t.name || e(_, "dashboard.sessions.unnamedSession", "Session {id}", { id: t.id.slice(0, 8) })
70
+ }), t.agent?.name ? /* @__PURE__ */ i("p", {
55
71
  className: "truncate text-[11px] text-text-secondary",
56
- children: e.agent.name
72
+ children: t.agent.name
57
73
  }) : null]
58
- }), /* @__PURE__ */ i("div", {
74
+ }), /* @__PURE__ */ a("div", {
59
75
  className: "flex flex-col items-end gap-1",
60
- children: [/* @__PURE__ */ r(a, {
76
+ children: [/* @__PURE__ */ i(o, {
61
77
  variant: "outline",
62
- className: `text-[10px] ${c[e.status] ?? ""} ${l.has(e.status) ? "motion-safe:animate-vc-glow-pulse" : ""}`,
63
- children: e.status.toLowerCase()
64
- }), e.startedAt ? /* @__PURE__ */ r("span", {
78
+ className: `text-[10px] ${u[t.status] ?? ""} ${d.has(t.status) ? "motion-safe:animate-vc-glow-pulse" : ""}`,
79
+ children: f(_, t.status)
80
+ }), t.startedAt ? /* @__PURE__ */ i("span", {
65
81
  className: "text-[10px] text-text-secondary",
66
- children: u(e.startedAt)
82
+ children: p(_, t.startedAt)
67
83
  }) : null]
68
84
  })]
69
- }) }, e.id))
85
+ }) }, t.id))
70
86
  })
71
87
  });
72
88
  }
73
89
  //#endregion
74
- export { d as SessionsWidget };
90
+ export { m as SessionsWidget };
75
91
 
76
92
  //# sourceMappingURL=SessionsWidget.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SessionsWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/SessionsWidget.tsx"],"sourcesContent":["import { Terminal, ArrowRight } from 'lucide-react';\nimport { Badge, Button, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport type { WidgetProps } from '../types';\n\nconst STATUS_BADGE: Record<string, string> = {\n RUNNING: 'bg-status-success-bg text-status-success-text',\n ACTIVE: 'bg-status-success-bg text-status-success-text',\n STARTING: 'bg-status-warning-bg text-status-warning-text',\n STOPPED: 'bg-text-tertiary/20 text-text-tertiary',\n ERROR: 'bg-status-error-bg text-status-error-text',\n};\n\n// Live statuses get a subtle opacity pulse (reduced-motion-safe, no movement).\nconst LIVE_STATUSES = new Set(['RUNNING', 'ACTIVE']);\n\nfunction formatRelative(dateString: string | null | undefined): string {\n if (!dateString) return '';\n const diffMs = Date.now() - new Date(dateString).getTime();\n const diffMin = Math.floor(diffMs / 60000);\n if (diffMin < 1) return 'just now';\n if (diffMin < 60) return `${diffMin}m`;\n const diffHr = Math.floor(diffMin / 60);\n if (diffHr < 24) return `${diffHr}h`;\n return new Date(dateString).toLocaleDateString(undefined, {\n month: 'short',\n day: 'numeric',\n });\n}\n\nexport function SessionsWidget({ workspaceData, isLoading, navigate }: WidgetProps) {\n const sessions = workspaceData?.activeSessions ?? [];\n\n return (\n <WidgetWrapper\n title=\"Active Sessions\"\n icon={<Terminal 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('/vibecontrols/sessions')}\n >\n View all <ArrowRight className=\"ml-1 size-3\" />\n </Button>\n }\n >\n {sessions.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-sessions\"\n title=\"No active sessions\"\n className=\"py-6\"\n />\n ) : (\n <ul className=\"space-y-1.5\">\n {sessions.slice(0, 5).map((s) => (\n <li key={s.id}>\n <button\n type=\"button\"\n onClick={() => navigate(`/vibecontrols/sessions/${s.id}`)}\n className=\"flex w-full items-center justify-between gap-2 rounded-md p-2 text-left transition-colors hover:bg-accent/5\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {s.name || `Session ${s.id.slice(0, 8)}`}\n </p>\n {s.agent?.name ? (\n <p className=\"truncate text-[11px] text-text-secondary\">{s.agent.name}</p>\n ) : null}\n </div>\n <div className=\"flex flex-col items-end gap-1\">\n <Badge\n variant=\"outline\"\n className={`text-[10px] ${STATUS_BADGE[s.status] ?? ''} ${\n LIVE_STATUSES.has(s.status) ? 'motion-safe:animate-vc-glow-pulse' : ''\n }`}\n >\n {s.status.toLowerCase()}\n </Badge>\n {s.startedAt ? (\n <span className=\"text-[10px] text-text-secondary\">\n {formatRelative(s.startedAt)}\n </span>\n ) : null}\n </div>\n </button>\n </li>\n ))}\n </ul>\n )}\n </WidgetWrapper>\n );\n}\n"],"mappings":";;;;;AAKA,IAAM,IAAuC;CAC3C,SAAS;CACT,QAAQ;CACR,UAAU;CACV,SAAS;CACT,OAAO;CACR,EAGK,IAAgB,IAAI,IAAI,CAAC,WAAW,SAAS,CAAC;AAEpD,SAAS,EAAe,GAA+C;AACrE,KAAI,CAAC,EAAY,QAAO;CACxB,IAAM,IAAS,KAAK,KAAK,GAAG,IAAI,KAAK,EAAW,CAAC,SAAS,EACpD,IAAU,KAAK,MAAM,IAAS,IAAM;AAC1C,KAAI,IAAU,EAAG,QAAO;AACxB,KAAI,IAAU,GAAI,QAAO,GAAG,EAAQ;CACpC,IAAM,IAAS,KAAK,MAAM,IAAU,GAAG;AAEvC,QADI,IAAS,KAAW,GAAG,EAAO,KAC3B,IAAI,KAAK,EAAW,CAAC,mBAAmB,KAAA,GAAW;EACxD,OAAO;EACP,KAAK;EACN,CAAC;;AAGJ,SAAgB,EAAe,EAAE,kBAAe,cAAW,eAAyB;CAClF,IAAM,IAAW,GAAe,kBAAkB,EAAE;AAEpD,QACE,kBAAC,GAAD;EACE,OAAM;EACN,MAAM,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA;EACrC,MAAK;EACM;EACX,aAAa,CAAC;EACd,cACE,kBAAC,GAAD;GACE,SAAQ;GACR,MAAK;GACL,WAAU;GACV,eAAe,EAAS,yBAAyB;aAJnD,CAKC,aACU,kBAAC,GAAD,EAAY,WAAU,eAAgB,CAAA,CACxC;;YAGV,EAAS,WAAW,IACnB,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,WAAU;GACV,CAAA,GAEF,kBAAC,MAAD;GAAI,WAAU;aACX,EAAS,MAAM,GAAG,EAAE,CAAC,KAAK,MACzB,kBAAC,MAAD,EAAA,UACE,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAS,0BAA0B,EAAE,KAAK;IACzD,WAAU;cAHZ,CAKE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAE,QAAQ,WAAW,EAAE,GAAG,MAAM,GAAG,EAAE;MACpC,CAAA,EACH,EAAE,OAAO,OACR,kBAAC,KAAD;MAAG,WAAU;gBAA4C,EAAE,MAAM;MAAS,CAAA,GACxE,KACA;QACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MACE,SAAQ;MACR,WAAW,eAAe,EAAa,EAAE,WAAW,GAAG,GACrD,EAAc,IAAI,EAAE,OAAO,GAAG,sCAAsC;gBAGrE,EAAE,OAAO,aAAa;MACjB,CAAA,EACP,EAAE,YACD,kBAAC,QAAD;MAAM,WAAU;gBACb,EAAe,EAAE,UAAU;MACvB,CAAA,GACL,KACA;OACC;OACN,EA9BI,EAAE,GA8BN,CACL;GACC,CAAA;EAEO,CAAA"}
1
+ {"version":3,"file":"SessionsWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/SessionsWidget.tsx"],"sourcesContent":["import { Terminal, ArrowRight } from 'lucide-react';\nimport { Badge, Button, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport type { I18nContextValue } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport { tWithFallback } from '../../../utils/i18n';\nimport type { WidgetProps } from '../types';\n\nconst STATUS_BADGE: Record<string, string> = {\n RUNNING: 'bg-status-success-bg text-status-success-text',\n ACTIVE: 'bg-status-success-bg text-status-success-text',\n STARTING: 'bg-status-warning-bg text-status-warning-text',\n STOPPED: 'bg-text-tertiary/20 text-text-tertiary',\n ERROR: 'bg-status-error-bg text-status-error-text',\n};\n\n// Live statuses get a subtle opacity pulse (reduced-motion-safe, no movement).\nconst LIVE_STATUSES = new Set(['RUNNING', 'ACTIVE']);\n\n/**\n * Badge copy for a session status, resolved through i18n with **literal**\n * keys — a lookup table would force a computed key, which the key extractor\n * (fluidgrids-app/scripts/extract-i18n-keys.ts) cannot see and the seeder can\n * therefore never ship. Unknown statuses keep the previous behaviour of\n * lower-casing the raw enum value. [BOFF-5997]\n */\nfunction resolveStatusLabel(t: I18nContextValue['t'], status: string): string {\n switch (status) {\n case 'RUNNING':\n return tWithFallback(t, 'dashboard.sessions.statusRunning', 'running');\n case 'ACTIVE':\n return tWithFallback(t, 'dashboard.sessions.statusActive', 'active');\n case 'STARTING':\n return tWithFallback(t, 'dashboard.sessions.statusStarting', 'starting');\n case 'STOPPED':\n return tWithFallback(t, 'dashboard.sessions.statusStopped', 'stopped');\n case 'ERROR':\n return tWithFallback(t, 'dashboard.sessions.statusError', 'error');\n default:\n return status.toLowerCase();\n }\n}\n\nfunction formatRelative(t: I18nContextValue['t'], dateString: string | null | undefined): string {\n if (!dateString) return '';\n const diffMs = Date.now() - new Date(dateString).getTime();\n const diffMin = Math.floor(diffMs / 60000);\n if (diffMin < 1) return tWithFallback(t, 'dashboard.sessions.justNow', 'just now');\n if (diffMin < 60)\n return tWithFallback(t, 'dashboard.sessions.minutesAgo', '{count}m', { count: diffMin });\n const diffHr = Math.floor(diffMin / 60);\n if (diffHr < 24)\n return tWithFallback(t, 'dashboard.sessions.hoursAgo', '{count}h', { count: diffHr });\n return new Date(dateString).toLocaleDateString(undefined, {\n month: 'short',\n day: 'numeric',\n });\n}\n\nexport function SessionsWidget({ workspaceData, isLoading, navigate }: WidgetProps) {\n const { t } = useI18n();\n const sessions = workspaceData?.activeSessions ?? [];\n\n return (\n <WidgetWrapper\n title={tWithFallback(t, 'dashboard.sessions.title', 'Active Sessions')}\n icon={<Terminal 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('/vibecontrols/sessions')}\n >\n {tWithFallback(t, 'dashboard.sessions.viewAll', 'View all')}{' '}\n <ArrowRight className=\"ml-1 size-3\" />\n </Button>\n }\n >\n {sessions.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-sessions\"\n title={tWithFallback(t, 'dashboard.sessions.emptyTitle', 'No active sessions')}\n className=\"py-6\"\n />\n ) : (\n <ul className=\"space-y-1.5\">\n {sessions.slice(0, 5).map((s) => (\n <li key={s.id}>\n <button\n type=\"button\"\n onClick={() => navigate(`/vibecontrols/sessions/${s.id}`)}\n className=\"flex w-full items-center justify-between gap-2 rounded-md p-2 text-left transition-colors hover:bg-accent/5\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {s.name ||\n tWithFallback(t, 'dashboard.sessions.unnamedSession', 'Session {id}', {\n id: s.id.slice(0, 8),\n })}\n </p>\n {s.agent?.name ? (\n <p className=\"truncate text-[11px] text-text-secondary\">{s.agent.name}</p>\n ) : null}\n </div>\n <div className=\"flex flex-col items-end gap-1\">\n <Badge\n variant=\"outline\"\n className={`text-[10px] ${STATUS_BADGE[s.status] ?? ''} ${\n LIVE_STATUSES.has(s.status) ? 'motion-safe:animate-vc-glow-pulse' : ''\n }`}\n >\n {resolveStatusLabel(t, s.status)}\n </Badge>\n {s.startedAt ? (\n <span className=\"text-[10px] text-text-secondary\">\n {formatRelative(t, s.startedAt)}\n </span>\n ) : null}\n </div>\n </button>\n </li>\n ))}\n </ul>\n )}\n </WidgetWrapper>\n );\n}\n"],"mappings":";;;;;;;AAQA,IAAM,IAAuC;CAC3C,SAAS;CACT,QAAQ;CACR,UAAU;CACV,SAAS;CACT,OAAO;CACR,EAGK,IAAgB,IAAI,IAAI,CAAC,WAAW,SAAS,CAAC;AASpD,SAAS,EAAmB,GAA0B,GAAwB;AAC5E,SAAQ,GAAR;EACE,KAAK,UACH,QAAO,EAAc,GAAG,oCAAoC,UAAU;EACxE,KAAK,SACH,QAAO,EAAc,GAAG,mCAAmC,SAAS;EACtE,KAAK,WACH,QAAO,EAAc,GAAG,qCAAqC,WAAW;EAC1E,KAAK,UACH,QAAO,EAAc,GAAG,oCAAoC,UAAU;EACxE,KAAK,QACH,QAAO,EAAc,GAAG,kCAAkC,QAAQ;EACpE,QACE,QAAO,EAAO,aAAa;;;AAIjC,SAAS,EAAe,GAA0B,GAA+C;AAC/F,KAAI,CAAC,EAAY,QAAO;CACxB,IAAM,IAAS,KAAK,KAAK,GAAG,IAAI,KAAK,EAAW,CAAC,SAAS,EACpD,IAAU,KAAK,MAAM,IAAS,IAAM;AAC1C,KAAI,IAAU,EAAG,QAAO,EAAc,GAAG,8BAA8B,WAAW;AAClF,KAAI,IAAU,GACZ,QAAO,EAAc,GAAG,iCAAiC,YAAY,EAAE,OAAO,GAAS,CAAC;CAC1F,IAAM,IAAS,KAAK,MAAM,IAAU,GAAG;AAGvC,QAFI,IAAS,KACJ,EAAc,GAAG,+BAA+B,YAAY,EAAE,OAAO,GAAQ,CAAC,GAChF,IAAI,KAAK,EAAW,CAAC,mBAAmB,KAAA,GAAW;EACxD,OAAO;EACP,KAAK;EACN,CAAC;;AAGJ,SAAgB,EAAe,EAAE,kBAAe,cAAW,eAAyB;CAClF,IAAM,EAAE,SAAM,GAAS,EACjB,IAAW,GAAe,kBAAkB,EAAE;AAEpD,QACE,kBAAC,GAAD;EACE,OAAO,EAAc,GAAG,4BAA4B,kBAAkB;EACtE,MAAM,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA;EACrC,MAAK;EACM;EACX,aAAa,CAAC;EACd,cACE,kBAAC,GAAD;GACE,SAAQ;GACR,MAAK;GACL,WAAU;GACV,eAAe,EAAS,yBAAyB;aAJnD;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,qBAAqB;GAC9E,WAAU;GACV,CAAA,GAEF,kBAAC,MAAD;GAAI,WAAU;aACX,EAAS,MAAM,GAAG,EAAE,CAAC,KAAK,MACzB,kBAAC,MAAD,EAAA,UACE,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAS,0BAA0B,EAAE,KAAK;IACzD,WAAU;cAHZ,CAKE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAE,QACD,EAAc,GAAG,qCAAqC,gBAAgB,EACpE,IAAI,EAAE,GAAG,MAAM,GAAG,EAAE,EACrB,CAAC;MACF,CAAA,EACH,EAAE,OAAO,OACR,kBAAC,KAAD;MAAG,WAAU;gBAA4C,EAAE,MAAM;MAAS,CAAA,GACxE,KACA;QACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MACE,SAAQ;MACR,WAAW,eAAe,EAAa,EAAE,WAAW,GAAG,GACrD,EAAc,IAAI,EAAE,OAAO,GAAG,sCAAsC;gBAGrE,EAAmB,GAAG,EAAE,OAAO;MAC1B,CAAA,EACP,EAAE,YACD,kBAAC,QAAD;MAAM,WAAU;gBACb,EAAe,GAAG,EAAE,UAAU;MAC1B,CAAA,GACL,KACA;OACC;OACN,EAjCI,EAAE,GAiCN,CACL;GACC,CAAA;EAEO,CAAA"}
@@ -1,84 +1,82 @@
1
- import { WidgetWrapper as e } from "../WidgetWrapper.js";
2
- import { HardDrive as t } from "lucide-react";
3
- import { jsx as n, jsxs as r } from "react/jsx-runtime";
4
- import { useUpgradePrompt as i } from "@burdenoff/fe-libs/shared/hooks";
1
+ import { tWithFallback as e } from "../../../utils/i18n.js";
2
+ import { WidgetWrapper as t } from "../WidgetWrapper.js";
3
+ import { HardDrive as n } from "lucide-react";
4
+ import { jsx as r, jsxs as i } from "react/jsx-runtime";
5
+ import { useUpgradePrompt as a } from "@burdenoff/fe-libs/shared/hooks";
6
+ import { useI18n as o } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
5
7
  //#region src/pages/dashboard/widgets/StorageWidget.tsx
6
- var a = 50 * 1024 * 1024, o = 5 * 1024 * 1024 * 1024;
7
- function s(e) {
8
- if (e === 0) return "0 B";
9
- let t = 1024, n = [
10
- "B",
11
- "KB",
12
- "MB",
13
- "GB",
14
- "TB"
15
- ], r = Math.floor(Math.log(e) / Math.log(t));
16
- return `${parseFloat((e / t ** +r).toFixed(1))} ${n[r]}`;
17
- }
18
- function c(e, t) {
19
- let n = 1024, r = [
20
- "B",
21
- "KB",
22
- "MB",
23
- "GB",
24
- "TB"
8
+ var s = 50 * 1024 * 1024, c = 5 * 1024 * 1024 * 1024;
9
+ function l(t) {
10
+ return [
11
+ e(t, "dashboard.storage.unitBytes", "B"),
12
+ e(t, "dashboard.storage.unitKilobytes", "KB"),
13
+ e(t, "dashboard.storage.unitMegabytes", "MB"),
14
+ e(t, "dashboard.storage.unitGigabytes", "GB"),
15
+ e(t, "dashboard.storage.unitTerabytes", "TB")
25
16
  ];
26
- if (t <= 0) return s(e);
27
- let i = Math.min(Math.floor(Math.log(t) / Math.log(n)), r.length - 1), a = e / n ** +i;
28
- return `${Number.isInteger(a) ? a.toString() : a.toFixed(1)} ${r[i]}`;
29
17
  }
30
- function l({ workspaceData: l, isLoading: u }) {
31
- let { isFree: d } = i({ resource: "storage" }), f = l?.storageUsage, p = d ? a : o, m = f?.usedBytes ?? 0, h = f?.totalBytes && f.totalBytes > 0 ? f.totalBytes : p, g = h > 0 ? Math.min(m / h * 100, 100) : 0, _ = g > 80 ? "bg-status-error-bg" : g > 60 ? "bg-status-warning-bg" : "bg-status-success-bg";
32
- return /* @__PURE__ */ n(e, {
33
- title: "Storage",
34
- icon: /* @__PURE__ */ n(t, { className: "size-4" }),
18
+ function u(t, n) {
19
+ if (n === 0) return `0 ${e(t, "dashboard.storage.unitBytes", "B")}`;
20
+ let r = 1024, i = l(t), a = Math.floor(Math.log(n) / Math.log(r));
21
+ return `${parseFloat((n / r ** +a).toFixed(1))} ${i[a]}`;
22
+ }
23
+ function d(e, t, n) {
24
+ let r = 1024, i = l(e);
25
+ if (n <= 0) return u(e, t);
26
+ let a = Math.min(Math.floor(Math.log(n) / Math.log(r)), i.length - 1), o = t / r ** +a;
27
+ return `${Number.isInteger(o) ? o.toString() : o.toFixed(1)} ${i[a]}`;
28
+ }
29
+ function f({ workspaceData: l, isLoading: f }) {
30
+ let { t: p } = o(), { isFree: m } = a({ resource: "storage" }), h = l?.storageUsage, g = m ? s : c, _ = h?.usedBytes ?? 0, v = h?.totalBytes && h.totalBytes > 0 ? h.totalBytes : g, y = v > 0 ? Math.min(_ / v * 100, 100) : 0, b = y > 80 ? "bg-status-error-bg" : y > 60 ? "bg-status-warning-bg" : "bg-status-success-bg";
31
+ return /* @__PURE__ */ r(t, {
32
+ title: e(p, "dashboard.storage.title", "Storage"),
33
+ icon: /* @__PURE__ */ r(n, { className: "size-4" }),
35
34
  size: "small",
36
- isLoading: u,
37
- children: /* @__PURE__ */ r("div", {
35
+ isLoading: f,
36
+ children: /* @__PURE__ */ i("div", {
38
37
  className: "space-y-3",
39
- children: [/* @__PURE__ */ r("div", {
38
+ children: [/* @__PURE__ */ i("div", {
40
39
  className: "space-y-1.5",
41
40
  children: [
42
- /* @__PURE__ */ r("div", {
41
+ /* @__PURE__ */ i("div", {
43
42
  className: "flex justify-between text-xs",
44
- children: [/* @__PURE__ */ n("span", {
43
+ children: [/* @__PURE__ */ r("span", {
45
44
  className: "text-text-secondary",
46
- children: "Used"
45
+ children: e(p, "dashboard.storage.used", "Used")
47
46
  }), /* @__PURE__ */ r("span", {
48
47
  className: "font-medium text-text-primary",
49
- children: [
50
- c(m, h),
51
- " / ",
52
- s(h)
53
- ]
48
+ children: e(p, "dashboard.storage.usedOfTotal", "{used} / {total}", {
49
+ used: d(p, _, v),
50
+ total: u(p, v)
51
+ })
54
52
  })]
55
53
  }),
56
- /* @__PURE__ */ n("div", {
54
+ /* @__PURE__ */ r("div", {
57
55
  className: "h-2 w-full bg-bg-canvas rounded-full overflow-hidden",
58
- children: /* @__PURE__ */ n("div", {
59
- className: `h-full rounded-full ${_} transition-all duration-500`,
60
- style: { width: `${g}%` }
56
+ children: /* @__PURE__ */ r("div", {
57
+ className: `h-full rounded-full ${b} transition-all duration-500`,
58
+ style: { width: `${y}%` }
61
59
  })
62
60
  }),
63
- /* @__PURE__ */ n("p", {
61
+ /* @__PURE__ */ r("p", {
64
62
  className: "text-[11px] text-text-secondary text-right",
65
- children: `${g.toFixed(1)}% used`
63
+ children: e(p, "dashboard.storage.percentUsed", "{percent}% used", { percent: y.toFixed(1) })
66
64
  })
67
65
  ]
68
- }), f?.fileCount != null && /* @__PURE__ */ r("div", {
66
+ }), h?.fileCount != null && /* @__PURE__ */ i("div", {
69
67
  className: "flex items-center justify-between text-xs",
70
- children: [/* @__PURE__ */ n("span", {
68
+ children: [/* @__PURE__ */ r("span", {
71
69
  className: "text-text-secondary",
72
- children: "Total files"
73
- }), /* @__PURE__ */ n("span", {
70
+ children: e(p, "dashboard.storage.totalFiles", "Total files")
71
+ }), /* @__PURE__ */ r("span", {
74
72
  className: "font-medium text-text-primary",
75
- children: f.fileCount.toLocaleString()
73
+ children: h.fileCount.toLocaleString()
76
74
  })]
77
75
  })]
78
76
  })
79
77
  });
80
78
  }
81
79
  //#endregion
82
- export { l as StorageWidget };
80
+ export { f as StorageWidget };
83
81
 
84
82
  //# sourceMappingURL=StorageWidget.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"StorageWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/StorageWidget.tsx"],"sourcesContent":["import { HardDrive } from 'lucide-react';\nimport { useUpgradePrompt } from '@burdenoff/fe-libs/shared/hooks';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport type { WidgetProps } from '../types';\n\nconst FREE_TIER_TOTAL_BYTES = 50 * 1024 * 1024; // 50 MB\nconst PAID_TIER_TOTAL_BYTES = 5 * 1024 * 1024 * 1024; // 5 GB\n\nfunction formatBytes(bytes: number): string {\n if (bytes === 0) {\n // For 0 we want to render the unit the user will see for the total\n // (e.g. \"0 MB / 50 MB\"), so let the caller pick a unit explicitly.\n return '0 B';\n }\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;\n}\n\n/**\n * Pick the display unit of the *total* so the \"used / total\" line is\n * consistent (e.g. \"0 MB / 50 MB\" not \"0 B / 50 MB\").\n */\nfunction formatBytesAs(bytes: number, totalBytes: number): string {\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\n if (totalBytes <= 0) return formatBytes(bytes);\n const totalIdx = Math.min(Math.floor(Math.log(totalBytes) / Math.log(k)), sizes.length - 1);\n const value = bytes / Math.pow(k, totalIdx);\n // Render as integer when exact (e.g. 0, 50); else one decimal.\n const rendered = Number.isInteger(value) ? value.toString() : value.toFixed(1);\n return `${rendered} ${sizes[totalIdx]}`;\n}\n\nexport function StorageWidget({ workspaceData, isLoading }: WidgetProps) {\n const { isFree } = useUpgradePrompt({ resource: 'storage' });\n const storage = workspaceData?.storageUsage;\n\n // Fall back to plan-tier defaults when the backend hasn't reported usage\n // yet. Storage is core, so \"metrics unavailable\" is not an acceptable UX.\n const fallbackTotalBytes = isFree ? FREE_TIER_TOTAL_BYTES : PAID_TIER_TOTAL_BYTES;\n const usedBytes = storage?.usedBytes ?? 0;\n const totalBytes =\n storage?.totalBytes && storage.totalBytes > 0 ? storage.totalBytes : fallbackTotalBytes;\n const percentage = totalBytes > 0 ? Math.min((usedBytes / totalBytes) * 100, 100) : 0;\n const barColor =\n percentage > 80\n ? 'bg-status-error-bg'\n : percentage > 60\n ? 'bg-status-warning-bg'\n : 'bg-status-success-bg';\n\n return (\n <WidgetWrapper\n title=\"Storage\"\n icon={<HardDrive className=\"size-4\" />}\n size=\"small\"\n isLoading={isLoading}\n >\n <div className=\"space-y-3\">\n <div className=\"space-y-1.5\">\n <div className=\"flex justify-between text-xs\">\n <span className=\"text-text-secondary\">Used</span>\n <span className=\"font-medium text-text-primary\">\n {formatBytesAs(usedBytes, totalBytes)} / {formatBytes(totalBytes)}\n </span>\n </div>\n <div className=\"h-2 w-full bg-bg-canvas rounded-full overflow-hidden\">\n <div\n className={`h-full rounded-full ${barColor} transition-all duration-500`}\n style={{ width: `${percentage}%` }}\n />\n </div>\n <p className=\"text-[11px] text-text-secondary text-right\">\n {`${percentage.toFixed(1)}% used`}\n </p>\n </div>\n {storage?.fileCount != null && (\n <div className=\"flex items-center justify-between text-xs\">\n <span className=\"text-text-secondary\">Total files</span>\n <span className=\"font-medium text-text-primary\">\n {storage.fileCount.toLocaleString()}\n </span>\n </div>\n )}\n </div>\n </WidgetWrapper>\n );\n}\n"],"mappings":";;;;;AAKA,IAAM,IAAwB,KAAK,OAAO,MACpC,IAAwB,IAAI,OAAO,OAAO;AAEhD,SAAS,EAAY,GAAuB;AAC1C,KAAI,MAAU,EAGZ,QAAO;CAET,IAAM,IAAI,MACJ,IAAQ;EAAC;EAAK;EAAM;EAAM;EAAM;EAAK,EACrC,IAAI,KAAK,MAAM,KAAK,IAAI,EAAM,GAAG,KAAK,IAAI,EAAE,CAAC;AACnD,QAAO,GAAG,YAAY,IAAiB,MAAG,GAAI,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAM;;AAOrE,SAAS,EAAc,GAAe,GAA4B;CAChE,IAAM,IAAI,MACJ,IAAQ;EAAC;EAAK;EAAM;EAAM;EAAM;EAAK;AAC3C,KAAI,KAAc,EAAG,QAAO,EAAY,EAAM;CAC9C,IAAM,IAAW,KAAK,IAAI,KAAK,MAAM,KAAK,IAAI,EAAW,GAAG,KAAK,IAAI,EAAE,CAAC,EAAE,EAAM,SAAS,EAAE,EACrF,IAAQ,IAAiB,MAAG;AAGlC,QAAO,GADU,OAAO,UAAU,EAAM,GAAG,EAAM,UAAU,GAAG,EAAM,QAAQ,EAAE,CAC3D,GAAG,EAAM;;AAG9B,SAAgB,EAAc,EAAE,kBAAe,gBAA0B;CACvE,IAAM,EAAE,cAAW,EAAiB,EAAE,UAAU,WAAW,CAAC,EACtD,IAAU,GAAe,cAIzB,IAAqB,IAAS,IAAwB,GACtD,IAAY,GAAS,aAAa,GAClC,IACJ,GAAS,cAAc,EAAQ,aAAa,IAAI,EAAQ,aAAa,GACjE,IAAa,IAAa,IAAI,KAAK,IAAK,IAAY,IAAc,KAAK,IAAI,GAAG,GAC9E,IACJ,IAAa,KACT,uBACA,IAAa,KACX,yBACA;AAER,QACE,kBAAC,GAAD;EACE,OAAM;EACN,MAAM,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;EACtC,MAAK;EACM;YAEX,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,QAAD;OAAM,WAAU;iBAAsB;OAAW,CAAA,EACjD,kBAAC,QAAD;OAAM,WAAU;iBAAhB;QACG,EAAc,GAAW,EAAW;QAAC;QAAI,EAAY,EAAW;QAC5D;SACH;;KACN,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,OAAD;OACE,WAAW,uBAAuB,EAAS;OAC3C,OAAO,EAAE,OAAO,GAAG,EAAW,IAAI;OAClC,CAAA;MACE,CAAA;KACN,kBAAC,KAAD;MAAG,WAAU;gBACV,GAAG,EAAW,QAAQ,EAAE,CAAC;MACxB,CAAA;KACA;OACL,GAAS,aAAa,QACrB,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,QAAD;KAAM,WAAU;eAAsB;KAAkB,CAAA,EACxD,kBAAC,QAAD;KAAM,WAAU;eACb,EAAQ,UAAU,gBAAgB;KAC9B,CAAA,CACH;MAEJ;;EACQ,CAAA"}
1
+ {"version":3,"file":"StorageWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/StorageWidget.tsx"],"sourcesContent":["import { HardDrive } from 'lucide-react';\nimport { useUpgradePrompt } from '@burdenoff/fe-libs/shared/hooks';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport type { I18nContextValue } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport { tWithFallback } from '../../../utils/i18n';\nimport type { WidgetProps } from '../types';\n\nconst FREE_TIER_TOTAL_BYTES = 50 * 1024 * 1024; // 50 MB\nconst PAID_TIER_TOTAL_BYTES = 5 * 1024 * 1024 * 1024; // 5 GB\n\n/**\n * Byte-size unit symbols, each translated through a **literal** key — an array\n * of keys indexed by magnitude would hand the translator a computed key, which\n * the extractor (fluidgrids-app/scripts/extract-i18n-keys.ts) cannot see and\n * the seeder can therefore never ship. Order is load-bearing: the index is the\n * power of 1024. [BOFF-5997]\n */\nfunction byteUnits(t: I18nContextValue['t']): string[] {\n return [\n tWithFallback(t, 'dashboard.storage.unitBytes', 'B'),\n tWithFallback(t, 'dashboard.storage.unitKilobytes', 'KB'),\n tWithFallback(t, 'dashboard.storage.unitMegabytes', 'MB'),\n tWithFallback(t, 'dashboard.storage.unitGigabytes', 'GB'),\n tWithFallback(t, 'dashboard.storage.unitTerabytes', 'TB'),\n ];\n}\n\nfunction formatBytes(t: I18nContextValue['t'], bytes: number): string {\n if (bytes === 0) {\n // For 0 we want to render the unit the user will see for the total\n // (e.g. \"0 MB / 50 MB\"), so let the caller pick a unit explicitly.\n return `0 ${tWithFallback(t, 'dashboard.storage.unitBytes', 'B')}`;\n }\n const k = 1024;\n const sizes = byteUnits(t);\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;\n}\n\n/**\n * Pick the display unit of the *total* so the \"used / total\" line is\n * consistent (e.g. \"0 MB / 50 MB\" not \"0 B / 50 MB\").\n */\nfunction formatBytesAs(t: I18nContextValue['t'], bytes: number, totalBytes: number): string {\n const k = 1024;\n const sizes = byteUnits(t);\n if (totalBytes <= 0) return formatBytes(t, bytes);\n const totalIdx = Math.min(Math.floor(Math.log(totalBytes) / Math.log(k)), sizes.length - 1);\n const value = bytes / Math.pow(k, totalIdx);\n // Render as integer when exact (e.g. 0, 50); else one decimal.\n const rendered = Number.isInteger(value) ? value.toString() : value.toFixed(1);\n return `${rendered} ${sizes[totalIdx]}`;\n}\n\nexport function StorageWidget({ workspaceData, isLoading }: WidgetProps) {\n const { t } = useI18n();\n const { isFree } = useUpgradePrompt({ resource: 'storage' });\n const storage = workspaceData?.storageUsage;\n\n // Fall back to plan-tier defaults when the backend hasn't reported usage\n // yet. Storage is core, so \"metrics unavailable\" is not an acceptable UX.\n const fallbackTotalBytes = isFree ? FREE_TIER_TOTAL_BYTES : PAID_TIER_TOTAL_BYTES;\n const usedBytes = storage?.usedBytes ?? 0;\n const totalBytes =\n storage?.totalBytes && storage.totalBytes > 0 ? storage.totalBytes : fallbackTotalBytes;\n const percentage = totalBytes > 0 ? Math.min((usedBytes / totalBytes) * 100, 100) : 0;\n const barColor =\n percentage > 80\n ? 'bg-status-error-bg'\n : percentage > 60\n ? 'bg-status-warning-bg'\n : 'bg-status-success-bg';\n\n return (\n <WidgetWrapper\n title={tWithFallback(t, 'dashboard.storage.title', 'Storage')}\n icon={<HardDrive className=\"size-4\" />}\n size=\"small\"\n isLoading={isLoading}\n >\n <div className=\"space-y-3\">\n <div className=\"space-y-1.5\">\n <div className=\"flex justify-between text-xs\">\n <span className=\"text-text-secondary\">\n {tWithFallback(t, 'dashboard.storage.used', 'Used')}\n </span>\n <span className=\"font-medium text-text-primary\">\n {tWithFallback(t, 'dashboard.storage.usedOfTotal', '{used} / {total}', {\n used: formatBytesAs(t, usedBytes, totalBytes),\n total: formatBytes(t, totalBytes),\n })}\n </span>\n </div>\n <div className=\"h-2 w-full bg-bg-canvas rounded-full overflow-hidden\">\n <div\n className={`h-full rounded-full ${barColor} transition-all duration-500`}\n style={{ width: `${percentage}%` }}\n />\n </div>\n <p className=\"text-[11px] text-text-secondary text-right\">\n {tWithFallback(t, 'dashboard.storage.percentUsed', '{percent}% used', {\n percent: percentage.toFixed(1),\n })}\n </p>\n </div>\n {storage?.fileCount != null && (\n <div className=\"flex items-center justify-between text-xs\">\n <span className=\"text-text-secondary\">\n {tWithFallback(t, 'dashboard.storage.totalFiles', 'Total files')}\n </span>\n <span className=\"font-medium text-text-primary\">\n {storage.fileCount.toLocaleString()}\n </span>\n </div>\n )}\n </div>\n </WidgetWrapper>\n );\n}\n"],"mappings":";;;;;;;AAQA,IAAM,IAAwB,KAAK,OAAO,MACpC,IAAwB,IAAI,OAAO,OAAO;AAShD,SAAS,EAAU,GAAoC;AACrD,QAAO;EACL,EAAc,GAAG,+BAA+B,IAAI;EACpD,EAAc,GAAG,mCAAmC,KAAK;EACzD,EAAc,GAAG,mCAAmC,KAAK;EACzD,EAAc,GAAG,mCAAmC,KAAK;EACzD,EAAc,GAAG,mCAAmC,KAAK;EAC1D;;AAGH,SAAS,EAAY,GAA0B,GAAuB;AACpE,KAAI,MAAU,EAGZ,QAAO,KAAK,EAAc,GAAG,+BAA+B,IAAI;CAElE,IAAM,IAAI,MACJ,IAAQ,EAAU,EAAE,EACpB,IAAI,KAAK,MAAM,KAAK,IAAI,EAAM,GAAG,KAAK,IAAI,EAAE,CAAC;AACnD,QAAO,GAAG,YAAY,IAAiB,MAAG,GAAI,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAM;;AAOrE,SAAS,EAAc,GAA0B,GAAe,GAA4B;CAC1F,IAAM,IAAI,MACJ,IAAQ,EAAU,EAAE;AAC1B,KAAI,KAAc,EAAG,QAAO,EAAY,GAAG,EAAM;CACjD,IAAM,IAAW,KAAK,IAAI,KAAK,MAAM,KAAK,IAAI,EAAW,GAAG,KAAK,IAAI,EAAE,CAAC,EAAE,EAAM,SAAS,EAAE,EACrF,IAAQ,IAAiB,MAAG;AAGlC,QAAO,GADU,OAAO,UAAU,EAAM,GAAG,EAAM,UAAU,GAAG,EAAM,QAAQ,EAAE,CAC3D,GAAG,EAAM;;AAG9B,SAAgB,EAAc,EAAE,kBAAe,gBAA0B;CACvE,IAAM,EAAE,SAAM,GAAS,EACjB,EAAE,cAAW,EAAiB,EAAE,UAAU,WAAW,CAAC,EACtD,IAAU,GAAe,cAIzB,IAAqB,IAAS,IAAwB,GACtD,IAAY,GAAS,aAAa,GAClC,IACJ,GAAS,cAAc,EAAQ,aAAa,IAAI,EAAQ,aAAa,GACjE,IAAa,IAAa,IAAI,KAAK,IAAK,IAAY,IAAc,KAAK,IAAI,GAAG,GAC9E,IACJ,IAAa,KACT,uBACA,IAAa,KACX,yBACA;AAER,QACE,kBAAC,GAAD;EACE,OAAO,EAAc,GAAG,2BAA2B,UAAU;EAC7D,MAAM,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;EACtC,MAAK;EACM;YAEX,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAc,GAAG,0BAA0B,OAAO;OAC9C,CAAA,EACP,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAc,GAAG,iCAAiC,oBAAoB;QACrE,MAAM,EAAc,GAAG,GAAW,EAAW;QAC7C,OAAO,EAAY,GAAG,EAAW;QAClC,CAAC;OACG,CAAA,CACH;;KACN,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,OAAD;OACE,WAAW,uBAAuB,EAAS;OAC3C,OAAO,EAAE,OAAO,GAAG,EAAW,IAAI;OAClC,CAAA;MACE,CAAA;KACN,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAc,GAAG,iCAAiC,mBAAmB,EACpE,SAAS,EAAW,QAAQ,EAAE,EAC/B,CAAC;MACA,CAAA;KACA;OACL,GAAS,aAAa,QACrB,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,QAAD;KAAM,WAAU;eACb,EAAc,GAAG,gCAAgC,cAAc;KAC3D,CAAA,EACP,kBAAC,QAAD;KAAM,WAAU;eACb,EAAQ,UAAU,gBAAgB;KAC9B,CAAA,CACH;MAEJ;;EACQ,CAAA"}
@@ -1,9 +1,11 @@
1
- import { WidgetWrapper as e } from "../WidgetWrapper.js";
2
- import { ArrowRight as t, Tags as n } from "lucide-react";
3
- import { jsx as r, jsxs as i } from "react/jsx-runtime";
4
- import { Badge as a, Button as o, IllustratedEmptyState as s } 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, Tags as r } from "lucide-react";
4
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
5
+ import { Badge as o, Button as s, IllustratedEmptyState as c } from "@burdenoff/fe-libs/ui";
6
+ import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
5
7
  //#region src/pages/dashboard/widgets/TagCloudWidget.tsx
6
- var c = [
8
+ var u = [
7
9
  "bg-status-info-bg-subtle text-status-info-text border-border-default",
8
10
  "bg-status-success-bg-subtle text-status-success-text border-border-default",
9
11
  "bg-accent-purple-subtle text-accent-purple border-border-default",
@@ -13,48 +15,44 @@ var c = [
13
15
  "bg-accent-orange-subtle text-accent-orange border-border-default",
14
16
  "bg-accent-rose-subtle text-accent-rose border-border-default"
15
17
  ];
16
- function l({ workspaceData: l, isLoading: u, navigate: d }) {
17
- let f = l?.tags.items ?? [], p = l?.tags.total ?? 0;
18
- return /* @__PURE__ */ r(e, {
19
- title: "Tags",
20
- icon: /* @__PURE__ */ r(n, { className: "size-4" }),
18
+ function d({ workspaceData: d, isLoading: f, navigate: p }) {
19
+ let { t: m } = l(), h = d?.tags.items ?? [], g = d?.tags.total ?? 0;
20
+ return /* @__PURE__ */ i(t, {
21
+ title: e(m, "dashboard.tagCloud.title", "Tags"),
22
+ icon: /* @__PURE__ */ i(r, { className: "size-4" }),
21
23
  size: "small",
22
- isLoading: u,
23
- unavailable: !l,
24
- headerAction: /* @__PURE__ */ r(o, {
24
+ isLoading: f,
25
+ unavailable: !d,
26
+ headerAction: /* @__PURE__ */ i(s, {
25
27
  variant: "ghost",
26
28
  size: "sm",
27
29
  className: "text-xs",
28
- onClick: () => d("/tags"),
29
- children: /* @__PURE__ */ r(t, { className: "size-3" })
30
+ onClick: () => p("/tags"),
31
+ children: /* @__PURE__ */ i(n, { className: "size-3" })
30
32
  }),
31
- children: f.length === 0 ? /* @__PURE__ */ r(s, {
33
+ children: h.length === 0 ? /* @__PURE__ */ i(c, {
32
34
  illustration: "empty-generic",
33
- title: "No tags created yet",
34
- description: "Tags help you label and filter projects and content across the workspace.",
35
+ title: e(m, "dashboard.tagCloud.emptyTitle", "No tags created yet"),
36
+ description: e(m, "dashboard.tagCloud.emptyDescription", "Tags help you label and filter projects and content across the workspace."),
35
37
  className: "py-4"
36
- }) : /* @__PURE__ */ i("div", {
38
+ }) : /* @__PURE__ */ a("div", {
37
39
  className: "space-y-2",
38
- children: [/* @__PURE__ */ r("div", {
40
+ children: [/* @__PURE__ */ i("div", {
39
41
  className: "flex flex-wrap gap-1.5",
40
- children: f.slice(0, 12).map((e, t) => /* @__PURE__ */ r(a, {
42
+ children: h.slice(0, 12).map((e, t) => /* @__PURE__ */ i(o, {
41
43
  variant: "outline",
42
- className: `text-[11px] cursor-pointer hover:opacity-80 transition-opacity ${c[t % c.length]}`,
43
- onClick: () => d("/tags"),
44
+ className: `text-[11px] cursor-pointer hover:opacity-80 transition-opacity ${u[t % u.length]}`,
45
+ onClick: () => p("/tags"),
44
46
  children: e.label
45
47
  }, e.id))
46
- }), p > 12 && /* @__PURE__ */ i("p", {
48
+ }), g > 12 && /* @__PURE__ */ i("p", {
47
49
  className: "text-[11px] text-text-secondary text-center",
48
- children: [
49
- "+",
50
- p - 12,
51
- " more tags"
52
- ]
50
+ children: e(m, "dashboard.tagCloud.more", "+{count} more tags", { count: g - 12 })
53
51
  })]
54
52
  })
55
53
  });
56
54
  }
57
55
  //#endregion
58
- export { l as TagCloudWidget };
56
+ export { d as TagCloudWidget };
59
57
 
60
58
  //# sourceMappingURL=TagCloudWidget.js.map