@burdenoff/microfe-timecampus 2026.626.2 → 2026.703.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/generated/wspace-operations.js +996 -816
  2. package/dist/generated/wspace-operations.js.map +1 -1
  3. package/dist/generated/wspace-types.js +28 -0
  4. package/dist/generated/wspace-types.js.map +1 -0
  5. package/dist/index.js +4 -4
  6. package/dist/timecampus/TimeCampusRoutes.js +16 -12
  7. package/dist/timecampus/TimeCampusRoutes.js.map +1 -1
  8. package/dist/timecampus/components/Modal.js +65 -0
  9. package/dist/timecampus/components/Modal.js.map +1 -0
  10. package/dist/timecampus/components/StatusBadge.js +28 -0
  11. package/dist/timecampus/components/StatusBadge.js.map +1 -0
  12. package/dist/timecampus/components/Toast.js +87 -0
  13. package/dist/timecampus/components/Toast.js.map +1 -0
  14. package/dist/timecampus/index.js +1 -1
  15. package/dist/timecampus/pages/HomePage.js +143 -135
  16. package/dist/timecampus/pages/HomePage.js.map +1 -1
  17. package/dist/timecampus/pages/applets/AppletStorePage.js +14 -19
  18. package/dist/timecampus/pages/applets/AppletStorePage.js.map +1 -1
  19. package/dist/timecampus/pages/applets/InstalledAppletsPage.js +49 -37
  20. package/dist/timecampus/pages/applets/InstalledAppletsPage.js.map +1 -1
  21. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +315 -87
  22. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
  23. package/dist/timecampus/pages/channels/ChannelsPage.js +340 -46
  24. package/dist/timecampus/pages/channels/ChannelsPage.js.map +1 -1
  25. package/dist/timecampus/pages/client-portal/ClientPortalPage.js +374 -92
  26. package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
  27. package/dist/timecampus/pages/dashboard/ReportsPage.js +70 -37
  28. package/dist/timecampus/pages/dashboard/ReportsPage.js.map +1 -1
  29. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +366 -60
  30. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js.map +1 -1
  31. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +461 -87
  32. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
  33. package/dist/timecampus/pages/geofencing/GeofencingPage.js +314 -55
  34. package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
  35. package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js +30 -27
  36. package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js.map +1 -1
  37. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +269 -74
  38. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
  39. package/dist/timecampus/pages/meetings/ActionItemFormModal.js +127 -0
  40. package/dist/timecampus/pages/meetings/ActionItemFormModal.js.map +1 -0
  41. package/dist/timecampus/pages/meetings/MeetingCreatePage.js +62 -52
  42. package/dist/timecampus/pages/meetings/MeetingCreatePage.js.map +1 -1
  43. package/dist/timecampus/pages/meetings/MeetingDetailPage.js +361 -74
  44. package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
  45. package/dist/timecampus/pages/meetings/MeetingFormModal.js +156 -0
  46. package/dist/timecampus/pages/meetings/MeetingFormModal.js.map +1 -0
  47. package/dist/timecampus/pages/meetings/MeetingsPage.js +175 -74
  48. package/dist/timecampus/pages/meetings/MeetingsPage.js.map +1 -1
  49. package/dist/timecampus/pages/meetings/ParticipantFormModal.js +105 -0
  50. package/dist/timecampus/pages/meetings/ParticipantFormModal.js.map +1 -0
  51. package/dist/timecampus/pages/pomodoro/PomodoroPage.js +307 -67
  52. package/dist/timecampus/pages/pomodoro/PomodoroPage.js.map +1 -1
  53. package/dist/timecampus/pages/projects/ProjectCreatePage.js +51 -48
  54. package/dist/timecampus/pages/projects/ProjectCreatePage.js.map +1 -1
  55. package/dist/timecampus/pages/projects/ProjectDetailPage.js +248 -51
  56. package/dist/timecampus/pages/projects/ProjectDetailPage.js.map +1 -1
  57. package/dist/timecampus/pages/projects/ProjectsPage.js +354 -87
  58. package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
  59. package/dist/timecampus/pages/projects/TasksPage.js +367 -65
  60. package/dist/timecampus/pages/projects/TasksPage.js.map +1 -1
  61. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +674 -104
  62. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
  63. package/dist/timecampus/pages/team/TeamAnalyticsPage.js +46 -34
  64. package/dist/timecampus/pages/team/TeamAnalyticsPage.js.map +1 -1
  65. package/dist/timecampus/pages/team/TeamMembersPage.js +93 -69
  66. package/dist/timecampus/pages/team/TeamMembersPage.js.map +1 -1
  67. package/dist/timecampus/pages/team/TeamPage.js +4 -3
  68. package/dist/timecampus/pages/team/TeamPage.js.map +1 -1
  69. package/dist/timecampus/pages/templates/TemplateDetailPage.js +72 -53
  70. package/dist/timecampus/pages/templates/TemplateDetailPage.js.map +1 -1
  71. package/dist/timecampus/pages/templates/TemplatesPage.js +40 -35
  72. package/dist/timecampus/pages/templates/TemplatesPage.js.map +1 -1
  73. package/dist/timecampus/pages/time/TimeEntriesPage.js +484 -97
  74. package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
  75. package/dist/timecampus/pages/time/TimeTrackingPage.js +210 -99
  76. package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
  77. package/dist/timecampus/pages/time/TimerPage.js +229 -97
  78. package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
  79. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +315 -110
  80. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
  81. package/dist/timecampus/pages/timeline/TimelinePage.js +250 -0
  82. package/dist/timecampus/pages/timeline/TimelinePage.js.map +1 -0
  83. package/dist/timecampus/pages/widgets/WidgetDetailPage.js +158 -76
  84. package/dist/timecampus/pages/widgets/WidgetDetailPage.js.map +1 -1
  85. package/dist/timecampus/pages/widgets/WidgetsPage.js +181 -52
  86. package/dist/timecampus/pages/widgets/WidgetsPage.js.map +1 -1
  87. package/dist/timecampus/pages/workflows/WorkflowsPage.js +212 -61
  88. package/dist/timecampus/pages/workflows/WorkflowsPage.js.map +1 -1
  89. package/dist/timecampus/store/timecampusStore.js +13 -1
  90. package/dist/timecampus/store/timecampusStore.js.map +1 -1
  91. package/dist/timecampus/utils/helpers.js +20 -10
  92. package/dist/timecampus/utils/helpers.js.map +1 -1
  93. package/package.json +1 -1
@@ -1,72 +1,75 @@
1
- import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
1
+ import { useTimeCampusNavigate as e } from "../../providers/TimeCampusProvider.js";
2
2
  import { LoadingSkeleton as t } from "../../components/LoadingSkeleton.js";
3
3
  import { useListConfigurationsQuery as n } from "../../../generated/wspace-operations.js";
4
4
  import { PageLayout as r } from "../../components/PageLayout.js";
5
5
  import { EmptyState as i } from "../../components/EmptyState.js";
6
- import { ArrowRight as a, Factory as o } from "lucide-react";
7
- import { jsx as s, jsxs as c } from "react/jsx-runtime";
6
+ import { TimeCampusConfigScope as a } from "../../../generated/wspace-types.js";
7
+ import { ArrowRight as o, Factory as s } from "lucide-react";
8
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
8
9
  //#region src/timecampus/pages/industry-solutions/IndustrySolutionsPage.tsx
9
- function l() {
10
- let { workspaceId: l = "" } = e(), { data: u, loading: d, error: f } = n({ variables: { scope: "WORKSPACE" } }), p = (u?.configurations ?? []).filter((e) => e.key.startsWith("industry."));
11
- return d ? /* @__PURE__ */ s(r, {
10
+ function u() {
11
+ let u = e(), { data: d, loading: f, error: p } = n({ variables: { scope: a.Workspace } }), m = (d?.configurations ?? []).filter((e) => e.key.startsWith("industry."));
12
+ return f ? /* @__PURE__ */ c(r, {
12
13
  title: "Industry Solutions",
13
14
  description: "Time tracking solutions tailored to your industry",
14
- children: /* @__PURE__ */ s(t, {
15
+ children: /* @__PURE__ */ c(t, {
15
16
  variant: "card",
16
17
  count: 6
17
18
  })
18
- }) : f ? /* @__PURE__ */ s(r, {
19
+ }) : p ? /* @__PURE__ */ c(r, {
19
20
  title: "Industry Solutions",
20
21
  description: "Time tracking solutions tailored to your industry",
21
- children: /* @__PURE__ */ s("div", {
22
+ children: /* @__PURE__ */ c("div", {
22
23
  className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
23
24
  children: "Failed to load industry solutions. Please try again later."
24
25
  })
25
- }) : /* @__PURE__ */ s(r, {
26
+ }) : /* @__PURE__ */ c(r, {
26
27
  title: "Industry Solutions",
27
28
  description: "Time tracking solutions tailored to your industry",
28
29
  purpose: "Start from a setup built for how your industry actually works — agencies, field services, manufacturing and more — instead of configuring time tracking from scratch.",
29
- children: p.length === 0 ? /* @__PURE__ */ s(i, {
30
+ children: m.length === 0 ? /* @__PURE__ */ c(i, {
30
31
  illustration: "empty-generic",
31
32
  title: "No industry solutions configured yet",
32
33
  description: "Industry templates are stored as workspace configurations with the 'industry.' key prefix — once added, ready-made setups for your sector appear here."
33
- }) : /* @__PURE__ */ s("div", {
34
+ }) : /* @__PURE__ */ c("div", {
34
35
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
35
- children: p.map((e) => {
36
- let t = e.value, n = t.name ?? e.key.replace("industry.", ""), r = t.industry ?? "General", i = e.description ?? t.description ?? "", l = t.features ?? [];
37
- return /* @__PURE__ */ c("div", {
36
+ children: m.map((e) => {
37
+ let t = e.value, n = t.name ?? e.key.replace("industry.", ""), r = t.industry ?? "General", i = e.description ?? t.description ?? "", a = t.features ?? [];
38
+ return /* @__PURE__ */ l("div", {
38
39
  className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop p-5 transition-colors hover:bg-bg-sunken/50 cursor-pointer",
39
40
  children: [
40
- /* @__PURE__ */ c("div", {
41
+ /* @__PURE__ */ l("div", {
41
42
  className: "flex items-start gap-3",
42
- children: [/* @__PURE__ */ s("div", {
43
+ children: [/* @__PURE__ */ c("div", {
43
44
  className: "flex h-12 w-12 items-center justify-center rounded-xl bg-action-primary-bg/10",
44
- children: /* @__PURE__ */ s(o, { className: "h-6 w-6 text-primary" })
45
- }), /* @__PURE__ */ c("div", {
45
+ children: /* @__PURE__ */ c(s, { className: "h-6 w-6 text-primary" })
46
+ }), /* @__PURE__ */ l("div", {
46
47
  className: "flex-1",
47
- children: [/* @__PURE__ */ s("p", {
48
+ children: [/* @__PURE__ */ c("p", {
48
49
  className: "text-sm font-semibold text-text-primary",
49
50
  children: n
50
- }), /* @__PURE__ */ s("p", {
51
+ }), /* @__PURE__ */ c("p", {
51
52
  className: "text-xs text-text-secondary",
52
53
  children: r
53
54
  })]
54
55
  })]
55
56
  }),
56
- /* @__PURE__ */ s("p", {
57
+ /* @__PURE__ */ c("p", {
57
58
  className: "mt-3 text-xs text-text-secondary",
58
59
  children: i
59
60
  }),
60
- /* @__PURE__ */ s("div", {
61
+ /* @__PURE__ */ c("div", {
61
62
  className: "mt-3 flex flex-wrap gap-1",
62
- children: l.map((e) => /* @__PURE__ */ s("span", {
63
+ children: a.map((e) => /* @__PURE__ */ c("span", {
63
64
  className: "inline-flex rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary",
64
65
  children: e
65
66
  }, e))
66
67
  }),
67
- /* @__PURE__ */ c("button", {
68
+ /* @__PURE__ */ l("button", {
69
+ type: "button",
70
+ onClick: () => u("/templates"),
68
71
  className: "mt-3 flex items-center gap-1 text-xs font-medium text-primary hover:underline",
69
- children: ["Learn more ", /* @__PURE__ */ s(a, { className: "h-3 w-3" })]
72
+ children: ["Explore templates ", /* @__PURE__ */ c(o, { className: "h-3 w-3" })]
70
73
  })
71
74
  ]
72
75
  }, e.id);
@@ -75,6 +78,6 @@ function l() {
75
78
  });
76
79
  }
77
80
  //#endregion
78
- export { l as IndustrySolutionsPage };
81
+ export { u as IndustrySolutionsPage };
79
82
 
80
83
  //# sourceMappingURL=IndustrySolutionsPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"IndustrySolutionsPage.js","names":[],"sources":["../../../../src/timecampus/pages/industry-solutions/IndustrySolutionsPage.tsx"],"sourcesContent":["import { Factory, ArrowRight } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListConfigurationsQuery,\n type ConfigurationFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function IndustrySolutionsPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const { data, loading, error } = useListConfigurationsQuery({\n variables: { scope: \"WORKSPACE\" as any },\n });\n\n const configurations = data?.configurations ?? [];\n\n // Filter configurations that represent industry solutions (key starts with 'industry.')\n const solutions = configurations.filter((c: ConfigurationFieldsFragment) =>\n c.key.startsWith(\"industry.\"),\n );\n\n if (loading) {\n return (\n <PageLayout\n title=\"Industry Solutions\"\n description=\"Time tracking solutions tailored to your industry\"\n >\n <LoadingSkeleton variant=\"card\" count={6} />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"Industry Solutions\"\n description=\"Time tracking solutions tailored to your industry\"\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 industry solutions. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Industry Solutions\"\n description=\"Time tracking solutions tailored to your industry\"\n purpose=\"Start from a setup built for how your industry actually works — agencies, field services, manufacturing and more — instead of configuring time tracking from scratch.\"\n >\n {solutions.length === 0 ? (\n <EmptyState\n illustration=\"empty-generic\"\n title=\"No industry solutions configured yet\"\n description=\"Industry templates are stored as workspace configurations with the 'industry.' key prefix — once added, ready-made setups for your sector appear here.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {solutions.map((solution: ConfigurationFieldsFragment) => {\n const val = solution.value as Record<string, unknown>;\n const name =\n (val.name as string) ?? solution.key.replace(\"industry.\", \"\");\n const industry = (val.industry as string) ?? \"General\";\n const description =\n solution.description ?? (val.description as string) ?? \"\";\n const features = (val.features as string[]) ?? [];\n\n return (\n <div\n key={solution.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 gap-3\">\n <div className=\"flex h-12 w-12 items-center justify-center rounded-xl bg-action-primary-bg/10\">\n <Factory className=\"h-6 w-6 text-primary\" />\n </div>\n <div className=\"flex-1\">\n <p className=\"text-sm font-semibold text-text-primary\">\n {name}\n </p>\n <p className=\"text-xs text-text-secondary\">{industry}</p>\n </div>\n </div>\n <p className=\"mt-3 text-xs text-text-secondary\">\n {description}\n </p>\n <div className=\"mt-3 flex flex-wrap gap-1\">\n {features.map((feature) => (\n <span\n key={feature}\n className=\"inline-flex rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\"\n >\n {feature}\n </span>\n ))}\n </div>\n <button className=\"mt-3 flex items-center gap-1 text-xs font-medium text-primary hover:underline\">\n Learn more <ArrowRight className=\"h-3 w-3\" />\n </button>\n </div>\n );\n })}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;AAUA,SAAgB,IAAwB;CACtC,IAAM,EAAE,iBAAc,OAAO,GAAe,EAEtC,EAAE,SAAM,YAAS,aAAU,EAA2B,EAC1D,WAAW,EAAE,OAAO,aAAoB,EACzC,CAAC,EAKI,KAHiB,GAAM,kBAAkB,EAAE,EAGhB,QAAQ,MACvC,EAAE,IAAI,WAAW,YAAY,CAC9B;AA0BD,QAxBI,IAEA,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA;EACjC,CAAA,GAIb,IAEA,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,OAAD;GAAK,WAAU;aAAoG;GAE7G,CAAA;EACK,CAAA,GAKf,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SAAQ;YAEP,EAAU,WAAW,IACpB,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,aAAY;GACZ,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAU,KAAK,MAA0C;IACxD,IAAM,IAAM,EAAS,OACf,IACH,EAAI,QAAmB,EAAS,IAAI,QAAQ,aAAa,GAAG,EACzD,IAAY,EAAI,YAAuB,WACvC,IACJ,EAAS,eAAgB,EAAI,eAA0B,IACnD,IAAY,EAAI,YAAyB,EAAE;AAEjD,WACE,kBAAC,OAAD;KAEE,WAAU;eAFZ;MAIE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA;QACxC,CAAA,EACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV;SACC,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBAA+B;SAAa,CAAA,CACrD;UACF;;MACN,kBAAC,KAAD;OAAG,WAAU;iBACV;OACC,CAAA;MACJ,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAS,KAAK,MACb,kBAAC,QAAD;QAEE,WAAU;kBAET;QACI,EAJA,EAIA,CACP;OACE,CAAA;MACN,kBAAC,UAAD;OAAQ,WAAU;iBAAlB,CAAkG,eACrF,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA,CACtC;;MACL;OA9BC,EAAS,GA8BV;KAER;GACE,CAAA;EAEG,CAAA"}
1
+ {"version":3,"file":"IndustrySolutionsPage.js","names":[],"sources":["../../../../src/timecampus/pages/industry-solutions/IndustrySolutionsPage.tsx"],"sourcesContent":["import { Factory, ArrowRight } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { useTimeCampusNavigate } from \"../../providers/TimeCampusProvider\";\nimport {\n useListConfigurationsQuery,\n type ConfigurationFieldsFragment,\n} from \"@/generated/wspace-operations\";\nimport { TimeCampusConfigScope } from \"@/generated/wspace-types\";\n\nexport function IndustrySolutionsPage() {\n const navigate = useTimeCampusNavigate();\n\n const { data, loading, error } = useListConfigurationsQuery({\n variables: { scope: TimeCampusConfigScope.Workspace },\n });\n\n const configurations = data?.configurations ?? [];\n\n // Filter configurations that represent industry solutions (key starts with 'industry.')\n const solutions = configurations.filter((c: ConfigurationFieldsFragment) =>\n c.key.startsWith(\"industry.\"),\n );\n\n if (loading) {\n return (\n <PageLayout\n title=\"Industry Solutions\"\n description=\"Time tracking solutions tailored to your industry\"\n >\n <LoadingSkeleton variant=\"card\" count={6} />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"Industry Solutions\"\n description=\"Time tracking solutions tailored to your industry\"\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 industry solutions. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Industry Solutions\"\n description=\"Time tracking solutions tailored to your industry\"\n purpose=\"Start from a setup built for how your industry actually works — agencies, field services, manufacturing and more — instead of configuring time tracking from scratch.\"\n >\n {solutions.length === 0 ? (\n <EmptyState\n illustration=\"empty-generic\"\n title=\"No industry solutions configured yet\"\n description=\"Industry templates are stored as workspace configurations with the 'industry.' key prefix — once added, ready-made setups for your sector appear here.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {solutions.map((solution: ConfigurationFieldsFragment) => {\n const val = solution.value as Record<string, unknown>;\n const name =\n (val.name as string) ?? solution.key.replace(\"industry.\", \"\");\n const industry = (val.industry as string) ?? \"General\";\n const description =\n solution.description ?? (val.description as string) ?? \"\";\n const features = (val.features as string[]) ?? [];\n\n return (\n <div\n key={solution.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 gap-3\">\n <div className=\"flex h-12 w-12 items-center justify-center rounded-xl bg-action-primary-bg/10\">\n <Factory className=\"h-6 w-6 text-primary\" />\n </div>\n <div className=\"flex-1\">\n <p className=\"text-sm font-semibold text-text-primary\">\n {name}\n </p>\n <p className=\"text-xs text-text-secondary\">{industry}</p>\n </div>\n </div>\n <p className=\"mt-3 text-xs text-text-secondary\">\n {description}\n </p>\n <div className=\"mt-3 flex flex-wrap gap-1\">\n {features.map((feature) => (\n <span\n key={feature}\n className=\"inline-flex rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\"\n >\n {feature}\n </span>\n ))}\n </div>\n <button\n type=\"button\"\n onClick={() => navigate(\"/templates\")}\n className=\"mt-3 flex items-center gap-1 text-xs font-medium text-primary hover:underline\"\n >\n Explore templates <ArrowRight className=\"h-3 w-3\" />\n </button>\n </div>\n );\n })}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;AAWA,SAAgB,IAAwB;CACtC,IAAM,IAAW,GAAuB,EAElC,EAAE,SAAM,YAAS,aAAU,EAA2B,EAC1D,WAAW,EAAE,OAAO,EAAsB,WAAW,EACtD,CAAC,EAKI,KAHiB,GAAM,kBAAkB,EAAE,EAGhB,QAAQ,MACvC,EAAE,IAAI,WAAW,YAAY,CAC9B;AA0BD,QAxBI,IAEA,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA;EACjC,CAAA,GAIb,IAEA,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,OAAD;GAAK,WAAU;aAAoG;GAE7G,CAAA;EACK,CAAA,GAKf,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SAAQ;YAEP,EAAU,WAAW,IACpB,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,aAAY;GACZ,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAU,KAAK,MAA0C;IACxD,IAAM,IAAM,EAAS,OACf,IACH,EAAI,QAAmB,EAAS,IAAI,QAAQ,aAAa,GAAG,EACzD,IAAY,EAAI,YAAuB,WACvC,IACJ,EAAS,eAAgB,EAAI,eAA0B,IACnD,IAAY,EAAI,YAAyB,EAAE;AAEjD,WACE,kBAAC,OAAD;KAEE,WAAU;eAFZ;MAIE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA;QACxC,CAAA,EACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV;SACC,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBAA+B;SAAa,CAAA,CACrD;UACF;;MACN,kBAAC,KAAD;OAAG,WAAU;iBACV;OACC,CAAA;MACJ,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAS,KAAK,MACb,kBAAC,QAAD;QAEE,WAAU;kBAET;QACI,EAJA,EAIA,CACP;OACE,CAAA;MACN,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAS,aAAa;OACrC,WAAU;iBAHZ,CAIC,sBACmB,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA,CAC7C;;MACL;OAlCC,EAAS,GAkCV;KAER;GACE,CAAA;EAEG,CAAA"}
@@ -2,100 +2,295 @@ import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
2
2
  import { formatHours as t } from "../../utils/helpers.js";
3
3
  import { StatCard as n } from "../../components/StatCard.js";
4
4
  import { LoadingSkeleton as r } from "../../components/LoadingSkeleton.js";
5
- import { useGetLocationStatsQuery as i, useListLocationEntriesQuery as a } from "../../../generated/wspace-operations.js";
6
- import { PageLayout as o } from "../../components/PageLayout.js";
7
- import { EmptyState as s } from "../../components/EmptyState.js";
8
- import { Clock as c, MapPin as l } from "lucide-react";
9
- import { jsx as u, jsxs as d } from "react/jsx-runtime";
5
+ import { useCreateLocationEntryMutation as i, useDeleteLocationEntryMutation as a, useGetLocationStatsQuery as o, useListLocationEntriesQuery as s } from "../../../generated/wspace-operations.js";
6
+ import { PageLayout as c } from "../../components/PageLayout.js";
7
+ import { EmptyState as l } from "../../components/EmptyState.js";
8
+ import { Modal as u } from "../../components/Modal.js";
9
+ import { useState as d } from "react";
10
+ import { Clock as f, MapPin as p, Plus as m, Trash2 as h } from "lucide-react";
11
+ import { Fragment as g, jsx as _, jsxs as v } from "react/jsx-runtime";
10
12
  //#region src/timecampus/pages/location-tracking/LocationTrackingPage.tsx
11
- function f() {
12
- let { workspaceId: f = "" } = e(), { data: p, loading: m, error: h } = a(), { data: g, loading: _, error: v } = i(), y = p?.locationEntries?.items ?? [], b = g?.locationStats;
13
- return m && _ ? /* @__PURE__ */ u(o, {
13
+ var y = 25, b = "mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary", x = {
14
+ address: "",
15
+ latitude: "",
16
+ longitude: "",
17
+ startTime: "",
18
+ endTime: ""
19
+ };
20
+ function S() {
21
+ let { currentUser: S } = e(), { data: C, loading: w, error: T, refetch: E, fetchMore: D } = s({ variables: {
22
+ limit: y,
23
+ offset: 0
24
+ } }), { data: O, loading: k, error: A, refetch: j } = o(), [M, { loading: N, error: P }] = i(), [F, { loading: I }] = a(), [L, R] = d(!1), [z, B] = d(x), [V, H] = d(null), [U, W] = d(!1), G = C?.locationEntries?.items ?? [], K = C?.locationEntries?.hasMore ?? !1, q = O?.locationStats, J = () => {
25
+ let e = /* @__PURE__ */ new Date(), t = (/* @__PURE__ */ new Date(e.getTime() - e.getTimezoneOffset() * 6e4)).toISOString().slice(0, 16);
26
+ B({
27
+ ...x,
28
+ startTime: t
29
+ }), R(!0);
30
+ }, Y = !!S?.id && z.latitude.trim() !== "" && z.longitude.trim() !== "" && z.startTime.trim() !== "", X = async () => {
31
+ if (!Y || !S?.id) return;
32
+ let e = new Date(z.startTime).toISOString(), t = z.endTime ? new Date(z.endTime).toISOString() : void 0, n = t ? Math.max(0, Math.round((new Date(t).getTime() - new Date(e).getTime()) / 1e3)) : void 0;
33
+ await M({ variables: { input: {
34
+ userId: S.id,
35
+ latitude: Number(z.latitude),
36
+ longitude: Number(z.longitude),
37
+ address: z.address.trim() || void 0,
38
+ startTime: e,
39
+ endTime: t,
40
+ duration: n
41
+ } } }), R(!1), await Promise.all([E(), j()]);
42
+ }, Z = async () => {
43
+ V && (await F({ variables: { id: V.id } }), H(null), await Promise.all([E(), j()]));
44
+ }, Q = async () => {
45
+ W(!0);
46
+ try {
47
+ await D({
48
+ variables: {
49
+ limit: y,
50
+ offset: G.length
51
+ },
52
+ updateQuery: (e, { fetchMoreResult: t }) => t ? { locationEntries: {
53
+ ...t.locationEntries,
54
+ items: [...e.locationEntries.items, ...t.locationEntries.items]
55
+ } } : e
56
+ });
57
+ } finally {
58
+ W(!1);
59
+ }
60
+ }, $ = /* @__PURE__ */ v("button", {
61
+ onClick: J,
62
+ 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",
63
+ children: [/* @__PURE__ */ _(m, { className: "h-4 w-4" }), "Add Location Entry"]
64
+ });
65
+ return w && k ? /* @__PURE__ */ _(c, {
14
66
  title: "Location Tracking",
15
67
  description: "Track work locations and productivity",
16
- children: /* @__PURE__ */ u(r, {
68
+ children: /* @__PURE__ */ _(r, {
17
69
  variant: "list",
18
70
  count: 4
19
71
  })
20
- }) : h && v ? /* @__PURE__ */ u(o, {
72
+ }) : T && A ? /* @__PURE__ */ _(c, {
21
73
  title: "Location Tracking",
22
74
  description: "Track work locations and productivity",
23
- children: /* @__PURE__ */ u("div", {
75
+ children: /* @__PURE__ */ _("div", {
24
76
  className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
25
77
  children: "Failed to load location data. Please try again later."
26
78
  })
27
- }) : /* @__PURE__ */ d(o, {
79
+ }) : /* @__PURE__ */ v(c, {
28
80
  title: "Location Tracking",
29
81
  description: "Track work locations and productivity",
30
82
  purpose: "See where work actually happens — office, home, client sites — and how long you spend at each, so you can verify on-site time, support field teams, and back up location-based billing.",
31
- children: [/* @__PURE__ */ d("div", {
32
- className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
33
- children: [
34
- /* @__PURE__ */ u(n, {
35
- featured: !0,
36
- title: "Total Locations",
37
- value: String(b?.totalLocations ?? 0),
38
- icon: /* @__PURE__ */ u(l, { className: "h-5 w-5" })
39
- }),
40
- /* @__PURE__ */ u(n, {
41
- title: "Unique Addresses",
42
- value: String(b?.uniqueAddresses ?? 0),
43
- icon: /* @__PURE__ */ u(l, { className: "h-5 w-5" })
44
- }),
45
- /* @__PURE__ */ u(n, {
46
- title: "Most Frequent",
47
- value: b?.mostFrequentLocation ?? "N/A"
48
- }),
49
- /* @__PURE__ */ u(n, {
50
- title: "Total Duration",
51
- value: t((b?.totalDuration ?? 0) / 3600),
52
- icon: /* @__PURE__ */ u(c, { className: "h-5 w-5" }),
53
- description: "Tracked time"
54
- })
55
- ]
56
- }), y.length === 0 && !m ? /* @__PURE__ */ u(s, {
57
- illustration: "empty-data",
58
- title: "No location entries found",
59
- description: "With location tracking enabled, the places you work from will show up here with time spent at each."
60
- }) : /* @__PURE__ */ u("div", {
61
- className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop",
62
- children: /* @__PURE__ */ u("div", {
63
- className: "divide-y divide-border",
64
- children: y.map((e) => /* @__PURE__ */ d("div", {
65
- className: "flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors",
66
- children: [/* @__PURE__ */ d("div", {
67
- className: "flex items-center gap-3",
68
- children: [/* @__PURE__ */ u(l, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
69
- className: "text-sm font-medium text-text-primary",
70
- children: e.address ?? "Unknown Location"
71
- }), /* @__PURE__ */ d("p", {
72
- className: "text-xs text-text-secondary",
73
- children: [
74
- e.geofenceId ? "Geofenced" : "Manual",
75
- " ·",
76
- " ",
77
- new Date(e.startTime).toLocaleDateString()
78
- ]
79
- })] })]
80
- }), /* @__PURE__ */ u("div", {
81
- className: "flex items-center gap-4",
82
- children: /* @__PURE__ */ d("div", {
83
- className: "text-right",
84
- children: [/* @__PURE__ */ u("p", {
83
+ actions: $,
84
+ children: [
85
+ /* @__PURE__ */ v("div", {
86
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
87
+ children: [
88
+ /* @__PURE__ */ _(n, {
89
+ featured: !0,
90
+ title: "Total Locations",
91
+ value: String(q?.totalLocations ?? 0),
92
+ icon: /* @__PURE__ */ _(p, { className: "h-5 w-5" })
93
+ }),
94
+ /* @__PURE__ */ _(n, {
95
+ title: "Unique Addresses",
96
+ value: String(q?.uniqueAddresses ?? 0),
97
+ icon: /* @__PURE__ */ _(p, { className: "h-5 w-5" })
98
+ }),
99
+ /* @__PURE__ */ _(n, {
100
+ title: "Most Frequent",
101
+ value: q?.mostFrequentLocation ?? "N/A"
102
+ }),
103
+ /* @__PURE__ */ _(n, {
104
+ title: "Total Duration",
105
+ value: t((q?.totalDuration ?? 0) / 3600),
106
+ icon: /* @__PURE__ */ _(f, { className: "h-5 w-5" }),
107
+ description: "Tracked time"
108
+ })
109
+ ]
110
+ }),
111
+ G.length === 0 && !w ? /* @__PURE__ */ _(l, {
112
+ illustration: "empty-data",
113
+ title: "No location entries found",
114
+ description: "With location tracking enabled, the places you work from will show up here with time spent at each. You can also add an entry manually.",
115
+ action: $
116
+ }) : /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _("div", {
117
+ className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop",
118
+ children: /* @__PURE__ */ _("div", {
119
+ className: "divide-y divide-border",
120
+ children: G.map((e) => /* @__PURE__ */ v("div", {
121
+ className: "flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors",
122
+ children: [/* @__PURE__ */ v("div", {
123
+ className: "flex items-center gap-3",
124
+ children: [/* @__PURE__ */ _(p, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("p", {
85
125
  className: "text-sm font-medium text-text-primary",
86
- children: e.duration ? t(e.duration / 3600) : "Active"
87
- }), /* @__PURE__ */ u("p", {
126
+ children: e.address ?? "Unknown Location"
127
+ }), /* @__PURE__ */ v("p", {
88
128
  className: "text-xs text-text-secondary",
89
- children: "duration"
129
+ children: [
130
+ e.geofenceId ? "Geofenced" : "Manual",
131
+ " ·",
132
+ " ",
133
+ new Date(e.startTime).toLocaleDateString()
134
+ ]
135
+ })] })]
136
+ }), /* @__PURE__ */ v("div", {
137
+ className: "flex items-center gap-4",
138
+ children: [/* @__PURE__ */ v("div", {
139
+ className: "text-right",
140
+ children: [/* @__PURE__ */ _("p", {
141
+ className: "text-sm font-medium text-text-primary",
142
+ children: e.duration ? t(e.duration / 3600) : "Active"
143
+ }), /* @__PURE__ */ _("p", {
144
+ className: "text-xs text-text-secondary",
145
+ children: "duration"
146
+ })]
147
+ }), /* @__PURE__ */ _("button", {
148
+ onClick: () => H(e),
149
+ "aria-label": "Delete entry",
150
+ className: "rounded-md border border-border-subtle p-1.5 text-status-error-text hover:bg-status-error-bg",
151
+ children: /* @__PURE__ */ _(h, { className: "h-4 w-4" })
90
152
  })]
91
- })
92
- })]
93
- }, e.id))
153
+ })]
154
+ }, e.id))
155
+ })
156
+ }), K && /* @__PURE__ */ _("div", {
157
+ className: "mt-6 flex justify-center",
158
+ children: /* @__PURE__ */ _("button", {
159
+ onClick: Q,
160
+ disabled: U,
161
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50 disabled:opacity-50",
162
+ children: U ? "Loading..." : "Load more"
163
+ })
164
+ })] }),
165
+ /* @__PURE__ */ v(u, {
166
+ open: L,
167
+ onClose: () => R(!1),
168
+ title: "Add Location Entry",
169
+ description: "Manually log a work location. Only you and people you share with can see this.",
170
+ size: "lg",
171
+ footer: /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _("button", {
172
+ onClick: () => R(!1),
173
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
174
+ children: "Cancel"
175
+ }), /* @__PURE__ */ _("button", {
176
+ onClick: X,
177
+ disabled: N || !Y,
178
+ className: "rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50",
179
+ children: N ? "Saving..." : "Add Entry"
180
+ })] }),
181
+ children: [
182
+ P && /* @__PURE__ */ _("div", {
183
+ className: "mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text",
184
+ children: "Failed to add location entry. Please check the values and try again."
185
+ }),
186
+ !S?.id && /* @__PURE__ */ _("div", {
187
+ className: "mb-4 rounded-lg border border-status-warning-bg/40 bg-status-warning-bg p-3 text-sm text-status-warning-text",
188
+ children: "You must be signed in to add a location entry."
189
+ }),
190
+ /* @__PURE__ */ v("div", {
191
+ className: "space-y-4",
192
+ children: [
193
+ /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
194
+ className: "block text-sm font-medium text-text-primary",
195
+ children: "Address (optional)"
196
+ }), /* @__PURE__ */ _("input", {
197
+ type: "text",
198
+ placeholder: "e.g. Client site, Chennai",
199
+ value: z.address,
200
+ onChange: (e) => B({
201
+ ...z,
202
+ address: e.target.value
203
+ }),
204
+ className: b
205
+ })] }),
206
+ /* @__PURE__ */ v("div", {
207
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
208
+ children: [/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
209
+ className: "block text-sm font-medium text-text-primary",
210
+ children: "Latitude"
211
+ }), /* @__PURE__ */ _("input", {
212
+ type: "number",
213
+ step: "any",
214
+ placeholder: "0.000000",
215
+ value: z.latitude,
216
+ onChange: (e) => B({
217
+ ...z,
218
+ latitude: e.target.value
219
+ }),
220
+ className: b
221
+ })] }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
222
+ className: "block text-sm font-medium text-text-primary",
223
+ children: "Longitude"
224
+ }), /* @__PURE__ */ _("input", {
225
+ type: "number",
226
+ step: "any",
227
+ placeholder: "0.000000",
228
+ value: z.longitude,
229
+ onChange: (e) => B({
230
+ ...z,
231
+ longitude: e.target.value
232
+ }),
233
+ className: b
234
+ })] })]
235
+ }),
236
+ /* @__PURE__ */ v("div", {
237
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
238
+ children: [/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
239
+ className: "block text-sm font-medium text-text-primary",
240
+ children: "Start Time"
241
+ }), /* @__PURE__ */ _("input", {
242
+ type: "datetime-local",
243
+ value: z.startTime,
244
+ onChange: (e) => B({
245
+ ...z,
246
+ startTime: e.target.value
247
+ }),
248
+ className: b
249
+ })] }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
250
+ className: "block text-sm font-medium text-text-primary",
251
+ children: "End Time (optional)"
252
+ }), /* @__PURE__ */ _("input", {
253
+ type: "datetime-local",
254
+ value: z.endTime,
255
+ onChange: (e) => B({
256
+ ...z,
257
+ endTime: e.target.value
258
+ }),
259
+ className: b
260
+ })] })]
261
+ })
262
+ ]
263
+ })
264
+ ]
265
+ }),
266
+ /* @__PURE__ */ _(u, {
267
+ open: V !== null,
268
+ onClose: () => H(null),
269
+ title: "Delete Location Entry",
270
+ description: "This action cannot be undone.",
271
+ footer: /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _("button", {
272
+ onClick: () => H(null),
273
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
274
+ children: "Cancel"
275
+ }), /* @__PURE__ */ _("button", {
276
+ onClick: Z,
277
+ disabled: I,
278
+ className: "rounded-md bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg/80 disabled:opacity-50",
279
+ children: I ? "Deleting..." : "Delete"
280
+ })] }),
281
+ children: /* @__PURE__ */ v("p", {
282
+ className: "text-sm text-text-secondary",
283
+ children: [
284
+ "Are you sure you want to delete this location entry",
285
+ V?.address ? ` at ${V.address}` : "",
286
+ "?"
287
+ ]
288
+ })
94
289
  })
95
- })]
290
+ ]
96
291
  });
97
292
  }
98
293
  //#endregion
99
- export { f as LocationTrackingPage };
294
+ export { S as LocationTrackingPage };
100
295
 
101
296
  //# sourceMappingURL=LocationTrackingPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"LocationTrackingPage.js","names":[],"sources":["../../../../src/timecampus/pages/location-tracking/LocationTrackingPage.tsx"],"sourcesContent":["import { MapPin, Clock } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { formatHours } from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListLocationEntriesQuery,\n useGetLocationStatsQuery,\n type LocationEntryFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function LocationTrackingPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const {\n data: entriesData,\n loading: entriesLoading,\n error: entriesError,\n } = useListLocationEntriesQuery();\n const {\n data: statsData,\n loading: statsLoading,\n error: statsError,\n } = useGetLocationStatsQuery();\n\n const locations = entriesData?.locationEntries?.items ?? [];\n const stats = statsData?.locationStats;\n\n if (entriesLoading && statsLoading) {\n return (\n <PageLayout\n title=\"Location Tracking\"\n description=\"Track work locations and productivity\"\n >\n <LoadingSkeleton variant=\"list\" count={4} />\n </PageLayout>\n );\n }\n\n if (entriesError && statsError) {\n return (\n <PageLayout\n title=\"Location Tracking\"\n description=\"Track work locations and productivity\"\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 location data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Location Tracking\"\n description=\"Track work locations and productivity\"\n purpose=\"See where work actually happens — office, home, client sites — and how long you spend at each, so you can verify on-site time, support field teams, and back up location-based billing.\"\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n featured\n title=\"Total Locations\"\n value={String(stats?.totalLocations ?? 0)}\n icon={<MapPin className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Unique Addresses\"\n value={String(stats?.uniqueAddresses ?? 0)}\n icon={<MapPin className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Most Frequent\"\n value={stats?.mostFrequentLocation ?? \"N/A\"}\n />\n <StatCard\n title=\"Total Duration\"\n value={formatHours((stats?.totalDuration ?? 0) / 3600)}\n icon={<Clock className=\"h-5 w-5\" />}\n description=\"Tracked time\"\n />\n </div>\n\n {locations.length === 0 && !entriesLoading ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No location entries found\"\n description=\"With location tracking enabled, the places you work from will show up here with time spent at each.\"\n />\n ) : (\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop\">\n <div className=\"divide-y divide-border\">\n {locations.map((location: LocationEntryFieldsFragment) => (\n <div\n key={location.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 <MapPin className=\"h-5 w-5 text-text-secondary\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {location.address ?? \"Unknown Location\"}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {location.geofenceId ? \"Geofenced\" : \"Manual\"} &middot;{\" \"}\n {new Date(location.startTime).toLocaleDateString()}\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 {location.duration\n ? formatHours(location.duration / 3600)\n : \"Active\"}\n </p>\n <p className=\"text-xs text-text-secondary\">duration</p>\n </div>\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;AAaA,SAAgB,IAAuB;CACrC,IAAM,EAAE,iBAAc,OAAO,GAAe,EAEtC,EACJ,MAAM,GACN,SAAS,GACT,OAAO,MACL,GAA6B,EAC3B,EACJ,MAAM,GACN,SAAS,GACT,OAAO,MACL,GAA0B,EAExB,IAAY,GAAa,iBAAiB,SAAS,EAAE,EACrD,IAAQ,GAAW;AA0BzB,QAxBI,KAAkB,IAElB,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA;EACjC,CAAA,GAIb,KAAgB,IAEhB,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;YAHV,CAKE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KACE,UAAA;KACA,OAAM;KACN,OAAO,OAAO,GAAO,kBAAkB,EAAE;KACzC,MAAM,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;KACpC,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,OAAO,GAAO,mBAAmB,EAAE;KAC1C,MAAM,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;KACpC,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,GAAO,wBAAwB;KACtC,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,GAAa,GAAO,iBAAiB,KAAK,KAAK;KACtD,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;KACnC,aAAY;KACZ,CAAA;IACE;MAEL,EAAU,WAAW,KAAK,CAAC,IAC1B,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,aAAY;GACZ,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAU,KAAK,MACd,kBAAC,OAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,+BAAgC,CAAA,EAClD,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAS,WAAW;OACnB,CAAA,EACJ,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAS,aAAa,cAAc;QAAS;QAAU;QACvD,IAAI,KAAK,EAAS,UAAU,CAAC,oBAAoB;QAChD;SACA,EAAA,CAAA,CACF;SACN,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAS,WACN,EAAY,EAAS,WAAW,KAAK,GACrC;QACF,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBAA8B;QAAY,CAAA,CACnD;;MACF,CAAA,CACF;OAzBC,EAAS,GAyBV,CACN;IACE,CAAA;GACF,CAAA,CAEG"}
1
+ {"version":3,"file":"LocationTrackingPage.js","names":[],"sources":["../../../../src/timecampus/pages/location-tracking/LocationTrackingPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { MapPin, Clock, Plus, Trash2 } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { Modal } from \"../../components/Modal\";\nimport { formatHours } from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListLocationEntriesQuery,\n useGetLocationStatsQuery,\n useCreateLocationEntryMutation,\n useDeleteLocationEntryMutation,\n type LocationEntryFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nconst PAGE_SIZE = 25;\n\nconst inputClass =\n \"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary\";\n\ninterface EntryFormState {\n address: string;\n latitude: string;\n longitude: string;\n startTime: string;\n endTime: string;\n}\n\nconst emptyForm: EntryFormState = {\n address: \"\",\n latitude: \"\",\n longitude: \"\",\n startTime: \"\",\n endTime: \"\",\n};\n\nexport function LocationTrackingPage() {\n const { currentUser } = useTimeCampus();\n\n const {\n data: entriesData,\n loading: entriesLoading,\n error: entriesError,\n refetch: refetchEntries,\n fetchMore,\n } = useListLocationEntriesQuery({\n variables: { limit: PAGE_SIZE, offset: 0 },\n });\n const {\n data: statsData,\n loading: statsLoading,\n error: statsError,\n refetch: refetchStats,\n } = useGetLocationStatsQuery();\n\n const [createEntry, { loading: creating, error: createError }] =\n useCreateLocationEntryMutation();\n const [deleteEntry, { loading: deleting }] = useDeleteLocationEntryMutation();\n\n const [formOpen, setFormOpen] = useState(false);\n const [form, setForm] = useState<EntryFormState>(emptyForm);\n const [confirmDelete, setConfirmDelete] =\n useState<LocationEntryFieldsFragment | null>(null);\n const [loadingMore, setLoadingMore] = useState(false);\n\n const locations = entriesData?.locationEntries?.items ?? [];\n const hasMore = entriesData?.locationEntries?.hasMore ?? false;\n const stats = statsData?.locationStats;\n\n const openCreate = () => {\n const now = new Date();\n const localIso = new Date(now.getTime() - now.getTimezoneOffset() * 60000)\n .toISOString()\n .slice(0, 16);\n setForm({ ...emptyForm, startTime: localIso });\n setFormOpen(true);\n };\n\n const canSubmit =\n !!currentUser?.id &&\n form.latitude.trim() !== \"\" &&\n form.longitude.trim() !== \"\" &&\n form.startTime.trim() !== \"\";\n\n const handleSubmit = async () => {\n if (!canSubmit || !currentUser?.id) return;\n const startTime = new Date(form.startTime).toISOString();\n const endTime = form.endTime\n ? new Date(form.endTime).toISOString()\n : undefined;\n const duration = endTime\n ? Math.max(\n 0,\n Math.round(\n (new Date(endTime).getTime() - new Date(startTime).getTime()) /\n 1000,\n ),\n )\n : undefined;\n await createEntry({\n variables: {\n input: {\n userId: currentUser.id,\n latitude: Number(form.latitude),\n longitude: Number(form.longitude),\n address: form.address.trim() || undefined,\n startTime,\n endTime,\n duration,\n },\n },\n });\n setFormOpen(false);\n await Promise.all([refetchEntries(), refetchStats()]);\n };\n\n const handleDelete = async () => {\n if (!confirmDelete) return;\n await deleteEntry({ variables: { id: confirmDelete.id } });\n setConfirmDelete(null);\n await Promise.all([refetchEntries(), refetchStats()]);\n };\n\n const handleLoadMore = async () => {\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { limit: PAGE_SIZE, offset: locations.length },\n updateQuery: (prev, { fetchMoreResult }) => {\n if (!fetchMoreResult) return prev;\n return {\n locationEntries: {\n ...fetchMoreResult.locationEntries,\n items: [\n ...prev.locationEntries.items,\n ...fetchMoreResult.locationEntries.items,\n ],\n },\n };\n },\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n const addButton = (\n <button\n onClick={openCreate}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus className=\"h-4 w-4\" />\n Add Location Entry\n </button>\n );\n\n if (entriesLoading && statsLoading) {\n return (\n <PageLayout\n title=\"Location Tracking\"\n description=\"Track work locations and productivity\"\n >\n <LoadingSkeleton variant=\"list\" count={4} />\n </PageLayout>\n );\n }\n\n if (entriesError && statsError) {\n return (\n <PageLayout\n title=\"Location Tracking\"\n description=\"Track work locations and productivity\"\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 location data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Location Tracking\"\n description=\"Track work locations and productivity\"\n purpose=\"See where work actually happens — office, home, client sites — and how long you spend at each, so you can verify on-site time, support field teams, and back up location-based billing.\"\n actions={addButton}\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n featured\n title=\"Total Locations\"\n value={String(stats?.totalLocations ?? 0)}\n icon={<MapPin className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Unique Addresses\"\n value={String(stats?.uniqueAddresses ?? 0)}\n icon={<MapPin className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Most Frequent\"\n value={stats?.mostFrequentLocation ?? \"N/A\"}\n />\n <StatCard\n title=\"Total Duration\"\n value={formatHours((stats?.totalDuration ?? 0) / 3600)}\n icon={<Clock className=\"h-5 w-5\" />}\n description=\"Tracked time\"\n />\n </div>\n\n {locations.length === 0 && !entriesLoading ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No location entries found\"\n description=\"With location tracking enabled, the places you work from will show up here with time spent at each. You can also add an entry manually.\"\n action={addButton}\n />\n ) : (\n <>\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop\">\n <div className=\"divide-y divide-border\">\n {locations.map((location: LocationEntryFieldsFragment) => (\n <div\n key={location.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 <MapPin className=\"h-5 w-5 text-text-secondary\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {location.address ?? \"Unknown Location\"}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {location.geofenceId ? \"Geofenced\" : \"Manual\"} &middot;{\" \"}\n {new Date(location.startTime).toLocaleDateString()}\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 {location.duration\n ? formatHours(location.duration / 3600)\n : \"Active\"}\n </p>\n <p className=\"text-xs text-text-secondary\">duration</p>\n </div>\n <button\n onClick={() => setConfirmDelete(location)}\n aria-label=\"Delete entry\"\n className=\"rounded-md border border-border-subtle p-1.5 text-status-error-text hover:bg-status-error-bg\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n ))}\n </div>\n </div>\n {hasMore && (\n <div className=\"mt-6 flex justify-center\">\n <button\n onClick={handleLoadMore}\n disabled={loadingMore}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50 disabled:opacity-50\"\n >\n {loadingMore ? \"Loading...\" : \"Load more\"}\n </button>\n </div>\n )}\n </>\n )}\n\n <Modal\n open={formOpen}\n onClose={() => setFormOpen(false)}\n title=\"Add Location Entry\"\n description=\"Manually log a work location. Only you and people you share with can see this.\"\n size=\"lg\"\n footer={\n <>\n <button\n onClick={() => setFormOpen(false)}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Cancel\n </button>\n <button\n onClick={handleSubmit}\n disabled={creating || !canSubmit}\n className=\"rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {creating ? \"Saving...\" : \"Add Entry\"}\n </button>\n </>\n }\n >\n {createError && (\n <div className=\"mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text\">\n Failed to add location entry. Please check the values and try again.\n </div>\n )}\n {!currentUser?.id && (\n <div className=\"mb-4 rounded-lg border border-status-warning-bg/40 bg-status-warning-bg p-3 text-sm text-status-warning-text\">\n You must be signed in to add a location entry.\n </div>\n )}\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Address (optional)\n </label>\n <input\n type=\"text\"\n placeholder=\"e.g. Client site, Chennai\"\n value={form.address}\n onChange={(e) => setForm({ ...form, address: e.target.value })}\n className={inputClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Latitude\n </label>\n <input\n type=\"number\"\n step=\"any\"\n placeholder=\"0.000000\"\n value={form.latitude}\n onChange={(e) => setForm({ ...form, latitude: e.target.value })}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Longitude\n </label>\n <input\n type=\"number\"\n step=\"any\"\n placeholder=\"0.000000\"\n value={form.longitude}\n onChange={(e) =>\n setForm({ ...form, longitude: e.target.value })\n }\n className={inputClass}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Start Time\n </label>\n <input\n type=\"datetime-local\"\n value={form.startTime}\n onChange={(e) =>\n setForm({ ...form, startTime: e.target.value })\n }\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n End Time (optional)\n </label>\n <input\n type=\"datetime-local\"\n value={form.endTime}\n onChange={(e) => setForm({ ...form, endTime: e.target.value })}\n className={inputClass}\n />\n </div>\n </div>\n </div>\n </Modal>\n\n <Modal\n open={confirmDelete !== null}\n onClose={() => setConfirmDelete(null)}\n title=\"Delete Location Entry\"\n description=\"This action cannot be undone.\"\n footer={\n <>\n <button\n onClick={() => setConfirmDelete(null)}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Cancel\n </button>\n <button\n onClick={handleDelete}\n disabled={deleting}\n className=\"rounded-md bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg/80 disabled:opacity-50\"\n >\n {deleting ? \"Deleting...\" : \"Delete\"}\n </button>\n </>\n }\n >\n <p className=\"text-sm text-text-secondary\">\n Are you sure you want to delete this location entry\n {confirmDelete?.address ? ` at ${confirmDelete.address}` : \"\"}?\n </p>\n </Modal>\n </PageLayout>\n );\n}\n"],"mappings":"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"}