@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.
- package/dist/timecampus/components/EmptyState.js +14 -0
- package/dist/timecampus/components/EmptyState.js.map +1 -0
- package/dist/timecampus/components/PageLayout.js +23 -15
- package/dist/timecampus/components/PageLayout.js.map +1 -1
- package/dist/timecampus/pages/applets/AppletStorePage.js +33 -30
- package/dist/timecampus/pages/applets/AppletStorePage.js.map +1 -1
- package/dist/timecampus/pages/applets/InstalledAppletsPage.js +37 -31
- package/dist/timecampus/pages/applets/InstalledAppletsPage.js.map +1 -1
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +48 -44
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
- package/dist/timecampus/pages/application-usage/ApplicationUsageSettingsPage.js +35 -31
- package/dist/timecampus/pages/application-usage/ApplicationUsageSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/channels/ChannelsPage.js +27 -24
- package/dist/timecampus/pages/channels/ChannelsPage.js.map +1 -1
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js +46 -38
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
- package/dist/timecampus/pages/dashboard/AnalyticsPage.js +66 -39
- package/dist/timecampus/pages/dashboard/AnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/dashboard/ReportsPage.js +42 -36
- package/dist/timecampus/pages/dashboard/ReportsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailAnalyticsPage.js +2 -0
- package/dist/timecampus/pages/email-tracking/EmailAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailSettingsPage.js +35 -31
- package/dist/timecampus/pages/email-tracking/EmailSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +31 -25
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +43 -35
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/focus-score/FocusScorePage.js +55 -51
- package/dist/timecampus/pages/focus-score/FocusScorePage.js.map +1 -1
- package/dist/timecampus/pages/focus-score/FocusScoreSettingsPage.js +32 -28
- package/dist/timecampus/pages/focus-score/FocusScoreSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/geofencing/GeofencingPage.js +32 -25
- package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
- package/dist/timecampus/pages/geofencing/GeofencingSettingsPage.js +29 -25
- package/dist/timecampus/pages/geofencing/GeofencingSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js +32 -29
- package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js.map +1 -1
- package/dist/timecampus/pages/location-tracking/LocationPrivacySettingsPage.js +29 -25
- package/dist/timecampus/pages/location-tracking/LocationPrivacySettingsPage.js.map +1 -1
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +39 -35
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingAnalyticsPage.js +2 -0
- package/dist/timecampus/pages/meetings/MeetingAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingCreatePage.js +33 -29
- package/dist/timecampus/pages/meetings/MeetingCreatePage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js +122 -104
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingsPage.js +42 -32
- package/dist/timecampus/pages/meetings/MeetingsPage.js.map +1 -1
- package/dist/timecampus/pages/pomodoro/PomodoroAnalyticsPage.js +2 -0
- package/dist/timecampus/pages/pomodoro/PomodoroAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/pomodoro/PomodoroPage.js +145 -114
- package/dist/timecampus/pages/pomodoro/PomodoroPage.js.map +1 -1
- package/dist/timecampus/pages/pomodoro/PomodoroSettingsPage.js +49 -45
- package/dist/timecampus/pages/pomodoro/PomodoroSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectCreatePage.js +28 -24
- package/dist/timecampus/pages/projects/ProjectCreatePage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectDetailPage.js +109 -78
- package/dist/timecampus/pages/projects/ProjectDetailPage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectsPage.js +11 -7
- package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
- package/dist/timecampus/pages/projects/TasksPage.js +40 -30
- package/dist/timecampus/pages/projects/TasksPage.js.map +1 -1
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +50 -46
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamAnalyticsPage.js +18 -14
- package/dist/timecampus/pages/team/TeamAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamMembersPage.js +41 -35
- package/dist/timecampus/pages/team/TeamMembersPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamPage.js +91 -61
- package/dist/timecampus/pages/team/TeamPage.js.map +1 -1
- package/dist/timecampus/pages/team-activity/TeamActivityPage.js +40 -36
- package/dist/timecampus/pages/team-activity/TeamActivityPage.js.map +1 -1
- package/dist/timecampus/pages/team-activity/TeamActivitySettingsPage.js +33 -29
- package/dist/timecampus/pages/team-activity/TeamActivitySettingsPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplateApplicationPage.js +17 -13
- package/dist/timecampus/pages/templates/TemplateApplicationPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplateDetailPage.js +67 -49
- package/dist/timecampus/pages/templates/TemplateDetailPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplatesPage.js +34 -31
- package/dist/timecampus/pages/templates/TemplatesPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeEntriesPage.js +48 -39
- package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeTrackingPage.js +65 -64
- package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimerPage.js +103 -69
- package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +53 -49
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
- package/dist/timecampus/pages/widgets/WidgetDetailPage.js +65 -47
- package/dist/timecampus/pages/widgets/WidgetDetailPage.js.map +1 -1
- package/dist/timecampus/pages/widgets/WidgetsPage.js +38 -31
- package/dist/timecampus/pages/widgets/WidgetsPage.js.map +1 -1
- package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js +2 -0
- package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js.map +1 -1
- package/dist/timecampus/pages/work-patterns/WorkPatternsSettingsPage.js +35 -31
- package/dist/timecampus/pages/work-patterns/WorkPatternsSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/workflows/WorkflowsPage.js +43 -33
- package/dist/timecampus/pages/workflows/WorkflowsPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -5,46 +5,47 @@ import { PageLayout as i } from "../../components/PageLayout.js";
|
|
|
5
5
|
import { useCallback as a, useEffect as o, useState as s } from "react";
|
|
6
6
|
import { Settings as c } from "lucide-react";
|
|
7
7
|
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
8
|
+
import { GlassCard as d } from "@burdenoff/fe-libs/ui";
|
|
8
9
|
//#region src/timecampus/pages/application-usage/ApplicationUsageSettingsPage.tsx
|
|
9
|
-
var
|
|
10
|
+
var f = "timecampus.appusage.settings", p = {
|
|
10
11
|
trackActiveApplications: !0,
|
|
11
12
|
trackBrowserTabs: !0,
|
|
12
13
|
distractionAlerts: !1
|
|
13
14
|
};
|
|
14
|
-
function
|
|
15
|
-
let { workspaceId:
|
|
15
|
+
function m() {
|
|
16
|
+
let { workspaceId: m = "" } = e(), { data: h, loading: g, error: _ } = n({
|
|
16
17
|
variables: {
|
|
17
18
|
scope: "WORKSPACE",
|
|
18
19
|
limit: 100
|
|
19
20
|
},
|
|
20
|
-
skip: !
|
|
21
|
-
}), [
|
|
21
|
+
skip: !m
|
|
22
|
+
}), [v, { loading: y }] = r(), [b, x] = s(p);
|
|
22
23
|
o(() => {
|
|
23
|
-
if (
|
|
24
|
-
let e =
|
|
24
|
+
if (h?.configurations) {
|
|
25
|
+
let e = h.configurations.find((e) => e.key === f);
|
|
25
26
|
if (e?.value) {
|
|
26
27
|
let t = e.value;
|
|
27
|
-
|
|
28
|
-
trackActiveApplications: t.trackActiveApplications ??
|
|
29
|
-
trackBrowserTabs: t.trackBrowserTabs ??
|
|
30
|
-
distractionAlerts: t.distractionAlerts ??
|
|
28
|
+
x({
|
|
29
|
+
trackActiveApplications: t.trackActiveApplications ?? p.trackActiveApplications,
|
|
30
|
+
trackBrowserTabs: t.trackBrowserTabs ?? p.trackBrowserTabs,
|
|
31
|
+
distractionAlerts: t.distractionAlerts ?? p.distractionAlerts
|
|
31
32
|
});
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
|
-
}, [
|
|
35
|
-
let
|
|
36
|
-
await
|
|
37
|
-
key:
|
|
38
|
-
value:
|
|
35
|
+
}, [h]);
|
|
36
|
+
let S = a(async () => {
|
|
37
|
+
await v({ variables: { input: {
|
|
38
|
+
key: f,
|
|
39
|
+
value: b,
|
|
39
40
|
description: "Application usage tracking settings",
|
|
40
41
|
scope: "WORKSPACE"
|
|
41
42
|
} } });
|
|
42
|
-
}, [
|
|
43
|
-
return
|
|
43
|
+
}, [v, b]);
|
|
44
|
+
return g ? /* @__PURE__ */ l(i, {
|
|
44
45
|
title: "App Usage Settings",
|
|
45
46
|
description: "Configure application tracking",
|
|
46
47
|
children: /* @__PURE__ */ l(t, { variant: "detail" })
|
|
47
|
-
}) :
|
|
48
|
+
}) : _ ? /* @__PURE__ */ l(i, {
|
|
48
49
|
title: "App Usage Settings",
|
|
49
50
|
description: "Configure application tracking",
|
|
50
51
|
children: /* @__PURE__ */ l("div", {
|
|
@@ -54,10 +55,13 @@ function p() {
|
|
|
54
55
|
}) : /* @__PURE__ */ l(i, {
|
|
55
56
|
title: "App Usage Settings",
|
|
56
57
|
description: "Configure application tracking",
|
|
58
|
+
purpose: "Decide which apps and browser activity TimeCampus watches and when it warns you about distractions — control the trade-off between richer focus insights and your privacy.",
|
|
57
59
|
children: /* @__PURE__ */ u("div", {
|
|
58
60
|
className: "mx-auto max-w-2xl space-y-6",
|
|
59
|
-
children: [/* @__PURE__ */ u(
|
|
60
|
-
|
|
61
|
+
children: [/* @__PURE__ */ u(d, {
|
|
62
|
+
treatment: "glass",
|
|
63
|
+
glow: !0,
|
|
64
|
+
className: "p-6",
|
|
61
65
|
children: [/* @__PURE__ */ u("h3", {
|
|
62
66
|
className: "mb-4 flex items-center gap-2 text-sm font-semibold text-text-primary",
|
|
63
67
|
children: [/* @__PURE__ */ l(c, { className: "h-4 w-4" }), "Tracking Configuration"]
|
|
@@ -71,8 +75,8 @@ function p() {
|
|
|
71
75
|
children: "Track active applications"
|
|
72
76
|
}), /* @__PURE__ */ l("input", {
|
|
73
77
|
type: "checkbox",
|
|
74
|
-
checked:
|
|
75
|
-
onChange: (e) =>
|
|
78
|
+
checked: b.trackActiveApplications,
|
|
79
|
+
onChange: (e) => x((t) => ({
|
|
76
80
|
...t,
|
|
77
81
|
trackActiveApplications: e.target.checked
|
|
78
82
|
})),
|
|
@@ -86,8 +90,8 @@ function p() {
|
|
|
86
90
|
children: "Track browser tabs"
|
|
87
91
|
}), /* @__PURE__ */ l("input", {
|
|
88
92
|
type: "checkbox",
|
|
89
|
-
checked:
|
|
90
|
-
onChange: (e) =>
|
|
93
|
+
checked: b.trackBrowserTabs,
|
|
94
|
+
onChange: (e) => x((t) => ({
|
|
91
95
|
...t,
|
|
92
96
|
trackBrowserTabs: e.target.checked
|
|
93
97
|
})),
|
|
@@ -101,8 +105,8 @@ function p() {
|
|
|
101
105
|
children: "Distraction alerts"
|
|
102
106
|
}), /* @__PURE__ */ l("input", {
|
|
103
107
|
type: "checkbox",
|
|
104
|
-
checked:
|
|
105
|
-
onChange: (e) =>
|
|
108
|
+
checked: b.distractionAlerts,
|
|
109
|
+
onChange: (e) => x((t) => ({
|
|
106
110
|
...t,
|
|
107
111
|
distractionAlerts: e.target.checked
|
|
108
112
|
})),
|
|
@@ -114,16 +118,16 @@ function p() {
|
|
|
114
118
|
}), /* @__PURE__ */ l("div", {
|
|
115
119
|
className: "flex justify-end",
|
|
116
120
|
children: /* @__PURE__ */ l("button", {
|
|
117
|
-
onClick:
|
|
118
|
-
disabled:
|
|
121
|
+
onClick: S,
|
|
122
|
+
disabled: y,
|
|
119
123
|
className: "rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50",
|
|
120
|
-
children:
|
|
124
|
+
children: y ? "Saving..." : "Save Settings"
|
|
121
125
|
})
|
|
122
126
|
})]
|
|
123
127
|
})
|
|
124
128
|
});
|
|
125
129
|
}
|
|
126
130
|
//#endregion
|
|
127
|
-
export {
|
|
131
|
+
export { m as ApplicationUsageSettingsPage };
|
|
128
132
|
|
|
129
133
|
//# sourceMappingURL=ApplicationUsageSettingsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApplicationUsageSettingsPage.js","names":[],"sources":["../../../../src/timecampus/pages/application-usage/ApplicationUsageSettingsPage.tsx"],"sourcesContent":["import { useState, useEffect, useCallback } from \"react\";\nimport { Settings } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListConfigurationsQuery,\n useSetConfigurationMutation,\n type ConfigurationFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nconst APP_USAGE_SETTINGS_KEY = \"timecampus.appusage.settings\";\n\ninterface AppUsageSettings {\n trackActiveApplications: boolean;\n trackBrowserTabs: boolean;\n distractionAlerts: boolean;\n}\n\nconst DEFAULT_SETTINGS: AppUsageSettings = {\n trackActiveApplications: true,\n trackBrowserTabs: true,\n distractionAlerts: false,\n};\n\nexport function ApplicationUsageSettingsPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n const { data, loading, error } = useListConfigurationsQuery({\n variables: { scope: \"WORKSPACE\" as never, limit: 100 },\n skip: !workspaceId,\n });\n\n const [setConfiguration, { loading: saving }] = useSetConfigurationMutation();\n const [settings, setSettings] = useState<AppUsageSettings>(DEFAULT_SETTINGS);\n\n useEffect(() => {\n if (data?.configurations) {\n const config = data.configurations.find(\n (c: ConfigurationFieldsFragment) => c.key === APP_USAGE_SETTINGS_KEY,\n );\n if (config?.value) {\n const val = config.value as Partial<AppUsageSettings>;\n setSettings({\n trackActiveApplications:\n val.trackActiveApplications ??\n DEFAULT_SETTINGS.trackActiveApplications,\n trackBrowserTabs:\n val.trackBrowserTabs ?? DEFAULT_SETTINGS.trackBrowserTabs,\n distractionAlerts:\n val.distractionAlerts ?? DEFAULT_SETTINGS.distractionAlerts,\n });\n }\n }\n }, [data]);\n\n const handleSave = useCallback(async () => {\n await setConfiguration({\n variables: {\n input: {\n key: APP_USAGE_SETTINGS_KEY,\n value: settings as unknown as Record<string, unknown>,\n description: \"Application usage tracking settings\",\n scope: \"WORKSPACE\" as never,\n },\n },\n });\n }, [setConfiguration, settings]);\n\n if (loading) {\n return (\n <PageLayout\n title=\"App Usage Settings\"\n description=\"Configure application tracking\"\n >\n <LoadingSkeleton variant=\"detail\" />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"App Usage Settings\"\n description=\"Configure application tracking\"\n >\n <div className=\"rounded-xl border border-status-error-border/50 bg-status-error-bg/10 p-4 text-sm text-status-error-text\">\n Failed to load settings. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"App Usage Settings\"\n description=\"Configure application tracking\"\n >\n <div className=\"mx-auto max-w-2xl space-y-6\">\n <
|
|
1
|
+
{"version":3,"file":"ApplicationUsageSettingsPage.js","names":[],"sources":["../../../../src/timecampus/pages/application-usage/ApplicationUsageSettingsPage.tsx"],"sourcesContent":["import { useState, useEffect, useCallback } from \"react\";\nimport { Settings } from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListConfigurationsQuery,\n useSetConfigurationMutation,\n type ConfigurationFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nconst APP_USAGE_SETTINGS_KEY = \"timecampus.appusage.settings\";\n\ninterface AppUsageSettings {\n trackActiveApplications: boolean;\n trackBrowserTabs: boolean;\n distractionAlerts: boolean;\n}\n\nconst DEFAULT_SETTINGS: AppUsageSettings = {\n trackActiveApplications: true,\n trackBrowserTabs: true,\n distractionAlerts: false,\n};\n\nexport function ApplicationUsageSettingsPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n const { data, loading, error } = useListConfigurationsQuery({\n variables: { scope: \"WORKSPACE\" as never, limit: 100 },\n skip: !workspaceId,\n });\n\n const [setConfiguration, { loading: saving }] = useSetConfigurationMutation();\n const [settings, setSettings] = useState<AppUsageSettings>(DEFAULT_SETTINGS);\n\n useEffect(() => {\n if (data?.configurations) {\n const config = data.configurations.find(\n (c: ConfigurationFieldsFragment) => c.key === APP_USAGE_SETTINGS_KEY,\n );\n if (config?.value) {\n const val = config.value as Partial<AppUsageSettings>;\n setSettings({\n trackActiveApplications:\n val.trackActiveApplications ??\n DEFAULT_SETTINGS.trackActiveApplications,\n trackBrowserTabs:\n val.trackBrowserTabs ?? DEFAULT_SETTINGS.trackBrowserTabs,\n distractionAlerts:\n val.distractionAlerts ?? DEFAULT_SETTINGS.distractionAlerts,\n });\n }\n }\n }, [data]);\n\n const handleSave = useCallback(async () => {\n await setConfiguration({\n variables: {\n input: {\n key: APP_USAGE_SETTINGS_KEY,\n value: settings as unknown as Record<string, unknown>,\n description: \"Application usage tracking settings\",\n scope: \"WORKSPACE\" as never,\n },\n },\n });\n }, [setConfiguration, settings]);\n\n if (loading) {\n return (\n <PageLayout\n title=\"App Usage Settings\"\n description=\"Configure application tracking\"\n >\n <LoadingSkeleton variant=\"detail\" />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"App Usage Settings\"\n description=\"Configure application tracking\"\n >\n <div className=\"rounded-xl border border-status-error-border/50 bg-status-error-bg/10 p-4 text-sm text-status-error-text\">\n Failed to load settings. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"App Usage Settings\"\n description=\"Configure application tracking\"\n purpose=\"Decide which apps and browser activity TimeCampus watches and when it warns you about distractions — control the trade-off between richer focus insights and your privacy.\"\n >\n <div className=\"mx-auto max-w-2xl space-y-6\">\n <GlassCard treatment=\"glass\" glow className=\"p-6\">\n <h3 className=\"mb-4 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Settings className=\"h-4 w-4\" />\n Tracking Configuration\n </h3>\n <div className=\"space-y-3\">\n <label className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-primary\">\n Track active applications\n </span>\n <input\n type=\"checkbox\"\n checked={settings.trackActiveApplications}\n onChange={(e) =>\n setSettings((s) => ({\n ...s,\n trackActiveApplications: e.target.checked,\n }))\n }\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n </label>\n <label className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-primary\">\n Track browser tabs\n </span>\n <input\n type=\"checkbox\"\n checked={settings.trackBrowserTabs}\n onChange={(e) =>\n setSettings((s) => ({\n ...s,\n trackBrowserTabs: e.target.checked,\n }))\n }\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n </label>\n <label className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-primary\">\n Distraction alerts\n </span>\n <input\n type=\"checkbox\"\n checked={settings.distractionAlerts}\n onChange={(e) =>\n setSettings((s) => ({\n ...s,\n distractionAlerts: e.target.checked,\n }))\n }\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n </label>\n </div>\n </GlassCard>\n <div className=\"flex justify-end\">\n <button\n onClick={handleSave}\n disabled={saving}\n className=\"rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {saving ? \"Saving...\" : \"Save Settings\"}\n </button>\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;AAYA,IAAM,IAAyB,gCAQzB,IAAqC;CACzC,yBAAyB;CACzB,kBAAkB;CAClB,mBAAmB;CACpB;AAED,SAAgB,IAA+B;CAC7C,IAAM,EAAE,iBAAc,OAAO,GAAe,EACtC,EAAE,SAAM,YAAS,aAAU,EAA2B;EAC1D,WAAW;GAAE,OAAO;GAAsB,OAAO;GAAK;EACtD,MAAM,CAAC;EACR,CAAC,EAEI,CAAC,GAAkB,EAAE,SAAS,OAAY,GAA6B,EACvE,CAAC,GAAU,KAAe,EAA2B,EAAiB;AAE5E,SAAgB;AACd,MAAI,GAAM,gBAAgB;GACxB,IAAM,IAAS,EAAK,eAAe,MAChC,MAAmC,EAAE,QAAQ,EAC/C;AACD,OAAI,GAAQ,OAAO;IACjB,IAAM,IAAM,EAAO;AACnB,MAAY;KACV,yBACE,EAAI,2BACJ,EAAiB;KACnB,kBACE,EAAI,oBAAoB,EAAiB;KAC3C,mBACE,EAAI,qBAAqB,EAAiB;KAC7C,CAAC;;;IAGL,CAAC,EAAK,CAAC;CAEV,IAAM,IAAa,EAAY,YAAY;AACzC,QAAM,EAAiB,EACrB,WAAW,EACT,OAAO;GACL,KAAK;GACL,OAAO;GACP,aAAa;GACb,OAAO;GACR,EACF,EACF,CAAC;IACD,CAAC,GAAkB,EAAS,CAAC;AA0BhC,QAxBI,IAEA,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,GAAD,EAAiB,SAAQ,UAAW,CAAA;EACzB,CAAA,GAIb,IAEA,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,OAAD;GAAK,WAAU;aAA2G;GAEpH,CAAA;EACK,CAAA,GAKf,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SAAQ;YAER,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD;IAAW,WAAU;IAAQ,MAAA;IAAK,WAAU;cAA5C,CACE,kBAAC,MAAD;KAAI,WAAU;eAAd,CACE,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA,EAAA,yBAE7B;QACL,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,SAAD;OAAO,WAAU;iBAAjB,CACE,kBAAC,QAAD;QAAM,WAAU;kBAA4B;QAErC,CAAA,EACP,kBAAC,SAAD;QACE,MAAK;QACL,SAAS,EAAS;QAClB,WAAW,MACT,GAAa,OAAO;SAClB,GAAG;SACH,yBAAyB,EAAE,OAAO;SACnC,EAAE;QAEL,WAAU;QACV,CAAA,CACI;;MACR,kBAAC,SAAD;OAAO,WAAU;iBAAjB,CACE,kBAAC,QAAD;QAAM,WAAU;kBAA4B;QAErC,CAAA,EACP,kBAAC,SAAD;QACE,MAAK;QACL,SAAS,EAAS;QAClB,WAAW,MACT,GAAa,OAAO;SAClB,GAAG;SACH,kBAAkB,EAAE,OAAO;SAC5B,EAAE;QAEL,WAAU;QACV,CAAA,CACI;;MACR,kBAAC,SAAD;OAAO,WAAU;iBAAjB,CACE,kBAAC,QAAD;QAAM,WAAU;kBAA4B;QAErC,CAAA,EACP,kBAAC,SAAD;QACE,MAAK;QACL,SAAS,EAAS;QAClB,WAAW,MACT,GAAa,OAAO;SAClB,GAAG;SACH,mBAAmB,EAAE,OAAO;SAC7B,EAAE;QAEL,WAAU;QACV,CAAA,CACI;;MACJ;OACI;OACZ,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,UAAD;KACE,SAAS;KACT,UAAU;KACV,WAAU;eAET,IAAS,cAAc;KACjB,CAAA;IACL,CAAA,CACF;;EACK,CAAA"}
|
|
@@ -3,55 +3,58 @@ import { getStatusColor as t } from "../../utils/helpers.js";
|
|
|
3
3
|
import { LoadingSkeleton as n } from "../../components/LoadingSkeleton.js";
|
|
4
4
|
import { useListChannelConfigsQuery as r, useListChannelStatusesQuery as i } from "../../../generated/wspace-operations.js";
|
|
5
5
|
import { PageLayout as a } from "../../components/PageLayout.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { EmptyState as o } from "../../components/EmptyState.js";
|
|
7
|
+
import { Wifi as s, WifiOff as c } from "lucide-react";
|
|
8
|
+
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
8
9
|
//#region src/timecampus/pages/channels/ChannelsPage.tsx
|
|
9
|
-
function
|
|
10
|
-
let { workspaceId:
|
|
11
|
-
return
|
|
10
|
+
function d() {
|
|
11
|
+
let { workspaceId: d = "" } = e(), { data: f, loading: p, error: m } = r(), { data: h } = i(), g = f?.channelConfigs ?? [], _ = h?.channelStatuses ?? [], v = new Map(_.map((e) => [e.channelId, e]));
|
|
12
|
+
return p ? /* @__PURE__ */ l(a, {
|
|
12
13
|
title: "Channels",
|
|
13
14
|
description: "Manage time tracking channels and integrations",
|
|
14
|
-
children: /* @__PURE__ */
|
|
15
|
+
children: /* @__PURE__ */ l(n, {
|
|
15
16
|
variant: "card",
|
|
16
17
|
count: 6
|
|
17
18
|
})
|
|
18
|
-
}) :
|
|
19
|
+
}) : m ? /* @__PURE__ */ l(a, {
|
|
19
20
|
title: "Channels",
|
|
20
21
|
description: "Manage time tracking channels and integrations",
|
|
21
|
-
children: /* @__PURE__ */
|
|
22
|
+
children: /* @__PURE__ */ l("div", {
|
|
22
23
|
className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
|
|
23
24
|
children: "Failed to load channels data. Please try again later."
|
|
24
25
|
})
|
|
25
|
-
}) : /* @__PURE__ */
|
|
26
|
+
}) : /* @__PURE__ */ l(a, {
|
|
26
27
|
title: "Channels",
|
|
27
28
|
description: "Manage time tracking channels and integrations",
|
|
28
|
-
|
|
29
|
+
purpose: "Connect the tools and devices that feed time into TimeCampus — calendars, desktop trackers, integrations — and keep an eye on each connection's health so no work hours slip through the cracks.",
|
|
30
|
+
children: g.length === 0 ? /* @__PURE__ */ l(o, {
|
|
31
|
+
illustration: "empty-generic",
|
|
32
|
+
title: "No channels configured yet",
|
|
33
|
+
description: "Set up your first tracking channel to start pulling time and activity from your tools and devices."
|
|
34
|
+
}) : /* @__PURE__ */ l("div", {
|
|
29
35
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
30
|
-
children:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}) : h.map((e) => {
|
|
34
|
-
let n = _.get(e.id), r = e.status === "active", i = e.lastSync ? new Date(e.lastSync).toLocaleString() : "Never";
|
|
35
|
-
return /* @__PURE__ */ l("div", {
|
|
36
|
+
children: g.map((e) => {
|
|
37
|
+
let n = v.get(e.id), r = e.status === "active", i = e.lastSync ? new Date(e.lastSync).toLocaleString() : "Never";
|
|
38
|
+
return /* @__PURE__ */ u("div", {
|
|
36
39
|
className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop p-5 transition-colors hover:bg-bg-sunken/50 cursor-pointer",
|
|
37
|
-
children: [/* @__PURE__ */
|
|
40
|
+
children: [/* @__PURE__ */ u("div", {
|
|
38
41
|
className: "flex items-start justify-between",
|
|
39
|
-
children: [/* @__PURE__ */
|
|
42
|
+
children: [/* @__PURE__ */ u("div", {
|
|
40
43
|
className: "flex items-center gap-3",
|
|
41
|
-
children: [r ? /* @__PURE__ */
|
|
44
|
+
children: [r ? /* @__PURE__ */ l(s, { className: "h-5 w-5 text-status-success-text" }) : /* @__PURE__ */ l(c, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("p", {
|
|
42
45
|
className: "text-sm font-semibold text-text-primary",
|
|
43
46
|
children: e.name
|
|
44
|
-
}), /* @__PURE__ */
|
|
47
|
+
}), /* @__PURE__ */ l("p", {
|
|
45
48
|
className: "text-xs text-text-secondary",
|
|
46
49
|
children: e.type
|
|
47
50
|
})] })]
|
|
48
|
-
}), /* @__PURE__ */
|
|
51
|
+
}), /* @__PURE__ */ l("span", {
|
|
49
52
|
className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${t(e.status)}`,
|
|
50
53
|
children: e.status
|
|
51
54
|
})]
|
|
52
|
-
}), /* @__PURE__ */
|
|
55
|
+
}), /* @__PURE__ */ u("div", {
|
|
53
56
|
className: "mt-3 flex items-center justify-between text-xs text-text-secondary",
|
|
54
|
-
children: [/* @__PURE__ */
|
|
57
|
+
children: [/* @__PURE__ */ l("span", { children: n?.isHealthy ? "Healthy" : "Unhealthy" }), /* @__PURE__ */ u("span", { children: ["Sync: ", i] })]
|
|
55
58
|
})]
|
|
56
59
|
}, e.id);
|
|
57
60
|
})
|
|
@@ -59,6 +62,6 @@ function u() {
|
|
|
59
62
|
});
|
|
60
63
|
}
|
|
61
64
|
//#endregion
|
|
62
|
-
export {
|
|
65
|
+
export { d as ChannelsPage };
|
|
63
66
|
|
|
64
67
|
//# sourceMappingURL=ChannelsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChannelsPage.js","names":[],"sources":["../../../../src/timecampus/pages/channels/ChannelsPage.tsx"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"ChannelsPage.js","names":[],"sources":["../../../../src/timecampus/pages/channels/ChannelsPage.tsx"],"sourcesContent":["import { Wifi, WifiOff } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { getStatusColor } from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListChannelConfigsQuery,\n useListChannelStatusesQuery,\n type ChannelConfigFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function ChannelsPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const {\n data: configsData,\n loading: configsLoading,\n error: configsError,\n } = useListChannelConfigsQuery();\n const { data: statusesData } = useListChannelStatusesQuery();\n\n const channels = configsData?.channelConfigs ?? [];\n const statuses = statusesData?.channelStatuses ?? [];\n\n type ChannelStatusItem = {\n channelId: string;\n name: string;\n type: string;\n status: string;\n isHealthy: boolean;\n lastSync?: string | null;\n };\n\n // Build a lookup map for status info\n const statusMap = new Map<string, ChannelStatusItem>(\n statuses.map((s: ChannelStatusItem) => [s.channelId, s]),\n );\n\n if (configsLoading) {\n return (\n <PageLayout\n title=\"Channels\"\n description=\"Manage time tracking channels and integrations\"\n >\n <LoadingSkeleton variant=\"card\" count={6} />\n </PageLayout>\n );\n }\n\n if (configsError) {\n return (\n <PageLayout\n title=\"Channels\"\n description=\"Manage time tracking channels and integrations\"\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 channels data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Channels\"\n description=\"Manage time tracking channels and integrations\"\n purpose=\"Connect the tools and devices that feed time into TimeCampus — calendars, desktop trackers, integrations — and keep an eye on each connection's health so no work hours slip through the cracks.\"\n >\n {channels.length === 0 ? (\n <EmptyState\n illustration=\"empty-generic\"\n title=\"No channels configured yet\"\n description=\"Set up your first tracking channel to start pulling time and activity from your tools and devices.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {channels.map((channel: ChannelConfigFieldsFragment) => {\n const statusInfo = statusMap.get(channel.id);\n const isActive = channel.status === \"active\";\n const lastSync = channel.lastSync\n ? new Date(channel.lastSync).toLocaleString()\n : \"Never\";\n\n return (\n <div\n key={channel.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 {isActive ? (\n <Wifi className=\"h-5 w-5 text-status-success-text\" />\n ) : (\n <WifiOff className=\"h-5 w-5 text-text-secondary\" />\n )}\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">\n {channel.name}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {channel.type}\n </p>\n </div>\n </div>\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getStatusColor(channel.status)}`}\n >\n {channel.status}\n </span>\n </div>\n <div className=\"mt-3 flex items-center justify-between text-xs text-text-secondary\">\n <span>{statusInfo?.isHealthy ? \"Healthy\" : \"Unhealthy\"}</span>\n <span>Sync: {lastSync}</span>\n </div>\n </div>\n );\n })}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;AAYA,SAAgB,IAAe;CAC7B,IAAM,EAAE,iBAAc,OAAO,GAAe,EAEtC,EACJ,MAAM,GACN,SAAS,GACT,OAAO,MACL,GAA4B,EAC1B,EAAE,MAAM,MAAiB,GAA6B,EAEtD,IAAW,GAAa,kBAAkB,EAAE,EAC5C,IAAW,GAAc,mBAAmB,EAAE,EAY9C,IAAY,IAAI,IACpB,EAAS,KAAK,MAAyB,CAAC,EAAE,WAAW,EAAE,CAAC,CACzD;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;YAEP,EAAS,WAAW,IACnB,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,aAAY;GACZ,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAS,KAAK,MAAyC;IACtD,IAAM,IAAa,EAAU,IAAI,EAAQ,GAAG,EACtC,IAAW,EAAQ,WAAW,UAC9B,IAAW,EAAQ,WACrB,IAAI,KAAK,EAAQ,SAAS,CAAC,gBAAgB,GAC3C;AAEJ,WACE,kBAAC,OAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACG,IACC,kBAAC,GAAD,EAAM,WAAU,oCAAqC,CAAA,GAErD,kBAAC,GAAD,EAAS,WAAU,+BAAgC,CAAA,EAErD,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAQ;QACP,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAQ;QACP,CAAA,CACA,EAAA,CAAA,CACF;UACN,kBAAC,QAAD;OACE,WAAW,4DAA4D,EAAe,EAAQ,OAAO;iBAEpG,EAAQ;OACJ,CAAA,CACH;SACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,QAAD,EAAA,UAAO,GAAY,YAAY,YAAY,aAAmB,CAAA,EAC9D,kBAAC,QAAD,EAAA,UAAA,CAAM,UAAO,EAAgB,EAAA,CAAA,CACzB;QACF;OA7BC,EAAQ,GA6BT;KAER;GACE,CAAA;EAEG,CAAA"}
|
|
@@ -4,79 +4,87 @@ import { StatCard as r } from "../../components/StatCard.js";
|
|
|
4
4
|
import { LoadingSkeleton as i } from "../../components/LoadingSkeleton.js";
|
|
5
5
|
import { useGetClientPortalStatsQuery as a, useListClientPortalConfigsQuery as o } from "../../../generated/wspace-operations.js";
|
|
6
6
|
import { PageLayout as s } from "../../components/PageLayout.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { EmptyState as c } from "../../components/EmptyState.js";
|
|
8
|
+
import { Building2 as l, DollarSign as u, FolderOpen as d, Plus as f } from "lucide-react";
|
|
9
|
+
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
9
10
|
//#region src/timecampus/pages/client-portal/ClientPortalPage.tsx
|
|
10
|
-
function
|
|
11
|
-
let { workspaceId:
|
|
12
|
-
return
|
|
11
|
+
function h() {
|
|
12
|
+
let { workspaceId: h = "" } = e(), { data: g, loading: _, error: v } = o(), { data: y, loading: b } = a(), x = g?.clientPortalConfigs?.items ?? [], S = y?.clientPortalStats;
|
|
13
|
+
return _ ? /* @__PURE__ */ p(s, {
|
|
13
14
|
title: "Client Portal",
|
|
14
15
|
description: "Manage clients and their projects",
|
|
15
|
-
children: /* @__PURE__ */
|
|
16
|
+
children: /* @__PURE__ */ p(i, {
|
|
16
17
|
variant: "list",
|
|
17
18
|
count: 4
|
|
18
19
|
})
|
|
19
|
-
}) :
|
|
20
|
+
}) : v ? /* @__PURE__ */ p(s, {
|
|
20
21
|
title: "Client Portal",
|
|
21
22
|
description: "Manage clients and their projects",
|
|
22
|
-
children: /* @__PURE__ */
|
|
23
|
+
children: /* @__PURE__ */ p("div", {
|
|
23
24
|
className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
|
|
24
25
|
children: "Failed to load client portal data. Please try again later."
|
|
25
26
|
})
|
|
26
|
-
}) : /* @__PURE__ */
|
|
27
|
+
}) : /* @__PURE__ */ m(s, {
|
|
27
28
|
title: "Client Portal",
|
|
28
29
|
description: "Manage clients and their projects",
|
|
29
|
-
|
|
30
|
+
purpose: "Give clients a transparent window into the work you do for them — the projects, hours and progress tied to each one — so billing conversations are backed by data and trust, not spreadsheets.",
|
|
31
|
+
actions: /* @__PURE__ */ m("button", {
|
|
30
32
|
className: "inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
|
|
31
|
-
children: [/* @__PURE__ */ f
|
|
33
|
+
children: [/* @__PURE__ */ p(f, { className: "h-4 w-4" }), "Add Client"]
|
|
32
34
|
}),
|
|
33
|
-
children: [/* @__PURE__ */
|
|
35
|
+
children: [/* @__PURE__ */ m("div", {
|
|
34
36
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
|
|
35
37
|
children: [
|
|
36
|
-
/* @__PURE__ */
|
|
38
|
+
/* @__PURE__ */ p(r, {
|
|
39
|
+
featured: !0,
|
|
37
40
|
title: "Active Clients",
|
|
38
|
-
value: String(
|
|
39
|
-
icon: /* @__PURE__ */
|
|
41
|
+
value: String(S?.activeClients ?? 0),
|
|
42
|
+
icon: /* @__PURE__ */ p(l, { className: "h-5 w-5" })
|
|
40
43
|
}),
|
|
41
|
-
/* @__PURE__ */
|
|
44
|
+
/* @__PURE__ */ p(r, {
|
|
42
45
|
title: "Total Projects",
|
|
43
|
-
value: String(
|
|
44
|
-
icon: /* @__PURE__ */
|
|
46
|
+
value: String(S?.totalProjectsShared ?? 0),
|
|
47
|
+
icon: /* @__PURE__ */ p(d, { className: "h-5 w-5" })
|
|
45
48
|
}),
|
|
46
|
-
/* @__PURE__ */
|
|
49
|
+
/* @__PURE__ */ p(r, {
|
|
47
50
|
title: "Total Clients",
|
|
48
|
-
value: String(
|
|
49
|
-
icon: /* @__PURE__ */
|
|
51
|
+
value: String(S?.totalClients ?? 0),
|
|
52
|
+
icon: /* @__PURE__ */ p(u, { className: "h-5 w-5" })
|
|
50
53
|
}),
|
|
51
|
-
/* @__PURE__ */
|
|
54
|
+
/* @__PURE__ */ p(r, {
|
|
52
55
|
title: "Active",
|
|
53
|
-
value: String(
|
|
56
|
+
value: String(S?.activeClients ?? 0),
|
|
54
57
|
trend: {
|
|
55
58
|
value: 0,
|
|
56
59
|
label: "clients"
|
|
57
60
|
}
|
|
58
61
|
})
|
|
59
62
|
]
|
|
60
|
-
}), /* @__PURE__ */
|
|
63
|
+
}), x.length === 0 ? /* @__PURE__ */ p(c, {
|
|
64
|
+
illustration: "empty-people",
|
|
65
|
+
title: "No clients configured yet",
|
|
66
|
+
description: "Add your first client to share projects and tracked hours with them.",
|
|
67
|
+
action: /* @__PURE__ */ m("button", {
|
|
68
|
+
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",
|
|
69
|
+
children: [/* @__PURE__ */ p(f, { className: "h-4 w-4" }), "Add Client"]
|
|
70
|
+
})
|
|
71
|
+
}) : /* @__PURE__ */ p("div", {
|
|
61
72
|
className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop",
|
|
62
|
-
children: /* @__PURE__ */
|
|
73
|
+
children: /* @__PURE__ */ p("div", {
|
|
63
74
|
className: "divide-y divide-border",
|
|
64
|
-
children:
|
|
65
|
-
className: "p-8 text-center text-sm text-text-secondary",
|
|
66
|
-
children: "No clients configured yet. Add your first client to get started."
|
|
67
|
-
}) : b.map((e) => {
|
|
75
|
+
children: x.map((e) => {
|
|
68
76
|
let r = e.projects.reduce((e, t) => e + t.trackedHours, 0);
|
|
69
|
-
return /* @__PURE__ */
|
|
77
|
+
return /* @__PURE__ */ m("div", {
|
|
70
78
|
className: "flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors cursor-pointer",
|
|
71
|
-
children: [/* @__PURE__ */
|
|
79
|
+
children: [/* @__PURE__ */ m("div", {
|
|
72
80
|
className: "flex items-center gap-3",
|
|
73
|
-
children: [/* @__PURE__ */
|
|
81
|
+
children: [/* @__PURE__ */ p("div", {
|
|
74
82
|
className: "flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-sm font-medium text-primary",
|
|
75
83
|
children: t(e.clientName)
|
|
76
|
-
}), /* @__PURE__ */
|
|
84
|
+
}), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("p", {
|
|
77
85
|
className: "text-sm font-medium text-text-primary",
|
|
78
86
|
children: e.clientName
|
|
79
|
-
}), /* @__PURE__ */
|
|
87
|
+
}), /* @__PURE__ */ m("p", {
|
|
80
88
|
className: "text-xs text-text-secondary",
|
|
81
89
|
children: [
|
|
82
90
|
e.projects.length,
|
|
@@ -86,12 +94,12 @@ function m() {
|
|
|
86
94
|
"h tracked"
|
|
87
95
|
]
|
|
88
96
|
})] })]
|
|
89
|
-
}), /* @__PURE__ */
|
|
97
|
+
}), /* @__PURE__ */ m("div", {
|
|
90
98
|
className: "flex items-center gap-4",
|
|
91
|
-
children: [e.contactEmail && /* @__PURE__ */
|
|
99
|
+
children: [e.contactEmail && /* @__PURE__ */ p("span", {
|
|
92
100
|
className: "text-xs text-text-secondary",
|
|
93
101
|
children: e.contactEmail
|
|
94
|
-
}), /* @__PURE__ */
|
|
102
|
+
}), /* @__PURE__ */ p("span", {
|
|
95
103
|
className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${n(e.isActive ? "active" : "inactive")}`,
|
|
96
104
|
children: e.isActive ? "active" : "inactive"
|
|
97
105
|
})]
|
|
@@ -103,6 +111,6 @@ function m() {
|
|
|
103
111
|
});
|
|
104
112
|
}
|
|
105
113
|
//#endregion
|
|
106
|
-
export {
|
|
114
|
+
export { h as ClientPortalPage };
|
|
107
115
|
|
|
108
116
|
//# sourceMappingURL=ClientPortalPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClientPortalPage.js","names":[],"sources":["../../../../src/timecampus/pages/client-portal/ClientPortalPage.tsx"],"sourcesContent":["import { Building2, Plus, DollarSign, FolderOpen } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport {\n getStatusColor,\n formatCurrency,\n getInitials,\n} from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListClientPortalConfigsQuery,\n useGetClientPortalStatsQuery,\n type ClientPortalFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function ClientPortalPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const {\n data: configsData,\n loading: configsLoading,\n error: configsError,\n } = useListClientPortalConfigsQuery();\n const { data: statsData, loading: statsLoading } =\n useGetClientPortalStatsQuery();\n\n const clients = configsData?.clientPortalConfigs?.items ?? [];\n const stats = statsData?.clientPortalStats;\n\n if (configsLoading) {\n return (\n <PageLayout\n title=\"Client Portal\"\n description=\"Manage clients and their projects\"\n >\n <LoadingSkeleton variant=\"list\" count={4} />\n </PageLayout>\n );\n }\n\n if (configsError) {\n return (\n <PageLayout\n title=\"Client Portal\"\n description=\"Manage clients and their projects\"\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 client portal data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Client Portal\"\n description=\"Manage clients and their projects\"\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 Add Client\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=\"Active Clients\"\n value={String(stats?.activeClients ?? 0)}\n icon={<Building2 className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Total Projects\"\n value={String(stats?.totalProjectsShared ?? 0)}\n icon={<FolderOpen className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Total Clients\"\n value={String(stats?.totalClients ?? 0)}\n icon={<DollarSign className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Active\"\n value={String(stats?.activeClients ?? 0)}\n trend={{ value: 0, label: \"clients\" }}\n />\n </div>\n\n
|
|
1
|
+
{"version":3,"file":"ClientPortalPage.js","names":[],"sources":["../../../../src/timecampus/pages/client-portal/ClientPortalPage.tsx"],"sourcesContent":["import { Building2, Plus, DollarSign, FolderOpen } 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 formatCurrency,\n getInitials,\n} from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListClientPortalConfigsQuery,\n useGetClientPortalStatsQuery,\n type ClientPortalFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function ClientPortalPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const {\n data: configsData,\n loading: configsLoading,\n error: configsError,\n } = useListClientPortalConfigsQuery();\n const { data: statsData, loading: statsLoading } =\n useGetClientPortalStatsQuery();\n\n const clients = configsData?.clientPortalConfigs?.items ?? [];\n const stats = statsData?.clientPortalStats;\n\n if (configsLoading) {\n return (\n <PageLayout\n title=\"Client Portal\"\n description=\"Manage clients and their projects\"\n >\n <LoadingSkeleton variant=\"list\" count={4} />\n </PageLayout>\n );\n }\n\n if (configsError) {\n return (\n <PageLayout\n title=\"Client Portal\"\n description=\"Manage clients and their projects\"\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 client portal data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Client Portal\"\n description=\"Manage clients and their projects\"\n purpose=\"Give clients a transparent window into the work you do for them — the projects, hours and progress tied to each one — so billing conversations are backed by data and trust, not spreadsheets.\"\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 Add Client\n </button>\n }\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n featured\n title=\"Active Clients\"\n value={String(stats?.activeClients ?? 0)}\n icon={<Building2 className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Total Projects\"\n value={String(stats?.totalProjectsShared ?? 0)}\n icon={<FolderOpen className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Total Clients\"\n value={String(stats?.totalClients ?? 0)}\n icon={<DollarSign className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Active\"\n value={String(stats?.activeClients ?? 0)}\n trend={{ value: 0, label: \"clients\" }}\n />\n </div>\n\n {clients.length === 0 ? (\n <EmptyState\n illustration=\"empty-people\"\n title=\"No clients configured yet\"\n description=\"Add your first client to share projects and tracked hours with them.\"\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 Add Client\n </button>\n }\n />\n ) : (\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop\">\n <div className=\"divide-y divide-border\">\n {clients.map((client: ClientPortalFieldsFragment) => {\n const totalTrackedHours = client.projects.reduce(\n (sum: number, p: { trackedHours: number }) =>\n sum + p.trackedHours,\n 0,\n );\n return (\n <div\n key={client.id}\n className=\"flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors cursor-pointer\"\n >\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 text-sm font-medium text-primary\">\n {getInitials(client.clientName)}\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {client.clientName}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {client.projects.length} projects ·{\" \"}\n {totalTrackedHours}h tracked\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-4\">\n {client.contactEmail && (\n <span className=\"text-xs text-text-secondary\">\n {client.contactEmail}\n </span>\n )}\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getStatusColor(client.isActive ? \"active\" : \"inactive\")}`}\n >\n {client.isActive ? \"active\" : \"inactive\"}\n </span>\n </div>\n </div>\n );\n })}\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;AAiBA,SAAgB,IAAmB;CACjC,IAAM,EAAE,iBAAc,OAAO,GAAe,EAEtC,EACJ,MAAM,GACN,SAAS,GACT,OAAO,MACL,GAAiC,EAC/B,EAAE,MAAM,GAAW,SAAS,MAChC,GAA8B,EAE1B,IAAU,GAAa,qBAAqB,SAAS,EAAE,EACvD,IAAQ,GAAW;AA0BzB,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,aAErB;;YARb,CAWE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KACE,UAAA;KACA,OAAM;KACN,OAAO,OAAO,GAAO,iBAAiB,EAAE;KACxC,MAAM,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA;KACvC,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,OAAO,GAAO,uBAAuB,EAAE;KAC9C,MAAM,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;KACxC,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,OAAO,GAAO,gBAAgB,EAAE;KACvC,MAAM,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;KACxC,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,OAAO,GAAO,iBAAiB,EAAE;KACxC,OAAO;MAAE,OAAO;MAAG,OAAO;MAAW;KACrC,CAAA;IACE;MAEL,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,aAErB;;GAEX,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAQ,KAAK,MAAuC;KACnD,IAAM,IAAoB,EAAO,SAAS,QACvC,GAAa,MACZ,IAAM,EAAE,cACV,EACD;AACD,YACE,kBAAC,OAAD;MAEE,WAAU;gBAFZ,CAIE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACZ,EAAY,EAAO,WAAW;QAC3B,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAO;QACN,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACG,EAAO,SAAS;SAAO;SAAmB;SAC1C;SAAkB;SACjB;UACA,EAAA,CAAA,CACF;UACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACG,EAAO,gBACN,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAO;QACH,CAAA,EAET,kBAAC,QAAD;QACE,WAAW,4DAA4D,EAAe,EAAO,WAAW,WAAW,WAAW;kBAE7H,EAAO,WAAW,WAAW;QACzB,CAAA,CACH;SACF;QA7BC,EAAO,GA6BR;MAER;IACE,CAAA;GACF,CAAA,CAEG"}
|