@burdenoff/microfe-technospam 2026.1102.3 → 2026.1102.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/generated/global-types.d.ts +307 -41
- package/dist/generated/global-types.d.ts.map +1 -1
- package/dist/generated/wspace-types.d.ts +2025 -11
- package/dist/generated/wspace-types.d.ts.map +1 -1
- package/dist/stories/fixtures/technospamFixtures.d.ts.map +1 -1
- package/dist/technospam/TechnoSpamRoutes.d.ts.map +1 -1
- package/dist/technospam/TechnoSpamRoutes.js +32 -28
- package/dist/technospam/TechnoSpamRoutes.js.map +1 -1
- package/dist/technospam/api/assessmentApi.d.ts +183 -0
- package/dist/technospam/api/assessmentApi.d.ts.map +1 -0
- package/dist/technospam/api/assessmentApi.js +278 -0
- package/dist/technospam/api/assessmentApi.js.map +1 -0
- package/dist/technospam/api/technospamApi.d.ts +62 -0
- package/dist/technospam/api/technospamApi.d.ts.map +1 -1
- package/dist/technospam/api/technospamApi.js +429 -297
- package/dist/technospam/api/technospamApi.js.map +1 -1
- package/dist/technospam/components/CourseAssessments.d.ts +9 -0
- package/dist/technospam/components/CourseAssessments.d.ts.map +1 -0
- package/dist/technospam/components/CourseAssessments.js +66 -0
- package/dist/technospam/components/CourseAssessments.js.map +1 -0
- package/dist/technospam/components/StatusBadge.d.ts.map +1 -1
- package/dist/technospam/components/StatusBadge.js +12 -1
- package/dist/technospam/components/StatusBadge.js.map +1 -1
- package/dist/technospam/components/admin/AssessmentsAdminTab.d.ts +7 -0
- package/dist/technospam/components/admin/AssessmentsAdminTab.d.ts.map +1 -0
- package/dist/technospam/components/admin/AssessmentsAdminTab.js +421 -0
- package/dist/technospam/components/admin/AssessmentsAdminTab.js.map +1 -0
- package/dist/technospam/components/admin/EnterpriseIntegrationsTab.d.ts +2 -1
- package/dist/technospam/components/admin/EnterpriseIntegrationsTab.d.ts.map +1 -1
- package/dist/technospam/components/admin/EnterpriseIntegrationsTab.js +325 -95
- package/dist/technospam/components/admin/EnterpriseIntegrationsTab.js.map +1 -1
- package/dist/technospam/components/lesson/LessonKindFields.d.ts +3 -1
- package/dist/technospam/components/lesson/LessonKindFields.d.ts.map +1 -1
- package/dist/technospam/components/lesson/LessonKindFields.js +2 -2
- package/dist/technospam/components/lesson/LessonKindFields.js.map +1 -1
- package/dist/technospam/hooks/useAssessmentIntegrity.d.ts +29 -0
- package/dist/technospam/hooks/useAssessmentIntegrity.d.ts.map +1 -0
- package/dist/technospam/hooks/useAssessmentIntegrity.js +57 -0
- package/dist/technospam/hooks/useAssessmentIntegrity.js.map +1 -0
- package/dist/technospam/lib/enterprise.d.ts +20 -0
- package/dist/technospam/lib/enterprise.d.ts.map +1 -1
- package/dist/technospam/lib/enterprise.js +33 -1
- package/dist/technospam/lib/enterprise.js.map +1 -1
- package/dist/technospam/pages/AdminAdminDashboardPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/AdminAdminDashboardPagePage.js +364 -359
- package/dist/technospam/pages/AdminAdminDashboardPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesAssessmentPagePage.d.ts +9 -0
- package/dist/technospam/pages/CoursesAssessmentPagePage.d.ts.map +1 -0
- package/dist/technospam/pages/CoursesAssessmentPagePage.js +464 -0
- package/dist/technospam/pages/CoursesAssessmentPagePage.js.map +1 -0
- package/dist/technospam/pages/CoursesCourseDetailsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/CoursesCourseDetailsPagePage.js +210 -208
- package/dist/technospam/pages/CoursesCourseDetailsPagePage.js.map +1 -1
- package/dist/technospam/utils/assessment.d.ts +64 -0
- package/dist/technospam/utils/assessment.d.ts.map +1 -0
- package/dist/technospam/utils/assessment.js +122 -0
- package/dist/technospam/utils/assessment.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
import { formatDateTime as e, humanizeEnum as t } from "../../utils/format.js";
|
|
2
|
+
import { StatusBadge as n } from "../StatusBadge.js";
|
|
3
|
+
import { EmptyView as r, ErrorView as i, LoadingRows as a } from "../StateViews.js";
|
|
4
|
+
import { notify as o } from "../../lib/toast.js";
|
|
5
|
+
import { useCoursesQuery as s } from "../../api/technospamApi.js";
|
|
6
|
+
import { draftFromLesson as c, draftToInput as l } from "../lesson/lessonAuthoring.js";
|
|
7
|
+
import { LessonKindFields as u } from "../lesson/LessonKindFields.js";
|
|
8
|
+
import { useArchiveAssessment as d, useAssessmentAttemptEventsQuery as f, useAssessmentAttemptsQuery as p, useAssessmentQuestionKeyQuery as m, useAssessmentsQuery as h, useCreateAssessment as g, usePublishAssessment as _, useReviewAssessmentAttempt as v, useUpdateAssessment as y } from "../../api/assessmentApi.js";
|
|
9
|
+
import { assessmentSettingsToInput as b, draftFromAssessment as x, emptyAssessmentDraft as S } from "../../utils/assessment.js";
|
|
10
|
+
import { useState as C } from "react";
|
|
11
|
+
import { Archive as w, Check as T, ListChecks as E, Pencil as D, Plus as O, Rocket as k, ShieldCheck as A, X as j } from "lucide-react";
|
|
12
|
+
import { Fragment as M, jsx as N, jsxs as P } from "react/jsx-runtime";
|
|
13
|
+
import { Button as F, GlassCard as I, Input as L, Select as R, SelectContent as z, SelectItem as B, SelectTrigger as V, SelectValue as H, Textarea as U } from "@burdenoff/fe-libs/ui";
|
|
14
|
+
//#region src/technospam/components/admin/AssessmentsAdminTab.tsx
|
|
15
|
+
var W = [
|
|
16
|
+
"INTEGRITY_MONITORING",
|
|
17
|
+
"REVIEW_REQUIRED",
|
|
18
|
+
"NONE"
|
|
19
|
+
], G = [
|
|
20
|
+
"FLAGGED",
|
|
21
|
+
"PENDING_REVIEW",
|
|
22
|
+
"ALL"
|
|
23
|
+
];
|
|
24
|
+
function K({ label: e, children: t }) {
|
|
25
|
+
return /* @__PURE__ */ P("label", {
|
|
26
|
+
className: "flex flex-col gap-1.5",
|
|
27
|
+
children: [/* @__PURE__ */ N("span", {
|
|
28
|
+
className: "text-xs font-medium text-text-secondary",
|
|
29
|
+
children: e
|
|
30
|
+
}), t]
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function q({ label: e, checked: t, onChange: n }) {
|
|
34
|
+
return /* @__PURE__ */ P("label", {
|
|
35
|
+
className: "flex items-center gap-2 text-sm text-text-primary",
|
|
36
|
+
children: [/* @__PURE__ */ N("input", {
|
|
37
|
+
type: "checkbox",
|
|
38
|
+
checked: t,
|
|
39
|
+
onChange: (e) => n(e.target.checked)
|
|
40
|
+
}), e]
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function J({ courseId: e, assessment: n, onDone: r }) {
|
|
44
|
+
let [i, s] = C(() => n ? x(n) : S()), { data: d, loading: f } = m(n?.id ?? null), [p, h] = C(n ? null : c()), [_, { loading: v }] = g(), [w, { loading: E }] = y(), D = p ?? (d ? c(null, d.tecAssessmentQuestionKey) : null), O = (e, t) => s((n) => ({
|
|
45
|
+
...n,
|
|
46
|
+
[e]: t
|
|
47
|
+
})), k = async () => {
|
|
48
|
+
let t = b(i);
|
|
49
|
+
if (!t.ok) return o.error(t.error);
|
|
50
|
+
let a = {};
|
|
51
|
+
if (D) {
|
|
52
|
+
let e = l("QUIZ", D);
|
|
53
|
+
if (!e.ok) return o.error(e.error);
|
|
54
|
+
a = { questions: e.input.quizQuestions };
|
|
55
|
+
}
|
|
56
|
+
if (n) {
|
|
57
|
+
(await w({ variables: {
|
|
58
|
+
id: n.id,
|
|
59
|
+
input: {
|
|
60
|
+
...t.input,
|
|
61
|
+
...a
|
|
62
|
+
}
|
|
63
|
+
} })).data?.tecUpdateAssessment && (o.success("Assessment saved"), r());
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
(await _({ variables: { input: {
|
|
67
|
+
courseId: e,
|
|
68
|
+
...t.input,
|
|
69
|
+
...a
|
|
70
|
+
} } })).data?.tecCreateAssessment && (o.success("Assessment created", "Publish it when the questions are ready."), r());
|
|
71
|
+
};
|
|
72
|
+
return /* @__PURE__ */ P(I, {
|
|
73
|
+
className: "space-y-4 p-4 sm:p-5",
|
|
74
|
+
"data-testid": "assessment-form",
|
|
75
|
+
children: [
|
|
76
|
+
/* @__PURE__ */ P("div", {
|
|
77
|
+
className: "grid grid-cols-1 gap-3 sm:grid-cols-2",
|
|
78
|
+
children: [
|
|
79
|
+
/* @__PURE__ */ N(K, {
|
|
80
|
+
label: "Title",
|
|
81
|
+
children: /* @__PURE__ */ N(L, {
|
|
82
|
+
value: i.title,
|
|
83
|
+
onChange: (e) => O("title", e.target.value)
|
|
84
|
+
})
|
|
85
|
+
}),
|
|
86
|
+
/* @__PURE__ */ N(K, {
|
|
87
|
+
label: "Integrity mode",
|
|
88
|
+
children: /* @__PURE__ */ P(R, {
|
|
89
|
+
value: i.proctoringMode,
|
|
90
|
+
onValueChange: (e) => O("proctoringMode", e),
|
|
91
|
+
children: [/* @__PURE__ */ N(V, {
|
|
92
|
+
"aria-label": "Integrity mode",
|
|
93
|
+
children: /* @__PURE__ */ N(H, {})
|
|
94
|
+
}), /* @__PURE__ */ N(z, { children: W.map((e) => /* @__PURE__ */ N(B, {
|
|
95
|
+
value: e,
|
|
96
|
+
children: t(e)
|
|
97
|
+
}, e)) })]
|
|
98
|
+
})
|
|
99
|
+
}),
|
|
100
|
+
/* @__PURE__ */ N(K, {
|
|
101
|
+
label: "Time limit (minutes)",
|
|
102
|
+
children: /* @__PURE__ */ N(L, {
|
|
103
|
+
inputMode: "numeric",
|
|
104
|
+
value: i.timeLimitMinutes,
|
|
105
|
+
onChange: (e) => O("timeLimitMinutes", e.target.value)
|
|
106
|
+
})
|
|
107
|
+
}),
|
|
108
|
+
/* @__PURE__ */ N(K, {
|
|
109
|
+
label: "Pass mark (%)",
|
|
110
|
+
children: /* @__PURE__ */ N(L, {
|
|
111
|
+
inputMode: "numeric",
|
|
112
|
+
value: i.passingScore,
|
|
113
|
+
onChange: (e) => O("passingScore", e.target.value)
|
|
114
|
+
})
|
|
115
|
+
}),
|
|
116
|
+
/* @__PURE__ */ N(K, {
|
|
117
|
+
label: "Attempts per learner",
|
|
118
|
+
children: /* @__PURE__ */ N(L, {
|
|
119
|
+
inputMode: "numeric",
|
|
120
|
+
value: i.maxAttempts,
|
|
121
|
+
onChange: (e) => O("maxAttempts", e.target.value)
|
|
122
|
+
})
|
|
123
|
+
}),
|
|
124
|
+
/* @__PURE__ */ N(K, {
|
|
125
|
+
label: "Allowed integrity events",
|
|
126
|
+
children: /* @__PURE__ */ N(L, {
|
|
127
|
+
inputMode: "numeric",
|
|
128
|
+
value: i.maxIntegrityViolations,
|
|
129
|
+
onChange: (e) => O("maxIntegrityViolations", e.target.value)
|
|
130
|
+
})
|
|
131
|
+
}),
|
|
132
|
+
/* @__PURE__ */ N("div", {
|
|
133
|
+
className: "sm:col-span-2",
|
|
134
|
+
children: /* @__PURE__ */ N(K, {
|
|
135
|
+
label: "Description",
|
|
136
|
+
children: /* @__PURE__ */ N(U, {
|
|
137
|
+
value: i.description,
|
|
138
|
+
onChange: (e) => O("description", e.target.value)
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
}),
|
|
142
|
+
/* @__PURE__ */ P("div", {
|
|
143
|
+
className: "flex flex-wrap gap-x-5 gap-y-2 sm:col-span-2",
|
|
144
|
+
children: [
|
|
145
|
+
/* @__PURE__ */ N(q, {
|
|
146
|
+
label: "Require fullscreen",
|
|
147
|
+
checked: i.requireFullscreen,
|
|
148
|
+
onChange: (e) => O("requireFullscreen", e)
|
|
149
|
+
}),
|
|
150
|
+
/* @__PURE__ */ N(q, {
|
|
151
|
+
label: "Block copy and paste",
|
|
152
|
+
checked: i.blockClipboard,
|
|
153
|
+
onChange: (e) => O("blockClipboard", e)
|
|
154
|
+
}),
|
|
155
|
+
/* @__PURE__ */ N(q, {
|
|
156
|
+
label: "Shuffle questions",
|
|
157
|
+
checked: i.shuffleQuestions,
|
|
158
|
+
onChange: (e) => O("shuffleQuestions", e)
|
|
159
|
+
}),
|
|
160
|
+
/* @__PURE__ */ N(q, {
|
|
161
|
+
label: "Shuffle options",
|
|
162
|
+
checked: i.shuffleOptions,
|
|
163
|
+
onChange: (e) => O("shuffleOptions", e)
|
|
164
|
+
}),
|
|
165
|
+
/* @__PURE__ */ N(q, {
|
|
166
|
+
label: "Issue course certificate",
|
|
167
|
+
checked: i.issuesCertificate,
|
|
168
|
+
onChange: (e) => O("issuesCertificate", e)
|
|
169
|
+
})
|
|
170
|
+
]
|
|
171
|
+
})
|
|
172
|
+
]
|
|
173
|
+
}),
|
|
174
|
+
D ? /* @__PURE__ */ N(u, {
|
|
175
|
+
kind: "QUIZ",
|
|
176
|
+
draft: D,
|
|
177
|
+
onChange: h,
|
|
178
|
+
heading: `Questions (learners need ${i.passingScore || "?"}% to pass)`
|
|
179
|
+
}) : f ? /* @__PURE__ */ N(a, { count: 2 }) : null,
|
|
180
|
+
/* @__PURE__ */ P("div", {
|
|
181
|
+
className: "flex justify-end gap-2 border-t border-border-seam pt-3",
|
|
182
|
+
children: [/* @__PURE__ */ P(F, {
|
|
183
|
+
variant: "outline",
|
|
184
|
+
onClick: r,
|
|
185
|
+
children: [/* @__PURE__ */ N(j, { size: 15 }), " Cancel"]
|
|
186
|
+
}), /* @__PURE__ */ P(F, {
|
|
187
|
+
onClick: () => void k(),
|
|
188
|
+
disabled: v || E,
|
|
189
|
+
"data-testid": "assessment-save",
|
|
190
|
+
children: [
|
|
191
|
+
/* @__PURE__ */ N(T, { size: 15 }),
|
|
192
|
+
" ",
|
|
193
|
+
v || E ? "Saving…" : "Save assessment"
|
|
194
|
+
]
|
|
195
|
+
})]
|
|
196
|
+
})
|
|
197
|
+
]
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
function Y({ attempt: r, title: i }) {
|
|
201
|
+
let [a, s] = C(!1), [c, l] = C(""), { data: u } = f(a ? r.id : null), [d, { loading: p }] = v(), m = r.proctoringStatus === "FLAGGED" || r.proctoringStatus === "PENDING_REVIEW", h = async (e) => {
|
|
202
|
+
let t = (await d({ variables: {
|
|
203
|
+
id: r.id,
|
|
204
|
+
decision: e,
|
|
205
|
+
notes: c.trim() || null
|
|
206
|
+
} })).data?.tecReviewAssessmentAttempt;
|
|
207
|
+
t && o.success(e === "VERIFIED" ? "Attempt verified" : "Attempt rejected", t.certificateId ? "The certificate was issued." : void 0);
|
|
208
|
+
};
|
|
209
|
+
return /* @__PURE__ */ P(I, {
|
|
210
|
+
className: "space-y-3 p-4",
|
|
211
|
+
"data-testid": "assessment-review-row",
|
|
212
|
+
children: [/* @__PURE__ */ P("div", {
|
|
213
|
+
className: "flex flex-wrap items-center justify-between gap-3",
|
|
214
|
+
children: [/* @__PURE__ */ P("div", {
|
|
215
|
+
className: "min-w-0",
|
|
216
|
+
children: [/* @__PURE__ */ N("div", {
|
|
217
|
+
className: "truncate text-sm font-semibold text-text-primary",
|
|
218
|
+
children: i
|
|
219
|
+
}), /* @__PURE__ */ P("div", {
|
|
220
|
+
className: "text-xs text-text-muted",
|
|
221
|
+
children: [
|
|
222
|
+
"Learner ",
|
|
223
|
+
r.learnerId,
|
|
224
|
+
" · attempt ",
|
|
225
|
+
r.attemptNumber,
|
|
226
|
+
" · ",
|
|
227
|
+
r.score ?? 0,
|
|
228
|
+
"% (",
|
|
229
|
+
r.passed ? "passed" : "not passed",
|
|
230
|
+
") · ",
|
|
231
|
+
r.integrityViolations,
|
|
232
|
+
" integrity events · ",
|
|
233
|
+
e(r.submittedAt ?? r.startedAt)
|
|
234
|
+
]
|
|
235
|
+
})]
|
|
236
|
+
}), /* @__PURE__ */ P("div", {
|
|
237
|
+
className: "flex items-center gap-2",
|
|
238
|
+
children: [/* @__PURE__ */ N(n, { status: r.proctoringStatus }), /* @__PURE__ */ N(F, {
|
|
239
|
+
size: "sm",
|
|
240
|
+
variant: "outline",
|
|
241
|
+
onClick: () => s((e) => !e),
|
|
242
|
+
children: a ? "Hide" : "Review"
|
|
243
|
+
})]
|
|
244
|
+
})]
|
|
245
|
+
}), a ? /* @__PURE__ */ P("div", {
|
|
246
|
+
className: "space-y-3",
|
|
247
|
+
children: [/* @__PURE__ */ N("ol", {
|
|
248
|
+
className: "max-h-56 space-y-1 overflow-auto rounded-lg bg-bg-sunken p-3 text-xs",
|
|
249
|
+
"data-testid": "assessment-events",
|
|
250
|
+
children: (u?.tecAssessmentAttemptEvents ?? []).map((n) => /* @__PURE__ */ P("li", {
|
|
251
|
+
className: n.violation ? "text-status-warning-text" : "text-text-secondary",
|
|
252
|
+
children: [
|
|
253
|
+
e(n.occurredAt),
|
|
254
|
+
" · ",
|
|
255
|
+
t(n.type),
|
|
256
|
+
n.violation ? " (violation)" : ""
|
|
257
|
+
]
|
|
258
|
+
}, n.id))
|
|
259
|
+
}), m ? /* @__PURE__ */ P(M, { children: [/* @__PURE__ */ N(U, {
|
|
260
|
+
"aria-label": "Review notes",
|
|
261
|
+
placeholder: "Notes for the learner (optional)",
|
|
262
|
+
value: c,
|
|
263
|
+
onChange: (e) => l(e.target.value)
|
|
264
|
+
}), /* @__PURE__ */ P("div", {
|
|
265
|
+
className: "flex gap-2",
|
|
266
|
+
children: [/* @__PURE__ */ P(F, {
|
|
267
|
+
size: "sm",
|
|
268
|
+
disabled: p,
|
|
269
|
+
onClick: () => void h("VERIFIED"),
|
|
270
|
+
children: [/* @__PURE__ */ N(T, { size: 14 }), " Verify"]
|
|
271
|
+
}), /* @__PURE__ */ P(F, {
|
|
272
|
+
size: "sm",
|
|
273
|
+
variant: "outline",
|
|
274
|
+
disabled: p,
|
|
275
|
+
onClick: () => void h("REJECTED"),
|
|
276
|
+
children: [/* @__PURE__ */ N(j, { size: 14 }), " Reject"]
|
|
277
|
+
})]
|
|
278
|
+
})] }) : r.reviewNotes ? /* @__PURE__ */ P("p", {
|
|
279
|
+
className: "text-sm text-text-secondary",
|
|
280
|
+
children: ["Reviewer note: ", r.reviewNotes]
|
|
281
|
+
}) : null]
|
|
282
|
+
}) : null]
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
function X() {
|
|
286
|
+
let { data: e } = s({}), c = e?.tecListCourses?.items ?? [], [l, u] = C(""), f = l || c[0]?.id || "", { data: m, loading: g, error: v } = h({ courseId: f }, !f), [y, b] = C(null), [x] = _(), [S] = d(), [T, j] = C("FLAGGED"), M = p({ proctoringStatus: T === "ALL" ? null : T }), L = m?.tecListAssessments ?? [], U = new Map(L.map((e) => [e.id, e.title]));
|
|
287
|
+
return /* @__PURE__ */ P("div", {
|
|
288
|
+
className: "space-y-6",
|
|
289
|
+
children: [/* @__PURE__ */ P("section", {
|
|
290
|
+
className: "space-y-3",
|
|
291
|
+
children: [
|
|
292
|
+
/* @__PURE__ */ P("div", {
|
|
293
|
+
className: "flex flex-wrap items-end justify-between gap-3",
|
|
294
|
+
children: [/* @__PURE__ */ N(K, {
|
|
295
|
+
label: "Course",
|
|
296
|
+
children: /* @__PURE__ */ P(R, {
|
|
297
|
+
value: f,
|
|
298
|
+
onValueChange: (e) => {
|
|
299
|
+
u(e), b(null);
|
|
300
|
+
},
|
|
301
|
+
children: [/* @__PURE__ */ N(V, {
|
|
302
|
+
"aria-label": "Course",
|
|
303
|
+
className: "min-w-64",
|
|
304
|
+
children: /* @__PURE__ */ N(H, { placeholder: "Choose a course" })
|
|
305
|
+
}), /* @__PURE__ */ N(z, { children: c.map((e) => /* @__PURE__ */ N(B, {
|
|
306
|
+
value: e.id,
|
|
307
|
+
children: e.title
|
|
308
|
+
}, e.id)) })]
|
|
309
|
+
})
|
|
310
|
+
}), f && y === null ? /* @__PURE__ */ P(F, {
|
|
311
|
+
size: "sm",
|
|
312
|
+
onClick: () => b("new"),
|
|
313
|
+
"data-testid": "assessment-new",
|
|
314
|
+
children: [/* @__PURE__ */ N(O, { size: 15 }), " New assessment"]
|
|
315
|
+
}) : null]
|
|
316
|
+
}),
|
|
317
|
+
y !== null && f ? /* @__PURE__ */ N(J, {
|
|
318
|
+
courseId: f,
|
|
319
|
+
assessment: y === "new" ? null : y,
|
|
320
|
+
onDone: () => b(null)
|
|
321
|
+
}, y === "new" ? "new" : y.id) : null,
|
|
322
|
+
f ? v ? /* @__PURE__ */ N(i, {
|
|
323
|
+
title: "Could not load assessments",
|
|
324
|
+
error: v
|
|
325
|
+
}) : g && L.length === 0 ? /* @__PURE__ */ N(a, { count: 2 }) : L.length === 0 ? /* @__PURE__ */ N(r, {
|
|
326
|
+
icon: /* @__PURE__ */ N(A, { size: 26 }),
|
|
327
|
+
title: "No assessments for this course",
|
|
328
|
+
description: "Add a timed, integrity-monitored exam that issues the course certificate."
|
|
329
|
+
}) : /* @__PURE__ */ N("div", {
|
|
330
|
+
className: "space-y-2",
|
|
331
|
+
children: L.map((e) => /* @__PURE__ */ P(I, {
|
|
332
|
+
className: "flex flex-wrap items-center justify-between gap-3 p-4",
|
|
333
|
+
children: [/* @__PURE__ */ P("div", {
|
|
334
|
+
className: "min-w-0",
|
|
335
|
+
children: [/* @__PURE__ */ N("div", {
|
|
336
|
+
className: "truncate text-sm font-semibold text-text-primary",
|
|
337
|
+
children: e.title
|
|
338
|
+
}), /* @__PURE__ */ P("div", {
|
|
339
|
+
className: "text-xs text-text-muted",
|
|
340
|
+
children: [
|
|
341
|
+
e.questionCount,
|
|
342
|
+
" questions · ",
|
|
343
|
+
e.timeLimitMinutes,
|
|
344
|
+
" min · pass",
|
|
345
|
+
" ",
|
|
346
|
+
e.passingScore,
|
|
347
|
+
"% · ",
|
|
348
|
+
t(e.proctoringMode)
|
|
349
|
+
]
|
|
350
|
+
})]
|
|
351
|
+
}), /* @__PURE__ */ P("div", {
|
|
352
|
+
className: "flex flex-wrap items-center gap-2",
|
|
353
|
+
children: [
|
|
354
|
+
/* @__PURE__ */ N(n, { status: e.status }),
|
|
355
|
+
/* @__PURE__ */ P(F, {
|
|
356
|
+
size: "sm",
|
|
357
|
+
variant: "outline",
|
|
358
|
+
onClick: () => b(e),
|
|
359
|
+
children: [/* @__PURE__ */ N(D, { size: 14 }), " Edit"]
|
|
360
|
+
}),
|
|
361
|
+
e.status === "PUBLISHED" ? /* @__PURE__ */ P(F, {
|
|
362
|
+
size: "sm",
|
|
363
|
+
variant: "outline",
|
|
364
|
+
onClick: () => void S({ variables: { id: e.id } }).then((e) => {
|
|
365
|
+
e.data?.tecArchiveAssessment && o.success("Assessment archived");
|
|
366
|
+
}),
|
|
367
|
+
children: [/* @__PURE__ */ N(w, { size: 14 }), " Archive"]
|
|
368
|
+
}) : /* @__PURE__ */ P(F, {
|
|
369
|
+
size: "sm",
|
|
370
|
+
onClick: () => void x({ variables: { id: e.id } }).then((e) => {
|
|
371
|
+
e.data?.tecPublishAssessment && o.success("Assessment published");
|
|
372
|
+
}),
|
|
373
|
+
children: [/* @__PURE__ */ N(k, { size: 14 }), " Publish"]
|
|
374
|
+
})
|
|
375
|
+
]
|
|
376
|
+
})]
|
|
377
|
+
}, e.id))
|
|
378
|
+
}) : /* @__PURE__ */ N(r, {
|
|
379
|
+
title: "No courses yet",
|
|
380
|
+
description: "Create a course first, then add its assessments."
|
|
381
|
+
})
|
|
382
|
+
]
|
|
383
|
+
}), /* @__PURE__ */ P("section", {
|
|
384
|
+
className: "space-y-3",
|
|
385
|
+
children: [/* @__PURE__ */ P("div", {
|
|
386
|
+
className: "flex flex-wrap items-end justify-between gap-3",
|
|
387
|
+
children: [/* @__PURE__ */ P("h3", {
|
|
388
|
+
className: "flex items-center gap-2 text-sm font-semibold text-text-primary",
|
|
389
|
+
children: [/* @__PURE__ */ N(E, { size: 16 }), " Review queue"]
|
|
390
|
+
}), /* @__PURE__ */ P(R, {
|
|
391
|
+
value: T,
|
|
392
|
+
onValueChange: (e) => j(e),
|
|
393
|
+
children: [/* @__PURE__ */ N(V, {
|
|
394
|
+
"aria-label": "Review filter",
|
|
395
|
+
className: "min-w-48",
|
|
396
|
+
children: /* @__PURE__ */ N(H, {})
|
|
397
|
+
}), /* @__PURE__ */ N(z, { children: G.map((e) => /* @__PURE__ */ N(B, {
|
|
398
|
+
value: e,
|
|
399
|
+
children: e === "ALL" ? "All attempts" : t(e)
|
|
400
|
+
}, e)) })]
|
|
401
|
+
})]
|
|
402
|
+
}), M.error ? /* @__PURE__ */ N(i, {
|
|
403
|
+
title: "Could not load attempts",
|
|
404
|
+
error: M.error
|
|
405
|
+
}) : (M.data?.tecListAssessmentAttempts ?? []).length === 0 ? /* @__PURE__ */ N(r, {
|
|
406
|
+
title: "Nothing to review",
|
|
407
|
+
description: "Flagged and review-required attempts appear here."
|
|
408
|
+
}) : /* @__PURE__ */ N("div", {
|
|
409
|
+
className: "space-y-2",
|
|
410
|
+
children: (M.data?.tecListAssessmentAttempts ?? []).map((e) => /* @__PURE__ */ N(Y, {
|
|
411
|
+
attempt: e,
|
|
412
|
+
title: U.get(e.assessmentId) ?? "Assessment attempt"
|
|
413
|
+
}, e.id))
|
|
414
|
+
})]
|
|
415
|
+
})]
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
//#endregion
|
|
419
|
+
export { X as AssessmentsAdminTab };
|
|
420
|
+
|
|
421
|
+
//# sourceMappingURL=AssessmentsAdminTab.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssessmentsAdminTab.js","names":[],"sources":["../../../../src/technospam/components/admin/AssessmentsAdminTab.tsx"],"sourcesContent":["import { useState, type ReactNode } from 'react';\nimport { Archive, Check, ListChecks, Pencil, Plus, Rocket, ShieldCheck, X } from 'lucide-react';\nimport {\n Button,\n GlassCard,\n Input,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport { useCoursesQuery } from '../../api/technospamApi';\nimport {\n useArchiveAssessment,\n useAssessmentAttemptEventsQuery,\n useAssessmentAttemptsQuery,\n useAssessmentQuestionKeyQuery,\n useAssessmentsQuery,\n useCreateAssessment,\n usePublishAssessment,\n useReviewAssessmentAttempt,\n useUpdateAssessment,\n type TecAssessment,\n type TecAssessmentAttempt,\n type TecProctoringMode,\n type TecProctoringStatus,\n} from '../../api/assessmentApi';\nimport { LessonKindFields } from '../lesson/LessonKindFields';\nimport { draftFromLesson, draftToInput, type LessonKindDraft } from '../lesson/lessonAuthoring';\nimport { EmptyView, ErrorView, LoadingRows } from '../StateViews';\nimport { StatusBadge } from '../StatusBadge';\nimport { notify } from '../../lib/toast';\nimport { formatDateTime, humanizeEnum } from '../../utils/format';\nimport {\n assessmentSettingsToInput,\n draftFromAssessment,\n emptyAssessmentDraft,\n type AssessmentSettingsDraft,\n} from '../../utils/assessment';\n\nconst MODES: TecProctoringMode[] = ['INTEGRITY_MONITORING', 'REVIEW_REQUIRED', 'NONE'];\nconst REVIEW_FILTERS: (TecProctoringStatus | 'ALL')[] = ['FLAGGED', 'PENDING_REVIEW', 'ALL'];\n\nfunction Field({ label, children }: { label: string; children: ReactNode }) {\n return (\n <label className=\"flex flex-col gap-1.5\">\n <span className=\"text-xs font-medium text-text-secondary\">{label}</span>\n {children}\n </label>\n );\n}\n\nfunction Toggle({\n label,\n checked,\n onChange,\n}: {\n label: string;\n checked: boolean;\n onChange: (value: boolean) => void;\n}) {\n return (\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input type=\"checkbox\" checked={checked} onChange={(e) => onChange(e.target.checked)} />\n {label}\n </label>\n );\n}\n\n/**\n * Create / edit form for one assessment: settings plus the question set with\n * its answer key (reusing the quiz-lesson editor).\n */\nfunction AssessmentForm({\n courseId,\n assessment,\n onDone,\n}: {\n courseId: string;\n assessment: TecAssessment | null;\n onDone: () => void;\n}) {\n const [settings, setSettings] = useState<AssessmentSettingsDraft>(() =>\n assessment ? draftFromAssessment(assessment) : emptyAssessmentDraft()\n );\n const { data: keyData, loading: keyLoading } = useAssessmentQuestionKeyQuery(\n assessment?.id ?? null\n );\n const [questions, setQuestions] = useState<LessonKindDraft | null>(\n assessment ? null : draftFromLesson()\n );\n const [create, { loading: creating }] = useCreateAssessment();\n const [update, { loading: updating }] = useUpdateAssessment();\n const quiz =\n questions ?? (keyData ? draftFromLesson(null, keyData.tecAssessmentQuestionKey) : null);\n const set = <K extends keyof AssessmentSettingsDraft>(\n key: K,\n value: AssessmentSettingsDraft[K]\n ): void => setSettings((prev) => ({ ...prev, [key]: value }));\n\n const save = async (): Promise<void> => {\n const parsed = assessmentSettingsToInput(settings);\n if (!parsed.ok) return notify.error(parsed.error);\n let questionInput: Record<string, unknown> = {};\n if (quiz) {\n const q = draftToInput('QUIZ', quiz);\n if (!q.ok) return notify.error(q.error);\n questionInput = { questions: q.input.quizQuestions };\n }\n if (assessment) {\n const res = await update({\n variables: { id: assessment.id, input: { ...parsed.input, ...questionInput } },\n });\n if (res.data?.tecUpdateAssessment) {\n notify.success('Assessment saved');\n onDone();\n }\n return;\n }\n const res = await create({\n variables: { input: { courseId, ...parsed.input, ...questionInput } },\n });\n if (res.data?.tecCreateAssessment) {\n notify.success('Assessment created', 'Publish it when the questions are ready.');\n onDone();\n }\n };\n\n return (\n <GlassCard className=\"space-y-4 p-4 sm:p-5\" data-testid=\"assessment-form\">\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <Field label=\"Title\">\n <Input value={settings.title} onChange={(e) => set('title', e.target.value)} />\n </Field>\n <Field label=\"Integrity mode\">\n <Select\n value={settings.proctoringMode}\n onValueChange={(v) => set('proctoringMode', v as TecProctoringMode)}\n >\n <SelectTrigger aria-label=\"Integrity mode\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {MODES.map((mode) => (\n <SelectItem key={mode} value={mode}>\n {humanizeEnum(mode)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </Field>\n <Field label=\"Time limit (minutes)\">\n <Input\n inputMode=\"numeric\"\n value={settings.timeLimitMinutes}\n onChange={(e) => set('timeLimitMinutes', e.target.value)}\n />\n </Field>\n <Field label=\"Pass mark (%)\">\n <Input\n inputMode=\"numeric\"\n value={settings.passingScore}\n onChange={(e) => set('passingScore', e.target.value)}\n />\n </Field>\n <Field label=\"Attempts per learner\">\n <Input\n inputMode=\"numeric\"\n value={settings.maxAttempts}\n onChange={(e) => set('maxAttempts', e.target.value)}\n />\n </Field>\n <Field label=\"Allowed integrity events\">\n <Input\n inputMode=\"numeric\"\n value={settings.maxIntegrityViolations}\n onChange={(e) => set('maxIntegrityViolations', e.target.value)}\n />\n </Field>\n <div className=\"sm:col-span-2\">\n <Field label=\"Description\">\n <Textarea\n value={settings.description}\n onChange={(e) => set('description', e.target.value)}\n />\n </Field>\n </div>\n <div className=\"flex flex-wrap gap-x-5 gap-y-2 sm:col-span-2\">\n <Toggle\n label=\"Require fullscreen\"\n checked={settings.requireFullscreen}\n onChange={(v) => set('requireFullscreen', v)}\n />\n <Toggle\n label=\"Block copy and paste\"\n checked={settings.blockClipboard}\n onChange={(v) => set('blockClipboard', v)}\n />\n <Toggle\n label=\"Shuffle questions\"\n checked={settings.shuffleQuestions}\n onChange={(v) => set('shuffleQuestions', v)}\n />\n <Toggle\n label=\"Shuffle options\"\n checked={settings.shuffleOptions}\n onChange={(v) => set('shuffleOptions', v)}\n />\n <Toggle\n label=\"Issue course certificate\"\n checked={settings.issuesCertificate}\n onChange={(v) => set('issuesCertificate', v)}\n />\n </div>\n </div>\n {quiz ? (\n <LessonKindFields\n kind=\"QUIZ\"\n draft={quiz}\n onChange={setQuestions}\n heading={`Questions (learners need ${settings.passingScore || '?'}% to pass)`}\n />\n ) : keyLoading ? (\n <LoadingRows count={2} />\n ) : null}\n <div className=\"flex justify-end gap-2 border-t border-border-seam pt-3\">\n <Button variant=\"outline\" onClick={onDone}>\n <X size={15} /> Cancel\n </Button>\n <Button\n onClick={() => void save()}\n disabled={creating || updating}\n data-testid=\"assessment-save\"\n >\n <Check size={15} /> {creating || updating ? 'Saving…' : 'Save assessment'}\n </Button>\n </div>\n </GlassCard>\n );\n}\n\nfunction AttemptReviewRow({ attempt, title }: { attempt: TecAssessmentAttempt; title: string }) {\n const [open, setOpen] = useState(false);\n const [notes, setNotes] = useState('');\n const { data: eventsData } = useAssessmentAttemptEventsQuery(open ? attempt.id : null);\n const [review, { loading }] = useReviewAssessmentAttempt();\n const reviewable =\n attempt.proctoringStatus === 'FLAGGED' || attempt.proctoringStatus === 'PENDING_REVIEW';\n\n const decide = async (decision: 'VERIFIED' | 'REJECTED'): Promise<void> => {\n const res = await review({\n variables: { id: attempt.id, decision, notes: notes.trim() || null },\n });\n const done = res.data?.tecReviewAssessmentAttempt;\n if (done) {\n notify.success(\n decision === 'VERIFIED' ? 'Attempt verified' : 'Attempt rejected',\n done.certificateId ? 'The certificate was issued.' : undefined\n );\n }\n };\n\n return (\n <GlassCard className=\"space-y-3 p-4\" data-testid=\"assessment-review-row\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-semibold text-text-primary\">{title}</div>\n <div className=\"text-xs text-text-muted\">\n Learner {attempt.learnerId} · attempt {attempt.attemptNumber} · {attempt.score ?? 0}% (\n {attempt.passed ? 'passed' : 'not passed'}) · {attempt.integrityViolations} integrity\n events · {formatDateTime(attempt.submittedAt ?? attempt.startedAt)}\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <StatusBadge status={attempt.proctoringStatus} />\n <Button size=\"sm\" variant=\"outline\" onClick={() => setOpen((v) => !v)}>\n {open ? 'Hide' : 'Review'}\n </Button>\n </div>\n </div>\n {open ? (\n <div className=\"space-y-3\">\n <ol\n className=\"max-h-56 space-y-1 overflow-auto rounded-lg bg-bg-sunken p-3 text-xs\"\n data-testid=\"assessment-events\"\n >\n {(eventsData?.tecAssessmentAttemptEvents ?? []).map((event) => (\n <li\n key={event.id}\n className={event.violation ? 'text-status-warning-text' : 'text-text-secondary'}\n >\n {formatDateTime(event.occurredAt)} · {humanizeEnum(event.type)}\n {event.violation ? ' (violation)' : ''}\n </li>\n ))}\n </ol>\n {reviewable ? (\n <>\n <Textarea\n aria-label=\"Review notes\"\n placeholder=\"Notes for the learner (optional)\"\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n />\n <div className=\"flex gap-2\">\n <Button size=\"sm\" disabled={loading} onClick={() => void decide('VERIFIED')}>\n <Check size={14} /> Verify\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={loading}\n onClick={() => void decide('REJECTED')}\n >\n <X size={14} /> Reject\n </Button>\n </div>\n </>\n ) : attempt.reviewNotes ? (\n <p className=\"text-sm text-text-secondary\">Reviewer note: {attempt.reviewNotes}</p>\n ) : null}\n </div>\n ) : null}\n </GlassCard>\n );\n}\n\n/**\n * Authoring console tab for certification assessments (BOFF-4427): pick a\n * course, create / edit / publish / archive its assessments, and work the\n * review queue of flagged and review-required attempts.\n */\nexport function AssessmentsAdminTab() {\n const { data: courseData } = useCoursesQuery({});\n const courses = courseData?.tecListCourses?.items ?? [];\n const [courseId, setCourseId] = useState<string>('');\n const activeCourseId = courseId || courses[0]?.id || '';\n const { data, loading, error } = useAssessmentsQuery(\n { courseId: activeCourseId },\n !activeCourseId\n );\n const [editing, setEditing] = useState<TecAssessment | 'new' | null>(null);\n const [publish] = usePublishAssessment();\n const [archive] = useArchiveAssessment();\n const [filter, setFilter] = useState<TecProctoringStatus | 'ALL'>('FLAGGED');\n const attemptsQuery = useAssessmentAttemptsQuery({\n proctoringStatus: filter === 'ALL' ? null : filter,\n });\n const assessments = data?.tecListAssessments ?? [];\n const titles = new Map(assessments.map((a) => [a.id, a.title]));\n\n return (\n <div className=\"space-y-6\">\n <section className=\"space-y-3\">\n <div className=\"flex flex-wrap items-end justify-between gap-3\">\n <Field label=\"Course\">\n <Select\n value={activeCourseId}\n onValueChange={(v) => {\n setCourseId(v);\n setEditing(null);\n }}\n >\n <SelectTrigger aria-label=\"Course\" className=\"min-w-64\">\n <SelectValue placeholder=\"Choose a course\" />\n </SelectTrigger>\n <SelectContent>\n {courses.map((course) => (\n <SelectItem key={course.id} value={course.id}>\n {course.title}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </Field>\n {activeCourseId && editing === null ? (\n <Button size=\"sm\" onClick={() => setEditing('new')} data-testid=\"assessment-new\">\n <Plus size={15} /> New assessment\n </Button>\n ) : null}\n </div>\n\n {editing !== null && activeCourseId ? (\n <AssessmentForm\n key={editing === 'new' ? 'new' : editing.id}\n courseId={activeCourseId}\n assessment={editing === 'new' ? null : editing}\n onDone={() => setEditing(null)}\n />\n ) : null}\n\n {!activeCourseId ? (\n <EmptyView\n title=\"No courses yet\"\n description=\"Create a course first, then add its assessments.\"\n />\n ) : error ? (\n <ErrorView title=\"Could not load assessments\" error={error} />\n ) : loading && assessments.length === 0 ? (\n <LoadingRows count={2} />\n ) : assessments.length === 0 ? (\n <EmptyView\n icon={<ShieldCheck size={26} />}\n title=\"No assessments for this course\"\n description=\"Add a timed, integrity-monitored exam that issues the course certificate.\"\n />\n ) : (\n <div className=\"space-y-2\">\n {assessments.map((assessment) => (\n <GlassCard\n key={assessment.id}\n className=\"flex flex-wrap items-center justify-between gap-3 p-4\"\n >\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-semibold text-text-primary\">\n {assessment.title}\n </div>\n <div className=\"text-xs text-text-muted\">\n {assessment.questionCount} questions · {assessment.timeLimitMinutes} min · pass{' '}\n {assessment.passingScore}% · {humanizeEnum(assessment.proctoringMode)}\n </div>\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <StatusBadge status={assessment.status} />\n <Button size=\"sm\" variant=\"outline\" onClick={() => setEditing(assessment)}>\n <Pencil size={14} /> Edit\n </Button>\n {assessment.status !== 'PUBLISHED' ? (\n <Button\n size=\"sm\"\n onClick={() =>\n void publish({ variables: { id: assessment.id } }).then((res) => {\n if (res.data?.tecPublishAssessment)\n notify.success('Assessment published');\n })\n }\n >\n <Rocket size={14} /> Publish\n </Button>\n ) : (\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() =>\n void archive({ variables: { id: assessment.id } }).then((res) => {\n if (res.data?.tecArchiveAssessment) notify.success('Assessment archived');\n })\n }\n >\n <Archive size={14} /> Archive\n </Button>\n )}\n </div>\n </GlassCard>\n ))}\n </div>\n )}\n </section>\n\n <section className=\"space-y-3\">\n <div className=\"flex flex-wrap items-end justify-between gap-3\">\n <h3 className=\"flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <ListChecks size={16} /> Review queue\n </h3>\n <Select value={filter} onValueChange={(v) => setFilter(v as TecProctoringStatus | 'ALL')}>\n <SelectTrigger aria-label=\"Review filter\" className=\"min-w-48\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {REVIEW_FILTERS.map((f) => (\n <SelectItem key={f} value={f}>\n {f === 'ALL' ? 'All attempts' : humanizeEnum(f)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n {attemptsQuery.error ? (\n <ErrorView title=\"Could not load attempts\" error={attemptsQuery.error} />\n ) : (attemptsQuery.data?.tecListAssessmentAttempts ?? []).length === 0 ? (\n <EmptyView\n title=\"Nothing to review\"\n description=\"Flagged and review-required attempts appear here.\"\n />\n ) : (\n <div className=\"space-y-2\">\n {(attemptsQuery.data?.tecListAssessmentAttempts ?? []).map((attempt) => (\n <AttemptReviewRow\n key={attempt.id}\n attempt={attempt}\n title={titles.get(attempt.assessmentId) ?? 'Assessment attempt'}\n />\n ))}\n </div>\n )}\n </section>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Enterprise connectors (BOFF-4430): ATS certificate verification, xAPI
|
|
3
|
-
* export for LMS / LRS / HRIS,
|
|
3
|
+
* export for LMS / LRS / HRIS, the scheduled push to a connected LRS
|
|
4
|
+
* (BOFF-10575), and a pointer to platform SSO/SCIM.
|
|
4
5
|
*/
|
|
5
6
|
export declare function EnterpriseIntegrationsTab(): import("react").JSX.Element;
|
|
6
7
|
//# sourceMappingURL=EnterpriseIntegrationsTab.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EnterpriseIntegrationsTab.d.ts","sourceRoot":"","sources":["../../../../src/technospam/components/admin/EnterpriseIntegrationsTab.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"EnterpriseIntegrationsTab.d.ts","sourceRoot":"","sources":["../../../../src/technospam/components/admin/EnterpriseIntegrationsTab.tsx"],"names":[],"mappings":"AA0gBA;;;;GAIG;AACH,wBAAgB,yBAAyB,gCASxC"}
|