@burdenoff/microfe-timecampus 2026.719.3 → 2026.719.4

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.
@@ -2,7 +2,7 @@ import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
2
2
  import { formatRelativeTime 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 { useGetTimeEntryStatsQuery as i, useListWorkPatternsQuery as a } from "../../../generated/wspace-operations.js";
5
+ import { useGetTimeEntryStatsQuery as i, useListTeamWorkPatternsQuery as a } from "../../../generated/wspace-operations.js";
6
6
  import { PageLayout as o } from "../../components/PageLayout.js";
7
7
  import { StatusBadge as s } from "../../components/StatusBadge.js";
8
8
  import { EmptyState as c } from "../../components/EmptyState.js";
@@ -13,16 +13,16 @@ import "../../components/index.js";
13
13
  import { Activity as f, CheckCircle as p, Clock as m, Users as h, Zap as g } from "lucide-react";
14
14
  import { jsx as _, jsxs as v } from "react/jsx-runtime";
15
15
  //#region src/timecampus/pages/team-activity/TeamActivityPage.tsx
16
- var y = (e) => `${Number.isInteger(e) ? e : e.toFixed(1)}h`, b = (e) => e.slice(0, 2).toUpperCase(), x = (e) => e.slice(0, 8), S = (e) => new Date(e).toLocaleDateString(void 0, {
16
+ var y = (e) => `${Number.isInteger(e) ? e : e.toFixed(1)}h`, b = (e) => e.slice(0, 2).toUpperCase(), x = (e) => new Date(e).toLocaleDateString(void 0, {
17
17
  month: "short",
18
18
  day: "numeric"
19
19
  });
20
- function C() {
21
- let { workspaceId: C = "" } = e(), { data: w, loading: T, error: E } = i({ skip: !C }), { data: D, loading: O, error: k } = a({
20
+ function S() {
21
+ let { workspaceId: S = "" } = e(), { data: C, loading: w, error: T } = i({ skip: !S }), { data: E, loading: D, error: O } = a({
22
22
  variables: { pagination: { limit: 10 } },
23
- skip: !C
24
- }), A = w?.timeEntryStats, j = D?.workPatterns ?? [];
25
- if (T && O) return /* @__PURE__ */ _(o, {
23
+ skip: !S
24
+ }), k = C?.timeEntryStats, A = E?.teamWorkPatterns ?? [];
25
+ if (w && D) return /* @__PURE__ */ _(o, {
26
26
  title: "Team Activity",
27
27
  description: "Real-time team activity feed",
28
28
  children: /* @__PURE__ */ _(r, {
@@ -30,7 +30,7 @@ function C() {
30
30
  count: 5
31
31
  })
32
32
  });
33
- if (E && k) return /* @__PURE__ */ _(o, {
33
+ if (T && O) return /* @__PURE__ */ _(o, {
34
34
  title: "Team Activity",
35
35
  description: "Real-time team activity feed",
36
36
  children: /* @__PURE__ */ _("div", {
@@ -38,8 +38,8 @@ function C() {
38
38
  children: "Failed to load team activity data. Please try again later."
39
39
  })
40
40
  });
41
- let M = [...j].reverse().map((e) => ({
42
- label: S(e.date),
41
+ let j = [...A].reverse().map(({ pattern: e }) => ({
42
+ label: x(e.date),
43
43
  focus: Math.round(e.focusHours * 10) / 10,
44
44
  meetings: Math.round(e.meetingHours * 10) / 10,
45
45
  breaks: Math.round(e.breakHours * 10) / 10
@@ -57,34 +57,34 @@ function C() {
57
57
  /* @__PURE__ */ _(n, {
58
58
  featured: !0,
59
59
  title: "Active Timers",
60
- value: String(A?.activeTimers ?? 0),
60
+ value: String(k?.activeTimers ?? 0),
61
61
  icon: /* @__PURE__ */ _(h, { className: "h-5 w-5" })
62
62
  }),
63
63
  /* @__PURE__ */ _(n, {
64
64
  title: "Total Entries",
65
- value: String(A?.totalEntries ?? 0),
65
+ value: String(k?.totalEntries ?? 0),
66
66
  icon: /* @__PURE__ */ _(p, { className: "h-5 w-5" }),
67
67
  description: "Today"
68
68
  }),
69
69
  /* @__PURE__ */ _(n, {
70
70
  title: "Team Hours",
71
- value: y(Math.round(A?.totalHours ?? 0)),
71
+ value: y(Math.round(k?.totalHours ?? 0)),
72
72
  icon: /* @__PURE__ */ _(m, { className: "h-5 w-5" }),
73
73
  description: "Total tracked"
74
74
  }),
75
75
  /* @__PURE__ */ _(n, {
76
76
  title: "Billable Hours",
77
- value: y(Math.round(A?.billableHours ?? 0)),
77
+ value: y(Math.round(k?.billableHours ?? 0)),
78
78
  icon: /* @__PURE__ */ _(f, { className: "h-5 w-5" }),
79
79
  iconBgColor: "bg-status-success-bg text-status-success-text"
80
80
  })
81
81
  ]
82
82
  }),
83
- M.length > 0 && /* @__PURE__ */ _(l, {
83
+ j.length > 0 && /* @__PURE__ */ _(l, {
84
84
  title: "Recent Work Patterns",
85
85
  description: "Focus, meeting and break hours across the latest logged patterns.",
86
86
  children: /* @__PURE__ */ _(u, {
87
- data: M,
87
+ data: j,
88
88
  xKey: "label",
89
89
  stacked: !0,
90
90
  height: 240,
@@ -111,36 +111,40 @@ function C() {
111
111
  children: [/* @__PURE__ */ _("h3", {
112
112
  className: "mb-4 text-sm font-semibold text-text-primary",
113
113
  children: "Activity Feed"
114
- }), j.length === 0 && !O ? /* @__PURE__ */ _(c, {
114
+ }), A.length === 0 && !D ? /* @__PURE__ */ _(c, {
115
115
  illustration: "empty-notifications",
116
116
  title: "No recent activity yet",
117
117
  description: "As your team tracks time and completes focus sessions, their activity will stream in here."
118
118
  }) : /* @__PURE__ */ _("ul", {
119
119
  className: "space-y-3",
120
- children: j.map((e) => {
121
- let n = e.deepWorkSessions > 0, r = !!e.energyLevel || !!e.mood || !!e.stressLevel;
120
+ children: A.map(({ memberName: e, memberAvatar: n, pattern: r }, i) => {
121
+ let a = r.deepWorkSessions > 0, o = !!r.energyLevel || !!r.mood || !!r.stressLevel;
122
122
  return /* @__PURE__ */ _("li", {
123
123
  className: "rounded-lg border border-border-subtle bg-bg-surface/40 p-4",
124
124
  children: /* @__PURE__ */ v("div", {
125
125
  className: "flex items-start gap-3",
126
- children: [/* @__PURE__ */ _("div", {
126
+ children: [n ? /* @__PURE__ */ _("img", {
127
+ src: n,
128
+ alt: e,
129
+ className: "mt-0.5 h-9 w-9 shrink-0 rounded-full border border-border-seam object-cover"
130
+ }) : /* @__PURE__ */ _("div", {
127
131
  className: "mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-action-primary-bg/10 text-xs font-semibold uppercase text-primary",
128
- children: b(e.userId)
132
+ children: b(e)
129
133
  }), /* @__PURE__ */ v("div", {
130
134
  className: "min-w-0 flex-1",
131
135
  children: [
132
136
  /* @__PURE__ */ v("div", {
133
137
  className: "flex flex-wrap items-center gap-x-2 gap-y-1",
134
138
  children: [
135
- /* @__PURE__ */ v("span", {
136
- className: "inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary",
137
- children: ["#", x(e.userId)]
139
+ /* @__PURE__ */ _("span", {
140
+ className: "text-sm font-medium text-text-primary",
141
+ children: e
138
142
  }),
139
143
  /* @__PURE__ */ _("span", {
140
144
  className: "text-xs text-text-secondary",
141
- children: t(e.createdAt)
145
+ children: t(r.createdAt)
142
146
  }),
143
- n && /* @__PURE__ */ _(s, {
147
+ a && /* @__PURE__ */ _(s, {
144
148
  variant: "success",
145
149
  label: "Deep work"
146
150
  })
@@ -148,19 +152,19 @@ function C() {
148
152
  }),
149
153
  /* @__PURE__ */ v("p", {
150
154
  className: "mt-1.5 flex items-center gap-1.5 text-sm font-medium text-text-primary",
151
- children: [_(n ? g : f, { className: "h-3.5 w-3.5 text-primary" }), n ? "Completed deep work session" : "Logged work pattern"]
155
+ children: [_(a ? g : f, { className: "h-3.5 w-3.5 text-primary" }), a ? "Completed deep work session" : "Logged work pattern"]
152
156
  }),
153
157
  /* @__PURE__ */ v("div", {
154
158
  className: "mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs text-text-secondary",
155
159
  children: [
156
- /* @__PURE__ */ v("span", { children: [e.focusHours.toFixed(1), "h focus"] }),
157
- /* @__PURE__ */ v("span", { children: [e.meetingHours.toFixed(1), "h meetings"] }),
158
- e.deepWorkSessions > 0 && /* @__PURE__ */ v("span", { children: [e.deepWorkSessions, " deep sessions"] }),
159
- e.interruptions > 0 && /* @__PURE__ */ v("span", { children: [e.interruptions, " interruptions"] }),
160
- /* @__PURE__ */ _("span", { children: S(e.date) })
160
+ /* @__PURE__ */ v("span", { children: [r.focusHours.toFixed(1), "h focus"] }),
161
+ /* @__PURE__ */ v("span", { children: [r.meetingHours.toFixed(1), "h meetings"] }),
162
+ r.deepWorkSessions > 0 && /* @__PURE__ */ v("span", { children: [r.deepWorkSessions, " deep sessions"] }),
163
+ r.interruptions > 0 && /* @__PURE__ */ v("span", { children: [r.interruptions, " interruptions"] }),
164
+ /* @__PURE__ */ _("span", { children: x(r.date) })
161
165
  ]
162
166
  }),
163
- e.productivityScore != null && /* @__PURE__ */ v("div", {
167
+ r.productivityScore != null && /* @__PURE__ */ v("div", {
164
168
  className: "mt-3",
165
169
  children: [/* @__PURE__ */ v("div", {
166
170
  className: "mb-1 flex items-center justify-between text-xs",
@@ -169,10 +173,10 @@ function C() {
169
173
  children: "Productivity"
170
174
  }), /* @__PURE__ */ v("span", {
171
175
  className: "tabular-nums text-text-primary",
172
- children: [Math.round(e.productivityScore), "%"]
176
+ children: [Math.round(r.productivityScore), "%"]
173
177
  })]
174
178
  }), /* @__PURE__ */ _(d, {
175
- value: e.productivityScore,
179
+ value: r.productivityScore,
176
180
  max: 100,
177
181
  higherIsWorse: !1,
178
182
  thresholds: {
@@ -183,27 +187,27 @@ function C() {
183
187
  size: "sm"
184
188
  })]
185
189
  }),
186
- r && /* @__PURE__ */ v("div", {
190
+ o && /* @__PURE__ */ v("div", {
187
191
  className: "mt-3 flex flex-wrap gap-1.5",
188
192
  children: [
189
- e.energyLevel && /* @__PURE__ */ _(s, {
193
+ r.energyLevel && /* @__PURE__ */ _(s, {
190
194
  variant: "info",
191
- label: `Energy: ${e.energyLevel}`
195
+ label: `Energy: ${r.energyLevel}`
192
196
  }),
193
- e.mood && /* @__PURE__ */ _(s, {
197
+ r.mood && /* @__PURE__ */ _(s, {
194
198
  variant: "muted",
195
- label: `Mood: ${e.mood}`
199
+ label: `Mood: ${r.mood}`
196
200
  }),
197
- e.stressLevel && /* @__PURE__ */ _(s, {
201
+ r.stressLevel && /* @__PURE__ */ _(s, {
198
202
  variant: "warning",
199
- label: `Stress: ${e.stressLevel}`
203
+ label: `Stress: ${r.stressLevel}`
200
204
  })
201
205
  ]
202
206
  })
203
207
  ]
204
208
  })]
205
209
  })
206
- }, e.id);
210
+ }, `${r.id}-${i}`);
207
211
  })
208
212
  })]
209
213
  })
@@ -212,6 +216,6 @@ function C() {
212
216
  });
213
217
  }
214
218
  //#endregion
215
- export { C as TeamActivityPage };
219
+ export { S as TeamActivityPage };
216
220
 
217
221
  //# sourceMappingURL=TeamActivityPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TeamActivityPage.js","names":[],"sources":["../../../../src/timecampus/pages/team-activity/TeamActivityPage.tsx"],"sourcesContent":["import { Activity, Users, Clock, CheckCircle, Zap } from \"lucide-react\";\nimport {\n PageLayout,\n StatCard,\n ChartCard,\n StatusBadge,\n EmptyState,\n LoadingSkeleton,\n BarsChart,\n ProgressMeter,\n} from \"../../components\";\nimport { formatRelativeTime } from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useGetTimeEntryStatsQuery,\n useListWorkPatternsQuery,\n type WorkPatternFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nconst formatHrs = (v: number): string =>\n `${Number.isInteger(v) ? v : v.toFixed(1)}h`;\n\n// Member identity (name/avatar) is owned by the groups module and is NOT\n// fetched by this page — the work-pattern feed only carries a raw `userId`\n// UUID. Until a member-lookup field is available we render a stable monogram +\n// short-id chip instead of a bare UUID. See report FUNC-GAP note.\nconst monogram = (userId: string): string => userId.slice(0, 2).toUpperCase();\nconst shortId = (userId: string): string => userId.slice(0, 8);\n\nconst shortDate = (value: string): string =>\n new Date(value).toLocaleDateString(undefined, {\n month: \"short\",\n day: \"numeric\",\n });\n\nexport function TeamActivityPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const {\n data: statsData,\n loading: statsLoading,\n error: statsError,\n } = useGetTimeEntryStatsQuery({ skip: !workspaceId });\n const {\n data: patternsData,\n loading: patternsLoading,\n error: patternsError,\n } = useListWorkPatternsQuery({\n variables: { pagination: { limit: 10 } },\n skip: !workspaceId,\n });\n\n const stats = statsData?.timeEntryStats;\n const patterns = patternsData?.workPatterns ?? [];\n\n if (statsLoading && patternsLoading) {\n return (\n <PageLayout\n title=\"Team Activity\"\n description=\"Real-time team activity feed\"\n >\n <LoadingSkeleton variant=\"list\" count={5} />\n </PageLayout>\n );\n }\n\n if (statsError && patternsError) {\n return (\n <PageLayout\n title=\"Team Activity\"\n description=\"Real-time team activity feed\"\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 activity data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n // Recent work patterns as a real stacked composition — sourced only from the\n // rows already fetched above (oldest → newest for a left-to-right timeline).\n const patternBars = [...patterns].reverse().map((pattern) => ({\n label: shortDate(pattern.date),\n focus: Math.round(pattern.focusHours * 10) / 10,\n meetings: Math.round(pattern.meetingHours * 10) / 10,\n breaks: Math.round(pattern.breakHours * 10) / 10,\n }));\n\n return (\n <PageLayout\n title=\"Team Activity\"\n description=\"Real-time team activity feed\"\n purpose=\"A live pulse of what your team is doing right now — who's tracking, recent deep-work sessions, and today's hours — so you stay connected to the team's progress without interrupting anyone.\"\n >\n <div className=\"space-y-6\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n featured\n title=\"Active Timers\"\n value={String(stats?.activeTimers ?? 0)}\n icon={<Users className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Total Entries\"\n value={String(stats?.totalEntries ?? 0)}\n icon={<CheckCircle className=\"h-5 w-5\" />}\n description=\"Today\"\n />\n <StatCard\n title=\"Team Hours\"\n value={formatHrs(Math.round(stats?.totalHours ?? 0))}\n icon={<Clock className=\"h-5 w-5\" />}\n description=\"Total tracked\"\n />\n <StatCard\n title=\"Billable Hours\"\n value={formatHrs(Math.round(stats?.billableHours ?? 0))}\n icon={<Activity className=\"h-5 w-5\" />}\n iconBgColor=\"bg-status-success-bg text-status-success-text\"\n />\n </div>\n\n {patternBars.length > 0 && (\n <ChartCard\n title=\"Recent Work Patterns\"\n description=\"Focus, meeting and break hours across the latest logged patterns.\"\n >\n <BarsChart\n data={patternBars}\n xKey=\"label\"\n stacked\n height={240}\n valueFormat={formatHrs}\n series={[\n { key: \"focus\", label: \"Focus\" },\n { key: \"meetings\", label: \"Meetings\" },\n { key: \"breaks\", label: \"Breaks\" },\n ]}\n emptyLabel=\"No work patterns yet\"\n />\n </ChartCard>\n )}\n\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop p-4 sm:p-6\">\n <h3 className=\"mb-4 text-sm font-semibold text-text-primary\">\n Activity Feed\n </h3>\n {patterns.length === 0 && !patternsLoading ? (\n <EmptyState\n illustration=\"empty-notifications\"\n title=\"No recent activity yet\"\n description=\"As your team tracks time and completes focus sessions, their activity will stream in here.\"\n />\n ) : (\n <ul className=\"space-y-3\">\n {patterns.map((pattern: WorkPatternFieldsFragment) => {\n const isDeepWork = pattern.deepWorkSessions > 0;\n const hasVitals =\n Boolean(pattern.energyLevel) ||\n Boolean(pattern.mood) ||\n Boolean(pattern.stressLevel);\n return (\n <li\n key={pattern.id}\n className=\"rounded-lg border border-border-subtle bg-bg-surface/40 p-4\"\n >\n <div className=\"flex items-start gap-3\">\n <div className=\"mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-action-primary-bg/10 text-xs font-semibold uppercase text-primary\">\n {monogram(pattern.userId)}\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1\">\n <span className=\"inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\">\n #{shortId(pattern.userId)}\n </span>\n <span className=\"text-xs text-text-secondary\">\n {formatRelativeTime(pattern.createdAt)}\n </span>\n {isDeepWork && (\n <StatusBadge variant=\"success\" label=\"Deep work\" />\n )}\n </div>\n <p className=\"mt-1.5 flex items-center gap-1.5 text-sm font-medium text-text-primary\">\n {isDeepWork ? (\n <Zap className=\"h-3.5 w-3.5 text-primary\" />\n ) : (\n <Activity className=\"h-3.5 w-3.5 text-primary\" />\n )}\n {isDeepWork\n ? \"Completed deep work session\"\n : \"Logged work pattern\"}\n </p>\n <div className=\"mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs text-text-secondary\">\n <span>{pattern.focusHours.toFixed(1)}h focus</span>\n <span>\n {pattern.meetingHours.toFixed(1)}h meetings\n </span>\n {pattern.deepWorkSessions > 0 && (\n <span>\n {pattern.deepWorkSessions} deep sessions\n </span>\n )}\n {pattern.interruptions > 0 && (\n <span>{pattern.interruptions} interruptions</span>\n )}\n <span>{shortDate(pattern.date)}</span>\n </div>\n {pattern.productivityScore != null && (\n <div className=\"mt-3\">\n <div className=\"mb-1 flex items-center justify-between text-xs\">\n <span className=\"text-text-secondary\">\n Productivity\n </span>\n <span className=\"tabular-nums text-text-primary\">\n {Math.round(pattern.productivityScore)}%\n </span>\n </div>\n <ProgressMeter\n value={pattern.productivityScore}\n max={100}\n higherIsWorse={false}\n thresholds={{ warning: 0.6, danger: 0.4 }}\n showValue={false}\n size=\"sm\"\n />\n </div>\n )}\n {hasVitals && (\n <div className=\"mt-3 flex flex-wrap gap-1.5\">\n {pattern.energyLevel && (\n <StatusBadge\n variant=\"info\"\n label={`Energy: ${pattern.energyLevel}`}\n />\n )}\n {pattern.mood && (\n <StatusBadge\n variant=\"muted\"\n label={`Mood: ${pattern.mood}`}\n />\n )}\n {pattern.stressLevel && (\n <StatusBadge\n variant=\"warning\"\n label={`Stress: ${pattern.stressLevel}`}\n />\n )}\n </div>\n )}\n </div>\n </div>\n </li>\n );\n })}\n </ul>\n )}\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AAmBA,IAAM,KAAa,MACjB,GAAG,OAAO,UAAU,EAAE,GAAG,IAAI,EAAE,QAAQ,EAAE,CAAC,IAMtC,KAAY,MAA2B,EAAO,MAAM,GAAG,EAAE,CAAC,aAAa,EACvE,KAAW,MAA2B,EAAO,MAAM,GAAG,EAAE,EAExD,KAAa,MACjB,IAAI,KAAK,EAAM,CAAC,mBAAmB,KAAA,GAAW;CAC5C,OAAO;CACP,KAAK;CACN,CAAC;AAEJ,SAAgB,IAAmB;CACjC,IAAM,EAAE,iBAAc,OAAO,GAAe,EAEtC,EACJ,MAAM,GACN,SAAS,GACT,OAAO,MACL,EAA0B,EAAE,MAAM,CAAC,GAAa,CAAC,EAC/C,EACJ,MAAM,GACN,SAAS,GACT,OAAO,MACL,EAAyB;EAC3B,WAAW,EAAE,YAAY,EAAE,OAAO,IAAI,EAAE;EACxC,MAAM,CAAC;EACR,CAAC,EAEI,IAAQ,GAAW,gBACnB,IAAW,GAAc,gBAAgB,EAAE;AAEjD,KAAI,KAAgB,EAClB,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA;EACjC,CAAA;AAIjB,KAAI,KAAc,EAChB,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,OAAD;GAAK,WAAU;aAAoG;GAE7G,CAAA;EACK,CAAA;CAMjB,IAAM,IAAc,CAAC,GAAG,EAAS,CAAC,SAAS,CAAC,KAAK,OAAa;EAC5D,OAAO,EAAU,EAAQ,KAAK;EAC9B,OAAO,KAAK,MAAM,EAAQ,aAAa,GAAG,GAAG;EAC7C,UAAU,KAAK,MAAM,EAAQ,eAAe,GAAG,GAAG;EAClD,QAAQ,KAAK,MAAM,EAAQ,aAAa,GAAG,GAAG;EAC/C,EAAE;AAEH,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SAAQ;YAER,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OACE,UAAA;OACA,OAAM;OACN,OAAO,OAAO,GAAO,gBAAgB,EAAE;OACvC,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;OACnC,CAAA;MACF,kBAAC,GAAD;OACE,OAAM;OACN,OAAO,OAAO,GAAO,gBAAgB,EAAE;OACvC,MAAM,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;OACzC,aAAY;OACZ,CAAA;MACF,kBAAC,GAAD;OACE,OAAM;OACN,OAAO,EAAU,KAAK,MAAM,GAAO,cAAc,EAAE,CAAC;OACpD,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;OACnC,aAAY;OACZ,CAAA;MACF,kBAAC,GAAD;OACE,OAAM;OACN,OAAO,EAAU,KAAK,MAAM,GAAO,iBAAiB,EAAE,CAAC;OACvD,MAAM,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;OACtC,aAAY;OACZ,CAAA;MACE;;IAEL,EAAY,SAAS,KACpB,kBAAC,GAAD;KACE,OAAM;KACN,aAAY;eAEZ,kBAAC,GAAD;MACE,MAAM;MACN,MAAK;MACL,SAAA;MACA,QAAQ;MACR,aAAa;MACb,QAAQ;OACN;QAAE,KAAK;QAAS,OAAO;QAAS;OAChC;QAAE,KAAK;QAAY,OAAO;QAAY;OACtC;QAAE,KAAK;QAAU,OAAO;QAAU;OACnC;MACD,YAAW;MACX,CAAA;KACQ,CAAA;IAGd,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAA+C;MAExD,CAAA,EACJ,EAAS,WAAW,KAAK,CAAC,IACzB,kBAAC,GAAD;MACE,cAAa;MACb,OAAM;MACN,aAAY;MACZ,CAAA,GAEF,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAS,KAAK,MAAuC;OACpD,IAAM,IAAa,EAAQ,mBAAmB,GACxC,IACJ,EAAQ,EAAQ,eAChB,EAAQ,EAAQ,QAChB,EAAQ,EAAQ;AAClB,cACE,kBAAC,MAAD;QAEE,WAAU;kBAEV,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;oBACZ,EAAS,EAAQ,OAAO;UACrB,CAAA,EACN,kBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,kBAAC,OAAD;YAAK,WAAU;sBAAf;aACE,kBAAC,QAAD;cAAM,WAAU;wBAAhB,CAAyH,KACrH,EAAQ,EAAQ,OAAO,CACpB;;aACP,kBAAC,QAAD;cAAM,WAAU;wBACb,EAAmB,EAAQ,UAAU;cACjC,CAAA;aACN,KACC,kBAAC,GAAD;cAAa,SAAQ;cAAU,OAAM;cAAc,CAAA;aAEjD;;WACN,kBAAC,KAAD;YAAG,WAAU;sBAAb,CAEI,EADD,IACE,IAEA,GAFD,EAAK,WAAU,4BAA6B,CAEK,EAElD,IACG,gCACA,sBACF;;WACJ,kBAAC,OAAD;YAAK,WAAU;sBAAf;aACE,kBAAC,QAAD,EAAA,UAAA,CAAO,EAAQ,WAAW,QAAQ,EAAE,EAAC,UAAc,EAAA,CAAA;aACnD,kBAAC,QAAD,EAAA,UAAA,CACG,EAAQ,aAAa,QAAQ,EAAE,EAAC,aAC5B,EAAA,CAAA;aACN,EAAQ,mBAAmB,KAC1B,kBAAC,QAAD,EAAA,UAAA,CACG,EAAQ,kBAAiB,iBACrB,EAAA,CAAA;aAER,EAAQ,gBAAgB,KACvB,kBAAC,QAAD,EAAA,UAAA,CAAO,EAAQ,eAAc,iBAAqB,EAAA,CAAA;aAEpD,kBAAC,QAAD,EAAA,UAAO,EAAU,EAAQ,KAAK,EAAQ,CAAA;aAClC;;WACL,EAAQ,qBAAqB,QAC5B,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,OAAD;aAAK,WAAU;uBAAf,CACE,kBAAC,QAAD;cAAM,WAAU;wBAAsB;cAE/B,CAAA,EACP,kBAAC,QAAD;cAAM,WAAU;wBAAhB,CACG,KAAK,MAAM,EAAQ,kBAAkB,EAAC,IAClC;gBACH;gBACN,kBAAC,GAAD;aACE,OAAO,EAAQ;aACf,KAAK;aACL,eAAe;aACf,YAAY;cAAE,SAAS;cAAK,QAAQ;cAAK;aACzC,WAAW;aACX,MAAK;aACL,CAAA,CACE;;WAEP,KACC,kBAAC,OAAD;YAAK,WAAU;sBAAf;aACG,EAAQ,eACP,kBAAC,GAAD;cACE,SAAQ;cACR,OAAO,WAAW,EAAQ;cAC1B,CAAA;aAEH,EAAQ,QACP,kBAAC,GAAD;cACE,SAAQ;cACR,OAAO,SAAS,EAAQ;cACxB,CAAA;aAEH,EAAQ,eACP,kBAAC,GAAD;cACE,SAAQ;cACR,OAAO,WAAW,EAAQ;cAC1B,CAAA;aAEA;;WAEJ;YACF;;QACH,EAxFE,EAAQ,GAwFV;QAEP;MACC,CAAA,CAEH;;IACF;;EACK,CAAA"}
1
+ {"version":3,"file":"TeamActivityPage.js","names":[],"sources":["../../../../src/timecampus/pages/team-activity/TeamActivityPage.tsx"],"sourcesContent":["import { Activity, Users, Clock, CheckCircle, Zap } from \"lucide-react\";\nimport {\n PageLayout,\n StatCard,\n ChartCard,\n StatusBadge,\n EmptyState,\n LoadingSkeleton,\n BarsChart,\n ProgressMeter,\n} from \"../../components\";\nimport { formatRelativeTime } from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useGetTimeEntryStatsQuery,\n useListTeamWorkPatternsQuery,\n type TeamWorkPatternEntryFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nconst formatHrs = (v: number): string =>\n `${Number.isInteger(v) ? v : v.toFixed(1)}h`;\n\n// Fallback monogram/short-id for the rare case the identity service didn't\n// resolve a member (left the workspace, lookup failure, etc.) — the backend\n// (teamWorkPatterns) already substitutes a \"Member <shortId>\" display name in\n// that case, this just derives a matching two-letter monogram from it.\nconst monogram = (memberName: string): string =>\n memberName.slice(0, 2).toUpperCase();\n\nconst shortDate = (value: string): string =>\n new Date(value).toLocaleDateString(undefined, {\n month: \"short\",\n day: \"numeric\",\n });\n\nexport function TeamActivityPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const {\n data: statsData,\n loading: statsLoading,\n error: statsError,\n } = useGetTimeEntryStatsQuery({ skip: !workspaceId });\n // Team-wide (BOFF-4037): every active workspace member's recent activity,\n // each resolved to that member's real display name/avatar — never just\n // the calling actor's own patterns repeated under everyone else's name.\n const {\n data: patternsData,\n loading: patternsLoading,\n error: patternsError,\n } = useListTeamWorkPatternsQuery({\n variables: { pagination: { limit: 10 } },\n skip: !workspaceId,\n });\n\n const stats = statsData?.timeEntryStats;\n const entries = patternsData?.teamWorkPatterns ?? [];\n\n if (statsLoading && patternsLoading) {\n return (\n <PageLayout\n title=\"Team Activity\"\n description=\"Real-time team activity feed\"\n >\n <LoadingSkeleton variant=\"list\" count={5} />\n </PageLayout>\n );\n }\n\n if (statsError && patternsError) {\n return (\n <PageLayout\n title=\"Team Activity\"\n description=\"Real-time team activity feed\"\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 activity data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n // Recent work patterns as a real stacked composition — sourced only from the\n // rows already fetched above (oldest → newest for a left-to-right timeline).\n const patternBars = [...entries].reverse().map(({ pattern }) => ({\n label: shortDate(pattern.date),\n focus: Math.round(pattern.focusHours * 10) / 10,\n meetings: Math.round(pattern.meetingHours * 10) / 10,\n breaks: Math.round(pattern.breakHours * 10) / 10,\n }));\n\n return (\n <PageLayout\n title=\"Team Activity\"\n description=\"Real-time team activity feed\"\n purpose=\"A live pulse of what your team is doing right now — who's tracking, recent deep-work sessions, and today's hours — so you stay connected to the team's progress without interrupting anyone.\"\n >\n <div className=\"space-y-6\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n featured\n title=\"Active Timers\"\n value={String(stats?.activeTimers ?? 0)}\n icon={<Users className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Total Entries\"\n value={String(stats?.totalEntries ?? 0)}\n icon={<CheckCircle className=\"h-5 w-5\" />}\n description=\"Today\"\n />\n <StatCard\n title=\"Team Hours\"\n value={formatHrs(Math.round(stats?.totalHours ?? 0))}\n icon={<Clock className=\"h-5 w-5\" />}\n description=\"Total tracked\"\n />\n <StatCard\n title=\"Billable Hours\"\n value={formatHrs(Math.round(stats?.billableHours ?? 0))}\n icon={<Activity className=\"h-5 w-5\" />}\n iconBgColor=\"bg-status-success-bg text-status-success-text\"\n />\n </div>\n\n {patternBars.length > 0 && (\n <ChartCard\n title=\"Recent Work Patterns\"\n description=\"Focus, meeting and break hours across the latest logged patterns.\"\n >\n <BarsChart\n data={patternBars}\n xKey=\"label\"\n stacked\n height={240}\n valueFormat={formatHrs}\n series={[\n { key: \"focus\", label: \"Focus\" },\n { key: \"meetings\", label: \"Meetings\" },\n { key: \"breaks\", label: \"Breaks\" },\n ]}\n emptyLabel=\"No work patterns yet\"\n />\n </ChartCard>\n )}\n\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop p-4 sm:p-6\">\n <h3 className=\"mb-4 text-sm font-semibold text-text-primary\">\n Activity Feed\n </h3>\n {entries.length === 0 && !patternsLoading ? (\n <EmptyState\n illustration=\"empty-notifications\"\n title=\"No recent activity yet\"\n description=\"As your team tracks time and completes focus sessions, their activity will stream in here.\"\n />\n ) : (\n <ul className=\"space-y-3\">\n {entries.map(\n (\n {\n memberName,\n memberAvatar,\n pattern,\n }: TeamWorkPatternEntryFieldsFragment,\n index: number,\n ) => {\n const isDeepWork = pattern.deepWorkSessions > 0;\n const hasVitals =\n Boolean(pattern.energyLevel) ||\n Boolean(pattern.mood) ||\n Boolean(pattern.stressLevel);\n return (\n <li\n key={`${pattern.id}-${index}`}\n className=\"rounded-lg border border-border-subtle bg-bg-surface/40 p-4\"\n >\n <div className=\"flex items-start gap-3\">\n {memberAvatar ? (\n <img\n src={memberAvatar}\n alt={memberName}\n className=\"mt-0.5 h-9 w-9 shrink-0 rounded-full border border-border-seam object-cover\"\n />\n ) : (\n <div className=\"mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-action-primary-bg/10 text-xs font-semibold uppercase text-primary\">\n {monogram(memberName)}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1\">\n <span className=\"text-sm font-medium text-text-primary\">\n {memberName}\n </span>\n <span className=\"text-xs text-text-secondary\">\n {formatRelativeTime(pattern.createdAt)}\n </span>\n {isDeepWork && (\n <StatusBadge\n variant=\"success\"\n label=\"Deep work\"\n />\n )}\n </div>\n <p className=\"mt-1.5 flex items-center gap-1.5 text-sm font-medium text-text-primary\">\n {isDeepWork ? (\n <Zap className=\"h-3.5 w-3.5 text-primary\" />\n ) : (\n <Activity className=\"h-3.5 w-3.5 text-primary\" />\n )}\n {isDeepWork\n ? \"Completed deep work session\"\n : \"Logged work pattern\"}\n </p>\n <div className=\"mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs text-text-secondary\">\n <span>{pattern.focusHours.toFixed(1)}h focus</span>\n <span>\n {pattern.meetingHours.toFixed(1)}h meetings\n </span>\n {pattern.deepWorkSessions > 0 && (\n <span>\n {pattern.deepWorkSessions} deep sessions\n </span>\n )}\n {pattern.interruptions > 0 && (\n <span>{pattern.interruptions} interruptions</span>\n )}\n <span>{shortDate(pattern.date)}</span>\n </div>\n {pattern.productivityScore != null && (\n <div className=\"mt-3\">\n <div className=\"mb-1 flex items-center justify-between text-xs\">\n <span className=\"text-text-secondary\">\n Productivity\n </span>\n <span className=\"tabular-nums text-text-primary\">\n {Math.round(pattern.productivityScore)}%\n </span>\n </div>\n <ProgressMeter\n value={pattern.productivityScore}\n max={100}\n higherIsWorse={false}\n thresholds={{ warning: 0.6, danger: 0.4 }}\n showValue={false}\n size=\"sm\"\n />\n </div>\n )}\n {hasVitals && (\n <div className=\"mt-3 flex flex-wrap gap-1.5\">\n {pattern.energyLevel && (\n <StatusBadge\n variant=\"info\"\n label={`Energy: ${pattern.energyLevel}`}\n />\n )}\n {pattern.mood && (\n <StatusBadge\n variant=\"muted\"\n label={`Mood: ${pattern.mood}`}\n />\n )}\n {pattern.stressLevel && (\n <StatusBadge\n variant=\"warning\"\n label={`Stress: ${pattern.stressLevel}`}\n />\n )}\n </div>\n )}\n </div>\n </div>\n </li>\n );\n },\n )}\n </ul>\n )}\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"TimelinePage.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/timeline/TimelinePage.tsx"],"names":[],"mappings":"AA0FA,wBAAgB,YAAY,gCA+N3B"}
1
+ {"version":3,"file":"TimelinePage.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/timeline/TimelinePage.tsx"],"names":[],"mappings":"AAqOA,wBAAgB,YAAY,gCA+M3B"}