@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,191 +0,0 @@
|
|
|
1
|
-
import { useTimeCampusNavigate as e } from "../../providers/TimeCampusProvider.js";
|
|
2
|
-
import { StatCard as t } from "../../components/StatCard.js";
|
|
3
|
-
import { LoadingSkeleton as n } from "../../components/LoadingSkeleton.js";
|
|
4
|
-
import { useListConfigurationsQuery as r, useSetConfigurationMutation as i } from "../../../generated/wspace-operations.js";
|
|
5
|
-
import { PageLayout as a } from "../../components/PageLayout.js";
|
|
6
|
-
import { StatusBadge as o } from "../../components/StatusBadge.js";
|
|
7
|
-
import { EmptyState as s } from "../../components/EmptyState.js";
|
|
8
|
-
import { ChartCard as c } from "../../components/ChartCard.js";
|
|
9
|
-
import { DonutChart as l } from "../../components/charts/DonutChart.js";
|
|
10
|
-
import "../../components/index.js";
|
|
11
|
-
import { TimeCampusConfigScope as u } from "../../../generated/wspace-types.js";
|
|
12
|
-
import { useState as d } from "react";
|
|
13
|
-
import { ArrowRight as f, Briefcase as p, Building2 as m, Code2 as h, LayoutTemplate as g, Megaphone as _, Palette as v, Sparkles as y, Tags as b, User as x } from "lucide-react";
|
|
14
|
-
import { jsx as S, jsxs as C } from "react/jsx-runtime";
|
|
15
|
-
import { GlassCard as w } from "@burdenoff/fe-libs/ui";
|
|
16
|
-
//#region src/timecampus/pages/templates/TemplatesPage.tsx
|
|
17
|
-
function T(e) {
|
|
18
|
-
let t = e.toLowerCase();
|
|
19
|
-
return t.includes("consult") ? p : t.includes("agenc") ? _ : t.includes("freelanc") ? x : t.includes("software") || t.includes("engineer") || t.includes("dev") ? h : t.includes("market") ? _ : t.includes("design") ? v : y;
|
|
20
|
-
}
|
|
21
|
-
function E() {
|
|
22
|
-
let p = e(), { data: h, loading: _, error: v, refetch: y } = r({ variables: { scope: u.Workspace } }), [x, { error: E }] = i(), [D, O] = d(null), k = h?.timeCampusConfigurations ?? [], A = k.filter((e) => e.key.startsWith("template.") && !e.key.startsWith("template.applied.")), j = new Set(k.filter((e) => e.key.startsWith("template.applied.")).map((e) => e.key.replace("template.applied.", ""))), M = A.reduce((e, t) => {
|
|
23
|
-
let n = (t.value ?? {}).category ?? "General";
|
|
24
|
-
return e[n] = (e[n] ?? 0) + 1, e;
|
|
25
|
-
}, {}), N = new Set(A.map((e) => (e.value ?? {}).industry ?? "Any")), P = Object.entries(M).sort((e, t) => t[1] - e[1]).map(([e, t]) => ({
|
|
26
|
-
name: e,
|
|
27
|
-
value: t
|
|
28
|
-
})), F = async (e) => {
|
|
29
|
-
let t = (e.value ?? {}).name ?? e.key.replace("template.", "");
|
|
30
|
-
O(e.id);
|
|
31
|
-
try {
|
|
32
|
-
await x({ variables: { input: {
|
|
33
|
-
key: `template.applied.${e.id}`,
|
|
34
|
-
scope: u.Workspace,
|
|
35
|
-
value: {
|
|
36
|
-
templateId: e.id,
|
|
37
|
-
name: t,
|
|
38
|
-
appliedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
39
|
-
},
|
|
40
|
-
description: `Applied template: ${t}`
|
|
41
|
-
} } }), await y();
|
|
42
|
-
} finally {
|
|
43
|
-
O(null);
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
return _ ? /* @__PURE__ */ S(a, {
|
|
47
|
-
title: "Templates",
|
|
48
|
-
description: "Pre-built workspace templates for different workflows",
|
|
49
|
-
children: /* @__PURE__ */ S(n, {
|
|
50
|
-
variant: "card",
|
|
51
|
-
count: 6
|
|
52
|
-
})
|
|
53
|
-
}) : v ? /* @__PURE__ */ S(a, {
|
|
54
|
-
title: "Templates",
|
|
55
|
-
description: "Pre-built workspace templates for different workflows",
|
|
56
|
-
children: /* @__PURE__ */ S("div", {
|
|
57
|
-
className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
|
|
58
|
-
children: "Failed to load template configurations. Please try again later."
|
|
59
|
-
})
|
|
60
|
-
}) : /* @__PURE__ */ S(a, {
|
|
61
|
-
title: "Templates",
|
|
62
|
-
description: "Pre-built workspace templates for different workflows",
|
|
63
|
-
purpose: "Skip the blank-page setup — apply a ready-made template that pre-loads projects, categories and settings for a common workflow, so a new team or workspace is productive in minutes.",
|
|
64
|
-
children: A.length === 0 ? /* @__PURE__ */ S(s, {
|
|
65
|
-
illustration: "empty-generic",
|
|
66
|
-
title: "No templates configured yet",
|
|
67
|
-
description: "Templates are stored as workspace configurations with the 'template.' key prefix — once added, ready-to-apply workflow setups appear here."
|
|
68
|
-
}) : /* @__PURE__ */ C("div", {
|
|
69
|
-
className: "space-y-6",
|
|
70
|
-
children: [
|
|
71
|
-
E && /* @__PURE__ */ S("div", {
|
|
72
|
-
className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text",
|
|
73
|
-
children: "Couldn't apply that template. Please try again."
|
|
74
|
-
}),
|
|
75
|
-
/* @__PURE__ */ C("div", {
|
|
76
|
-
className: "grid grid-cols-1 gap-4 sm:grid-cols-3",
|
|
77
|
-
children: [
|
|
78
|
-
/* @__PURE__ */ S(t, {
|
|
79
|
-
featured: !0,
|
|
80
|
-
title: "Templates",
|
|
81
|
-
value: A.length,
|
|
82
|
-
icon: /* @__PURE__ */ S(g, { className: "h-5 w-5" }),
|
|
83
|
-
description: "Ready to apply"
|
|
84
|
-
}),
|
|
85
|
-
/* @__PURE__ */ S(t, {
|
|
86
|
-
title: "Categories",
|
|
87
|
-
value: Object.keys(M).length,
|
|
88
|
-
icon: /* @__PURE__ */ S(b, { className: "h-5 w-5" }),
|
|
89
|
-
description: "Workflow types",
|
|
90
|
-
iconBgColor: "bg-status-info-bg text-status-info-text"
|
|
91
|
-
}),
|
|
92
|
-
/* @__PURE__ */ S(t, {
|
|
93
|
-
title: "Industries",
|
|
94
|
-
value: N.size,
|
|
95
|
-
icon: /* @__PURE__ */ S(m, { className: "h-5 w-5" }),
|
|
96
|
-
description: "Covered",
|
|
97
|
-
iconBgColor: "bg-status-success-bg text-status-success-text"
|
|
98
|
-
})
|
|
99
|
-
]
|
|
100
|
-
}),
|
|
101
|
-
/* @__PURE__ */ C("div", {
|
|
102
|
-
className: "grid grid-cols-1 gap-6 lg:grid-cols-3",
|
|
103
|
-
children: [/* @__PURE__ */ S(c, {
|
|
104
|
-
title: "By category",
|
|
105
|
-
description: "How the gallery breaks down",
|
|
106
|
-
className: "lg:col-span-1",
|
|
107
|
-
children: /* @__PURE__ */ S(l, {
|
|
108
|
-
data: P,
|
|
109
|
-
centerLabel: "Templates",
|
|
110
|
-
centerValue: A.length,
|
|
111
|
-
height: 240,
|
|
112
|
-
ariaLabel: "Templates grouped by category",
|
|
113
|
-
emptyLabel: "No templates to summarise yet."
|
|
114
|
-
})
|
|
115
|
-
}), /* @__PURE__ */ S("div", {
|
|
116
|
-
className: "lg:col-span-2",
|
|
117
|
-
children: /* @__PURE__ */ S("div", {
|
|
118
|
-
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
119
|
-
children: A.map((e) => {
|
|
120
|
-
let t = e.value ?? {}, n = t.name ?? e.key.replace("template.", ""), r = t.category ?? "General", i = t.industry ?? "Any", a = t.description ?? "", s = j.has(e.id), c = D === e.id;
|
|
121
|
-
return /* @__PURE__ */ C(w, {
|
|
122
|
-
treatment: "glass",
|
|
123
|
-
role: "button",
|
|
124
|
-
tabIndex: 0,
|
|
125
|
-
onClick: () => p(`/templates/${e.id}`),
|
|
126
|
-
onKeyDown: (t) => {
|
|
127
|
-
(t.key === "Enter" || t.key === " ") && (t.preventDefault(), p(`/templates/${e.id}`));
|
|
128
|
-
},
|
|
129
|
-
className: "flex cursor-pointer flex-col p-5",
|
|
130
|
-
children: [
|
|
131
|
-
/* @__PURE__ */ C("div", {
|
|
132
|
-
className: "flex items-start gap-3",
|
|
133
|
-
children: [/* @__PURE__ */ S("div", {
|
|
134
|
-
className: "flex h-12 w-12 shrink-0 items-center justify-center rounded-xl bg-action-primary-bg/10 text-primary",
|
|
135
|
-
children: /* @__PURE__ */ S(T(r), { className: "h-6 w-6" })
|
|
136
|
-
}), /* @__PURE__ */ C("div", {
|
|
137
|
-
className: "min-w-0 flex-1",
|
|
138
|
-
children: [/* @__PURE__ */ C("div", {
|
|
139
|
-
className: "flex items-center gap-2",
|
|
140
|
-
children: [/* @__PURE__ */ S("p", {
|
|
141
|
-
className: "truncate text-sm font-semibold text-text-primary",
|
|
142
|
-
children: n
|
|
143
|
-
}), s && /* @__PURE__ */ S(o, {
|
|
144
|
-
variant: "success",
|
|
145
|
-
label: "Applied"
|
|
146
|
-
})]
|
|
147
|
-
}), /* @__PURE__ */ C("div", {
|
|
148
|
-
className: "mt-1 flex flex-wrap items-center gap-1.5",
|
|
149
|
-
children: [/* @__PURE__ */ S(o, {
|
|
150
|
-
variant: "default",
|
|
151
|
-
label: r
|
|
152
|
-
}), /* @__PURE__ */ S(o, {
|
|
153
|
-
variant: "muted",
|
|
154
|
-
label: i
|
|
155
|
-
})]
|
|
156
|
-
})]
|
|
157
|
-
})]
|
|
158
|
-
}),
|
|
159
|
-
a && /* @__PURE__ */ S("p", {
|
|
160
|
-
className: "mt-3 line-clamp-2 text-xs text-text-secondary",
|
|
161
|
-
children: a
|
|
162
|
-
}),
|
|
163
|
-
/* @__PURE__ */ C("div", {
|
|
164
|
-
className: "mt-4 flex items-center justify-between gap-2 border-t border-border-seam pt-3",
|
|
165
|
-
children: [/* @__PURE__ */ C("span", {
|
|
166
|
-
className: "inline-flex items-center gap-1 text-xs font-medium text-primary",
|
|
167
|
-
children: ["View template", /* @__PURE__ */ S(f, { className: "h-3 w-3" })]
|
|
168
|
-
}), /* @__PURE__ */ S("button", {
|
|
169
|
-
type: "button",
|
|
170
|
-
onClick: (t) => {
|
|
171
|
-
t.stopPropagation(), F(e);
|
|
172
|
-
},
|
|
173
|
-
disabled: c,
|
|
174
|
-
className: "inline-flex items-center gap-1 rounded-md bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50",
|
|
175
|
-
children: c ? "Applying..." : s ? "Apply again" : "Apply Template"
|
|
176
|
-
})]
|
|
177
|
-
})
|
|
178
|
-
]
|
|
179
|
-
}, e.id);
|
|
180
|
-
})
|
|
181
|
-
})
|
|
182
|
-
})]
|
|
183
|
-
})
|
|
184
|
-
]
|
|
185
|
-
})
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
//#endregion
|
|
189
|
-
export { E as TemplatesPage };
|
|
190
|
-
|
|
191
|
-
//# sourceMappingURL=TemplatesPage.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TemplatesPage.js","names":[],"sources":["../../../../src/timecampus/pages/templates/TemplatesPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport {\n Sparkles,\n ArrowRight,\n Briefcase,\n Megaphone,\n User,\n Code2,\n Palette,\n LayoutTemplate,\n Tags,\n Building2,\n type LucideIcon,\n} from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport {\n PageLayout,\n EmptyState,\n LoadingSkeleton,\n StatCard,\n StatusBadge,\n ChartCard,\n DonutChart,\n} from \"../../components\";\nimport { useTimeCampusNavigate } from \"../../providers/TimeCampusProvider\";\nimport {\n useListConfigurationsQuery,\n useSetConfigurationMutation,\n type ConfigurationFieldsFragment,\n} from \"@/generated/wspace-operations\";\nimport { TimeCampusConfigScope } from \"@/generated/wspace-types\";\n\nfunction categoryIcon(category: string): LucideIcon {\n const c = category.toLowerCase();\n if (c.includes(\"consult\")) return Briefcase;\n if (c.includes(\"agenc\")) return Megaphone;\n if (c.includes(\"freelanc\")) return User;\n if (c.includes(\"software\") || c.includes(\"engineer\") || c.includes(\"dev\"))\n return Code2;\n if (c.includes(\"market\")) return Megaphone;\n if (c.includes(\"design\")) return Palette;\n return Sparkles;\n}\n\nexport function TemplatesPage() {\n const navigate = useTimeCampusNavigate();\n\n const { data, loading, error, refetch } = useListConfigurationsQuery({\n variables: { scope: TimeCampusConfigScope.Workspace },\n });\n\n const [setConfiguration, { error: applyError }] =\n useSetConfigurationMutation();\n const [applyingId, setApplyingId] = useState<string | null>(null);\n\n const configurations: ConfigurationFieldsFragment[] =\n data?.timeCampusConfigurations ?? [];\n\n // Filter configurations that represent templates (key starts with 'template.')\n const templates = configurations.filter(\n (c: ConfigurationFieldsFragment) =>\n c.key.startsWith(\"template.\") && !c.key.startsWith(\"template.applied.\"),\n );\n\n // Templates already applied to this workspace — real, no fabrication.\n const appliedIds = new Set(\n configurations\n .filter((c: ConfigurationFieldsFragment) =>\n c.key.startsWith(\"template.applied.\"),\n )\n .map((c) => c.key.replace(\"template.applied.\", \"\")),\n );\n\n // Real, derived aggregates over the templates the page already loads.\n const categoryCounts = templates.reduce<Record<string, number>>(\n (acc, template) => {\n const val = (template.value as Record<string, unknown>) ?? {};\n const category = (val.category as string) ?? \"General\";\n acc[category] = (acc[category] ?? 0) + 1;\n return acc;\n },\n {},\n );\n const industrySet = new Set(\n templates.map((template) => {\n const val = (template.value as Record<string, unknown>) ?? {};\n return (val.industry as string) ?? \"Any\";\n }),\n );\n const categoryMix = Object.entries(categoryCounts)\n .sort((a, b) => b[1] - a[1])\n .map(([category, count]) => ({ name: category, value: count }));\n\n const handleApply = async (template: ConfigurationFieldsFragment) => {\n const val = (template.value as Record<string, unknown>) ?? {};\n const name = (val.name as string) ?? template.key.replace(\"template.\", \"\");\n setApplyingId(template.id);\n try {\n await setConfiguration({\n variables: {\n input: {\n key: `template.applied.${template.id}`,\n scope: TimeCampusConfigScope.Workspace,\n value: {\n templateId: template.id,\n name,\n appliedAt: new Date().toISOString(),\n },\n description: `Applied template: ${name}`,\n },\n },\n });\n await refetch();\n } finally {\n setApplyingId(null);\n }\n };\n\n if (loading) {\n return (\n <PageLayout\n title=\"Templates\"\n description=\"Pre-built workspace templates for different workflows\"\n >\n <LoadingSkeleton variant=\"card\" count={6} />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"Templates\"\n description=\"Pre-built workspace templates for different workflows\"\n >\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n Failed to load template configurations. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Templates\"\n description=\"Pre-built workspace templates for different workflows\"\n purpose=\"Skip the blank-page setup — apply a ready-made template that pre-loads projects, categories and settings for a common workflow, so a new team or workspace is productive in minutes.\"\n >\n {templates.length === 0 ? (\n <EmptyState\n illustration=\"empty-generic\"\n title=\"No templates configured yet\"\n description=\"Templates are stored as workspace configurations with the 'template.' key prefix — once added, ready-to-apply workflow setups appear here.\"\n />\n ) : (\n <div className=\"space-y-6\">\n {applyError && (\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text\">\n Couldn't apply that template. Please try again.\n </div>\n )}\n\n {/* Gallery summary — derived from the templates below */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <StatCard\n featured\n title=\"Templates\"\n value={templates.length}\n icon={<LayoutTemplate className=\"h-5 w-5\" />}\n description=\"Ready to apply\"\n />\n <StatCard\n title=\"Categories\"\n value={Object.keys(categoryCounts).length}\n icon={<Tags className=\"h-5 w-5\" />}\n description=\"Workflow types\"\n iconBgColor=\"bg-status-info-bg text-status-info-text\"\n />\n <StatCard\n title=\"Industries\"\n value={industrySet.size}\n icon={<Building2 className=\"h-5 w-5\" />}\n description=\"Covered\"\n iconBgColor=\"bg-status-success-bg text-status-success-text\"\n />\n </div>\n\n {/* Category mix ring + template cards */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <ChartCard\n title=\"By category\"\n description=\"How the gallery breaks down\"\n className=\"lg:col-span-1\"\n >\n <DonutChart\n data={categoryMix}\n centerLabel=\"Templates\"\n centerValue={templates.length}\n height={240}\n ariaLabel=\"Templates grouped by category\"\n emptyLabel=\"No templates to summarise yet.\"\n />\n </ChartCard>\n\n <div className=\"lg:col-span-2\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n {templates.map((template: ConfigurationFieldsFragment) => {\n const val = (template.value as Record<string, unknown>) ?? {};\n const name =\n (val.name as string) ??\n template.key.replace(\"template.\", \"\");\n const category = (val.category as string) ?? \"General\";\n const industry = (val.industry as string) ?? \"Any\";\n const description = (val.description as string) ?? \"\";\n const isApplied = appliedIds.has(template.id);\n const isApplying = applyingId === template.id;\n const Icon = categoryIcon(category);\n\n return (\n <GlassCard\n key={template.id}\n treatment=\"glass\"\n role=\"button\"\n tabIndex={0}\n onClick={() => navigate(`/templates/${template.id}`)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n navigate(`/templates/${template.id}`);\n }\n }}\n className=\"flex cursor-pointer flex-col p-5\"\n >\n <div className=\"flex items-start gap-3\">\n <div className=\"flex h-12 w-12 shrink-0 items-center justify-center rounded-xl bg-action-primary-bg/10 text-primary\">\n <Icon className=\"h-6 w-6\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <p className=\"truncate text-sm font-semibold text-text-primary\">\n {name}\n </p>\n {isApplied && (\n <StatusBadge variant=\"success\" label=\"Applied\" />\n )}\n </div>\n <div className=\"mt-1 flex flex-wrap items-center gap-1.5\">\n <StatusBadge variant=\"default\" label={category} />\n <StatusBadge variant=\"muted\" label={industry} />\n </div>\n </div>\n </div>\n\n {description && (\n <p className=\"mt-3 line-clamp-2 text-xs text-text-secondary\">\n {description}\n </p>\n )}\n\n <div className=\"mt-4 flex items-center justify-between gap-2 border-t border-border-seam pt-3\">\n <span className=\"inline-flex items-center gap-1 text-xs font-medium text-primary\">\n View template\n <ArrowRight className=\"h-3 w-3\" />\n </span>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n void handleApply(template);\n }}\n disabled={isApplying}\n className=\"inline-flex items-center gap-1 rounded-md bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {isApplying\n ? \"Applying...\"\n : isApplied\n ? \"Apply again\"\n : \"Apply Template\"}\n </button>\n </div>\n </GlassCard>\n );\n })}\n </div>\n </div>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import { useTimeCampusNavigate as e } from "../../providers/TimeCampusProvider.js";
|
|
2
|
-
import { LoadingSkeleton as t } from "../../components/LoadingSkeleton.js";
|
|
3
|
-
import { useDeleteConfigurationMutation as n, useGetConfigurationQuery as r, useUpdateConfigurationMutation as i } from "../../../generated/wspace-operations.js";
|
|
4
|
-
import { PageLayout as a } from "../../components/PageLayout.js";
|
|
5
|
-
import { useEffect as o, useState as s } from "react";
|
|
6
|
-
import { ArrowLeft as c, LayoutGrid as l, Save as u, Trash2 as d } from "lucide-react";
|
|
7
|
-
import { useParams as f } from "react-router-dom";
|
|
8
|
-
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
9
|
-
import { GlassCard as h, NextSteps as g } from "@burdenoff/fe-libs/ui";
|
|
10
|
-
//#region src/timecampus/pages/widgets/WidgetDetailPage.tsx
|
|
11
|
-
var _ = [
|
|
12
|
-
{
|
|
13
|
-
value: "timer",
|
|
14
|
-
label: "Active timer"
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
value: "project-progress",
|
|
18
|
-
label: "Project progress"
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
value: "focus-score",
|
|
22
|
-
label: "Focus score"
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
value: "team-stats",
|
|
26
|
-
label: "Team stats"
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
value: "billable-hours",
|
|
30
|
-
label: "Billable hours"
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
value: "recent-entries",
|
|
34
|
-
label: "Recent time entries"
|
|
35
|
-
}
|
|
36
|
-
];
|
|
37
|
-
function v() {
|
|
38
|
-
let { id: v } = f(), y = e(), { data: b, loading: x, error: S } = r({
|
|
39
|
-
variables: { id: v ?? "" },
|
|
40
|
-
skip: !v
|
|
41
|
-
}), [C, { loading: w, error: T }] = i(), [E, { loading: D }] = n(), O = b?.configuration, [k, A] = s(""), [j, M] = s(_[0].value), [N, P] = s("");
|
|
42
|
-
o(() => {
|
|
43
|
-
if (O) {
|
|
44
|
-
let e = O.value ?? {};
|
|
45
|
-
A(e.name ?? O.key.replace("widget.", "")), M(e.type ?? _[0].value), P(e.description ?? O.description ?? "");
|
|
46
|
-
}
|
|
47
|
-
}, [O]);
|
|
48
|
-
let F = async () => {
|
|
49
|
-
if (!O || !k.trim()) return;
|
|
50
|
-
let e = O.value ?? {}, t = N.trim();
|
|
51
|
-
await C({ variables: {
|
|
52
|
-
id: O.id,
|
|
53
|
-
input: {
|
|
54
|
-
value: {
|
|
55
|
-
...e,
|
|
56
|
-
name: k.trim(),
|
|
57
|
-
type: j,
|
|
58
|
-
description: t || void 0
|
|
59
|
-
},
|
|
60
|
-
description: t || void 0
|
|
61
|
-
}
|
|
62
|
-
} }), y("/widgets");
|
|
63
|
-
};
|
|
64
|
-
return x ? /* @__PURE__ */ p(a, {
|
|
65
|
-
title: "Widget Details",
|
|
66
|
-
description: "Loading...",
|
|
67
|
-
children: /* @__PURE__ */ p(t, { variant: "detail" })
|
|
68
|
-
}) : S || !O ? /* @__PURE__ */ p(a, {
|
|
69
|
-
title: "Widget Details",
|
|
70
|
-
description: `Widget ID: ${v}`,
|
|
71
|
-
actions: /* @__PURE__ */ m("button", {
|
|
72
|
-
type: "button",
|
|
73
|
-
onClick: () => y("/widgets"),
|
|
74
|
-
className: "inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
|
|
75
|
-
children: [/* @__PURE__ */ p(c, { className: "h-4 w-4" }), "Back to Widgets"]
|
|
76
|
-
}),
|
|
77
|
-
children: /* @__PURE__ */ p("div", {
|
|
78
|
-
className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
|
|
79
|
-
children: S ? "Failed to load widget details. Please try again later." : "Widget configuration not found."
|
|
80
|
-
})
|
|
81
|
-
}) : /* @__PURE__ */ p(a, {
|
|
82
|
-
title: "Widget Details",
|
|
83
|
-
description: `Widget ID: ${v}`,
|
|
84
|
-
purpose: "Edit what this widget shows and how it's labelled, then save it back to your workspace — or remove it if you no longer need it on your dashboard.",
|
|
85
|
-
actions: /* @__PURE__ */ m("button", {
|
|
86
|
-
type: "button",
|
|
87
|
-
onClick: () => y("/widgets"),
|
|
88
|
-
className: "inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
|
|
89
|
-
children: [/* @__PURE__ */ p(c, { className: "h-4 w-4" }), "Back to Widgets"]
|
|
90
|
-
}),
|
|
91
|
-
children: /* @__PURE__ */ m("div", {
|
|
92
|
-
className: "mx-auto max-w-2xl",
|
|
93
|
-
children: [/* @__PURE__ */ p(g, {
|
|
94
|
-
storageKey: "timecampus-widget-detail",
|
|
95
|
-
className: "mb-6",
|
|
96
|
-
steps: [{
|
|
97
|
-
id: "save",
|
|
98
|
-
label: "Save your changes",
|
|
99
|
-
description: "Update this widget's name, metric or description.",
|
|
100
|
-
onClick: () => void F()
|
|
101
|
-
}, {
|
|
102
|
-
id: "browse",
|
|
103
|
-
label: "Browse other widgets",
|
|
104
|
-
description: "Manage the rest of your dashboard widgets.",
|
|
105
|
-
onClick: () => y("/widgets")
|
|
106
|
-
}]
|
|
107
|
-
}), /* @__PURE__ */ m(h, {
|
|
108
|
-
treatment: "glass",
|
|
109
|
-
glow: !0,
|
|
110
|
-
className: "p-6",
|
|
111
|
-
children: [
|
|
112
|
-
/* @__PURE__ */ m("div", {
|
|
113
|
-
className: "flex items-start gap-4",
|
|
114
|
-
children: [/* @__PURE__ */ p("div", {
|
|
115
|
-
className: "flex h-16 w-16 items-center justify-center rounded-xl bg-action-primary-bg/10",
|
|
116
|
-
children: /* @__PURE__ */ p(l, { className: "h-8 w-8 text-primary" })
|
|
117
|
-
}), /* @__PURE__ */ m("div", {
|
|
118
|
-
className: "flex-1",
|
|
119
|
-
children: [/* @__PURE__ */ p("h2", {
|
|
120
|
-
className: "text-lg font-semibold text-text-primary",
|
|
121
|
-
children: k || "Untitled widget"
|
|
122
|
-
}), /* @__PURE__ */ p("p", {
|
|
123
|
-
className: "text-sm text-text-secondary",
|
|
124
|
-
children: "This widget is saved to your workspace and can be shown on your dashboard."
|
|
125
|
-
})]
|
|
126
|
-
})]
|
|
127
|
-
}),
|
|
128
|
-
T && /* @__PURE__ */ p("div", {
|
|
129
|
-
className: "mt-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text",
|
|
130
|
-
children: "Failed to save widget. Please try again."
|
|
131
|
-
}),
|
|
132
|
-
/* @__PURE__ */ m("div", {
|
|
133
|
-
className: "mt-6 space-y-4",
|
|
134
|
-
children: [
|
|
135
|
-
/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
|
|
136
|
-
className: "block text-sm font-medium text-text-primary",
|
|
137
|
-
children: "Widget Name"
|
|
138
|
-
}), /* @__PURE__ */ p("input", {
|
|
139
|
-
type: "text",
|
|
140
|
-
value: k,
|
|
141
|
-
onChange: (e) => A(e.target.value),
|
|
142
|
-
className: "mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary"
|
|
143
|
-
})] }),
|
|
144
|
-
/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
|
|
145
|
-
className: "block text-sm font-medium text-text-primary",
|
|
146
|
-
children: "Metric"
|
|
147
|
-
}), /* @__PURE__ */ p("select", {
|
|
148
|
-
value: j,
|
|
149
|
-
onChange: (e) => M(e.target.value),
|
|
150
|
-
className: "mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary",
|
|
151
|
-
children: _.map((e) => /* @__PURE__ */ p("option", {
|
|
152
|
-
value: e.value,
|
|
153
|
-
children: e.label
|
|
154
|
-
}, e.value))
|
|
155
|
-
})] }),
|
|
156
|
-
/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ m("label", {
|
|
157
|
-
className: "block text-sm font-medium text-text-primary",
|
|
158
|
-
children: ["Description", /* @__PURE__ */ p("span", {
|
|
159
|
-
className: "ml-1 font-normal text-text-secondary",
|
|
160
|
-
children: "(optional)"
|
|
161
|
-
})]
|
|
162
|
-
}), /* @__PURE__ */ p("textarea", {
|
|
163
|
-
rows: 3,
|
|
164
|
-
value: N,
|
|
165
|
-
onChange: (e) => P(e.target.value),
|
|
166
|
-
className: "mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary"
|
|
167
|
-
})] })
|
|
168
|
-
]
|
|
169
|
-
}),
|
|
170
|
-
/* @__PURE__ */ m("div", {
|
|
171
|
-
className: "mt-6 flex items-center justify-between",
|
|
172
|
-
children: [/* @__PURE__ */ m("button", {
|
|
173
|
-
type: "button",
|
|
174
|
-
onClick: async () => {
|
|
175
|
-
O && (await E({ variables: { id: O.id } }), y("/widgets"));
|
|
176
|
-
},
|
|
177
|
-
disabled: D,
|
|
178
|
-
className: "inline-flex items-center gap-1.5 rounded-md border border-status-error-bg/40 px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg disabled:opacity-50",
|
|
179
|
-
children: [/* @__PURE__ */ p(d, { className: "h-4 w-4" }), D ? "Deleting..." : "Delete"]
|
|
180
|
-
}), /* @__PURE__ */ m("button", {
|
|
181
|
-
type: "button",
|
|
182
|
-
onClick: F,
|
|
183
|
-
disabled: w || !k.trim(),
|
|
184
|
-
className: "inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50",
|
|
185
|
-
children: [/* @__PURE__ */ p(u, { className: "h-4 w-4" }), w ? "Saving..." : "Save Changes"]
|
|
186
|
-
})]
|
|
187
|
-
})
|
|
188
|
-
]
|
|
189
|
-
})]
|
|
190
|
-
})
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
//#endregion
|
|
194
|
-
export { v as WidgetDetailPage };
|
|
195
|
-
|
|
196
|
-
//# sourceMappingURL=WidgetDetailPage.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"WidgetDetailPage.js","names":[],"sources":["../../../../src/timecampus/pages/widgets/WidgetDetailPage.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { useParams } from \"react-router-dom\";\nimport { LayoutGrid, ArrowLeft, Save, Trash2 } from \"lucide-react\";\nimport { GlassCard, NextSteps } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { useTimeCampusNavigate } from \"../../providers/TimeCampusProvider\";\nimport {\n useGetConfigurationQuery,\n useUpdateConfigurationMutation,\n useDeleteConfigurationMutation,\n} from \"@/generated/wspace-operations\";\n\nconst WIDGET_TYPES = [\n { value: \"timer\", label: \"Active timer\" },\n { value: \"project-progress\", label: \"Project progress\" },\n { value: \"focus-score\", label: \"Focus score\" },\n { value: \"team-stats\", label: \"Team stats\" },\n { value: \"billable-hours\", label: \"Billable hours\" },\n { value: \"recent-entries\", label: \"Recent time entries\" },\n];\n\nexport function WidgetDetailPage() {\n const { id } = useParams<{ id: string }>();\n const navigate = useTimeCampusNavigate();\n\n const { data, loading, error } = useGetConfigurationQuery({\n variables: { id: id ?? \"\" },\n skip: !id,\n });\n\n const [updateConfiguration, { loading: saving, error: saveError }] =\n useUpdateConfigurationMutation();\n const [deleteConfiguration, { loading: deleting }] =\n useDeleteConfigurationMutation();\n\n const config = data?.configuration;\n\n const [name, setName] = useState(\"\");\n const [type, setType] = useState(WIDGET_TYPES[0].value);\n const [description, setDescription] = useState(\"\");\n\n useEffect(() => {\n if (config) {\n const val = (config.value as Record<string, unknown>) ?? {};\n setName((val.name as string) ?? config.key.replace(\"widget.\", \"\"));\n setType((val.type as string) ?? WIDGET_TYPES[0].value);\n setDescription((val.description as string) ?? config.description ?? \"\");\n }\n }, [config]);\n\n const handleSave = async () => {\n if (!config || !name.trim()) return;\n const val = (config.value as Record<string, unknown>) ?? {};\n const trimmedDescription = description.trim();\n await updateConfiguration({\n variables: {\n id: config.id,\n input: {\n value: {\n ...val,\n name: name.trim(),\n type,\n description: trimmedDescription || undefined,\n },\n description: trimmedDescription || undefined,\n },\n },\n });\n navigate(\"/widgets\");\n };\n\n const handleDelete = async () => {\n if (!config) return;\n await deleteConfiguration({ variables: { id: config.id } });\n navigate(\"/widgets\");\n };\n\n if (loading) {\n return (\n <PageLayout title=\"Widget Details\" description=\"Loading...\">\n <LoadingSkeleton variant=\"detail\" />\n </PageLayout>\n );\n }\n\n if (error || !config) {\n return (\n <PageLayout\n title=\"Widget Details\"\n description={`Widget ID: ${id}`}\n actions={\n <button\n type=\"button\"\n onClick={() => navigate(\"/widgets\")}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Widgets\n </button>\n }\n >\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n {error\n ? \"Failed to load widget details. Please try again later.\"\n : \"Widget configuration not found.\"}\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Widget Details\"\n description={`Widget ID: ${id}`}\n purpose=\"Edit what this widget shows and how it's labelled, then save it back to your workspace — or remove it if you no longer need it on your dashboard.\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigate(\"/widgets\")}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Widgets\n </button>\n }\n >\n <div className=\"mx-auto max-w-2xl\">\n <NextSteps\n storageKey=\"timecampus-widget-detail\"\n className=\"mb-6\"\n steps={[\n {\n id: \"save\",\n label: \"Save your changes\",\n description: \"Update this widget's name, metric or description.\",\n onClick: () => void handleSave(),\n },\n {\n id: \"browse\",\n label: \"Browse other widgets\",\n description: \"Manage the rest of your dashboard widgets.\",\n onClick: () => navigate(\"/widgets\"),\n },\n ]}\n />\n <GlassCard treatment=\"glass\" glow className=\"p-6\">\n <div className=\"flex items-start gap-4\">\n <div className=\"flex h-16 w-16 items-center justify-center rounded-xl bg-action-primary-bg/10\">\n <LayoutGrid className=\"h-8 w-8 text-primary\" />\n </div>\n <div className=\"flex-1\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {name || \"Untitled widget\"}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n This widget is saved to your workspace and can be shown on your\n dashboard.\n </p>\n </div>\n </div>\n\n {saveError && (\n <div className=\"mt-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text\">\n Failed to save widget. Please try again.\n </div>\n )}\n\n <div className=\"mt-6 space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Widget Name\n </label>\n <input\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n className=\"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Metric\n </label>\n <select\n value={type}\n onChange={(e) => setType(e.target.value)}\n className=\"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n {WIDGET_TYPES.map((t) => (\n <option key={t.value} value={t.value}>\n {t.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Description\n <span className=\"ml-1 font-normal text-text-secondary\">\n (optional)\n </span>\n </label>\n <textarea\n rows={3}\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n className=\"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary\"\n />\n </div>\n </div>\n\n <div className=\"mt-6 flex items-center justify-between\">\n <button\n type=\"button\"\n onClick={handleDelete}\n disabled={deleting}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-status-error-bg/40 px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg disabled:opacity-50\"\n >\n <Trash2 className=\"h-4 w-4\" />\n {deleting ? \"Deleting...\" : \"Delete\"}\n </button>\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={saving || !name.trim()}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n <Save className=\"h-4 w-4\" />\n {saving ? \"Saving...\" : \"Save Changes\"}\n </button>\n </div>\n </GlassCard>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|