@burdenoff/microfe-technospam 2026.1031.4 → 2026.1031.5
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/technospam/lib/a11y.d.ts +11 -0
- package/dist/technospam/lib/a11y.d.ts.map +1 -0
- package/dist/technospam/lib/a11y.js +6 -0
- package/dist/technospam/lib/a11y.js.map +1 -0
- package/dist/technospam/pages/AdminAdminDashboardPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/AdminAdminDashboardPagePage.js +150 -148
- package/dist/technospam/pages/AdminAdminDashboardPagePage.js.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js +47 -45
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesCoursesPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/CoursesCoursesPagePage.js +74 -72
- package/dist/technospam/pages/CoursesCoursesPagePage.js.map +1 -1
- package/dist/technospam/pages/HackathonsHackathonsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/HackathonsHackathonsPagePage.js +83 -81
- package/dist/technospam/pages/HackathonsHackathonsPagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipSessionsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/MentorshipSessionsPagePage.js +99 -97
- package/dist/technospam/pages/MentorshipSessionsPagePage.js.map +1 -1
- package/package.json +1 -1
|
@@ -11,45 +11,46 @@ import { notify as p } from "../lib/toast.js";
|
|
|
11
11
|
import { useCancelSession as m, useCompleteSession as h, useConfirmSession as g, useMentorsQuery as _, useSessionsQuery as v } from "../api/technospamApi.js";
|
|
12
12
|
import "../components/index.js";
|
|
13
13
|
import { FOCUS_PARAMS as y } from "../utils/pageContext.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
14
|
+
import { TAB_PANEL_FOCUS_CLASS as b } from "../lib/a11y.js";
|
|
15
|
+
import { focusableCardProps as x, useFocusedRecordParam as S } from "../hooks/useFocusedRecordParam.js";
|
|
16
|
+
import { useState as C } from "react";
|
|
17
|
+
import { CalendarClock as w, Check as T, CheckCircle2 as E, Clock as D, MessageSquare as O, Users as k, Video as A, X as j } from "lucide-react";
|
|
18
|
+
import { jsx as M, jsxs as N } from "react/jsx-runtime";
|
|
19
|
+
import { Button as P, GlassCard as F, Input as I, Tabs as L, TabsContent as R, TabsList as ee, TabsTrigger as z } from "@burdenoff/fe-libs/ui";
|
|
19
20
|
//#region src/technospam/pages/MentorshipSessionsPagePage.tsx
|
|
20
21
|
var B = new Set(["REQUESTED", "CONFIRMED"]);
|
|
21
22
|
function V({ session: e, showMentor: t, isMentor: n, onCancel: r, onConfirm: a, onComplete: o, busy: u, focused: d, onFocusChange: f }) {
|
|
22
|
-
let p = () => f(e.id), m = () => f(d ? null : e.id), h = B.has(e.status), [g, _] =
|
|
23
|
+
let p = () => f(e.id), m = () => f(d ? null : e.id), h = B.has(e.status), [g, _] = C("none"), [v, y] = C(""), [b, S] = C("5"), [O, k] = C(""), L = async () => {
|
|
23
24
|
await a(e.id, v.trim()), _("none"), y("");
|
|
24
25
|
}, R = async () => {
|
|
25
|
-
await o(e.id, Math.min(5, Math.max(1, Number(
|
|
26
|
+
await o(e.id, Math.min(5, Math.max(1, Number(b) || 5)), O.trim()), _("none");
|
|
26
27
|
};
|
|
27
|
-
return /* @__PURE__ */
|
|
28
|
+
return /* @__PURE__ */ N(F, {
|
|
28
29
|
className: i("flex cursor-pointer flex-col gap-3 p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md", d && "ring-2 ring-accent-primary"),
|
|
29
|
-
...
|
|
30
|
+
...x(d, m),
|
|
30
31
|
children: [
|
|
31
|
-
/* @__PURE__ */
|
|
32
|
+
/* @__PURE__ */ N("div", {
|
|
32
33
|
className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",
|
|
33
|
-
children: [/* @__PURE__ */
|
|
34
|
+
children: [/* @__PURE__ */ N("div", {
|
|
34
35
|
className: "flex items-start gap-3",
|
|
35
|
-
children: [/* @__PURE__ */
|
|
36
|
+
children: [/* @__PURE__ */ M("span", {
|
|
36
37
|
className: "flex h-11 w-11 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-accent-primary to-accent-purple text-white shadow-sm",
|
|
37
|
-
children: /* @__PURE__ */
|
|
38
|
-
}), /* @__PURE__ */
|
|
38
|
+
children: /* @__PURE__ */ M(A, { size: 18 })
|
|
39
|
+
}), /* @__PURE__ */ N("div", {
|
|
39
40
|
className: "min-w-0",
|
|
40
41
|
children: [
|
|
41
|
-
/* @__PURE__ */
|
|
42
|
+
/* @__PURE__ */ N("div", {
|
|
42
43
|
className: "flex flex-wrap items-center gap-2",
|
|
43
44
|
children: [
|
|
44
|
-
/* @__PURE__ */
|
|
45
|
+
/* @__PURE__ */ M("button", {
|
|
45
46
|
type: "button",
|
|
46
47
|
"aria-pressed": d,
|
|
47
48
|
onClick: m,
|
|
48
49
|
className: "truncate text-left text-sm font-semibold text-text-primary hover:text-accent-primary",
|
|
49
50
|
children: e.topic
|
|
50
51
|
}),
|
|
51
|
-
/* @__PURE__ */
|
|
52
|
-
e.rating ? /* @__PURE__ */
|
|
52
|
+
/* @__PURE__ */ M(l, { status: e.status }),
|
|
53
|
+
e.rating ? /* @__PURE__ */ N("span", {
|
|
53
54
|
className: "text-xs text-text-muted",
|
|
54
55
|
children: [
|
|
55
56
|
"★ ",
|
|
@@ -59,107 +60,107 @@ function V({ session: e, showMentor: t, isMentor: n, onCancel: r, onConfirm: a,
|
|
|
59
60
|
}) : null
|
|
60
61
|
]
|
|
61
62
|
}),
|
|
62
|
-
t && e.mentor?.headline ? /* @__PURE__ */
|
|
63
|
+
t && e.mentor?.headline ? /* @__PURE__ */ N("div", {
|
|
63
64
|
className: "mt-0.5 truncate text-xs text-text-secondary",
|
|
64
65
|
children: ["with ", e.mentor.headline]
|
|
65
66
|
}) : null,
|
|
66
|
-
/* @__PURE__ */
|
|
67
|
+
/* @__PURE__ */ N("div", {
|
|
67
68
|
className: "mt-1 flex flex-wrap items-center gap-3 text-xs text-text-muted",
|
|
68
|
-
children: [/* @__PURE__ */
|
|
69
|
+
children: [/* @__PURE__ */ N("span", {
|
|
69
70
|
className: "inline-flex items-center gap-1",
|
|
70
71
|
children: [
|
|
71
|
-
/* @__PURE__ */
|
|
72
|
+
/* @__PURE__ */ M(w, { size: 13 }),
|
|
72
73
|
" ",
|
|
73
74
|
s(e.scheduledAt)
|
|
74
75
|
]
|
|
75
|
-
}), /* @__PURE__ */
|
|
76
|
+
}), /* @__PURE__ */ N("span", {
|
|
76
77
|
className: "inline-flex items-center gap-1",
|
|
77
78
|
children: [
|
|
78
|
-
/* @__PURE__ */
|
|
79
|
+
/* @__PURE__ */ M(D, { size: 13 }),
|
|
79
80
|
" ",
|
|
80
81
|
c(e.durationMinutes)
|
|
81
82
|
]
|
|
82
83
|
})]
|
|
83
84
|
}),
|
|
84
|
-
e.agenda ? /* @__PURE__ */
|
|
85
|
+
e.agenda ? /* @__PURE__ */ M("p", {
|
|
85
86
|
className: "mt-1 line-clamp-2 text-xs text-text-secondary",
|
|
86
87
|
children: e.agenda
|
|
87
88
|
}) : null
|
|
88
89
|
]
|
|
89
90
|
})]
|
|
90
|
-
}), /* @__PURE__ */
|
|
91
|
+
}), /* @__PURE__ */ N("div", {
|
|
91
92
|
className: "flex shrink-0 flex-wrap items-center gap-2",
|
|
92
93
|
children: [
|
|
93
|
-
e.meetingUrl ? /* @__PURE__ */
|
|
94
|
+
e.meetingUrl ? /* @__PURE__ */ N(P, {
|
|
94
95
|
size: "sm",
|
|
95
96
|
onClick: () => window.open(e.meetingUrl ?? "", "_blank", "noopener,noreferrer"),
|
|
96
|
-
children: [/* @__PURE__ */
|
|
97
|
+
children: [/* @__PURE__ */ M(A, { size: 15 }), " Join"]
|
|
97
98
|
}) : null,
|
|
98
|
-
n && e.status === "REQUESTED" ? /* @__PURE__ */
|
|
99
|
+
n && e.status === "REQUESTED" ? /* @__PURE__ */ N(P, {
|
|
99
100
|
variant: "outline",
|
|
100
101
|
size: "sm",
|
|
101
102
|
onClick: () => {
|
|
102
103
|
p(), _(g === "confirm" ? "none" : "confirm");
|
|
103
104
|
},
|
|
104
|
-
children: [/* @__PURE__ */
|
|
105
|
+
children: [/* @__PURE__ */ M(T, { size: 15 }), " Confirm"]
|
|
105
106
|
}) : null,
|
|
106
|
-
n && e.status === "CONFIRMED" ? /* @__PURE__ */
|
|
107
|
+
n && e.status === "CONFIRMED" ? /* @__PURE__ */ N(P, {
|
|
107
108
|
variant: "outline",
|
|
108
109
|
size: "sm",
|
|
109
110
|
onClick: () => {
|
|
110
111
|
p(), _(g === "complete" ? "none" : "complete");
|
|
111
112
|
},
|
|
112
|
-
children: [/* @__PURE__ */
|
|
113
|
+
children: [/* @__PURE__ */ M(E, { size: 15 }), " Mark complete"]
|
|
113
114
|
}) : null,
|
|
114
|
-
h ? /* @__PURE__ */
|
|
115
|
+
h ? /* @__PURE__ */ N(P, {
|
|
115
116
|
variant: "ghost",
|
|
116
117
|
size: "sm",
|
|
117
118
|
onClick: () => {
|
|
118
119
|
p(), r(e.id);
|
|
119
120
|
},
|
|
120
121
|
disabled: u,
|
|
121
|
-
children: [/* @__PURE__ */ j
|
|
122
|
+
children: [/* @__PURE__ */ M(j, { size: 15 }), " Cancel"]
|
|
122
123
|
}) : null
|
|
123
124
|
]
|
|
124
125
|
})]
|
|
125
126
|
}),
|
|
126
|
-
g === "confirm" ? /* @__PURE__ */
|
|
127
|
+
g === "confirm" ? /* @__PURE__ */ N("div", {
|
|
127
128
|
className: "flex flex-col gap-2 rounded-lg border border-border-seam bg-bg-sunken p-3 sm:flex-row sm:items-center",
|
|
128
|
-
children: [/* @__PURE__ */
|
|
129
|
+
children: [/* @__PURE__ */ M(I, {
|
|
129
130
|
className: "flex-1",
|
|
130
131
|
placeholder: "Meeting link (e.g. https://meet.…)",
|
|
131
132
|
value: v,
|
|
132
133
|
onChange: (e) => y(e.target.value)
|
|
133
|
-
}), /* @__PURE__ */
|
|
134
|
+
}), /* @__PURE__ */ M(P, {
|
|
134
135
|
size: "sm",
|
|
135
136
|
onClick: L,
|
|
136
137
|
disabled: u,
|
|
137
138
|
children: "Confirm session"
|
|
138
139
|
})]
|
|
139
140
|
}) : null,
|
|
140
|
-
g === "complete" ? /* @__PURE__ */
|
|
141
|
+
g === "complete" ? /* @__PURE__ */ N("div", {
|
|
141
142
|
className: "flex flex-col gap-2 rounded-lg border border-border-seam bg-bg-sunken p-3",
|
|
142
143
|
children: [
|
|
143
|
-
/* @__PURE__ */
|
|
144
|
+
/* @__PURE__ */ N("div", {
|
|
144
145
|
className: "flex items-center gap-2",
|
|
145
|
-
children: [/* @__PURE__ */
|
|
146
|
+
children: [/* @__PURE__ */ M("label", {
|
|
146
147
|
className: "text-xs text-text-secondary",
|
|
147
148
|
children: "Rating (1–5)"
|
|
148
|
-
}), /* @__PURE__ */
|
|
149
|
+
}), /* @__PURE__ */ M(I, {
|
|
149
150
|
type: "number",
|
|
150
151
|
min: 1,
|
|
151
152
|
max: 5,
|
|
152
153
|
className: "w-20",
|
|
153
|
-
value:
|
|
154
|
-
onChange: (e) =>
|
|
154
|
+
value: b,
|
|
155
|
+
onChange: (e) => S(e.target.value)
|
|
155
156
|
})]
|
|
156
157
|
}),
|
|
157
|
-
/* @__PURE__ */
|
|
158
|
+
/* @__PURE__ */ M(I, {
|
|
158
159
|
placeholder: "Feedback for the mentee (optional)",
|
|
159
160
|
value: O,
|
|
160
|
-
onChange: (e) =>
|
|
161
|
+
onChange: (e) => k(e.target.value)
|
|
161
162
|
}),
|
|
162
|
-
/* @__PURE__ */
|
|
163
|
+
/* @__PURE__ */ M("div", { children: /* @__PURE__ */ M(P, {
|
|
163
164
|
size: "sm",
|
|
164
165
|
onClick: R,
|
|
165
166
|
disabled: u,
|
|
@@ -171,20 +172,20 @@ function V({ session: e, showMentor: t, isMentor: n, onCancel: r, onConfirm: a,
|
|
|
171
172
|
});
|
|
172
173
|
}
|
|
173
174
|
function H({ loading: e, error: t, sessions: n, showMentor: r, emptyTitle: i, emptyDescription: a, emptyAction: o, isMentor: s, onCancel: c, onConfirm: l, onComplete: p, busyId: m, focusedId: h, onFocusChange: g }) {
|
|
174
|
-
return e && n.length === 0 ? /* @__PURE__ */
|
|
175
|
+
return e && n.length === 0 ? /* @__PURE__ */ M(f, {
|
|
175
176
|
count: 3,
|
|
176
177
|
height: "h-24"
|
|
177
|
-
}) : t ? /* @__PURE__ */
|
|
178
|
+
}) : t ? /* @__PURE__ */ M(d, {
|
|
178
179
|
title: "Couldn't load sessions",
|
|
179
180
|
error: t
|
|
180
|
-
}) : n.length === 0 ? /* @__PURE__ */
|
|
181
|
-
icon: /* @__PURE__ */
|
|
181
|
+
}) : n.length === 0 ? /* @__PURE__ */ M(u, {
|
|
182
|
+
icon: /* @__PURE__ */ M(O, { size: 26 }),
|
|
182
183
|
title: i,
|
|
183
184
|
description: a,
|
|
184
185
|
action: o
|
|
185
|
-
}) : /* @__PURE__ */
|
|
186
|
+
}) : /* @__PURE__ */ M("div", {
|
|
186
187
|
className: "space-y-3",
|
|
187
|
-
children: [...n].sort((e, t) => new Date(t.scheduledAt).getTime() - new Date(e.scheduledAt).getTime()).map((e) => /* @__PURE__ */
|
|
188
|
+
children: [...n].sort((e, t) => new Date(t.scheduledAt).getTime() - new Date(e.scheduledAt).getTime()).map((e) => /* @__PURE__ */ M(V, {
|
|
188
189
|
session: e,
|
|
189
190
|
showMentor: r,
|
|
190
191
|
isMentor: s,
|
|
@@ -198,8 +199,8 @@ function H({ loading: e, error: t, sessions: n, showMentor: r, emptyTitle: i, em
|
|
|
198
199
|
});
|
|
199
200
|
}
|
|
200
201
|
function U() {
|
|
201
|
-
let i = t(), { currentUser: s, workspaceId: c } = e(), { emit: l } = n(), d = r(), [
|
|
202
|
-
|
|
202
|
+
let i = t(), { currentUser: s, workspaceId: c } = e(), { emit: l } = n(), d = r(), [x, T] = C("mentee"), [D, A] = C(null), [j, F] = S(y.session), I = s?.id, B = v(I ? { menteeId: I } : {}), V = _({}), U = V.data?.tecListMentors?.items.find((e) => e.userId === s?.id), W = U?.id, G = v(W ? { mentorId: W } : {}), [K] = m(), [q] = g(), [te] = h(), J = I ? B.data?.tecListSessions?.items ?? [] : [], Y = W ? G.data?.tecListSessions?.items ?? [] : [], X = [...J, ...Y], ne = X.filter((e) => e.status === "REQUESTED" || e.status === "CONFIRMED").length, re = X.filter((e) => e.status === "COMPLETED").length, Z = async (e) => {
|
|
203
|
+
A(e), l("technospam.session.cancel_clicked", {
|
|
203
204
|
workspaceId: c,
|
|
204
205
|
entityId: e,
|
|
205
206
|
entityType: "TEC_MENTOR_SESSION"
|
|
@@ -211,26 +212,26 @@ function U() {
|
|
|
211
212
|
entityType: "TEC_MENTOR_SESSION"
|
|
212
213
|
}), p.success("Session cancelled", "The session has been cancelled."));
|
|
213
214
|
} finally {
|
|
214
|
-
|
|
215
|
+
A(null);
|
|
215
216
|
}
|
|
216
217
|
}, Q = async (e, t) => {
|
|
217
|
-
|
|
218
|
+
A(e);
|
|
218
219
|
try {
|
|
219
220
|
(await q({ variables: {
|
|
220
221
|
id: e,
|
|
221
222
|
meetingUrl: t || void 0
|
|
222
223
|
} })).data?.tecConfirmSession && (p.success("Session confirmed", "The mentee has been notified."), await G.refetch());
|
|
223
224
|
} finally {
|
|
224
|
-
|
|
225
|
+
A(null);
|
|
225
226
|
}
|
|
226
227
|
}, $ = async (e, t, n) => {
|
|
227
|
-
|
|
228
|
+
A(e), l("technospam.session.complete_clicked", {
|
|
228
229
|
workspaceId: c,
|
|
229
230
|
entityId: e,
|
|
230
231
|
entityType: "TEC_MENTOR_SESSION"
|
|
231
232
|
});
|
|
232
233
|
try {
|
|
233
|
-
(await
|
|
234
|
+
(await te({ variables: {
|
|
234
235
|
id: e,
|
|
235
236
|
rating: t,
|
|
236
237
|
feedback: n || void 0
|
|
@@ -240,70 +241,71 @@ function U() {
|
|
|
240
241
|
entityType: "TEC_MENTOR_SESSION"
|
|
241
242
|
}), p.success("Session completed", "Nice work — the session is wrapped up."), await G.refetch());
|
|
242
243
|
} finally {
|
|
243
|
-
|
|
244
|
+
A(null);
|
|
244
245
|
}
|
|
245
246
|
};
|
|
246
|
-
return s?.id ? /* @__PURE__ */
|
|
247
|
+
return s?.id ? /* @__PURE__ */ N(a, {
|
|
247
248
|
hero: !0,
|
|
248
249
|
title: d("technospam.mentorship.sessionsTitle", "My Sessions"),
|
|
249
250
|
description: d("technospam.mentorship.sessionsDescription", "Manage the mentorship sessions you've booked and the ones you host."),
|
|
250
|
-
actions: /* @__PURE__ */
|
|
251
|
+
actions: /* @__PURE__ */ N(P, {
|
|
251
252
|
variant: "outline",
|
|
252
253
|
onClick: () => i("/mentorship/mentors-page"),
|
|
253
254
|
children: [
|
|
254
|
-
/* @__PURE__ */
|
|
255
|
+
/* @__PURE__ */ M(k, { size: 16 }),
|
|
255
256
|
" ",
|
|
256
257
|
d("technospam.common.findMentor", "Find a mentor")
|
|
257
258
|
]
|
|
258
259
|
}),
|
|
259
|
-
children: [/* @__PURE__ */
|
|
260
|
+
children: [/* @__PURE__ */ N("div", {
|
|
260
261
|
className: "mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4",
|
|
261
262
|
children: [
|
|
262
|
-
/* @__PURE__ */
|
|
263
|
+
/* @__PURE__ */ M(o, {
|
|
263
264
|
label: d("technospam.mentorship.statAsMentee", "As mentee"),
|
|
264
265
|
value: J.length,
|
|
265
|
-
icon:
|
|
266
|
+
icon: O,
|
|
266
267
|
tone: "blue"
|
|
267
268
|
}),
|
|
268
|
-
/* @__PURE__ */
|
|
269
|
+
/* @__PURE__ */ M(o, {
|
|
269
270
|
label: d("technospam.mentorship.statAsMentor", "As mentor"),
|
|
270
271
|
value: W ? Y.length : 0,
|
|
271
|
-
icon:
|
|
272
|
+
icon: k,
|
|
272
273
|
tone: "purple"
|
|
273
274
|
}),
|
|
274
|
-
/* @__PURE__ */
|
|
275
|
+
/* @__PURE__ */ M(o, {
|
|
275
276
|
label: d("technospam.mentorship.statUpcoming", "Upcoming"),
|
|
276
|
-
value:
|
|
277
|
-
icon:
|
|
277
|
+
value: ne,
|
|
278
|
+
icon: w,
|
|
278
279
|
tone: "teal"
|
|
279
280
|
}),
|
|
280
|
-
/* @__PURE__ */
|
|
281
|
+
/* @__PURE__ */ M(o, {
|
|
281
282
|
label: d("technospam.mentorship.statCompleted", "Completed"),
|
|
282
|
-
value:
|
|
283
|
-
icon:
|
|
283
|
+
value: re,
|
|
284
|
+
icon: E,
|
|
284
285
|
tone: "green"
|
|
285
286
|
})
|
|
286
287
|
]
|
|
287
|
-
}), /* @__PURE__ */
|
|
288
|
-
value:
|
|
289
|
-
onValueChange: (e) =>
|
|
288
|
+
}), /* @__PURE__ */ N(L, {
|
|
289
|
+
value: x,
|
|
290
|
+
onValueChange: (e) => T(e),
|
|
290
291
|
className: "gap-4",
|
|
291
|
-
children: [/* @__PURE__ */
|
|
292
|
+
children: [/* @__PURE__ */ N(ee, { children: [/* @__PURE__ */ M(z, {
|
|
292
293
|
value: "mentee",
|
|
293
294
|
children: d("technospam.mentorship.tabAsMentee", "As mentee")
|
|
294
|
-
}), /* @__PURE__ */
|
|
295
|
+
}), /* @__PURE__ */ M(z, {
|
|
295
296
|
value: "mentor",
|
|
296
297
|
children: d("technospam.mentorship.tabAsMentor", "As mentor")
|
|
297
|
-
})] }), /* @__PURE__ */
|
|
298
|
-
value:
|
|
299
|
-
|
|
298
|
+
})] }), /* @__PURE__ */ M(R, {
|
|
299
|
+
value: x,
|
|
300
|
+
className: b,
|
|
301
|
+
children: x === "mentee" ? /* @__PURE__ */ M(H, {
|
|
300
302
|
loading: B.loading,
|
|
301
303
|
error: B.error,
|
|
302
304
|
sessions: J,
|
|
303
305
|
showMentor: !0,
|
|
304
306
|
emptyTitle: d("technospam.mentorship.noSessionsBookedTitle", "No sessions booked yet"),
|
|
305
307
|
emptyDescription: d("technospam.mentorship.noSessionsBookedDesc", "Find a mentor and request a session to get personalized guidance."),
|
|
306
|
-
emptyAction: /* @__PURE__ */
|
|
308
|
+
emptyAction: /* @__PURE__ */ M(P, {
|
|
307
309
|
onClick: () => i("/mentorship/mentors-page"),
|
|
308
310
|
children: d("technospam.common.findMentor", "Find a mentor")
|
|
309
311
|
}),
|
|
@@ -311,13 +313,13 @@ function U() {
|
|
|
311
313
|
onCancel: Z,
|
|
312
314
|
onConfirm: Q,
|
|
313
315
|
onComplete: $,
|
|
314
|
-
busyId:
|
|
315
|
-
focusedId:
|
|
316
|
-
onFocusChange:
|
|
317
|
-
}) : V.loading && !U ? /* @__PURE__ */
|
|
316
|
+
busyId: D,
|
|
317
|
+
focusedId: j,
|
|
318
|
+
onFocusChange: F
|
|
319
|
+
}) : V.loading && !U ? /* @__PURE__ */ M(f, {
|
|
318
320
|
count: 3,
|
|
319
321
|
height: "h-24"
|
|
320
|
-
}) : W ? /* @__PURE__ */
|
|
322
|
+
}) : W ? /* @__PURE__ */ M(H, {
|
|
321
323
|
loading: G.loading,
|
|
322
324
|
error: G.error,
|
|
323
325
|
sessions: Y,
|
|
@@ -328,26 +330,26 @@ function U() {
|
|
|
328
330
|
onCancel: Z,
|
|
329
331
|
onConfirm: Q,
|
|
330
332
|
onComplete: $,
|
|
331
|
-
busyId:
|
|
332
|
-
focusedId:
|
|
333
|
-
onFocusChange:
|
|
334
|
-
}) : /* @__PURE__ */
|
|
335
|
-
icon: /* @__PURE__ */
|
|
333
|
+
busyId: D,
|
|
334
|
+
focusedId: j,
|
|
335
|
+
onFocusChange: F
|
|
336
|
+
}) : /* @__PURE__ */ M(u, {
|
|
337
|
+
icon: /* @__PURE__ */ M(k, { size: 26 }),
|
|
336
338
|
title: d("technospam.mentorship.notAMentorTitle", "You're not a mentor yet"),
|
|
337
339
|
description: d("technospam.mentorship.notAMentorDesc", "Create a mentor profile to start hosting sessions and helping others build."),
|
|
338
|
-
action: /* @__PURE__ */
|
|
340
|
+
action: /* @__PURE__ */ M(P, {
|
|
339
341
|
onClick: () => i("/mentorship/mentors-page"),
|
|
340
342
|
children: d("technospam.common.becomeAMentor", "Become a mentor")
|
|
341
343
|
})
|
|
342
344
|
})
|
|
343
345
|
})]
|
|
344
346
|
})]
|
|
345
|
-
}) : /* @__PURE__ */
|
|
347
|
+
}) : /* @__PURE__ */ M(a, {
|
|
346
348
|
hero: !0,
|
|
347
349
|
title: d("technospam.mentorship.sessionsTitle", "My Sessions"),
|
|
348
350
|
description: d("technospam.mentorship.sessionsDescription", "Track your mentorship sessions."),
|
|
349
|
-
children: /* @__PURE__ */
|
|
350
|
-
icon: /* @__PURE__ */
|
|
351
|
+
children: /* @__PURE__ */ M(u, {
|
|
352
|
+
icon: /* @__PURE__ */ M(O, { size: 26 }),
|
|
351
353
|
title: d("technospam.mentorship.signInTitle", "Sign in to view your sessions"),
|
|
352
354
|
description: d("technospam.mentorship.signInDesc", "Your booked and hosted mentorship sessions will appear here once you're signed in.")
|
|
353
355
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MentorshipSessionsPagePage.js","names":[],"sources":["../../../src/technospam/pages/MentorshipSessionsPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n CalendarClock,\n Check,\n CheckCircle2,\n Clock,\n MessageSquare,\n Users,\n Video,\n X,\n} from 'lucide-react';\nimport {\n Button,\n GlassCard,\n Input,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n LoadingRows,\n PageLayout,\n StatCard,\n StatusBadge,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n useCancelSession,\n useCompleteSession,\n useConfirmSession,\n useMentorsQuery,\n useSessionsQuery,\n type TecMentorSession,\n} from '../api/technospamApi';\nimport { formatDateTime, formatDuration } from '../utils/format';\nimport { notify } from '../lib/toast';\nimport { useTechnoSpamAnalytics } from '../hooks/useTechnoSpamAnalytics';\nimport {\n FOCUSED_CARD_CLASS,\n focusableCardProps,\n useFocusedRecordParam,\n} from '../hooks/useFocusedRecordParam';\nimport { FOCUS_PARAMS } from '../utils/pageContext';\nimport { cn } from '../utils/cn';\nimport { useTr } from '../hooks/useTr';\n\ntype Perspective = 'mentee' | 'mentor';\n\nconst CANCELLABLE = new Set(['REQUESTED', 'CONFIRMED']);\n\nfunction SessionRow({\n session,\n showMentor,\n isMentor,\n onCancel,\n onConfirm,\n onComplete,\n busy,\n focused,\n onFocusChange,\n}: {\n session: TecMentorSession;\n showMentor: boolean;\n isMentor: boolean;\n onCancel: (id: string) => void;\n onConfirm: (id: string, meetingUrl: string) => Promise<void>;\n onComplete: (id: string, rating: number, feedback: string) => Promise<void>;\n busy: boolean;\n /** True when this is the session the page (and the assistant) is focused on. */\n focused: boolean;\n onFocusChange: (id: string | null) => void;\n}) {\n // Acting on a session focuses it, so \"cancel this session\" names this one.\n const focus = () => onFocusChange(session.id);\n const toggleFocus = () => onFocusChange(focused ? null : session.id);\n const canCancel = CANCELLABLE.has(session.status);\n const [mode, setMode] = useState<'none' | 'confirm' | 'complete'>('none');\n const [meetingUrl, setMeetingUrl] = useState('');\n const [rating, setRating] = useState('5');\n const [feedback, setFeedback] = useState('');\n\n const submitConfirm = async () => {\n await onConfirm(session.id, meetingUrl.trim());\n setMode('none');\n setMeetingUrl('');\n };\n const submitComplete = async () => {\n await onComplete(session.id, Math.min(5, Math.max(1, Number(rating) || 5)), feedback.trim());\n setMode('none');\n };\n\n return (\n <GlassCard\n className={cn(\n 'flex cursor-pointer flex-col gap-3 p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md',\n focused && FOCUSED_CARD_CLASS\n )}\n {...focusableCardProps(focused, toggleFocus)}\n >\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-start gap-3\">\n <span className=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-accent-primary to-accent-purple text-white shadow-sm\">\n <Video size={18} />\n </span>\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n aria-pressed={focused}\n onClick={toggleFocus}\n className=\"truncate text-left text-sm font-semibold text-text-primary hover:text-accent-primary\"\n >\n {session.topic}\n </button>\n <StatusBadge status={session.status} />\n {session.rating ? (\n <span className=\"text-xs text-text-muted\">★ {session.rating}/5</span>\n ) : null}\n </div>\n {showMentor && session.mentor?.headline ? (\n <div className=\"mt-0.5 truncate text-xs text-text-secondary\">\n with {session.mentor.headline}\n </div>\n ) : null}\n <div className=\"mt-1 flex flex-wrap items-center gap-3 text-xs text-text-muted\">\n <span className=\"inline-flex items-center gap-1\">\n <CalendarClock size={13} /> {formatDateTime(session.scheduledAt)}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Clock size={13} /> {formatDuration(session.durationMinutes)}\n </span>\n </div>\n {session.agenda ? (\n <p className=\"mt-1 line-clamp-2 text-xs text-text-secondary\">{session.agenda}</p>\n ) : null}\n </div>\n </div>\n <div className=\"flex shrink-0 flex-wrap items-center gap-2\">\n {session.meetingUrl ? (\n <Button\n size=\"sm\"\n onClick={() => window.open(session.meetingUrl ?? '', '_blank', 'noopener,noreferrer')}\n >\n <Video size={15} /> Join\n </Button>\n ) : null}\n {isMentor && session.status === 'REQUESTED' ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n focus();\n setMode(mode === 'confirm' ? 'none' : 'confirm');\n }}\n >\n <Check size={15} /> Confirm\n </Button>\n ) : null}\n {isMentor && session.status === 'CONFIRMED' ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n focus();\n setMode(mode === 'complete' ? 'none' : 'complete');\n }}\n >\n <CheckCircle2 size={15} /> Mark complete\n </Button>\n ) : null}\n {canCancel ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n focus();\n onCancel(session.id);\n }}\n disabled={busy}\n >\n <X size={15} /> Cancel\n </Button>\n ) : null}\n </div>\n </div>\n\n {mode === 'confirm' ? (\n <div className=\"flex flex-col gap-2 rounded-lg border border-border-seam bg-bg-sunken p-3 sm:flex-row sm:items-center\">\n <Input\n className=\"flex-1\"\n placeholder=\"Meeting link (e.g. https://meet.…)\"\n value={meetingUrl}\n onChange={(e) => setMeetingUrl(e.target.value)}\n />\n <Button size=\"sm\" onClick={submitConfirm} disabled={busy}>\n Confirm session\n </Button>\n </div>\n ) : null}\n {mode === 'complete' ? (\n <div className=\"flex flex-col gap-2 rounded-lg border border-border-seam bg-bg-sunken p-3\">\n <div className=\"flex items-center gap-2\">\n <label className=\"text-xs text-text-secondary\">Rating (1–5)</label>\n <Input\n type=\"number\"\n min={1}\n max={5}\n className=\"w-20\"\n value={rating}\n onChange={(e) => setRating(e.target.value)}\n />\n </div>\n <Input\n placeholder=\"Feedback for the mentee (optional)\"\n value={feedback}\n onChange={(e) => setFeedback(e.target.value)}\n />\n <div>\n <Button size=\"sm\" onClick={submitComplete} disabled={busy}>\n Complete session\n </Button>\n </div>\n </div>\n ) : null}\n </GlassCard>\n );\n}\n\nfunction SessionsList({\n loading,\n error,\n sessions,\n showMentor,\n emptyTitle,\n emptyDescription,\n emptyAction,\n isMentor,\n onCancel,\n onConfirm,\n onComplete,\n busyId,\n focusedId,\n onFocusChange,\n}: {\n loading: boolean;\n error?: { message?: string };\n sessions: TecMentorSession[];\n showMentor: boolean;\n emptyTitle: string;\n emptyDescription: string;\n emptyAction?: React.ReactNode;\n isMentor: boolean;\n onCancel: (id: string) => void;\n onConfirm: (id: string, meetingUrl: string) => Promise<void>;\n onComplete: (id: string, rating: number, feedback: string) => Promise<void>;\n busyId: string | null;\n focusedId: string | null;\n onFocusChange: (id: string | null) => void;\n}) {\n if (loading && sessions.length === 0) return <LoadingRows count={3} height=\"h-24\" />;\n if (error) return <ErrorView title=\"Couldn't load sessions\" error={error} />;\n if (sessions.length === 0) {\n return (\n <EmptyView\n icon={<MessageSquare size={26} />}\n title={emptyTitle}\n description={emptyDescription}\n action={emptyAction}\n />\n );\n }\n const sorted = [...sessions].sort(\n (a, b) => new Date(b.scheduledAt).getTime() - new Date(a.scheduledAt).getTime()\n );\n return (\n <div className=\"space-y-3\">\n {sorted.map((session) => (\n <SessionRow\n key={session.id}\n session={session}\n showMentor={showMentor}\n isMentor={isMentor}\n onCancel={onCancel}\n onConfirm={onConfirm}\n onComplete={onComplete}\n busy={busyId === session.id}\n focused={focusedId === session.id}\n onFocusChange={onFocusChange}\n />\n ))}\n </div>\n );\n}\n\nexport function MentorshipSessionsPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { currentUser, workspaceId } = useTechnoSpam();\n const { emit } = useTechnoSpamAnalytics();\n const tr = useTr();\n const [tab, setTab] = useState<Perspective>('mentee');\n const [cancellingId, setCancellingId] = useState<string | null>(null);\n // The focused session lives in `?sessionId=` so the assistant's page context\n // names it (\"cancel this session\") and a reload keeps it focused.\n const [focusedSessionId, setFocusedSessionId] = useFocusedRecordParam(FOCUS_PARAMS.session);\n\n const menteeId = currentUser?.id;\n\n // As mentee: sessions the current user booked.\n const menteeQ = useSessionsQuery(menteeId ? { menteeId } : {});\n\n // Resolve whether the current user is a mentor, to load their mentor sessions.\n const mentorsQ = useMentorsQuery({});\n const myMentor = mentorsQ.data?.tecListMentors?.items.find((m) => m.userId === currentUser?.id);\n const mentorId = myMentor?.id;\n const mentorQ = useSessionsQuery(mentorId ? { mentorId } : {});\n\n const [cancelSession] = useCancelSession();\n const [confirmSession] = useConfirmSession();\n const [completeSession] = useCompleteSession();\n\n const menteeSessions = menteeId ? (menteeQ.data?.tecListSessions?.items ?? []) : [];\n const mentorSessions = mentorId ? (mentorQ.data?.tecListSessions?.items ?? []) : [];\n const allSessions = [...menteeSessions, ...mentorSessions];\n const upcomingCount = allSessions.filter(\n (s) => s.status === 'REQUESTED' || s.status === 'CONFIRMED'\n ).length;\n const completedCount = allSessions.filter((s) => s.status === 'COMPLETED').length;\n\n const handleCancel = async (id: string) => {\n setCancellingId(id);\n emit('technospam.session.cancel_clicked', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_MENTOR_SESSION',\n });\n try {\n const res = await cancelSession({ variables: { id } });\n if (res.data?.tecCancelSession) {\n emit('technospam.session.cancelled', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_MENTOR_SESSION',\n });\n notify.success('Session cancelled', 'The session has been cancelled.');\n }\n } finally {\n setCancellingId(null);\n }\n };\n\n const handleConfirm = async (id: string, meetingUrl: string) => {\n setCancellingId(id);\n try {\n const res = await confirmSession({\n variables: { id, meetingUrl: meetingUrl || undefined },\n });\n if (res.data?.tecConfirmSession) {\n notify.success('Session confirmed', 'The mentee has been notified.');\n await mentorQ.refetch();\n }\n } finally {\n setCancellingId(null);\n }\n };\n\n const handleComplete = async (id: string, rating: number, feedback: string) => {\n setCancellingId(id);\n emit('technospam.session.complete_clicked', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_MENTOR_SESSION',\n });\n try {\n const res = await completeSession({\n variables: { id, rating, feedback: feedback || undefined },\n });\n if (res.data?.tecCompleteSession) {\n emit('technospam.session.completed', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_MENTOR_SESSION',\n });\n notify.success('Session completed', 'Nice work — the session is wrapped up.');\n await mentorQ.refetch();\n }\n } finally {\n setCancellingId(null);\n }\n };\n\n if (!currentUser?.id) {\n return (\n <PageLayout\n hero\n title={tr('technospam.mentorship.sessionsTitle', 'My Sessions')}\n description={tr(\n 'technospam.mentorship.sessionsDescription',\n 'Track your mentorship sessions.'\n )}\n >\n <EmptyView\n icon={<MessageSquare size={26} />}\n title={tr('technospam.mentorship.signInTitle', 'Sign in to view your sessions')}\n description={tr(\n 'technospam.mentorship.signInDesc',\n \"Your booked and hosted mentorship sessions will appear here once you're signed in.\"\n )}\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n hero\n title={tr('technospam.mentorship.sessionsTitle', 'My Sessions')}\n description={tr(\n 'technospam.mentorship.sessionsDescription',\n \"Manage the mentorship sessions you've booked and the ones you host.\"\n )}\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/mentorship/mentors-page')}>\n <Users size={16} /> {tr('technospam.common.findMentor', 'Find a mentor')}\n </Button>\n }\n >\n <div className=\"mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard\n label={tr('technospam.mentorship.statAsMentee', 'As mentee')}\n value={menteeSessions.length}\n icon={MessageSquare}\n tone=\"blue\"\n />\n <StatCard\n label={tr('technospam.mentorship.statAsMentor', 'As mentor')}\n value={mentorId ? mentorSessions.length : 0}\n icon={Users}\n tone=\"purple\"\n />\n <StatCard\n label={tr('technospam.mentorship.statUpcoming', 'Upcoming')}\n value={upcomingCount}\n icon={CalendarClock}\n tone=\"teal\"\n />\n <StatCard\n label={tr('technospam.mentorship.statCompleted', 'Completed')}\n value={completedCount}\n icon={CheckCircle2}\n tone=\"green\"\n />\n </div>\n\n {/* The tabs filter one list, so that list is their tab panel: Radix points every\n trigger's aria-controls at a panel id, and without a TabsContent the id\n resolved to nothing (axe aria-valid-attr-value, critical; BOFF-3157). */}\n <Tabs value={tab} onValueChange={(v) => setTab(v as Perspective)} className=\"gap-4\">\n <TabsList>\n <TabsTrigger value=\"mentee\">\n {tr('technospam.mentorship.tabAsMentee', 'As mentee')}\n </TabsTrigger>\n <TabsTrigger value=\"mentor\">\n {tr('technospam.mentorship.tabAsMentor', 'As mentor')}\n </TabsTrigger>\n </TabsList>\n\n <TabsContent value={tab}>\n {tab === 'mentee' ? (\n <SessionsList\n loading={menteeQ.loading}\n error={menteeQ.error}\n sessions={menteeSessions}\n showMentor\n emptyTitle={tr(\n 'technospam.mentorship.noSessionsBookedTitle',\n 'No sessions booked yet'\n )}\n emptyDescription={tr(\n 'technospam.mentorship.noSessionsBookedDesc',\n 'Find a mentor and request a session to get personalized guidance.'\n )}\n emptyAction={\n <Button onClick={() => navigate('/mentorship/mentors-page')}>\n {tr('technospam.common.findMentor', 'Find a mentor')}\n </Button>\n }\n isMentor={false}\n onCancel={handleCancel}\n onConfirm={handleConfirm}\n onComplete={handleComplete}\n busyId={cancellingId}\n focusedId={focusedSessionId}\n onFocusChange={setFocusedSessionId}\n />\n ) : mentorsQ.loading && !myMentor ? (\n <LoadingRows count={3} height=\"h-24\" />\n ) : !mentorId ? (\n <EmptyView\n icon={<Users size={26} />}\n title={tr('technospam.mentorship.notAMentorTitle', \"You're not a mentor yet\")}\n description={tr(\n 'technospam.mentorship.notAMentorDesc',\n 'Create a mentor profile to start hosting sessions and helping others build.'\n )}\n action={\n <Button onClick={() => navigate('/mentorship/mentors-page')}>\n {tr('technospam.common.becomeAMentor', 'Become a mentor')}\n </Button>\n }\n />\n ) : (\n <SessionsList\n loading={mentorQ.loading}\n error={mentorQ.error}\n sessions={mentorSessions}\n showMentor={false}\n emptyTitle={tr(\n 'technospam.mentorship.noSessionsToHostTitle',\n 'No sessions to host yet'\n )}\n emptyDescription={tr(\n 'technospam.mentorship.noSessionsToHostDesc',\n 'When mentees request time with you, their sessions will show up here.'\n )}\n isMentor\n onCancel={handleCancel}\n onConfirm={handleConfirm}\n onComplete={handleComplete}\n busyId={cancellingId}\n focusedId={focusedSessionId}\n onFocusChange={setFocusedSessionId}\n />\n )}\n </TabsContent>\n </Tabs>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MentorshipSessionsPagePage.js","names":[],"sources":["../../../src/technospam/pages/MentorshipSessionsPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n CalendarClock,\n Check,\n CheckCircle2,\n Clock,\n MessageSquare,\n Users,\n Video,\n X,\n} from 'lucide-react';\nimport {\n Button,\n GlassCard,\n Input,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { TAB_PANEL_FOCUS_CLASS } from '../lib/a11y';\nimport {\n EmptyView,\n ErrorView,\n LoadingRows,\n PageLayout,\n StatCard,\n StatusBadge,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n useCancelSession,\n useCompleteSession,\n useConfirmSession,\n useMentorsQuery,\n useSessionsQuery,\n type TecMentorSession,\n} from '../api/technospamApi';\nimport { formatDateTime, formatDuration } from '../utils/format';\nimport { notify } from '../lib/toast';\nimport { useTechnoSpamAnalytics } from '../hooks/useTechnoSpamAnalytics';\nimport {\n FOCUSED_CARD_CLASS,\n focusableCardProps,\n useFocusedRecordParam,\n} from '../hooks/useFocusedRecordParam';\nimport { FOCUS_PARAMS } from '../utils/pageContext';\nimport { cn } from '../utils/cn';\nimport { useTr } from '../hooks/useTr';\n\ntype Perspective = 'mentee' | 'mentor';\n\nconst CANCELLABLE = new Set(['REQUESTED', 'CONFIRMED']);\n\nfunction SessionRow({\n session,\n showMentor,\n isMentor,\n onCancel,\n onConfirm,\n onComplete,\n busy,\n focused,\n onFocusChange,\n}: {\n session: TecMentorSession;\n showMentor: boolean;\n isMentor: boolean;\n onCancel: (id: string) => void;\n onConfirm: (id: string, meetingUrl: string) => Promise<void>;\n onComplete: (id: string, rating: number, feedback: string) => Promise<void>;\n busy: boolean;\n /** True when this is the session the page (and the assistant) is focused on. */\n focused: boolean;\n onFocusChange: (id: string | null) => void;\n}) {\n // Acting on a session focuses it, so \"cancel this session\" names this one.\n const focus = () => onFocusChange(session.id);\n const toggleFocus = () => onFocusChange(focused ? null : session.id);\n const canCancel = CANCELLABLE.has(session.status);\n const [mode, setMode] = useState<'none' | 'confirm' | 'complete'>('none');\n const [meetingUrl, setMeetingUrl] = useState('');\n const [rating, setRating] = useState('5');\n const [feedback, setFeedback] = useState('');\n\n const submitConfirm = async () => {\n await onConfirm(session.id, meetingUrl.trim());\n setMode('none');\n setMeetingUrl('');\n };\n const submitComplete = async () => {\n await onComplete(session.id, Math.min(5, Math.max(1, Number(rating) || 5)), feedback.trim());\n setMode('none');\n };\n\n return (\n <GlassCard\n className={cn(\n 'flex cursor-pointer flex-col gap-3 p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md',\n focused && FOCUSED_CARD_CLASS\n )}\n {...focusableCardProps(focused, toggleFocus)}\n >\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-start gap-3\">\n <span className=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-accent-primary to-accent-purple text-white shadow-sm\">\n <Video size={18} />\n </span>\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n aria-pressed={focused}\n onClick={toggleFocus}\n className=\"truncate text-left text-sm font-semibold text-text-primary hover:text-accent-primary\"\n >\n {session.topic}\n </button>\n <StatusBadge status={session.status} />\n {session.rating ? (\n <span className=\"text-xs text-text-muted\">★ {session.rating}/5</span>\n ) : null}\n </div>\n {showMentor && session.mentor?.headline ? (\n <div className=\"mt-0.5 truncate text-xs text-text-secondary\">\n with {session.mentor.headline}\n </div>\n ) : null}\n <div className=\"mt-1 flex flex-wrap items-center gap-3 text-xs text-text-muted\">\n <span className=\"inline-flex items-center gap-1\">\n <CalendarClock size={13} /> {formatDateTime(session.scheduledAt)}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Clock size={13} /> {formatDuration(session.durationMinutes)}\n </span>\n </div>\n {session.agenda ? (\n <p className=\"mt-1 line-clamp-2 text-xs text-text-secondary\">{session.agenda}</p>\n ) : null}\n </div>\n </div>\n <div className=\"flex shrink-0 flex-wrap items-center gap-2\">\n {session.meetingUrl ? (\n <Button\n size=\"sm\"\n onClick={() => window.open(session.meetingUrl ?? '', '_blank', 'noopener,noreferrer')}\n >\n <Video size={15} /> Join\n </Button>\n ) : null}\n {isMentor && session.status === 'REQUESTED' ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n focus();\n setMode(mode === 'confirm' ? 'none' : 'confirm');\n }}\n >\n <Check size={15} /> Confirm\n </Button>\n ) : null}\n {isMentor && session.status === 'CONFIRMED' ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n focus();\n setMode(mode === 'complete' ? 'none' : 'complete');\n }}\n >\n <CheckCircle2 size={15} /> Mark complete\n </Button>\n ) : null}\n {canCancel ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n focus();\n onCancel(session.id);\n }}\n disabled={busy}\n >\n <X size={15} /> Cancel\n </Button>\n ) : null}\n </div>\n </div>\n\n {mode === 'confirm' ? (\n <div className=\"flex flex-col gap-2 rounded-lg border border-border-seam bg-bg-sunken p-3 sm:flex-row sm:items-center\">\n <Input\n className=\"flex-1\"\n placeholder=\"Meeting link (e.g. https://meet.…)\"\n value={meetingUrl}\n onChange={(e) => setMeetingUrl(e.target.value)}\n />\n <Button size=\"sm\" onClick={submitConfirm} disabled={busy}>\n Confirm session\n </Button>\n </div>\n ) : null}\n {mode === 'complete' ? (\n <div className=\"flex flex-col gap-2 rounded-lg border border-border-seam bg-bg-sunken p-3\">\n <div className=\"flex items-center gap-2\">\n <label className=\"text-xs text-text-secondary\">Rating (1–5)</label>\n <Input\n type=\"number\"\n min={1}\n max={5}\n className=\"w-20\"\n value={rating}\n onChange={(e) => setRating(e.target.value)}\n />\n </div>\n <Input\n placeholder=\"Feedback for the mentee (optional)\"\n value={feedback}\n onChange={(e) => setFeedback(e.target.value)}\n />\n <div>\n <Button size=\"sm\" onClick={submitComplete} disabled={busy}>\n Complete session\n </Button>\n </div>\n </div>\n ) : null}\n </GlassCard>\n );\n}\n\nfunction SessionsList({\n loading,\n error,\n sessions,\n showMentor,\n emptyTitle,\n emptyDescription,\n emptyAction,\n isMentor,\n onCancel,\n onConfirm,\n onComplete,\n busyId,\n focusedId,\n onFocusChange,\n}: {\n loading: boolean;\n error?: { message?: string };\n sessions: TecMentorSession[];\n showMentor: boolean;\n emptyTitle: string;\n emptyDescription: string;\n emptyAction?: React.ReactNode;\n isMentor: boolean;\n onCancel: (id: string) => void;\n onConfirm: (id: string, meetingUrl: string) => Promise<void>;\n onComplete: (id: string, rating: number, feedback: string) => Promise<void>;\n busyId: string | null;\n focusedId: string | null;\n onFocusChange: (id: string | null) => void;\n}) {\n if (loading && sessions.length === 0) return <LoadingRows count={3} height=\"h-24\" />;\n if (error) return <ErrorView title=\"Couldn't load sessions\" error={error} />;\n if (sessions.length === 0) {\n return (\n <EmptyView\n icon={<MessageSquare size={26} />}\n title={emptyTitle}\n description={emptyDescription}\n action={emptyAction}\n />\n );\n }\n const sorted = [...sessions].sort(\n (a, b) => new Date(b.scheduledAt).getTime() - new Date(a.scheduledAt).getTime()\n );\n return (\n <div className=\"space-y-3\">\n {sorted.map((session) => (\n <SessionRow\n key={session.id}\n session={session}\n showMentor={showMentor}\n isMentor={isMentor}\n onCancel={onCancel}\n onConfirm={onConfirm}\n onComplete={onComplete}\n busy={busyId === session.id}\n focused={focusedId === session.id}\n onFocusChange={onFocusChange}\n />\n ))}\n </div>\n );\n}\n\nexport function MentorshipSessionsPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { currentUser, workspaceId } = useTechnoSpam();\n const { emit } = useTechnoSpamAnalytics();\n const tr = useTr();\n const [tab, setTab] = useState<Perspective>('mentee');\n const [cancellingId, setCancellingId] = useState<string | null>(null);\n // The focused session lives in `?sessionId=` so the assistant's page context\n // names it (\"cancel this session\") and a reload keeps it focused.\n const [focusedSessionId, setFocusedSessionId] = useFocusedRecordParam(FOCUS_PARAMS.session);\n\n const menteeId = currentUser?.id;\n\n // As mentee: sessions the current user booked.\n const menteeQ = useSessionsQuery(menteeId ? { menteeId } : {});\n\n // Resolve whether the current user is a mentor, to load their mentor sessions.\n const mentorsQ = useMentorsQuery({});\n const myMentor = mentorsQ.data?.tecListMentors?.items.find((m) => m.userId === currentUser?.id);\n const mentorId = myMentor?.id;\n const mentorQ = useSessionsQuery(mentorId ? { mentorId } : {});\n\n const [cancelSession] = useCancelSession();\n const [confirmSession] = useConfirmSession();\n const [completeSession] = useCompleteSession();\n\n const menteeSessions = menteeId ? (menteeQ.data?.tecListSessions?.items ?? []) : [];\n const mentorSessions = mentorId ? (mentorQ.data?.tecListSessions?.items ?? []) : [];\n const allSessions = [...menteeSessions, ...mentorSessions];\n const upcomingCount = allSessions.filter(\n (s) => s.status === 'REQUESTED' || s.status === 'CONFIRMED'\n ).length;\n const completedCount = allSessions.filter((s) => s.status === 'COMPLETED').length;\n\n const handleCancel = async (id: string) => {\n setCancellingId(id);\n emit('technospam.session.cancel_clicked', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_MENTOR_SESSION',\n });\n try {\n const res = await cancelSession({ variables: { id } });\n if (res.data?.tecCancelSession) {\n emit('technospam.session.cancelled', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_MENTOR_SESSION',\n });\n notify.success('Session cancelled', 'The session has been cancelled.');\n }\n } finally {\n setCancellingId(null);\n }\n };\n\n const handleConfirm = async (id: string, meetingUrl: string) => {\n setCancellingId(id);\n try {\n const res = await confirmSession({\n variables: { id, meetingUrl: meetingUrl || undefined },\n });\n if (res.data?.tecConfirmSession) {\n notify.success('Session confirmed', 'The mentee has been notified.');\n await mentorQ.refetch();\n }\n } finally {\n setCancellingId(null);\n }\n };\n\n const handleComplete = async (id: string, rating: number, feedback: string) => {\n setCancellingId(id);\n emit('technospam.session.complete_clicked', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_MENTOR_SESSION',\n });\n try {\n const res = await completeSession({\n variables: { id, rating, feedback: feedback || undefined },\n });\n if (res.data?.tecCompleteSession) {\n emit('technospam.session.completed', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_MENTOR_SESSION',\n });\n notify.success('Session completed', 'Nice work — the session is wrapped up.');\n await mentorQ.refetch();\n }\n } finally {\n setCancellingId(null);\n }\n };\n\n if (!currentUser?.id) {\n return (\n <PageLayout\n hero\n title={tr('technospam.mentorship.sessionsTitle', 'My Sessions')}\n description={tr(\n 'technospam.mentorship.sessionsDescription',\n 'Track your mentorship sessions.'\n )}\n >\n <EmptyView\n icon={<MessageSquare size={26} />}\n title={tr('technospam.mentorship.signInTitle', 'Sign in to view your sessions')}\n description={tr(\n 'technospam.mentorship.signInDesc',\n \"Your booked and hosted mentorship sessions will appear here once you're signed in.\"\n )}\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n hero\n title={tr('technospam.mentorship.sessionsTitle', 'My Sessions')}\n description={tr(\n 'technospam.mentorship.sessionsDescription',\n \"Manage the mentorship sessions you've booked and the ones you host.\"\n )}\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/mentorship/mentors-page')}>\n <Users size={16} /> {tr('technospam.common.findMentor', 'Find a mentor')}\n </Button>\n }\n >\n <div className=\"mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard\n label={tr('technospam.mentorship.statAsMentee', 'As mentee')}\n value={menteeSessions.length}\n icon={MessageSquare}\n tone=\"blue\"\n />\n <StatCard\n label={tr('technospam.mentorship.statAsMentor', 'As mentor')}\n value={mentorId ? mentorSessions.length : 0}\n icon={Users}\n tone=\"purple\"\n />\n <StatCard\n label={tr('technospam.mentorship.statUpcoming', 'Upcoming')}\n value={upcomingCount}\n icon={CalendarClock}\n tone=\"teal\"\n />\n <StatCard\n label={tr('technospam.mentorship.statCompleted', 'Completed')}\n value={completedCount}\n icon={CheckCircle2}\n tone=\"green\"\n />\n </div>\n\n {/* The tabs filter one list, so that list is their tab panel: Radix points every\n trigger's aria-controls at a panel id, and without a TabsContent the id\n resolved to nothing (axe aria-valid-attr-value, critical; BOFF-3157). */}\n <Tabs value={tab} onValueChange={(v) => setTab(v as Perspective)} className=\"gap-4\">\n <TabsList>\n <TabsTrigger value=\"mentee\">\n {tr('technospam.mentorship.tabAsMentee', 'As mentee')}\n </TabsTrigger>\n <TabsTrigger value=\"mentor\">\n {tr('technospam.mentorship.tabAsMentor', 'As mentor')}\n </TabsTrigger>\n </TabsList>\n\n <TabsContent value={tab} className={TAB_PANEL_FOCUS_CLASS}>\n {tab === 'mentee' ? (\n <SessionsList\n loading={menteeQ.loading}\n error={menteeQ.error}\n sessions={menteeSessions}\n showMentor\n emptyTitle={tr(\n 'technospam.mentorship.noSessionsBookedTitle',\n 'No sessions booked yet'\n )}\n emptyDescription={tr(\n 'technospam.mentorship.noSessionsBookedDesc',\n 'Find a mentor and request a session to get personalized guidance.'\n )}\n emptyAction={\n <Button onClick={() => navigate('/mentorship/mentors-page')}>\n {tr('technospam.common.findMentor', 'Find a mentor')}\n </Button>\n }\n isMentor={false}\n onCancel={handleCancel}\n onConfirm={handleConfirm}\n onComplete={handleComplete}\n busyId={cancellingId}\n focusedId={focusedSessionId}\n onFocusChange={setFocusedSessionId}\n />\n ) : mentorsQ.loading && !myMentor ? (\n <LoadingRows count={3} height=\"h-24\" />\n ) : !mentorId ? (\n <EmptyView\n icon={<Users size={26} />}\n title={tr('technospam.mentorship.notAMentorTitle', \"You're not a mentor yet\")}\n description={tr(\n 'technospam.mentorship.notAMentorDesc',\n 'Create a mentor profile to start hosting sessions and helping others build.'\n )}\n action={\n <Button onClick={() => navigate('/mentorship/mentors-page')}>\n {tr('technospam.common.becomeAMentor', 'Become a mentor')}\n </Button>\n }\n />\n ) : (\n <SessionsList\n loading={mentorQ.loading}\n error={mentorQ.error}\n sessions={mentorSessions}\n showMentor={false}\n emptyTitle={tr(\n 'technospam.mentorship.noSessionsToHostTitle',\n 'No sessions to host yet'\n )}\n emptyDescription={tr(\n 'technospam.mentorship.noSessionsToHostDesc',\n 'When mentees request time with you, their sessions will show up here.'\n )}\n isMentor\n onCancel={handleCancel}\n onConfirm={handleConfirm}\n onComplete={handleComplete}\n busyId={cancellingId}\n focusedId={focusedSessionId}\n onFocusChange={setFocusedSessionId}\n />\n )}\n </TabsContent>\n </Tabs>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|