@burdenoff/microfe-timecampus 2026.703.7 → 2026.707.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/generated/wspace-operations.js +1023 -1514
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/generated/wspace-types.js +12 -18
- package/dist/generated/wspace-types.js.map +1 -1
- package/dist/index.js +4 -4
- package/dist/timecampus/TimeCampusRoot.js +82 -150
- package/dist/timecampus/TimeCampusRoot.js.map +1 -1
- package/dist/timecampus/TimeCampusRoutes.js +96 -164
- package/dist/timecampus/TimeCampusRoutes.js.map +1 -1
- package/dist/timecampus/components/index.js +2 -2
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +7 -7
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js +6 -6
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +8 -8
- package/dist/timecampus/pages/geofencing/GeofencingPage.js +6 -6
- package/dist/timecampus/pages/lifetime/FiniteLifePage.js +91 -91
- package/dist/timecampus/pages/lifetime/FiniteLifePage.js.map +1 -1
- package/dist/timecampus/pages/lifetime/LifeBalancePage.js +18 -18
- package/dist/timecampus/pages/lifetime/LifeTimelinePage.js +7 -7
- package/dist/timecampus/pages/lifetime/ValuesGoalsPage.js +1 -1
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +6 -6
- package/dist/timecampus/pages/projects/ProjectDetailPage.js +7 -7
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +212 -212
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamMembersPage.js +4 -4
- package/dist/timecampus/pages/time/TimeTrackingPage.js +143 -143
- package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +1 -1
- package/dist/timecampus/pages/timeline/TimelinePage.js +1 -1
- package/dist/timecampus/pages/timeline/TimelinePage.js.map +1 -1
- package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js +87 -93
- package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js.map +1 -1
- package/package.json +1 -1
- package/dist/timecampus/pages/applets/AppletStorePage.js +0 -167
- package/dist/timecampus/pages/applets/AppletStorePage.js.map +0 -1
- package/dist/timecampus/pages/applets/InstalledAppletsPage.js +0 -162
- package/dist/timecampus/pages/applets/InstalledAppletsPage.js.map +0 -1
- package/dist/timecampus/pages/application-usage/ApplicationUsageSettingsPage.js +0 -133
- package/dist/timecampus/pages/application-usage/ApplicationUsageSettingsPage.js.map +0 -1
- package/dist/timecampus/pages/channels/ChannelsPage.js +0 -418
- package/dist/timecampus/pages/channels/ChannelsPage.js.map +0 -1
- package/dist/timecampus/pages/email-tracking/EmailSettingsPage.js +0 -142
- package/dist/timecampus/pages/email-tracking/EmailSettingsPage.js.map +0 -1
- package/dist/timecampus/pages/focus-score/FocusScoreSettingsPage.js +0 -119
- package/dist/timecampus/pages/focus-score/FocusScoreSettingsPage.js.map +0 -1
- package/dist/timecampus/pages/geofencing/GeofencingSettingsPage.js +0 -110
- package/dist/timecampus/pages/geofencing/GeofencingSettingsPage.js.map +0 -1
- package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js +0 -83
- package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js.map +0 -1
- package/dist/timecampus/pages/location-tracking/LocationPrivacySettingsPage.js +0 -198
- package/dist/timecampus/pages/location-tracking/LocationPrivacySettingsPage.js.map +0 -1
- package/dist/timecampus/pages/team-activity/TeamActivitySettingsPage.js +0 -124
- package/dist/timecampus/pages/team-activity/TeamActivitySettingsPage.js.map +0 -1
- package/dist/timecampus/pages/templates/TemplateApplicationPage.js +0 -88
- package/dist/timecampus/pages/templates/TemplateApplicationPage.js.map +0 -1
- package/dist/timecampus/pages/templates/TemplateDetailPage.js +0 -135
- package/dist/timecampus/pages/templates/TemplateDetailPage.js.map +0 -1
- package/dist/timecampus/pages/templates/TemplatesPage.js +0 -191
- package/dist/timecampus/pages/templates/TemplatesPage.js.map +0 -1
- package/dist/timecampus/pages/widgets/WidgetDetailPage.js +0 -196
- package/dist/timecampus/pages/widgets/WidgetDetailPage.js.map +0 -1
- package/dist/timecampus/pages/widgets/WidgetsPage.js +0 -297
- package/dist/timecampus/pages/widgets/WidgetsPage.js.map +0 -1
- package/dist/timecampus/pages/work-patterns/WorkPatternsSettingsPage.js +0 -133
- package/dist/timecampus/pages/work-patterns/WorkPatternsSettingsPage.js.map +0 -1
- package/dist/timecampus/pages/workflows/WorkflowsPage.js +0 -311
- package/dist/timecampus/pages/workflows/WorkflowsPage.js.map +0 -1
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
|
|
2
|
-
import { LoadingSkeleton as t } from "../../components/LoadingSkeleton.js";
|
|
3
|
-
import { useListConfigurationsQuery as n, useSetConfigurationMutation as r } from "../../../generated/wspace-operations.js";
|
|
4
|
-
import { PageLayout as i } from "../../components/PageLayout.js";
|
|
5
|
-
import { useCallback as a, useEffect as o, useState as s } from "react";
|
|
6
|
-
import { Settings as c } from "lucide-react";
|
|
7
|
-
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
8
|
-
import { GlassCard as d } from "@burdenoff/fe-libs/ui";
|
|
9
|
-
//#region src/timecampus/pages/application-usage/ApplicationUsageSettingsPage.tsx
|
|
10
|
-
var f = "timecampus.appusage.settings", p = {
|
|
11
|
-
trackActiveApplications: !0,
|
|
12
|
-
trackBrowserTabs: !0,
|
|
13
|
-
distractionAlerts: !1
|
|
14
|
-
};
|
|
15
|
-
function m() {
|
|
16
|
-
let { workspaceId: m = "" } = e(), { data: h, loading: g, error: _ } = n({
|
|
17
|
-
variables: {
|
|
18
|
-
scope: "WORKSPACE",
|
|
19
|
-
limit: 100
|
|
20
|
-
},
|
|
21
|
-
skip: !m
|
|
22
|
-
}), [v, { loading: y }] = r(), [b, x] = s(p);
|
|
23
|
-
o(() => {
|
|
24
|
-
if (h?.configurations) {
|
|
25
|
-
let e = h.configurations.find((e) => e.key === f);
|
|
26
|
-
if (e?.value) {
|
|
27
|
-
let t = e.value;
|
|
28
|
-
x({
|
|
29
|
-
trackActiveApplications: t.trackActiveApplications ?? p.trackActiveApplications,
|
|
30
|
-
trackBrowserTabs: t.trackBrowserTabs ?? p.trackBrowserTabs,
|
|
31
|
-
distractionAlerts: t.distractionAlerts ?? p.distractionAlerts
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}, [h]);
|
|
36
|
-
let S = a(async () => {
|
|
37
|
-
await v({ variables: { input: {
|
|
38
|
-
key: f,
|
|
39
|
-
value: b,
|
|
40
|
-
description: "Application usage tracking settings",
|
|
41
|
-
scope: "WORKSPACE"
|
|
42
|
-
} } });
|
|
43
|
-
}, [v, b]);
|
|
44
|
-
return g ? /* @__PURE__ */ l(i, {
|
|
45
|
-
title: "App Usage Settings",
|
|
46
|
-
description: "Configure application tracking",
|
|
47
|
-
children: /* @__PURE__ */ l(t, { variant: "detail" })
|
|
48
|
-
}) : _ ? /* @__PURE__ */ l(i, {
|
|
49
|
-
title: "App Usage Settings",
|
|
50
|
-
description: "Configure application tracking",
|
|
51
|
-
children: /* @__PURE__ */ l("div", {
|
|
52
|
-
className: "rounded-xl border border-status-error-border/50 bg-status-error-bg/10 p-4 text-sm text-status-error-text",
|
|
53
|
-
children: "Failed to load settings. Please try again later."
|
|
54
|
-
})
|
|
55
|
-
}) : /* @__PURE__ */ l(i, {
|
|
56
|
-
title: "App Usage Settings",
|
|
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.",
|
|
59
|
-
children: /* @__PURE__ */ u("div", {
|
|
60
|
-
className: "mx-auto max-w-2xl space-y-6",
|
|
61
|
-
children: [/* @__PURE__ */ u(d, {
|
|
62
|
-
treatment: "glass",
|
|
63
|
-
glow: !0,
|
|
64
|
-
className: "p-6",
|
|
65
|
-
children: [/* @__PURE__ */ u("h3", {
|
|
66
|
-
className: "mb-4 flex items-center gap-2 text-sm font-semibold text-text-primary",
|
|
67
|
-
children: [/* @__PURE__ */ l(c, { className: "h-4 w-4" }), "Tracking Configuration"]
|
|
68
|
-
}), /* @__PURE__ */ u("div", {
|
|
69
|
-
className: "space-y-3",
|
|
70
|
-
children: [
|
|
71
|
-
/* @__PURE__ */ u("label", {
|
|
72
|
-
className: "flex items-center justify-between",
|
|
73
|
-
children: [/* @__PURE__ */ l("span", {
|
|
74
|
-
className: "text-sm text-text-primary",
|
|
75
|
-
children: "Track active applications"
|
|
76
|
-
}), /* @__PURE__ */ l("input", {
|
|
77
|
-
type: "checkbox",
|
|
78
|
-
checked: b.trackActiveApplications,
|
|
79
|
-
onChange: (e) => x((t) => ({
|
|
80
|
-
...t,
|
|
81
|
-
trackActiveApplications: e.target.checked
|
|
82
|
-
})),
|
|
83
|
-
className: "h-4 w-4 rounded border-border-subtle"
|
|
84
|
-
})]
|
|
85
|
-
}),
|
|
86
|
-
/* @__PURE__ */ u("label", {
|
|
87
|
-
className: "flex items-center justify-between",
|
|
88
|
-
children: [/* @__PURE__ */ l("span", {
|
|
89
|
-
className: "text-sm text-text-primary",
|
|
90
|
-
children: "Track browser tabs"
|
|
91
|
-
}), /* @__PURE__ */ l("input", {
|
|
92
|
-
type: "checkbox",
|
|
93
|
-
checked: b.trackBrowserTabs,
|
|
94
|
-
onChange: (e) => x((t) => ({
|
|
95
|
-
...t,
|
|
96
|
-
trackBrowserTabs: e.target.checked
|
|
97
|
-
})),
|
|
98
|
-
className: "h-4 w-4 rounded border-border-subtle"
|
|
99
|
-
})]
|
|
100
|
-
}),
|
|
101
|
-
/* @__PURE__ */ u("label", {
|
|
102
|
-
className: "flex items-center justify-between",
|
|
103
|
-
children: [/* @__PURE__ */ l("span", {
|
|
104
|
-
className: "text-sm text-text-primary",
|
|
105
|
-
children: "Distraction alerts"
|
|
106
|
-
}), /* @__PURE__ */ l("input", {
|
|
107
|
-
type: "checkbox",
|
|
108
|
-
checked: b.distractionAlerts,
|
|
109
|
-
onChange: (e) => x((t) => ({
|
|
110
|
-
...t,
|
|
111
|
-
distractionAlerts: e.target.checked
|
|
112
|
-
})),
|
|
113
|
-
className: "h-4 w-4 rounded border-border-subtle"
|
|
114
|
-
})]
|
|
115
|
-
})
|
|
116
|
-
]
|
|
117
|
-
})]
|
|
118
|
-
}), /* @__PURE__ */ l("div", {
|
|
119
|
-
className: "flex justify-end",
|
|
120
|
-
children: /* @__PURE__ */ l("button", {
|
|
121
|
-
onClick: S,
|
|
122
|
-
disabled: y,
|
|
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",
|
|
124
|
-
children: y ? "Saving..." : "Save Settings"
|
|
125
|
-
})
|
|
126
|
-
})]
|
|
127
|
-
})
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
//#endregion
|
|
131
|
-
export { m as ApplicationUsageSettingsPage };
|
|
132
|
-
|
|
133
|
-
//# sourceMappingURL=ApplicationUsageSettingsPage.js.map
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1,418 +0,0 @@
|
|
|
1
|
-
import { LoadingSkeleton as e } from "../../components/LoadingSkeleton.js";
|
|
2
|
-
import { useCreateChannelConfigMutation as t, useDeleteChannelConfigMutation as n, useListChannelConfigsQuery as r, useListChannelStatusesQuery as i, useSyncChannelMutation as a, useToggleChannelMutation as ee, useUpdateChannelConfigMutation as te } from "../../../generated/wspace-operations.js";
|
|
3
|
-
import { PageLayout as o } from "../../components/PageLayout.js";
|
|
4
|
-
import { StatusBadge as ne } from "../../components/StatusBadge.js";
|
|
5
|
-
import { EmptyState as re } from "../../components/EmptyState.js";
|
|
6
|
-
import { ToastContainer as s, useToast as ie } from "../../components/Toast.js";
|
|
7
|
-
import { Modal as c } from "../../components/Modal.js";
|
|
8
|
-
import { ChannelStatus as l, TimeCampusChannelType as u } from "../../../generated/wspace-types.js";
|
|
9
|
-
import { useState as d } from "react";
|
|
10
|
-
import { Pencil as ae, Plus as oe, Power as f, RefreshCw as p, Trash2 as m, Wifi as h, WifiOff as g } from "lucide-react";
|
|
11
|
-
import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
12
|
-
import { GlassCard as se } from "@burdenoff/fe-libs/ui";
|
|
13
|
-
//#region src/timecampus/pages/channels/ChannelsPage.tsx
|
|
14
|
-
var b = 25, ce = [
|
|
15
|
-
{
|
|
16
|
-
value: u.Calendar,
|
|
17
|
-
label: "Calendar"
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
value: u.Desktop,
|
|
21
|
-
label: "Desktop"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
value: u.Browser,
|
|
25
|
-
label: "Browser"
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
value: u.Mobile,
|
|
29
|
-
label: "Mobile"
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
value: u.Ide,
|
|
33
|
-
label: "IDE"
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
value: u.Cli,
|
|
37
|
-
label: "CLI"
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
value: u.Api,
|
|
41
|
-
label: "API"
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
value: u.Custom,
|
|
45
|
-
label: "Custom"
|
|
46
|
-
}
|
|
47
|
-
], x = "mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary";
|
|
48
|
-
function le(e) {
|
|
49
|
-
if (e == null || e <= 0) return "Manual";
|
|
50
|
-
if (e % 3600 == 0) {
|
|
51
|
-
let t = e / 3600;
|
|
52
|
-
return `Every ${t} hour${t > 1 ? "s" : ""}`;
|
|
53
|
-
}
|
|
54
|
-
return e % 60 == 0 ? `Every ${e / 60} min` : `Every ${e}s`;
|
|
55
|
-
}
|
|
56
|
-
var ue = {
|
|
57
|
-
healthy: "bg-status-success-text",
|
|
58
|
-
degraded: "bg-status-warning-text",
|
|
59
|
-
unknown: "bg-text-muted"
|
|
60
|
-
}, de = {
|
|
61
|
-
healthy: "Healthy connection",
|
|
62
|
-
degraded: "Sync needs attention",
|
|
63
|
-
unknown: "Not synced yet"
|
|
64
|
-
}, S = {
|
|
65
|
-
name: "",
|
|
66
|
-
type: u.Calendar,
|
|
67
|
-
syncInterval: ""
|
|
68
|
-
};
|
|
69
|
-
function C() {
|
|
70
|
-
let { toasts: C, addToast: w, dismissToast: fe } = ie(), { data: pe, loading: T, error: E, refetch: D, fetchMore: O } = r({
|
|
71
|
-
variables: {
|
|
72
|
-
limit: b,
|
|
73
|
-
offset: 0
|
|
74
|
-
},
|
|
75
|
-
notifyOnNetworkStatusChange: !0
|
|
76
|
-
}), { data: k } = i(), [A, { loading: j }] = t(), [M, { loading: N }] = te(), [P, { loading: F }] = n(), [I, { loading: L }] = ee(), [R, { loading: z }] = a(), B = pe?.channelConfigs ?? [], me = k?.channelStatuses ?? [], [V, H] = d(!1), [he, ge] = d(!0), [_e, U] = d(!1), [W, G] = d(null), [K, q] = d(S), [J, Y] = d(null), [ve, X] = d(null), ye = new Map(me.map((e) => [e.channelId, e])), be = () => {
|
|
77
|
-
G(null), q(S), U(!0);
|
|
78
|
-
}, xe = (e) => {
|
|
79
|
-
G(e), q({
|
|
80
|
-
name: e.name,
|
|
81
|
-
type: Object.values(u).find((t) => t === e.type) ?? u.Custom,
|
|
82
|
-
syncInterval: e.syncInterval == null ? "" : String(e.syncInterval)
|
|
83
|
-
}), U(!0);
|
|
84
|
-
}, Z = () => {
|
|
85
|
-
U(!1), G(null);
|
|
86
|
-
}, Q = async () => {
|
|
87
|
-
if (!K.name.trim()) return;
|
|
88
|
-
let e = K.syncInterval ? Number(K.syncInterval) : void 0;
|
|
89
|
-
try {
|
|
90
|
-
W ? (await M({ variables: {
|
|
91
|
-
id: W.id,
|
|
92
|
-
input: {
|
|
93
|
-
name: K.name.trim(),
|
|
94
|
-
type: K.type,
|
|
95
|
-
syncInterval: e
|
|
96
|
-
}
|
|
97
|
-
} }), w({
|
|
98
|
-
title: "Channel updated",
|
|
99
|
-
variant: "success"
|
|
100
|
-
})) : (await A({ variables: { input: {
|
|
101
|
-
name: K.name.trim(),
|
|
102
|
-
type: K.type,
|
|
103
|
-
syncInterval: e,
|
|
104
|
-
status: l.Active
|
|
105
|
-
} } }), w({
|
|
106
|
-
title: "Channel added",
|
|
107
|
-
variant: "success"
|
|
108
|
-
})), Z(), await D();
|
|
109
|
-
} catch {
|
|
110
|
-
w({
|
|
111
|
-
title: W ? "Failed to update channel" : "Failed to add channel",
|
|
112
|
-
variant: "error"
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
}, Se = async () => {
|
|
116
|
-
if (J) try {
|
|
117
|
-
await P({ variables: { id: J.id } }), w({
|
|
118
|
-
title: "Channel deleted",
|
|
119
|
-
variant: "success"
|
|
120
|
-
}), Y(null), await D();
|
|
121
|
-
} catch {
|
|
122
|
-
w({
|
|
123
|
-
title: "Failed to delete channel",
|
|
124
|
-
variant: "error"
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
}, Ce = async (e) => {
|
|
128
|
-
let t = e.status.toLowerCase() === "active";
|
|
129
|
-
X(e.id);
|
|
130
|
-
try {
|
|
131
|
-
await I({ variables: {
|
|
132
|
-
id: e.id,
|
|
133
|
-
status: t ? l.Inactive : l.Active
|
|
134
|
-
} }), w({
|
|
135
|
-
title: t ? "Channel disabled" : "Channel enabled",
|
|
136
|
-
variant: "success"
|
|
137
|
-
}), await D();
|
|
138
|
-
} catch {
|
|
139
|
-
w({
|
|
140
|
-
title: "Failed to toggle channel",
|
|
141
|
-
variant: "error"
|
|
142
|
-
});
|
|
143
|
-
} finally {
|
|
144
|
-
X(null);
|
|
145
|
-
}
|
|
146
|
-
}, we = async (e) => {
|
|
147
|
-
X(e.id);
|
|
148
|
-
try {
|
|
149
|
-
await R({ variables: { id: e.id } }), w({
|
|
150
|
-
title: "Sync started",
|
|
151
|
-
variant: "success"
|
|
152
|
-
}), await D();
|
|
153
|
-
} catch {
|
|
154
|
-
w({
|
|
155
|
-
title: "Failed to sync channel",
|
|
156
|
-
variant: "error"
|
|
157
|
-
});
|
|
158
|
-
} finally {
|
|
159
|
-
X(null);
|
|
160
|
-
}
|
|
161
|
-
}, Te = async () => {
|
|
162
|
-
H(!0);
|
|
163
|
-
try {
|
|
164
|
-
((await O({
|
|
165
|
-
variables: {
|
|
166
|
-
limit: b,
|
|
167
|
-
offset: B.length
|
|
168
|
-
},
|
|
169
|
-
updateQuery: (e, { fetchMoreResult: t }) => t ? {
|
|
170
|
-
...e,
|
|
171
|
-
channelConfigs: [...e.channelConfigs, ...t.channelConfigs]
|
|
172
|
-
} : e
|
|
173
|
-
})).data?.channelConfigs?.length ?? 0) < b && ge(!1);
|
|
174
|
-
} finally {
|
|
175
|
-
H(!1);
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
if (T && B.length === 0) return /* @__PURE__ */ v(o, {
|
|
179
|
-
title: "Channels",
|
|
180
|
-
description: "Manage time tracking channels and integrations",
|
|
181
|
-
children: /* @__PURE__ */ v(e, {
|
|
182
|
-
variant: "card",
|
|
183
|
-
count: 6
|
|
184
|
-
})
|
|
185
|
-
});
|
|
186
|
-
if (E) return /* @__PURE__ */ v(o, {
|
|
187
|
-
title: "Channels",
|
|
188
|
-
description: "Manage time tracking channels and integrations",
|
|
189
|
-
children: /* @__PURE__ */ v("div", {
|
|
190
|
-
className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
|
|
191
|
-
children: "Failed to load channels data. Please try again later."
|
|
192
|
-
})
|
|
193
|
-
});
|
|
194
|
-
let $ = /* @__PURE__ */ y("button", {
|
|
195
|
-
onClick: be,
|
|
196
|
-
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",
|
|
197
|
-
children: [/* @__PURE__ */ v(oe, { className: "h-4 w-4" }), "Add Channel"]
|
|
198
|
-
});
|
|
199
|
-
return /* @__PURE__ */ y(o, {
|
|
200
|
-
title: "Channels",
|
|
201
|
-
description: "Manage time tracking channels and integrations",
|
|
202
|
-
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.",
|
|
203
|
-
actions: $,
|
|
204
|
-
children: [
|
|
205
|
-
B.length === 0 ? /* @__PURE__ */ v(re, {
|
|
206
|
-
illustration: "empty-generic",
|
|
207
|
-
title: "No channels configured yet",
|
|
208
|
-
description: "Set up your first tracking channel to start pulling time and activity from your tools and devices.",
|
|
209
|
-
action: $
|
|
210
|
-
}) : /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v("div", {
|
|
211
|
-
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
212
|
-
children: B.map((e) => {
|
|
213
|
-
let t = ye.get(e.id), n = e.status.toLowerCase() === "active", r = e.lastSync ? new Date(e.lastSync).toLocaleString() : "Never", i = t ? t.isHealthy ? "healthy" : "degraded" : "unknown", a = ve === e.id;
|
|
214
|
-
return /* @__PURE__ */ y(se, {
|
|
215
|
-
treatment: "glass",
|
|
216
|
-
className: "flex flex-col p-5",
|
|
217
|
-
children: [
|
|
218
|
-
/* @__PURE__ */ y("div", {
|
|
219
|
-
className: "flex items-start justify-between gap-3",
|
|
220
|
-
children: [/* @__PURE__ */ y("div", {
|
|
221
|
-
className: "flex min-w-0 items-center gap-3",
|
|
222
|
-
children: [/* @__PURE__ */ v("div", {
|
|
223
|
-
className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary",
|
|
224
|
-
children: n ? /* @__PURE__ */ v(h, { className: "h-5 w-5" }) : /* @__PURE__ */ v(g, { className: "h-5 w-5 text-text-secondary" })
|
|
225
|
-
}), /* @__PURE__ */ y("div", {
|
|
226
|
-
className: "min-w-0",
|
|
227
|
-
children: [/* @__PURE__ */ v("p", {
|
|
228
|
-
className: "truncate text-sm font-semibold text-text-primary",
|
|
229
|
-
children: e.name
|
|
230
|
-
}), /* @__PURE__ */ v("p", {
|
|
231
|
-
className: "text-xs capitalize text-text-secondary",
|
|
232
|
-
children: e.type
|
|
233
|
-
})]
|
|
234
|
-
})]
|
|
235
|
-
}), /* @__PURE__ */ v(ne, {
|
|
236
|
-
label: e.status,
|
|
237
|
-
status: e.status.toLowerCase(),
|
|
238
|
-
dot: !0
|
|
239
|
-
})]
|
|
240
|
-
}),
|
|
241
|
-
/* @__PURE__ */ y("div", {
|
|
242
|
-
className: "mt-4 space-y-2 text-xs",
|
|
243
|
-
children: [
|
|
244
|
-
/* @__PURE__ */ y("div", {
|
|
245
|
-
className: "flex items-center gap-2",
|
|
246
|
-
children: [/* @__PURE__ */ v("span", {
|
|
247
|
-
className: `h-2 w-2 shrink-0 rounded-full ${ue[i]}`,
|
|
248
|
-
"aria-hidden": "true"
|
|
249
|
-
}), /* @__PURE__ */ v("span", {
|
|
250
|
-
className: "text-text-secondary",
|
|
251
|
-
children: de[i]
|
|
252
|
-
})]
|
|
253
|
-
}),
|
|
254
|
-
/* @__PURE__ */ y("div", {
|
|
255
|
-
className: "flex items-center justify-between gap-2",
|
|
256
|
-
children: [/* @__PURE__ */ v("span", {
|
|
257
|
-
className: "text-text-secondary",
|
|
258
|
-
children: "Last sync"
|
|
259
|
-
}), /* @__PURE__ */ v("span", {
|
|
260
|
-
className: "truncate font-medium text-text-primary",
|
|
261
|
-
children: r
|
|
262
|
-
})]
|
|
263
|
-
}),
|
|
264
|
-
/* @__PURE__ */ y("div", {
|
|
265
|
-
className: "flex items-center justify-between gap-2",
|
|
266
|
-
children: [/* @__PURE__ */ v("span", {
|
|
267
|
-
className: "text-text-secondary",
|
|
268
|
-
children: "Interval"
|
|
269
|
-
}), /* @__PURE__ */ v("span", {
|
|
270
|
-
className: "font-medium text-text-primary",
|
|
271
|
-
children: le(e.syncInterval)
|
|
272
|
-
})]
|
|
273
|
-
})
|
|
274
|
-
]
|
|
275
|
-
}),
|
|
276
|
-
/* @__PURE__ */ y("div", {
|
|
277
|
-
className: "mt-4 flex items-center gap-1.5 border-t border-border-seam pt-3",
|
|
278
|
-
children: [
|
|
279
|
-
/* @__PURE__ */ y("button", {
|
|
280
|
-
type: "button",
|
|
281
|
-
disabled: a && z,
|
|
282
|
-
onClick: () => we(e),
|
|
283
|
-
title: "Sync now",
|
|
284
|
-
className: "inline-flex items-center gap-1 rounded-md border border-border-seam bg-bg-surface px-2 py-1 text-xs text-text-secondary hover:bg-bg-sunken/50 disabled:opacity-50",
|
|
285
|
-
children: [/* @__PURE__ */ v(p, { className: `h-3.5 w-3.5 ${a && z ? "animate-spin" : ""}` }), "Sync"]
|
|
286
|
-
}),
|
|
287
|
-
/* @__PURE__ */ y("button", {
|
|
288
|
-
type: "button",
|
|
289
|
-
disabled: a && L,
|
|
290
|
-
onClick: () => Ce(e),
|
|
291
|
-
title: n ? "Disable channel" : "Enable channel",
|
|
292
|
-
className: "inline-flex items-center gap-1 rounded-md border border-border-seam bg-bg-surface px-2 py-1 text-xs text-text-secondary hover:bg-bg-sunken/50 disabled:opacity-50",
|
|
293
|
-
children: [/* @__PURE__ */ v(f, { className: "h-3.5 w-3.5" }), n ? "Disable" : "Enable"]
|
|
294
|
-
}),
|
|
295
|
-
/* @__PURE__ */ y("div", {
|
|
296
|
-
className: "ml-auto flex items-center gap-1",
|
|
297
|
-
children: [/* @__PURE__ */ v("button", {
|
|
298
|
-
type: "button",
|
|
299
|
-
onClick: () => xe(e),
|
|
300
|
-
title: "Edit channel",
|
|
301
|
-
className: "inline-flex items-center rounded-md border border-border-seam bg-bg-surface p-1.5 text-text-secondary hover:bg-bg-sunken/50",
|
|
302
|
-
children: /* @__PURE__ */ v(ae, { className: "h-3.5 w-3.5" })
|
|
303
|
-
}), /* @__PURE__ */ v("button", {
|
|
304
|
-
type: "button",
|
|
305
|
-
onClick: () => Y(e),
|
|
306
|
-
title: "Delete channel",
|
|
307
|
-
className: "inline-flex items-center rounded-md border border-border-seam bg-bg-surface p-1.5 text-status-error-text hover:bg-status-error-bg",
|
|
308
|
-
children: /* @__PURE__ */ v(m, { className: "h-3.5 w-3.5" })
|
|
309
|
-
})]
|
|
310
|
-
})
|
|
311
|
-
]
|
|
312
|
-
})
|
|
313
|
-
]
|
|
314
|
-
}, e.id);
|
|
315
|
-
})
|
|
316
|
-
}), he && B.length >= b && /* @__PURE__ */ v("div", {
|
|
317
|
-
className: "mt-4 flex justify-center",
|
|
318
|
-
children: /* @__PURE__ */ v("button", {
|
|
319
|
-
onClick: Te,
|
|
320
|
-
disabled: V,
|
|
321
|
-
className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50 disabled:opacity-50",
|
|
322
|
-
children: V ? "Loading..." : "Load more"
|
|
323
|
-
})
|
|
324
|
-
})] }),
|
|
325
|
-
/* @__PURE__ */ v(c, {
|
|
326
|
-
open: _e,
|
|
327
|
-
onClose: Z,
|
|
328
|
-
title: W ? "Edit Channel" : "Add Channel",
|
|
329
|
-
description: W ? "Update this channel's configuration." : "Connect a new tracking channel to feed time into TimeCampus.",
|
|
330
|
-
footer: /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v("button", {
|
|
331
|
-
onClick: Z,
|
|
332
|
-
className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
|
|
333
|
-
children: "Cancel"
|
|
334
|
-
}), /* @__PURE__ */ v("button", {
|
|
335
|
-
onClick: Q,
|
|
336
|
-
disabled: j || N || !K.name.trim(),
|
|
337
|
-
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",
|
|
338
|
-
children: j || N ? "Saving..." : W ? "Save Changes" : "Add Channel"
|
|
339
|
-
})] }),
|
|
340
|
-
children: /* @__PURE__ */ y("div", {
|
|
341
|
-
className: "space-y-4",
|
|
342
|
-
children: [/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
|
|
343
|
-
className: "block text-sm font-medium text-text-primary",
|
|
344
|
-
children: "Name"
|
|
345
|
-
}), /* @__PURE__ */ v("input", {
|
|
346
|
-
type: "text",
|
|
347
|
-
placeholder: "e.g. Google Calendar",
|
|
348
|
-
value: K.name,
|
|
349
|
-
onChange: (e) => q({
|
|
350
|
-
...K,
|
|
351
|
-
name: e.target.value
|
|
352
|
-
}),
|
|
353
|
-
className: x
|
|
354
|
-
})] }), /* @__PURE__ */ y("div", {
|
|
355
|
-
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
356
|
-
children: [/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
|
|
357
|
-
className: "block text-sm font-medium text-text-primary",
|
|
358
|
-
children: "Type"
|
|
359
|
-
}), /* @__PURE__ */ v("select", {
|
|
360
|
-
value: K.type,
|
|
361
|
-
onChange: (e) => q({
|
|
362
|
-
...K,
|
|
363
|
-
type: e.target.value
|
|
364
|
-
}),
|
|
365
|
-
className: x,
|
|
366
|
-
children: ce.map((e) => /* @__PURE__ */ v("option", {
|
|
367
|
-
value: e.value,
|
|
368
|
-
children: e.label
|
|
369
|
-
}, e.value))
|
|
370
|
-
})] }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
|
|
371
|
-
className: "block text-sm font-medium text-text-primary",
|
|
372
|
-
children: "Sync Interval (seconds)"
|
|
373
|
-
}), /* @__PURE__ */ v("input", {
|
|
374
|
-
type: "number",
|
|
375
|
-
min: 0,
|
|
376
|
-
placeholder: "e.g. 300",
|
|
377
|
-
value: K.syncInterval,
|
|
378
|
-
onChange: (e) => q({
|
|
379
|
-
...K,
|
|
380
|
-
syncInterval: e.target.value
|
|
381
|
-
}),
|
|
382
|
-
className: x
|
|
383
|
-
})] })]
|
|
384
|
-
})]
|
|
385
|
-
})
|
|
386
|
-
}),
|
|
387
|
-
/* @__PURE__ */ v(c, {
|
|
388
|
-
open: J !== null,
|
|
389
|
-
onClose: () => Y(null),
|
|
390
|
-
title: "Delete channel?",
|
|
391
|
-
description: J ? `"${J.name}" will be removed. This cannot be undone.` : void 0,
|
|
392
|
-
size: "sm",
|
|
393
|
-
footer: /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v("button", {
|
|
394
|
-
onClick: () => Y(null),
|
|
395
|
-
className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
|
|
396
|
-
children: "Cancel"
|
|
397
|
-
}), /* @__PURE__ */ v("button", {
|
|
398
|
-
onClick: Se,
|
|
399
|
-
disabled: F,
|
|
400
|
-
className: "rounded-md bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg/80 disabled:opacity-50",
|
|
401
|
-
children: F ? "Deleting..." : "Delete"
|
|
402
|
-
})] }),
|
|
403
|
-
children: /* @__PURE__ */ v("p", {
|
|
404
|
-
className: "text-sm text-text-secondary",
|
|
405
|
-
children: "Deleting a channel stops it from feeding new time into TimeCampus. Previously captured entries are kept."
|
|
406
|
-
})
|
|
407
|
-
}),
|
|
408
|
-
/* @__PURE__ */ v(s, {
|
|
409
|
-
toasts: C,
|
|
410
|
-
onDismiss: fe
|
|
411
|
-
})
|
|
412
|
-
]
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
//#endregion
|
|
416
|
-
export { C as ChannelsPage };
|
|
417
|
-
|
|
418
|
-
//# sourceMappingURL=ChannelsPage.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ChannelsPage.js","names":[],"sources":["../../../../src/timecampus/pages/channels/ChannelsPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport {\n Wifi,\n WifiOff,\n Plus,\n RefreshCw,\n Pencil,\n Trash2,\n Power,\n} from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { StatusBadge } from \"../../components/StatusBadge\";\nimport { Modal } from \"../../components/Modal\";\nimport { ToastContainer, useToast } from \"../../components/Toast\";\nimport {\n useListChannelConfigsQuery,\n useListChannelStatusesQuery,\n useCreateChannelConfigMutation,\n useUpdateChannelConfigMutation,\n useDeleteChannelConfigMutation,\n useToggleChannelMutation,\n useSyncChannelMutation,\n type ChannelConfigFieldsFragment,\n} from \"@/generated/wspace-operations\";\nimport { ChannelStatus, TimeCampusChannelType } from \"@/generated/wspace-types\";\n\nconst PAGE_SIZE = 25;\n\nconst CHANNEL_TYPE_OPTIONS: { value: TimeCampusChannelType; label: string }[] =\n [\n { value: TimeCampusChannelType.Calendar, label: \"Calendar\" },\n { value: TimeCampusChannelType.Desktop, label: \"Desktop\" },\n { value: TimeCampusChannelType.Browser, label: \"Browser\" },\n { value: TimeCampusChannelType.Mobile, label: \"Mobile\" },\n { value: TimeCampusChannelType.Ide, label: \"IDE\" },\n { value: TimeCampusChannelType.Cli, label: \"CLI\" },\n { value: TimeCampusChannelType.Api, label: \"API\" },\n { value: TimeCampusChannelType.Custom, label: \"Custom\" },\n ];\n\nconst inputClass =\n \"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary\";\n\n/** Turn a sync interval in seconds into a human cadence label. */\nfunction formatInterval(seconds?: number | null): string {\n if (seconds == null || seconds <= 0) return \"Manual\";\n if (seconds % 3600 === 0) {\n const h = seconds / 3600;\n return `Every ${h} hour${h > 1 ? \"s\" : \"\"}`;\n }\n if (seconds % 60 === 0) {\n const m = seconds / 60;\n return `Every ${m} min`;\n }\n return `Every ${seconds}s`;\n}\n\ntype ChannelHealth = \"healthy\" | \"degraded\" | \"unknown\";\n\nconst HEALTH_DOT: Record<ChannelHealth, string> = {\n healthy: \"bg-status-success-text\",\n degraded: \"bg-status-warning-text\",\n unknown: \"bg-text-muted\",\n};\n\nconst HEALTH_LABEL: Record<ChannelHealth, string> = {\n healthy: \"Healthy connection\",\n degraded: \"Sync needs attention\",\n unknown: \"Not synced yet\",\n};\n\ninterface ChannelFormState {\n name: string;\n type: TimeCampusChannelType;\n syncInterval: string;\n}\n\nconst emptyForm: ChannelFormState = {\n name: \"\",\n type: TimeCampusChannelType.Calendar,\n syncInterval: \"\",\n};\n\nexport function ChannelsPage() {\n const { toasts, addToast, dismissToast } = useToast();\n\n const {\n data: configsData,\n loading: configsLoading,\n error: configsError,\n refetch,\n fetchMore,\n } = useListChannelConfigsQuery({\n variables: { limit: PAGE_SIZE, offset: 0 },\n notifyOnNetworkStatusChange: true,\n });\n const { data: statusesData } = useListChannelStatusesQuery();\n\n const [createChannel, { loading: creating }] =\n useCreateChannelConfigMutation();\n const [updateChannel, { loading: updating }] =\n useUpdateChannelConfigMutation();\n const [deleteChannel, { loading: deleting }] =\n useDeleteChannelConfigMutation();\n const [toggleChannel, { loading: toggling }] = useToggleChannelMutation();\n const [syncChannel, { loading: syncing }] = useSyncChannelMutation();\n\n const channels = configsData?.channelConfigs ?? [];\n const statuses = statusesData?.channelStatuses ?? [];\n\n const [loadingMore, setLoadingMore] = useState(false);\n const [canLoadMore, setCanLoadMore] = useState(true);\n\n // Create / edit modal state\n const [formOpen, setFormOpen] = useState(false);\n const [editing, setEditing] = useState<ChannelConfigFieldsFragment | null>(\n null,\n );\n const [form, setForm] = useState<ChannelFormState>(emptyForm);\n\n // Delete confirm state\n const [deleteTarget, setDeleteTarget] =\n useState<ChannelConfigFieldsFragment | null>(null);\n\n // Per-row action tracking (which channel id is mid-mutation)\n const [busyId, setBusyId] = useState<string | null>(null);\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 const statusMap = new Map<string, ChannelStatusItem>(\n statuses.map((s: ChannelStatusItem) => [s.channelId, s]),\n );\n\n const openCreate = () => {\n setEditing(null);\n setForm(emptyForm);\n setFormOpen(true);\n };\n\n const openEdit = (channel: ChannelConfigFieldsFragment) => {\n setEditing(channel);\n setForm({\n name: channel.name,\n type:\n (Object.values(TimeCampusChannelType).find(\n (t) => t === channel.type,\n ) as TimeCampusChannelType | undefined) ?? TimeCampusChannelType.Custom,\n syncInterval:\n channel.syncInterval != null ? String(channel.syncInterval) : \"\",\n });\n setFormOpen(true);\n };\n\n const closeForm = () => {\n setFormOpen(false);\n setEditing(null);\n };\n\n const handleSubmit = async () => {\n if (!form.name.trim()) return;\n const syncInterval = form.syncInterval\n ? Number(form.syncInterval)\n : undefined;\n try {\n if (editing) {\n await updateChannel({\n variables: {\n id: editing.id,\n input: {\n name: form.name.trim(),\n type: form.type,\n syncInterval,\n },\n },\n });\n addToast({ title: \"Channel updated\", variant: \"success\" });\n } else {\n await createChannel({\n variables: {\n input: {\n name: form.name.trim(),\n type: form.type,\n syncInterval,\n status: ChannelStatus.Active,\n },\n },\n });\n addToast({ title: \"Channel added\", variant: \"success\" });\n }\n closeForm();\n await refetch();\n } catch {\n addToast({\n title: editing ? \"Failed to update channel\" : \"Failed to add channel\",\n variant: \"error\",\n });\n }\n };\n\n const handleDelete = async () => {\n if (!deleteTarget) return;\n try {\n await deleteChannel({ variables: { id: deleteTarget.id } });\n addToast({ title: \"Channel deleted\", variant: \"success\" });\n setDeleteTarget(null);\n await refetch();\n } catch {\n addToast({ title: \"Failed to delete channel\", variant: \"error\" });\n }\n };\n\n const handleToggle = async (channel: ChannelConfigFieldsFragment) => {\n const isActive = channel.status.toLowerCase() === \"active\";\n setBusyId(channel.id);\n try {\n await toggleChannel({\n variables: {\n id: channel.id,\n status: isActive ? ChannelStatus.Inactive : ChannelStatus.Active,\n },\n });\n addToast({\n title: isActive ? \"Channel disabled\" : \"Channel enabled\",\n variant: \"success\",\n });\n await refetch();\n } catch {\n addToast({ title: \"Failed to toggle channel\", variant: \"error\" });\n } finally {\n setBusyId(null);\n }\n };\n\n const handleSync = async (channel: ChannelConfigFieldsFragment) => {\n setBusyId(channel.id);\n try {\n await syncChannel({ variables: { id: channel.id } });\n addToast({ title: \"Sync started\", variant: \"success\" });\n await refetch();\n } catch {\n addToast({ title: \"Failed to sync channel\", variant: \"error\" });\n } finally {\n setBusyId(null);\n }\n };\n\n const handleLoadMore = async () => {\n setLoadingMore(true);\n try {\n const res = await fetchMore({\n variables: { limit: PAGE_SIZE, offset: channels.length },\n updateQuery: (prev, { fetchMoreResult }) => {\n if (!fetchMoreResult) return prev;\n return {\n ...prev,\n channelConfigs: [\n ...prev.channelConfigs,\n ...fetchMoreResult.channelConfigs,\n ],\n };\n },\n });\n if ((res.data?.channelConfigs?.length ?? 0) < PAGE_SIZE) {\n setCanLoadMore(false);\n }\n } finally {\n setLoadingMore(false);\n }\n };\n\n if (configsLoading && channels.length === 0) {\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 const addButton = (\n <button\n onClick={openCreate}\n 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 >\n <Plus className=\"h-4 w-4\" />\n Add Channel\n </button>\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 actions={addButton}\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 action={addButton}\n />\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.toLowerCase() === \"active\";\n const lastSync = channel.lastSync\n ? new Date(channel.lastSync).toLocaleString()\n : \"Never\";\n const health: ChannelHealth = statusInfo\n ? statusInfo.isHealthy\n ? \"healthy\"\n : \"degraded\"\n : \"unknown\";\n const rowBusy = busyId === channel.id;\n\n return (\n <GlassCard\n key={channel.id}\n treatment=\"glass\"\n className=\"flex flex-col p-5\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <div className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n {isActive ? (\n <Wifi className=\"h-5 w-5\" />\n ) : (\n <WifiOff className=\"h-5 w-5 text-text-secondary\" />\n )}\n </div>\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-text-primary\">\n {channel.name}\n </p>\n <p className=\"text-xs capitalize text-text-secondary\">\n {channel.type}\n </p>\n </div>\n </div>\n <StatusBadge\n label={channel.status}\n status={channel.status.toLowerCase()}\n dot\n />\n </div>\n <div className=\"mt-4 space-y-2 text-xs\">\n <div className=\"flex items-center gap-2\">\n <span\n className={`h-2 w-2 shrink-0 rounded-full ${HEALTH_DOT[health]}`}\n aria-hidden=\"true\"\n />\n <span className=\"text-text-secondary\">\n {HEALTH_LABEL[health]}\n </span>\n </div>\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"text-text-secondary\">Last sync</span>\n <span className=\"truncate font-medium text-text-primary\">\n {lastSync}\n </span>\n </div>\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"text-text-secondary\">Interval</span>\n <span className=\"font-medium text-text-primary\">\n {formatInterval(channel.syncInterval)}\n </span>\n </div>\n </div>\n <div className=\"mt-4 flex items-center gap-1.5 border-t border-border-seam pt-3\">\n <button\n type=\"button\"\n disabled={rowBusy && syncing}\n onClick={() => handleSync(channel)}\n title=\"Sync now\"\n className=\"inline-flex items-center gap-1 rounded-md border border-border-seam bg-bg-surface px-2 py-1 text-xs text-text-secondary hover:bg-bg-sunken/50 disabled:opacity-50\"\n >\n <RefreshCw\n className={`h-3.5 w-3.5 ${rowBusy && syncing ? \"animate-spin\" : \"\"}`}\n />\n Sync\n </button>\n <button\n type=\"button\"\n disabled={rowBusy && toggling}\n onClick={() => handleToggle(channel)}\n title={isActive ? \"Disable channel\" : \"Enable channel\"}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-seam bg-bg-surface px-2 py-1 text-xs text-text-secondary hover:bg-bg-sunken/50 disabled:opacity-50\"\n >\n <Power className=\"h-3.5 w-3.5\" />\n {isActive ? \"Disable\" : \"Enable\"}\n </button>\n <div className=\"ml-auto flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => openEdit(channel)}\n title=\"Edit channel\"\n className=\"inline-flex items-center rounded-md border border-border-seam bg-bg-surface p-1.5 text-text-secondary hover:bg-bg-sunken/50\"\n >\n <Pencil className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setDeleteTarget(channel)}\n title=\"Delete channel\"\n className=\"inline-flex items-center rounded-md border border-border-seam bg-bg-surface p-1.5 text-status-error-text hover:bg-status-error-bg\"\n >\n <Trash2 className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n </div>\n </GlassCard>\n );\n })}\n </div>\n\n {canLoadMore && channels.length >= PAGE_SIZE && (\n <div className=\"mt-4 flex justify-center\">\n <button\n onClick={handleLoadMore}\n disabled={loadingMore}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50 disabled:opacity-50\"\n >\n {loadingMore ? \"Loading...\" : \"Load more\"}\n </button>\n </div>\n )}\n </>\n )}\n\n <Modal\n open={formOpen}\n onClose={closeForm}\n title={editing ? \"Edit Channel\" : \"Add Channel\"}\n description={\n editing\n ? \"Update this channel's configuration.\"\n : \"Connect a new tracking channel to feed time into TimeCampus.\"\n }\n footer={\n <>\n <button\n onClick={closeForm}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Cancel\n </button>\n <button\n onClick={handleSubmit}\n disabled={creating || updating || !form.name.trim()}\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 {creating || updating\n ? \"Saving...\"\n : editing\n ? \"Save Changes\"\n : \"Add Channel\"}\n </button>\n </>\n }\n >\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Name\n </label>\n <input\n type=\"text\"\n placeholder=\"e.g. Google Calendar\"\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n className={inputClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Type\n </label>\n <select\n value={form.type}\n onChange={(e) =>\n setForm({\n ...form,\n type: e.target.value as TimeCampusChannelType,\n })\n }\n className={inputClass}\n >\n {CHANNEL_TYPE_OPTIONS.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Sync Interval (seconds)\n </label>\n <input\n type=\"number\"\n min={0}\n placeholder=\"e.g. 300\"\n value={form.syncInterval}\n onChange={(e) =>\n setForm({ ...form, syncInterval: e.target.value })\n }\n className={inputClass}\n />\n </div>\n </div>\n </div>\n </Modal>\n\n <Modal\n open={deleteTarget !== null}\n onClose={() => setDeleteTarget(null)}\n title=\"Delete channel?\"\n description={\n deleteTarget\n ? `\"${deleteTarget.name}\" will be removed. This cannot be undone.`\n : undefined\n }\n size=\"sm\"\n footer={\n <>\n <button\n onClick={() => setDeleteTarget(null)}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Cancel\n </button>\n <button\n onClick={handleDelete}\n disabled={deleting}\n className=\"rounded-md bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg/80 disabled:opacity-50\"\n >\n {deleting ? \"Deleting...\" : \"Delete\"}\n </button>\n </>\n }\n >\n <p className=\"text-sm text-text-secondary\">\n Deleting a channel stops it from feeding new time into TimeCampus.\n Previously captured entries are kept.\n </p>\n </Modal>\n\n <ToastContainer toasts={toasts} onDismiss={dismissToast} />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|