@burdenoff/microfe-technospam 2026.1030.1 → 2026.1030.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/technospam/api/technospamApi.d.ts +28 -0
- package/dist/technospam/api/technospamApi.d.ts.map +1 -1
- package/dist/technospam/api/technospamApi.js +193 -153
- package/dist/technospam/api/technospamApi.js.map +1 -1
- package/dist/technospam/components/SubmissionReviewPanel.d.ts +15 -0
- package/dist/technospam/components/SubmissionReviewPanel.d.ts.map +1 -0
- package/dist/technospam/components/SubmissionReviewPanel.js +85 -0
- package/dist/technospam/components/SubmissionReviewPanel.js.map +1 -0
- package/dist/technospam/components/index.d.ts +1 -0
- package/dist/technospam/components/index.d.ts.map +1 -1
- package/dist/technospam/components/index.js +1 -0
- package/dist/technospam/pages/AdminAdminDashboardPagePage.js +1 -1
- package/dist/technospam/pages/AnalyticsAnalyticsPagePage.js +1 -1
- package/dist/technospam/pages/ApplicationsPortfolioPagePage.js +1 -1
- package/dist/technospam/pages/ApplicationsProblemDetailsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemDetailsPagePage.js +104 -94
- package/dist/technospam/pages/ApplicationsProblemDetailsPagePage.js.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js +1 -1
- package/dist/technospam/pages/CoursesCourseDetailsPagePage.js +1 -1
- package/dist/technospam/pages/CoursesCoursesPagePage.js +1 -1
- package/dist/technospam/pages/CoursesMyLearningPagePage.js +1 -1
- package/dist/technospam/pages/DashboardDashboardPagePage.js +1 -1
- package/dist/technospam/pages/HackathonsHackathonDetailsPagePage.js +1 -1
- package/dist/technospam/pages/HackathonsHackathonsPagePage.js +1 -1
- package/dist/technospam/pages/MentorshipMentorProfilePagePage.js +1 -1
- package/dist/technospam/pages/MentorshipMentorsPagePage.js +1 -1
- package/dist/technospam/pages/MentorshipSessionsPagePage.js +1 -1
- package/dist/technospam/pages/StudyPlannerStudyPlannerPagePage.js +1 -1
- package/package.json +1 -1
|
@@ -1,51 +1,52 @@
|
|
|
1
|
-
import { useTechnoSpam as e } from "../providers/TechnoSpamProvider.js";
|
|
2
|
-
import { useTechnoSpamAnalytics as
|
|
3
|
-
import { cn as
|
|
4
|
-
import { PageLayout as
|
|
5
|
-
import { StatCard as
|
|
6
|
-
import { formatDateTime as
|
|
7
|
-
import { StatusBadge as
|
|
8
|
-
import { EmptyView as
|
|
1
|
+
import { useTechnoSpam as e, useTechnoSpamNavigate as t } from "../providers/TechnoSpamProvider.js";
|
|
2
|
+
import { useTechnoSpamAnalytics as n } from "../hooks/useTechnoSpamAnalytics.js";
|
|
3
|
+
import { cn as r } from "../utils/cn.js";
|
|
4
|
+
import { PageLayout as i } from "../components/PageLayout.js";
|
|
5
|
+
import { StatCard as a } from "../components/StatCard.js";
|
|
6
|
+
import { formatDateTime as o, humanizeEnum as s } from "../utils/format.js";
|
|
7
|
+
import { StatusBadge as c } from "../components/StatusBadge.js";
|
|
8
|
+
import { EmptyView as l, ErrorView as u, LoadingRows as ee } from "../components/StateViews.js";
|
|
9
9
|
import { TagStrip as te } from "../components/TagStrip.js";
|
|
10
|
-
import { PremiumBadge as ne, UpgradeGate as
|
|
11
|
-
import { CodePanel as re, IoBlock as
|
|
12
|
-
import "../
|
|
13
|
-
import { notify as f } from "../lib/toast.js";
|
|
10
|
+
import { PremiumBadge as ne, UpgradeGate as d } from "../components/UpgradeGate.js";
|
|
11
|
+
import { CodePanel as re, IoBlock as f, TestResultsPanel as ie } from "../components/SubmissionViews.js";
|
|
12
|
+
import { notify as p } from "../lib/toast.js";
|
|
14
13
|
import { useProblemQuery as ae, useRegradeSubmission as oe, useSubmissionsQuery as se, useSubmitSolution as ce } from "../api/technospamApi.js";
|
|
15
|
-
import {
|
|
16
|
-
import
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
14
|
+
import { SubmissionReviewPanel as le } from "../components/SubmissionReviewPanel.js";
|
|
15
|
+
import "../components/index.js";
|
|
16
|
+
import { FOCUS_PARAMS as ue } from "../utils/pageContext.js";
|
|
17
|
+
import { focusableCardProps as m, useFocusedRecordParam as de } from "../hooks/useFocusedRecordParam.js";
|
|
18
|
+
import { useEffect as h, useMemo as fe, useRef as g, useState as _ } from "react";
|
|
19
|
+
import { Clock as v, Code2 as y, FileText as b, FlaskConical as pe, Layers as x, RefreshCw as S, Send as me, Sparkles as he, Trophy as C } from "lucide-react";
|
|
20
|
+
import { useSearchParams as ge } from "react-router";
|
|
21
|
+
import { Fragment as _e, jsx as w, jsxs as T } from "react/jsx-runtime";
|
|
22
|
+
import { Badge as ve, Button as E, GlassCard as D, Select as ye, SelectContent as be, SelectItem as xe, SelectTrigger as Se, SelectValue as Ce, Skeleton as O, Textarea as we } from "@burdenoff/fe-libs/ui";
|
|
22
23
|
//#region src/technospam/pages/ApplicationsProblemDetailsPagePage.tsx
|
|
23
24
|
var k = [
|
|
24
25
|
"javascript",
|
|
25
26
|
"python",
|
|
26
27
|
"typescript"
|
|
27
|
-
],
|
|
28
|
-
function
|
|
28
|
+
], Te = 3e3, Ee = 3 * 6e4;
|
|
29
|
+
function De({ s: e, focused: t, onToggle: n }) {
|
|
29
30
|
return /* @__PURE__ */ T("div", {
|
|
30
|
-
className:
|
|
31
|
-
...
|
|
31
|
+
className: r("cursor-pointer rounded-lg border border-transparent px-2 py-3 transition-colors hover:border-border-seam hover:bg-bg-sunken", t && "ring-2 ring-accent-primary"),
|
|
32
|
+
...m(t, n),
|
|
32
33
|
children: [/* @__PURE__ */ T("div", {
|
|
33
34
|
className: "flex flex-wrap items-center gap-x-4 gap-y-1",
|
|
34
35
|
children: [
|
|
35
36
|
/* @__PURE__ */ w("button", {
|
|
36
37
|
type: "button",
|
|
37
38
|
"aria-pressed": t,
|
|
38
|
-
"aria-label": `Focus submission from ${
|
|
39
|
-
onClick:
|
|
39
|
+
"aria-label": `Focus submission from ${o(e.createdAt)}`,
|
|
40
|
+
onClick: n,
|
|
40
41
|
className: "rounded-full",
|
|
41
|
-
children: /* @__PURE__ */ w(
|
|
42
|
+
children: /* @__PURE__ */ w(c, { status: e.verdict })
|
|
42
43
|
}),
|
|
43
44
|
/* @__PURE__ */ T("span", {
|
|
44
45
|
className: "inline-flex items-center gap-1 text-sm font-medium text-text-primary",
|
|
45
46
|
children: [
|
|
46
47
|
/* @__PURE__ */ w(y, { size: 13 }),
|
|
47
48
|
" ",
|
|
48
|
-
|
|
49
|
+
s(e.language)
|
|
49
50
|
]
|
|
50
51
|
}),
|
|
51
52
|
/* @__PURE__ */ T("span", {
|
|
@@ -68,7 +69,7 @@ function Ce({ s: e, focused: t, onToggle: r }) {
|
|
|
68
69
|
}),
|
|
69
70
|
/* @__PURE__ */ w("span", {
|
|
70
71
|
className: "ml-auto text-xs text-text-muted",
|
|
71
|
-
children:
|
|
72
|
+
children: o(e.createdAt)
|
|
72
73
|
})
|
|
73
74
|
]
|
|
74
75
|
}), e.totalCount > 0 ? /* @__PURE__ */ T("p", {
|
|
@@ -84,69 +85,69 @@ function Ce({ s: e, focused: t, onToggle: r }) {
|
|
|
84
85
|
});
|
|
85
86
|
}
|
|
86
87
|
function A() {
|
|
87
|
-
let [
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
let [r] = ge(), o = r.get("id") ?? "", [m, A] = de(ue.submission), { currentUser: Oe, workspaceId: j } = e(), ke = t(), M = Oe?.id, { emit: N } = n(), { data: Ae, loading: je, error: P, refetch: F } = ae(o);
|
|
89
|
+
h(() => {
|
|
90
|
+
o && N("technospam.problem.viewed", {
|
|
90
91
|
workspaceId: j,
|
|
91
|
-
entityId:
|
|
92
|
+
entityId: o,
|
|
92
93
|
entityType: "TEC_PROBLEM"
|
|
93
94
|
});
|
|
94
95
|
}, [
|
|
95
|
-
|
|
96
|
+
o,
|
|
96
97
|
j,
|
|
97
98
|
N
|
|
98
99
|
]);
|
|
99
|
-
let { data: I, loading:
|
|
100
|
+
let { data: I, loading: Me, error: L, refetch: R } = se({
|
|
100
101
|
...M ? { learnerId: M } : {},
|
|
101
|
-
problemId:
|
|
102
|
-
}), [
|
|
103
|
-
|
|
102
|
+
problemId: o
|
|
103
|
+
}), [Ne, { loading: z }] = ce(), [Pe, { loading: B }] = oe(), V = Ae?.tecProblem ?? null, H = fe(() => V?.languages?.length ? V.languages : k, [V]), [U, W] = _(""), [G, K] = _(""), q = g(!1);
|
|
104
|
+
h(() => {
|
|
104
105
|
V && !q.current && (W(V.languages[0] ?? k[0]), K(V.starterCode ?? ""), q.current = !0);
|
|
105
106
|
}, [V]);
|
|
106
|
-
let J =
|
|
107
|
-
if (!
|
|
107
|
+
let J = fe(() => [...I?.tecListSubmissions?.items ?? []].sort((e, t) => new Date(t.createdAt).getTime() - new Date(e.createdAt).getTime()), [I]), Fe = async () => {
|
|
108
|
+
if (!o) return;
|
|
108
109
|
if (!U) {
|
|
109
|
-
|
|
110
|
+
p.error("Pick a language", "Choose a language before submitting.");
|
|
110
111
|
return;
|
|
111
112
|
}
|
|
112
113
|
if (!G.trim()) {
|
|
113
|
-
|
|
114
|
+
p.error("Add your solution", "Write some code before submitting.");
|
|
114
115
|
return;
|
|
115
116
|
}
|
|
116
117
|
N("technospam.problem.submit_clicked", {
|
|
117
118
|
workspaceId: j,
|
|
118
|
-
entityId:
|
|
119
|
+
entityId: o,
|
|
119
120
|
entityType: "TEC_PROBLEM",
|
|
120
121
|
language: U
|
|
121
122
|
});
|
|
122
|
-
let e = await
|
|
123
|
-
problemId:
|
|
123
|
+
let e = await Ne({ variables: { input: {
|
|
124
|
+
problemId: o,
|
|
124
125
|
language: U,
|
|
125
126
|
code: G
|
|
126
127
|
} } });
|
|
127
128
|
if (e.data?.tecSubmitSolution) {
|
|
128
129
|
N("technospam.problem.submitted", {
|
|
129
130
|
workspaceId: j,
|
|
130
|
-
entityId:
|
|
131
|
+
entityId: o,
|
|
131
132
|
entityType: "TEC_PROBLEM",
|
|
132
133
|
submissionId: e.data.tecSubmitSolution.id
|
|
133
134
|
});
|
|
134
135
|
let t = e.data.tecSubmitSolution;
|
|
135
|
-
t.verdict === "PENDING" ?
|
|
136
|
+
t.verdict === "PENDING" ? p.info("Solution submitted", "Grading has started — your verdict will appear here in a moment.") : t.verdict === "ACCEPTED" ? p.success("Accepted", `Passed all ${t.totalCount} test cases.`) : p.error(s(t.verdict), `Passed ${t.passedCount} of ${t.totalCount} test cases.`), await Promise.all([F(), R()]);
|
|
136
137
|
}
|
|
137
|
-
},
|
|
138
|
-
let t = (await
|
|
139
|
-
t && (t.verdict === "PENDING" ?
|
|
140
|
-
}, Y = J.find((e) => (e.testResults?.length ?? 0) > 0) ?? null, X = J[0] ?? null, Z = X?.verdict === "PENDING" ? X.id : null, [Q,
|
|
141
|
-
|
|
138
|
+
}, Ie = async (e) => {
|
|
139
|
+
let t = (await Pe({ variables: { id: e } })).data?.tecRegradeSubmission;
|
|
140
|
+
t && (t.verdict === "PENDING" ? p.error("Still pending", "The grader is not available yet. Please try again shortly.") : p.success("Grading complete", `Verdict: ${s(t.verdict)}.`), await Promise.all([F(), R()]));
|
|
141
|
+
}, Y = J.find((e) => (e.testResults?.length ?? 0) > 0) ?? null, X = J[0] ?? null, Z = X?.verdict === "PENDING" ? X.id : null, [Q, Le] = _(null), Re = Z !== null && Q !== Z;
|
|
142
|
+
h(() => {
|
|
142
143
|
if (!Z || Q === Z) return;
|
|
143
144
|
let e = Date.now(), t = window.setInterval(() => {
|
|
144
|
-
if (Date.now() - e >
|
|
145
|
-
window.clearInterval(t),
|
|
145
|
+
if (Date.now() - e > Ee) {
|
|
146
|
+
window.clearInterval(t), Le(Z);
|
|
146
147
|
return;
|
|
147
148
|
}
|
|
148
149
|
R();
|
|
149
|
-
},
|
|
150
|
+
}, Te);
|
|
150
151
|
return () => window.clearInterval(t);
|
|
151
152
|
}, [
|
|
152
153
|
Z,
|
|
@@ -154,7 +155,7 @@ function A() {
|
|
|
154
155
|
R
|
|
155
156
|
]);
|
|
156
157
|
let $ = g(null);
|
|
157
|
-
return
|
|
158
|
+
return h(() => {
|
|
158
159
|
if (Z) {
|
|
159
160
|
$.current = Z;
|
|
160
161
|
return;
|
|
@@ -162,26 +163,26 @@ function A() {
|
|
|
162
163
|
let e = $.current;
|
|
163
164
|
if (!e) return;
|
|
164
165
|
let t = J.find((t) => t.id === e);
|
|
165
|
-
!t || t.verdict === "PENDING" || ($.current = null, t.verdict === "ACCEPTED" ?
|
|
166
|
+
!t || t.verdict === "PENDING" || ($.current = null, t.verdict === "ACCEPTED" ? p.success("Accepted", `Passed all ${t.totalCount} test cases.`) : p.error(s(t.verdict), `Passed ${t.passedCount} of ${t.totalCount} test cases.`), F());
|
|
166
167
|
}, [
|
|
167
168
|
Z,
|
|
168
169
|
J,
|
|
169
170
|
F
|
|
170
|
-
]),
|
|
171
|
+
]), je && !V ? /* @__PURE__ */ T(i, {
|
|
171
172
|
title: "Problem",
|
|
172
173
|
backPath: "/applications/problems-page",
|
|
173
174
|
children: [/* @__PURE__ */ w(O, { className: "mb-4 h-28 w-full rounded-xl" }), /* @__PURE__ */ w(O, { className: "h-72 w-full rounded-xl" })]
|
|
174
|
-
}) : P || !V ? /* @__PURE__ */ w(
|
|
175
|
+
}) : P || !V ? /* @__PURE__ */ w(i, {
|
|
175
176
|
title: "Problem",
|
|
176
177
|
backPath: "/applications/problems-page",
|
|
177
|
-
children: P ? /* @__PURE__ */ w(
|
|
178
|
+
children: P ? /* @__PURE__ */ w(u, {
|
|
178
179
|
title: "Couldn't load problem",
|
|
179
180
|
error: P
|
|
180
|
-
}) : /* @__PURE__ */ w(
|
|
181
|
+
}) : /* @__PURE__ */ w(l, {
|
|
181
182
|
title: "Problem not found",
|
|
182
183
|
description: "This problem may have been unpublished."
|
|
183
184
|
})
|
|
184
|
-
}) : /* @__PURE__ */ T(
|
|
185
|
+
}) : /* @__PURE__ */ T(i, {
|
|
185
186
|
hero: !0,
|
|
186
187
|
backPath: "/applications/problems-page",
|
|
187
188
|
title: V.title,
|
|
@@ -190,12 +191,12 @@ function A() {
|
|
|
190
191
|
/* @__PURE__ */ T("div", {
|
|
191
192
|
className: "mb-5 flex flex-wrap items-center gap-2",
|
|
192
193
|
children: [
|
|
193
|
-
/* @__PURE__ */ w(
|
|
194
|
+
/* @__PURE__ */ w(c, {
|
|
194
195
|
status: V.difficulty,
|
|
195
196
|
kind: "level"
|
|
196
197
|
}),
|
|
197
|
-
/* @__PURE__ */ w(
|
|
198
|
-
V.topic ? /* @__PURE__ */ T(
|
|
198
|
+
/* @__PURE__ */ w(c, { status: V.status }),
|
|
199
|
+
V.topic ? /* @__PURE__ */ T(ve, {
|
|
199
200
|
variant: "outline",
|
|
200
201
|
className: "gap-1",
|
|
201
202
|
children: [
|
|
@@ -211,25 +212,25 @@ function A() {
|
|
|
211
212
|
/* @__PURE__ */ T("div", {
|
|
212
213
|
className: "mb-6 grid grid-cols-2 gap-3 sm:grid-cols-4",
|
|
213
214
|
children: [
|
|
214
|
-
/* @__PURE__ */ w(
|
|
215
|
+
/* @__PURE__ */ w(a, {
|
|
215
216
|
label: "Points",
|
|
216
217
|
value: V.points,
|
|
217
218
|
icon: C,
|
|
218
219
|
tone: "amber"
|
|
219
220
|
}),
|
|
220
|
-
/* @__PURE__ */ w(
|
|
221
|
+
/* @__PURE__ */ w(a, {
|
|
221
222
|
label: "Languages",
|
|
222
223
|
value: H.length,
|
|
223
224
|
icon: y,
|
|
224
225
|
tone: "blue"
|
|
225
226
|
}),
|
|
226
|
-
/* @__PURE__ */ w(
|
|
227
|
+
/* @__PURE__ */ w(a, {
|
|
227
228
|
label: "Difficulty",
|
|
228
|
-
value:
|
|
229
|
+
value: s(V.difficulty),
|
|
229
230
|
icon: x,
|
|
230
231
|
tone: "purple"
|
|
231
232
|
}),
|
|
232
|
-
/* @__PURE__ */ w(
|
|
233
|
+
/* @__PURE__ */ w(a, {
|
|
233
234
|
label: "My submissions",
|
|
234
235
|
value: J.length,
|
|
235
236
|
icon: b,
|
|
@@ -247,7 +248,7 @@ function A() {
|
|
|
247
248
|
children: [/* @__PURE__ */ w("h2", {
|
|
248
249
|
className: "mb-2 text-base font-semibold text-text-primary",
|
|
249
250
|
children: "Problem statement"
|
|
250
|
-
}), V.isLocked ? /* @__PURE__ */ w(
|
|
251
|
+
}), V.isLocked ? /* @__PURE__ */ w(d, { description: "This problem's statement is part of a paid TechnoSpam plan. Ask a workspace admin to upgrade to unlock it." }) : V.statement ? /* @__PURE__ */ w("p", {
|
|
251
252
|
className: "whitespace-pre-wrap text-sm leading-relaxed text-text-secondary",
|
|
252
253
|
children: V.statement
|
|
253
254
|
}) : /* @__PURE__ */ w("p", {
|
|
@@ -255,7 +256,7 @@ function A() {
|
|
|
255
256
|
children: "The statement for this problem is being prepared."
|
|
256
257
|
})]
|
|
257
258
|
}),
|
|
258
|
-
V.isLocked ? /* @__PURE__ */ w(
|
|
259
|
+
V.isLocked ? /* @__PURE__ */ w(d, {
|
|
259
260
|
compact: !0,
|
|
260
261
|
description: "Starter code and sample test cases unlock with a paid TechnoSpam plan."
|
|
261
262
|
}) : V.starterCode ? /* @__PURE__ */ T(D, {
|
|
@@ -264,7 +265,7 @@ function A() {
|
|
|
264
265
|
className: "mb-3 text-base font-semibold text-text-primary",
|
|
265
266
|
children: "Starter code"
|
|
266
267
|
}), /* @__PURE__ */ w(re, {
|
|
267
|
-
label:
|
|
268
|
+
label: s(H[0] ?? "code"),
|
|
268
269
|
bodyClassName: "max-h-72",
|
|
269
270
|
children: V.starterCode
|
|
270
271
|
})]
|
|
@@ -282,15 +283,15 @@ function A() {
|
|
|
282
283
|
children: [/* @__PURE__ */ w("span", {
|
|
283
284
|
className: "text-sm text-text-secondary",
|
|
284
285
|
children: "Language"
|
|
285
|
-
}), /* @__PURE__ */ T(
|
|
286
|
+
}), /* @__PURE__ */ T(ye, {
|
|
286
287
|
value: U,
|
|
287
288
|
onValueChange: W,
|
|
288
|
-
children: [/* @__PURE__ */ w(
|
|
289
|
+
children: [/* @__PURE__ */ w(Se, {
|
|
289
290
|
className: "w-full sm:w-48",
|
|
290
|
-
children: /* @__PURE__ */ w(
|
|
291
|
-
}), /* @__PURE__ */ w(
|
|
291
|
+
children: /* @__PURE__ */ w(Ce, { placeholder: "Choose a language" })
|
|
292
|
+
}), /* @__PURE__ */ w(be, { children: H.map((e) => /* @__PURE__ */ w(xe, {
|
|
292
293
|
value: e,
|
|
293
|
-
children:
|
|
294
|
+
children: s(e)
|
|
294
295
|
}, e)) })]
|
|
295
296
|
})]
|
|
296
297
|
})]
|
|
@@ -311,10 +312,10 @@ function A() {
|
|
|
311
312
|
children: [
|
|
312
313
|
/* @__PURE__ */ w(y, { size: 13 }),
|
|
313
314
|
" ",
|
|
314
|
-
U ?
|
|
315
|
+
U ? s(U) : "solution"
|
|
315
316
|
]
|
|
316
317
|
})]
|
|
317
|
-
}), /* @__PURE__ */ w(
|
|
318
|
+
}), /* @__PURE__ */ w(we, {
|
|
318
319
|
value: G,
|
|
319
320
|
onChange: (e) => K(e.target.value),
|
|
320
321
|
placeholder: "Write your solution…",
|
|
@@ -324,10 +325,10 @@ function A() {
|
|
|
324
325
|
}),
|
|
325
326
|
/* @__PURE__ */ T(E, {
|
|
326
327
|
className: "mt-4 w-full",
|
|
327
|
-
onClick:
|
|
328
|
+
onClick: Fe,
|
|
328
329
|
disabled: z || !G.trim(),
|
|
329
330
|
children: [
|
|
330
|
-
/* @__PURE__ */ w(
|
|
331
|
+
/* @__PURE__ */ w(me, { size: 16 }),
|
|
331
332
|
" ",
|
|
332
333
|
z ? "Running test cases…" : "Run & submit"
|
|
333
334
|
]
|
|
@@ -343,7 +344,7 @@ function A() {
|
|
|
343
344
|
children: [
|
|
344
345
|
/* @__PURE__ */ T("h2", {
|
|
345
346
|
className: "mb-1 flex items-center gap-2 text-base font-semibold text-text-primary",
|
|
346
|
-
children: [/* @__PURE__ */ w(
|
|
347
|
+
children: [/* @__PURE__ */ w(pe, { size: 16 }), " Sample test cases"]
|
|
347
348
|
}),
|
|
348
349
|
/* @__PURE__ */ w("p", {
|
|
349
350
|
className: "mb-3 text-sm text-text-secondary",
|
|
@@ -369,10 +370,10 @@ function A() {
|
|
|
369
370
|
})]
|
|
370
371
|
}), /* @__PURE__ */ T("div", {
|
|
371
372
|
className: "grid grid-cols-1 gap-2 sm:grid-cols-2",
|
|
372
|
-
children: [/* @__PURE__ */ w(
|
|
373
|
+
children: [/* @__PURE__ */ w(f, {
|
|
373
374
|
label: "Input",
|
|
374
375
|
value: e.input
|
|
375
|
-
}), /* @__PURE__ */ w(
|
|
376
|
+
}), /* @__PURE__ */ w(f, {
|
|
376
377
|
label: "Expected output",
|
|
377
378
|
value: e.expectedOutput
|
|
378
379
|
})]
|
|
@@ -388,7 +389,7 @@ function A() {
|
|
|
388
389
|
className: "mb-2 text-base font-semibold text-text-primary",
|
|
389
390
|
children: "Latest run"
|
|
390
391
|
}),
|
|
391
|
-
|
|
392
|
+
Re ? /* @__PURE__ */ T("p", {
|
|
392
393
|
className: "mb-3 inline-flex items-center gap-2 text-sm text-text-secondary",
|
|
393
394
|
children: [/* @__PURE__ */ w(S, {
|
|
394
395
|
size: 14,
|
|
@@ -400,7 +401,7 @@ function A() {
|
|
|
400
401
|
}),
|
|
401
402
|
/* @__PURE__ */ T(E, {
|
|
402
403
|
variant: "outline",
|
|
403
|
-
onClick: () => void
|
|
404
|
+
onClick: () => void Ie(Z),
|
|
404
405
|
disabled: B,
|
|
405
406
|
children: [
|
|
406
407
|
/* @__PURE__ */ w(S, { size: 16 }),
|
|
@@ -409,13 +410,22 @@ function A() {
|
|
|
409
410
|
]
|
|
410
411
|
})
|
|
411
412
|
]
|
|
412
|
-
}) : Y ? /* @__PURE__ */ T(D, {
|
|
413
|
+
}) : Y ? /* @__PURE__ */ T(_e, { children: [/* @__PURE__ */ T(D, {
|
|
413
414
|
className: "p-5",
|
|
414
415
|
children: [/* @__PURE__ */ w("h2", {
|
|
415
416
|
className: "mb-3 text-base font-semibold text-text-primary",
|
|
416
417
|
children: "Latest run"
|
|
417
418
|
}), /* @__PURE__ */ w(ie, { submission: Y })]
|
|
418
|
-
})
|
|
419
|
+
}), /* @__PURE__ */ T(D, {
|
|
420
|
+
className: "p-5",
|
|
421
|
+
children: [/* @__PURE__ */ T("h2", {
|
|
422
|
+
className: "mb-3 flex items-center gap-2 text-base font-semibold text-text-primary",
|
|
423
|
+
children: [/* @__PURE__ */ w(he, { size: 16 }), " TechnoSage review"]
|
|
424
|
+
}), /* @__PURE__ */ w(le, {
|
|
425
|
+
submission: Y,
|
|
426
|
+
onOpenProblem: (e) => ke(`/applications/problem-details-page?id=${e}`)
|
|
427
|
+
})]
|
|
428
|
+
})] }) : null
|
|
419
429
|
]
|
|
420
430
|
}), /* @__PURE__ */ w("div", {
|
|
421
431
|
className: "lg:col-span-1",
|
|
@@ -424,22 +434,22 @@ function A() {
|
|
|
424
434
|
children: [/* @__PURE__ */ w("h2", {
|
|
425
435
|
className: "mb-3 text-base font-semibold text-text-primary",
|
|
426
436
|
children: "My submissions"
|
|
427
|
-
}),
|
|
437
|
+
}), Me && !J.length ? /* @__PURE__ */ w(ee, {
|
|
428
438
|
count: 3,
|
|
429
439
|
height: "h-12"
|
|
430
|
-
}) : L ? /* @__PURE__ */ w(
|
|
440
|
+
}) : L ? /* @__PURE__ */ w(u, {
|
|
431
441
|
title: "Couldn't load submissions",
|
|
432
442
|
error: L
|
|
433
|
-
}) : J.length === 0 ? /* @__PURE__ */ w(
|
|
443
|
+
}) : J.length === 0 ? /* @__PURE__ */ w(l, {
|
|
434
444
|
icon: /* @__PURE__ */ w(b, { size: 24 }),
|
|
435
445
|
title: "No submissions yet",
|
|
436
446
|
description: "Submit a solution above to see your verdicts and points here."
|
|
437
447
|
}) : /* @__PURE__ */ w("div", {
|
|
438
448
|
className: "space-y-1",
|
|
439
|
-
children: J.map((e) => /* @__PURE__ */ w(
|
|
449
|
+
children: J.map((e) => /* @__PURE__ */ w(De, {
|
|
440
450
|
s: e,
|
|
441
|
-
focused:
|
|
442
|
-
onToggle: () => A(
|
|
451
|
+
focused: m === e.id,
|
|
452
|
+
onToggle: () => A(m === e.id ? null : e.id)
|
|
443
453
|
}, e.id))
|
|
444
454
|
})]
|
|
445
455
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApplicationsProblemDetailsPagePage.js","names":[],"sources":["../../../src/technospam/pages/ApplicationsProblemDetailsPagePage.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport { useSearchParams } from 'react-router';\nimport {\n Clock,\n Code2,\n FileText,\n FlaskConical,\n Layers,\n RefreshCw,\n Send,\n Tag,\n Trophy,\n} from 'lucide-react';\nimport {\n Badge,\n Button,\n GlassCard,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Skeleton,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport {\n CodePanel,\n EmptyView,\n ErrorView,\n IoBlock,\n LoadingRows,\n PageLayout,\n PremiumBadge,\n StatCard,\n StatusBadge,\n TagStrip,\n TestResultsPanel,\n UpgradeGate,\n} from '../components';\nimport { useTechnoSpam } from '../providers/TechnoSpamProvider';\nimport {\n useProblemQuery,\n useRegradeSubmission,\n useSubmissionsQuery,\n useSubmitSolution,\n type TecSubmission,\n} from '../api/technospamApi';\nimport { formatDateTime, humanizeEnum } 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';\n\nconst FALLBACK_LANGUAGES = ['javascript', 'python', 'typescript'];\n\n/**\n * A submission made while the grading sandbox is cold comes back PENDING and is\n * graded in the background (BOFF-4414). Poll the submissions list until the\n * verdict lands, giving up after a few minutes so a genuinely failed grade\n * falls back to the manual re-run button.\n */\nconst PENDING_POLL_INTERVAL_MS = 3_000;\nconst PENDING_POLL_MAX_MS = 3 * 60_000;\n\nfunction SubmissionRow({\n s,\n focused,\n onToggle,\n}: {\n s: TecSubmission;\n /** The focused submission (`?submissionId=`) is the one the assistant acts on. */\n focused: boolean;\n onToggle: () => void;\n}) {\n return (\n <div\n className={cn(\n 'cursor-pointer rounded-lg border border-transparent px-2 py-3 transition-colors hover:border-border-seam hover:bg-bg-sunken',\n focused && FOCUSED_CARD_CLASS\n )}\n {...focusableCardProps(focused, onToggle)}\n >\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-1\">\n <button\n type=\"button\"\n aria-pressed={focused}\n aria-label={`Focus submission from ${formatDateTime(s.createdAt)}`}\n onClick={onToggle}\n className=\"rounded-full\"\n >\n <StatusBadge status={s.verdict} />\n </button>\n <span className=\"inline-flex items-center gap-1 text-sm font-medium text-text-primary\">\n <Code2 size={13} /> {humanizeEnum(s.language)}\n </span>\n <span className=\"inline-flex items-center gap-1 text-xs text-text-muted\">\n <Clock size={12} /> {s.runtimeMs} ms\n </span>\n <span className=\"inline-flex items-center gap-1 text-xs text-text-muted\">\n <Trophy size={12} /> {s.pointsAwarded} pts\n </span>\n <span className=\"ml-auto text-xs text-text-muted\">{formatDateTime(s.createdAt)}</span>\n </div>\n {s.totalCount > 0 ? (\n <p className=\"mt-1 px-0.5 text-xs text-text-muted\">\n Passed {s.passedCount} of {s.totalCount} test cases\n </p>\n ) : null}\n </div>\n );\n}\n\nexport function ApplicationsProblemDetailsPagePage() {\n const [params] = useSearchParams();\n const id = params.get('id') ?? '';\n // The focused submission lives in `?submissionId=` so the assistant's page\n // context names it (\"explain this submission\").\n const [focusedSubmissionId, setFocusedSubmissionId] = useFocusedRecordParam(\n FOCUS_PARAMS.submission\n );\n const { currentUser, workspaceId } = useTechnoSpam();\n const learnerId = currentUser?.id;\n const { emit } = useTechnoSpamAnalytics();\n\n const { data, loading, error, refetch } = useProblemQuery(id);\n\n useEffect(() => {\n if (id) {\n emit('technospam.problem.viewed', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_PROBLEM',\n });\n }\n }, [id, workspaceId, emit]);\n const {\n data: subsData,\n loading: subsLoading,\n error: subsError,\n refetch: refetchSubs,\n } = useSubmissionsQuery({ ...(learnerId ? { learnerId } : {}), problemId: id });\n const [submit, { loading: submitting }] = useSubmitSolution();\n const [regrade, { loading: regrading }] = useRegradeSubmission();\n\n const problem = data?.tecProblem ?? null;\n\n const languages = useMemo(\n () => (problem?.languages?.length ? problem.languages : FALLBACK_LANGUAGES),\n [problem]\n );\n\n const [language, setLanguage] = useState('');\n const [code, setCode] = useState('');\n const initedRef = useRef(false);\n\n useEffect(() => {\n if (problem && !initedRef.current) {\n setLanguage(problem.languages[0] ?? FALLBACK_LANGUAGES[0]!);\n setCode(problem.starterCode ?? '');\n initedRef.current = true;\n }\n }, [problem]);\n\n const submissions = useMemo(\n () =>\n [...(subsData?.tecListSubmissions?.items ?? [])].sort(\n (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()\n ),\n [subsData]\n );\n\n const handleSubmit = async () => {\n if (!id) return;\n if (!language) {\n notify.error('Pick a language', 'Choose a language before submitting.');\n return;\n }\n if (!code.trim()) {\n notify.error('Add your solution', 'Write some code before submitting.');\n return;\n }\n emit('technospam.problem.submit_clicked', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_PROBLEM',\n language,\n });\n const res = await submit({ variables: { input: { problemId: id, language, code } } });\n if (res.data?.tecSubmitSolution) {\n emit('technospam.problem.submitted', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_PROBLEM',\n submissionId: res.data.tecSubmitSolution.id,\n });\n const graded = res.data.tecSubmitSolution;\n if (graded.verdict === 'PENDING') {\n notify.info(\n 'Solution submitted',\n 'Grading has started — your verdict will appear here in a moment.'\n );\n } else if (graded.verdict === 'ACCEPTED') {\n notify.success('Accepted', `Passed all ${graded.totalCount} test cases.`);\n } else {\n notify.error(\n humanizeEnum(graded.verdict),\n `Passed ${graded.passedCount} of ${graded.totalCount} test cases.`\n );\n }\n await Promise.all([refetch(), refetchSubs()]);\n }\n };\n\n const handleRegrade = async (submissionId: string) => {\n const res = await regrade({ variables: { id: submissionId } });\n const graded = res.data?.tecRegradeSubmission;\n if (!graded) return;\n if (graded.verdict === 'PENDING') {\n notify.error('Still pending', 'The grader is not available yet. Please try again shortly.');\n } else {\n notify.success('Grading complete', `Verdict: ${humanizeEnum(graded.verdict)}.`);\n }\n await Promise.all([refetch(), refetchSubs()]);\n };\n\n /** Most recent submission that actually carries grading detail. */\n const latestGraded = submissions.find((s) => (s.testResults?.length ?? 0) > 0) ?? null;\n const latestSubmission = submissions[0] ?? null;\n const pendingId = latestSubmission?.verdict === 'PENDING' ? latestSubmission.id : null;\n\n // Poll while the newest submission is still being graded in the background.\n const [pollExpiredFor, setPollExpiredFor] = useState<string | null>(null);\n const polling = pendingId !== null && pollExpiredFor !== pendingId;\n useEffect(() => {\n if (!pendingId || pollExpiredFor === pendingId) return undefined;\n const startedAt = Date.now();\n const timer = window.setInterval(() => {\n if (Date.now() - startedAt > PENDING_POLL_MAX_MS) {\n window.clearInterval(timer);\n setPollExpiredFor(pendingId);\n return;\n }\n void refetchSubs();\n }, PENDING_POLL_INTERVAL_MS);\n return () => window.clearInterval(timer);\n }, [pendingId, pollExpiredFor, refetchSubs]);\n\n // Announce the verdict once a background-graded submission settles.\n const awaitedIdRef = useRef<string | null>(null);\n useEffect(() => {\n if (pendingId) {\n awaitedIdRef.current = pendingId;\n return;\n }\n const awaitedId = awaitedIdRef.current;\n if (!awaitedId) return;\n const settled = submissions.find((s) => s.id === awaitedId);\n if (!settled || settled.verdict === 'PENDING') return;\n awaitedIdRef.current = null;\n if (settled.verdict === 'ACCEPTED') {\n notify.success('Accepted', `Passed all ${settled.totalCount} test cases.`);\n } else {\n notify.error(\n humanizeEnum(settled.verdict),\n `Passed ${settled.passedCount} of ${settled.totalCount} test cases.`\n );\n }\n void refetch();\n }, [pendingId, submissions, refetch]);\n\n if (loading && !problem) {\n return (\n <PageLayout title=\"Problem\" backPath=\"/applications/problems-page\">\n <Skeleton className=\"mb-4 h-28 w-full rounded-xl\" />\n <Skeleton className=\"h-72 w-full rounded-xl\" />\n </PageLayout>\n );\n }\n\n if (error || !problem) {\n return (\n <PageLayout title=\"Problem\" backPath=\"/applications/problems-page\">\n {error ? (\n <ErrorView title=\"Couldn't load problem\" error={error} />\n ) : (\n <EmptyView\n title=\"Problem not found\"\n description=\"This problem may have been unpublished.\"\n />\n )}\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n hero\n backPath=\"/applications/problems-page\"\n title={problem.title}\n description={problem.topic ?? undefined}\n >\n <div className=\"mb-5 flex flex-wrap items-center gap-2\">\n <StatusBadge status={problem.difficulty} kind=\"level\" />\n <StatusBadge status={problem.status} />\n {problem.topic ? (\n <Badge variant=\"outline\" className=\"gap-1\">\n <Layers size={12} /> {problem.topic}\n </Badge>\n ) : null}\n {problem.isPremiumOnly ? <PremiumBadge locked={problem.isLocked} /> : null}\n <TagStrip tags={problem.tags} />\n </div>\n\n <div className=\"mb-6 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard label=\"Points\" value={problem.points} icon={Trophy} tone=\"amber\" />\n <StatCard label=\"Languages\" value={languages.length} icon={Code2} tone=\"blue\" />\n <StatCard\n label=\"Difficulty\"\n value={humanizeEnum(problem.difficulty)}\n icon={Layers}\n tone=\"purple\"\n />\n <StatCard label=\"My submissions\" value={submissions.length} icon={FileText} tone=\"teal\" />\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 lg:grid-cols-3\">\n <div className=\"space-y-5 lg:col-span-2\">\n <GlassCard className=\"p-5\">\n <h2 className=\"mb-2 text-base font-semibold text-text-primary\">Problem statement</h2>\n {problem.isLocked ? (\n // BOFF-4416: the API already nulled `statement` for a locked\n // premium problem — this explains why, rather than showing the\n // generic \"being prepared\" message used for a real content gap.\n <UpgradeGate description=\"This problem's statement is part of a paid TechnoSpam plan. Ask a workspace admin to upgrade to unlock it.\" />\n ) : problem.statement ? (\n <p className=\"whitespace-pre-wrap text-sm leading-relaxed text-text-secondary\">\n {problem.statement}\n </p>\n ) : (\n <p className=\"py-4 text-center text-sm text-text-muted\">\n The statement for this problem is being prepared.\n </p>\n )}\n </GlassCard>\n\n {problem.isLocked ? (\n <UpgradeGate\n compact\n description=\"Starter code and sample test cases unlock with a paid TechnoSpam plan.\"\n />\n ) : problem.starterCode ? (\n <GlassCard className=\"p-5\">\n <h2 className=\"mb-3 text-base font-semibold text-text-primary\">Starter code</h2>\n <CodePanel label={humanizeEnum(languages[0] ?? 'code')} bodyClassName=\"max-h-72\">\n {problem.starterCode}\n </CodePanel>\n </GlassCard>\n ) : null}\n\n <GlassCard className=\"p-5\">\n <div className=\"mb-3 flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n <h2 className=\"text-base font-semibold text-text-primary\">Submit a solution</h2>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-text-secondary\">Language</span>\n <Select value={language} onValueChange={setLanguage}>\n <SelectTrigger className=\"w-full sm:w-48\">\n <SelectValue placeholder=\"Choose a language\" />\n </SelectTrigger>\n <SelectContent>\n {languages.map((l) => (\n <SelectItem key={l} value={l}>\n {humanizeEnum(l)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"overflow-hidden rounded-xl border border-border-strong bg-bg-sunken shadow-sm\">\n <div className=\"flex items-center justify-between gap-2 border-b border-border-seam bg-bg-canvas/40 px-4 py-2\">\n <div className=\"flex items-center gap-1.5\">\n <span className=\"h-2.5 w-2.5 rounded-full bg-status-error-border\" />\n <span className=\"h-2.5 w-2.5 rounded-full bg-status-warning-border\" />\n <span className=\"h-2.5 w-2.5 rounded-full bg-status-success-border\" />\n </div>\n <span className=\"inline-flex items-center gap-1.5 font-mono text-xs text-text-muted\">\n <Code2 size={13} /> {language ? humanizeEnum(language) : 'solution'}\n </span>\n </div>\n <Textarea\n value={code}\n onChange={(e) => setCode(e.target.value)}\n placeholder=\"Write your solution…\"\n className=\"min-h-[280px] resize-y rounded-none border-0 bg-transparent font-mono text-xs leading-relaxed focus-visible:ring-0 focus-visible:ring-offset-0\"\n spellCheck={false}\n />\n </div>\n\n <Button\n className=\"mt-4 w-full\"\n onClick={handleSubmit}\n disabled={submitting || !code.trim()}\n >\n <Send size={16} /> {submitting ? 'Running test cases…' : 'Run & submit'}\n </Button>\n <p className=\"mt-2 text-center text-xs text-text-muted\">\n Your code runs against this problem's test cases in an isolated sandbox.\n </p>\n </GlassCard>\n\n {problem.publicTestCases?.length ? (\n <GlassCard className=\"p-5\">\n <h2 className=\"mb-1 flex items-center gap-2 text-base font-semibold text-text-primary\">\n <FlaskConical size={16} /> Sample test cases\n </h2>\n <p className=\"mb-3 text-sm text-text-secondary\">\n Your solution reads each input from stdin and must print the expected output. Hidden\n test cases are also run when you submit.\n </p>\n <div className=\"space-y-3\">\n {problem.publicTestCases.map((testCase) => (\n <div key={testCase.id} className=\"rounded-xl border border-border-seam p-3\">\n <div className=\"mb-2 flex flex-wrap items-center gap-x-3 gap-y-1\">\n <span className=\"text-sm font-medium text-text-primary\">{testCase.name}</span>\n <span className=\"inline-flex items-center gap-1 text-xs text-text-muted\">\n <Clock size={12} /> {testCase.timeLimitMs} ms limit\n </span>\n </div>\n <div className=\"grid grid-cols-1 gap-2 sm:grid-cols-2\">\n <IoBlock label=\"Input\" value={testCase.input} />\n <IoBlock label=\"Expected output\" value={testCase.expectedOutput} />\n </div>\n </div>\n ))}\n </div>\n </GlassCard>\n ) : null}\n\n {pendingId ? (\n <GlassCard className=\"p-5\">\n <h2 className=\"mb-2 text-base font-semibold text-text-primary\">Latest run</h2>\n {polling ? (\n <p className=\"mb-3 inline-flex items-center gap-2 text-sm text-text-secondary\">\n <RefreshCw size={14} className=\"animate-spin\" />\n Grading your submission — the grader is starting up. Your verdict will appear here\n automatically.\n </p>\n ) : (\n <p className=\"mb-3 text-sm text-text-secondary\">\n Your most recent submission has not been graded yet — the grader was unavailable.\n You can re-run it without resubmitting your code.\n </p>\n )}\n <Button\n variant=\"outline\"\n onClick={() => void handleRegrade(pendingId)}\n disabled={regrading}\n >\n <RefreshCw size={16} /> {regrading ? 'Re-running…' : 'Re-run grader'}\n </Button>\n </GlassCard>\n ) : latestGraded ? (\n <GlassCard className=\"p-5\">\n <h2 className=\"mb-3 text-base font-semibold text-text-primary\">Latest run</h2>\n <TestResultsPanel submission={latestGraded} />\n </GlassCard>\n ) : null}\n </div>\n\n <div className=\"lg:col-span-1\">\n <GlassCard className=\"p-5\">\n <h2 className=\"mb-3 text-base font-semibold text-text-primary\">My submissions</h2>\n {subsLoading && !submissions.length ? (\n <LoadingRows count={3} height=\"h-12\" />\n ) : subsError ? (\n <ErrorView title=\"Couldn't load submissions\" error={subsError} />\n ) : submissions.length === 0 ? (\n <EmptyView\n icon={<FileText size={24} />}\n title=\"No submissions yet\"\n description=\"Submit a solution above to see your verdicts and points here.\"\n />\n ) : (\n <div className=\"space-y-1\">\n {submissions.map((s) => (\n <SubmissionRow\n key={s.id}\n s={s}\n focused={focusedSubmissionId === s.id}\n onToggle={() =>\n setFocusedSubmissionId(focusedSubmissionId === s.id ? null : s.id)\n }\n />\n ))}\n </div>\n )}\n </GlassCard>\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ApplicationsProblemDetailsPagePage.js","names":[],"sources":["../../../src/technospam/pages/ApplicationsProblemDetailsPagePage.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport { useSearchParams } from 'react-router';\nimport {\n Clock,\n Code2,\n FileText,\n FlaskConical,\n Layers,\n RefreshCw,\n Send,\n Sparkles,\n Tag,\n Trophy,\n} from 'lucide-react';\nimport {\n Badge,\n Button,\n GlassCard,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Skeleton,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport {\n CodePanel,\n EmptyView,\n ErrorView,\n IoBlock,\n LoadingRows,\n PageLayout,\n PremiumBadge,\n StatCard,\n StatusBadge,\n SubmissionReviewPanel,\n TagStrip,\n TestResultsPanel,\n UpgradeGate,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n useProblemQuery,\n useRegradeSubmission,\n useSubmissionsQuery,\n useSubmitSolution,\n type TecSubmission,\n} from '../api/technospamApi';\nimport { formatDateTime, humanizeEnum } 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';\n\nconst FALLBACK_LANGUAGES = ['javascript', 'python', 'typescript'];\n\n/**\n * A submission made while the grading sandbox is cold comes back PENDING and is\n * graded in the background (BOFF-4414). Poll the submissions list until the\n * verdict lands, giving up after a few minutes so a genuinely failed grade\n * falls back to the manual re-run button.\n */\nconst PENDING_POLL_INTERVAL_MS = 3_000;\nconst PENDING_POLL_MAX_MS = 3 * 60_000;\n\nfunction SubmissionRow({\n s,\n focused,\n onToggle,\n}: {\n s: TecSubmission;\n /** The focused submission (`?submissionId=`) is the one the assistant acts on. */\n focused: boolean;\n onToggle: () => void;\n}) {\n return (\n <div\n className={cn(\n 'cursor-pointer rounded-lg border border-transparent px-2 py-3 transition-colors hover:border-border-seam hover:bg-bg-sunken',\n focused && FOCUSED_CARD_CLASS\n )}\n {...focusableCardProps(focused, onToggle)}\n >\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-1\">\n <button\n type=\"button\"\n aria-pressed={focused}\n aria-label={`Focus submission from ${formatDateTime(s.createdAt)}`}\n onClick={onToggle}\n className=\"rounded-full\"\n >\n <StatusBadge status={s.verdict} />\n </button>\n <span className=\"inline-flex items-center gap-1 text-sm font-medium text-text-primary\">\n <Code2 size={13} /> {humanizeEnum(s.language)}\n </span>\n <span className=\"inline-flex items-center gap-1 text-xs text-text-muted\">\n <Clock size={12} /> {s.runtimeMs} ms\n </span>\n <span className=\"inline-flex items-center gap-1 text-xs text-text-muted\">\n <Trophy size={12} /> {s.pointsAwarded} pts\n </span>\n <span className=\"ml-auto text-xs text-text-muted\">{formatDateTime(s.createdAt)}</span>\n </div>\n {s.totalCount > 0 ? (\n <p className=\"mt-1 px-0.5 text-xs text-text-muted\">\n Passed {s.passedCount} of {s.totalCount} test cases\n </p>\n ) : null}\n </div>\n );\n}\n\nexport function ApplicationsProblemDetailsPagePage() {\n const [params] = useSearchParams();\n const id = params.get('id') ?? '';\n // The focused submission lives in `?submissionId=` so the assistant's page\n // context names it (\"explain this submission\").\n const [focusedSubmissionId, setFocusedSubmissionId] = useFocusedRecordParam(\n FOCUS_PARAMS.submission\n );\n const { currentUser, workspaceId } = useTechnoSpam();\n const navigate = useTechnoSpamNavigate();\n const learnerId = currentUser?.id;\n const { emit } = useTechnoSpamAnalytics();\n\n const { data, loading, error, refetch } = useProblemQuery(id);\n\n useEffect(() => {\n if (id) {\n emit('technospam.problem.viewed', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_PROBLEM',\n });\n }\n }, [id, workspaceId, emit]);\n const {\n data: subsData,\n loading: subsLoading,\n error: subsError,\n refetch: refetchSubs,\n } = useSubmissionsQuery({ ...(learnerId ? { learnerId } : {}), problemId: id });\n const [submit, { loading: submitting }] = useSubmitSolution();\n const [regrade, { loading: regrading }] = useRegradeSubmission();\n\n const problem = data?.tecProblem ?? null;\n\n const languages = useMemo(\n () => (problem?.languages?.length ? problem.languages : FALLBACK_LANGUAGES),\n [problem]\n );\n\n const [language, setLanguage] = useState('');\n const [code, setCode] = useState('');\n const initedRef = useRef(false);\n\n useEffect(() => {\n if (problem && !initedRef.current) {\n setLanguage(problem.languages[0] ?? FALLBACK_LANGUAGES[0]!);\n setCode(problem.starterCode ?? '');\n initedRef.current = true;\n }\n }, [problem]);\n\n const submissions = useMemo(\n () =>\n [...(subsData?.tecListSubmissions?.items ?? [])].sort(\n (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()\n ),\n [subsData]\n );\n\n const handleSubmit = async () => {\n if (!id) return;\n if (!language) {\n notify.error('Pick a language', 'Choose a language before submitting.');\n return;\n }\n if (!code.trim()) {\n notify.error('Add your solution', 'Write some code before submitting.');\n return;\n }\n emit('technospam.problem.submit_clicked', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_PROBLEM',\n language,\n });\n const res = await submit({ variables: { input: { problemId: id, language, code } } });\n if (res.data?.tecSubmitSolution) {\n emit('technospam.problem.submitted', {\n workspaceId,\n entityId: id,\n entityType: 'TEC_PROBLEM',\n submissionId: res.data.tecSubmitSolution.id,\n });\n const graded = res.data.tecSubmitSolution;\n if (graded.verdict === 'PENDING') {\n notify.info(\n 'Solution submitted',\n 'Grading has started — your verdict will appear here in a moment.'\n );\n } else if (graded.verdict === 'ACCEPTED') {\n notify.success('Accepted', `Passed all ${graded.totalCount} test cases.`);\n } else {\n notify.error(\n humanizeEnum(graded.verdict),\n `Passed ${graded.passedCount} of ${graded.totalCount} test cases.`\n );\n }\n await Promise.all([refetch(), refetchSubs()]);\n }\n };\n\n const handleRegrade = async (submissionId: string) => {\n const res = await regrade({ variables: { id: submissionId } });\n const graded = res.data?.tecRegradeSubmission;\n if (!graded) return;\n if (graded.verdict === 'PENDING') {\n notify.error('Still pending', 'The grader is not available yet. Please try again shortly.');\n } else {\n notify.success('Grading complete', `Verdict: ${humanizeEnum(graded.verdict)}.`);\n }\n await Promise.all([refetch(), refetchSubs()]);\n };\n\n /** Most recent submission that actually carries grading detail. */\n const latestGraded = submissions.find((s) => (s.testResults?.length ?? 0) > 0) ?? null;\n const latestSubmission = submissions[0] ?? null;\n const pendingId = latestSubmission?.verdict === 'PENDING' ? latestSubmission.id : null;\n\n // Poll while the newest submission is still being graded in the background.\n const [pollExpiredFor, setPollExpiredFor] = useState<string | null>(null);\n const polling = pendingId !== null && pollExpiredFor !== pendingId;\n useEffect(() => {\n if (!pendingId || pollExpiredFor === pendingId) return undefined;\n const startedAt = Date.now();\n const timer = window.setInterval(() => {\n if (Date.now() - startedAt > PENDING_POLL_MAX_MS) {\n window.clearInterval(timer);\n setPollExpiredFor(pendingId);\n return;\n }\n void refetchSubs();\n }, PENDING_POLL_INTERVAL_MS);\n return () => window.clearInterval(timer);\n }, [pendingId, pollExpiredFor, refetchSubs]);\n\n // Announce the verdict once a background-graded submission settles.\n const awaitedIdRef = useRef<string | null>(null);\n useEffect(() => {\n if (pendingId) {\n awaitedIdRef.current = pendingId;\n return;\n }\n const awaitedId = awaitedIdRef.current;\n if (!awaitedId) return;\n const settled = submissions.find((s) => s.id === awaitedId);\n if (!settled || settled.verdict === 'PENDING') return;\n awaitedIdRef.current = null;\n if (settled.verdict === 'ACCEPTED') {\n notify.success('Accepted', `Passed all ${settled.totalCount} test cases.`);\n } else {\n notify.error(\n humanizeEnum(settled.verdict),\n `Passed ${settled.passedCount} of ${settled.totalCount} test cases.`\n );\n }\n void refetch();\n }, [pendingId, submissions, refetch]);\n\n if (loading && !problem) {\n return (\n <PageLayout title=\"Problem\" backPath=\"/applications/problems-page\">\n <Skeleton className=\"mb-4 h-28 w-full rounded-xl\" />\n <Skeleton className=\"h-72 w-full rounded-xl\" />\n </PageLayout>\n );\n }\n\n if (error || !problem) {\n return (\n <PageLayout title=\"Problem\" backPath=\"/applications/problems-page\">\n {error ? (\n <ErrorView title=\"Couldn't load problem\" error={error} />\n ) : (\n <EmptyView\n title=\"Problem not found\"\n description=\"This problem may have been unpublished.\"\n />\n )}\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n hero\n backPath=\"/applications/problems-page\"\n title={problem.title}\n description={problem.topic ?? undefined}\n >\n <div className=\"mb-5 flex flex-wrap items-center gap-2\">\n <StatusBadge status={problem.difficulty} kind=\"level\" />\n <StatusBadge status={problem.status} />\n {problem.topic ? (\n <Badge variant=\"outline\" className=\"gap-1\">\n <Layers size={12} /> {problem.topic}\n </Badge>\n ) : null}\n {problem.isPremiumOnly ? <PremiumBadge locked={problem.isLocked} /> : null}\n <TagStrip tags={problem.tags} />\n </div>\n\n <div className=\"mb-6 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard label=\"Points\" value={problem.points} icon={Trophy} tone=\"amber\" />\n <StatCard label=\"Languages\" value={languages.length} icon={Code2} tone=\"blue\" />\n <StatCard\n label=\"Difficulty\"\n value={humanizeEnum(problem.difficulty)}\n icon={Layers}\n tone=\"purple\"\n />\n <StatCard label=\"My submissions\" value={submissions.length} icon={FileText} tone=\"teal\" />\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 lg:grid-cols-3\">\n <div className=\"space-y-5 lg:col-span-2\">\n <GlassCard className=\"p-5\">\n <h2 className=\"mb-2 text-base font-semibold text-text-primary\">Problem statement</h2>\n {problem.isLocked ? (\n // BOFF-4416: the API already nulled `statement` for a locked\n // premium problem — this explains why, rather than showing the\n // generic \"being prepared\" message used for a real content gap.\n <UpgradeGate description=\"This problem's statement is part of a paid TechnoSpam plan. Ask a workspace admin to upgrade to unlock it.\" />\n ) : problem.statement ? (\n <p className=\"whitespace-pre-wrap text-sm leading-relaxed text-text-secondary\">\n {problem.statement}\n </p>\n ) : (\n <p className=\"py-4 text-center text-sm text-text-muted\">\n The statement for this problem is being prepared.\n </p>\n )}\n </GlassCard>\n\n {problem.isLocked ? (\n <UpgradeGate\n compact\n description=\"Starter code and sample test cases unlock with a paid TechnoSpam plan.\"\n />\n ) : problem.starterCode ? (\n <GlassCard className=\"p-5\">\n <h2 className=\"mb-3 text-base font-semibold text-text-primary\">Starter code</h2>\n <CodePanel label={humanizeEnum(languages[0] ?? 'code')} bodyClassName=\"max-h-72\">\n {problem.starterCode}\n </CodePanel>\n </GlassCard>\n ) : null}\n\n <GlassCard className=\"p-5\">\n <div className=\"mb-3 flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n <h2 className=\"text-base font-semibold text-text-primary\">Submit a solution</h2>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-text-secondary\">Language</span>\n <Select value={language} onValueChange={setLanguage}>\n <SelectTrigger className=\"w-full sm:w-48\">\n <SelectValue placeholder=\"Choose a language\" />\n </SelectTrigger>\n <SelectContent>\n {languages.map((l) => (\n <SelectItem key={l} value={l}>\n {humanizeEnum(l)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"overflow-hidden rounded-xl border border-border-strong bg-bg-sunken shadow-sm\">\n <div className=\"flex items-center justify-between gap-2 border-b border-border-seam bg-bg-canvas/40 px-4 py-2\">\n <div className=\"flex items-center gap-1.5\">\n <span className=\"h-2.5 w-2.5 rounded-full bg-status-error-border\" />\n <span className=\"h-2.5 w-2.5 rounded-full bg-status-warning-border\" />\n <span className=\"h-2.5 w-2.5 rounded-full bg-status-success-border\" />\n </div>\n <span className=\"inline-flex items-center gap-1.5 font-mono text-xs text-text-muted\">\n <Code2 size={13} /> {language ? humanizeEnum(language) : 'solution'}\n </span>\n </div>\n <Textarea\n value={code}\n onChange={(e) => setCode(e.target.value)}\n placeholder=\"Write your solution…\"\n className=\"min-h-[280px] resize-y rounded-none border-0 bg-transparent font-mono text-xs leading-relaxed focus-visible:ring-0 focus-visible:ring-offset-0\"\n spellCheck={false}\n />\n </div>\n\n <Button\n className=\"mt-4 w-full\"\n onClick={handleSubmit}\n disabled={submitting || !code.trim()}\n >\n <Send size={16} /> {submitting ? 'Running test cases…' : 'Run & submit'}\n </Button>\n <p className=\"mt-2 text-center text-xs text-text-muted\">\n Your code runs against this problem's test cases in an isolated sandbox.\n </p>\n </GlassCard>\n\n {problem.publicTestCases?.length ? (\n <GlassCard className=\"p-5\">\n <h2 className=\"mb-1 flex items-center gap-2 text-base font-semibold text-text-primary\">\n <FlaskConical size={16} /> Sample test cases\n </h2>\n <p className=\"mb-3 text-sm text-text-secondary\">\n Your solution reads each input from stdin and must print the expected output. Hidden\n test cases are also run when you submit.\n </p>\n <div className=\"space-y-3\">\n {problem.publicTestCases.map((testCase) => (\n <div key={testCase.id} className=\"rounded-xl border border-border-seam p-3\">\n <div className=\"mb-2 flex flex-wrap items-center gap-x-3 gap-y-1\">\n <span className=\"text-sm font-medium text-text-primary\">{testCase.name}</span>\n <span className=\"inline-flex items-center gap-1 text-xs text-text-muted\">\n <Clock size={12} /> {testCase.timeLimitMs} ms limit\n </span>\n </div>\n <div className=\"grid grid-cols-1 gap-2 sm:grid-cols-2\">\n <IoBlock label=\"Input\" value={testCase.input} />\n <IoBlock label=\"Expected output\" value={testCase.expectedOutput} />\n </div>\n </div>\n ))}\n </div>\n </GlassCard>\n ) : null}\n\n {pendingId ? (\n <GlassCard className=\"p-5\">\n <h2 className=\"mb-2 text-base font-semibold text-text-primary\">Latest run</h2>\n {polling ? (\n <p className=\"mb-3 inline-flex items-center gap-2 text-sm text-text-secondary\">\n <RefreshCw size={14} className=\"animate-spin\" />\n Grading your submission — the grader is starting up. Your verdict will appear here\n automatically.\n </p>\n ) : (\n <p className=\"mb-3 text-sm text-text-secondary\">\n Your most recent submission has not been graded yet — the grader was unavailable.\n You can re-run it without resubmitting your code.\n </p>\n )}\n <Button\n variant=\"outline\"\n onClick={() => void handleRegrade(pendingId)}\n disabled={regrading}\n >\n <RefreshCw size={16} /> {regrading ? 'Re-running…' : 'Re-run grader'}\n </Button>\n </GlassCard>\n ) : latestGraded ? (\n <>\n <GlassCard className=\"p-5\">\n <h2 className=\"mb-3 text-base font-semibold text-text-primary\">Latest run</h2>\n <TestResultsPanel submission={latestGraded} />\n </GlassCard>\n <GlassCard className=\"p-5\">\n <h2 className=\"mb-3 flex items-center gap-2 text-base font-semibold text-text-primary\">\n <Sparkles size={16} /> TechnoSage review\n </h2>\n <SubmissionReviewPanel\n submission={latestGraded}\n onOpenProblem={(problemId) =>\n navigate(`/applications/problem-details-page?id=${problemId}`)\n }\n />\n </GlassCard>\n </>\n ) : null}\n </div>\n\n <div className=\"lg:col-span-1\">\n <GlassCard className=\"p-5\">\n <h2 className=\"mb-3 text-base font-semibold text-text-primary\">My submissions</h2>\n {subsLoading && !submissions.length ? (\n <LoadingRows count={3} height=\"h-12\" />\n ) : subsError ? (\n <ErrorView title=\"Couldn't load submissions\" error={subsError} />\n ) : submissions.length === 0 ? (\n <EmptyView\n icon={<FileText size={24} />}\n title=\"No submissions yet\"\n description=\"Submit a solution above to see your verdicts and points here.\"\n />\n ) : (\n <div className=\"space-y-1\">\n {submissions.map((s) => (\n <SubmissionRow\n key={s.id}\n s={s}\n focused={focusedSubmissionId === s.id}\n onToggle={() =>\n setFocusedSubmissionId(focusedSubmissionId === s.id ? null : s.id)\n }\n />\n ))}\n </div>\n )}\n </GlassCard>\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -8,8 +8,8 @@ import { StatusBadge as c } from "../components/StatusBadge.js";
|
|
|
8
8
|
import { LoadMore as l } from "../components/LoadMore.js";
|
|
9
9
|
import { EmptyView as ee, ErrorView as u, LoadingGrid as d } from "../components/StateViews.js";
|
|
10
10
|
import { PremiumBadge as f } from "../components/UpgradeGate.js";
|
|
11
|
-
import "../components/index.js";
|
|
12
11
|
import { pageArg as p, usePageLimit as m, useProblemsQuery as te, useSubmissionsQuery as h } from "../api/technospamApi.js";
|
|
12
|
+
import "../components/index.js";
|
|
13
13
|
import { useMemo as g, useState as _ } from "react";
|
|
14
14
|
import { Building2 as v, CheckCircle2 as y, Code2 as b, Flame as x, Gauge as ne, Layers as S, Search as re, Tag as C, Trophy as w } from "lucide-react";
|
|
15
15
|
import { jsx as T, jsxs as E } from "react/jsx-runtime";
|
|
@@ -8,9 +8,9 @@ import { formatDate as s, formatDuration as c, humanizeEnum as l } from "../util
|
|
|
8
8
|
import { EmptyView as u, ErrorView as ee } from "../components/StateViews.js";
|
|
9
9
|
import { TagStrip as te } from "../components/TagStrip.js";
|
|
10
10
|
import { PremiumBadge as ne, UpgradeGate as d } from "../components/UpgradeGate.js";
|
|
11
|
-
import "../components/index.js";
|
|
12
11
|
import { notify as f } from "../lib/toast.js";
|
|
13
12
|
import { useCourseQuery as re, useEnroll as ie, useIssueCertificate as p, useMyCertificatesQuery as m, useUpdateProgress as ae } from "../api/technospamApi.js";
|
|
13
|
+
import "../components/index.js";
|
|
14
14
|
import { FOCUS_PARAMS as oe } from "../utils/pageContext.js";
|
|
15
15
|
import { useFocusedRecordParam as se } from "../hooks/useFocusedRecordParam.js";
|
|
16
16
|
import { useEffect as h } from "react";
|
|
@@ -9,8 +9,8 @@ import { StatusBadge as l } from "../components/StatusBadge.js";
|
|
|
9
9
|
import { LoadMore as u } from "../components/LoadMore.js";
|
|
10
10
|
import { EmptyView as d, ErrorView as f, LoadingGrid as p } from "../components/StateViews.js";
|
|
11
11
|
import { PremiumBadge as m } from "../components/UpgradeGate.js";
|
|
12
|
-
import "../components/index.js";
|
|
13
12
|
import { pageArg as h, useCoursesQuery as g, usePageLimit as _ } from "../api/technospamApi.js";
|
|
13
|
+
import "../components/index.js";
|
|
14
14
|
import { useMemo as v, useState as y } from "react";
|
|
15
15
|
import { BookOpen as b, Clock as x, GraduationCap as S, Layers as C, Search as w, Sparkles as T } from "lucide-react";
|
|
16
16
|
import { jsx as E, jsxs as D } from "react/jsx-runtime";
|
|
@@ -6,8 +6,8 @@ import { coverGradient as a, topicIcon as o } from "../components/MediaCover.js"
|
|
|
6
6
|
import { formatDate as s, relativeTime as c } from "../utils/format.js";
|
|
7
7
|
import { StatusBadge as l } from "../components/StatusBadge.js";
|
|
8
8
|
import { EmptyView as u, ErrorView as d, LoadingRows as f } from "../components/StateViews.js";
|
|
9
|
-
import "../components/index.js";
|
|
10
9
|
import { useEnrollmentsQuery as p, useMyCertificatesQuery as m } from "../api/technospamApi.js";
|
|
10
|
+
import "../components/index.js";
|
|
11
11
|
import { FOCUS_PARAMS as h } from "../utils/pageContext.js";
|
|
12
12
|
import { focusableCardProps as g, useFocusedRecordParam as _ } from "../hooks/useFocusedRecordParam.js";
|
|
13
13
|
import { createElement as v } from "react";
|