@burdenoff/microfe-timecampus 2026.624.1 → 2026.625.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/timecampus/components/ErrorBoundary.js +2 -2
- package/dist/timecampus/components/ErrorBoundary.js.map +1 -1
- package/dist/timecampus/components/LoadingSkeleton.js +1 -1
- package/dist/timecampus/components/LoadingSkeleton.js.map +1 -1
- package/dist/timecampus/components/PageLayout.js +17 -13
- package/dist/timecampus/components/PageLayout.js.map +1 -1
- package/dist/timecampus/components/StatCard.js +19 -16
- package/dist/timecampus/components/StatCard.js.map +1 -1
- package/dist/timecampus/pages/HomePage.js +169 -149
- package/dist/timecampus/pages/HomePage.js.map +1 -1
- package/dist/timecampus/pages/applets/AppletStorePage.js +11 -11
- package/dist/timecampus/pages/applets/AppletStorePage.js.map +1 -1
- package/dist/timecampus/pages/applets/InstalledAppletsPage.js +11 -11
- package/dist/timecampus/pages/applets/InstalledAppletsPage.js.map +1 -1
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +19 -19
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
- package/dist/timecampus/pages/application-usage/ApplicationUsageSettingsPage.js +10 -10
- package/dist/timecampus/pages/application-usage/ApplicationUsageSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/channels/ChannelsPage.js +11 -11
- package/dist/timecampus/pages/channels/ChannelsPage.js.map +1 -1
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js +15 -15
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
- package/dist/timecampus/pages/dashboard/AnalyticsPage.js +11 -11
- package/dist/timecampus/pages/dashboard/AnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/dashboard/ReportsPage.js +11 -11
- package/dist/timecampus/pages/dashboard/ReportsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailAnalyticsPage.js +14 -14
- package/dist/timecampus/pages/email-tracking/EmailAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailSettingsPage.js +10 -10
- package/dist/timecampus/pages/email-tracking/EmailSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +7 -7
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +15 -15
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/focus-score/FocusScorePage.js +21 -21
- package/dist/timecampus/pages/focus-score/FocusScorePage.js.map +1 -1
- package/dist/timecampus/pages/focus-score/FocusScoreSettingsPage.js +10 -10
- package/dist/timecampus/pages/focus-score/FocusScoreSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/geofencing/GeofencingPage.js +10 -10
- package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
- package/dist/timecampus/pages/geofencing/GeofencingSettingsPage.js +10 -10
- package/dist/timecampus/pages/geofencing/GeofencingSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js +10 -10
- package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js.map +1 -1
- package/dist/timecampus/pages/location-tracking/LocationPrivacySettingsPage.js +10 -10
- package/dist/timecampus/pages/location-tracking/LocationPrivacySettingsPage.js.map +1 -1
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +15 -15
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingAnalyticsPage.js +15 -15
- package/dist/timecampus/pages/meetings/MeetingAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingCreatePage.js +5 -5
- package/dist/timecampus/pages/meetings/MeetingCreatePage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js +16 -16
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingsPage.js +10 -10
- package/dist/timecampus/pages/meetings/MeetingsPage.js.map +1 -1
- package/dist/timecampus/pages/pomodoro/PomodoroAnalyticsPage.js +17 -17
- package/dist/timecampus/pages/pomodoro/PomodoroAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/pomodoro/PomodoroPage.js +16 -16
- package/dist/timecampus/pages/pomodoro/PomodoroPage.js.map +1 -1
- package/dist/timecampus/pages/pomodoro/PomodoroSettingsPage.js +12 -12
- package/dist/timecampus/pages/pomodoro/PomodoroSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectCreatePage.js +6 -6
- package/dist/timecampus/pages/projects/ProjectCreatePage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectDetailPage.js +17 -17
- package/dist/timecampus/pages/projects/ProjectDetailPage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectsPage.js +10 -10
- package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
- package/dist/timecampus/pages/projects/TasksPage.js +10 -10
- package/dist/timecampus/pages/projects/TasksPage.js.map +1 -1
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +18 -18
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamAnalyticsPage.js +11 -11
- package/dist/timecampus/pages/team/TeamAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamMembersPage.js +12 -12
- package/dist/timecampus/pages/team/TeamMembersPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamPage.js +16 -16
- package/dist/timecampus/pages/team/TeamPage.js.map +1 -1
- package/dist/timecampus/pages/team-activity/TeamActivityPage.js +16 -16
- package/dist/timecampus/pages/team-activity/TeamActivityPage.js.map +1 -1
- package/dist/timecampus/pages/team-activity/TeamActivitySettingsPage.js +10 -10
- package/dist/timecampus/pages/team-activity/TeamActivitySettingsPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplateApplicationPage.js +11 -11
- package/dist/timecampus/pages/templates/TemplateApplicationPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplateDetailPage.js +12 -12
- package/dist/timecampus/pages/templates/TemplateDetailPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplatesPage.js +11 -11
- package/dist/timecampus/pages/templates/TemplatesPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeEntriesPage.js +10 -10
- package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeTrackingPage.js +17 -17
- package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimerPage.js +10 -10
- package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +20 -20
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
- package/dist/timecampus/pages/widgets/WidgetDetailPage.js +11 -11
- package/dist/timecampus/pages/widgets/WidgetDetailPage.js.map +1 -1
- package/dist/timecampus/pages/widgets/WidgetsPage.js +12 -12
- package/dist/timecampus/pages/widgets/WidgetsPage.js.map +1 -1
- package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js +16 -16
- package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js.map +1 -1
- package/dist/timecampus/pages/work-patterns/WorkPatternsSettingsPage.js +10 -10
- package/dist/timecampus/pages/work-patterns/WorkPatternsSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/workflows/WorkflowsPage.js +10 -10
- package/dist/timecampus/pages/workflows/WorkflowsPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { formatHours as e, getStatusColor as t } from "../../utils/helpers.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { LoadingSkeleton as n } from "../../components/LoadingSkeleton.js";
|
|
3
|
+
import { useListProjectsQuery as r } from "../../../generated/wspace-operations.js";
|
|
4
|
+
import { PageLayout as i } from "../../components/PageLayout.js";
|
|
5
5
|
import { FolderOpen as a, Plus as o } from "lucide-react";
|
|
6
6
|
import { jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
7
7
|
import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
@@ -10,22 +10,22 @@ function u() {
|
|
|
10
10
|
let { t: u } = l(), d = (e, t) => {
|
|
11
11
|
let n = u(e);
|
|
12
12
|
return n === e ? t : n;
|
|
13
|
-
}, { data: f, loading: p, error: m } =
|
|
14
|
-
return m ? /* @__PURE__ */ s(
|
|
13
|
+
}, { data: f, loading: p, error: m } = r({ variables: { limit: 50 } }), h = f?.projects?.items ?? [];
|
|
14
|
+
return m ? /* @__PURE__ */ s(i, {
|
|
15
15
|
title: d("timecampus.projects.title", "Projects"),
|
|
16
16
|
description: d("timecampus.projects.description", "Manage and track project time"),
|
|
17
17
|
children: /* @__PURE__ */ s("div", {
|
|
18
|
-
className: "rounded-xl border border-
|
|
18
|
+
className: "rounded-xl border border-status-error-bg/40 bg-status-error-bg p-6 text-sm text-status-error-text",
|
|
19
19
|
children: d("timecampus.projects.error.loadFailed", "Failed to load projects. Please try again later.")
|
|
20
20
|
})
|
|
21
|
-
}) : /* @__PURE__ */ s(
|
|
21
|
+
}) : /* @__PURE__ */ s(i, {
|
|
22
22
|
title: d("timecampus.projects.title", "Projects"),
|
|
23
23
|
description: d("timecampus.projects.description", "Manage and track project time"),
|
|
24
24
|
actions: /* @__PURE__ */ c("button", {
|
|
25
25
|
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",
|
|
26
26
|
children: [/* @__PURE__ */ s(o, { className: "h-4 w-4" }), d("timecampus.projects.actions.newProject", "New Project")]
|
|
27
27
|
}),
|
|
28
|
-
children: p ? /* @__PURE__ */ s(
|
|
28
|
+
children: p ? /* @__PURE__ */ s(n, {
|
|
29
29
|
variant: "card",
|
|
30
30
|
count: 4
|
|
31
31
|
}) : h.length === 0 ? /* @__PURE__ */ c("div", {
|
|
@@ -37,7 +37,7 @@ function u() {
|
|
|
37
37
|
}) : /* @__PURE__ */ s("div", {
|
|
38
38
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
39
39
|
children: h.map((n) => /* @__PURE__ */ c("div", {
|
|
40
|
-
className: "rounded-xl border border-border-
|
|
40
|
+
className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop p-5 transition-colors hover:bg-bg-sunken/50 cursor-pointer",
|
|
41
41
|
children: [
|
|
42
42
|
/* @__PURE__ */ c("div", {
|
|
43
43
|
className: "flex items-start justify-between",
|
|
@@ -45,7 +45,7 @@ function u() {
|
|
|
45
45
|
className: "flex items-center gap-3",
|
|
46
46
|
children: [/* @__PURE__ */ s("div", {
|
|
47
47
|
className: "h-3 w-3 rounded-full",
|
|
48
|
-
style: { backgroundColor: n.color ?? "
|
|
48
|
+
style: { backgroundColor: n.color ?? "var(--color-accent-blue)" }
|
|
49
49
|
}), /* @__PURE__ */ c("div", { children: [/* @__PURE__ */ s("p", {
|
|
50
50
|
className: "text-sm font-semibold text-text-primary",
|
|
51
51
|
children: n.name
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectsPage.js","names":[],"sources":["../../../../src/timecampus/pages/projects/ProjectsPage.tsx"],"sourcesContent":["import { FolderOpen, Plus } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { getStatusColor, formatHours } from \"../../utils/helpers\";\nimport {\n useListProjectsQuery,\n type ListProjectsQuery,\n} from \"@/generated/wspace-operations\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\n\ntype ProjectItem = NonNullable<ListProjectsQuery[\"projects\"]>[\"items\"][number];\n\nexport function ProjectsPage() {\n const { t: translate } = useI18n();\n const t = (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n const { data, loading, error } = useListProjectsQuery({\n variables: { limit: 50 },\n });\n\n const projects = data?.projects?.items ?? [];\n\n if (error) {\n return (\n <PageLayout\n title={t(\"timecampus.projects.title\", \"Projects\")}\n description={t(\n \"timecampus.projects.description\",\n \"Manage and track project time\",\n )}\n >\n <div className=\"rounded-xl border border-
|
|
1
|
+
{"version":3,"file":"ProjectsPage.js","names":[],"sources":["../../../../src/timecampus/pages/projects/ProjectsPage.tsx"],"sourcesContent":["import { FolderOpen, Plus } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { getStatusColor, formatHours } from \"../../utils/helpers\";\nimport {\n useListProjectsQuery,\n type ListProjectsQuery,\n} from \"@/generated/wspace-operations\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\n\ntype ProjectItem = NonNullable<ListProjectsQuery[\"projects\"]>[\"items\"][number];\n\nexport function ProjectsPage() {\n const { t: translate } = useI18n();\n const t = (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n const { data, loading, error } = useListProjectsQuery({\n variables: { limit: 50 },\n });\n\n const projects = data?.projects?.items ?? [];\n\n if (error) {\n return (\n <PageLayout\n title={t(\"timecampus.projects.title\", \"Projects\")}\n description={t(\n \"timecampus.projects.description\",\n \"Manage and track project time\",\n )}\n >\n <div className=\"rounded-xl border border-status-error-bg/40 bg-status-error-bg p-6 text-sm text-status-error-text\">\n {t(\n \"timecampus.projects.error.loadFailed\",\n \"Failed to load projects. Please try again later.\",\n )}\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={t(\"timecampus.projects.title\", \"Projects\")}\n description={t(\n \"timecampus.projects.description\",\n \"Manage and track project time\",\n )}\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 {t(\"timecampus.projects.actions.newProject\", \"New Project\")}\n </button>\n }\n >\n {loading ? (\n <LoadingSkeleton variant=\"card\" count={4} />\n ) : projects.length === 0 ? (\n <div className=\"rounded-xl border border-dashed border-border-subtle bg-bg-surface p-12 text-center\">\n <FolderOpen className=\"mx-auto h-10 w-10 text-text-secondary\" />\n <p className=\"mt-4 text-sm text-text-secondary\">\n {t(\n \"timecampus.projects.empty\",\n \"No projects yet. Create your first project to get started.\",\n )}\n </p>\n </div>\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {projects.map((project: ProjectItem) => (\n <div\n key={project.id}\n className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop p-5 transition-colors hover:bg-bg-sunken/50 cursor-pointer\"\n >\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-center gap-3\">\n <div\n className=\"h-3 w-3 rounded-full\"\n style={{\n backgroundColor:\n project.color ?? \"var(--color-accent-blue)\",\n }}\n />\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">\n {project.name}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {project.clientId ??\n t(\"timecampus.projects.card.noClient\", \"No client\")}\n </p>\n </div>\n </div>\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getStatusColor(project.status)}`}\n >\n {project.status}\n </span>\n </div>\n <div className=\"mt-4\">\n <div className=\"flex items-center justify-between text-xs text-text-secondary\">\n <span>\n {t(\"timecampus.projects.card.progress\", \"Progress\")}\n </span>\n <span>\n {project.estimatedHours\n ? Math.round(\n (project.trackedHours / project.estimatedHours) * 100,\n )\n : 0}\n %\n </span>\n </div>\n <div className=\"mt-1 h-2 rounded-full bg-bg-sunken\">\n <div\n className=\"h-2 rounded-full bg-action-primary-bg\"\n style={{\n width: `${project.estimatedHours ? Math.min(Math.round((project.trackedHours / project.estimatedHours) * 100), 100) : 0}%`,\n }}\n />\n </div>\n </div>\n <div className=\"mt-3 flex items-center justify-between text-xs text-text-secondary\">\n <span>\n {formatHours(project.trackedHours)} /{\" \"}\n {formatHours(project.estimatedHours ?? 0)}\n </span>\n <span>\n {project.tasks?.length ?? 0}{\" \"}\n {t(\"timecampus.projects.card.tasks\", \"tasks\")}\n </span>\n </div>\n </div>\n ))}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;AAYA,SAAgB,IAAe;CAC7B,IAAM,EAAE,GAAG,MAAc,GAAS,EAC5B,KAAK,GAAa,MAA6B;EACnD,IAAM,IAAQ,EAAU,EAAI;AAC5B,SAAO,MAAU,IAAM,IAAW;IAE9B,EAAE,SAAM,YAAS,aAAU,EAAqB,EACpD,WAAW,EAAE,OAAO,IAAI,EACzB,CAAC,EAEI,IAAW,GAAM,UAAU,SAAS,EAAE;AAqB5C,QAnBI,IAEA,kBAAC,GAAD;EACE,OAAO,EAAE,6BAA6B,WAAW;EACjD,aAAa,EACX,mCACA,gCACD;YAED,kBAAC,OAAD;GAAK,WAAU;aACZ,EACC,wCACA,mDACD;GACG,CAAA;EACK,CAAA,GAKf,kBAAC,GAAD;EACE,OAAO,EAAE,6BAA6B,WAAW;EACjD,aAAa,EACX,mCACA,gCACD;EACD,SACE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAC3B,EAAE,0CAA0C,cAAc,CACpD;;YAGV,IACC,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA,GAC1C,EAAS,WAAW,IACtB,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD,EAAY,WAAU,yCAA0C,CAAA,EAChE,kBAAC,KAAD;IAAG,WAAU;cACV,EACC,6BACA,6DACD;IACC,CAAA,CACA;OAEN,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAS,KAAK,MACb,kBAAC,OAAD;IAEE,WAAU;cAFZ;KAIE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QACE,WAAU;QACV,OAAO,EACL,iBACE,EAAQ,SAAS,4BACpB;QACD,CAAA,EACF,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAQ;QACP,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAQ,YACP,EAAE,qCAAqC,YAAY;QACnD,CAAA,CACA,EAAA,CAAA,CACF;UACN,kBAAC,QAAD;OACE,WAAW,4DAA4D,EAAe,EAAQ,OAAO;iBAEpG,EAAQ;OACJ,CAAA,CACH;;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD,EAAA,UACG,EAAE,qCAAqC,WAAW,EAC9C,CAAA,EACP,kBAAC,QAAD,EAAA,UAAA,CACG,EAAQ,iBACL,KAAK,MACF,EAAQ,eAAe,EAAQ,iBAAkB,IACnD,GACD,GAAE,IAED,EAAA,CAAA,CACH;UACN,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,OAAD;QACE,WAAU;QACV,OAAO,EACL,OAAO,GAAG,EAAQ,iBAAiB,KAAK,IAAI,KAAK,MAAO,EAAQ,eAAe,EAAQ,iBAAkB,IAAI,EAAE,IAAI,GAAG,EAAE,IACzH;QACD,CAAA;OACE,CAAA,CACF;;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,QAAD,EAAA,UAAA;OACG,EAAY,EAAQ,aAAa;OAAC;OAAG;OACrC,EAAY,EAAQ,kBAAkB,EAAE;OACpC,EAAA,CAAA,EACP,kBAAC,QAAD,EAAA,UAAA;OACG,EAAQ,OAAO,UAAU;OAAG;OAC5B,EAAE,kCAAkC,QAAQ;OACxC,EAAA,CAAA,CACH;;KACF;MA7DC,EAAQ,GA6DT,CACN;GACE,CAAA;EAEG,CAAA"}
|
|
@@ -1,42 +1,42 @@
|
|
|
1
1
|
import { getPriorityColor as e, getStatusColor as t } from "../../utils/helpers.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { LoadingSkeleton as n } from "../../components/LoadingSkeleton.js";
|
|
3
|
+
import { useListTasksQuery as r } from "../../../generated/wspace-operations.js";
|
|
4
|
+
import { PageLayout as i } from "../../components/PageLayout.js";
|
|
5
5
|
import { CheckSquare as a, Plus as o } from "lucide-react";
|
|
6
6
|
import { useParams as s } from "react-router-dom";
|
|
7
7
|
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
8
8
|
//#region src/timecampus/pages/projects/TasksPage.tsx
|
|
9
9
|
function u() {
|
|
10
|
-
let { projectId: u } = s(), { data: d, loading: f, error: p } =
|
|
10
|
+
let { projectId: u } = s(), { data: d, loading: f, error: p } = r({
|
|
11
11
|
variables: { projectId: u },
|
|
12
12
|
skip: !u
|
|
13
13
|
}), m = d?.tasks?.items ?? [];
|
|
14
|
-
return p ? /* @__PURE__ */ c(
|
|
14
|
+
return p ? /* @__PURE__ */ c(i, {
|
|
15
15
|
title: "Tasks",
|
|
16
16
|
description: `Tasks for project ${u}`,
|
|
17
17
|
children: /* @__PURE__ */ c("div", {
|
|
18
|
-
className: "rounded-xl border border-
|
|
18
|
+
className: "rounded-xl border border-status-error-bg/40 bg-status-error-bg p-6 text-sm text-status-error-text",
|
|
19
19
|
children: "Failed to load tasks. Please try again later."
|
|
20
20
|
})
|
|
21
|
-
}) : /* @__PURE__ */ c(
|
|
21
|
+
}) : /* @__PURE__ */ c(i, {
|
|
22
22
|
title: "Tasks",
|
|
23
23
|
description: `Tasks for project ${u}`,
|
|
24
24
|
actions: /* @__PURE__ */ l("button", {
|
|
25
25
|
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",
|
|
26
26
|
children: [/* @__PURE__ */ c(o, { className: "h-4 w-4" }), "Add Task"]
|
|
27
27
|
}),
|
|
28
|
-
children: f ? /* @__PURE__ */ c(
|
|
28
|
+
children: f ? /* @__PURE__ */ c(n, {
|
|
29
29
|
variant: "list",
|
|
30
30
|
count: 5
|
|
31
31
|
}) : /* @__PURE__ */ c("div", {
|
|
32
|
-
className: "rounded-xl border border-border-
|
|
32
|
+
className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop",
|
|
33
33
|
children: /* @__PURE__ */ l("div", {
|
|
34
34
|
className: "divide-y divide-border",
|
|
35
35
|
children: [m.map((n) => /* @__PURE__ */ l("div", {
|
|
36
36
|
className: "flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors",
|
|
37
37
|
children: [/* @__PURE__ */ l("div", {
|
|
38
38
|
className: "flex items-center gap-3",
|
|
39
|
-
children: [/* @__PURE__ */ c(a, { className: `h-5 w-5 ${n.status === "done" ? "text-
|
|
39
|
+
children: [/* @__PURE__ */ c(a, { className: `h-5 w-5 ${n.status === "done" ? "text-status-success-text" : "text-text-secondary"}` }), /* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("p", {
|
|
40
40
|
className: `text-sm font-medium ${n.status === "done" ? "text-text-secondary line-through" : "text-text-primary"}`,
|
|
41
41
|
children: n.title
|
|
42
42
|
}), /* @__PURE__ */ l("p", {
|
|
@@ -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-
|
|
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 <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop\">\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 {tasks.length === 0 && (\n <div className=\"p-8 text-center text-sm text-text-secondary\">\n No tasks yet. Add a task to get started.\n </div>\n )}\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;AAUA,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,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,GAE5C,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,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,EACD,EAAM,WAAW,KAChB,kBAAC,OAAD;KAAK,WAAU;eAA8C;KAEvD,CAAA,CAEJ;;GACF,CAAA;EAEG,CAAA"}
|
|
@@ -1,60 +1,60 @@
|
|
|
1
1
|
import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
|
|
2
2
|
import { getInitials as t } from "../../utils/helpers.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { StatCard as n } from "../../components/StatCard.js";
|
|
4
|
+
import { LoadingSkeleton as r } from "../../components/LoadingSkeleton.js";
|
|
5
|
+
import { useGetCapacityOverviewQuery as i, useListResourcePlansQuery as a } from "../../../generated/wspace-operations.js";
|
|
6
|
+
import { PageLayout as o } from "../../components/PageLayout.js";
|
|
7
7
|
import { CalendarRange as s, TrendingUp as c, Users as l } from "lucide-react";
|
|
8
8
|
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
9
9
|
//#region src/timecampus/pages/resource-planning/ResourcePlanningPage.tsx
|
|
10
10
|
function f() {
|
|
11
|
-
let { workspaceId: f = "" } = e(), p = /* @__PURE__ */ new Date(), m = new Date(p.getFullYear(), p.getMonth(), p.getDate()).toISOString(), h = new Date(p.getFullYear(), p.getMonth(), p.getDate() + 7).toISOString(), { data: g, loading: _, error: v } =
|
|
11
|
+
let { workspaceId: f = "" } = e(), p = /* @__PURE__ */ new Date(), m = new Date(p.getFullYear(), p.getMonth(), p.getDate()).toISOString(), h = new Date(p.getFullYear(), p.getMonth(), p.getDate() + 7).toISOString(), { data: g, loading: _, error: v } = a(), { data: y } = i({ variables: {
|
|
12
12
|
startDate: m,
|
|
13
13
|
endDate: h
|
|
14
14
|
} }), b = g?.resourcePlans?.items ?? [], x = y?.capacityOverview, S = b.flatMap((e) => e.allocations.map((t) => ({
|
|
15
15
|
...t,
|
|
16
16
|
planName: e.name
|
|
17
17
|
})));
|
|
18
|
-
if (_) return /* @__PURE__ */ u(
|
|
18
|
+
if (_) return /* @__PURE__ */ u(o, {
|
|
19
19
|
title: "Resource Planning",
|
|
20
20
|
description: "Plan and optimize team resource allocation",
|
|
21
|
-
children: /* @__PURE__ */ u(
|
|
21
|
+
children: /* @__PURE__ */ u(r, {
|
|
22
22
|
variant: "list",
|
|
23
23
|
count: 5
|
|
24
24
|
})
|
|
25
25
|
});
|
|
26
|
-
if (v) return /* @__PURE__ */ u(
|
|
26
|
+
if (v) return /* @__PURE__ */ u(o, {
|
|
27
27
|
title: "Resource Planning",
|
|
28
28
|
description: "Plan and optimize team resource allocation",
|
|
29
29
|
children: /* @__PURE__ */ u("div", {
|
|
30
|
-
className: "rounded-lg border border-
|
|
30
|
+
className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
|
|
31
31
|
children: "Failed to load resource planning data. Please try again later."
|
|
32
32
|
})
|
|
33
33
|
});
|
|
34
34
|
let C = x?.utilizationRate ?? 0, w = x?.overallocatedUsers?.length ?? 0, T = x?.availableCapacity ?? 0;
|
|
35
|
-
return /* @__PURE__ */ d(
|
|
35
|
+
return /* @__PURE__ */ d(o, {
|
|
36
36
|
title: "Resource Planning",
|
|
37
37
|
description: "Plan and optimize team resource allocation",
|
|
38
38
|
children: [/* @__PURE__ */ d("div", {
|
|
39
39
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
|
|
40
40
|
children: [
|
|
41
|
-
/* @__PURE__ */ u(
|
|
41
|
+
/* @__PURE__ */ u(n, {
|
|
42
42
|
title: "Team Utilization",
|
|
43
43
|
value: `${Math.round(C)}%`,
|
|
44
44
|
icon: /* @__PURE__ */ u(c, { className: "h-5 w-5" })
|
|
45
45
|
}),
|
|
46
|
-
/* @__PURE__ */ u(
|
|
46
|
+
/* @__PURE__ */ u(n, {
|
|
47
47
|
title: "Over-allocated",
|
|
48
48
|
value: String(w),
|
|
49
49
|
icon: /* @__PURE__ */ u(l, { className: "h-5 w-5" }),
|
|
50
|
-
iconBgColor: "bg-
|
|
50
|
+
iconBgColor: "bg-status-error-bg text-status-error-text"
|
|
51
51
|
}),
|
|
52
|
-
/* @__PURE__ */ u(
|
|
52
|
+
/* @__PURE__ */ u(n, {
|
|
53
53
|
title: "Active Plans",
|
|
54
54
|
value: String(b.length),
|
|
55
55
|
icon: /* @__PURE__ */ u(l, { className: "h-5 w-5" })
|
|
56
56
|
}),
|
|
57
|
-
/* @__PURE__ */ u(
|
|
57
|
+
/* @__PURE__ */ u(n, {
|
|
58
58
|
title: "Available Capacity",
|
|
59
59
|
value: `${Math.round(T)}h`,
|
|
60
60
|
icon: /* @__PURE__ */ u(s, { className: "h-5 w-5" }),
|
|
@@ -62,7 +62,7 @@ function f() {
|
|
|
62
62
|
})
|
|
63
63
|
]
|
|
64
64
|
}), /* @__PURE__ */ u("div", {
|
|
65
|
-
className: "rounded-xl border border-border-
|
|
65
|
+
className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop",
|
|
66
66
|
children: /* @__PURE__ */ u("div", {
|
|
67
67
|
className: "divide-y divide-border",
|
|
68
68
|
children: S.length === 0 ? /* @__PURE__ */ u("div", {
|
|
@@ -105,13 +105,13 @@ function f() {
|
|
|
105
105
|
children: [/* @__PURE__ */ u("div", {
|
|
106
106
|
className: "flex items-center justify-between text-xs mb-0.5",
|
|
107
107
|
children: /* @__PURE__ */ d("span", {
|
|
108
|
-
className: `font-medium ${n > 100 ? "text-
|
|
108
|
+
className: `font-medium ${n > 100 ? "text-status-error-text" : n < 70 ? "text-status-warning-text" : "text-status-success-text"}`,
|
|
109
109
|
children: [n, "%"]
|
|
110
110
|
})
|
|
111
111
|
}), /* @__PURE__ */ u("div", {
|
|
112
112
|
className: "h-2 rounded-full bg-bg-sunken",
|
|
113
113
|
children: /* @__PURE__ */ u("div", {
|
|
114
|
-
className: `h-2 rounded-full ${n > 100 ? "bg-
|
|
114
|
+
className: `h-2 rounded-full ${n > 100 ? "bg-status-error-text" : n < 70 ? "bg-status-warning-text" : "bg-status-success-text"}`,
|
|
115
115
|
style: { width: `${Math.min(n, 100)}%` }
|
|
116
116
|
})
|
|
117
117
|
})]
|
|
@@ -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-
|
|
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 <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop\">\n <div className=\"divide-y divide-border\">\n {allocations.length === 0 ? (\n <div className=\"p-8 text-center text-sm text-text-secondary\">\n No resource allocations found. Create a resource plan to start\n allocating team members.\n </div>\n ) : (\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 )}\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;AAaA,SAAgB,IAAuB;CACrC,IAAM,EAAE,iBAAc,OAAO,GAAe,EAEtC,oBAAM,IAAI,MAAM,EAChB,IAAY,IAAI,KACpB,EAAI,aAAa,EACjB,EAAI,UAAU,EACd,EAAI,SAAS,CACd,CAAC,aAAa,EACT,IAAU,IAAI,KAClB,EAAI,aAAa,EACjB,EAAI,UAAU,EACd,EAAI,SAAS,GAAG,EACjB,CAAC,aAAa,EAET,EACJ,MAAM,GACN,SAAS,GACT,OAAO,MACL,GAA2B,EACzB,EAAE,MAAM,MAAiB,EAA4B,EACzD,WAAW;EAAE;EAAW;EAAS,EAClC,CAAC,EAEI,IAAQ,GAAW,eAAe,SAAS,EAAE,EAC7C,IAAW,GAAc,kBAGzB,IAAc,EAAM,SAAS,MACjC,EAAK,YAAY,KAAK,OAA6C;EACjE,GAAG;EACH,UAAU,EAAK;EAChB,EAAE,CACJ;AAED,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,IAAkB,GAAU,mBAAmB,GAC/C,IAAqB,GAAU,oBAAoB,UAAU,GAC7D,IAAiB,GAAU,qBAAqB;AAEtD,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAFd,CAIE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,GAAG,KAAK,MAAM,EAAgB,CAAC;KACtC,MAAM,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;KACxC,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,OAAO,EAAmB;KACjC,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;KACnC,aAAY;KACZ,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,OAAO,EAAM,OAAO;KAC3B,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;KACnC,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,GAAG,KAAK,MAAM,EAAe,CAAC;KACrC,MAAM,kBAAC,GAAD,EAAe,WAAU,WAAY,CAAA;KAC3C,aAAY;KACZ,CAAA;IACE;MAEN,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAY,WAAW,IACtB,kBAAC,OAAD;KAAK,WAAU;eAA8C;KAGvD,CAAA,GAEN,EAAY,KAER,MACG;KACH,IAAM,IACJ,EAAM,oBACL,EAAM,iBAAiB,IACpB,KAAK,MACF,EAAM,cAAc,EAAM,iBAAkB,IAC9C,GACD;AACN,YACE,kBAAC,OAAD;MAEE,WAAU;gBAFZ,CAIE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACZ,EAAY,EAAM,OAAO;QACtB,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAM;QACL,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACG,EAAM;SAAS;SAAU;SACzB,EAAM,UAAU,MAAM,GAAG,GAAG;SAAC;SAAI;SACjC,EAAM,QAAQ,MAAM,GAAG,GAAG;SACzB;UACA,EAAA,CAAA,CACF;UACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,KAAD;SAAG,WAAU;mBAAb,CACG,EAAM,gBAAe,cACpB;;QACA,CAAA,EACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,QAAD;UACE,WAAW,eAAe,IAAc,MAAM,2BAA2B,IAAc,KAAK,6BAA6B;oBAD3H,CAGG,GAAY,IACR;;SACH,CAAA,EACN,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,OAAD;UACE,WAAW,oBAAoB,IAAc,MAAM,yBAAyB,IAAc,KAAK,2BAA2B;UAC1H,OAAO,EAAE,OAAO,GAAG,KAAK,IAAI,GAAa,IAAI,CAAC,IAAI;UAClD,CAAA;SACE,CAAA,CACF;UACF;SACF;QAxCC,EAAM,GAwCP;MAGX;IAEC,CAAA;GACF,CAAA,CACK"}
|
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { LoadingSkeleton as t } from "../../components/LoadingSkeleton.js";
|
|
3
|
+
import { useGetTimeEntryStatsQuery as n, useGetWorkPatternSummaryQuery as r } from "../../../generated/wspace-operations.js";
|
|
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
7
|
//#region src/timecampus/pages/team/TeamAnalyticsPage.tsx
|
|
8
8
|
function l() {
|
|
9
|
-
let { workspaceId: l = "" } = e(), { data: u, loading: d, error: f } =
|
|
10
|
-
return d || m ? /* @__PURE__ */ s(
|
|
9
|
+
let { workspaceId: l = "" } = e(), { data: u, loading: d, error: f } = n({ skip: !l }), { data: p, loading: m } = r({ skip: !l }), h = u?.timeEntryStats, g = p?.workPatternSummary;
|
|
10
|
+
return d || m ? /* @__PURE__ */ s(i, {
|
|
11
11
|
title: "Team Analytics",
|
|
12
12
|
description: "Team performance insights and metrics",
|
|
13
|
-
children: /* @__PURE__ */ s(
|
|
13
|
+
children: /* @__PURE__ */ s(t, {
|
|
14
14
|
variant: "card",
|
|
15
15
|
count: 4
|
|
16
16
|
})
|
|
17
|
-
}) : f ? /* @__PURE__ */ s(
|
|
17
|
+
}) : f ? /* @__PURE__ */ s(i, {
|
|
18
18
|
title: "Team Analytics",
|
|
19
19
|
description: "Team performance insights and metrics",
|
|
20
20
|
children: /* @__PURE__ */ s("div", {
|
|
21
|
-
className: "rounded-lg border border-
|
|
21
|
+
className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
|
|
22
22
|
children: "Failed to load team analytics. Please try again later."
|
|
23
23
|
})
|
|
24
|
-
}) : /* @__PURE__ */ s(
|
|
24
|
+
}) : /* @__PURE__ */ s(i, {
|
|
25
25
|
title: "Team Analytics",
|
|
26
26
|
description: "Team performance insights and metrics",
|
|
27
27
|
children: /* @__PURE__ */ c("div", {
|
|
28
28
|
className: "grid grid-cols-1 gap-6 lg:grid-cols-2",
|
|
29
29
|
children: [/* @__PURE__ */ c("div", {
|
|
30
|
-
className: "rounded-xl border border-border-
|
|
30
|
+
className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6",
|
|
31
31
|
children: [/* @__PURE__ */ c("div", {
|
|
32
32
|
className: "mb-4 flex items-center gap-2",
|
|
33
33
|
children: [/* @__PURE__ */ s(o, { className: "h-5 w-5 text-primary" }), /* @__PURE__ */ s("h3", {
|
|
@@ -48,7 +48,7 @@ function l() {
|
|
|
48
48
|
})
|
|
49
49
|
})]
|
|
50
50
|
}), /* @__PURE__ */ c("div", {
|
|
51
|
-
className: "rounded-xl border border-border-
|
|
51
|
+
className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6",
|
|
52
52
|
children: [/* @__PURE__ */ c("div", {
|
|
53
53
|
className: "mb-4 flex items-center gap-2",
|
|
54
54
|
children: [/* @__PURE__ */ s(a, { className: "h-5 w-5 text-primary" }), /* @__PURE__ */ s("h3", {
|
|
@@ -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-
|
|
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 <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop 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 </div>\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop 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":";;;;;;;AASA,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;YAEZ,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,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,CACF;OACN,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"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
|
|
2
2
|
import { formatHours as t } from "../../utils/helpers.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { LoadingSkeleton as n } from "../../components/LoadingSkeleton.js";
|
|
4
|
+
import { useListTimeEntriesQuery as r } from "../../../generated/wspace-operations.js";
|
|
5
|
+
import { PageLayout as i } from "../../components/PageLayout.js";
|
|
6
6
|
import { Plus as a, Users as o } from "lucide-react";
|
|
7
7
|
import { jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
8
8
|
//#region src/timecampus/pages/team/TeamMembersPage.tsx
|
|
9
9
|
function l() {
|
|
10
|
-
let { workspaceId: l = "" } = e(), { data: u, loading: d, error: f } =
|
|
10
|
+
let { workspaceId: l = "" } = e(), { data: u, loading: d, error: f } = r({
|
|
11
11
|
variables: { limit: 100 },
|
|
12
12
|
skip: !l
|
|
13
13
|
}), p = (u?.timeEntries?.items ?? []).reduce((e, t) => {
|
|
@@ -21,25 +21,25 @@ function l() {
|
|
|
21
21
|
id: e,
|
|
22
22
|
...t
|
|
23
23
|
})).sort((e, t) => t.totalHours - e.totalHours);
|
|
24
|
-
return d ? /* @__PURE__ */ s(
|
|
24
|
+
return d ? /* @__PURE__ */ s(i, {
|
|
25
25
|
title: "Team Members",
|
|
26
26
|
description: "Manage team members and roles",
|
|
27
27
|
actions: /* @__PURE__ */ c("button", {
|
|
28
28
|
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
29
|
children: [/* @__PURE__ */ s(a, { className: "h-4 w-4" }), "Invite Member"]
|
|
30
30
|
}),
|
|
31
|
-
children: /* @__PURE__ */ s(
|
|
31
|
+
children: /* @__PURE__ */ s(n, {
|
|
32
32
|
variant: "list",
|
|
33
33
|
count: 5
|
|
34
34
|
})
|
|
35
|
-
}) : f ? /* @__PURE__ */ s(
|
|
35
|
+
}) : f ? /* @__PURE__ */ s(i, {
|
|
36
36
|
title: "Team Members",
|
|
37
37
|
description: "Manage team members and roles",
|
|
38
38
|
children: /* @__PURE__ */ s("div", {
|
|
39
|
-
className: "rounded-lg border border-
|
|
39
|
+
className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
|
|
40
40
|
children: "Failed to load team data. Please try again later."
|
|
41
41
|
})
|
|
42
|
-
}) : /* @__PURE__ */ c(
|
|
42
|
+
}) : /* @__PURE__ */ c(i, {
|
|
43
43
|
title: "Team Members",
|
|
44
44
|
description: "Manage team members and roles",
|
|
45
45
|
actions: /* @__PURE__ */ c("button", {
|
|
@@ -47,10 +47,10 @@ function l() {
|
|
|
47
47
|
children: [/* @__PURE__ */ s(a, { className: "h-4 w-4" }), "Invite Member"]
|
|
48
48
|
}),
|
|
49
49
|
children: [/* @__PURE__ */ s("div", {
|
|
50
|
-
className: "mb-4 rounded-lg border border-
|
|
50
|
+
className: "mb-4 rounded-lg border border-status-warning-bg/40 bg-status-warning-bg p-3 text-xs text-status-warning-text",
|
|
51
51
|
children: "Team member data is managed by the groups module. Showing time entry activity by project."
|
|
52
52
|
}), /* @__PURE__ */ s("div", {
|
|
53
|
-
className: "rounded-xl border border-border-
|
|
53
|
+
className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop",
|
|
54
54
|
children: /* @__PURE__ */ s("div", {
|
|
55
55
|
className: "divide-y divide-border",
|
|
56
56
|
children: m.length === 0 ? /* @__PURE__ */ s("div", {
|
|
@@ -82,7 +82,7 @@ function l() {
|
|
|
82
82
|
children: "total tracked"
|
|
83
83
|
})]
|
|
84
84
|
}), /* @__PURE__ */ s("span", {
|
|
85
|
-
className: "inline-flex rounded-full bg-
|
|
85
|
+
className: "inline-flex rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text",
|
|
86
86
|
children: "active"
|
|
87
87
|
})]
|
|
88
88
|
})]
|
|
@@ -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-
|
|
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 <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop\">\n <div className=\"divide-y divide-border\">\n {summaryList.length === 0 ? (\n <div className=\"p-8 text-center text-sm text-text-secondary\">\n No time entry data available.\n </div>\n ) : (\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 )}\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;AAUA,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,SACE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,gBAErB;;YAPb,CAUE,kBAAC,OAAD;GAAK,WAAU;aAA+G;GAGxH,CAAA,EACN,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAY,WAAW,IACtB,kBAAC,OAAD;KAAK,WAAU;eAA8C;KAEvD,CAAA,GAEN,EAAY,KAAK,MACf,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;IAEA,CAAA;GACF,CAAA,CACK"}
|