@burdenoff/microfe-timecampus 2026.626.2 → 2026.703.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generated/wspace-operations.js +996 -816
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/generated/wspace-types.js +28 -0
- package/dist/generated/wspace-types.js.map +1 -0
- package/dist/index.js +4 -4
- package/dist/timecampus/TimeCampusRoutes.js +16 -12
- package/dist/timecampus/TimeCampusRoutes.js.map +1 -1
- package/dist/timecampus/components/Modal.js +65 -0
- package/dist/timecampus/components/Modal.js.map +1 -0
- package/dist/timecampus/components/StatusBadge.js +28 -0
- package/dist/timecampus/components/StatusBadge.js.map +1 -0
- package/dist/timecampus/components/Toast.js +87 -0
- package/dist/timecampus/components/Toast.js.map +1 -0
- package/dist/timecampus/index.js +1 -1
- package/dist/timecampus/pages/HomePage.js +143 -135
- package/dist/timecampus/pages/HomePage.js.map +1 -1
- package/dist/timecampus/pages/applets/AppletStorePage.js +14 -19
- package/dist/timecampus/pages/applets/AppletStorePage.js.map +1 -1
- package/dist/timecampus/pages/applets/InstalledAppletsPage.js +49 -37
- package/dist/timecampus/pages/applets/InstalledAppletsPage.js.map +1 -1
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +315 -87
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
- package/dist/timecampus/pages/channels/ChannelsPage.js +340 -46
- package/dist/timecampus/pages/channels/ChannelsPage.js.map +1 -1
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js +374 -92
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
- package/dist/timecampus/pages/dashboard/ReportsPage.js +70 -37
- package/dist/timecampus/pages/dashboard/ReportsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +366 -60
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +461 -87
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/geofencing/GeofencingPage.js +314 -55
- package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
- package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js +30 -27
- package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js.map +1 -1
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +269 -74
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/ActionItemFormModal.js +127 -0
- package/dist/timecampus/pages/meetings/ActionItemFormModal.js.map +1 -0
- package/dist/timecampus/pages/meetings/MeetingCreatePage.js +62 -52
- package/dist/timecampus/pages/meetings/MeetingCreatePage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js +361 -74
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingFormModal.js +156 -0
- package/dist/timecampus/pages/meetings/MeetingFormModal.js.map +1 -0
- package/dist/timecampus/pages/meetings/MeetingsPage.js +175 -74
- package/dist/timecampus/pages/meetings/MeetingsPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/ParticipantFormModal.js +105 -0
- package/dist/timecampus/pages/meetings/ParticipantFormModal.js.map +1 -0
- package/dist/timecampus/pages/pomodoro/PomodoroPage.js +307 -67
- package/dist/timecampus/pages/pomodoro/PomodoroPage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectCreatePage.js +51 -48
- package/dist/timecampus/pages/projects/ProjectCreatePage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectDetailPage.js +248 -51
- package/dist/timecampus/pages/projects/ProjectDetailPage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectsPage.js +354 -87
- package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
- package/dist/timecampus/pages/projects/TasksPage.js +367 -65
- package/dist/timecampus/pages/projects/TasksPage.js.map +1 -1
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +674 -104
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamAnalyticsPage.js +46 -34
- package/dist/timecampus/pages/team/TeamAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamMembersPage.js +93 -69
- package/dist/timecampus/pages/team/TeamMembersPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamPage.js +4 -3
- package/dist/timecampus/pages/team/TeamPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplateDetailPage.js +72 -53
- package/dist/timecampus/pages/templates/TemplateDetailPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplatesPage.js +40 -35
- package/dist/timecampus/pages/templates/TemplatesPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeEntriesPage.js +484 -97
- package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeTrackingPage.js +210 -99
- package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimerPage.js +229 -97
- package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +315 -110
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
- package/dist/timecampus/pages/timeline/TimelinePage.js +250 -0
- package/dist/timecampus/pages/timeline/TimelinePage.js.map +1 -0
- package/dist/timecampus/pages/widgets/WidgetDetailPage.js +158 -76
- package/dist/timecampus/pages/widgets/WidgetDetailPage.js.map +1 -1
- package/dist/timecampus/pages/widgets/WidgetsPage.js +181 -52
- package/dist/timecampus/pages/widgets/WidgetsPage.js.map +1 -1
- package/dist/timecampus/pages/workflows/WorkflowsPage.js +212 -61
- package/dist/timecampus/pages/workflows/WorkflowsPage.js.map +1 -1
- package/dist/timecampus/store/timecampusStore.js +13 -1
- package/dist/timecampus/store/timecampusStore.js.map +1 -1
- package/dist/timecampus/utils/helpers.js +20 -10
- package/dist/timecampus/utils/helpers.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamPage.js","names":[],"sources":["../../../../src/timecampus/pages/team/TeamPage.tsx"],"sourcesContent":["import { Users, Clock, TrendingUp } from \"lucide-react\";\nimport { NextSteps } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport {\n useTimeCampus,\n useTimeCampusNavigate,\n} from \"../../providers/TimeCampusProvider\";\nimport { useGetTimeEntryStatsQuery } from \"@/generated/wspace-operations\";\n\nexport function TeamPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n const navigate = useTimeCampusNavigate();\n\n const { data, loading, error } = useGetTimeEntryStatsQuery({\n skip: !workspaceId,\n });\n\n const stats = data?.timeEntryStats;\n\n if (loading) {\n return (\n <PageLayout\n title=\"Team Overview\"\n description=\"Monitor team performance and activity\"\n >\n <LoadingSkeleton variant=\"card\" count={4} />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"Team Overview\"\n description=\"Monitor team performance and activity\"\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 team data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n const totalH = Math.floor(stats?.totalHours ?? 0);\n const totalM = Math.round(((stats?.totalHours ?? 0) - totalH) * 60);\n const billablePercent = stats?.totalHours\n ? Math.round(((stats.billableHours ?? 0) / stats.totalHours) * 100)\n : 0;\n\n return (\n <PageLayout\n title=\"Team Overview\"\n description=\"Monitor team performance and activity\"\n purpose=\"See how your whole team is spending its time at a glance — who's active, how many hours are billable, and where capacity is going — so you can balance workload and catch problems early.\"\n >\n <NextSteps\n storageKey=\"timecampus-team\"\n className=\"mb-6\"\n steps={[\n {\n id: \"members\",\n label: \"Manage team members\",\n description: \"Add people and check individual workloads.\",\n onClick: () => navigate(\"/team/members\"),\n },\n {\n id: \"analytics\",\n label: \"Open team analytics\",\n description: \"Compare utilisation and trends across the team.\",\n onClick: () => navigate(\"/team/analytics\"),\n },\n {\n id: \"activity\",\n label: \"Watch live team activity\",\n description: \"See what people are working on right now.\",\n onClick: () => navigate(\"/team-activity\"),\n },\n ]}\n />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n featured\n title=\"Total Entries\"\n value={String(stats?.totalEntries ?? 0)}\n icon={<Users className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Active Timers\"\n value={String(stats?.activeTimers ?? 0)}\n icon={<Users className=\"h-5 w-5\" />}\n iconBgColor=\"bg-status-success-bg text-status-success-text\"\n />\n <StatCard\n title=\"Total Hours\"\n value={`${totalH}h ${totalM}m`}\n icon={<Clock className=\"h-5 w-5\" />}\n trend={{ value: stats?.billableHours ?? 0, label: \"billable hours\" }}\n />\n <StatCard\n title=\"Billable Rate\"\n value={`${billablePercent}%`}\n icon={<TrendingUp className=\"h-5 w-5\" />}\n trend={{ value: billablePercent, label: \"of total hours\" }}\n />\n </div>\n\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6\">\n <h3 className=\"mb-4 text-sm font-semibold text-text-primary\">\n Team Activity Feed\n </h3>\n <
|
|
1
|
+
{"version":3,"file":"TeamPage.js","names":[],"sources":["../../../../src/timecampus/pages/team/TeamPage.tsx"],"sourcesContent":["import { Users, Clock, TrendingUp } from \"lucide-react\";\nimport { NextSteps } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport {\n useTimeCampus,\n useTimeCampusNavigate,\n} from \"../../providers/TimeCampusProvider\";\nimport { useGetTimeEntryStatsQuery } from \"@/generated/wspace-operations\";\n\nexport function TeamPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n const navigate = useTimeCampusNavigate();\n\n const { data, loading, error } = useGetTimeEntryStatsQuery({\n skip: !workspaceId,\n });\n\n const stats = data?.timeEntryStats;\n\n if (loading) {\n return (\n <PageLayout\n title=\"Team Overview\"\n description=\"Monitor team performance and activity\"\n >\n <LoadingSkeleton variant=\"card\" count={4} />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"Team Overview\"\n description=\"Monitor team performance and activity\"\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 team data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n const totalH = Math.floor(stats?.totalHours ?? 0);\n const totalM = Math.round(((stats?.totalHours ?? 0) - totalH) * 60);\n const billablePercent = stats?.totalHours\n ? Math.round(((stats.billableHours ?? 0) / stats.totalHours) * 100)\n : 0;\n\n return (\n <PageLayout\n title=\"Team Overview\"\n description=\"Monitor team performance and activity\"\n purpose=\"See how your whole team is spending its time at a glance — who's active, how many hours are billable, and where capacity is going — so you can balance workload and catch problems early.\"\n >\n <NextSteps\n storageKey=\"timecampus-team\"\n className=\"mb-6\"\n steps={[\n {\n id: \"members\",\n label: \"Manage team members\",\n description: \"Add people and check individual workloads.\",\n onClick: () => navigate(\"/team/members\"),\n },\n {\n id: \"analytics\",\n label: \"Open team analytics\",\n description: \"Compare utilisation and trends across the team.\",\n onClick: () => navigate(\"/team/analytics\"),\n },\n {\n id: \"activity\",\n label: \"Watch live team activity\",\n description: \"See what people are working on right now.\",\n onClick: () => navigate(\"/team-activity\"),\n },\n ]}\n />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n featured\n title=\"Total Entries\"\n value={String(stats?.totalEntries ?? 0)}\n icon={<Users className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Active Timers\"\n value={String(stats?.activeTimers ?? 0)}\n icon={<Users className=\"h-5 w-5\" />}\n iconBgColor=\"bg-status-success-bg text-status-success-text\"\n />\n <StatCard\n title=\"Total Hours\"\n value={`${totalH}h ${totalM}m`}\n icon={<Clock className=\"h-5 w-5\" />}\n trend={{ value: stats?.billableHours ?? 0, label: \"billable hours\" }}\n />\n <StatCard\n title=\"Billable Rate\"\n value={`${billablePercent}%`}\n icon={<TrendingUp className=\"h-5 w-5\" />}\n trend={{ value: billablePercent, label: \"of total hours\" }}\n />\n </div>\n\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6\">\n <h3 className=\"mb-4 text-sm font-semibold text-text-primary\">\n Team Activity Feed\n </h3>\n <button\n onClick={() => navigate(\"/team-activity\")}\n className=\"flex h-48 w-full items-center justify-center rounded-lg border border-dashed border-border-subtle transition-colors hover:bg-bg-sunken/50\"\n >\n <p className=\"text-sm text-text-secondary\">\n Open the live team activity timeline\n </p>\n </button>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;AAWA,SAAgB,IAAW;CACzB,IAAM,EAAE,iBAAc,OAAO,GAAe,EACtC,IAAW,GAAuB,EAElC,EAAE,SAAM,YAAS,aAAU,EAA0B,EACzD,MAAM,CAAC,GACR,CAAC,EAEI,IAAQ,GAAM;AAEpB,KAAI,EACF,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA;EACjC,CAAA;AAIjB,KAAI,EACF,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,OAAD;GAAK,WAAU;aAAoG;GAE7G,CAAA;EACK,CAAA;CAIjB,IAAM,IAAS,KAAK,MAAM,GAAO,cAAc,EAAE,EAC3C,IAAS,KAAK,QAAQ,GAAO,cAAc,KAAK,KAAU,GAAG,EAC7D,IAAkB,GAAO,aAC3B,KAAK,OAAQ,EAAM,iBAAiB,KAAK,EAAM,aAAc,IAAI,GACjE;AAEJ,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SAAQ;YAHV;GAKE,kBAAC,GAAD;IACE,YAAW;IACX,WAAU;IACV,OAAO;KACL;MACE,IAAI;MACJ,OAAO;MACP,aAAa;MACb,eAAe,EAAS,gBAAgB;MACzC;KACD;MACE,IAAI;MACJ,OAAO;MACP,aAAa;MACb,eAAe,EAAS,kBAAkB;MAC3C;KACD;MACE,IAAI;MACJ,OAAO;MACP,aAAa;MACb,eAAe,EAAS,iBAAiB;MAC1C;KACF;IACD,CAAA;GACF,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MACE,UAAA;MACA,OAAM;MACN,OAAO,OAAO,GAAO,gBAAgB,EAAE;MACvC,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;MACnC,CAAA;KACF,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,OAAO,GAAO,gBAAgB,EAAE;MACvC,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;MACnC,aAAY;MACZ,CAAA;KACF,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,GAAG,EAAO,IAAI,EAAO;MAC5B,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;MACnC,OAAO;OAAE,OAAO,GAAO,iBAAiB;OAAG,OAAO;OAAkB;MACpE,CAAA;KACF,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,GAAG,EAAgB;MAC1B,MAAM,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;MACxC,OAAO;OAAE,OAAO;OAAiB,OAAO;OAAkB;MAC1D,CAAA;KACE;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,MAAD;KAAI,WAAU;eAA+C;KAExD,CAAA,EACL,kBAAC,UAAD;KACE,eAAe,EAAS,iBAAiB;KACzC,WAAU;eAEV,kBAAC,KAAD;MAAG,WAAU;gBAA8B;MAEvC,CAAA;KACG,CAAA,CACL;;GACK"}
|
|
@@ -1,108 +1,127 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { LoadingSkeleton as
|
|
3
|
-
import { useGetConfigurationQuery as r } from "../../../generated/wspace-operations.js";
|
|
1
|
+
import { useTimeCampusNavigate as e } from "../../providers/TimeCampusProvider.js";
|
|
2
|
+
import { LoadingSkeleton as t } from "../../components/LoadingSkeleton.js";
|
|
3
|
+
import { useGetConfigurationQuery as n, useSetConfigurationMutation as r } from "../../../generated/wspace-operations.js";
|
|
4
4
|
import { PageLayout as i } from "../../components/PageLayout.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { TimeCampusConfigScope as a } from "../../../generated/wspace-types.js";
|
|
6
|
+
import { ArrowLeft as o, CheckCircle as s, Sparkles as c } from "lucide-react";
|
|
7
|
+
import { useParams as l } from "react-router-dom";
|
|
8
|
+
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
9
|
+
import { GlassCard as f, NextSteps as p } from "@burdenoff/fe-libs/ui";
|
|
9
10
|
//#region src/timecampus/pages/templates/TemplateDetailPage.tsx
|
|
10
|
-
function
|
|
11
|
-
let { id:
|
|
12
|
-
variables: { id:
|
|
13
|
-
skip: !
|
|
14
|
-
}), x =
|
|
15
|
-
|
|
11
|
+
function m() {
|
|
12
|
+
let { id: m } = l(), h = e(), { data: g, loading: _, error: v } = n({
|
|
13
|
+
variables: { id: m ?? "" },
|
|
14
|
+
skip: !m
|
|
15
|
+
}), [y, { loading: b }] = r(), x = g?.configuration, S = x?.value ?? {}, C = S.name ?? x?.key?.replace("template.", "") ?? "Template", w = x?.description ?? S.description ?? "A pre-built workspace template for time tracking workflows.", T = S.category ?? "General", E = S.industry ?? "Any", D = S.features ?? [], O = async () => {
|
|
16
|
+
m && (await y({ variables: { input: {
|
|
17
|
+
key: `template.applied.${m}`,
|
|
18
|
+
scope: a.Workspace,
|
|
19
|
+
value: {
|
|
20
|
+
templateId: m,
|
|
21
|
+
name: C,
|
|
22
|
+
appliedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
23
|
+
},
|
|
24
|
+
description: `Applied template: ${C}`
|
|
25
|
+
} } }), h("/templates"));
|
|
26
|
+
};
|
|
27
|
+
return _ ? /* @__PURE__ */ u(i, {
|
|
16
28
|
title: "Template Details",
|
|
17
29
|
description: "Loading...",
|
|
18
|
-
children: /* @__PURE__ */
|
|
19
|
-
}) :
|
|
30
|
+
children: /* @__PURE__ */ u(t, { variant: "detail" })
|
|
31
|
+
}) : v || !x ? /* @__PURE__ */ u(i, {
|
|
20
32
|
title: "Template Details",
|
|
21
|
-
description: `Template ID: ${
|
|
22
|
-
actions: /* @__PURE__ */
|
|
33
|
+
description: `Template ID: ${m}`,
|
|
34
|
+
actions: /* @__PURE__ */ d("button", {
|
|
35
|
+
type: "button",
|
|
36
|
+
onClick: () => h("/templates"),
|
|
23
37
|
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",
|
|
24
|
-
children: [/* @__PURE__ */
|
|
38
|
+
children: [/* @__PURE__ */ u(o, { className: "h-4 w-4" }), "Back to Templates"]
|
|
25
39
|
}),
|
|
26
|
-
children: /* @__PURE__ */
|
|
40
|
+
children: /* @__PURE__ */ u("div", {
|
|
27
41
|
className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
|
|
28
|
-
children:
|
|
42
|
+
children: v ? "Failed to load template details. Please try again later." : "Template configuration not found."
|
|
29
43
|
})
|
|
30
|
-
}) : /* @__PURE__ */
|
|
44
|
+
}) : /* @__PURE__ */ u(i, {
|
|
31
45
|
title: "Template Details",
|
|
32
|
-
description: `Template ID: ${
|
|
46
|
+
description: `Template ID: ${m}`,
|
|
33
47
|
purpose: "Review exactly what this template sets up — its features and who it's built for — before you apply it, so you know what will land in your workspace and whether it fits how your team works.",
|
|
34
|
-
actions: /* @__PURE__ */
|
|
48
|
+
actions: /* @__PURE__ */ d("button", {
|
|
49
|
+
type: "button",
|
|
50
|
+
onClick: () => h("/templates"),
|
|
35
51
|
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",
|
|
36
|
-
children: [/* @__PURE__ */
|
|
52
|
+
children: [/* @__PURE__ */ u(o, { className: "h-4 w-4" }), "Back to Templates"]
|
|
37
53
|
}),
|
|
38
|
-
children: /* @__PURE__ */
|
|
54
|
+
children: /* @__PURE__ */ d("div", {
|
|
39
55
|
className: "mx-auto max-w-2xl",
|
|
40
|
-
children: [/* @__PURE__ */
|
|
56
|
+
children: [/* @__PURE__ */ u(p, {
|
|
41
57
|
storageKey: "timecampus-template-detail",
|
|
42
58
|
className: "mb-6",
|
|
43
59
|
steps: [{
|
|
44
60
|
id: "apply",
|
|
45
61
|
label: "Apply this template",
|
|
46
62
|
description: "Set up your workspace with this configuration.",
|
|
47
|
-
onClick: () =>
|
|
63
|
+
onClick: () => void O()
|
|
48
64
|
}, {
|
|
49
65
|
id: "browse",
|
|
50
66
|
label: "Browse other templates",
|
|
51
67
|
description: "Compare with templates for other workflows.",
|
|
52
|
-
onClick: () =>
|
|
68
|
+
onClick: () => h("/templates")
|
|
53
69
|
}]
|
|
54
|
-
}), /* @__PURE__ */ f
|
|
70
|
+
}), /* @__PURE__ */ d(f, {
|
|
55
71
|
treatment: "glass",
|
|
56
72
|
glow: !0,
|
|
57
73
|
className: "p-6",
|
|
58
74
|
children: [
|
|
59
|
-
/* @__PURE__ */
|
|
75
|
+
/* @__PURE__ */ d("div", {
|
|
60
76
|
className: "flex items-start gap-4",
|
|
61
|
-
children: [/* @__PURE__ */
|
|
77
|
+
children: [/* @__PURE__ */ u("div", {
|
|
62
78
|
className: "flex h-16 w-16 items-center justify-center rounded-xl bg-action-primary-bg/10",
|
|
63
|
-
children: /* @__PURE__ */
|
|
64
|
-
}), /* @__PURE__ */
|
|
79
|
+
children: /* @__PURE__ */ u(c, { className: "h-8 w-8 text-primary" })
|
|
80
|
+
}), /* @__PURE__ */ d("div", {
|
|
65
81
|
className: "flex-1",
|
|
66
82
|
children: [
|
|
67
|
-
/* @__PURE__ */
|
|
83
|
+
/* @__PURE__ */ u("h2", {
|
|
68
84
|
className: "text-lg font-semibold text-text-primary",
|
|
69
85
|
children: C
|
|
70
86
|
}),
|
|
71
|
-
/* @__PURE__ */
|
|
87
|
+
/* @__PURE__ */ u("p", {
|
|
72
88
|
className: "text-sm text-text-secondary",
|
|
73
89
|
children: w
|
|
74
90
|
}),
|
|
75
|
-
/* @__PURE__ */
|
|
76
|
-
className: "mt-2 flex items-center gap-
|
|
77
|
-
children: [/* @__PURE__ */
|
|
78
|
-
className: "flex
|
|
79
|
-
children:
|
|
80
|
-
}), /* @__PURE__ */
|
|
81
|
-
className: "flex
|
|
82
|
-
children:
|
|
91
|
+
/* @__PURE__ */ d("div", {
|
|
92
|
+
className: "mt-2 flex flex-wrap items-center gap-2 text-xs text-text-secondary",
|
|
93
|
+
children: [/* @__PURE__ */ u("span", {
|
|
94
|
+
className: "inline-flex rounded-full bg-bg-sunken px-2 py-0.5 font-medium",
|
|
95
|
+
children: T
|
|
96
|
+
}), /* @__PURE__ */ u("span", {
|
|
97
|
+
className: "inline-flex rounded-full bg-bg-sunken px-2 py-0.5 font-medium",
|
|
98
|
+
children: E
|
|
83
99
|
})]
|
|
84
100
|
})
|
|
85
101
|
]
|
|
86
102
|
})]
|
|
87
103
|
}),
|
|
88
|
-
D.length > 0 && /* @__PURE__ */
|
|
104
|
+
D.length > 0 && /* @__PURE__ */ d("div", {
|
|
89
105
|
className: "mt-6",
|
|
90
|
-
children: [/* @__PURE__ */
|
|
106
|
+
children: [/* @__PURE__ */ u("h3", {
|
|
91
107
|
className: "text-sm font-semibold text-text-primary",
|
|
92
108
|
children: "Features"
|
|
93
|
-
}), /* @__PURE__ */
|
|
109
|
+
}), /* @__PURE__ */ u("ul", {
|
|
94
110
|
className: "mt-2 space-y-2",
|
|
95
|
-
children: D.map((e) => /* @__PURE__ */
|
|
111
|
+
children: D.map((e) => /* @__PURE__ */ d("li", {
|
|
96
112
|
className: "flex items-center gap-2 text-sm text-text-secondary",
|
|
97
|
-
children: [/* @__PURE__ */
|
|
113
|
+
children: [/* @__PURE__ */ u(s, { className: "h-4 w-4 text-status-success-text" }), e]
|
|
98
114
|
}, e))
|
|
99
115
|
})]
|
|
100
116
|
}),
|
|
101
|
-
/* @__PURE__ */
|
|
117
|
+
/* @__PURE__ */ u("div", {
|
|
102
118
|
className: "mt-6 flex justify-end",
|
|
103
|
-
children: /* @__PURE__ */
|
|
104
|
-
|
|
105
|
-
|
|
119
|
+
children: /* @__PURE__ */ u("button", {
|
|
120
|
+
type: "button",
|
|
121
|
+
onClick: () => void O(),
|
|
122
|
+
disabled: b,
|
|
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: b ? "Applying..." : "Apply Template"
|
|
106
125
|
})
|
|
107
126
|
})
|
|
108
127
|
]
|
|
@@ -111,6 +130,6 @@ function h() {
|
|
|
111
130
|
});
|
|
112
131
|
}
|
|
113
132
|
//#endregion
|
|
114
|
-
export {
|
|
133
|
+
export { m as TemplateDetailPage };
|
|
115
134
|
|
|
116
135
|
//# sourceMappingURL=TemplateDetailPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TemplateDetailPage.js","names":[],"sources":["../../../../src/timecampus/pages/templates/TemplateDetailPage.tsx"],"sourcesContent":["import { useParams } from \"react-router-dom\";\nimport { Sparkles, ArrowLeft,
|
|
1
|
+
{"version":3,"file":"TemplateDetailPage.js","names":[],"sources":["../../../../src/timecampus/pages/templates/TemplateDetailPage.tsx"],"sourcesContent":["import { useParams } from \"react-router-dom\";\nimport { Sparkles, ArrowLeft, CheckCircle } 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 useSetConfigurationMutation,\n} from \"@/generated/wspace-operations\";\nimport { TimeCampusConfigScope } from \"@/generated/wspace-types\";\n\nexport function TemplateDetailPage() {\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 const [setConfiguration, { loading: applying }] =\n useSetConfigurationMutation();\n\n const config = data?.configuration;\n const val = (config?.value as Record<string, unknown>) ?? {};\n const name =\n (val.name as string) ?? config?.key?.replace(\"template.\", \"\") ?? \"Template\";\n const description =\n config?.description ??\n (val.description as string) ??\n \"A pre-built workspace template for time tracking workflows.\";\n const category = (val.category as string) ?? \"General\";\n const industry = (val.industry as string) ?? \"Any\";\n const features = (val.features as string[]) ?? [];\n\n const handleApply = async () => {\n if (!id) return;\n await setConfiguration({\n variables: {\n input: {\n key: `template.applied.${id}`,\n scope: TimeCampusConfigScope.Workspace,\n value: {\n templateId: id,\n name,\n appliedAt: new Date().toISOString(),\n },\n description: `Applied template: ${name}`,\n },\n },\n });\n navigate(\"/templates\");\n };\n\n if (loading) {\n return (\n <PageLayout title=\"Template Details\" description=\"Loading...\">\n <LoadingSkeleton variant=\"detail\" />\n </PageLayout>\n );\n }\n\n if (error || !config) {\n return (\n <PageLayout\n title=\"Template Details\"\n description={`Template ID: ${id}`}\n actions={\n <button\n type=\"button\"\n onClick={() => navigate(\"/templates\")}\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 Templates\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 template details. Please try again later.\"\n : \"Template configuration not found.\"}\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Template Details\"\n description={`Template ID: ${id}`}\n purpose=\"Review exactly what this template sets up — its features and who it's built for — before you apply it, so you know what will land in your workspace and whether it fits how your team works.\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigate(\"/templates\")}\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 Templates\n </button>\n }\n >\n <div className=\"mx-auto max-w-2xl\">\n <NextSteps\n storageKey=\"timecampus-template-detail\"\n className=\"mb-6\"\n steps={[\n {\n id: \"apply\",\n label: \"Apply this template\",\n description: \"Set up your workspace with this configuration.\",\n onClick: () => void handleApply(),\n },\n {\n id: \"browse\",\n label: \"Browse other templates\",\n description: \"Compare with templates for other workflows.\",\n onClick: () => navigate(\"/templates\"),\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 <Sparkles className=\"h-8 w-8 text-primary\" />\n </div>\n <div className=\"flex-1\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {name}\n </h2>\n <p className=\"text-sm text-text-secondary\">{description}</p>\n <div className=\"mt-2 flex flex-wrap items-center gap-2 text-xs text-text-secondary\">\n <span className=\"inline-flex rounded-full bg-bg-sunken px-2 py-0.5 font-medium\">\n {category}\n </span>\n <span className=\"inline-flex rounded-full bg-bg-sunken px-2 py-0.5 font-medium\">\n {industry}\n </span>\n </div>\n </div>\n </div>\n\n {features.length > 0 && (\n <div className=\"mt-6\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n Features\n </h3>\n <ul className=\"mt-2 space-y-2\">\n {features.map((feature) => (\n <li\n key={feature}\n className=\"flex items-center gap-2 text-sm text-text-secondary\"\n >\n <CheckCircle className=\"h-4 w-4 text-status-success-text\" />\n {feature}\n </li>\n ))}\n </ul>\n </div>\n )}\n\n <div className=\"mt-6 flex justify-end\">\n <button\n type=\"button\"\n onClick={() => void handleApply()}\n disabled={applying}\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 {applying ? \"Applying...\" : \"Apply Template\"}\n </button>\n </div>\n </GlassCard>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useTimeCampusNavigate as e } from "../../providers/TimeCampusProvider.js";
|
|
2
2
|
import { LoadingSkeleton as t } from "../../components/LoadingSkeleton.js";
|
|
3
3
|
import { useListConfigurationsQuery as n } from "../../../generated/wspace-operations.js";
|
|
4
4
|
import { PageLayout as r } from "../../components/PageLayout.js";
|
|
5
5
|
import { EmptyState as i } from "../../components/EmptyState.js";
|
|
6
|
-
import {
|
|
6
|
+
import { TimeCampusConfigScope as a } from "../../../generated/wspace-types.js";
|
|
7
|
+
import { ArrowRight as o, Sparkles as s } from "lucide-react";
|
|
7
8
|
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
8
9
|
//#region src/timecampus/pages/templates/TemplatesPage.tsx
|
|
9
10
|
function u() {
|
|
10
|
-
let
|
|
11
|
+
let u = e(), { data: d, loading: f, error: p } = n({ variables: { scope: a.Workspace } }), m = (d?.configurations ?? []).filter((e) => e.key.startsWith("template."));
|
|
11
12
|
return f ? /* @__PURE__ */ c(r, {
|
|
12
13
|
title: "Templates",
|
|
13
14
|
description: "Pre-built workspace templates for different workflows",
|
|
@@ -33,41 +34,45 @@ function u() {
|
|
|
33
34
|
}) : /* @__PURE__ */ c("div", {
|
|
34
35
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
35
36
|
children: m.map((e) => {
|
|
36
|
-
let t = e.value, n = t.name ?? e.key.replace("template.", ""), r = t.category ?? "General", i = t.industry ?? "Any",
|
|
37
|
+
let t = e.value, n = t.name ?? e.key.replace("template.", ""), r = t.category ?? "General", i = t.industry ?? "Any", a = t.description ?? "";
|
|
37
38
|
return /* @__PURE__ */ l("div", {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
className: "
|
|
51
|
-
children: [
|
|
52
|
-
r,
|
|
53
|
-
" · ",
|
|
54
|
-
i
|
|
55
|
-
]
|
|
56
|
-
})]
|
|
57
|
-
})]
|
|
58
|
-
}), /* @__PURE__ */ l("div", {
|
|
59
|
-
className: "mt-3 flex items-center justify-between text-xs text-text-secondary",
|
|
60
|
-
children: [/* @__PURE__ */ l("div", {
|
|
61
|
-
className: "flex items-center gap-3",
|
|
62
|
-
children: [/* @__PURE__ */ l("div", {
|
|
63
|
-
className: "flex items-center gap-1",
|
|
64
|
-
children: [/* @__PURE__ */ c(o, { className: "h-3 w-3 fill-amber-400 text-status-warning-text" }), /* @__PURE__ */ c("span", { children: u })]
|
|
39
|
+
role: "button",
|
|
40
|
+
tabIndex: 0,
|
|
41
|
+
onClick: () => u(`/templates/${e.id}`),
|
|
42
|
+
onKeyDown: (t) => {
|
|
43
|
+
(t.key === "Enter" || t.key === " ") && (t.preventDefault(), u(`/templates/${e.id}`));
|
|
44
|
+
},
|
|
45
|
+
className: "cursor-pointer rounded-xl border border-border-seam bg-gloss-card p-5 shadow-pop transition-colors hover:bg-bg-sunken/50",
|
|
46
|
+
children: [
|
|
47
|
+
/* @__PURE__ */ l("div", {
|
|
48
|
+
className: "flex items-start gap-3",
|
|
49
|
+
children: [/* @__PURE__ */ c("div", {
|
|
50
|
+
className: "flex h-12 w-12 items-center justify-center rounded-xl bg-action-primary-bg/10",
|
|
51
|
+
children: /* @__PURE__ */ c(s, { className: "h-6 w-6 text-primary" })
|
|
65
52
|
}), /* @__PURE__ */ l("div", {
|
|
66
|
-
className: "flex
|
|
67
|
-
children: [/* @__PURE__ */ c(
|
|
53
|
+
className: "flex-1",
|
|
54
|
+
children: [/* @__PURE__ */ c("p", {
|
|
55
|
+
className: "text-sm font-semibold text-text-primary",
|
|
56
|
+
children: n
|
|
57
|
+
}), /* @__PURE__ */ l("p", {
|
|
58
|
+
className: "text-xs text-text-secondary",
|
|
59
|
+
children: [
|
|
60
|
+
r,
|
|
61
|
+
" · ",
|
|
62
|
+
i
|
|
63
|
+
]
|
|
64
|
+
})]
|
|
68
65
|
})]
|
|
69
|
-
}),
|
|
70
|
-
|
|
66
|
+
}),
|
|
67
|
+
a && /* @__PURE__ */ c("p", {
|
|
68
|
+
className: "mt-3 line-clamp-2 text-xs text-text-secondary",
|
|
69
|
+
children: a
|
|
70
|
+
}),
|
|
71
|
+
/* @__PURE__ */ l("div", {
|
|
72
|
+
className: "mt-3 flex items-center justify-end text-xs font-medium text-primary",
|
|
73
|
+
children: ["View template ", /* @__PURE__ */ c(o, { className: "ml-1 h-3 w-3" })]
|
|
74
|
+
})
|
|
75
|
+
]
|
|
71
76
|
}, e.id);
|
|
72
77
|
})
|
|
73
78
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TemplatesPage.js","names":[],"sources":["../../../../src/timecampus/pages/templates/TemplatesPage.tsx"],"sourcesContent":["import { Sparkles,
|
|
1
|
+
{"version":3,"file":"TemplatesPage.js","names":[],"sources":["../../../../src/timecampus/pages/templates/TemplatesPage.tsx"],"sourcesContent":["import { Sparkles, ArrowRight } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { useTimeCampusNavigate } from \"../../providers/TimeCampusProvider\";\nimport {\n useListConfigurationsQuery,\n type ConfigurationFieldsFragment,\n} from \"@/generated/wspace-operations\";\nimport { TimeCampusConfigScope } from \"@/generated/wspace-types\";\n\nexport function TemplatesPage() {\n const navigate = useTimeCampusNavigate();\n\n const { data, loading, error } = useListConfigurationsQuery({\n variables: { scope: TimeCampusConfigScope.Workspace },\n });\n\n const configurations = data?.configurations ?? [];\n\n // Filter configurations that represent templates (key starts with 'template.')\n const templates = configurations.filter((c: ConfigurationFieldsFragment) =>\n c.key.startsWith(\"template.\"),\n );\n\n if (loading) {\n return (\n <PageLayout\n title=\"Templates\"\n description=\"Pre-built workspace templates for different workflows\"\n >\n <LoadingSkeleton variant=\"card\" count={6} />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"Templates\"\n description=\"Pre-built workspace templates for different workflows\"\n >\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n Failed to load template configurations. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Templates\"\n description=\"Pre-built workspace templates for different workflows\"\n 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=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {templates.map((template: ConfigurationFieldsFragment) => {\n const val = template.value as Record<string, unknown>;\n const name =\n (val.name as string) ?? template.key.replace(\"template.\", \"\");\n const category = (val.category as string) ?? \"General\";\n const industry = (val.industry as string) ?? \"Any\";\n const description = (val.description as string) ?? \"\";\n\n return (\n <div\n key={template.id}\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=\"cursor-pointer rounded-xl border border-border-seam bg-gloss-card p-5 shadow-pop transition-colors hover:bg-bg-sunken/50\"\n >\n <div className=\"flex items-start gap-3\">\n <div className=\"flex h-12 w-12 items-center justify-center rounded-xl bg-action-primary-bg/10\">\n <Sparkles className=\"h-6 w-6 text-primary\" />\n </div>\n <div className=\"flex-1\">\n <p className=\"text-sm font-semibold text-text-primary\">\n {name}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {category} · {industry}\n </p>\n </div>\n </div>\n {description && (\n <p className=\"mt-3 line-clamp-2 text-xs text-text-secondary\">\n {description}\n </p>\n )}\n <div className=\"mt-3 flex items-center justify-end text-xs font-medium text-primary\">\n View template <ArrowRight className=\"ml-1 h-3 w-3\" />\n </div>\n </div>\n );\n })}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;AAWA,SAAgB,IAAgB;CAC9B,IAAM,IAAW,GAAuB,EAElC,EAAE,SAAM,YAAS,aAAU,EAA2B,EAC1D,WAAW,EAAE,OAAO,EAAsB,WAAW,EACtD,CAAC,EAKI,KAHiB,GAAM,kBAAkB,EAAE,EAGhB,QAAQ,MACvC,EAAE,IAAI,WAAW,YAAY,CAC9B;AA0BD,QAxBI,IAEA,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA;EACjC,CAAA,GAIb,IAEA,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,OAAD;GAAK,WAAU;aAAoG;GAE7G,CAAA;EACK,CAAA,GAKf,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SAAQ;YAEP,EAAU,WAAW,IACpB,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,aAAY;GACZ,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAU,KAAK,MAA0C;IACxD,IAAM,IAAM,EAAS,OACf,IACH,EAAI,QAAmB,EAAS,IAAI,QAAQ,aAAa,GAAG,EACzD,IAAY,EAAI,YAAuB,WACvC,IAAY,EAAI,YAAuB,OACvC,IAAe,EAAI,eAA0B;AAEnD,WACE,kBAAC,OAAD;KAEE,MAAK;KACL,UAAU;KACV,eAAe,EAAS,cAAc,EAAS,KAAK;KACpD,YAAY,MAAM;AAChB,OAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,SACjC,EAAE,gBAAgB,EAClB,EAAS,cAAc,EAAS,KAAK;;KAGzC,WAAU;eAXZ;MAaE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD,EAAU,WAAU,wBAAyB,CAAA;QACzC,CAAA,EACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV;SACC,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBAAb;UACG;UAAS;UAAW;UACnB;WACA;UACF;;MACL,KACC,kBAAC,KAAD;OAAG,WAAU;iBACV;OACC,CAAA;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CAAqF,kBACrE,kBAAC,GAAD,EAAY,WAAU,gBAAiB,CAAA,CACjD;;MACF;OAjCC,EAAS,GAiCV;KAER;GACE,CAAA;EAEG,CAAA"}
|