@burdenoff/microfe-timecampus 2026.625.1 → 2026.626.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/timecampus/components/EmptyState.js +14 -0
- package/dist/timecampus/components/EmptyState.js.map +1 -0
- package/dist/timecampus/components/PageLayout.js +23 -15
- package/dist/timecampus/components/PageLayout.js.map +1 -1
- package/dist/timecampus/pages/applets/AppletStorePage.js +33 -30
- package/dist/timecampus/pages/applets/AppletStorePage.js.map +1 -1
- package/dist/timecampus/pages/applets/InstalledAppletsPage.js +37 -31
- package/dist/timecampus/pages/applets/InstalledAppletsPage.js.map +1 -1
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +48 -44
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
- package/dist/timecampus/pages/application-usage/ApplicationUsageSettingsPage.js +35 -31
- package/dist/timecampus/pages/application-usage/ApplicationUsageSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/channels/ChannelsPage.js +27 -24
- package/dist/timecampus/pages/channels/ChannelsPage.js.map +1 -1
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js +46 -38
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
- package/dist/timecampus/pages/dashboard/AnalyticsPage.js +66 -39
- package/dist/timecampus/pages/dashboard/AnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/dashboard/ReportsPage.js +42 -36
- package/dist/timecampus/pages/dashboard/ReportsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailAnalyticsPage.js +2 -0
- package/dist/timecampus/pages/email-tracking/EmailAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailSettingsPage.js +35 -31
- package/dist/timecampus/pages/email-tracking/EmailSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +31 -25
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +43 -35
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/focus-score/FocusScorePage.js +55 -51
- package/dist/timecampus/pages/focus-score/FocusScorePage.js.map +1 -1
- package/dist/timecampus/pages/focus-score/FocusScoreSettingsPage.js +32 -28
- package/dist/timecampus/pages/focus-score/FocusScoreSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/geofencing/GeofencingPage.js +32 -25
- package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
- package/dist/timecampus/pages/geofencing/GeofencingSettingsPage.js +29 -25
- package/dist/timecampus/pages/geofencing/GeofencingSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js +32 -29
- package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js.map +1 -1
- package/dist/timecampus/pages/location-tracking/LocationPrivacySettingsPage.js +29 -25
- package/dist/timecampus/pages/location-tracking/LocationPrivacySettingsPage.js.map +1 -1
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +39 -35
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingAnalyticsPage.js +2 -0
- package/dist/timecampus/pages/meetings/MeetingAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingCreatePage.js +33 -29
- package/dist/timecampus/pages/meetings/MeetingCreatePage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js +122 -104
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingsPage.js +42 -32
- package/dist/timecampus/pages/meetings/MeetingsPage.js.map +1 -1
- package/dist/timecampus/pages/pomodoro/PomodoroAnalyticsPage.js +2 -0
- package/dist/timecampus/pages/pomodoro/PomodoroAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/pomodoro/PomodoroPage.js +145 -114
- package/dist/timecampus/pages/pomodoro/PomodoroPage.js.map +1 -1
- package/dist/timecampus/pages/pomodoro/PomodoroSettingsPage.js +49 -45
- package/dist/timecampus/pages/pomodoro/PomodoroSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectCreatePage.js +28 -24
- package/dist/timecampus/pages/projects/ProjectCreatePage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectDetailPage.js +109 -78
- package/dist/timecampus/pages/projects/ProjectDetailPage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectsPage.js +11 -7
- package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
- package/dist/timecampus/pages/projects/TasksPage.js +40 -30
- package/dist/timecampus/pages/projects/TasksPage.js.map +1 -1
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +50 -46
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamAnalyticsPage.js +18 -14
- package/dist/timecampus/pages/team/TeamAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamMembersPage.js +41 -35
- package/dist/timecampus/pages/team/TeamMembersPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamPage.js +91 -61
- package/dist/timecampus/pages/team/TeamPage.js.map +1 -1
- package/dist/timecampus/pages/team-activity/TeamActivityPage.js +40 -36
- package/dist/timecampus/pages/team-activity/TeamActivityPage.js.map +1 -1
- package/dist/timecampus/pages/team-activity/TeamActivitySettingsPage.js +33 -29
- package/dist/timecampus/pages/team-activity/TeamActivitySettingsPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplateApplicationPage.js +17 -13
- package/dist/timecampus/pages/templates/TemplateApplicationPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplateDetailPage.js +67 -49
- package/dist/timecampus/pages/templates/TemplateDetailPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplatesPage.js +34 -31
- package/dist/timecampus/pages/templates/TemplatesPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeEntriesPage.js +48 -39
- package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeTrackingPage.js +65 -64
- package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimerPage.js +103 -69
- package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +53 -49
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
- package/dist/timecampus/pages/widgets/WidgetDetailPage.js +65 -47
- package/dist/timecampus/pages/widgets/WidgetDetailPage.js.map +1 -1
- package/dist/timecampus/pages/widgets/WidgetsPage.js +38 -31
- package/dist/timecampus/pages/widgets/WidgetsPage.js.map +1 -1
- package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js +2 -0
- package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js.map +1 -1
- package/dist/timecampus/pages/work-patterns/WorkPatternsSettingsPage.js +35 -31
- package/dist/timecampus/pages/work-patterns/WorkPatternsSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/workflows/WorkflowsPage.js +43 -33
- package/dist/timecampus/pages/workflows/WorkflowsPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TasksPage.js","names":[],"sources":["../../../../src/timecampus/pages/projects/TasksPage.tsx"],"sourcesContent":["import { useParams } from \"react-router-dom\";\nimport { Plus, CheckSquare } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { getStatusColor, getPriorityColor } from \"../../utils/helpers\";\nimport {\n useListTasksQuery,\n type TaskFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function TasksPage() {\n const { projectId } = useParams<{ projectId: string }>();\n\n const { data, loading, error } = useListTasksQuery({\n variables: { projectId: projectId! },\n skip: !projectId,\n });\n\n const tasks = data?.tasks?.items ?? [];\n\n if (error) {\n return (\n <PageLayout title=\"Tasks\" description={`Tasks for project ${projectId}`}>\n <div className=\"rounded-xl border border-status-error-bg/40 bg-status-error-bg p-6 text-sm text-status-error-text\">\n Failed to load tasks. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Tasks\"\n description={`Tasks for project ${projectId}`}\n actions={\n <button className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\">\n <Plus className=\"h-4 w-4\" />\n Add Task\n </button>\n }\n >\n {loading ? (\n <LoadingSkeleton variant=\"list\" count={5} />\n ) : (\n <
|
|
1
|
+
{"version":3,"file":"TasksPage.js","names":[],"sources":["../../../../src/timecampus/pages/projects/TasksPage.tsx"],"sourcesContent":["import { useParams } from \"react-router-dom\";\nimport { Plus, CheckSquare } 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 { getStatusColor, getPriorityColor } from \"../../utils/helpers\";\nimport {\n useListTasksQuery,\n type TaskFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function TasksPage() {\n const { projectId } = useParams<{ projectId: string }>();\n\n const { data, loading, error } = useListTasksQuery({\n variables: { projectId: projectId! },\n skip: !projectId,\n });\n\n const tasks = data?.tasks?.items ?? [];\n\n if (error) {\n return (\n <PageLayout title=\"Tasks\" description={`Tasks for project ${projectId}`}>\n <div className=\"rounded-xl border border-status-error-bg/40 bg-status-error-bg p-6 text-sm text-status-error-text\">\n Failed to load tasks. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Tasks\"\n description={`Tasks for project ${projectId}`}\n purpose=\"Break a project down into the concrete pieces of work that need doing, then track hours against each one — so you can see what's done, what's blocking, and where effort is actually being spent.\"\n actions={\n <button className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\">\n <Plus className=\"h-4 w-4\" />\n Add Task\n </button>\n }\n >\n {loading ? (\n <LoadingSkeleton variant=\"list\" count={5} />\n ) : tasks.length === 0 ? (\n <EmptyState\n illustration=\"empty-generic\"\n title=\"No tasks yet\"\n description=\"Add a task to break this project into trackable work.\"\n action={\n <button className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\">\n <Plus className=\"h-4 w-4\" />\n Add Task\n </button>\n }\n />\n ) : (\n <GlassCard treatment=\"glass\" glow className=\"overflow-hidden\">\n <div className=\"divide-y divide-border\">\n {tasks.map((task: TaskFieldsFragment) => (\n <div\n key={task.id}\n className=\"flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n <CheckSquare\n className={`h-5 w-5 ${task.status === \"done\" ? \"text-status-success-text\" : \"text-text-secondary\"}`}\n />\n <div>\n <p\n className={`text-sm font-medium ${task.status === \"done\" ? \"text-text-secondary line-through\" : \"text-text-primary\"}`}\n >\n {task.title}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {task.assigneeId ?? \"Unassigned\"} ·{\" \"}\n {task.trackedHours}h tracked\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getPriorityColor(task.priority)}`}\n >\n {task.priority}\n </span>\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getStatusColor(task.status)}`}\n >\n {task.status}\n </span>\n </div>\n </div>\n ))}\n </div>\n </GlassCard>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;AAYA,SAAgB,IAAY;CAC1B,IAAM,EAAE,iBAAc,GAAkC,EAElD,EAAE,SAAM,YAAS,aAAU,EAAkB;EACjD,WAAW,EAAa,cAAY;EACpC,MAAM,CAAC;EACR,CAAC,EAEI,IAAQ,GAAM,OAAO,SAAS,EAAE;AAYtC,QAVI,IAEA,kBAAC,GAAD;EAAY,OAAM;EAAQ,aAAa,qBAAqB;YAC1D,kBAAC,OAAD;GAAK,WAAU;aAAoG;GAE7G,CAAA;EACK,CAAA,GAKf,kBAAC,GAAD;EACE,OAAM;EACN,aAAa,qBAAqB;EAClC,SAAQ;EACR,SACE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,WAErB;;YAGV,IACC,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA,GAC1C,EAAM,WAAW,IACnB,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,aAAY;GACZ,QACE,kBAAC,UAAD;IAAQ,WAAU;cAAlB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,WAErB;;GAEX,CAAA,GAEF,kBAAC,GAAD;GAAW,WAAU;GAAQ,MAAA;GAAK,WAAU;aAC1C,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAM,KAAK,MACV,kBAAC,OAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EACE,WAAW,WAAW,EAAK,WAAW,SAAS,6BAA6B,yBAC5E,CAAA,EACF,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;OACE,WAAW,uBAAuB,EAAK,WAAW,SAAS,qCAAqC;iBAE/F,EAAK;OACJ,CAAA,EACJ,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAK,cAAc;QAAa;QAAU;QAC1C,EAAK;QAAa;QACjB;SACA,EAAA,CAAA,CACF;SACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,QAAD;OACE,WAAW,4DAA4D,EAAiB,EAAK,SAAS;iBAErG,EAAK;OACD,CAAA,EACP,kBAAC,QAAD;OACE,WAAW,4DAA4D,EAAe,EAAK,OAAO;iBAEjG,EAAK;OACD,CAAA,CACH;QACF;OA/BC,EAAK,GA+BN,CACN;IACE,CAAA;GACI,CAAA;EAEH,CAAA"}
|
|
@@ -4,83 +4,87 @@ import { StatCard as n } from "../../components/StatCard.js";
|
|
|
4
4
|
import { LoadingSkeleton as r } from "../../components/LoadingSkeleton.js";
|
|
5
5
|
import { useGetCapacityOverviewQuery as i, useListResourcePlansQuery as a } from "../../../generated/wspace-operations.js";
|
|
6
6
|
import { PageLayout as o } from "../../components/PageLayout.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { EmptyState as s } from "../../components/EmptyState.js";
|
|
8
|
+
import { CalendarRange as c, TrendingUp as l, Users as u } from "lucide-react";
|
|
9
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
9
10
|
//#region src/timecampus/pages/resource-planning/ResourcePlanningPage.tsx
|
|
10
|
-
function
|
|
11
|
-
let { workspaceId:
|
|
12
|
-
startDate:
|
|
13
|
-
endDate:
|
|
14
|
-
} }),
|
|
11
|
+
function p() {
|
|
12
|
+
let { workspaceId: p = "" } = e(), m = /* @__PURE__ */ new Date(), h = new Date(m.getFullYear(), m.getMonth(), m.getDate()).toISOString(), g = new Date(m.getFullYear(), m.getMonth(), m.getDate() + 7).toISOString(), { data: _, loading: v, error: y } = a(), { data: b } = i({ variables: {
|
|
13
|
+
startDate: h,
|
|
14
|
+
endDate: g
|
|
15
|
+
} }), x = _?.resourcePlans?.items ?? [], S = b?.capacityOverview, C = x.flatMap((e) => e.allocations.map((t) => ({
|
|
15
16
|
...t,
|
|
16
17
|
planName: e.name
|
|
17
18
|
})));
|
|
18
|
-
if (
|
|
19
|
+
if (v) return /* @__PURE__ */ d(o, {
|
|
19
20
|
title: "Resource Planning",
|
|
20
21
|
description: "Plan and optimize team resource allocation",
|
|
21
|
-
children: /* @__PURE__ */
|
|
22
|
+
children: /* @__PURE__ */ d(r, {
|
|
22
23
|
variant: "list",
|
|
23
24
|
count: 5
|
|
24
25
|
})
|
|
25
26
|
});
|
|
26
|
-
if (
|
|
27
|
+
if (y) return /* @__PURE__ */ d(o, {
|
|
27
28
|
title: "Resource Planning",
|
|
28
29
|
description: "Plan and optimize team resource allocation",
|
|
29
|
-
children: /* @__PURE__ */
|
|
30
|
+
children: /* @__PURE__ */ d("div", {
|
|
30
31
|
className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
|
|
31
32
|
children: "Failed to load resource planning data. Please try again later."
|
|
32
33
|
})
|
|
33
34
|
});
|
|
34
|
-
let
|
|
35
|
-
return /* @__PURE__ */
|
|
35
|
+
let w = S?.utilizationRate ?? 0, T = S?.overallocatedUsers?.length ?? 0, E = S?.availableCapacity ?? 0;
|
|
36
|
+
return /* @__PURE__ */ f(o, {
|
|
36
37
|
title: "Resource Planning",
|
|
37
38
|
description: "Plan and optimize team resource allocation",
|
|
38
|
-
|
|
39
|
+
purpose: "Match the work ahead to the people you have — see who's over- or under-booked and how much capacity is free this week — so you can commit to deadlines you can actually hit without burning anyone out.",
|
|
40
|
+
children: [/* @__PURE__ */ f("div", {
|
|
39
41
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
|
|
40
42
|
children: [
|
|
41
|
-
/* @__PURE__ */
|
|
43
|
+
/* @__PURE__ */ d(n, {
|
|
44
|
+
featured: !0,
|
|
42
45
|
title: "Team Utilization",
|
|
43
|
-
value: `${Math.round(
|
|
44
|
-
icon: /* @__PURE__ */
|
|
46
|
+
value: `${Math.round(w)}%`,
|
|
47
|
+
icon: /* @__PURE__ */ d(l, { className: "h-5 w-5" })
|
|
45
48
|
}),
|
|
46
|
-
/* @__PURE__ */
|
|
49
|
+
/* @__PURE__ */ d(n, {
|
|
47
50
|
title: "Over-allocated",
|
|
48
|
-
value: String(
|
|
49
|
-
icon: /* @__PURE__ */ u
|
|
51
|
+
value: String(T),
|
|
52
|
+
icon: /* @__PURE__ */ d(u, { className: "h-5 w-5" }),
|
|
50
53
|
iconBgColor: "bg-status-error-bg text-status-error-text"
|
|
51
54
|
}),
|
|
52
|
-
/* @__PURE__ */
|
|
55
|
+
/* @__PURE__ */ d(n, {
|
|
53
56
|
title: "Active Plans",
|
|
54
|
-
value: String(
|
|
55
|
-
icon: /* @__PURE__ */ u
|
|
57
|
+
value: String(x.length),
|
|
58
|
+
icon: /* @__PURE__ */ d(u, { className: "h-5 w-5" })
|
|
56
59
|
}),
|
|
57
|
-
/* @__PURE__ */
|
|
60
|
+
/* @__PURE__ */ d(n, {
|
|
58
61
|
title: "Available Capacity",
|
|
59
|
-
value: `${Math.round(
|
|
60
|
-
icon: /* @__PURE__ */
|
|
62
|
+
value: `${Math.round(E)}h`,
|
|
63
|
+
icon: /* @__PURE__ */ d(c, { className: "h-5 w-5" }),
|
|
61
64
|
description: "This week"
|
|
62
65
|
})
|
|
63
66
|
]
|
|
64
|
-
}), /* @__PURE__ */
|
|
67
|
+
}), C.length === 0 ? /* @__PURE__ */ d(s, {
|
|
68
|
+
illustration: "empty-people",
|
|
69
|
+
title: "No resource allocations found",
|
|
70
|
+
description: "Create a resource plan to start allocating team members and balancing workload across the week."
|
|
71
|
+
}) : /* @__PURE__ */ d("div", {
|
|
65
72
|
className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop",
|
|
66
|
-
children: /* @__PURE__ */
|
|
73
|
+
children: /* @__PURE__ */ d("div", {
|
|
67
74
|
className: "divide-y divide-border",
|
|
68
|
-
children:
|
|
69
|
-
className: "p-8 text-center text-sm text-text-secondary",
|
|
70
|
-
children: "No resource allocations found. Create a resource plan to start allocating team members."
|
|
71
|
-
}) : S.map((e) => {
|
|
75
|
+
children: C.map((e) => {
|
|
72
76
|
let n = e.utilizationRate ?? (e.allocatedHours > 0 ? Math.round(e.actualHours / e.allocatedHours * 100) : 0);
|
|
73
|
-
return /* @__PURE__ */
|
|
77
|
+
return /* @__PURE__ */ f("div", {
|
|
74
78
|
className: "flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors",
|
|
75
|
-
children: [/* @__PURE__ */
|
|
79
|
+
children: [/* @__PURE__ */ f("div", {
|
|
76
80
|
className: "flex items-center gap-3",
|
|
77
|
-
children: [/* @__PURE__ */
|
|
81
|
+
children: [/* @__PURE__ */ d("div", {
|
|
78
82
|
className: "flex h-9 w-9 items-center justify-center rounded-full bg-action-primary-bg/10 text-sm font-medium text-primary",
|
|
79
83
|
children: t(e.userId)
|
|
80
|
-
}), /* @__PURE__ */
|
|
84
|
+
}), /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("p", {
|
|
81
85
|
className: "text-sm font-medium text-text-primary",
|
|
82
86
|
children: e.userId
|
|
83
|
-
}), /* @__PURE__ */
|
|
87
|
+
}), /* @__PURE__ */ f("p", {
|
|
84
88
|
className: "text-xs text-text-secondary",
|
|
85
89
|
children: [
|
|
86
90
|
e.planName,
|
|
@@ -92,25 +96,25 @@ function f() {
|
|
|
92
96
|
e.endDate.slice(0, 10)
|
|
93
97
|
]
|
|
94
98
|
})] })]
|
|
95
|
-
}), /* @__PURE__ */
|
|
99
|
+
}), /* @__PURE__ */ f("div", {
|
|
96
100
|
className: "flex items-center gap-4",
|
|
97
|
-
children: [/* @__PURE__ */
|
|
101
|
+
children: [/* @__PURE__ */ d("div", {
|
|
98
102
|
className: "text-right",
|
|
99
|
-
children: /* @__PURE__ */
|
|
103
|
+
children: /* @__PURE__ */ f("p", {
|
|
100
104
|
className: "text-sm font-medium text-text-primary",
|
|
101
105
|
children: [e.allocatedHours, "h allocated"]
|
|
102
106
|
})
|
|
103
|
-
}), /* @__PURE__ */
|
|
107
|
+
}), /* @__PURE__ */ f("div", {
|
|
104
108
|
className: "w-24",
|
|
105
|
-
children: [/* @__PURE__ */
|
|
109
|
+
children: [/* @__PURE__ */ d("div", {
|
|
106
110
|
className: "flex items-center justify-between text-xs mb-0.5",
|
|
107
|
-
children: /* @__PURE__ */
|
|
111
|
+
children: /* @__PURE__ */ f("span", {
|
|
108
112
|
className: `font-medium ${n > 100 ? "text-status-error-text" : n < 70 ? "text-status-warning-text" : "text-status-success-text"}`,
|
|
109
113
|
children: [n, "%"]
|
|
110
114
|
})
|
|
111
|
-
}), /* @__PURE__ */
|
|
115
|
+
}), /* @__PURE__ */ d("div", {
|
|
112
116
|
className: "h-2 rounded-full bg-bg-sunken",
|
|
113
|
-
children: /* @__PURE__ */
|
|
117
|
+
children: /* @__PURE__ */ d("div", {
|
|
114
118
|
className: `h-2 rounded-full ${n > 100 ? "bg-status-error-text" : n < 70 ? "bg-status-warning-text" : "bg-status-success-text"}`,
|
|
115
119
|
style: { width: `${Math.min(n, 100)}%` }
|
|
116
120
|
})
|
|
@@ -124,6 +128,6 @@ function f() {
|
|
|
124
128
|
});
|
|
125
129
|
}
|
|
126
130
|
//#endregion
|
|
127
|
-
export {
|
|
131
|
+
export { p as ResourcePlanningPage };
|
|
128
132
|
|
|
129
133
|
//# sourceMappingURL=ResourcePlanningPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResourcePlanningPage.js","names":[],"sources":["../../../../src/timecampus/pages/resource-planning/ResourcePlanningPage.tsx"],"sourcesContent":["import { CalendarRange, Users, TrendingUp } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { getStatusColor, getInitials } from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListResourcePlansQuery,\n useGetCapacityOverviewQuery,\n type ResourcePlanFieldsFragment,\n type ResourceAllocationFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function ResourcePlanningPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const now = new Date();\n const startDate = new Date(\n now.getFullYear(),\n now.getMonth(),\n now.getDate(),\n ).toISOString();\n const endDate = new Date(\n now.getFullYear(),\n now.getMonth(),\n now.getDate() + 7,\n ).toISOString();\n\n const {\n data: plansData,\n loading: plansLoading,\n error: plansError,\n } = useListResourcePlansQuery();\n const { data: capacityData } = useGetCapacityOverviewQuery({\n variables: { startDate, endDate },\n });\n\n const plans = plansData?.resourcePlans?.items ?? [];\n const capacity = capacityData?.capacityOverview;\n\n // Flatten all allocations across plans for display\n const allocations = plans.flatMap((plan: ResourcePlanFieldsFragment) =>\n plan.allocations.map((alloc: ResourceAllocationFieldsFragment) => ({\n ...alloc,\n planName: plan.name,\n })),\n );\n\n if (plansLoading) {\n return (\n <PageLayout\n title=\"Resource Planning\"\n description=\"Plan and optimize team resource allocation\"\n >\n <LoadingSkeleton variant=\"list\" count={5} />\n </PageLayout>\n );\n }\n\n if (plansError) {\n return (\n <PageLayout\n title=\"Resource Planning\"\n description=\"Plan and optimize team resource allocation\"\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 resource planning data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n const utilizationRate = capacity?.utilizationRate ?? 0;\n const overallocatedCount = capacity?.overallocatedUsers?.length ?? 0;\n const availableHours = capacity?.availableCapacity ?? 0;\n\n return (\n <PageLayout\n title=\"Resource Planning\"\n description=\"Plan and optimize team resource allocation\"\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n title=\"Team Utilization\"\n value={`${Math.round(utilizationRate)}%`}\n icon={<TrendingUp className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Over-allocated\"\n value={String(overallocatedCount)}\n icon={<Users className=\"h-5 w-5\" />}\n iconBgColor=\"bg-status-error-bg text-status-error-text\"\n />\n <StatCard\n title=\"Active Plans\"\n value={String(plans.length)}\n icon={<Users className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Available Capacity\"\n value={`${Math.round(availableHours)}h`}\n icon={<CalendarRange className=\"h-5 w-5\" />}\n description=\"This week\"\n />\n </div>\n\n
|
|
1
|
+
{"version":3,"file":"ResourcePlanningPage.js","names":[],"sources":["../../../../src/timecampus/pages/resource-planning/ResourcePlanningPage.tsx"],"sourcesContent":["import { CalendarRange, Users, TrendingUp } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { getStatusColor, getInitials } from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListResourcePlansQuery,\n useGetCapacityOverviewQuery,\n type ResourcePlanFieldsFragment,\n type ResourceAllocationFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function ResourcePlanningPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const now = new Date();\n const startDate = new Date(\n now.getFullYear(),\n now.getMonth(),\n now.getDate(),\n ).toISOString();\n const endDate = new Date(\n now.getFullYear(),\n now.getMonth(),\n now.getDate() + 7,\n ).toISOString();\n\n const {\n data: plansData,\n loading: plansLoading,\n error: plansError,\n } = useListResourcePlansQuery();\n const { data: capacityData } = useGetCapacityOverviewQuery({\n variables: { startDate, endDate },\n });\n\n const plans = plansData?.resourcePlans?.items ?? [];\n const capacity = capacityData?.capacityOverview;\n\n // Flatten all allocations across plans for display\n const allocations = plans.flatMap((plan: ResourcePlanFieldsFragment) =>\n plan.allocations.map((alloc: ResourceAllocationFieldsFragment) => ({\n ...alloc,\n planName: plan.name,\n })),\n );\n\n if (plansLoading) {\n return (\n <PageLayout\n title=\"Resource Planning\"\n description=\"Plan and optimize team resource allocation\"\n >\n <LoadingSkeleton variant=\"list\" count={5} />\n </PageLayout>\n );\n }\n\n if (plansError) {\n return (\n <PageLayout\n title=\"Resource Planning\"\n description=\"Plan and optimize team resource allocation\"\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 resource planning data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n const utilizationRate = capacity?.utilizationRate ?? 0;\n const overallocatedCount = capacity?.overallocatedUsers?.length ?? 0;\n const availableHours = capacity?.availableCapacity ?? 0;\n\n return (\n <PageLayout\n title=\"Resource Planning\"\n description=\"Plan and optimize team resource allocation\"\n purpose=\"Match the work ahead to the people you have — see who's over- or under-booked and how much capacity is free this week — so you can commit to deadlines you can actually hit without burning anyone out.\"\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n featured\n title=\"Team Utilization\"\n value={`${Math.round(utilizationRate)}%`}\n icon={<TrendingUp className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Over-allocated\"\n value={String(overallocatedCount)}\n icon={<Users className=\"h-5 w-5\" />}\n iconBgColor=\"bg-status-error-bg text-status-error-text\"\n />\n <StatCard\n title=\"Active Plans\"\n value={String(plans.length)}\n icon={<Users className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Available Capacity\"\n value={`${Math.round(availableHours)}h`}\n icon={<CalendarRange className=\"h-5 w-5\" />}\n description=\"This week\"\n />\n </div>\n\n {allocations.length === 0 ? (\n <EmptyState\n illustration=\"empty-people\"\n title=\"No resource allocations found\"\n description=\"Create a resource plan to start allocating team members and balancing workload across the week.\"\n />\n ) : (\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop\">\n <div className=\"divide-y divide-border\">\n {allocations.map(\n (\n alloc: ResourceAllocationFieldsFragment & { planName: string },\n ) => {\n const utilization =\n alloc.utilizationRate ??\n (alloc.allocatedHours > 0\n ? Math.round(\n (alloc.actualHours / alloc.allocatedHours) * 100,\n )\n : 0);\n return (\n <div\n key={alloc.id}\n className=\"flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-9 w-9 items-center justify-center rounded-full bg-action-primary-bg/10 text-sm font-medium text-primary\">\n {getInitials(alloc.userId)}\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {alloc.userId}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {alloc.planName} ·{\" \"}\n {alloc.startDate.slice(0, 10)} to{\" \"}\n {alloc.endDate.slice(0, 10)}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-4\">\n <div className=\"text-right\">\n <p className=\"text-sm font-medium text-text-primary\">\n {alloc.allocatedHours}h allocated\n </p>\n </div>\n <div className=\"w-24\">\n <div className=\"flex items-center justify-between text-xs mb-0.5\">\n <span\n className={`font-medium ${utilization > 100 ? \"text-status-error-text\" : utilization < 70 ? \"text-status-warning-text\" : \"text-status-success-text\"}`}\n >\n {utilization}%\n </span>\n </div>\n <div className=\"h-2 rounded-full bg-bg-sunken\">\n <div\n className={`h-2 rounded-full ${utilization > 100 ? \"bg-status-error-text\" : utilization < 70 ? \"bg-status-warning-text\" : \"bg-status-success-text\"}`}\n style={{ width: `${Math.min(utilization, 100)}%` }}\n />\n </div>\n </div>\n </div>\n </div>\n );\n },\n )}\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -4,17 +4,18 @@ import { useGetTimeEntryStatsQuery as n, useGetWorkPatternSummaryQuery as r } fr
|
|
|
4
4
|
import { PageLayout as i } from "../../components/PageLayout.js";
|
|
5
5
|
import { BarChart3 as a, Users as o } from "lucide-react";
|
|
6
6
|
import { jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
7
|
+
import { GlassCard as l } from "@burdenoff/fe-libs/ui";
|
|
7
8
|
//#region src/timecampus/pages/team/TeamAnalyticsPage.tsx
|
|
8
|
-
function
|
|
9
|
-
let { workspaceId:
|
|
10
|
-
return
|
|
9
|
+
function u() {
|
|
10
|
+
let { workspaceId: u = "" } = e(), { data: d, loading: f, error: p } = n({ skip: !u }), { data: m, loading: h } = r({ skip: !u }), g = d?.timeEntryStats, _ = m?.workPatternSummary;
|
|
11
|
+
return f || h ? /* @__PURE__ */ s(i, {
|
|
11
12
|
title: "Team Analytics",
|
|
12
13
|
description: "Team performance insights and metrics",
|
|
13
14
|
children: /* @__PURE__ */ s(t, {
|
|
14
15
|
variant: "card",
|
|
15
16
|
count: 4
|
|
16
17
|
})
|
|
17
|
-
}) :
|
|
18
|
+
}) : p ? /* @__PURE__ */ s(i, {
|
|
18
19
|
title: "Team Analytics",
|
|
19
20
|
description: "Team performance insights and metrics",
|
|
20
21
|
children: /* @__PURE__ */ s("div", {
|
|
@@ -24,10 +25,13 @@ function l() {
|
|
|
24
25
|
}) : /* @__PURE__ */ s(i, {
|
|
25
26
|
title: "Team Analytics",
|
|
26
27
|
description: "Team performance insights and metrics",
|
|
28
|
+
purpose: "Compare how your team works — hours by member, productive days, focus versus meeting time — so you can rebalance workload, recognise top contributors, and fix the patterns that slow everyone down.",
|
|
27
29
|
children: /* @__PURE__ */ c("div", {
|
|
28
30
|
className: "grid grid-cols-1 gap-6 lg:grid-cols-2",
|
|
29
|
-
children: [/* @__PURE__ */ c(
|
|
30
|
-
|
|
31
|
+
children: [/* @__PURE__ */ c(l, {
|
|
32
|
+
treatment: "glass",
|
|
33
|
+
glow: !0,
|
|
34
|
+
className: "p-6",
|
|
31
35
|
children: [/* @__PURE__ */ c("div", {
|
|
32
36
|
className: "mb-4 flex items-center gap-2",
|
|
33
37
|
children: [/* @__PURE__ */ s(o, { className: "h-5 w-5 text-primary" }), /* @__PURE__ */ s("h3", {
|
|
@@ -40,15 +44,15 @@ function l() {
|
|
|
40
44
|
className: "text-center",
|
|
41
45
|
children: [/* @__PURE__ */ s("p", {
|
|
42
46
|
className: "text-sm text-text-secondary",
|
|
43
|
-
children:
|
|
44
|
-
}),
|
|
47
|
+
children: g ? `${Math.round(g.totalHours)}h total · ${Math.round(g.billableHours)}h billable` : "No data available"
|
|
48
|
+
}), _?.mostProductiveDay && /* @__PURE__ */ c("p", {
|
|
45
49
|
className: "mt-1 text-xs text-text-secondary",
|
|
46
|
-
children: ["Most productive: ",
|
|
50
|
+
children: ["Most productive: ", _.mostProductiveDay]
|
|
47
51
|
})]
|
|
48
52
|
})
|
|
49
53
|
})]
|
|
50
54
|
}), /* @__PURE__ */ c("div", {
|
|
51
|
-
className: "rounded-xl border border-border-seam bg-
|
|
55
|
+
className: "rounded-xl border border-border-seam bg-bg-surface shadow-elevation-1 p-6",
|
|
52
56
|
children: [/* @__PURE__ */ c("div", {
|
|
53
57
|
className: "mb-4 flex items-center gap-2",
|
|
54
58
|
children: [/* @__PURE__ */ s(a, { className: "h-5 w-5 text-primary" }), /* @__PURE__ */ s("h3", {
|
|
@@ -61,10 +65,10 @@ function l() {
|
|
|
61
65
|
className: "text-center",
|
|
62
66
|
children: [/* @__PURE__ */ s("p", {
|
|
63
67
|
className: "text-sm text-text-secondary",
|
|
64
|
-
children:
|
|
65
|
-
}),
|
|
68
|
+
children: _ ? `${Math.round(_.averageProductivity)}% avg productivity · ${_.averageFocusHours.toFixed(1)}h avg focus` : "No data available"
|
|
69
|
+
}), _?.insights && _.insights.length > 0 && /* @__PURE__ */ s("p", {
|
|
66
70
|
className: "mt-1 text-xs text-text-secondary",
|
|
67
|
-
children:
|
|
71
|
+
children: _.insights[0].title
|
|
68
72
|
})]
|
|
69
73
|
})
|
|
70
74
|
})]
|
|
@@ -73,6 +77,6 @@ function l() {
|
|
|
73
77
|
});
|
|
74
78
|
}
|
|
75
79
|
//#endregion
|
|
76
|
-
export {
|
|
80
|
+
export { u as TeamAnalyticsPage };
|
|
77
81
|
|
|
78
82
|
//# sourceMappingURL=TeamAnalyticsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamAnalyticsPage.js","names":[],"sources":["../../../../src/timecampus/pages/team/TeamAnalyticsPage.tsx"],"sourcesContent":["import { BarChart3, Users } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useGetTimeEntryStatsQuery,\n useGetWorkPatternSummaryQuery,\n} from \"@/generated/wspace-operations\";\n\nexport function TeamAnalyticsPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const {\n data: statsData,\n loading: statsLoading,\n error: statsError,\n } = useGetTimeEntryStatsQuery({\n skip: !workspaceId,\n });\n\n const { data: patternData, loading: patternLoading } =\n useGetWorkPatternSummaryQuery({\n skip: !workspaceId,\n });\n\n const stats = statsData?.timeEntryStats;\n const pattern = patternData?.workPatternSummary;\n\n if (statsLoading || patternLoading) {\n return (\n <PageLayout\n title=\"Team Analytics\"\n description=\"Team performance insights and metrics\"\n >\n <LoadingSkeleton variant=\"card\" count={4} />\n </PageLayout>\n );\n }\n\n if (statsError) {\n return (\n <PageLayout\n title=\"Team Analytics\"\n description=\"Team performance insights and metrics\"\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 analytics. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Team Analytics\"\n description=\"Team performance insights and metrics\"\n >\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <
|
|
1
|
+
{"version":3,"file":"TeamAnalyticsPage.js","names":[],"sources":["../../../../src/timecampus/pages/team/TeamAnalyticsPage.tsx"],"sourcesContent":["import { BarChart3, Users } 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 useGetTimeEntryStatsQuery,\n useGetWorkPatternSummaryQuery,\n} from \"@/generated/wspace-operations\";\n\nexport function TeamAnalyticsPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const {\n data: statsData,\n loading: statsLoading,\n error: statsError,\n } = useGetTimeEntryStatsQuery({\n skip: !workspaceId,\n });\n\n const { data: patternData, loading: patternLoading } =\n useGetWorkPatternSummaryQuery({\n skip: !workspaceId,\n });\n\n const stats = statsData?.timeEntryStats;\n const pattern = patternData?.workPatternSummary;\n\n if (statsLoading || patternLoading) {\n return (\n <PageLayout\n title=\"Team Analytics\"\n description=\"Team performance insights and metrics\"\n >\n <LoadingSkeleton variant=\"card\" count={4} />\n </PageLayout>\n );\n }\n\n if (statsError) {\n return (\n <PageLayout\n title=\"Team Analytics\"\n description=\"Team performance insights and metrics\"\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 analytics. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Team Analytics\"\n description=\"Team performance insights and metrics\"\n purpose=\"Compare how your team works — hours by member, productive days, focus versus meeting time — so you can rebalance workload, recognise top contributors, and fix the patterns that slow everyone down.\"\n >\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <GlassCard treatment=\"glass\" glow className=\"p-6\">\n <div className=\"mb-4 flex items-center gap-2\">\n <Users className=\"h-5 w-5 text-primary\" />\n <h3 className=\"text-sm font-semibold text-text-primary\">\n Hours by Member\n </h3>\n </div>\n <div className=\"flex h-48 items-center justify-center rounded-lg border border-dashed border-border-subtle\">\n <div className=\"text-center\">\n <p className=\"text-sm text-text-secondary\">\n {stats\n ? `${Math.round(stats.totalHours)}h total · ${Math.round(stats.billableHours)}h billable`\n : \"No data available\"}\n </p>\n {pattern?.mostProductiveDay && (\n <p className=\"mt-1 text-xs text-text-secondary\">\n Most productive: {pattern.mostProductiveDay}\n </p>\n )}\n </div>\n </div>\n </GlassCard>\n <div className=\"rounded-xl border border-border-seam bg-bg-surface shadow-elevation-1 p-6\">\n <div className=\"mb-4 flex items-center gap-2\">\n <BarChart3 className=\"h-5 w-5 text-primary\" />\n <h3 className=\"text-sm font-semibold text-text-primary\">\n Productivity Trends\n </h3>\n </div>\n <div className=\"flex h-48 items-center justify-center rounded-lg border border-dashed border-border-subtle\">\n <div className=\"text-center\">\n <p className=\"text-sm text-text-secondary\">\n {pattern\n ? `${Math.round(pattern.averageProductivity)}% avg productivity · ${pattern.averageFocusHours.toFixed(1)}h avg focus`\n : \"No data available\"}\n </p>\n {pattern?.insights && pattern.insights.length > 0 && (\n <p className=\"mt-1 text-xs text-text-secondary\">\n {pattern.insights[0].title}\n </p>\n )}\n </div>\n </div>\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;AAUA,SAAgB,IAAoB;CAClC,IAAM,EAAE,iBAAc,OAAO,GAAe,EAEtC,EACJ,MAAM,GACN,SAAS,GACT,OAAO,MACL,EAA0B,EAC5B,MAAM,CAAC,GACR,CAAC,EAEI,EAAE,MAAM,GAAa,SAAS,MAClC,EAA8B,EAC5B,MAAM,CAAC,GACR,CAAC,EAEE,IAAQ,GAAW,gBACnB,IAAU,GAAa;AA0B7B,QAxBI,KAAgB,IAEhB,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;YAER,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD;IAAW,WAAU;IAAQ,MAAA;IAAK,WAAU;cAA5C,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAO,WAAU,wBAAyB,CAAA,EAC1C,kBAAC,MAAD;MAAI,WAAU;gBAA0C;MAEnD,CAAA,CACD;QACN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,IACG,GAAG,KAAK,MAAM,EAAM,WAAW,CAAC,YAAY,KAAK,MAAM,EAAM,cAAc,CAAC,cAC5E;OACF,CAAA,EACH,GAAS,qBACR,kBAAC,KAAD;OAAG,WAAU;iBAAb,CAAgD,qBAC5B,EAAQ,kBACxB;SAEF;;KACF,CAAA,CACI;OACZ,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAW,WAAU,wBAAyB,CAAA,EAC9C,kBAAC,MAAD;MAAI,WAAU;gBAA0C;MAEnD,CAAA,CACD;QACN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,IACG,GAAG,KAAK,MAAM,EAAQ,oBAAoB,CAAC,uBAAuB,EAAQ,kBAAkB,QAAQ,EAAE,CAAC,eACvG;OACF,CAAA,EACH,GAAS,YAAY,EAAQ,SAAS,SAAS,KAC9C,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAQ,SAAS,GAAG;OACnB,CAAA,CAEF;;KACF,CAAA,CACF;MACF;;EACK,CAAA"}
|
|
@@ -3,85 +3,91 @@ import { formatHours as t } from "../../utils/helpers.js";
|
|
|
3
3
|
import { LoadingSkeleton as n } from "../../components/LoadingSkeleton.js";
|
|
4
4
|
import { useListTimeEntriesQuery as r } from "../../../generated/wspace-operations.js";
|
|
5
5
|
import { PageLayout as i } from "../../components/PageLayout.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { EmptyState as a } from "../../components/EmptyState.js";
|
|
7
|
+
import { Plus as o, Users as s } from "lucide-react";
|
|
8
|
+
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
9
|
+
import { GlassCard as u } from "@burdenoff/fe-libs/ui";
|
|
8
10
|
//#region src/timecampus/pages/team/TeamMembersPage.tsx
|
|
9
|
-
function
|
|
10
|
-
let { workspaceId:
|
|
11
|
+
function d() {
|
|
12
|
+
let { workspaceId: d = "" } = e(), { data: f, loading: p, error: m } = r({
|
|
11
13
|
variables: { limit: 100 },
|
|
12
|
-
skip: !
|
|
13
|
-
}),
|
|
14
|
+
skip: !d
|
|
15
|
+
}), h = (f?.timeEntries?.items ?? []).reduce((e, t) => {
|
|
14
16
|
let n = t.project?.name ?? "Unassigned", r = t.projectId ?? "unassigned";
|
|
15
17
|
return e[r] || (e[r] = {
|
|
16
18
|
name: n,
|
|
17
19
|
totalHours: 0,
|
|
18
20
|
entries: 0
|
|
19
21
|
}), e[r].totalHours += (t.duration ?? 0) / 3600, e[r].entries += 1, e;
|
|
20
|
-
}, {}),
|
|
22
|
+
}, {}), g = Object.entries(h).map(([e, t]) => ({
|
|
21
23
|
id: e,
|
|
22
24
|
...t
|
|
23
25
|
})).sort((e, t) => t.totalHours - e.totalHours);
|
|
24
|
-
return
|
|
26
|
+
return p ? /* @__PURE__ */ c(i, {
|
|
25
27
|
title: "Team Members",
|
|
26
28
|
description: "Manage team members and roles",
|
|
27
|
-
actions: /* @__PURE__ */
|
|
29
|
+
actions: /* @__PURE__ */ l("button", {
|
|
28
30
|
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",
|
|
29
|
-
children: [/* @__PURE__ */
|
|
31
|
+
children: [/* @__PURE__ */ c(o, { className: "h-4 w-4" }), "Invite Member"]
|
|
30
32
|
}),
|
|
31
|
-
children: /* @__PURE__ */
|
|
33
|
+
children: /* @__PURE__ */ c(n, {
|
|
32
34
|
variant: "list",
|
|
33
35
|
count: 5
|
|
34
36
|
})
|
|
35
|
-
}) :
|
|
37
|
+
}) : m ? /* @__PURE__ */ c(i, {
|
|
36
38
|
title: "Team Members",
|
|
37
39
|
description: "Manage team members and roles",
|
|
38
|
-
children: /* @__PURE__ */
|
|
40
|
+
children: /* @__PURE__ */ c("div", {
|
|
39
41
|
className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
|
|
40
42
|
children: "Failed to load team data. Please try again later."
|
|
41
43
|
})
|
|
42
|
-
}) : /* @__PURE__ */
|
|
44
|
+
}) : /* @__PURE__ */ l(i, {
|
|
43
45
|
title: "Team Members",
|
|
44
46
|
description: "Manage team members and roles",
|
|
45
|
-
|
|
47
|
+
purpose: "Understand how effort is distributed across your team and projects — use it to spot who's overloaded, where time is concentrated, and when to invite or reassign people.",
|
|
48
|
+
actions: /* @__PURE__ */ l("button", {
|
|
46
49
|
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",
|
|
47
|
-
children: [/* @__PURE__ */
|
|
50
|
+
children: [/* @__PURE__ */ c(o, { className: "h-4 w-4" }), "Invite Member"]
|
|
48
51
|
}),
|
|
49
|
-
children: [/* @__PURE__ */
|
|
52
|
+
children: [/* @__PURE__ */ c("div", {
|
|
50
53
|
className: "mb-4 rounded-lg border border-status-warning-bg/40 bg-status-warning-bg p-3 text-xs text-status-warning-text",
|
|
51
54
|
children: "Team member data is managed by the groups module. Showing time entry activity by project."
|
|
52
|
-
}), /* @__PURE__ */
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
}), g.length === 0 ? /* @__PURE__ */ c(a, {
|
|
56
|
+
illustration: "empty-people",
|
|
57
|
+
title: "No time entry data available",
|
|
58
|
+
description: "Once your team starts tracking time, their activity by project shows up here."
|
|
59
|
+
}) : /* @__PURE__ */ c(u, {
|
|
60
|
+
treatment: "glass",
|
|
61
|
+
glow: !0,
|
|
62
|
+
className: "overflow-hidden",
|
|
63
|
+
children: /* @__PURE__ */ c("div", {
|
|
55
64
|
className: "divide-y divide-border",
|
|
56
|
-
children:
|
|
57
|
-
className: "p-8 text-center text-sm text-text-secondary",
|
|
58
|
-
children: "No time entry data available."
|
|
59
|
-
}) : m.map((e) => /* @__PURE__ */ c("div", {
|
|
65
|
+
children: g.map((e) => /* @__PURE__ */ l("div", {
|
|
60
66
|
className: "flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors",
|
|
61
|
-
children: [/* @__PURE__ */
|
|
67
|
+
children: [/* @__PURE__ */ l("div", {
|
|
62
68
|
className: "flex items-center gap-3",
|
|
63
|
-
children: [/* @__PURE__ */
|
|
69
|
+
children: [/* @__PURE__ */ c("div", {
|
|
64
70
|
className: "flex h-9 w-9 items-center justify-center rounded-full bg-action-primary-bg/10 text-sm font-medium text-primary",
|
|
65
|
-
children: /* @__PURE__ */ s
|
|
66
|
-
}), /* @__PURE__ */
|
|
71
|
+
children: /* @__PURE__ */ c(s, { className: "h-4 w-4" })
|
|
72
|
+
}), /* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("p", {
|
|
67
73
|
className: "text-sm font-medium text-text-primary",
|
|
68
74
|
children: e.name
|
|
69
|
-
}), /* @__PURE__ */
|
|
75
|
+
}), /* @__PURE__ */ l("p", {
|
|
70
76
|
className: "text-xs text-text-secondary",
|
|
71
77
|
children: [e.entries, " time entries"]
|
|
72
78
|
})] })]
|
|
73
|
-
}), /* @__PURE__ */
|
|
79
|
+
}), /* @__PURE__ */ l("div", {
|
|
74
80
|
className: "flex items-center gap-4",
|
|
75
|
-
children: [/* @__PURE__ */
|
|
81
|
+
children: [/* @__PURE__ */ l("div", {
|
|
76
82
|
className: "text-right",
|
|
77
|
-
children: [/* @__PURE__ */
|
|
83
|
+
children: [/* @__PURE__ */ c("p", {
|
|
78
84
|
className: "text-sm font-medium text-text-primary",
|
|
79
85
|
children: t(e.totalHours)
|
|
80
|
-
}), /* @__PURE__ */
|
|
86
|
+
}), /* @__PURE__ */ c("p", {
|
|
81
87
|
className: "text-xs text-text-secondary",
|
|
82
88
|
children: "total tracked"
|
|
83
89
|
})]
|
|
84
|
-
}), /* @__PURE__ */
|
|
90
|
+
}), /* @__PURE__ */ c("span", {
|
|
85
91
|
className: "inline-flex rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text",
|
|
86
92
|
children: "active"
|
|
87
93
|
})]
|
|
@@ -92,6 +98,6 @@ function l() {
|
|
|
92
98
|
});
|
|
93
99
|
}
|
|
94
100
|
//#endregion
|
|
95
|
-
export {
|
|
101
|
+
export { d as TeamMembersPage };
|
|
96
102
|
|
|
97
103
|
//# sourceMappingURL=TeamMembersPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamMembersPage.js","names":[],"sources":["../../../../src/timecampus/pages/team/TeamMembersPage.tsx"],"sourcesContent":["import { Users, Plus } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { formatHours } from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListTimeEntriesQuery,\n type TimeEntryFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function TeamMembersPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const { data, loading, error } = useListTimeEntriesQuery({\n variables: { limit: 100 },\n skip: !workspaceId,\n });\n\n const entries = data?.timeEntries?.items ?? [];\n\n // Group time entries by project to show team activity (member data comes from groups module)\n const projectSummary = entries.reduce<\n Record<string, { name: string; totalHours: number; entries: number }>\n >(\n (\n acc: Record<\n string,\n { name: string; totalHours: number; entries: number }\n >,\n entry: TimeEntryFieldsFragment,\n ) => {\n const projectName = entry.project?.name ?? \"Unassigned\";\n const projectId = entry.projectId ?? \"unassigned\";\n if (!acc[projectId]) {\n acc[projectId] = { name: projectName, totalHours: 0, entries: 0 };\n }\n acc[projectId].totalHours += (entry.duration ?? 0) / 3600;\n acc[projectId].entries += 1;\n return acc;\n },\n {},\n );\n\n const summaryList = Object.entries(projectSummary)\n .map(([id, info]) => ({\n id,\n ...(info as { name: string; totalHours: number; entries: number }),\n }))\n .sort((a, b) => b.totalHours - a.totalHours);\n\n if (loading) {\n return (\n <PageLayout\n title=\"Team Members\"\n description=\"Manage team members and roles\"\n actions={\n <button className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\">\n <Plus className=\"h-4 w-4\" />\n Invite Member\n </button>\n }\n >\n <LoadingSkeleton variant=\"list\" count={5} />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"Team Members\"\n description=\"Manage team members and roles\"\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 return (\n <PageLayout\n title=\"Team Members\"\n description=\"Manage team members and roles\"\n actions={\n <button className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\">\n <Plus className=\"h-4 w-4\" />\n Invite Member\n </button>\n }\n >\n <div className=\"mb-4 rounded-lg border border-status-warning-bg/40 bg-status-warning-bg p-3 text-xs text-status-warning-text\">\n Team member data is managed by the groups module. Showing time entry\n activity by project.\n </div>\n
|
|
1
|
+
{"version":3,"file":"TeamMembersPage.js","names":[],"sources":["../../../../src/timecampus/pages/team/TeamMembersPage.tsx"],"sourcesContent":["import { Users, Plus } 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 { formatHours } from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListTimeEntriesQuery,\n type TimeEntryFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function TeamMembersPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const { data, loading, error } = useListTimeEntriesQuery({\n variables: { limit: 100 },\n skip: !workspaceId,\n });\n\n const entries = data?.timeEntries?.items ?? [];\n\n // Group time entries by project to show team activity (member data comes from groups module)\n const projectSummary = entries.reduce<\n Record<string, { name: string; totalHours: number; entries: number }>\n >(\n (\n acc: Record<\n string,\n { name: string; totalHours: number; entries: number }\n >,\n entry: TimeEntryFieldsFragment,\n ) => {\n const projectName = entry.project?.name ?? \"Unassigned\";\n const projectId = entry.projectId ?? \"unassigned\";\n if (!acc[projectId]) {\n acc[projectId] = { name: projectName, totalHours: 0, entries: 0 };\n }\n acc[projectId].totalHours += (entry.duration ?? 0) / 3600;\n acc[projectId].entries += 1;\n return acc;\n },\n {},\n );\n\n const summaryList = Object.entries(projectSummary)\n .map(([id, info]) => ({\n id,\n ...(info as { name: string; totalHours: number; entries: number }),\n }))\n .sort((a, b) => b.totalHours - a.totalHours);\n\n if (loading) {\n return (\n <PageLayout\n title=\"Team Members\"\n description=\"Manage team members and roles\"\n actions={\n <button className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\">\n <Plus className=\"h-4 w-4\" />\n Invite Member\n </button>\n }\n >\n <LoadingSkeleton variant=\"list\" count={5} />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"Team Members\"\n description=\"Manage team members and roles\"\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 return (\n <PageLayout\n title=\"Team Members\"\n description=\"Manage team members and roles\"\n purpose=\"Understand how effort is distributed across your team and projects — use it to spot who's overloaded, where time is concentrated, and when to invite or reassign people.\"\n actions={\n <button className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\">\n <Plus className=\"h-4 w-4\" />\n Invite Member\n </button>\n }\n >\n <div className=\"mb-4 rounded-lg border border-status-warning-bg/40 bg-status-warning-bg p-3 text-xs text-status-warning-text\">\n Team member data is managed by the groups module. Showing time entry\n activity by project.\n </div>\n {summaryList.length === 0 ? (\n <EmptyState\n illustration=\"empty-people\"\n title=\"No time entry data available\"\n description=\"Once your team starts tracking time, their activity by project shows up here.\"\n />\n ) : (\n <GlassCard treatment=\"glass\" glow className=\"overflow-hidden\">\n <div className=\"divide-y divide-border\">\n {summaryList.map((project) => (\n <div\n key={project.id}\n className=\"flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-9 w-9 items-center justify-center rounded-full bg-action-primary-bg/10 text-sm font-medium text-primary\">\n <Users className=\"h-4 w-4\" />\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {project.name}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {project.entries} time entries\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-4\">\n <div className=\"text-right\">\n <p className=\"text-sm font-medium text-text-primary\">\n {formatHours(project.totalHours)}\n </p>\n <p className=\"text-xs text-text-secondary\">total tracked</p>\n </div>\n <span className=\"inline-flex rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text\">\n active\n </span>\n </div>\n </div>\n ))}\n </div>\n </GlassCard>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;AAYA,SAAgB,IAAkB;CAChC,IAAM,EAAE,iBAAc,OAAO,GAAe,EAEtC,EAAE,SAAM,YAAS,aAAU,EAAwB;EACvD,WAAW,EAAE,OAAO,KAAK;EACzB,MAAM,CAAC;EACR,CAAC,EAKI,KAHU,GAAM,aAAa,SAAS,EAAE,EAGf,QAI3B,GAIA,MACG;EACH,IAAM,IAAc,EAAM,SAAS,QAAQ,cACrC,IAAY,EAAM,aAAa;AAMrC,SALK,EAAI,OACP,EAAI,KAAa;GAAE,MAAM;GAAa,YAAY;GAAG,SAAS;GAAG,GAEnE,EAAI,GAAW,eAAe,EAAM,YAAY,KAAK,MACrD,EAAI,GAAW,WAAW,GACnB;IAET,EAAE,CACH,EAEK,IAAc,OAAO,QAAQ,EAAe,CAC/C,KAAK,CAAC,GAAI,QAAW;EACpB;EACA,GAAI;EACL,EAAE,CACF,MAAM,GAAG,MAAM,EAAE,aAAa,EAAE,WAAW;AAgC9C,QA9BI,IAEA,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,gBAErB;;YAGX,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA;EACjC,CAAA,GAIb,IAEA,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,OAAD;GAAK,WAAU;aAAoG;GAE7G,CAAA;EACK,CAAA,GAKf,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SAAQ;EACR,SACE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,gBAErB;;YARb,CAWE,kBAAC,OAAD;GAAK,WAAU;aAA+G;GAGxH,CAAA,EACL,EAAY,WAAW,IACtB,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,aAAY;GACZ,CAAA,GAEF,kBAAC,GAAD;GAAW,WAAU;GAAQ,MAAA;GAAK,WAAU;aAC1C,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAY,KAAK,MAChB,kBAAC,OAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;OACzB,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAQ;OACP,CAAA,EACJ,kBAAC,KAAD;OAAG,WAAU;iBAAb,CACG,EAAQ,SAAQ,gBACf;SACA,EAAA,CAAA,CACF;SACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAY,EAAQ,WAAW;QAC9B,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBAA8B;QAAiB,CAAA,CACxD;UACN,kBAAC,QAAD;OAAM,WAAU;iBAAyG;OAElH,CAAA,CACH;QACF;OA3BC,EAAQ,GA2BT,CACN;IACE,CAAA;GACI,CAAA,CAEH"}
|