@burdenoff/microfe-timecampus 2026.625.1 → 2026.626.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 (101) hide show
  1. package/dist/timecampus/components/EmptyState.js +14 -0
  2. package/dist/timecampus/components/EmptyState.js.map +1 -0
  3. package/dist/timecampus/components/PageLayout.js +23 -15
  4. package/dist/timecampus/components/PageLayout.js.map +1 -1
  5. package/dist/timecampus/pages/applets/AppletStorePage.js +33 -30
  6. package/dist/timecampus/pages/applets/AppletStorePage.js.map +1 -1
  7. package/dist/timecampus/pages/applets/InstalledAppletsPage.js +37 -31
  8. package/dist/timecampus/pages/applets/InstalledAppletsPage.js.map +1 -1
  9. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +48 -44
  10. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
  11. package/dist/timecampus/pages/application-usage/ApplicationUsageSettingsPage.js +35 -31
  12. package/dist/timecampus/pages/application-usage/ApplicationUsageSettingsPage.js.map +1 -1
  13. package/dist/timecampus/pages/channels/ChannelsPage.js +27 -24
  14. package/dist/timecampus/pages/channels/ChannelsPage.js.map +1 -1
  15. package/dist/timecampus/pages/client-portal/ClientPortalPage.js +46 -38
  16. package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
  17. package/dist/timecampus/pages/dashboard/AnalyticsPage.js +66 -39
  18. package/dist/timecampus/pages/dashboard/AnalyticsPage.js.map +1 -1
  19. package/dist/timecampus/pages/dashboard/ReportsPage.js +42 -36
  20. package/dist/timecampus/pages/dashboard/ReportsPage.js.map +1 -1
  21. package/dist/timecampus/pages/email-tracking/EmailAnalyticsPage.js +2 -0
  22. package/dist/timecampus/pages/email-tracking/EmailAnalyticsPage.js.map +1 -1
  23. package/dist/timecampus/pages/email-tracking/EmailSettingsPage.js +35 -31
  24. package/dist/timecampus/pages/email-tracking/EmailSettingsPage.js.map +1 -1
  25. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +31 -25
  26. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js.map +1 -1
  27. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +43 -35
  28. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
  29. package/dist/timecampus/pages/focus-score/FocusScorePage.js +55 -51
  30. package/dist/timecampus/pages/focus-score/FocusScorePage.js.map +1 -1
  31. package/dist/timecampus/pages/focus-score/FocusScoreSettingsPage.js +32 -28
  32. package/dist/timecampus/pages/focus-score/FocusScoreSettingsPage.js.map +1 -1
  33. package/dist/timecampus/pages/geofencing/GeofencingPage.js +32 -25
  34. package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
  35. package/dist/timecampus/pages/geofencing/GeofencingSettingsPage.js +29 -25
  36. package/dist/timecampus/pages/geofencing/GeofencingSettingsPage.js.map +1 -1
  37. package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js +32 -29
  38. package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js.map +1 -1
  39. package/dist/timecampus/pages/location-tracking/LocationPrivacySettingsPage.js +29 -25
  40. package/dist/timecampus/pages/location-tracking/LocationPrivacySettingsPage.js.map +1 -1
  41. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +39 -35
  42. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
  43. package/dist/timecampus/pages/meetings/MeetingAnalyticsPage.js +2 -0
  44. package/dist/timecampus/pages/meetings/MeetingAnalyticsPage.js.map +1 -1
  45. package/dist/timecampus/pages/meetings/MeetingCreatePage.js +33 -29
  46. package/dist/timecampus/pages/meetings/MeetingCreatePage.js.map +1 -1
  47. package/dist/timecampus/pages/meetings/MeetingDetailPage.js +122 -104
  48. package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
  49. package/dist/timecampus/pages/meetings/MeetingsPage.js +42 -32
  50. package/dist/timecampus/pages/meetings/MeetingsPage.js.map +1 -1
  51. package/dist/timecampus/pages/pomodoro/PomodoroAnalyticsPage.js +2 -0
  52. package/dist/timecampus/pages/pomodoro/PomodoroAnalyticsPage.js.map +1 -1
  53. package/dist/timecampus/pages/pomodoro/PomodoroPage.js +145 -114
  54. package/dist/timecampus/pages/pomodoro/PomodoroPage.js.map +1 -1
  55. package/dist/timecampus/pages/pomodoro/PomodoroSettingsPage.js +49 -45
  56. package/dist/timecampus/pages/pomodoro/PomodoroSettingsPage.js.map +1 -1
  57. package/dist/timecampus/pages/projects/ProjectCreatePage.js +28 -24
  58. package/dist/timecampus/pages/projects/ProjectCreatePage.js.map +1 -1
  59. package/dist/timecampus/pages/projects/ProjectDetailPage.js +109 -78
  60. package/dist/timecampus/pages/projects/ProjectDetailPage.js.map +1 -1
  61. package/dist/timecampus/pages/projects/ProjectsPage.js +11 -7
  62. package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
  63. package/dist/timecampus/pages/projects/TasksPage.js +40 -30
  64. package/dist/timecampus/pages/projects/TasksPage.js.map +1 -1
  65. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +50 -46
  66. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
  67. package/dist/timecampus/pages/team/TeamAnalyticsPage.js +18 -14
  68. package/dist/timecampus/pages/team/TeamAnalyticsPage.js.map +1 -1
  69. package/dist/timecampus/pages/team/TeamMembersPage.js +41 -35
  70. package/dist/timecampus/pages/team/TeamMembersPage.js.map +1 -1
  71. package/dist/timecampus/pages/team/TeamPage.js +91 -61
  72. package/dist/timecampus/pages/team/TeamPage.js.map +1 -1
  73. package/dist/timecampus/pages/team-activity/TeamActivityPage.js +40 -36
  74. package/dist/timecampus/pages/team-activity/TeamActivityPage.js.map +1 -1
  75. package/dist/timecampus/pages/team-activity/TeamActivitySettingsPage.js +33 -29
  76. package/dist/timecampus/pages/team-activity/TeamActivitySettingsPage.js.map +1 -1
  77. package/dist/timecampus/pages/templates/TemplateApplicationPage.js +17 -13
  78. package/dist/timecampus/pages/templates/TemplateApplicationPage.js.map +1 -1
  79. package/dist/timecampus/pages/templates/TemplateDetailPage.js +67 -49
  80. package/dist/timecampus/pages/templates/TemplateDetailPage.js.map +1 -1
  81. package/dist/timecampus/pages/templates/TemplatesPage.js +34 -31
  82. package/dist/timecampus/pages/templates/TemplatesPage.js.map +1 -1
  83. package/dist/timecampus/pages/time/TimeEntriesPage.js +48 -39
  84. package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
  85. package/dist/timecampus/pages/time/TimeTrackingPage.js +65 -64
  86. package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
  87. package/dist/timecampus/pages/time/TimerPage.js +103 -69
  88. package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
  89. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +53 -49
  90. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
  91. package/dist/timecampus/pages/widgets/WidgetDetailPage.js +65 -47
  92. package/dist/timecampus/pages/widgets/WidgetDetailPage.js.map +1 -1
  93. package/dist/timecampus/pages/widgets/WidgetsPage.js +38 -31
  94. package/dist/timecampus/pages/widgets/WidgetsPage.js.map +1 -1
  95. package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js +2 -0
  96. package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js.map +1 -1
  97. package/dist/timecampus/pages/work-patterns/WorkPatternsSettingsPage.js +35 -31
  98. package/dist/timecampus/pages/work-patterns/WorkPatternsSettingsPage.js.map +1 -1
  99. package/dist/timecampus/pages/workflows/WorkflowsPage.js +43 -33
  100. package/dist/timecampus/pages/workflows/WorkflowsPage.js.map +1 -1
  101. package/package.json +2 -2
@@ -4,80 +4,84 @@ import { StatCard as a } from "../../components/StatCard.js";
4
4
  import { LoadingSkeleton as o } from "../../components/LoadingSkeleton.js";
5
5
  import { useApproveRequestMutation as s, useGetApprovalStatsQuery as c, useListApprovalRequestsQuery as l, useRejectRequestMutation as u } from "../../../generated/wspace-operations.js";
6
6
  import { PageLayout as d } from "../../components/PageLayout.js";
7
- import { CheckCircle as f, Clock as p, XCircle as m } from "lucide-react";
8
- import { jsx as h, jsxs as g } from "react/jsx-runtime";
7
+ import { EmptyState as f } from "../../components/EmptyState.js";
8
+ import { CheckCircle as p, Clock as m, XCircle as h } from "lucide-react";
9
+ import { jsx as g, jsxs as _ } from "react/jsx-runtime";
9
10
  //#region src/timecampus/pages/time-approval/TimeApprovalPage.tsx
10
- function _() {
11
- let { workspaceId: _ = "" } = e(), { data: v, loading: y, error: b } = l(), { data: x, loading: S, error: C } = c(), [w, { loading: T }] = s(), [E, { loading: D }] = u(), O = v?.approvalRequests?.items ?? [], k = x?.approvalStats, A = T || D, j = async (e) => {
12
- await w({ variables: { id: e } });
13
- }, M = async (e) => {
14
- await E({ variables: {
11
+ function v() {
12
+ let { workspaceId: v = "" } = e(), { data: y, loading: b, error: x } = l(), { data: S, loading: C, error: w } = c(), [T, { loading: E }] = s(), [D, { loading: O }] = u(), k = y?.approvalRequests?.items ?? [], A = S?.approvalStats, j = E || O, M = async (e) => {
13
+ await T({ variables: { id: e } });
14
+ }, N = async (e) => {
15
+ await D({ variables: {
15
16
  id: e,
16
17
  reason: "Rejected by reviewer"
17
18
  } });
18
19
  };
19
- return y && S ? /* @__PURE__ */ h(d, {
20
+ return b && C ? /* @__PURE__ */ g(d, {
20
21
  title: "Time Approval",
21
22
  description: "Review and approve team timesheets",
22
- children: /* @__PURE__ */ h(o, {
23
+ children: /* @__PURE__ */ g(o, {
23
24
  variant: "list",
24
25
  count: 4
25
26
  })
26
- }) : b && C ? /* @__PURE__ */ h(d, {
27
+ }) : x && w ? /* @__PURE__ */ g(d, {
27
28
  title: "Time Approval",
28
29
  description: "Review and approve team timesheets",
29
- children: /* @__PURE__ */ h("div", {
30
+ children: /* @__PURE__ */ g("div", {
30
31
  className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
31
32
  children: "Failed to load approval data. Please try again later."
32
33
  })
33
- }) : /* @__PURE__ */ g(d, {
34
+ }) : /* @__PURE__ */ _(d, {
34
35
  title: "Time Approval",
35
36
  description: "Review and approve team timesheets",
36
- children: [/* @__PURE__ */ g("div", {
37
+ purpose: "The reviewer's queue for signing off team timesheets before they're billed or paid — approve or reject submissions in one place so payroll and invoices go out accurate and on time.",
38
+ children: [/* @__PURE__ */ _("div", {
37
39
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
38
40
  children: [
39
- /* @__PURE__ */ h(a, {
41
+ /* @__PURE__ */ g(a, {
42
+ featured: !0,
40
43
  title: "Pending Approvals",
41
- value: String(k?.pendingCount ?? 0),
42
- icon: /* @__PURE__ */ h(p, { className: "h-5 w-5" }),
44
+ value: String(A?.pendingCount ?? 0),
45
+ icon: /* @__PURE__ */ g(m, { className: "h-5 w-5" }),
43
46
  iconBgColor: "bg-status-warning-bg text-status-warning-text"
44
47
  }),
45
- /* @__PURE__ */ h(a, {
48
+ /* @__PURE__ */ g(a, {
46
49
  title: "Approved This Week",
47
- value: String(k?.approvedCount ?? 0),
48
- icon: /* @__PURE__ */ h(f, { className: "h-5 w-5" }),
50
+ value: String(A?.approvedCount ?? 0),
51
+ icon: /* @__PURE__ */ g(p, { className: "h-5 w-5" }),
49
52
  iconBgColor: "bg-status-success-bg text-status-success-text"
50
53
  }),
51
- /* @__PURE__ */ h(a, {
54
+ /* @__PURE__ */ g(a, {
52
55
  title: "Rejected",
53
- value: String(k?.rejectedCount ?? 0),
54
- icon: /* @__PURE__ */ h(m, { className: "h-5 w-5" }),
56
+ value: String(A?.rejectedCount ?? 0),
57
+ icon: /* @__PURE__ */ g(h, { className: "h-5 w-5" }),
55
58
  iconBgColor: "bg-status-error-bg text-status-error-text"
56
59
  }),
57
- /* @__PURE__ */ h(a, {
60
+ /* @__PURE__ */ g(a, {
58
61
  title: "Avg. Review Time",
59
- value: k?.averageReviewTime ? `${Math.round(k.averageReviewTime)}h` : "N/A",
62
+ value: A?.averageReviewTime ? `${Math.round(A.averageReviewTime)}h` : "N/A",
60
63
  description: "Target: 24h"
61
64
  })
62
65
  ]
63
- }), /* @__PURE__ */ h("div", {
66
+ }), k.length === 0 && !b ? /* @__PURE__ */ g(f, {
67
+ illustration: "empty-notifications",
68
+ title: "No approval requests found",
69
+ description: "You're all caught up — submitted timesheets waiting on your review will appear here."
70
+ }) : /* @__PURE__ */ g("div", {
64
71
  className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop",
65
72
  children: /* @__PURE__ */ g("div", {
66
73
  className: "divide-y divide-border",
67
- children: [O.length === 0 && !y && /* @__PURE__ */ h("div", {
68
- className: "p-4 text-center text-sm text-text-secondary",
69
- children: "No approval requests found."
70
- }), O.map((e) => /* @__PURE__ */ g("div", {
74
+ children: k.map((e) => /* @__PURE__ */ _("div", {
71
75
  className: "flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors",
72
- children: [/* @__PURE__ */ g("div", {
76
+ children: [/* @__PURE__ */ _("div", {
73
77
  className: "flex items-center gap-3",
74
- children: [/* @__PURE__ */ h("div", {
78
+ children: [/* @__PURE__ */ g("div", {
75
79
  className: "flex h-9 w-9 items-center justify-center rounded-full bg-action-primary-bg/10 text-sm font-medium text-primary",
76
80
  children: r(e.userId)
77
- }), /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("p", {
81
+ }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
78
82
  className: "text-sm font-medium text-text-primary",
79
83
  children: e.userId
80
- }), /* @__PURE__ */ g("p", {
84
+ }), /* @__PURE__ */ _("p", {
81
85
  className: "text-xs text-text-secondary",
82
86
  children: [
83
87
  e.period,
@@ -86,45 +90,45 @@ function _() {
86
90
  t(e.submittedAt)
87
91
  ]
88
92
  })] })]
89
- }), /* @__PURE__ */ g("div", {
93
+ }), /* @__PURE__ */ _("div", {
90
94
  className: "flex items-center gap-4",
91
95
  children: [
92
- /* @__PURE__ */ g("div", {
96
+ /* @__PURE__ */ _("div", {
93
97
  className: "text-right",
94
- children: [/* @__PURE__ */ h("p", {
98
+ children: [/* @__PURE__ */ g("p", {
95
99
  className: "text-sm font-medium text-text-primary",
96
100
  children: n(e.totalHours)
97
- }), /* @__PURE__ */ g("p", {
101
+ }), /* @__PURE__ */ _("p", {
98
102
  className: "text-xs text-text-secondary",
99
103
  children: [n(e.billableHours), " billable"]
100
104
  })]
101
105
  }),
102
- /* @__PURE__ */ h("span", {
106
+ /* @__PURE__ */ g("span", {
103
107
  className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${i(e.status)}`,
104
108
  children: e.status
105
109
  }),
106
- e.status === "PENDING" && /* @__PURE__ */ g("div", {
110
+ e.status === "PENDING" && /* @__PURE__ */ _("div", {
107
111
  className: "flex items-center gap-1",
108
- children: [/* @__PURE__ */ h("button", {
109
- onClick: () => j(e.id),
110
- disabled: A,
111
- className: "rounded-md p-1.5 text-status-success-text hover:bg-status-success-bg dark:hover:bg-status-success-text/20 disabled:opacity-50",
112
- children: /* @__PURE__ */ h(f, { className: "h-4 w-4" })
113
- }), /* @__PURE__ */ h("button", {
112
+ children: [/* @__PURE__ */ g("button", {
114
113
  onClick: () => M(e.id),
115
- disabled: A,
114
+ disabled: j,
115
+ className: "rounded-md p-1.5 text-status-success-text hover:bg-status-success-bg dark:hover:bg-status-success-text/20 disabled:opacity-50",
116
+ children: /* @__PURE__ */ g(p, { className: "h-4 w-4" })
117
+ }), /* @__PURE__ */ g("button", {
118
+ onClick: () => N(e.id),
119
+ disabled: j,
116
120
  className: "rounded-md p-1.5 text-status-error-text hover:bg-status-error-bg dark:hover:bg-status-error-text/20 disabled:opacity-50",
117
- children: /* @__PURE__ */ h(m, { className: "h-4 w-4" })
121
+ children: /* @__PURE__ */ g(h, { className: "h-4 w-4" })
118
122
  })]
119
123
  })
120
124
  ]
121
125
  })]
122
- }, e.id))]
126
+ }, e.id))
123
127
  })
124
128
  })]
125
129
  });
126
130
  }
127
131
  //#endregion
128
- export { _ as TimeApprovalPage };
132
+ export { v as TimeApprovalPage };
129
133
 
130
134
  //# sourceMappingURL=TimeApprovalPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TimeApprovalPage.js","names":[],"sources":["../../../../src/timecampus/pages/time-approval/TimeApprovalPage.tsx"],"sourcesContent":["import { CheckCircle, XCircle, Clock } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport {\n getStatusColor,\n getInitials,\n formatHours,\n formatDate,\n} from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListApprovalRequestsQuery,\n useGetApprovalStatsQuery,\n useApproveRequestMutation,\n useRejectRequestMutation,\n type ApprovalRequestFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function TimeApprovalPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const {\n data: requestsData,\n loading: requestsLoading,\n error: requestsError,\n } = useListApprovalRequestsQuery();\n const {\n data: statsData,\n loading: statsLoading,\n error: statsError,\n } = useGetApprovalStatsQuery();\n const [approveRequest, { loading: approving }] = useApproveRequestMutation();\n const [rejectRequest, { loading: rejecting }] = useRejectRequestMutation();\n\n const requests = requestsData?.approvalRequests?.items ?? [];\n const stats = statsData?.approvalStats;\n const mutating = approving || rejecting;\n\n const handleApprove = async (id: string) => {\n await approveRequest({ variables: { id } });\n };\n\n const handleReject = async (id: string) => {\n await rejectRequest({ variables: { id, reason: \"Rejected by reviewer\" } });\n };\n\n if (requestsLoading && statsLoading) {\n return (\n <PageLayout\n title=\"Time Approval\"\n description=\"Review and approve team timesheets\"\n >\n <LoadingSkeleton variant=\"list\" count={4} />\n </PageLayout>\n );\n }\n\n if (requestsError && statsError) {\n return (\n <PageLayout\n title=\"Time Approval\"\n description=\"Review and approve team timesheets\"\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 Failed to load approval data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Time Approval\"\n description=\"Review and approve team timesheets\"\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n title=\"Pending Approvals\"\n value={String(stats?.pendingCount ?? 0)}\n icon={<Clock className=\"h-5 w-5\" />}\n iconBgColor=\"bg-status-warning-bg text-status-warning-text\"\n />\n <StatCard\n title=\"Approved This Week\"\n value={String(stats?.approvedCount ?? 0)}\n icon={<CheckCircle className=\"h-5 w-5\" />}\n iconBgColor=\"bg-status-success-bg text-status-success-text\"\n />\n <StatCard\n title=\"Rejected\"\n value={String(stats?.rejectedCount ?? 0)}\n icon={<XCircle className=\"h-5 w-5\" />}\n iconBgColor=\"bg-status-error-bg text-status-error-text\"\n />\n <StatCard\n title=\"Avg. Review Time\"\n value={\n stats?.averageReviewTime\n ? `${Math.round(stats.averageReviewTime)}h`\n : \"N/A\"\n }\n description=\"Target: 24h\"\n />\n </div>\n\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop\">\n <div className=\"divide-y divide-border\">\n {requests.length === 0 && !requestsLoading && (\n <div className=\"p-4 text-center text-sm text-text-secondary\">\n No approval requests found.\n </div>\n )}\n {requests.map((request: ApprovalRequestFieldsFragment) => (\n <div\n key={request.id}\n className=\"flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-9 w-9 items-center justify-center rounded-full bg-action-primary-bg/10 text-sm font-medium text-primary\">\n {getInitials(request.userId)}\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {request.userId}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {request.period} &middot; Submitted{\" \"}\n {formatDate(request.submittedAt)}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-4\">\n <div className=\"text-right\">\n <p className=\"text-sm font-medium text-text-primary\">\n {formatHours(request.totalHours)}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {formatHours(request.billableHours)} billable\n </p>\n </div>\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getStatusColor(request.status)}`}\n >\n {request.status}\n </span>\n {request.status === \"PENDING\" && (\n <div className=\"flex items-center gap-1\">\n <button\n onClick={() => handleApprove(request.id)}\n disabled={mutating}\n className=\"rounded-md p-1.5 text-status-success-text hover:bg-status-success-bg dark:hover:bg-status-success-text/20 disabled:opacity-50\"\n >\n <CheckCircle className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => handleReject(request.id)}\n disabled={mutating}\n className=\"rounded-md p-1.5 text-status-error-text hover:bg-status-error-bg dark:hover:bg-status-error-text/20 disabled:opacity-50\"\n >\n <XCircle className=\"h-4 w-4\" />\n </button>\n </div>\n )}\n </div>\n </div>\n ))}\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TimeApprovalPage.js","names":[],"sources":["../../../../src/timecampus/pages/time-approval/TimeApprovalPage.tsx"],"sourcesContent":["import { CheckCircle, XCircle, Clock } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport {\n getStatusColor,\n getInitials,\n formatHours,\n formatDate,\n} from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListApprovalRequestsQuery,\n useGetApprovalStatsQuery,\n useApproveRequestMutation,\n useRejectRequestMutation,\n type ApprovalRequestFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function TimeApprovalPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const {\n data: requestsData,\n loading: requestsLoading,\n error: requestsError,\n } = useListApprovalRequestsQuery();\n const {\n data: statsData,\n loading: statsLoading,\n error: statsError,\n } = useGetApprovalStatsQuery();\n const [approveRequest, { loading: approving }] = useApproveRequestMutation();\n const [rejectRequest, { loading: rejecting }] = useRejectRequestMutation();\n\n const requests = requestsData?.approvalRequests?.items ?? [];\n const stats = statsData?.approvalStats;\n const mutating = approving || rejecting;\n\n const handleApprove = async (id: string) => {\n await approveRequest({ variables: { id } });\n };\n\n const handleReject = async (id: string) => {\n await rejectRequest({ variables: { id, reason: \"Rejected by reviewer\" } });\n };\n\n if (requestsLoading && statsLoading) {\n return (\n <PageLayout\n title=\"Time Approval\"\n description=\"Review and approve team timesheets\"\n >\n <LoadingSkeleton variant=\"list\" count={4} />\n </PageLayout>\n );\n }\n\n if (requestsError && statsError) {\n return (\n <PageLayout\n title=\"Time Approval\"\n description=\"Review and approve team timesheets\"\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 Failed to load approval data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Time Approval\"\n description=\"Review and approve team timesheets\"\n purpose=\"The reviewer's queue for signing off team timesheets before they're billed or paid — approve or reject submissions in one place so payroll and invoices go out accurate and on time.\"\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n featured\n title=\"Pending Approvals\"\n value={String(stats?.pendingCount ?? 0)}\n icon={<Clock className=\"h-5 w-5\" />}\n iconBgColor=\"bg-status-warning-bg text-status-warning-text\"\n />\n <StatCard\n title=\"Approved This Week\"\n value={String(stats?.approvedCount ?? 0)}\n icon={<CheckCircle className=\"h-5 w-5\" />}\n iconBgColor=\"bg-status-success-bg text-status-success-text\"\n />\n <StatCard\n title=\"Rejected\"\n value={String(stats?.rejectedCount ?? 0)}\n icon={<XCircle className=\"h-5 w-5\" />}\n iconBgColor=\"bg-status-error-bg text-status-error-text\"\n />\n <StatCard\n title=\"Avg. Review Time\"\n value={\n stats?.averageReviewTime\n ? `${Math.round(stats.averageReviewTime)}h`\n : \"N/A\"\n }\n description=\"Target: 24h\"\n />\n </div>\n\n {requests.length === 0 && !requestsLoading ? (\n <EmptyState\n illustration=\"empty-notifications\"\n title=\"No approval requests found\"\n description=\"You're all caught up — submitted timesheets waiting on your review will appear here.\"\n />\n ) : (\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop\">\n <div className=\"divide-y divide-border\">\n {requests.map((request: ApprovalRequestFieldsFragment) => (\n <div\n key={request.id}\n className=\"flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-9 w-9 items-center justify-center rounded-full bg-action-primary-bg/10 text-sm font-medium text-primary\">\n {getInitials(request.userId)}\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {request.userId}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {request.period} &middot; Submitted{\" \"}\n {formatDate(request.submittedAt)}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-4\">\n <div className=\"text-right\">\n <p className=\"text-sm font-medium text-text-primary\">\n {formatHours(request.totalHours)}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {formatHours(request.billableHours)} billable\n </p>\n </div>\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getStatusColor(request.status)}`}\n >\n {request.status}\n </span>\n {request.status === \"PENDING\" && (\n <div className=\"flex items-center gap-1\">\n <button\n onClick={() => handleApprove(request.id)}\n disabled={mutating}\n className=\"rounded-md p-1.5 text-status-success-text hover:bg-status-success-bg dark:hover:bg-status-success-text/20 disabled:opacity-50\"\n >\n <CheckCircle className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => handleReject(request.id)}\n disabled={mutating}\n className=\"rounded-md p-1.5 text-status-error-text hover:bg-status-error-bg dark:hover:bg-status-error-text/20 disabled:opacity-50\"\n >\n <XCircle className=\"h-4 w-4\" />\n </button>\n </div>\n )}\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1,96 +1,114 @@
1
- import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
2
- import { LoadingSkeleton as t } from "../../components/LoadingSkeleton.js";
3
- import { useGetConfigurationQuery as n } from "../../../generated/wspace-operations.js";
4
- import { PageLayout as r } from "../../components/PageLayout.js";
5
- import { ArrowLeft as i, Download as a, LayoutGrid as o, Star as s } from "lucide-react";
6
- import { useParams as c } from "react-router-dom";
7
- import { jsx as l, jsxs as u } from "react/jsx-runtime";
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";
8
9
  //#region src/timecampus/pages/widgets/WidgetDetailPage.tsx
9
- function d() {
10
- let { id: d } = c(), { workspaceId: f = "" } = e(), { data: p, loading: m, error: h } = n({
11
- variables: { id: d ?? "" },
12
- skip: !d
13
- }), g = p?.configuration, _ = g?.value ?? {}, v = _.name ?? g?.key?.replace("widget.", "") ?? "Widget", y = g?.description ?? _.description ?? "A configurable dashboard widget for time tracking.", b = _.rating ?? 0, x = _.downloads ?? 0;
14
- return m ? /* @__PURE__ */ l(r, {
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, {
15
16
  title: "Widget Details",
16
17
  description: "Loading...",
17
- children: /* @__PURE__ */ l(t, { variant: "detail" })
18
- }) : h || !g ? /* @__PURE__ */ l(r, {
18
+ children: /* @__PURE__ */ u(n, { variant: "detail" })
19
+ }) : y || !b ? /* @__PURE__ */ u(i, {
19
20
  title: "Widget Details",
20
- description: `Widget ID: ${d}`,
21
- actions: /* @__PURE__ */ u("button", {
21
+ description: `Widget ID: ${m}`,
22
+ actions: /* @__PURE__ */ d("button", {
22
23
  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",
23
- children: [/* @__PURE__ */ l(i, { className: "h-4 w-4" }), "Back to Widgets"]
24
+ children: [/* @__PURE__ */ u(a, { className: "h-4 w-4" }), "Back to Widgets"]
24
25
  }),
25
- children: /* @__PURE__ */ l("div", {
26
+ children: /* @__PURE__ */ u("div", {
26
27
  className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
27
- children: h ? "Failed to load widget details. Please try again later." : "Widget configuration not found."
28
+ children: y ? "Failed to load widget details. Please try again later." : "Widget configuration not found."
28
29
  })
29
- }) : /* @__PURE__ */ l(r, {
30
+ }) : /* @__PURE__ */ u(i, {
30
31
  title: "Widget Details",
31
- description: `Widget ID: ${d}`,
32
- actions: /* @__PURE__ */ u("button", {
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", {
33
35
  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",
34
- children: [/* @__PURE__ */ l(i, { className: "h-4 w-4" }), "Back to Widgets"]
36
+ children: [/* @__PURE__ */ u(a, { className: "h-4 w-4" }), "Back to Widgets"]
35
37
  }),
36
- children: /* @__PURE__ */ l("div", {
38
+ children: /* @__PURE__ */ d("div", {
37
39
  className: "mx-auto max-w-2xl",
38
- children: /* @__PURE__ */ u("div", {
39
- className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6",
40
+ children: [/* @__PURE__ */ u(p, {
41
+ storageKey: "timecampus-widget-detail",
42
+ className: "mb-6",
43
+ steps: [{
44
+ id: "install",
45
+ label: "Install this widget",
46
+ description: "Add it to your dashboard.",
47
+ onClick: () => {}
48
+ }, {
49
+ id: "browse",
50
+ label: "Browse other widgets",
51
+ description: "Find more panels for your dashboard.",
52
+ onClick: () => g("/widgets")
53
+ }]
54
+ }), /* @__PURE__ */ d(f, {
55
+ treatment: "glass",
56
+ glow: !0,
57
+ className: "p-6",
40
58
  children: [
41
- /* @__PURE__ */ u("div", {
59
+ /* @__PURE__ */ d("div", {
42
60
  className: "flex items-start gap-4",
43
- children: [/* @__PURE__ */ l("div", {
61
+ children: [/* @__PURE__ */ u("div", {
44
62
  className: "flex h-16 w-16 items-center justify-center rounded-xl bg-action-primary-bg/10",
45
- children: /* @__PURE__ */ l(o, { className: "h-8 w-8 text-primary" })
46
- }), /* @__PURE__ */ u("div", {
63
+ children: /* @__PURE__ */ u(s, { className: "h-8 w-8 text-primary" })
64
+ }), /* @__PURE__ */ d("div", {
47
65
  className: "flex-1",
48
66
  children: [
49
- /* @__PURE__ */ l("h2", {
67
+ /* @__PURE__ */ u("h2", {
50
68
  className: "text-lg font-semibold text-text-primary",
51
- children: v
69
+ children: S
52
70
  }),
53
- /* @__PURE__ */ l("p", {
71
+ /* @__PURE__ */ u("p", {
54
72
  className: "text-sm text-text-secondary",
55
- children: y
73
+ children: C
56
74
  }),
57
- /* @__PURE__ */ u("div", {
75
+ /* @__PURE__ */ d("div", {
58
76
  className: "mt-2 flex items-center gap-3 text-sm text-text-secondary",
59
77
  children: [
60
- /* @__PURE__ */ u("div", {
78
+ /* @__PURE__ */ d("div", {
61
79
  className: "flex items-center gap-1",
62
- children: [/* @__PURE__ */ l(s, { className: "h-4 w-4 fill-amber-400 text-status-warning-text" }), /* @__PURE__ */ l("span", { children: b })]
80
+ children: [/* @__PURE__ */ u(c, { className: "h-4 w-4 fill-amber-400 text-status-warning-text" }), /* @__PURE__ */ u("span", { children: w })]
63
81
  }),
64
- /* @__PURE__ */ l("span", { children: "·" }),
65
- /* @__PURE__ */ u("div", {
82
+ /* @__PURE__ */ u("span", { children: "·" }),
83
+ /* @__PURE__ */ d("div", {
66
84
  className: "flex items-center gap-1",
67
- children: [/* @__PURE__ */ l(a, { className: "h-4 w-4" }), /* @__PURE__ */ u("span", { children: [x.toLocaleString(), " installs"] })]
85
+ children: [/* @__PURE__ */ u(o, { className: "h-4 w-4" }), /* @__PURE__ */ d("span", { children: [T.toLocaleString(), " installs"] })]
68
86
  })
69
87
  ]
70
88
  })
71
89
  ]
72
90
  })]
73
91
  }),
74
- /* @__PURE__ */ l("div", {
92
+ /* @__PURE__ */ u("div", {
75
93
  className: "mt-6 flex h-48 items-center justify-center rounded-lg border border-dashed border-border-subtle",
76
- children: /* @__PURE__ */ l("p", {
94
+ children: /* @__PURE__ */ u("p", {
77
95
  className: "text-sm text-text-secondary",
78
96
  children: "Widget preview will render here"
79
97
  })
80
98
  }),
81
- /* @__PURE__ */ l("div", {
99
+ /* @__PURE__ */ u("div", {
82
100
  className: "mt-4 flex justify-end",
83
- children: /* @__PURE__ */ l("button", {
101
+ children: /* @__PURE__ */ u("button", {
84
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",
85
103
  children: "Install Widget"
86
104
  })
87
105
  })
88
106
  ]
89
- })
107
+ })]
90
108
  })
91
109
  });
92
110
  }
93
111
  //#endregion
94
- export { d as WidgetDetailPage };
112
+ export { m as WidgetDetailPage };
95
113
 
96
114
  //# 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 { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport { useGetConfigurationQuery } from \"@/generated/wspace-operations\";\n\nexport function WidgetDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { workspaceId = \"\" } = useTimeCampus();\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 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 <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop 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 </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;AAOA,SAAgB,IAAmB;CACjC,IAAM,EAAE,UAAO,GAA2B,EACpC,EAAE,iBAAc,OAAO,GAAe,EAEtC,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,SACE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,kBAE1B;;YAGX,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cAAf;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;KACF;;GACF,CAAA;EACK,CAAA"}
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"}
@@ -2,69 +2,76 @@ import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
2
2
  import { LoadingSkeleton as t } from "../../components/LoadingSkeleton.js";
3
3
  import { useListConfigurationsQuery as n } from "../../../generated/wspace-operations.js";
4
4
  import { PageLayout as r } from "../../components/PageLayout.js";
5
- import { LayoutGrid as i, Plus as a, Star as o } from "lucide-react";
6
- import { jsx as s, jsxs as c } from "react/jsx-runtime";
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";
7
8
  //#region src/timecampus/pages/widgets/WidgetsPage.tsx
8
- function l() {
9
- let { workspaceId: l = "" } = e(), { data: u, loading: d, error: f } = n({ variables: { scope: "WORKSPACE" } }), p = (u?.configurations ?? []).filter((e) => e.key.startsWith("widget."));
10
- return d ? /* @__PURE__ */ s(r, {
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, {
11
12
  title: "Widgets",
12
13
  description: "Customize your dashboard with widgets",
13
- children: /* @__PURE__ */ s(t, {
14
+ children: /* @__PURE__ */ c(t, {
14
15
  variant: "card",
15
16
  count: 6
16
17
  })
17
- }) : f ? /* @__PURE__ */ s(r, {
18
+ }) : p ? /* @__PURE__ */ c(r, {
18
19
  title: "Widgets",
19
20
  description: "Customize your dashboard with widgets",
20
- children: /* @__PURE__ */ s("div", {
21
+ children: /* @__PURE__ */ c("div", {
21
22
  className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
22
23
  children: "Failed to load widget configurations. Please try again later."
23
24
  })
24
- }) : /* @__PURE__ */ s(r, {
25
+ }) : /* @__PURE__ */ c(r, {
25
26
  title: "Widgets",
26
27
  description: "Customize your dashboard with widgets",
27
- actions: /* @__PURE__ */ c("button", {
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", {
28
30
  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",
29
- children: [/* @__PURE__ */ s(a, { className: "h-4 w-4" }), "Create Widget"]
31
+ children: [/* @__PURE__ */ c(o, { className: "h-4 w-4" }), "Create Widget"]
30
32
  }),
31
- children: /* @__PURE__ */ s("div", {
33
+ children: m.length === 0 ? /* @__PURE__ */ c(i, {
34
+ illustration: "empty-generic",
35
+ title: "No widgets configured yet",
36
+ description: "Add your first widget to start customising your dashboard — widgets are stored as workspace configurations and appear here.",
37
+ action: /* @__PURE__ */ l("button", {
38
+ 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"]
40
+ })
41
+ }) : /* @__PURE__ */ c("div", {
32
42
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
33
- children: p.length === 0 ? /* @__PURE__ */ s("div", {
34
- className: "col-span-full p-8 text-center text-sm text-text-secondary",
35
- children: "No widgets configured yet. Widgets are stored as workspace configurations with the \"widget.\" key prefix."
36
- }) : p.map((e) => {
37
- let t = e.value, n = t.name ?? e.key.replace("widget.", ""), r = t.category ?? "General", a = t.size ?? "medium", l = t.isInstalled ?? !1, u = t.rating ?? 0, d = t.downloads ?? 0;
38
- return /* @__PURE__ */ c("div", {
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", {
39
46
  className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop p-5 transition-colors hover:bg-bg-sunken/50 cursor-pointer",
40
- children: [/* @__PURE__ */ c("div", {
47
+ children: [/* @__PURE__ */ l("div", {
41
48
  className: "flex items-start justify-between",
42
- children: [/* @__PURE__ */ c("div", {
49
+ children: [/* @__PURE__ */ l("div", {
43
50
  className: "flex items-center gap-3",
44
- children: [/* @__PURE__ */ s("div", {
51
+ children: [/* @__PURE__ */ c("div", {
45
52
  className: "flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10",
46
- children: /* @__PURE__ */ s(i, { className: "h-5 w-5 text-primary" })
47
- }), /* @__PURE__ */ c("div", { children: [/* @__PURE__ */ s("p", {
53
+ children: /* @__PURE__ */ c(a, { className: "h-5 w-5 text-primary" })
54
+ }), /* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("p", {
48
55
  className: "text-sm font-semibold text-text-primary",
49
56
  children: n
50
- }), /* @__PURE__ */ c("p", {
57
+ }), /* @__PURE__ */ l("p", {
51
58
  className: "text-xs text-text-secondary",
52
59
  children: [
53
60
  r,
54
61
  " · ",
55
- a
62
+ i
56
63
  ]
57
64
  })] })]
58
- }), l && /* @__PURE__ */ s("span", {
65
+ }), o && /* @__PURE__ */ c("span", {
59
66
  className: "inline-flex rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text",
60
67
  children: "Installed"
61
68
  })]
62
- }), /* @__PURE__ */ c("div", {
69
+ }), /* @__PURE__ */ l("div", {
63
70
  className: "mt-3 flex items-center justify-between text-xs text-text-secondary",
64
- children: [/* @__PURE__ */ c("div", {
71
+ children: [/* @__PURE__ */ l("div", {
65
72
  className: "flex items-center gap-1",
66
- children: [/* @__PURE__ */ s(o, { className: "h-3 w-3 fill-amber-400 text-status-warning-text" }), /* @__PURE__ */ s("span", { children: u })]
67
- }), /* @__PURE__ */ c("span", { children: [d, " downloads"] })]
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"] })]
68
75
  })]
69
76
  }, e.id);
70
77
  })
@@ -72,6 +79,6 @@ function l() {
72
79
  });
73
80
  }
74
81
  //#endregion
75
- export { l as WidgetsPage };
82
+ export { u as WidgetsPage };
76
83
 
77
84
  //# sourceMappingURL=WidgetsPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"WidgetsPage.js","names":[],"sources":["../../../../src/timecampus/pages/widgets/WidgetsPage.tsx"],"sourcesContent":["import { LayoutGrid, Plus, Star } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListConfigurationsQuery,\n type ConfigurationFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function WidgetsPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const { data, loading, error } = useListConfigurationsQuery({\n variables: { scope: \"WORKSPACE\" as any },\n });\n\n const configurations = data?.configurations ?? [];\n\n // Filter configurations that represent widgets (key starts with 'widget.')\n const widgets = configurations.filter((c: ConfigurationFieldsFragment) =>\n c.key.startsWith(\"widget.\"),\n );\n\n if (loading) {\n return (\n <PageLayout\n title=\"Widgets\"\n description=\"Customize your dashboard with widgets\"\n >\n <LoadingSkeleton variant=\"card\" count={6} />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"Widgets\"\n description=\"Customize your dashboard with widgets\"\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 Failed to load widget configurations. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Widgets\"\n description=\"Customize your dashboard with widgets\"\n actions={\n <button 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\">\n <Plus className=\"h-4 w-4\" />\n Create Widget\n </button>\n }\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {widgets.length === 0 ? (\n <div className=\"col-span-full p-8 text-center text-sm text-text-secondary\">\n No widgets configured yet. Widgets are stored as workspace\n configurations with the \"widget.\" key prefix.\n </div>\n ) : (\n widgets.map((widget: ConfigurationFieldsFragment) => {\n const val = widget.value as Record<string, unknown>;\n const name =\n (val.name as string) ?? widget.key.replace(\"widget.\", \"\");\n const category = (val.category as string) ?? \"General\";\n const size = (val.size as string) ?? \"medium\";\n const isInstalled = (val.isInstalled as boolean) ?? false;\n const rating = (val.rating as number) ?? 0;\n const downloads = (val.downloads as number) ?? 0;\n\n return (\n <div\n key={widget.id}\n className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop p-5 transition-colors hover:bg-bg-sunken/50 cursor-pointer\"\n >\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10\">\n <LayoutGrid className=\"h-5 w-5 text-primary\" />\n </div>\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">\n {name}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {category} &middot; {size}\n </p>\n </div>\n </div>\n {isInstalled && (\n <span className=\"inline-flex rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text\">\n Installed\n </span>\n )}\n </div>\n <div className=\"mt-3 flex items-center justify-between text-xs text-text-secondary\">\n <div className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 fill-amber-400 text-status-warning-text\" />\n <span>{rating}</span>\n </div>\n <span>{downloads} downloads</span>\n </div>\n </div>\n );\n })\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;AASA,SAAgB,IAAc;CAC5B,IAAM,EAAE,iBAAc,OAAO,GAAe,EAEtC,EAAE,SAAM,YAAS,aAAU,EAA2B,EAC1D,WAAW,EAAE,OAAO,aAAoB,EACzC,CAAC,EAKI,KAHiB,GAAM,kBAAkB,EAAE,EAGlB,QAAQ,MACrC,EAAE,IAAI,WAAW,UAAU,CAC5B;AA0BD,QAxBI,IAEA,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA;EACjC,CAAA,GAIb,IAEA,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,OAAD;GAAK,WAAU;aAAoG;GAE7G,CAAA;EACK,CAAA,GAKf,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,gBAErB;;YAGX,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAQ,WAAW,IAClB,kBAAC,OAAD;IAAK,WAAU;cAA4D;IAGrE,CAAA,GAEN,EAAQ,KAAK,MAAwC;IACnD,IAAM,IAAM,EAAO,OACb,IACH,EAAI,QAAmB,EAAO,IAAI,QAAQ,WAAW,GAAG,EACrD,IAAY,EAAI,YAAuB,WACvC,IAAQ,EAAI,QAAmB,UAC/B,IAAe,EAAI,eAA2B,IAC9C,IAAU,EAAI,UAAqB,GACnC,IAAa,EAAI,aAAwB;AAE/C,WACE,kBAAC,OAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD,EAAY,WAAU,wBAAyB,CAAA;QAC3C,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV;QACC,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACG;SAAS;SAAW;SACnB;UACA,EAAA,CAAA,CACF;UACL,KACC,kBAAC,QAAD;OAAM,WAAU;iBAAyG;OAElH,CAAA,CAEL;SACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAM,WAAU,mDAAoD,CAAA,EACpE,kBAAC,QAAD,EAAA,UAAO,GAAc,CAAA,CACjB;UACN,kBAAC,QAAD,EAAA,UAAA,CAAO,GAAU,aAAiB,EAAA,CAAA,CAC9B;QACF;OA9BC,EAAO,GA8BR;KAER;GAEA,CAAA;EACK,CAAA"}
1
+ {"version":3,"file":"WidgetsPage.js","names":[],"sources":["../../../../src/timecampus/pages/widgets/WidgetsPage.tsx"],"sourcesContent":["import { LayoutGrid, Plus, Star } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListConfigurationsQuery,\n type ConfigurationFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function WidgetsPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const { data, loading, error } = useListConfigurationsQuery({\n variables: { scope: \"WORKSPACE\" as any },\n });\n\n const configurations = data?.configurations ?? [];\n\n // Filter configurations that represent widgets (key starts with 'widget.')\n const widgets = configurations.filter((c: ConfigurationFieldsFragment) =>\n c.key.startsWith(\"widget.\"),\n );\n\n if (loading) {\n return (\n <PageLayout\n title=\"Widgets\"\n description=\"Customize your dashboard with widgets\"\n >\n <LoadingSkeleton variant=\"card\" count={6} />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"Widgets\"\n description=\"Customize your dashboard with widgets\"\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 Failed to load widget configurations. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Widgets\"\n description=\"Customize your dashboard with widgets\"\n 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.\"\n actions={\n <button 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\">\n <Plus className=\"h-4 w-4\" />\n Create Widget\n </button>\n }\n >\n {widgets.length === 0 ? (\n <EmptyState\n illustration=\"empty-generic\"\n title=\"No widgets configured yet\"\n description=\"Add your first widget to start customising your dashboard — widgets are stored as workspace configurations and appear here.\"\n action={\n <button 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\">\n <Plus className=\"h-4 w-4\" />\n Create Widget\n </button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {widgets.map((widget: ConfigurationFieldsFragment) => {\n const val = widget.value as Record<string, unknown>;\n const name =\n (val.name as string) ?? widget.key.replace(\"widget.\", \"\");\n const category = (val.category as string) ?? \"General\";\n const size = (val.size as string) ?? \"medium\";\n const isInstalled = (val.isInstalled as boolean) ?? false;\n const rating = (val.rating as number) ?? 0;\n const downloads = (val.downloads as number) ?? 0;\n\n return (\n <div\n key={widget.id}\n className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop p-5 transition-colors hover:bg-bg-sunken/50 cursor-pointer\"\n >\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10\">\n <LayoutGrid className=\"h-5 w-5 text-primary\" />\n </div>\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">\n {name}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {category} &middot; {size}\n </p>\n </div>\n </div>\n {isInstalled && (\n <span className=\"inline-flex rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text\">\n Installed\n </span>\n )}\n </div>\n <div className=\"mt-3 flex items-center justify-between text-xs text-text-secondary\">\n <div className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 fill-amber-400 text-status-warning-text\" />\n <span>{rating}</span>\n </div>\n <span>{downloads} downloads</span>\n </div>\n </div>\n );\n })}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;AAUA,SAAgB,IAAc;CAC5B,IAAM,EAAE,iBAAc,OAAO,GAAe,EAEtC,EAAE,SAAM,YAAS,aAAU,EAA2B,EAC1D,WAAW,EAAE,OAAO,aAAoB,EACzC,CAAC,EAKI,KAHiB,GAAM,kBAAkB,EAAE,EAGlB,QAAQ,MACrC,EAAE,IAAI,WAAW,UAAU,CAC5B;AA0BD,QAxBI,IAEA,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA;EACjC,CAAA,GAIb,IAEA,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,OAAD;GAAK,WAAU;aAAoG;GAE7G,CAAA;EACK,CAAA,GAKf,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SAAQ;EACR,SACE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,gBAErB;;YAGV,EAAQ,WAAW,IAClB,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,aAAY;GACZ,QACE,kBAAC,UAAD;IAAQ,WAAU;cAAlB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,gBAErB;;GAEX,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAQ,KAAK,MAAwC;IACpD,IAAM,IAAM,EAAO,OACb,IACH,EAAI,QAAmB,EAAO,IAAI,QAAQ,WAAW,GAAG,EACrD,IAAY,EAAI,YAAuB,WACvC,IAAQ,EAAI,QAAmB,UAC/B,IAAe,EAAI,eAA2B,IAC9C,IAAU,EAAI,UAAqB,GACnC,IAAa,EAAI,aAAwB;AAE/C,WACE,kBAAC,OAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD,EAAY,WAAU,wBAAyB,CAAA;QAC3C,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV;QACC,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACG;SAAS;SAAW;SACnB;UACA,EAAA,CAAA,CACF;UACL,KACC,kBAAC,QAAD;OAAM,WAAU;iBAAyG;OAElH,CAAA,CAEL;SACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAM,WAAU,mDAAoD,CAAA,EACpE,kBAAC,QAAD,EAAA,UAAO,GAAc,CAAA,CACjB;UACN,kBAAC,QAAD,EAAA,UAAA,CAAO,GAAU,aAAiB,EAAA,CAAA,CAC9B;QACF;OA9BC,EAAO,GA8BR;KAER;GACE,CAAA;EAEG,CAAA"}
@@ -28,10 +28,12 @@ function f() {
28
28
  }) : /* @__PURE__ */ d(a, {
29
29
  title: "Work Patterns",
30
30
  description: "Analyze your work habits and productivity patterns",
31
+ purpose: "Learn how you actually work across a typical week — when you focus best, where meetings pile up, and which day is your peak — so you can schedule deep work for the hours you're sharpest.",
31
32
  children: [/* @__PURE__ */ d("div", {
32
33
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
33
34
  children: [
34
35
  /* @__PURE__ */ u(t, {
36
+ featured: !0,
35
37
  title: "Productivity Score",
36
38
  value: C,
37
39
  icon: /* @__PURE__ */ u(c, { className: "h-5 w-5" }),