@burdenoff/microfe-timecampus 2026.703.1 → 2026.703.2

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 (85) hide show
  1. package/dist/generated/wspace-types.js +28 -0
  2. package/dist/generated/wspace-types.js.map +1 -0
  3. package/dist/index.js +4 -4
  4. package/dist/timecampus/components/Modal.js +65 -0
  5. package/dist/timecampus/components/Modal.js.map +1 -0
  6. package/dist/timecampus/components/StatusBadge.js +28 -0
  7. package/dist/timecampus/components/StatusBadge.js.map +1 -0
  8. package/dist/timecampus/components/Toast.js +87 -0
  9. package/dist/timecampus/components/Toast.js.map +1 -0
  10. package/dist/timecampus/index.js +1 -1
  11. package/dist/timecampus/pages/HomePage.js +143 -135
  12. package/dist/timecampus/pages/HomePage.js.map +1 -1
  13. package/dist/timecampus/pages/applets/AppletStorePage.js +14 -19
  14. package/dist/timecampus/pages/applets/AppletStorePage.js.map +1 -1
  15. package/dist/timecampus/pages/applets/InstalledAppletsPage.js +49 -37
  16. package/dist/timecampus/pages/applets/InstalledAppletsPage.js.map +1 -1
  17. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +315 -87
  18. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
  19. package/dist/timecampus/pages/channels/ChannelsPage.js +340 -46
  20. package/dist/timecampus/pages/channels/ChannelsPage.js.map +1 -1
  21. package/dist/timecampus/pages/client-portal/ClientPortalPage.js +366 -118
  22. package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
  23. package/dist/timecampus/pages/dashboard/ReportsPage.js +70 -37
  24. package/dist/timecampus/pages/dashboard/ReportsPage.js.map +1 -1
  25. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +366 -60
  26. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js.map +1 -1
  27. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +461 -87
  28. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
  29. package/dist/timecampus/pages/geofencing/GeofencingPage.js +314 -55
  30. package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
  31. package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js +30 -27
  32. package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js.map +1 -1
  33. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +269 -74
  34. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
  35. package/dist/timecampus/pages/meetings/ActionItemFormModal.js +127 -0
  36. package/dist/timecampus/pages/meetings/ActionItemFormModal.js.map +1 -0
  37. package/dist/timecampus/pages/meetings/MeetingCreatePage.js +62 -52
  38. package/dist/timecampus/pages/meetings/MeetingCreatePage.js.map +1 -1
  39. package/dist/timecampus/pages/meetings/MeetingDetailPage.js +361 -74
  40. package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
  41. package/dist/timecampus/pages/meetings/MeetingFormModal.js +156 -0
  42. package/dist/timecampus/pages/meetings/MeetingFormModal.js.map +1 -0
  43. package/dist/timecampus/pages/meetings/MeetingsPage.js +175 -74
  44. package/dist/timecampus/pages/meetings/MeetingsPage.js.map +1 -1
  45. package/dist/timecampus/pages/meetings/ParticipantFormModal.js +105 -0
  46. package/dist/timecampus/pages/meetings/ParticipantFormModal.js.map +1 -0
  47. package/dist/timecampus/pages/pomodoro/PomodoroPage.js +307 -67
  48. package/dist/timecampus/pages/pomodoro/PomodoroPage.js.map +1 -1
  49. package/dist/timecampus/pages/projects/ProjectCreatePage.js +51 -48
  50. package/dist/timecampus/pages/projects/ProjectCreatePage.js.map +1 -1
  51. package/dist/timecampus/pages/projects/ProjectDetailPage.js +248 -51
  52. package/dist/timecampus/pages/projects/ProjectDetailPage.js.map +1 -1
  53. package/dist/timecampus/pages/projects/ProjectsPage.js +354 -87
  54. package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
  55. package/dist/timecampus/pages/projects/TasksPage.js +367 -65
  56. package/dist/timecampus/pages/projects/TasksPage.js.map +1 -1
  57. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +674 -104
  58. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
  59. package/dist/timecampus/pages/team/TeamAnalyticsPage.js +46 -34
  60. package/dist/timecampus/pages/team/TeamAnalyticsPage.js.map +1 -1
  61. package/dist/timecampus/pages/team/TeamMembersPage.js +93 -69
  62. package/dist/timecampus/pages/team/TeamMembersPage.js.map +1 -1
  63. package/dist/timecampus/pages/team/TeamPage.js +4 -3
  64. package/dist/timecampus/pages/team/TeamPage.js.map +1 -1
  65. package/dist/timecampus/pages/templates/TemplateDetailPage.js +72 -53
  66. package/dist/timecampus/pages/templates/TemplateDetailPage.js.map +1 -1
  67. package/dist/timecampus/pages/templates/TemplatesPage.js +40 -35
  68. package/dist/timecampus/pages/templates/TemplatesPage.js.map +1 -1
  69. package/dist/timecampus/pages/time/TimeEntriesPage.js +484 -97
  70. package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
  71. package/dist/timecampus/pages/time/TimeTrackingPage.js +210 -99
  72. package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
  73. package/dist/timecampus/pages/time/TimerPage.js +229 -97
  74. package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
  75. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +315 -110
  76. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
  77. package/dist/timecampus/pages/widgets/WidgetDetailPage.js +158 -76
  78. package/dist/timecampus/pages/widgets/WidgetDetailPage.js.map +1 -1
  79. package/dist/timecampus/pages/widgets/WidgetsPage.js +181 -52
  80. package/dist/timecampus/pages/widgets/WidgetsPage.js.map +1 -1
  81. package/dist/timecampus/pages/workflows/WorkflowsPage.js +212 -61
  82. package/dist/timecampus/pages/workflows/WorkflowsPage.js.map +1 -1
  83. package/dist/timecampus/store/timecampusStore.js +13 -1
  84. package/dist/timecampus/store/timecampusStore.js.map +1 -1
  85. package/package.json +1 -1
@@ -1,107 +1,189 @@
1
- import { useTimeCampus as e, useTimeCampusNavigate as t } from "../../providers/TimeCampusProvider.js";
2
- import { LoadingSkeleton as n } from "../../components/LoadingSkeleton.js";
3
- import { useGetConfigurationQuery as r } from "../../../generated/wspace-operations.js";
4
- import { PageLayout as i } from "../../components/PageLayout.js";
5
- import { ArrowLeft as a, Download as o, LayoutGrid as s, Star as c } from "lucide-react";
6
- import { useParams as l } from "react-router-dom";
7
- import { jsx as u, jsxs as d } from "react/jsx-runtime";
8
- import { GlassCard as f, NextSteps as p } from "@burdenoff/fe-libs/ui";
1
+ import { useTimeCampusNavigate as e } from "../../providers/TimeCampusProvider.js";
2
+ import { LoadingSkeleton as t } from "../../components/LoadingSkeleton.js";
3
+ import { useDeleteConfigurationMutation as n, useGetConfigurationQuery as r, useUpdateConfigurationMutation as i } from "../../../generated/wspace-operations.js";
4
+ import { PageLayout as a } from "../../components/PageLayout.js";
5
+ import { useEffect as o, useState as s } from "react";
6
+ import { ArrowLeft as c, LayoutGrid as l, Save as u, Trash2 as d } from "lucide-react";
7
+ import { useParams as f } from "react-router-dom";
8
+ import { jsx as p, jsxs as m } from "react/jsx-runtime";
9
+ import { GlassCard as h, NextSteps as g } from "@burdenoff/fe-libs/ui";
9
10
  //#region src/timecampus/pages/widgets/WidgetDetailPage.tsx
10
- function m() {
11
- let { id: m } = l(), { workspaceId: h = "" } = e(), g = t(), { data: _, loading: v, error: y } = r({
12
- variables: { id: m ?? "" },
13
- skip: !m
14
- }), b = _?.configuration, x = b?.value ?? {}, S = x.name ?? b?.key?.replace("widget.", "") ?? "Widget", C = b?.description ?? x.description ?? "A configurable dashboard widget for time tracking.", w = x.rating ?? 0, T = x.downloads ?? 0;
15
- return v ? /* @__PURE__ */ u(i, {
11
+ var _ = [
12
+ {
13
+ value: "timer",
14
+ label: "Active timer"
15
+ },
16
+ {
17
+ value: "project-progress",
18
+ label: "Project progress"
19
+ },
20
+ {
21
+ value: "focus-score",
22
+ label: "Focus score"
23
+ },
24
+ {
25
+ value: "team-stats",
26
+ label: "Team stats"
27
+ },
28
+ {
29
+ value: "billable-hours",
30
+ label: "Billable hours"
31
+ },
32
+ {
33
+ value: "recent-entries",
34
+ label: "Recent time entries"
35
+ }
36
+ ];
37
+ function v() {
38
+ let { id: v } = f(), y = e(), { data: b, loading: x, error: S } = r({
39
+ variables: { id: v ?? "" },
40
+ skip: !v
41
+ }), [C, { loading: w, error: T }] = i(), [E, { loading: D }] = n(), O = b?.configuration, [k, A] = s(""), [j, M] = s(_[0].value), [N, P] = s("");
42
+ o(() => {
43
+ if (O) {
44
+ let e = O.value ?? {};
45
+ A(e.name ?? O.key.replace("widget.", "")), M(e.type ?? _[0].value), P(e.description ?? O.description ?? "");
46
+ }
47
+ }, [O]);
48
+ let F = async () => {
49
+ if (!O || !k.trim()) return;
50
+ let e = O.value ?? {}, t = N.trim();
51
+ await C({ variables: {
52
+ id: O.id,
53
+ input: {
54
+ value: {
55
+ ...e,
56
+ name: k.trim(),
57
+ type: j,
58
+ description: t || void 0
59
+ },
60
+ description: t || void 0
61
+ }
62
+ } }), y("/widgets");
63
+ };
64
+ return x ? /* @__PURE__ */ p(a, {
16
65
  title: "Widget Details",
17
66
  description: "Loading...",
18
- children: /* @__PURE__ */ u(n, { variant: "detail" })
19
- }) : y || !b ? /* @__PURE__ */ u(i, {
67
+ children: /* @__PURE__ */ p(t, { variant: "detail" })
68
+ }) : S || !O ? /* @__PURE__ */ p(a, {
20
69
  title: "Widget Details",
21
- description: `Widget ID: ${m}`,
22
- actions: /* @__PURE__ */ d("button", {
70
+ description: `Widget ID: ${v}`,
71
+ actions: /* @__PURE__ */ m("button", {
72
+ type: "button",
73
+ onClick: () => y("/widgets"),
23
74
  className: "inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
24
- children: [/* @__PURE__ */ u(a, { className: "h-4 w-4" }), "Back to Widgets"]
75
+ children: [/* @__PURE__ */ p(c, { className: "h-4 w-4" }), "Back to Widgets"]
25
76
  }),
26
- children: /* @__PURE__ */ u("div", {
77
+ children: /* @__PURE__ */ p("div", {
27
78
  className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
28
- children: y ? "Failed to load widget details. Please try again later." : "Widget configuration not found."
79
+ children: S ? "Failed to load widget details. Please try again later." : "Widget configuration not found."
29
80
  })
30
- }) : /* @__PURE__ */ u(i, {
81
+ }) : /* @__PURE__ */ p(a, {
31
82
  title: "Widget Details",
32
- description: `Widget ID: ${m}`,
33
- purpose: "Preview what a widget shows and how it behaves before adding it to your dashboard, so you only install the panels that genuinely help you keep an eye on your time.",
34
- actions: /* @__PURE__ */ d("button", {
83
+ description: `Widget ID: ${v}`,
84
+ purpose: "Edit what this widget shows and how it's labelled, then save it back to your workspace — or remove it if you no longer need it on your dashboard.",
85
+ actions: /* @__PURE__ */ m("button", {
86
+ type: "button",
87
+ onClick: () => y("/widgets"),
35
88
  className: "inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
36
- children: [/* @__PURE__ */ u(a, { className: "h-4 w-4" }), "Back to Widgets"]
89
+ children: [/* @__PURE__ */ p(c, { className: "h-4 w-4" }), "Back to Widgets"]
37
90
  }),
38
- children: /* @__PURE__ */ d("div", {
91
+ children: /* @__PURE__ */ m("div", {
39
92
  className: "mx-auto max-w-2xl",
40
- children: [/* @__PURE__ */ u(p, {
93
+ children: [/* @__PURE__ */ p(g, {
41
94
  storageKey: "timecampus-widget-detail",
42
95
  className: "mb-6",
43
96
  steps: [{
44
- id: "install",
45
- label: "Install this widget",
46
- description: "Add it to your dashboard.",
47
- onClick: () => {}
97
+ id: "save",
98
+ label: "Save your changes",
99
+ description: "Update this widget's name, metric or description.",
100
+ onClick: () => void F()
48
101
  }, {
49
102
  id: "browse",
50
103
  label: "Browse other widgets",
51
- description: "Find more panels for your dashboard.",
52
- onClick: () => g("/widgets")
104
+ description: "Manage the rest of your dashboard widgets.",
105
+ onClick: () => y("/widgets")
53
106
  }]
54
- }), /* @__PURE__ */ d(f, {
107
+ }), /* @__PURE__ */ m(h, {
55
108
  treatment: "glass",
56
109
  glow: !0,
57
110
  className: "p-6",
58
111
  children: [
59
- /* @__PURE__ */ d("div", {
112
+ /* @__PURE__ */ m("div", {
60
113
  className: "flex items-start gap-4",
61
- children: [/* @__PURE__ */ u("div", {
114
+ children: [/* @__PURE__ */ p("div", {
62
115
  className: "flex h-16 w-16 items-center justify-center rounded-xl bg-action-primary-bg/10",
63
- children: /* @__PURE__ */ u(s, { className: "h-8 w-8 text-primary" })
64
- }), /* @__PURE__ */ d("div", {
116
+ children: /* @__PURE__ */ p(l, { className: "h-8 w-8 text-primary" })
117
+ }), /* @__PURE__ */ m("div", {
65
118
  className: "flex-1",
66
- children: [
67
- /* @__PURE__ */ u("h2", {
68
- className: "text-lg font-semibold text-text-primary",
69
- children: S
70
- }),
71
- /* @__PURE__ */ u("p", {
72
- className: "text-sm text-text-secondary",
73
- children: C
74
- }),
75
- /* @__PURE__ */ d("div", {
76
- className: "mt-2 flex items-center gap-3 text-sm text-text-secondary",
77
- children: [
78
- /* @__PURE__ */ d("div", {
79
- className: "flex items-center gap-1",
80
- children: [/* @__PURE__ */ u(c, { className: "h-4 w-4 fill-amber-400 text-status-warning-text" }), /* @__PURE__ */ u("span", { children: w })]
81
- }),
82
- /* @__PURE__ */ u("span", { children: "·" }),
83
- /* @__PURE__ */ d("div", {
84
- className: "flex items-center gap-1",
85
- children: [/* @__PURE__ */ u(o, { className: "h-4 w-4" }), /* @__PURE__ */ d("span", { children: [T.toLocaleString(), " installs"] })]
86
- })
87
- ]
88
- })
89
- ]
119
+ children: [/* @__PURE__ */ p("h2", {
120
+ className: "text-lg font-semibold text-text-primary",
121
+ children: k || "Untitled widget"
122
+ }), /* @__PURE__ */ p("p", {
123
+ className: "text-sm text-text-secondary",
124
+ children: "This widget is saved to your workspace and can be shown on your dashboard."
125
+ })]
90
126
  })]
91
127
  }),
92
- /* @__PURE__ */ u("div", {
93
- className: "mt-6 flex h-48 items-center justify-center rounded-lg border border-dashed border-border-subtle",
94
- children: /* @__PURE__ */ u("p", {
95
- className: "text-sm text-text-secondary",
96
- children: "Widget preview will render here"
97
- })
128
+ T && /* @__PURE__ */ p("div", {
129
+ className: "mt-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text",
130
+ children: "Failed to save widget. Please try again."
98
131
  }),
99
- /* @__PURE__ */ u("div", {
100
- className: "mt-4 flex justify-end",
101
- children: /* @__PURE__ */ u("button", {
102
- className: "rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
103
- children: "Install Widget"
104
- })
132
+ /* @__PURE__ */ m("div", {
133
+ className: "mt-6 space-y-4",
134
+ children: [
135
+ /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
136
+ className: "block text-sm font-medium text-text-primary",
137
+ children: "Widget Name"
138
+ }), /* @__PURE__ */ p("input", {
139
+ type: "text",
140
+ value: k,
141
+ onChange: (e) => A(e.target.value),
142
+ className: "mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary"
143
+ })] }),
144
+ /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
145
+ className: "block text-sm font-medium text-text-primary",
146
+ children: "Metric"
147
+ }), /* @__PURE__ */ p("select", {
148
+ value: j,
149
+ onChange: (e) => M(e.target.value),
150
+ className: "mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary",
151
+ children: _.map((e) => /* @__PURE__ */ p("option", {
152
+ value: e.value,
153
+ children: e.label
154
+ }, e.value))
155
+ })] }),
156
+ /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ m("label", {
157
+ className: "block text-sm font-medium text-text-primary",
158
+ children: ["Description", /* @__PURE__ */ p("span", {
159
+ className: "ml-1 font-normal text-text-secondary",
160
+ children: "(optional)"
161
+ })]
162
+ }), /* @__PURE__ */ p("textarea", {
163
+ rows: 3,
164
+ value: N,
165
+ onChange: (e) => P(e.target.value),
166
+ className: "mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary"
167
+ })] })
168
+ ]
169
+ }),
170
+ /* @__PURE__ */ m("div", {
171
+ className: "mt-6 flex items-center justify-between",
172
+ children: [/* @__PURE__ */ m("button", {
173
+ type: "button",
174
+ onClick: async () => {
175
+ O && (await E({ variables: { id: O.id } }), y("/widgets"));
176
+ },
177
+ disabled: D,
178
+ className: "inline-flex items-center gap-1.5 rounded-md border border-status-error-bg/40 px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg disabled:opacity-50",
179
+ children: [/* @__PURE__ */ p(d, { className: "h-4 w-4" }), D ? "Deleting..." : "Delete"]
180
+ }), /* @__PURE__ */ m("button", {
181
+ type: "button",
182
+ onClick: F,
183
+ disabled: w || !k.trim(),
184
+ className: "inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50",
185
+ children: [/* @__PURE__ */ p(u, { className: "h-4 w-4" }), w ? "Saving..." : "Save Changes"]
186
+ })]
105
187
  })
106
188
  ]
107
189
  })]
@@ -109,6 +191,6 @@ function m() {
109
191
  });
110
192
  }
111
193
  //#endregion
112
- export { m as WidgetDetailPage };
194
+ export { v as WidgetDetailPage };
113
195
 
114
196
  //# sourceMappingURL=WidgetDetailPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"WidgetDetailPage.js","names":[],"sources":["../../../../src/timecampus/pages/widgets/WidgetDetailPage.tsx"],"sourcesContent":["import { useParams } from \"react-router-dom\";\nimport { LayoutGrid, ArrowLeft, Star, Download } from \"lucide-react\";\nimport { GlassCard, NextSteps } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport {\n useTimeCampus,\n useTimeCampusNavigate,\n} from \"../../providers/TimeCampusProvider\";\nimport { useGetConfigurationQuery } from \"@/generated/wspace-operations\";\n\nexport function WidgetDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { workspaceId = \"\" } = useTimeCampus();\n const navigate = useTimeCampusNavigate();\n\n const { data, loading, error } = useGetConfigurationQuery({\n variables: { id: id ?? \"\" },\n skip: !id,\n });\n\n const config = data?.configuration;\n const val = (config?.value as Record<string, unknown>) ?? {};\n const name =\n (val.name as string) ?? config?.key?.replace(\"widget.\", \"\") ?? \"Widget\";\n const description =\n config?.description ??\n (val.description as string) ??\n \"A configurable dashboard widget for time tracking.\";\n const rating = (val.rating as number) ?? 0;\n const installs = (val.downloads as number) ?? 0;\n\n if (loading) {\n return (\n <PageLayout title=\"Widget Details\" description=\"Loading...\">\n <LoadingSkeleton variant=\"detail\" />\n </PageLayout>\n );\n }\n\n if (error || !config) {\n return (\n <PageLayout\n title=\"Widget Details\"\n description={`Widget ID: ${id}`}\n actions={\n <button className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\">\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Widgets\n </button>\n }\n >\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n {error\n ? \"Failed to load widget details. Please try again later.\"\n : \"Widget configuration not found.\"}\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Widget Details\"\n description={`Widget ID: ${id}`}\n purpose=\"Preview what a widget shows and how it behaves before adding it to your dashboard, so you only install the panels that genuinely help you keep an eye on your time.\"\n actions={\n <button className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\">\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Widgets\n </button>\n }\n >\n <div className=\"mx-auto max-w-2xl\">\n <NextSteps\n storageKey=\"timecampus-widget-detail\"\n className=\"mb-6\"\n steps={[\n {\n id: \"install\",\n label: \"Install this widget\",\n description: \"Add it to your dashboard.\",\n onClick: () => {},\n },\n {\n id: \"browse\",\n label: \"Browse other widgets\",\n description: \"Find more panels for your dashboard.\",\n onClick: () => navigate(\"/widgets\"),\n },\n ]}\n />\n <GlassCard treatment=\"glass\" glow className=\"p-6\">\n <div className=\"flex items-start gap-4\">\n <div className=\"flex h-16 w-16 items-center justify-center rounded-xl bg-action-primary-bg/10\">\n <LayoutGrid className=\"h-8 w-8 text-primary\" />\n </div>\n <div className=\"flex-1\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {name}\n </h2>\n <p className=\"text-sm text-text-secondary\">{description}</p>\n <div className=\"mt-2 flex items-center gap-3 text-sm text-text-secondary\">\n <div className=\"flex items-center gap-1\">\n <Star className=\"h-4 w-4 fill-amber-400 text-status-warning-text\" />\n <span>{rating}</span>\n </div>\n <span>&middot;</span>\n <div className=\"flex items-center gap-1\">\n <Download className=\"h-4 w-4\" />\n <span>{installs.toLocaleString()} installs</span>\n </div>\n </div>\n </div>\n </div>\n <div className=\"mt-6 flex h-48 items-center justify-center rounded-lg border border-dashed border-border-subtle\">\n <p className=\"text-sm text-text-secondary\">\n Widget preview will render here\n </p>\n </div>\n <div className=\"mt-4 flex justify-end\">\n <button className=\"rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\">\n Install Widget\n </button>\n </div>\n </GlassCard>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;AAWA,SAAgB,IAAmB;CACjC,IAAM,EAAE,UAAO,GAA2B,EACpC,EAAE,iBAAc,OAAO,GAAe,EACtC,IAAW,GAAuB,EAElC,EAAE,SAAM,YAAS,aAAU,EAAyB;EACxD,WAAW,EAAE,IAAI,KAAM,IAAI;EAC3B,MAAM,CAAC;EACR,CAAC,EAEI,IAAS,GAAM,eACf,IAAO,GAAQ,SAAqC,EAAE,EACtD,IACH,EAAI,QAAmB,GAAQ,KAAK,QAAQ,WAAW,GAAG,IAAI,UAC3D,IACJ,GAAQ,eACP,EAAI,eACL,sDACI,IAAU,EAAI,UAAqB,GACnC,IAAY,EAAI,aAAwB;AA+B9C,QA7BI,IAEA,kBAAC,GAAD;EAAY,OAAM;EAAiB,aAAY;YAC7C,kBAAC,GAAD,EAAiB,SAAQ,UAAW,CAAA;EACzB,CAAA,GAIb,KAAS,CAAC,IAEV,kBAAC,GAAD;EACE,OAAM;EACN,aAAa,cAAc;EAC3B,SACE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,kBAE1B;;YAGX,kBAAC,OAAD;GAAK,WAAU;aACZ,IACG,2DACA;GACA,CAAA;EACK,CAAA,GAKf,kBAAC,GAAD;EACE,OAAM;EACN,aAAa,cAAc;EAC3B,SAAQ;EACR,SACE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,kBAE1B;;YAGX,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD;IACE,YAAW;IACX,WAAU;IACV,OAAO,CACL;KACE,IAAI;KACJ,OAAO;KACP,aAAa;KACb,eAAe;KAChB,EACD;KACE,IAAI;KACJ,OAAO;KACP,aAAa;KACb,eAAe,EAAS,WAAW;KACpC,CACF;IACD,CAAA,EACF,kBAAC,GAAD;IAAW,WAAU;IAAQ,MAAA;IAAK,WAAU;cAA5C;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAY,WAAU,wBAAyB,CAAA;OAC3C,CAAA,EACN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,MAAD;SAAI,WAAU;mBACX;SACE,CAAA;QACL,kBAAC,KAAD;SAAG,WAAU;mBAA+B;SAAgB,CAAA;QAC5D,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD,EAAM,WAAU,mDAAoD,CAAA,EACpE,kBAAC,QAAD,EAAA,UAAO,GAAc,CAAA,CACjB;;UACN,kBAAC,QAAD,EAAA,UAAM,KAAe,CAAA;UACrB,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA,EAChC,kBAAC,QAAD,EAAA,UAAA,CAAO,EAAS,gBAAgB,EAAC,YAAgB,EAAA,CAAA,CAC7C;;UACF;;QACF;SACF;;KACN,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,KAAD;OAAG,WAAU;iBAA8B;OAEvC,CAAA;MACA,CAAA;KACN,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,UAAD;OAAQ,WAAU;iBAAuH;OAEhI,CAAA;MACL,CAAA;KACI;MACR;;EACK,CAAA"}
1
+ {"version":3,"file":"WidgetDetailPage.js","names":[],"sources":["../../../../src/timecampus/pages/widgets/WidgetDetailPage.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { useParams } from \"react-router-dom\";\nimport { LayoutGrid, ArrowLeft, Save, Trash2 } from \"lucide-react\";\nimport { GlassCard, NextSteps } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { useTimeCampusNavigate } from \"../../providers/TimeCampusProvider\";\nimport {\n useGetConfigurationQuery,\n useUpdateConfigurationMutation,\n useDeleteConfigurationMutation,\n} from \"@/generated/wspace-operations\";\n\nconst WIDGET_TYPES = [\n { value: \"timer\", label: \"Active timer\" },\n { value: \"project-progress\", label: \"Project progress\" },\n { value: \"focus-score\", label: \"Focus score\" },\n { value: \"team-stats\", label: \"Team stats\" },\n { value: \"billable-hours\", label: \"Billable hours\" },\n { value: \"recent-entries\", label: \"Recent time entries\" },\n];\n\nexport function WidgetDetailPage() {\n const { id } = useParams<{ id: string }>();\n const navigate = useTimeCampusNavigate();\n\n const { data, loading, error } = useGetConfigurationQuery({\n variables: { id: id ?? \"\" },\n skip: !id,\n });\n\n const [updateConfiguration, { loading: saving, error: saveError }] =\n useUpdateConfigurationMutation();\n const [deleteConfiguration, { loading: deleting }] =\n useDeleteConfigurationMutation();\n\n const config = data?.configuration;\n\n const [name, setName] = useState(\"\");\n const [type, setType] = useState(WIDGET_TYPES[0].value);\n const [description, setDescription] = useState(\"\");\n\n useEffect(() => {\n if (config) {\n const val = (config.value as Record<string, unknown>) ?? {};\n setName((val.name as string) ?? config.key.replace(\"widget.\", \"\"));\n setType((val.type as string) ?? WIDGET_TYPES[0].value);\n setDescription((val.description as string) ?? config.description ?? \"\");\n }\n }, [config]);\n\n const handleSave = async () => {\n if (!config || !name.trim()) return;\n const val = (config.value as Record<string, unknown>) ?? {};\n const trimmedDescription = description.trim();\n await updateConfiguration({\n variables: {\n id: config.id,\n input: {\n value: {\n ...val,\n name: name.trim(),\n type,\n description: trimmedDescription || undefined,\n },\n description: trimmedDescription || undefined,\n },\n },\n });\n navigate(\"/widgets\");\n };\n\n const handleDelete = async () => {\n if (!config) return;\n await deleteConfiguration({ variables: { id: config.id } });\n navigate(\"/widgets\");\n };\n\n if (loading) {\n return (\n <PageLayout title=\"Widget Details\" description=\"Loading...\">\n <LoadingSkeleton variant=\"detail\" />\n </PageLayout>\n );\n }\n\n if (error || !config) {\n return (\n <PageLayout\n title=\"Widget Details\"\n description={`Widget ID: ${id}`}\n actions={\n <button\n type=\"button\"\n onClick={() => navigate(\"/widgets\")}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Widgets\n </button>\n }\n >\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n {error\n ? \"Failed to load widget details. Please try again later.\"\n : \"Widget configuration not found.\"}\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Widget Details\"\n description={`Widget ID: ${id}`}\n purpose=\"Edit what this widget shows and how it's labelled, then save it back to your workspace — or remove it if you no longer need it on your dashboard.\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigate(\"/widgets\")}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Widgets\n </button>\n }\n >\n <div className=\"mx-auto max-w-2xl\">\n <NextSteps\n storageKey=\"timecampus-widget-detail\"\n className=\"mb-6\"\n steps={[\n {\n id: \"save\",\n label: \"Save your changes\",\n description: \"Update this widget's name, metric or description.\",\n onClick: () => void handleSave(),\n },\n {\n id: \"browse\",\n label: \"Browse other widgets\",\n description: \"Manage the rest of your dashboard widgets.\",\n onClick: () => navigate(\"/widgets\"),\n },\n ]}\n />\n <GlassCard treatment=\"glass\" glow className=\"p-6\">\n <div className=\"flex items-start gap-4\">\n <div className=\"flex h-16 w-16 items-center justify-center rounded-xl bg-action-primary-bg/10\">\n <LayoutGrid className=\"h-8 w-8 text-primary\" />\n </div>\n <div className=\"flex-1\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {name || \"Untitled widget\"}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n This widget is saved to your workspace and can be shown on your\n dashboard.\n </p>\n </div>\n </div>\n\n {saveError && (\n <div className=\"mt-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text\">\n Failed to save widget. Please try again.\n </div>\n )}\n\n <div className=\"mt-6 space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Widget Name\n </label>\n <input\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n className=\"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Metric\n </label>\n <select\n value={type}\n onChange={(e) => setType(e.target.value)}\n className=\"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n {WIDGET_TYPES.map((t) => (\n <option key={t.value} value={t.value}>\n {t.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Description\n <span className=\"ml-1 font-normal text-text-secondary\">\n (optional)\n </span>\n </label>\n <textarea\n rows={3}\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n className=\"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary\"\n />\n </div>\n </div>\n\n <div className=\"mt-6 flex items-center justify-between\">\n <button\n type=\"button\"\n onClick={handleDelete}\n disabled={deleting}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-status-error-bg/40 px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg disabled:opacity-50\"\n >\n <Trash2 className=\"h-4 w-4\" />\n {deleting ? \"Deleting...\" : \"Delete\"}\n </button>\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={saving || !name.trim()}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n <Save className=\"h-4 w-4\" />\n {saving ? \"Saving...\" : \"Save Changes\"}\n </button>\n </div>\n </GlassCard>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1,84 +1,213 @@
1
- import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
1
+ import { useTimeCampusNavigate as e } from "../../providers/TimeCampusProvider.js";
2
2
  import { LoadingSkeleton as t } from "../../components/LoadingSkeleton.js";
3
- import { useListConfigurationsQuery as n } from "../../../generated/wspace-operations.js";
4
- import { PageLayout as r } from "../../components/PageLayout.js";
5
- import { EmptyState as i } from "../../components/EmptyState.js";
6
- import { LayoutGrid as a, Plus as o, Star as s } from "lucide-react";
7
- import { jsx as c, jsxs as l } from "react/jsx-runtime";
3
+ import { useDeleteConfigurationMutation as n, useListConfigurationsQuery as r, useSetConfigurationMutation as i } from "../../../generated/wspace-operations.js";
4
+ import { PageLayout as a } from "../../components/PageLayout.js";
5
+ import { EmptyState as o } from "../../components/EmptyState.js";
6
+ import { TimeCampusConfigScope as s } from "../../../generated/wspace-types.js";
7
+ import { Modal as c } from "../../components/Modal.js";
8
+ import { useState as l } from "react";
9
+ import { LayoutGrid as u, Plus as d, Trash2 as f } from "lucide-react";
10
+ import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
8
11
  //#region src/timecampus/pages/widgets/WidgetsPage.tsx
9
- function u() {
10
- let { workspaceId: u = "" } = e(), { data: d, loading: f, error: p } = n({ variables: { scope: "WORKSPACE" } }), m = (d?.configurations ?? []).filter((e) => e.key.startsWith("widget."));
11
- return f ? /* @__PURE__ */ c(r, {
12
+ var g = [
13
+ {
14
+ value: "timer",
15
+ label: "Active timer"
16
+ },
17
+ {
18
+ value: "project-progress",
19
+ label: "Project progress"
20
+ },
21
+ {
22
+ value: "focus-score",
23
+ label: "Focus score"
24
+ },
25
+ {
26
+ value: "team-stats",
27
+ label: "Team stats"
28
+ },
29
+ {
30
+ value: "billable-hours",
31
+ label: "Billable hours"
32
+ },
33
+ {
34
+ value: "recent-entries",
35
+ label: "Recent time entries"
36
+ }
37
+ ];
38
+ function _(e) {
39
+ return g.find((t) => t.value === e)?.label ?? e;
40
+ }
41
+ function v() {
42
+ let v = e(), { data: y, loading: b, error: x, refetch: S } = r({ variables: { scope: s.Workspace } }), [C, { loading: w, error: T }] = i(), [E] = n(), [D, O] = l(!1), [k, A] = l(""), [j, M] = l(g[0].value), [N, P] = l(""), [F, I] = l(null), L = (y?.configurations ?? []).filter((e) => e.key.startsWith("widget.")), R = () => {
43
+ A(""), M(g[0].value), P("");
44
+ }, z = () => {
45
+ R(), O(!0);
46
+ }, B = async () => {
47
+ if (!k.trim()) return;
48
+ let e = typeof crypto < "u" && "randomUUID" in crypto ? crypto.randomUUID() : `${Date.now()}`, t = N.trim();
49
+ await C({ variables: { input: {
50
+ key: `widget.${e}`,
51
+ scope: s.Workspace,
52
+ value: {
53
+ name: k.trim(),
54
+ type: j,
55
+ description: t || void 0,
56
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
57
+ },
58
+ description: t || void 0
59
+ } } }), await S(), O(!1), R();
60
+ }, V = async (e) => {
61
+ I(e);
62
+ try {
63
+ await E({ variables: { id: e } }), await S();
64
+ } finally {
65
+ I(null);
66
+ }
67
+ };
68
+ return b ? /* @__PURE__ */ m(a, {
12
69
  title: "Widgets",
13
70
  description: "Customize your dashboard with widgets",
14
- children: /* @__PURE__ */ c(t, {
71
+ children: /* @__PURE__ */ m(t, {
15
72
  variant: "card",
16
73
  count: 6
17
74
  })
18
- }) : p ? /* @__PURE__ */ c(r, {
75
+ }) : x ? /* @__PURE__ */ m(a, {
19
76
  title: "Widgets",
20
77
  description: "Customize your dashboard with widgets",
21
- children: /* @__PURE__ */ c("div", {
78
+ children: /* @__PURE__ */ m("div", {
22
79
  className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
23
80
  children: "Failed to load widget configurations. Please try again later."
24
81
  })
25
- }) : /* @__PURE__ */ c(r, {
82
+ }) : /* @__PURE__ */ h(a, {
26
83
  title: "Widgets",
27
84
  description: "Customize your dashboard with widgets",
28
- purpose: "Build a dashboard that shows what you care about — pick and arrange widgets for timers, projects, focus and team stats so your most important numbers are the first thing you see.",
29
- actions: /* @__PURE__ */ l("button", {
85
+ purpose: "Build a dashboard that shows what you care about — define widgets for timers, projects, focus and team stats so your most important numbers are the first thing you see. Each widget you create is saved to your workspace configuration.",
86
+ actions: /* @__PURE__ */ h("button", {
87
+ type: "button",
88
+ onClick: z,
30
89
  className: "inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
31
- children: [/* @__PURE__ */ c(o, { className: "h-4 w-4" }), "Create Widget"]
90
+ children: [/* @__PURE__ */ m(d, { className: "h-4 w-4" }), "Create Widget"]
32
91
  }),
33
- children: m.length === 0 ? /* @__PURE__ */ c(i, {
92
+ children: [L.length === 0 ? /* @__PURE__ */ m(o, {
34
93
  illustration: "empty-generic",
35
94
  title: "No widgets configured yet",
36
95
  description: "Add your first widget to start customising your dashboard — widgets are stored as workspace configurations and appear here.",
37
- action: /* @__PURE__ */ l("button", {
96
+ action: /* @__PURE__ */ h("button", {
97
+ type: "button",
98
+ onClick: z,
38
99
  className: "inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
39
- children: [/* @__PURE__ */ c(o, { className: "h-4 w-4" }), "Create Widget"]
100
+ children: [/* @__PURE__ */ m(d, { className: "h-4 w-4" }), "Create Widget"]
40
101
  })
41
- }) : /* @__PURE__ */ c("div", {
102
+ }) : /* @__PURE__ */ m("div", {
42
103
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
43
- children: m.map((e) => {
44
- let t = e.value, n = t.name ?? e.key.replace("widget.", ""), r = t.category ?? "General", i = t.size ?? "medium", o = t.isInstalled ?? !1, u = t.rating ?? 0, d = t.downloads ?? 0;
45
- return /* @__PURE__ */ l("div", {
46
- className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop p-5 transition-colors hover:bg-bg-sunken/50 cursor-pointer",
47
- children: [/* @__PURE__ */ l("div", {
48
- className: "flex items-start justify-between",
49
- children: [/* @__PURE__ */ l("div", {
50
- className: "flex items-center gap-3",
51
- children: [/* @__PURE__ */ c("div", {
104
+ children: L.map((e) => {
105
+ let t = e.value, n = t.name ?? e.key.replace("widget.", ""), r = t.type ?? "timer", i = t.description ?? e.description ?? "";
106
+ return /* @__PURE__ */ h("div", {
107
+ role: "button",
108
+ tabIndex: 0,
109
+ onClick: () => v(`/widgets/${e.id}`),
110
+ onKeyDown: (t) => {
111
+ (t.key === "Enter" || t.key === " ") && (t.preventDefault(), v(`/widgets/${e.id}`));
112
+ },
113
+ className: "cursor-pointer rounded-xl border border-border-seam bg-gloss-card p-5 shadow-pop transition-colors hover:bg-bg-sunken/50",
114
+ children: [/* @__PURE__ */ h("div", {
115
+ className: "flex items-start justify-between gap-3",
116
+ children: [/* @__PURE__ */ h("div", {
117
+ className: "flex min-w-0 items-center gap-3",
118
+ children: [/* @__PURE__ */ m("div", {
52
119
  className: "flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10",
53
- children: /* @__PURE__ */ c(a, { className: "h-5 w-5 text-primary" })
54
- }), /* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("p", {
55
- className: "text-sm font-semibold text-text-primary",
56
- children: n
57
- }), /* @__PURE__ */ l("p", {
58
- className: "text-xs text-text-secondary",
59
- children: [
60
- r,
61
- " · ",
62
- i
63
- ]
64
- })] })]
65
- }), o && /* @__PURE__ */ c("span", {
66
- className: "inline-flex rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text",
67
- children: "Installed"
120
+ children: /* @__PURE__ */ m(u, { className: "h-5 w-5 text-primary" })
121
+ }), /* @__PURE__ */ h("div", {
122
+ className: "min-w-0",
123
+ children: [/* @__PURE__ */ m("p", {
124
+ className: "truncate text-sm font-semibold text-text-primary",
125
+ children: n
126
+ }), /* @__PURE__ */ m("p", {
127
+ className: "text-xs text-text-secondary",
128
+ children: _(r)
129
+ })]
130
+ })]
131
+ }), /* @__PURE__ */ m("button", {
132
+ type: "button",
133
+ "aria-label": `Delete ${n}`,
134
+ disabled: F === e.id,
135
+ onClick: (t) => {
136
+ t.stopPropagation(), V(e.id);
137
+ },
138
+ className: "rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-status-error-text disabled:opacity-50",
139
+ children: /* @__PURE__ */ m(f, { className: "h-4 w-4" })
68
140
  })]
69
- }), /* @__PURE__ */ l("div", {
70
- className: "mt-3 flex items-center justify-between text-xs text-text-secondary",
71
- children: [/* @__PURE__ */ l("div", {
72
- className: "flex items-center gap-1",
73
- children: [/* @__PURE__ */ c(s, { className: "h-3 w-3 fill-amber-400 text-status-warning-text" }), /* @__PURE__ */ c("span", { children: u })]
74
- }), /* @__PURE__ */ l("span", { children: [d, " downloads"] })]
141
+ }), i && /* @__PURE__ */ m("p", {
142
+ className: "mt-3 line-clamp-2 text-xs text-text-secondary",
143
+ children: i
75
144
  })]
76
145
  }, e.id);
77
146
  })
78
- })
147
+ }), /* @__PURE__ */ h(c, {
148
+ open: D,
149
+ onClose: () => O(!1),
150
+ title: "Create Widget",
151
+ description: "Define a dashboard widget for your workspace.",
152
+ footer: /* @__PURE__ */ h(p, { children: [/* @__PURE__ */ m("button", {
153
+ type: "button",
154
+ onClick: () => O(!1),
155
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
156
+ children: "Cancel"
157
+ }), /* @__PURE__ */ m("button", {
158
+ type: "button",
159
+ onClick: B,
160
+ disabled: w || !k.trim(),
161
+ className: "rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50",
162
+ children: w ? "Creating..." : "Create Widget"
163
+ })] }),
164
+ children: [T && /* @__PURE__ */ m("div", {
165
+ className: "mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text",
166
+ children: "Failed to create widget. Please try again."
167
+ }), /* @__PURE__ */ h("div", {
168
+ className: "space-y-4",
169
+ children: [
170
+ /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("label", {
171
+ className: "block text-sm font-medium text-text-primary",
172
+ children: "Widget Name"
173
+ }), /* @__PURE__ */ m("input", {
174
+ type: "text",
175
+ placeholder: "e.g. This week's billable hours",
176
+ value: k,
177
+ onChange: (e) => A(e.target.value),
178
+ className: "mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary"
179
+ })] }),
180
+ /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("label", {
181
+ className: "block text-sm font-medium text-text-primary",
182
+ children: "Metric"
183
+ }), /* @__PURE__ */ m("select", {
184
+ value: j,
185
+ onChange: (e) => M(e.target.value),
186
+ className: "mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary",
187
+ children: g.map((e) => /* @__PURE__ */ m("option", {
188
+ value: e.value,
189
+ children: e.label
190
+ }, e.value))
191
+ })] }),
192
+ /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ h("label", {
193
+ className: "block text-sm font-medium text-text-primary",
194
+ children: ["Description", /* @__PURE__ */ m("span", {
195
+ className: "ml-1 font-normal text-text-secondary",
196
+ children: "(optional)"
197
+ })]
198
+ }), /* @__PURE__ */ m("textarea", {
199
+ placeholder: "What this widget shows",
200
+ rows: 3,
201
+ value: N,
202
+ onChange: (e) => P(e.target.value),
203
+ className: "mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary"
204
+ })] })
205
+ ]
206
+ })]
207
+ })]
79
208
  });
80
209
  }
81
210
  //#endregion
82
- export { u as WidgetsPage };
211
+ export { v as WidgetsPage };
83
212
 
84
213
  //# sourceMappingURL=WidgetsPage.js.map