@burdenoff/microfe-timecampus 2026.624.1 → 2026.625.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/timecampus/components/ErrorBoundary.js +2 -2
- package/dist/timecampus/components/ErrorBoundary.js.map +1 -1
- package/dist/timecampus/components/LoadingSkeleton.js +1 -1
- package/dist/timecampus/components/LoadingSkeleton.js.map +1 -1
- package/dist/timecampus/components/PageLayout.js +17 -13
- package/dist/timecampus/components/PageLayout.js.map +1 -1
- package/dist/timecampus/components/StatCard.js +19 -16
- package/dist/timecampus/components/StatCard.js.map +1 -1
- package/dist/timecampus/pages/HomePage.js +169 -149
- package/dist/timecampus/pages/HomePage.js.map +1 -1
- package/dist/timecampus/pages/applets/AppletStorePage.js +11 -11
- package/dist/timecampus/pages/applets/AppletStorePage.js.map +1 -1
- package/dist/timecampus/pages/applets/InstalledAppletsPage.js +11 -11
- package/dist/timecampus/pages/applets/InstalledAppletsPage.js.map +1 -1
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +19 -19
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
- package/dist/timecampus/pages/application-usage/ApplicationUsageSettingsPage.js +10 -10
- package/dist/timecampus/pages/application-usage/ApplicationUsageSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/channels/ChannelsPage.js +11 -11
- package/dist/timecampus/pages/channels/ChannelsPage.js.map +1 -1
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js +15 -15
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
- package/dist/timecampus/pages/dashboard/AnalyticsPage.js +11 -11
- package/dist/timecampus/pages/dashboard/AnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/dashboard/ReportsPage.js +11 -11
- package/dist/timecampus/pages/dashboard/ReportsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailAnalyticsPage.js +14 -14
- package/dist/timecampus/pages/email-tracking/EmailAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailSettingsPage.js +10 -10
- package/dist/timecampus/pages/email-tracking/EmailSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +7 -7
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +15 -15
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/focus-score/FocusScorePage.js +21 -21
- package/dist/timecampus/pages/focus-score/FocusScorePage.js.map +1 -1
- package/dist/timecampus/pages/focus-score/FocusScoreSettingsPage.js +10 -10
- package/dist/timecampus/pages/focus-score/FocusScoreSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/geofencing/GeofencingPage.js +10 -10
- package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
- package/dist/timecampus/pages/geofencing/GeofencingSettingsPage.js +10 -10
- package/dist/timecampus/pages/geofencing/GeofencingSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js +10 -10
- package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js.map +1 -1
- package/dist/timecampus/pages/location-tracking/LocationPrivacySettingsPage.js +10 -10
- package/dist/timecampus/pages/location-tracking/LocationPrivacySettingsPage.js.map +1 -1
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +15 -15
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingAnalyticsPage.js +15 -15
- package/dist/timecampus/pages/meetings/MeetingAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingCreatePage.js +5 -5
- package/dist/timecampus/pages/meetings/MeetingCreatePage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js +16 -16
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingsPage.js +10 -10
- package/dist/timecampus/pages/meetings/MeetingsPage.js.map +1 -1
- package/dist/timecampus/pages/pomodoro/PomodoroAnalyticsPage.js +17 -17
- package/dist/timecampus/pages/pomodoro/PomodoroAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/pomodoro/PomodoroPage.js +16 -16
- package/dist/timecampus/pages/pomodoro/PomodoroPage.js.map +1 -1
- package/dist/timecampus/pages/pomodoro/PomodoroSettingsPage.js +12 -12
- package/dist/timecampus/pages/pomodoro/PomodoroSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectCreatePage.js +6 -6
- package/dist/timecampus/pages/projects/ProjectCreatePage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectDetailPage.js +17 -17
- package/dist/timecampus/pages/projects/ProjectDetailPage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectsPage.js +10 -10
- package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
- package/dist/timecampus/pages/projects/TasksPage.js +10 -10
- package/dist/timecampus/pages/projects/TasksPage.js.map +1 -1
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +18 -18
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamAnalyticsPage.js +11 -11
- package/dist/timecampus/pages/team/TeamAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamMembersPage.js +12 -12
- package/dist/timecampus/pages/team/TeamMembersPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamPage.js +16 -16
- package/dist/timecampus/pages/team/TeamPage.js.map +1 -1
- package/dist/timecampus/pages/team-activity/TeamActivityPage.js +16 -16
- package/dist/timecampus/pages/team-activity/TeamActivityPage.js.map +1 -1
- package/dist/timecampus/pages/team-activity/TeamActivitySettingsPage.js +10 -10
- package/dist/timecampus/pages/team-activity/TeamActivitySettingsPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplateApplicationPage.js +11 -11
- package/dist/timecampus/pages/templates/TemplateApplicationPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplateDetailPage.js +12 -12
- package/dist/timecampus/pages/templates/TemplateDetailPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplatesPage.js +11 -11
- package/dist/timecampus/pages/templates/TemplatesPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeEntriesPage.js +10 -10
- package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeTrackingPage.js +17 -17
- package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimerPage.js +10 -10
- package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +20 -20
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
- package/dist/timecampus/pages/widgets/WidgetDetailPage.js +11 -11
- package/dist/timecampus/pages/widgets/WidgetDetailPage.js.map +1 -1
- package/dist/timecampus/pages/widgets/WidgetsPage.js +12 -12
- package/dist/timecampus/pages/widgets/WidgetsPage.js.map +1 -1
- package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js +16 -16
- package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js.map +1 -1
- package/dist/timecampus/pages/work-patterns/WorkPatternsSettingsPage.js +10 -10
- package/dist/timecampus/pages/work-patterns/WorkPatternsSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/workflows/WorkflowsPage.js +10 -10
- package/dist/timecampus/pages/workflows/WorkflowsPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TemplatesPage.js","names":[],"sources":["../../../../src/timecampus/pages/templates/TemplatesPage.tsx"],"sourcesContent":["import { Sparkles, Star, Users } 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 TemplatesPage() {\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 templates (key starts with 'template.')\n const templates = configurations.filter((c: ConfigurationFieldsFragment) =>\n c.key.startsWith(\"template.\"),\n );\n\n if (loading) {\n return (\n <PageLayout\n title=\"Templates\"\n description=\"Pre-built workspace templates for different workflows\"\n >\n <LoadingSkeleton variant=\"card\" count={6} />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"Templates\"\n description=\"Pre-built workspace templates for different workflows\"\n >\n <div className=\"rounded-lg border border-
|
|
1
|
+
{"version":3,"file":"TemplatesPage.js","names":[],"sources":["../../../../src/timecampus/pages/templates/TemplatesPage.tsx"],"sourcesContent":["import { Sparkles, Star, Users } 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 TemplatesPage() {\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 templates (key starts with 'template.')\n const templates = configurations.filter((c: ConfigurationFieldsFragment) =>\n c.key.startsWith(\"template.\"),\n );\n\n if (loading) {\n return (\n <PageLayout\n title=\"Templates\"\n description=\"Pre-built workspace templates for different workflows\"\n >\n <LoadingSkeleton variant=\"card\" count={6} />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"Templates\"\n description=\"Pre-built workspace templates for different workflows\"\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 template configurations. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Templates\"\n description=\"Pre-built workspace templates for different workflows\"\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {templates.length === 0 ? (\n <div className=\"col-span-full p-8 text-center text-sm text-text-secondary\">\n No templates configured yet. Templates are stored as workspace\n configurations with the \"template.\" key prefix.\n </div>\n ) : (\n templates.map((template: ConfigurationFieldsFragment) => {\n const val = template.value as Record<string, unknown>;\n const name =\n (val.name as string) ?? template.key.replace(\"template.\", \"\");\n const category = (val.category as string) ?? \"General\";\n const industry = (val.industry as string) ?? \"Any\";\n const rating = (val.rating as number) ?? 0;\n const teamSize = (val.teamSize as string) ?? \"-\";\n const downloads = (val.downloads as number) ?? 0;\n\n return (\n <div\n key={template.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 gap-3\">\n <div className=\"flex h-12 w-12 items-center justify-center rounded-xl bg-action-primary-bg/10\">\n <Sparkles className=\"h-6 w-6 text-primary\" />\n </div>\n <div className=\"flex-1\">\n <p className=\"text-sm font-semibold text-text-primary\">\n {name}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {category} · {industry}\n </p>\n </div>\n </div>\n <div className=\"mt-3 flex items-center justify-between text-xs text-text-secondary\">\n <div className=\"flex items-center gap-3\">\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 <div className=\"flex items-center gap-1\">\n <Users className=\"h-3 w-3\" />\n <span>{teamSize}</span>\n </div>\n </div>\n <span>{downloads} uses</span>\n </div>\n </div>\n );\n })\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;AASA,SAAgB,IAAgB;CAC9B,IAAM,EAAE,iBAAc,OAAO,GAAe,EAEtC,EAAE,SAAM,YAAS,aAAU,EAA2B,EAC1D,WAAW,EAAE,OAAO,aAAoB,EACzC,CAAC,EAKI,KAHiB,GAAM,kBAAkB,EAAE,EAGhB,QAAQ,MACvC,EAAE,IAAI,WAAW,YAAY,CAC9B;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;YAEZ,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAU,WAAW,IACpB,kBAAC,OAAD;IAAK,WAAU;cAA4D;IAGrE,CAAA,GAEN,EAAU,KAAK,MAA0C;IACvD,IAAM,IAAM,EAAS,OACf,IACH,EAAI,QAAmB,EAAS,IAAI,QAAQ,aAAa,GAAG,EACzD,IAAY,EAAI,YAAuB,WACvC,IAAY,EAAI,YAAuB,OACvC,IAAU,EAAI,UAAqB,GACnC,IAAY,EAAI,YAAuB,KACvC,IAAa,EAAI,aAAwB;AAE/C,WACE,kBAAC,OAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAU,WAAU,wBAAyB,CAAA;OACzC,CAAA,EACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV;QACC,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACG;SAAS;SAAW;SACnB;UACA;SACF;SACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD,EAAM,WAAU,mDAAoD,CAAA,EACpE,kBAAC,QAAD,EAAA,UAAO,GAAc,CAAA,CACjB;WACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA,EAC7B,kBAAC,QAAD,EAAA,UAAO,GAAgB,CAAA,CACnB;UACF;UACN,kBAAC,QAAD,EAAA,UAAA,CAAO,GAAU,QAAY,EAAA,CAAA,CACzB;QACF;OA7BC,EAAS,GA6BV;KAER;GAEA,CAAA;EACK,CAAA"}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { formatDate as e, formatHours as t, getStatusColor as n } from "../../utils/helpers.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { LoadingSkeleton as r } from "../../components/LoadingSkeleton.js";
|
|
3
|
+
import { useListTimeEntriesQuery as i } from "../../../generated/wspace-operations.js";
|
|
4
|
+
import { PageLayout as a } from "../../components/PageLayout.js";
|
|
5
5
|
import { Filter as o, Plus as s } from "lucide-react";
|
|
6
6
|
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
7
7
|
//#region src/timecampus/pages/time/TimeEntriesPage.tsx
|
|
8
8
|
function u() {
|
|
9
|
-
let { data: u, loading: d, error: f } =
|
|
10
|
-
return f ? /* @__PURE__ */ c(
|
|
9
|
+
let { data: u, loading: d, error: f } = i({ variables: { limit: 50 } }), p = u?.timeEntries?.items ?? [];
|
|
10
|
+
return f ? /* @__PURE__ */ c(a, {
|
|
11
11
|
title: "Time Entries",
|
|
12
12
|
description: "View and manage all time entries",
|
|
13
13
|
children: /* @__PURE__ */ c("div", {
|
|
14
|
-
className: "rounded-xl border border-
|
|
14
|
+
className: "rounded-xl border border-status-error-bg/40 bg-status-error-bg p-6 text-sm text-status-error-text",
|
|
15
15
|
children: "Failed to load time entries. Please try again later."
|
|
16
16
|
})
|
|
17
|
-
}) : /* @__PURE__ */ c(
|
|
17
|
+
}) : /* @__PURE__ */ c(a, {
|
|
18
18
|
title: "Time Entries",
|
|
19
19
|
description: "View and manage all time entries",
|
|
20
20
|
actions: /* @__PURE__ */ l("div", {
|
|
@@ -27,11 +27,11 @@ function u() {
|
|
|
27
27
|
children: [/* @__PURE__ */ c(s, { className: "h-4 w-4" }), "Add Entry"]
|
|
28
28
|
})]
|
|
29
29
|
}),
|
|
30
|
-
children: d ? /* @__PURE__ */ c(
|
|
30
|
+
children: d ? /* @__PURE__ */ c(r, {
|
|
31
31
|
variant: "table",
|
|
32
32
|
count: 5
|
|
33
33
|
}) : /* @__PURE__ */ c("div", {
|
|
34
|
-
className: "rounded-xl border border-border-
|
|
34
|
+
className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop",
|
|
35
35
|
children: /* @__PURE__ */ c("div", {
|
|
36
36
|
className: "overflow-x-auto",
|
|
37
37
|
children: /* @__PURE__ */ l("table", {
|
|
@@ -88,7 +88,7 @@ function u() {
|
|
|
88
88
|
/* @__PURE__ */ c("td", {
|
|
89
89
|
className: "px-4 py-3",
|
|
90
90
|
children: /* @__PURE__ */ c("span", {
|
|
91
|
-
className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${r.billable ? "bg-
|
|
91
|
+
className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${r.billable ? "bg-status-success-bg text-status-success-text" : "bg-bg-sunken text-text-secondary"}`,
|
|
92
92
|
children: r.billable ? "Yes" : "No"
|
|
93
93
|
})
|
|
94
94
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeEntriesPage.js","names":[],"sources":["../../../../src/timecampus/pages/time/TimeEntriesPage.tsx"],"sourcesContent":["import { Clock, Plus, Filter } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { formatHours, formatDate, getStatusColor } from \"../../utils/helpers\";\nimport {\n useListTimeEntriesQuery,\n type TimeEntryFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function TimeEntriesPage() {\n const { data, loading, error } = useListTimeEntriesQuery({\n variables: { limit: 50 },\n });\n\n const entries = data?.timeEntries?.items ?? [];\n\n if (error) {\n return (\n <PageLayout\n title=\"Time Entries\"\n description=\"View and manage all time entries\"\n >\n <div className=\"rounded-xl border border-
|
|
1
|
+
{"version":3,"file":"TimeEntriesPage.js","names":[],"sources":["../../../../src/timecampus/pages/time/TimeEntriesPage.tsx"],"sourcesContent":["import { Clock, Plus, Filter } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { formatHours, formatDate, getStatusColor } from \"../../utils/helpers\";\nimport {\n useListTimeEntriesQuery,\n type TimeEntryFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function TimeEntriesPage() {\n const { data, loading, error } = useListTimeEntriesQuery({\n variables: { limit: 50 },\n });\n\n const entries = data?.timeEntries?.items ?? [];\n\n if (error) {\n return (\n <PageLayout\n title=\"Time Entries\"\n description=\"View and manage all time entries\"\n >\n <div className=\"rounded-xl border border-status-error-bg/40 bg-status-error-bg p-6 text-sm text-status-error-text\">\n Failed to load time entries. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Time Entries\"\n description=\"View and manage all time entries\"\n actions={\n <div className=\"flex items-center gap-2\">\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 <Filter className=\"h-4 w-4\" />\n Filter\n </button>\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 Add Entry\n </button>\n </div>\n }\n >\n {loading ? (\n <LoadingSkeleton variant=\"table\" count={5} />\n ) : (\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop\">\n <div className=\"overflow-x-auto\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"border-b border-border-subtle\">\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary\">\n Date\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary\">\n Project\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary\">\n Task\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary\">\n Duration\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary\">\n Billable\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary\">\n Status\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {entries.map((entry: TimeEntryFieldsFragment) => (\n <tr\n key={entry.id}\n className=\"hover:bg-bg-sunken/50 transition-colors\"\n >\n <td className=\"px-4 py-3 text-sm text-text-secondary\">\n {formatDate(entry.startTime)}\n </td>\n <td className=\"px-4 py-3 text-sm font-medium text-text-primary\">\n {entry.project?.name ?? \"No Project\"}\n </td>\n <td className=\"px-4 py-3 text-sm text-text-secondary\">\n {entry.task?.title ??\n entry.description ??\n \"No description\"}\n </td>\n <td className=\"px-4 py-3 text-sm font-mono text-text-primary\">\n {formatHours((entry.duration ?? 0) / 3600)}\n </td>\n <td className=\"px-4 py-3\">\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${entry.billable ? \"bg-status-success-bg text-status-success-text\" : \"bg-bg-sunken text-text-secondary\"}`}\n >\n {entry.billable ? \"Yes\" : \"No\"}\n </span>\n </td>\n <td className=\"px-4 py-3\">\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getStatusColor(entry.isRunning ? \"running\" : \"completed\")}`}\n >\n {entry.isRunning ? \"running\" : \"completed\"}\n </span>\n </td>\n </tr>\n ))}\n {entries.length === 0 && (\n <tr>\n <td\n colSpan={6}\n className=\"px-4 py-8 text-center text-sm text-text-secondary\"\n >\n No time entries found\n </td>\n </tr>\n )}\n </tbody>\n </table>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;AASA,SAAgB,IAAkB;CAChC,IAAM,EAAE,SAAM,YAAS,aAAU,EAAwB,EACvD,WAAW,EAAE,OAAO,IAAI,EACzB,CAAC,EAEI,IAAU,GAAM,aAAa,SAAS,EAAE;AAe9C,QAbI,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,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,UAAD;IAAQ,WAAU;cAAlB,CACE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA,EAAA,SAEvB;OACT,kBAAC,UAAD;IAAQ,WAAU;cAAlB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,YAErB;MACL;;YAGP,IACC,kBAAC,GAAD;GAAiB,SAAQ;GAAQ,OAAO;GAAK,CAAA,GAE7C,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,SAAD;KAAO,WAAU;eAAjB,CACE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD;MAAI,WAAU;gBAAd;OACE,kBAAC,MAAD;QAAI,WAAU;kBAA8D;QAEvE,CAAA;OACL,kBAAC,MAAD;QAAI,WAAU;kBAA8D;QAEvE,CAAA;OACL,kBAAC,MAAD;QAAI,WAAU;kBAA8D;QAEvE,CAAA;OACL,kBAAC,MAAD;QAAI,WAAU;kBAA8D;QAEvE,CAAA;OACL,kBAAC,MAAD;QAAI,WAAU;kBAA8D;QAEvE,CAAA;OACL,kBAAC,MAAD;QAAI,WAAU;kBAA8D;QAEvE,CAAA;OACF;SACC,CAAA,EACR,kBAAC,SAAD;MAAO,WAAU;gBAAjB,CACG,EAAQ,KAAK,MACZ,kBAAC,MAAD;OAEE,WAAU;iBAFZ;QAIE,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAW,EAAM,UAAU;SACzB,CAAA;QACL,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAM,SAAS,QAAQ;SACrB,CAAA;QACL,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAM,MAAM,SACX,EAAM,eACN;SACC,CAAA;QACL,kBAAC,MAAD;SAAI,WAAU;mBACX,GAAa,EAAM,YAAY,KAAK,KAAK;SACvC,CAAA;QACL,kBAAC,MAAD;SAAI,WAAU;mBACZ,kBAAC,QAAD;UACE,WAAW,4DAA4D,EAAM,WAAW,kDAAkD;oBAEzI,EAAM,WAAW,QAAQ;UACrB,CAAA;SACJ,CAAA;QACL,kBAAC,MAAD;SAAI,WAAU;mBACZ,kBAAC,QAAD;UACE,WAAW,4DAA4D,EAAe,EAAM,YAAY,YAAY,YAAY;oBAE/H,EAAM,YAAY,YAAY;UAC1B,CAAA;SACJ,CAAA;QACF;SA/BE,EAAM,GA+BR,CACL,EACD,EAAQ,WAAW,KAClB,kBAAC,MAAD,EAAA,UACE,kBAAC,MAAD;OACE,SAAS;OACT,WAAU;iBACX;OAEI,CAAA,EACF,CAAA,CAED;QACF;;IACJ,CAAA;GACF,CAAA;EAEG,CAAA"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { formatHours as e, formatTimerDuration as t } from "../../utils/helpers.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
2
|
+
import { StatCard as n } from "../../components/StatCard.js";
|
|
3
|
+
import { LoadingSkeleton as r } from "../../components/LoadingSkeleton.js";
|
|
4
|
+
import { useTimeCampusStore as i } from "../../store/timecampusStore.js";
|
|
5
|
+
import { useGetTimeEntryStatsQuery as a, useListTimeEntriesQuery as o } from "../../../generated/wspace-operations.js";
|
|
6
|
+
import { PageLayout as s } from "../../components/PageLayout.js";
|
|
7
7
|
import { Clock as c, Pause as l, Play as u, Square as d } from "lucide-react";
|
|
8
8
|
import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
9
9
|
import { useI18n as h } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
@@ -12,20 +12,20 @@ function g() {
|
|
|
12
12
|
let { t: g } = h(), _ = (e, t) => {
|
|
13
13
|
let n = g(e);
|
|
14
14
|
return n === e ? t : n;
|
|
15
|
-
}, { activeTimer: v, startTimer: y, stopTimer: b } =
|
|
16
|
-
return S ? /* @__PURE__ */ p(
|
|
15
|
+
}, { activeTimer: v, startTimer: y, stopTimer: b } = i(), { data: x, loading: S } = a(), { data: C, loading: w } = o({ variables: { limit: 10 } }), T = x?.timeEntryStats, E = C?.timeEntries?.items ?? [];
|
|
16
|
+
return S ? /* @__PURE__ */ p(s, {
|
|
17
17
|
title: _("timecampus.timeTracking.title", "Time Tracking"),
|
|
18
18
|
description: _("timecampus.timeTracking.description", "Track and manage your work hours"),
|
|
19
|
-
children: /* @__PURE__ */ p(
|
|
19
|
+
children: /* @__PURE__ */ p(r, {
|
|
20
20
|
variant: "card",
|
|
21
21
|
count: 4
|
|
22
22
|
})
|
|
23
|
-
}) : /* @__PURE__ */ m(
|
|
23
|
+
}) : /* @__PURE__ */ m(s, {
|
|
24
24
|
title: _("timecampus.timeTracking.title", "Time Tracking"),
|
|
25
25
|
description: _("timecampus.timeTracking.description", "Track and manage your work hours"),
|
|
26
26
|
actions: /* @__PURE__ */ p("button", {
|
|
27
27
|
onClick: () => v ? b() : y(),
|
|
28
|
-
className: `inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium ${v ? "bg-
|
|
28
|
+
className: `inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium ${v ? "bg-action-danger-bg text-action-primary-text hover:bg-action-danger-bg/90" : "bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90"}`,
|
|
29
29
|
children: v ? /* @__PURE__ */ m(f, { children: [
|
|
30
30
|
/* @__PURE__ */ p(d, { className: "h-4 w-4" }),
|
|
31
31
|
_("timecampus.timeTracking.actions.stop", "Stop"),
|
|
@@ -37,7 +37,7 @@ function g() {
|
|
|
37
37
|
children: [/* @__PURE__ */ m("div", {
|
|
38
38
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
|
|
39
39
|
children: [
|
|
40
|
-
/* @__PURE__ */ p(
|
|
40
|
+
/* @__PURE__ */ p(n, {
|
|
41
41
|
title: _("timecampus.timeTracking.stats.today", "Today"),
|
|
42
42
|
value: e(T?.totalHours ?? 0),
|
|
43
43
|
icon: /* @__PURE__ */ p(c, { className: "h-5 w-5" }),
|
|
@@ -46,30 +46,30 @@ function g() {
|
|
|
46
46
|
label: _("timecampus.timeTracking.stats.vsYesterday", "vs yesterday")
|
|
47
47
|
}
|
|
48
48
|
}),
|
|
49
|
-
/* @__PURE__ */ p(
|
|
49
|
+
/* @__PURE__ */ p(n, {
|
|
50
50
|
title: _("timecampus.timeTracking.stats.billable", "Billable"),
|
|
51
51
|
value: e(T?.billableHours ?? 0),
|
|
52
52
|
icon: /* @__PURE__ */ p(c, { className: "h-5 w-5" }),
|
|
53
53
|
description: `${T?.totalEntries ?? 0} ${_("timecampus.timeTracking.stats.entries", "entries")}`
|
|
54
54
|
}),
|
|
55
|
-
/* @__PURE__ */ p(
|
|
55
|
+
/* @__PURE__ */ p(n, {
|
|
56
56
|
title: _("timecampus.timeTracking.stats.activeTimers", "Active Timers"),
|
|
57
57
|
value: T?.activeTimers ?? 0,
|
|
58
58
|
icon: /* @__PURE__ */ p(c, { className: "h-5 w-5" }),
|
|
59
59
|
description: _("timecampus.timeTracking.stats.currentlyRunning", "Currently running")
|
|
60
60
|
}),
|
|
61
|
-
/* @__PURE__ */ p(
|
|
61
|
+
/* @__PURE__ */ p(n, {
|
|
62
62
|
title: _("timecampus.timeTracking.stats.activeTimer", "Active Timer"),
|
|
63
63
|
value: v ? t(v.seconds) : _("timecampus.timeTracking.stats.stopped", "Stopped"),
|
|
64
64
|
icon: p(v ? l : u, { className: "h-5 w-5" })
|
|
65
65
|
})
|
|
66
66
|
]
|
|
67
67
|
}), /* @__PURE__ */ m("div", {
|
|
68
|
-
className: "rounded-xl border border-border-
|
|
68
|
+
className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6",
|
|
69
69
|
children: [/* @__PURE__ */ p("h3", {
|
|
70
70
|
className: "mb-4 text-sm font-semibold text-text-primary",
|
|
71
71
|
children: _("timecampus.timeTracking.entries.title", "Today's Entries")
|
|
72
|
-
}), w ? /* @__PURE__ */ p(
|
|
72
|
+
}), w ? /* @__PURE__ */ p(r, {
|
|
73
73
|
variant: "list",
|
|
74
74
|
count: 4
|
|
75
75
|
}) : E.length === 0 ? /* @__PURE__ */ p("div", {
|
|
@@ -97,7 +97,7 @@ function g() {
|
|
|
97
97
|
className: "text-sm font-mono font-medium text-text-primary",
|
|
98
98
|
children: e((t.duration ?? 0) / 3600)
|
|
99
99
|
}), /* @__PURE__ */ p("span", {
|
|
100
|
-
className: "inline-flex items-center rounded-full bg-
|
|
100
|
+
className: "inline-flex items-center rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text",
|
|
101
101
|
children: t.isRunning ? _("timecampus.timeTracking.entries.status.running", "running") : _("timecampus.timeTracking.entries.status.completed", "completed")
|
|
102
102
|
})]
|
|
103
103
|
})]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeTrackingPage.js","names":[],"sources":["../../../../src/timecampus/pages/time/TimeTrackingPage.tsx"],"sourcesContent":["import { Clock, Play, Pause, Square } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { formatHours, formatTimerDuration } from \"../../utils/helpers\";\nimport { useTimeCampusStore } from \"../../store/timecampusStore\";\nimport {\n useGetTimeEntryStatsQuery,\n useListTimeEntriesQuery,\n type TimeEntryFieldsFragment,\n} from \"@/generated/wspace-operations\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\n\nexport function TimeTrackingPage() {\n const { t: translate } = useI18n();\n const t = (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n const { activeTimer, startTimer, stopTimer } = useTimeCampusStore();\n\n const { data: statsData, loading: statsLoading } =\n useGetTimeEntryStatsQuery();\n const { data: entriesData, loading: entriesLoading } =\n useListTimeEntriesQuery({\n variables: { limit: 10 },\n });\n\n const stats = statsData?.timeEntryStats;\n const todayEntries = entriesData?.timeEntries?.items ?? [];\n\n if (statsLoading) {\n return (\n <PageLayout\n title={t(\"timecampus.timeTracking.title\", \"Time Tracking\")}\n description={t(\n \"timecampus.timeTracking.description\",\n \"Track and manage your work hours\",\n )}\n >\n <LoadingSkeleton variant=\"card\" count={4} />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={t(\"timecampus.timeTracking.title\", \"Time Tracking\")}\n description={t(\n \"timecampus.timeTracking.description\",\n \"Track and manage your work hours\",\n )}\n actions={\n <button\n onClick={() => (activeTimer ? stopTimer() : startTimer())}\n className={`inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium ${activeTimer ? \"bg-
|
|
1
|
+
{"version":3,"file":"TimeTrackingPage.js","names":[],"sources":["../../../../src/timecampus/pages/time/TimeTrackingPage.tsx"],"sourcesContent":["import { Clock, Play, Pause, Square } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { formatHours, formatTimerDuration } from \"../../utils/helpers\";\nimport { useTimeCampusStore } from \"../../store/timecampusStore\";\nimport {\n useGetTimeEntryStatsQuery,\n useListTimeEntriesQuery,\n type TimeEntryFieldsFragment,\n} from \"@/generated/wspace-operations\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\n\nexport function TimeTrackingPage() {\n const { t: translate } = useI18n();\n const t = (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n const { activeTimer, startTimer, stopTimer } = useTimeCampusStore();\n\n const { data: statsData, loading: statsLoading } =\n useGetTimeEntryStatsQuery();\n const { data: entriesData, loading: entriesLoading } =\n useListTimeEntriesQuery({\n variables: { limit: 10 },\n });\n\n const stats = statsData?.timeEntryStats;\n const todayEntries = entriesData?.timeEntries?.items ?? [];\n\n if (statsLoading) {\n return (\n <PageLayout\n title={t(\"timecampus.timeTracking.title\", \"Time Tracking\")}\n description={t(\n \"timecampus.timeTracking.description\",\n \"Track and manage your work hours\",\n )}\n >\n <LoadingSkeleton variant=\"card\" count={4} />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={t(\"timecampus.timeTracking.title\", \"Time Tracking\")}\n description={t(\n \"timecampus.timeTracking.description\",\n \"Track and manage your work hours\",\n )}\n actions={\n <button\n onClick={() => (activeTimer ? stopTimer() : startTimer())}\n className={`inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium ${activeTimer ? \"bg-action-danger-bg text-action-primary-text hover:bg-action-danger-bg/90\" : \"bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90\"}`}\n >\n {activeTimer ? (\n <>\n <Square className=\"h-4 w-4\" />\n {t(\"timecampus.timeTracking.actions.stop\", \"Stop\")} (\n {formatTimerDuration(activeTimer.seconds)})\n </>\n ) : (\n <>\n <Play className=\"h-4 w-4\" />\n {t(\"timecampus.timeTracking.actions.startTimer\", \"Start Timer\")}\n </>\n )}\n </button>\n }\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n title={t(\"timecampus.timeTracking.stats.today\", \"Today\")}\n value={formatHours(stats?.totalHours ?? 0)}\n icon={<Clock className=\"h-5 w-5\" />}\n trend={{\n value: 15,\n label: t(\n \"timecampus.timeTracking.stats.vsYesterday\",\n \"vs yesterday\",\n ),\n }}\n />\n <StatCard\n title={t(\"timecampus.timeTracking.stats.billable\", \"Billable\")}\n value={formatHours(stats?.billableHours ?? 0)}\n icon={<Clock className=\"h-5 w-5\" />}\n description={`${stats?.totalEntries ?? 0} ${t(\"timecampus.timeTracking.stats.entries\", \"entries\")}`}\n />\n <StatCard\n title={t(\n \"timecampus.timeTracking.stats.activeTimers\",\n \"Active Timers\",\n )}\n value={stats?.activeTimers ?? 0}\n icon={<Clock className=\"h-5 w-5\" />}\n description={t(\n \"timecampus.timeTracking.stats.currentlyRunning\",\n \"Currently running\",\n )}\n />\n <StatCard\n title={t(\"timecampus.timeTracking.stats.activeTimer\", \"Active Timer\")}\n value={\n activeTimer\n ? formatTimerDuration(activeTimer.seconds)\n : t(\"timecampus.timeTracking.stats.stopped\", \"Stopped\")\n }\n icon={\n activeTimer ? (\n <Pause className=\"h-5 w-5\" />\n ) : (\n <Play className=\"h-5 w-5\" />\n )\n }\n />\n </div>\n\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6\">\n <h3 className=\"mb-4 text-sm font-semibold text-text-primary\">\n {t(\"timecampus.timeTracking.entries.title\", \"Today's Entries\")}\n </h3>\n {entriesLoading ? (\n <LoadingSkeleton variant=\"list\" count={4} />\n ) : todayEntries.length === 0 ? (\n <div className=\"flex h-48 items-center justify-center rounded-lg border border-dashed border-border-subtle\">\n <p className=\"text-sm text-text-secondary\">\n {t(\n \"timecampus.timeTracking.entries.empty\",\n \"No time entries yet. Start a timer to begin tracking.\",\n )}\n </p>\n </div>\n ) : (\n <div className=\"flex flex-col gap-2\">\n {todayEntries.map((entry: TimeEntryFieldsFragment) => (\n <div\n key={entry.id}\n className=\"flex items-center justify-between rounded-lg border border-border-subtle p-3 transition-colors hover:bg-bg-sunken/50\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {entry.project?.name ??\n t(\n \"timecampus.timeTracking.entries.noProject\",\n \"No Project\",\n )}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {entry.description ??\n t(\n \"timecampus.timeTracking.entries.noDescription\",\n \"No description\",\n )}\n </p>\n </div>\n <div className=\"ml-4 text-right\">\n <p className=\"text-sm font-mono font-medium text-text-primary\">\n {formatHours((entry.duration ?? 0) / 3600)}\n </p>\n <span className=\"inline-flex items-center rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text\">\n {entry.isRunning\n ? t(\n \"timecampus.timeTracking.entries.status.running\",\n \"running\",\n )\n : t(\n \"timecampus.timeTracking.entries.status.completed\",\n \"completed\",\n )}\n </span>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { formatTimerDuration as e } from "../../utils/helpers.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { LoadingSkeleton as t } from "../../components/LoadingSkeleton.js";
|
|
3
|
+
import { useTimeCampusStore as n } from "../../store/timecampusStore.js";
|
|
4
|
+
import { useListRunningTimersQuery as r } from "../../../generated/wspace-operations.js";
|
|
5
|
+
import { PageLayout as i } from "../../components/PageLayout.js";
|
|
6
6
|
import { Play as a, RotateCcw as o, Square as s } from "lucide-react";
|
|
7
7
|
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
8
8
|
//#region src/timecampus/pages/time/TimerPage.tsx
|
|
9
9
|
function u() {
|
|
10
|
-
let { activeTimer: u, startTimer: d, stopTimer: f } =
|
|
11
|
-
return /* @__PURE__ */ c(
|
|
10
|
+
let { activeTimer: u, startTimer: d, stopTimer: f } = n(), { data: p, loading: m } = r(), h = p?.runningTimers ?? [];
|
|
11
|
+
return /* @__PURE__ */ c(i, {
|
|
12
12
|
title: "Timer",
|
|
13
13
|
description: "Focus timer for deep work sessions",
|
|
14
14
|
children: /* @__PURE__ */ l("div", {
|
|
15
15
|
className: "mx-auto max-w-lg",
|
|
16
16
|
children: [/* @__PURE__ */ l("div", {
|
|
17
|
-
className: "rounded-xl border border-border-
|
|
17
|
+
className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop p-8 text-center",
|
|
18
18
|
children: [
|
|
19
19
|
/* @__PURE__ */ c("p", {
|
|
20
20
|
className: "text-6xl font-mono font-bold text-text-primary",
|
|
@@ -28,7 +28,7 @@ function u() {
|
|
|
28
28
|
className: "mt-6 flex items-center justify-center gap-3",
|
|
29
29
|
children: [u ? /* @__PURE__ */ l("button", {
|
|
30
30
|
onClick: f,
|
|
31
|
-
className: "inline-flex items-center gap-2 rounded-md bg-
|
|
31
|
+
className: "inline-flex items-center gap-2 rounded-md bg-action-danger-bg px-6 py-3 text-sm font-medium text-action-primary-text hover:bg-action-danger-bg/90",
|
|
32
32
|
children: [/* @__PURE__ */ c(s, { className: "h-4 w-4" }), "Stop"]
|
|
33
33
|
}) : /* @__PURE__ */ l("button", {
|
|
34
34
|
onClick: () => d(),
|
|
@@ -41,11 +41,11 @@ function u() {
|
|
|
41
41
|
})
|
|
42
42
|
]
|
|
43
43
|
}), /* @__PURE__ */ l("div", {
|
|
44
|
-
className: "mt-6 rounded-xl border border-border-
|
|
44
|
+
className: "mt-6 rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6",
|
|
45
45
|
children: [/* @__PURE__ */ c("h3", {
|
|
46
46
|
className: "mb-4 text-sm font-semibold text-text-primary",
|
|
47
47
|
children: "Running Timers"
|
|
48
|
-
}), m ? /* @__PURE__ */ c(
|
|
48
|
+
}), m ? /* @__PURE__ */ c(t, {
|
|
49
49
|
variant: "list",
|
|
50
50
|
count: 2
|
|
51
51
|
}) : h.length === 0 ? /* @__PURE__ */ c("p", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimerPage.js","names":[],"sources":["../../../../src/timecampus/pages/time/TimerPage.tsx"],"sourcesContent":["import { Play, Square, RotateCcw } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { formatTimerDuration } from \"../../utils/helpers\";\nimport { useTimeCampusStore } from \"../../store/timecampusStore\";\nimport {\n useListRunningTimersQuery,\n type ListRunningTimersQuery,\n} from \"@/generated/wspace-operations\";\n\ntype RunningTimerItem = ListRunningTimersQuery[\"runningTimers\"][number];\n\nexport function TimerPage() {\n const { activeTimer, startTimer, stopTimer } = useTimeCampusStore();\n\n const { data: runningData, loading: runningLoading } =\n useListRunningTimersQuery();\n\n const runningTimers = runningData?.runningTimers ?? [];\n\n return (\n <PageLayout title=\"Timer\" description=\"Focus timer for deep work sessions\">\n <div className=\"mx-auto max-w-lg\">\n <div className=\"rounded-xl border border-border-
|
|
1
|
+
{"version":3,"file":"TimerPage.js","names":[],"sources":["../../../../src/timecampus/pages/time/TimerPage.tsx"],"sourcesContent":["import { Play, Square, RotateCcw } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { formatTimerDuration } from \"../../utils/helpers\";\nimport { useTimeCampusStore } from \"../../store/timecampusStore\";\nimport {\n useListRunningTimersQuery,\n type ListRunningTimersQuery,\n} from \"@/generated/wspace-operations\";\n\ntype RunningTimerItem = ListRunningTimersQuery[\"runningTimers\"][number];\n\nexport function TimerPage() {\n const { activeTimer, startTimer, stopTimer } = useTimeCampusStore();\n\n const { data: runningData, loading: runningLoading } =\n useListRunningTimersQuery();\n\n const runningTimers = runningData?.runningTimers ?? [];\n\n return (\n <PageLayout title=\"Timer\" description=\"Focus timer for deep work sessions\">\n <div className=\"mx-auto max-w-lg\">\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop p-8 text-center\">\n <p className=\"text-6xl font-mono font-bold text-text-primary\">\n {activeTimer\n ? formatTimerDuration(activeTimer.seconds)\n : \"00:00:00\"}\n </p>\n {activeTimer?.description && (\n <p className=\"mt-2 text-sm text-text-secondary\">\n {activeTimer.description}\n </p>\n )}\n <div className=\"mt-6 flex items-center justify-center gap-3\">\n {activeTimer ? (\n <button\n onClick={stopTimer}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-danger-bg px-6 py-3 text-sm font-medium text-action-primary-text hover:bg-action-danger-bg/90\"\n >\n <Square className=\"h-4 w-4\" />\n Stop\n </button>\n ) : (\n <button\n onClick={() => startTimer()}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-6 py-3 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Play className=\"h-4 w-4\" />\n Start\n </button>\n )}\n <button className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-4 py-3 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\">\n <RotateCcw className=\"h-4 w-4\" />\n Reset\n </button>\n </div>\n </div>\n\n {/* Server-side running timers */}\n <div className=\"mt-6 rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6\">\n <h3 className=\"mb-4 text-sm font-semibold text-text-primary\">\n Running Timers\n </h3>\n {runningLoading ? (\n <LoadingSkeleton variant=\"list\" count={2} />\n ) : runningTimers.length === 0 ? (\n <p className=\"text-center text-sm text-text-secondary py-4\">\n No active timers on the server\n </p>\n ) : (\n <div className=\"flex flex-col gap-2\">\n {runningTimers.map((timer: RunningTimerItem) => (\n <div\n key={timer.timeEntry.id}\n className=\"flex items-center justify-between rounded-lg border border-border-subtle p-3\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {timer.timeEntry.project?.name ?? \"No Project\"}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {timer.timeEntry.description ?? \"No description\"}\n </p>\n </div>\n <p className=\"ml-4 text-sm font-mono font-medium text-text-primary\">\n {formatTimerDuration(timer.elapsedSeconds)}\n </p>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;AAYA,SAAgB,IAAY;CAC1B,IAAM,EAAE,gBAAa,eAAY,iBAAc,GAAoB,EAE7D,EAAE,MAAM,GAAa,SAAS,MAClC,GAA2B,EAEvB,IAAgB,GAAa,iBAAiB,EAAE;AAEtD,QACE,kBAAC,GAAD;EAAY,OAAM;EAAQ,aAAY;YACpC,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,KAAD;MAAG,WAAU;gBACV,IACG,EAAoB,EAAY,QAAQ,GACxC;MACF,CAAA;KACH,GAAa,eACZ,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAY;MACX,CAAA;KAEN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACG,IACC,kBAAC,UAAD;OACE,SAAS;OACT,WAAU;iBAFZ,CAIE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA,EAAA,OAEvB;WAET,kBAAC,UAAD;OACE,eAAe,GAAY;OAC3B,WAAU;iBAFZ,CAIE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,QAErB;UAEX,kBAAC,UAAD;OAAQ,WAAU;iBAAlB,CACE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,QAE1B;SACL;;KACF;OAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,MAAD;KAAI,WAAU;eAA+C;KAExD,CAAA,EACJ,IACC,kBAAC,GAAD;KAAiB,SAAQ;KAAO,OAAO;KAAK,CAAA,GAC1C,EAAc,WAAW,IAC3B,kBAAC,KAAD;KAAG,WAAU;eAA+C;KAExD,CAAA,GAEJ,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAc,KAAK,MAClB,kBAAC,OAAD;MAEE,WAAU;gBAFZ,CAIE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAM,UAAU,SAAS,QAAQ;QAChC,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAM,UAAU,eAAe;QAC9B,CAAA,CACA;UACN,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAoB,EAAM,eAAe;OACxC,CAAA,CACA;QAdC,EAAM,UAAU,GAcjB,CACN;KACE,CAAA,CAEJ;MACF;;EACK,CAAA"}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
|
|
2
2
|
import { formatDate as t, formatHours as n, getInitials as r, getStatusColor as i } from "../../utils/helpers.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { StatCard as a } from "../../components/StatCard.js";
|
|
4
|
+
import { LoadingSkeleton as o } from "../../components/LoadingSkeleton.js";
|
|
5
|
+
import { useApproveRequestMutation as s, useGetApprovalStatsQuery as c, useListApprovalRequestsQuery as l, useRejectRequestMutation as u } from "../../../generated/wspace-operations.js";
|
|
6
|
+
import { PageLayout as d } from "../../components/PageLayout.js";
|
|
7
7
|
import { CheckCircle as f, Clock as p, XCircle as m } from "lucide-react";
|
|
8
8
|
import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
9
9
|
//#region src/timecampus/pages/time-approval/TimeApprovalPage.tsx
|
|
10
10
|
function _() {
|
|
11
|
-
let { workspaceId: _ = "" } = e(), { data: v, loading: y, error: b } =
|
|
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
12
|
await w({ variables: { id: e } });
|
|
13
13
|
}, M = async (e) => {
|
|
14
14
|
await E({ variables: {
|
|
@@ -16,52 +16,52 @@ function _() {
|
|
|
16
16
|
reason: "Rejected by reviewer"
|
|
17
17
|
} });
|
|
18
18
|
};
|
|
19
|
-
return y && S ? /* @__PURE__ */ h(
|
|
19
|
+
return y && S ? /* @__PURE__ */ h(d, {
|
|
20
20
|
title: "Time Approval",
|
|
21
21
|
description: "Review and approve team timesheets",
|
|
22
|
-
children: /* @__PURE__ */ h(
|
|
22
|
+
children: /* @__PURE__ */ h(o, {
|
|
23
23
|
variant: "list",
|
|
24
24
|
count: 4
|
|
25
25
|
})
|
|
26
|
-
}) : b && C ? /* @__PURE__ */ h(
|
|
26
|
+
}) : b && C ? /* @__PURE__ */ h(d, {
|
|
27
27
|
title: "Time Approval",
|
|
28
28
|
description: "Review and approve team timesheets",
|
|
29
29
|
children: /* @__PURE__ */ h("div", {
|
|
30
|
-
className: "rounded-lg border border-
|
|
30
|
+
className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
|
|
31
31
|
children: "Failed to load approval data. Please try again later."
|
|
32
32
|
})
|
|
33
|
-
}) : /* @__PURE__ */ g(
|
|
33
|
+
}) : /* @__PURE__ */ g(d, {
|
|
34
34
|
title: "Time Approval",
|
|
35
35
|
description: "Review and approve team timesheets",
|
|
36
36
|
children: [/* @__PURE__ */ g("div", {
|
|
37
37
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
|
|
38
38
|
children: [
|
|
39
|
-
/* @__PURE__ */ h(
|
|
39
|
+
/* @__PURE__ */ h(a, {
|
|
40
40
|
title: "Pending Approvals",
|
|
41
41
|
value: String(k?.pendingCount ?? 0),
|
|
42
42
|
icon: /* @__PURE__ */ h(p, { className: "h-5 w-5" }),
|
|
43
|
-
iconBgColor: "bg-
|
|
43
|
+
iconBgColor: "bg-status-warning-bg text-status-warning-text"
|
|
44
44
|
}),
|
|
45
|
-
/* @__PURE__ */ h(
|
|
45
|
+
/* @__PURE__ */ h(a, {
|
|
46
46
|
title: "Approved This Week",
|
|
47
47
|
value: String(k?.approvedCount ?? 0),
|
|
48
48
|
icon: /* @__PURE__ */ h(f, { className: "h-5 w-5" }),
|
|
49
|
-
iconBgColor: "bg-
|
|
49
|
+
iconBgColor: "bg-status-success-bg text-status-success-text"
|
|
50
50
|
}),
|
|
51
|
-
/* @__PURE__ */ h(
|
|
51
|
+
/* @__PURE__ */ h(a, {
|
|
52
52
|
title: "Rejected",
|
|
53
53
|
value: String(k?.rejectedCount ?? 0),
|
|
54
54
|
icon: /* @__PURE__ */ h(m, { className: "h-5 w-5" }),
|
|
55
|
-
iconBgColor: "bg-
|
|
55
|
+
iconBgColor: "bg-status-error-bg text-status-error-text"
|
|
56
56
|
}),
|
|
57
|
-
/* @__PURE__ */ h(
|
|
57
|
+
/* @__PURE__ */ h(a, {
|
|
58
58
|
title: "Avg. Review Time",
|
|
59
59
|
value: k?.averageReviewTime ? `${Math.round(k.averageReviewTime)}h` : "N/A",
|
|
60
60
|
description: "Target: 24h"
|
|
61
61
|
})
|
|
62
62
|
]
|
|
63
63
|
}), /* @__PURE__ */ h("div", {
|
|
64
|
-
className: "rounded-xl border border-border-
|
|
64
|
+
className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop",
|
|
65
65
|
children: /* @__PURE__ */ g("div", {
|
|
66
66
|
className: "divide-y divide-border",
|
|
67
67
|
children: [O.length === 0 && !y && /* @__PURE__ */ h("div", {
|
|
@@ -108,12 +108,12 @@ function _() {
|
|
|
108
108
|
children: [/* @__PURE__ */ h("button", {
|
|
109
109
|
onClick: () => j(e.id),
|
|
110
110
|
disabled: A,
|
|
111
|
-
className: "rounded-md p-1.5 text-
|
|
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
112
|
children: /* @__PURE__ */ h(f, { className: "h-4 w-4" })
|
|
113
113
|
}), /* @__PURE__ */ h("button", {
|
|
114
114
|
onClick: () => M(e.id),
|
|
115
115
|
disabled: A,
|
|
116
|
-
className: "rounded-md p-1.5 text-
|
|
116
|
+
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
117
|
children: /* @__PURE__ */ h(m, { className: "h-4 w-4" })
|
|
118
118
|
})]
|
|
119
119
|
})
|
|
@@ -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-
|
|
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} · 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":";;;;;;;;;AAmBA,SAAgB,IAAmB;CACjC,IAAM,EAAE,iBAAc,OAAO,GAAe,EAEtC,EACJ,MAAM,GACN,SAAS,GACT,OAAO,MACL,GAA8B,EAC5B,EACJ,MAAM,GACN,SAAS,GACT,OAAO,MACL,GAA0B,EACxB,CAAC,GAAgB,EAAE,SAAS,OAAe,GAA2B,EACtE,CAAC,GAAe,EAAE,SAAS,OAAe,GAA0B,EAEpE,IAAW,GAAc,kBAAkB,SAAS,EAAE,EACtD,IAAQ,GAAW,eACnB,IAAW,KAAa,GAExB,IAAgB,OAAO,MAAe;AAC1C,QAAM,EAAe,EAAE,WAAW,EAAE,OAAI,EAAE,CAAC;IAGvC,IAAe,OAAO,MAAe;AACzC,QAAM,EAAc,EAAE,WAAW;GAAE;GAAI,QAAQ;GAAwB,EAAE,CAAC;;AA2B5E,QAxBI,KAAmB,IAEnB,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA;EACjC,CAAA,GAIb,KAAiB,IAEjB,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,OAAD;GAAK,WAAU;aAAoG;GAE7G,CAAA;EACK,CAAA,GAKf,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAFd,CAIE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,OAAO,GAAO,gBAAgB,EAAE;KACvC,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;KACnC,aAAY;KACZ,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,OAAO,GAAO,iBAAiB,EAAE;KACxC,MAAM,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;KACzC,aAAY;KACZ,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,OAAO,GAAO,iBAAiB,EAAE;KACxC,MAAM,kBAAC,GAAD,EAAS,WAAU,WAAY,CAAA;KACrC,aAAY;KACZ,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OACE,GAAO,oBACH,GAAG,KAAK,MAAM,EAAM,kBAAkB,CAAC,KACvC;KAEN,aAAY;KACZ,CAAA;IACE;MAEN,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,EAAS,WAAW,KAAK,CAAC,KACzB,kBAAC,OAAD;KAAK,WAAU;eAA8C;KAEvD,CAAA,EAEP,EAAS,KAAK,MACb,kBAAC,OAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAY,EAAQ,OAAO;OACxB,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAQ;OACP,CAAA,EACJ,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAQ;QAAO;QAAoB;QACnC,EAAW,EAAQ,YAAY;QAC9B;SACA,EAAA,CAAA,CACF;SACN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAY,EAAQ,WAAW;SAC9B,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBAAb,CACG,EAAY,EAAQ,cAAc,EAAC,YAClC;WACA;;OACN,kBAAC,QAAD;QACE,WAAW,4DAA4D,EAAe,EAAQ,OAAO;kBAEpG,EAAQ;QACJ,CAAA;OACN,EAAQ,WAAW,aAClB,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,UAAD;SACE,eAAe,EAAc,EAAQ,GAAG;SACxC,UAAU;SACV,WAAU;mBAEV,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;SAC5B,CAAA,EACT,kBAAC,UAAD;SACE,eAAe,EAAa,EAAQ,GAAG;SACvC,UAAU;SACV,WAAU;mBAEV,kBAAC,GAAD,EAAS,WAAU,WAAY,CAAA;SACxB,CAAA,CACL;;OAEJ;QACF;OAlDC,EAAQ,GAkDT,CACN,CACE;;GACF,CAAA,CACK"}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
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
5
|
import { ArrowLeft as i, Download as a, LayoutGrid as o, Star as s } from "lucide-react";
|
|
6
6
|
import { useParams as c } from "react-router-dom";
|
|
7
7
|
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
8
8
|
//#region src/timecampus/pages/widgets/WidgetDetailPage.tsx
|
|
9
9
|
function d() {
|
|
10
|
-
let { id: d } = c(), { workspaceId: f = "" } = e(), { data: p, loading: m, error: h } =
|
|
10
|
+
let { id: d } = c(), { workspaceId: f = "" } = e(), { data: p, loading: m, error: h } = n({
|
|
11
11
|
variables: { id: d ?? "" },
|
|
12
12
|
skip: !d
|
|
13
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(
|
|
14
|
+
return m ? /* @__PURE__ */ l(r, {
|
|
15
15
|
title: "Widget Details",
|
|
16
16
|
description: "Loading...",
|
|
17
|
-
children: /* @__PURE__ */ l(
|
|
18
|
-
}) : h || !g ? /* @__PURE__ */ l(
|
|
17
|
+
children: /* @__PURE__ */ l(t, { variant: "detail" })
|
|
18
|
+
}) : h || !g ? /* @__PURE__ */ l(r, {
|
|
19
19
|
title: "Widget Details",
|
|
20
20
|
description: `Widget ID: ${d}`,
|
|
21
21
|
actions: /* @__PURE__ */ u("button", {
|
|
@@ -23,10 +23,10 @@ function d() {
|
|
|
23
23
|
children: [/* @__PURE__ */ l(i, { className: "h-4 w-4" }), "Back to Widgets"]
|
|
24
24
|
}),
|
|
25
25
|
children: /* @__PURE__ */ l("div", {
|
|
26
|
-
className: "rounded-lg border border-
|
|
26
|
+
className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
|
|
27
27
|
children: h ? "Failed to load widget details. Please try again later." : "Widget configuration not found."
|
|
28
28
|
})
|
|
29
|
-
}) : /* @__PURE__ */ l(
|
|
29
|
+
}) : /* @__PURE__ */ l(r, {
|
|
30
30
|
title: "Widget Details",
|
|
31
31
|
description: `Widget ID: ${d}`,
|
|
32
32
|
actions: /* @__PURE__ */ u("button", {
|
|
@@ -36,7 +36,7 @@ function d() {
|
|
|
36
36
|
children: /* @__PURE__ */ l("div", {
|
|
37
37
|
className: "mx-auto max-w-2xl",
|
|
38
38
|
children: /* @__PURE__ */ u("div", {
|
|
39
|
-
className: "rounded-xl border border-border-
|
|
39
|
+
className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6",
|
|
40
40
|
children: [
|
|
41
41
|
/* @__PURE__ */ u("div", {
|
|
42
42
|
className: "flex items-start gap-4",
|
|
@@ -59,7 +59,7 @@ function d() {
|
|
|
59
59
|
children: [
|
|
60
60
|
/* @__PURE__ */ u("div", {
|
|
61
61
|
className: "flex items-center gap-1",
|
|
62
|
-
children: [/* @__PURE__ */ l(s, { className: "h-4 w-4 fill-amber-400 text-
|
|
62
|
+
children: [/* @__PURE__ */ l(s, { className: "h-4 w-4 fill-amber-400 text-status-warning-text" }), /* @__PURE__ */ l("span", { children: b })]
|
|
63
63
|
}),
|
|
64
64
|
/* @__PURE__ */ l("span", { children: "·" }),
|
|
65
65
|
/* @__PURE__ */ u("div", {
|