@burdenoff/microfe-technospam 2026.1102.4 → 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/wspace-types.d.ts +295 -3
- package/dist/generated/wspace-types.d.ts.map +1 -1
- 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/StatusBadge.d.ts.map +1 -1
- package/dist/technospam/components/StatusBadge.js +4 -0
- package/dist/technospam/components/StatusBadge.js.map +1 -1
- 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/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/package.json +1 -1
|
@@ -2,44 +2,44 @@ import { formatDate as e } from "../../utils/format.js";
|
|
|
2
2
|
import { StatusBadge as t } from "../StatusBadge.js";
|
|
3
3
|
import { ErrorView as n } from "../StateViews.js";
|
|
4
4
|
import { notify as r } from "../../lib/toast.js";
|
|
5
|
-
import { useCoursesQuery as i,
|
|
6
|
-
import { atsVerificationSnippet as
|
|
7
|
-
import { useState as
|
|
8
|
-
import { BadgeCheck as
|
|
9
|
-
import { jsx as
|
|
10
|
-
import { Button as
|
|
5
|
+
import { useCoursesQuery as i, useLrsConnectionQuery as a, useVerifyCertificatesQuery as o, useXapiExport as s, useXapiLrsActions as c, useXapiLrsSyncQuery as l } from "../../api/technospamApi.js";
|
|
6
|
+
import { atsVerificationSnippet as u, dayStartIso as d, lrsConnectionName as f, lrsSyncSummary as p, nextDayStartIso as m, normalizeLrsEndpoint as h, parseSerials as g, validateLrsForm as _, xapiExportFileName as v } from "../../lib/enterprise.js";
|
|
7
|
+
import { useEffect as y, useState as b } from "react";
|
|
8
|
+
import { BadgeCheck as x, Download as S, FileJson as C, KeyRound as w, Loader2 as T, RefreshCw as E, Search as D, Send as O, Unplug as k } from "lucide-react";
|
|
9
|
+
import { jsx as A, jsxs as j } from "react/jsx-runtime";
|
|
10
|
+
import { Button as M, GlassCard as N, Input as P, Select as F, SelectContent as I, SelectItem as L, SelectTrigger as R, SelectValue as z, Textarea as B } from "@burdenoff/fe-libs/ui";
|
|
11
11
|
//#region src/technospam/components/admin/EnterpriseIntegrationsTab.tsx
|
|
12
|
-
var
|
|
13
|
-
function
|
|
14
|
-
return /* @__PURE__ */
|
|
12
|
+
var V = "https://graphqlworkspaces.burdenoff.com/workspaces/graphql", H = "__all__";
|
|
13
|
+
function U({ icon: e, title: t, description: n }) {
|
|
14
|
+
return /* @__PURE__ */ j("div", {
|
|
15
15
|
className: "mb-4 flex items-start gap-3",
|
|
16
|
-
children: [/* @__PURE__ */
|
|
16
|
+
children: [/* @__PURE__ */ A("span", {
|
|
17
17
|
className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-accent-primary/10 text-accent-primary",
|
|
18
|
-
children: /* @__PURE__ */
|
|
19
|
-
}), /* @__PURE__ */
|
|
18
|
+
children: /* @__PURE__ */ A(e, { size: 18 })
|
|
19
|
+
}), /* @__PURE__ */ j("div", {
|
|
20
20
|
className: "min-w-0",
|
|
21
|
-
children: [/* @__PURE__ */
|
|
21
|
+
children: [/* @__PURE__ */ A("h2", {
|
|
22
22
|
className: "text-base font-semibold text-text-primary",
|
|
23
23
|
children: t
|
|
24
|
-
}), /* @__PURE__ */
|
|
24
|
+
}), /* @__PURE__ */ A("p", {
|
|
25
25
|
className: "mt-0.5 text-sm text-text-secondary",
|
|
26
26
|
children: n
|
|
27
27
|
})]
|
|
28
28
|
})]
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
|
-
function
|
|
32
|
-
let [r, i] =
|
|
33
|
-
return /* @__PURE__ */
|
|
31
|
+
function W() {
|
|
32
|
+
let [r, i] = b(""), [a, s] = b([]), { data: c, loading: l, error: d } = o(a), f = g(r), p = f.length > 25, m = c?.tecVerifyCertificates ?? [];
|
|
33
|
+
return /* @__PURE__ */ j(N, {
|
|
34
34
|
className: "p-5",
|
|
35
35
|
"data-testid": "tec-enterprise-verify",
|
|
36
36
|
children: [
|
|
37
|
-
/* @__PURE__ */
|
|
38
|
-
icon:
|
|
37
|
+
/* @__PURE__ */ A(U, {
|
|
38
|
+
icon: x,
|
|
39
39
|
title: "Certificate verification for ATS partners",
|
|
40
40
|
description: "Recruiters and applicant-tracking systems confirm a candidate's TechnoSpam certificate by its serial. The check is public and returns only the course, score, dates and status — never the learner's identity."
|
|
41
41
|
}),
|
|
42
|
-
/* @__PURE__ */
|
|
42
|
+
/* @__PURE__ */ j("label", {
|
|
43
43
|
htmlFor: "tec-verify-serials",
|
|
44
44
|
className: "mb-1 block text-xs font-medium text-text-muted",
|
|
45
45
|
children: [
|
|
@@ -48,7 +48,7 @@ function N() {
|
|
|
48
48
|
")"
|
|
49
49
|
]
|
|
50
50
|
}),
|
|
51
|
-
/* @__PURE__ */
|
|
51
|
+
/* @__PURE__ */ A(B, {
|
|
52
52
|
id: "tec-verify-serials",
|
|
53
53
|
value: r,
|
|
54
54
|
onChange: (e) => i(e.target.value),
|
|
@@ -56,201 +56,431 @@ function N() {
|
|
|
56
56
|
rows: 3,
|
|
57
57
|
className: "font-mono text-sm"
|
|
58
58
|
}),
|
|
59
|
-
/* @__PURE__ */
|
|
59
|
+
/* @__PURE__ */ j("div", {
|
|
60
60
|
className: "mt-3 flex flex-wrap items-center gap-3",
|
|
61
|
-
children: [/* @__PURE__ */
|
|
61
|
+
children: [/* @__PURE__ */ j(M, {
|
|
62
62
|
size: "sm",
|
|
63
|
-
onClick: () =>
|
|
64
|
-
disabled:
|
|
65
|
-
children: [
|
|
63
|
+
onClick: () => s(f),
|
|
64
|
+
disabled: f.length === 0 || p || l,
|
|
65
|
+
children: [l ? /* @__PURE__ */ A(T, {
|
|
66
66
|
size: 15,
|
|
67
67
|
className: "animate-spin"
|
|
68
|
-
}) : /* @__PURE__ */
|
|
69
|
-
}), /* @__PURE__ */
|
|
68
|
+
}) : /* @__PURE__ */ A(D, { size: 15 }), "Verify"]
|
|
69
|
+
}), /* @__PURE__ */ A("span", {
|
|
70
70
|
className: "text-xs text-text-muted",
|
|
71
|
-
children:
|
|
71
|
+
children: p ? `Too many serials: ${f.length} of at most 25.` : `${f.length} serial${f.length === 1 ? "" : "s"}`
|
|
72
72
|
})]
|
|
73
73
|
}),
|
|
74
|
-
|
|
74
|
+
d ? /* @__PURE__ */ A("div", {
|
|
75
75
|
className: "mt-4",
|
|
76
|
-
children: /* @__PURE__ */
|
|
76
|
+
children: /* @__PURE__ */ A(n, {
|
|
77
77
|
title: "Couldn't verify",
|
|
78
|
-
error:
|
|
78
|
+
error: d
|
|
79
79
|
})
|
|
80
|
-
}) :
|
|
80
|
+
}) : m.length > 0 ? /* @__PURE__ */ A("ul", {
|
|
81
81
|
className: "mt-4 space-y-2",
|
|
82
82
|
"aria-label": "Verification results",
|
|
83
|
-
children:
|
|
83
|
+
children: m.map((n) => /* @__PURE__ */ j("li", {
|
|
84
84
|
className: "flex flex-col gap-1 rounded-lg border border-border-seam bg-bg-sunken p-3 sm:flex-row sm:items-center sm:justify-between",
|
|
85
|
-
children: [/* @__PURE__ */
|
|
85
|
+
children: [/* @__PURE__ */ j("div", {
|
|
86
86
|
className: "min-w-0",
|
|
87
|
-
children: [/* @__PURE__ */
|
|
87
|
+
children: [/* @__PURE__ */ A("div", {
|
|
88
88
|
className: "truncate font-mono text-sm text-text-primary",
|
|
89
89
|
children: n.serial
|
|
90
|
-
}), /* @__PURE__ */
|
|
90
|
+
}), /* @__PURE__ */ A("div", {
|
|
91
91
|
className: "mt-0.5 text-xs text-text-muted",
|
|
92
92
|
children: n.status === "NOT_FOUND" ? "No TechnoSpam certificate has this serial." : `${n.courseTitle ?? "Course"} · score ${n.score ?? 0} · issued ${e(n.issuedAt)}${n.revokedAt ? ` · revoked ${e(n.revokedAt)}` : ""}`
|
|
93
93
|
})]
|
|
94
|
-
}), /* @__PURE__ */
|
|
94
|
+
}), /* @__PURE__ */ A(t, { status: n.status })]
|
|
95
95
|
}, n.serial))
|
|
96
96
|
}) : null,
|
|
97
|
-
/* @__PURE__ */
|
|
97
|
+
/* @__PURE__ */ j("details", {
|
|
98
98
|
className: "mt-5 rounded-lg border border-border-seam p-3",
|
|
99
99
|
children: [
|
|
100
|
-
/* @__PURE__ */
|
|
100
|
+
/* @__PURE__ */ A("summary", {
|
|
101
101
|
className: "cursor-pointer text-sm font-medium text-text-primary",
|
|
102
102
|
children: "Integration details for ATS partners"
|
|
103
103
|
}),
|
|
104
|
-
/* @__PURE__ */
|
|
104
|
+
/* @__PURE__ */ j("p", {
|
|
105
105
|
className: "mt-2 text-sm text-text-secondary",
|
|
106
106
|
children: [
|
|
107
107
|
"No API key is needed. Batch checks use ",
|
|
108
|
-
/* @__PURE__ */
|
|
108
|
+
/* @__PURE__ */ A("code", { children: "tecVerifyCertificates(serials: [...])" }),
|
|
109
109
|
" ",
|
|
110
110
|
"with up to ",
|
|
111
111
|
25,
|
|
112
112
|
" serials per request."
|
|
113
113
|
]
|
|
114
114
|
}),
|
|
115
|
-
/* @__PURE__ */
|
|
115
|
+
/* @__PURE__ */ A("pre", {
|
|
116
116
|
className: "mt-2 overflow-x-auto rounded-md bg-bg-sunken p-3 text-xs text-text-secondary",
|
|
117
|
-
children:
|
|
117
|
+
children: u(V)
|
|
118
118
|
})
|
|
119
119
|
]
|
|
120
120
|
})
|
|
121
121
|
]
|
|
122
122
|
});
|
|
123
123
|
}
|
|
124
|
-
function
|
|
124
|
+
function G(e, t) {
|
|
125
125
|
let n = new Blob([JSON.stringify(t, null, 2)], { type: "application/json" }), r = URL.createObjectURL(n), i = document.createElement("a");
|
|
126
126
|
i.href = r, i.download = e, document.body.appendChild(i), i.click(), i.remove(), URL.revokeObjectURL(r);
|
|
127
127
|
}
|
|
128
|
-
function
|
|
129
|
-
let { data: e } = i({}), t = e?.tecListCourses?.items ?? [], [a,
|
|
130
|
-
return /* @__PURE__ */
|
|
128
|
+
function K() {
|
|
129
|
+
let { data: e } = i({}), t = e?.tecListCourses?.items ?? [], [a, o] = b(H), [c, l] = b(""), [u, f] = b(""), [p, h] = b(null), { run: g, running: _, error: y } = s();
|
|
130
|
+
return /* @__PURE__ */ j(N, {
|
|
131
131
|
className: "p-5",
|
|
132
132
|
"data-testid": "tec-enterprise-xapi",
|
|
133
133
|
children: [
|
|
134
|
-
/* @__PURE__ */
|
|
135
|
-
icon:
|
|
134
|
+
/* @__PURE__ */ A(U, {
|
|
135
|
+
icon: C,
|
|
136
136
|
title: "xAPI learning-record export (LMS / LRS / HRIS)",
|
|
137
137
|
description: "Download learner activity as standard xAPI 1.0.3 statements — enrollments, lesson and course completions, certificates and revocations — ready to POST to any Learning Record Store. Statement ids are stable, so re-importing the same range is safe."
|
|
138
138
|
}),
|
|
139
|
-
/* @__PURE__ */
|
|
139
|
+
/* @__PURE__ */ j("div", {
|
|
140
140
|
className: "flex flex-wrap items-end gap-3",
|
|
141
141
|
children: [
|
|
142
|
-
/* @__PURE__ */
|
|
142
|
+
/* @__PURE__ */ j("div", {
|
|
143
143
|
className: "min-w-48",
|
|
144
|
-
children: [/* @__PURE__ */
|
|
144
|
+
children: [/* @__PURE__ */ A("label", {
|
|
145
145
|
className: "mb-1 block text-xs font-medium text-text-muted",
|
|
146
146
|
htmlFor: "tec-xapi-course",
|
|
147
147
|
children: "Course"
|
|
148
|
-
}), /* @__PURE__ */
|
|
148
|
+
}), /* @__PURE__ */ j(F, {
|
|
149
149
|
value: a,
|
|
150
|
-
onValueChange:
|
|
151
|
-
children: [/* @__PURE__ */
|
|
150
|
+
onValueChange: o,
|
|
151
|
+
children: [/* @__PURE__ */ A(R, {
|
|
152
152
|
id: "tec-xapi-course",
|
|
153
153
|
className: "w-56",
|
|
154
|
-
children: /* @__PURE__ */
|
|
155
|
-
}), /* @__PURE__ */
|
|
156
|
-
value:
|
|
154
|
+
children: /* @__PURE__ */ A(z, { placeholder: "All courses" })
|
|
155
|
+
}), /* @__PURE__ */ j(I, { children: [/* @__PURE__ */ A(L, {
|
|
156
|
+
value: H,
|
|
157
157
|
children: "All courses"
|
|
158
|
-
}), t.map((e) => /* @__PURE__ */
|
|
158
|
+
}), t.map((e) => /* @__PURE__ */ A(L, {
|
|
159
159
|
value: e.id,
|
|
160
160
|
children: e.title
|
|
161
161
|
}, e.id))] })]
|
|
162
162
|
})]
|
|
163
163
|
}),
|
|
164
|
-
/* @__PURE__ */
|
|
164
|
+
/* @__PURE__ */ j("div", { children: [/* @__PURE__ */ A("label", {
|
|
165
165
|
className: "mb-1 block text-xs font-medium text-text-muted",
|
|
166
166
|
htmlFor: "tec-xapi-from",
|
|
167
167
|
children: "From"
|
|
168
|
-
}), /* @__PURE__ */
|
|
168
|
+
}), /* @__PURE__ */ A(P, {
|
|
169
169
|
id: "tec-xapi-from",
|
|
170
170
|
type: "date",
|
|
171
|
-
value:
|
|
172
|
-
max:
|
|
173
|
-
onChange: (e) =>
|
|
171
|
+
value: c,
|
|
172
|
+
max: u || void 0,
|
|
173
|
+
onChange: (e) => l(e.target.value),
|
|
174
174
|
className: "w-40"
|
|
175
175
|
})] }),
|
|
176
|
-
/* @__PURE__ */
|
|
176
|
+
/* @__PURE__ */ j("div", { children: [/* @__PURE__ */ A("label", {
|
|
177
177
|
className: "mb-1 block text-xs font-medium text-text-muted",
|
|
178
178
|
htmlFor: "tec-xapi-to",
|
|
179
179
|
children: "To"
|
|
180
|
-
}), /* @__PURE__ */
|
|
180
|
+
}), /* @__PURE__ */ A(P, {
|
|
181
181
|
id: "tec-xapi-to",
|
|
182
182
|
type: "date",
|
|
183
|
-
value:
|
|
184
|
-
min:
|
|
185
|
-
onChange: (e) =>
|
|
183
|
+
value: u,
|
|
184
|
+
min: c || void 0,
|
|
185
|
+
onChange: (e) => f(e.target.value),
|
|
186
186
|
className: "w-40"
|
|
187
187
|
})] }),
|
|
188
|
-
/* @__PURE__ */
|
|
188
|
+
/* @__PURE__ */ j(M, {
|
|
189
189
|
size: "sm",
|
|
190
190
|
onClick: async () => {
|
|
191
|
-
let e = t.find((e) => e.id === a), n = await
|
|
191
|
+
let e = t.find((e) => e.id === a), n = await g({
|
|
192
192
|
courseId: e?.id,
|
|
193
|
-
since: c
|
|
194
|
-
until:
|
|
193
|
+
since: d(c),
|
|
194
|
+
until: m(u)
|
|
195
195
|
});
|
|
196
196
|
if (n) {
|
|
197
|
-
if (
|
|
197
|
+
if (h(n.statements.length), n.statements.length === 0) {
|
|
198
198
|
r.info("Nothing to export", "No learner activity in this range.");
|
|
199
199
|
return;
|
|
200
200
|
}
|
|
201
|
-
|
|
201
|
+
G(v(/* @__PURE__ */ new Date(), e?.slug), n.statements), r.success("xAPI export downloaded", `${n.statements.length} statement${n.statements.length === 1 ? "" : "s"}${n.truncated ? " (first 5,000 — narrow the date range for the rest)" : ""}.`);
|
|
202
202
|
}
|
|
203
203
|
},
|
|
204
|
-
disabled:
|
|
205
|
-
children: [
|
|
204
|
+
disabled: _,
|
|
205
|
+
children: [_ ? /* @__PURE__ */ A(T, {
|
|
206
206
|
size: 15,
|
|
207
207
|
className: "animate-spin"
|
|
208
|
-
}) : /* @__PURE__ */
|
|
208
|
+
}) : /* @__PURE__ */ A(S, { size: 15 }), "Export statements"]
|
|
209
209
|
})
|
|
210
210
|
]
|
|
211
211
|
}),
|
|
212
|
-
|
|
212
|
+
p !== null && !y ? /* @__PURE__ */ j("p", {
|
|
213
213
|
className: "mt-3 text-xs text-text-muted",
|
|
214
214
|
role: "status",
|
|
215
215
|
children: [
|
|
216
216
|
"Last export: ",
|
|
217
|
-
|
|
217
|
+
p,
|
|
218
218
|
" statement",
|
|
219
|
-
|
|
219
|
+
p === 1 ? "" : "s",
|
|
220
220
|
"."
|
|
221
221
|
]
|
|
222
222
|
}) : null,
|
|
223
|
-
|
|
223
|
+
y ? /* @__PURE__ */ A("div", {
|
|
224
224
|
className: "mt-4",
|
|
225
|
-
children: /* @__PURE__ */
|
|
225
|
+
children: /* @__PURE__ */ A(n, {
|
|
226
226
|
title: "Couldn't export learning records",
|
|
227
|
-
error:
|
|
227
|
+
error: y
|
|
228
228
|
})
|
|
229
229
|
}) : null
|
|
230
230
|
]
|
|
231
231
|
});
|
|
232
232
|
}
|
|
233
|
-
|
|
234
|
-
|
|
233
|
+
var q = 5e3, J = 3e4, Y = {
|
|
234
|
+
endpoint: "",
|
|
235
|
+
key: "",
|
|
236
|
+
secret: ""
|
|
237
|
+
};
|
|
238
|
+
function X(e) {
|
|
239
|
+
if (!e) return "Never";
|
|
240
|
+
let t = new Date(e);
|
|
241
|
+
return Number.isNaN(t.getTime()) ? "Never" : t.toLocaleString();
|
|
242
|
+
}
|
|
243
|
+
function Z({ id: e, label: t, error: n, children: r }) {
|
|
244
|
+
return /* @__PURE__ */ j("div", {
|
|
245
|
+
className: "min-w-0",
|
|
246
|
+
children: [
|
|
247
|
+
/* @__PURE__ */ A("label", {
|
|
248
|
+
htmlFor: e,
|
|
249
|
+
className: "mb-1 block text-xs font-medium text-text-muted",
|
|
250
|
+
children: t
|
|
251
|
+
}),
|
|
252
|
+
r,
|
|
253
|
+
n ? /* @__PURE__ */ A("p", {
|
|
254
|
+
id: `${e}-error`,
|
|
255
|
+
className: "mt-1 text-xs text-status-error-text",
|
|
256
|
+
role: "alert",
|
|
257
|
+
children: n
|
|
258
|
+
}) : null
|
|
259
|
+
]
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
function Q({ busy: e, onConnect: t }) {
|
|
263
|
+
let [n, r] = b(Y), [i, a] = b({}), o = (e) => (t) => r((n) => ({
|
|
264
|
+
...n,
|
|
265
|
+
[e]: t.target.value
|
|
266
|
+
}));
|
|
267
|
+
return /* @__PURE__ */ j("form", {
|
|
268
|
+
onSubmit: async (e) => {
|
|
269
|
+
e.preventDefault();
|
|
270
|
+
let i = _(n);
|
|
271
|
+
a(i), !(Object.keys(i).length > 0) && await t(n) && r(Y);
|
|
272
|
+
},
|
|
273
|
+
noValidate: !0,
|
|
274
|
+
"aria-label": "Connect a Learning Record Store",
|
|
275
|
+
children: [/* @__PURE__ */ j("div", {
|
|
276
|
+
className: "grid gap-3 sm:grid-cols-3",
|
|
277
|
+
children: [
|
|
278
|
+
/* @__PURE__ */ A(Z, {
|
|
279
|
+
id: "tec-lrs-endpoint",
|
|
280
|
+
label: "LRS endpoint",
|
|
281
|
+
error: i.endpoint,
|
|
282
|
+
children: /* @__PURE__ */ A(P, {
|
|
283
|
+
id: "tec-lrs-endpoint",
|
|
284
|
+
type: "url",
|
|
285
|
+
inputMode: "url",
|
|
286
|
+
autoComplete: "off",
|
|
287
|
+
placeholder: "https://lrs.example.com/data/xAPI",
|
|
288
|
+
value: n.endpoint,
|
|
289
|
+
onChange: o("endpoint"),
|
|
290
|
+
"aria-invalid": !!i.endpoint,
|
|
291
|
+
"aria-describedby": i.endpoint ? "tec-lrs-endpoint-error" : void 0
|
|
292
|
+
})
|
|
293
|
+
}),
|
|
294
|
+
/* @__PURE__ */ A(Z, {
|
|
295
|
+
id: "tec-lrs-key",
|
|
296
|
+
label: "Key",
|
|
297
|
+
error: i.key,
|
|
298
|
+
children: /* @__PURE__ */ A(P, {
|
|
299
|
+
id: "tec-lrs-key",
|
|
300
|
+
autoComplete: "off",
|
|
301
|
+
value: n.key,
|
|
302
|
+
onChange: o("key"),
|
|
303
|
+
"aria-invalid": !!i.key,
|
|
304
|
+
"aria-describedby": i.key ? "tec-lrs-key-error" : void 0
|
|
305
|
+
})
|
|
306
|
+
}),
|
|
307
|
+
/* @__PURE__ */ A(Z, {
|
|
308
|
+
id: "tec-lrs-secret",
|
|
309
|
+
label: "Secret",
|
|
310
|
+
error: i.secret,
|
|
311
|
+
children: /* @__PURE__ */ A(P, {
|
|
312
|
+
id: "tec-lrs-secret",
|
|
313
|
+
type: "password",
|
|
314
|
+
autoComplete: "new-password",
|
|
315
|
+
value: n.secret,
|
|
316
|
+
onChange: o("secret"),
|
|
317
|
+
"aria-invalid": !!i.secret,
|
|
318
|
+
"aria-describedby": i.secret ? "tec-lrs-secret-error" : void 0
|
|
319
|
+
})
|
|
320
|
+
})
|
|
321
|
+
]
|
|
322
|
+
}), /* @__PURE__ */ j("div", {
|
|
323
|
+
className: "mt-3 flex flex-wrap items-center gap-3",
|
|
324
|
+
children: [/* @__PURE__ */ j(M, {
|
|
325
|
+
size: "sm",
|
|
326
|
+
type: "submit",
|
|
327
|
+
disabled: e,
|
|
328
|
+
children: [e ? /* @__PURE__ */ A(T, {
|
|
329
|
+
size: 15,
|
|
330
|
+
className: "animate-spin"
|
|
331
|
+
}) : /* @__PURE__ */ A(O, { size: 15 }), "Connect LRS"]
|
|
332
|
+
}), /* @__PURE__ */ A("span", {
|
|
333
|
+
className: "text-xs text-text-muted",
|
|
334
|
+
children: "The secret is stored encrypted in your workspace integrations, never in the TechnoSpam database."
|
|
335
|
+
})]
|
|
336
|
+
})]
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
function $({ label: e, value: t }) {
|
|
340
|
+
return /* @__PURE__ */ j("div", {
|
|
341
|
+
className: "min-w-0",
|
|
342
|
+
children: [/* @__PURE__ */ A("dt", {
|
|
343
|
+
className: "text-xs text-text-muted",
|
|
344
|
+
children: e
|
|
345
|
+
}), /* @__PURE__ */ A("dd", {
|
|
346
|
+
className: "mt-0.5 truncate text-sm text-text-primary",
|
|
347
|
+
children: t
|
|
348
|
+
})]
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
function ee() {
|
|
352
|
+
let { data: e, loading: r, error: i, startPolling: o, stopPolling: s } = l(), u = e?.tecXapiLrsSync ?? null, { data: d } = a(u?.connectionId), { connect: m, disconnect: g, syncNow: _, busy: v } = c(), b = d?.getConnection ?? null, x = typeof b?.config?.baseUrl == "string" ? b.config.baseUrl : null, S = u ? u.state === "RUNNING" || u.state === "NEVER_RUN" ? q : J : null;
|
|
353
|
+
return y(() => {
|
|
354
|
+
if (S !== null) return o(S), () => s();
|
|
355
|
+
}, [
|
|
356
|
+
S,
|
|
357
|
+
o,
|
|
358
|
+
s
|
|
359
|
+
]), /* @__PURE__ */ j(N, {
|
|
360
|
+
className: "p-5",
|
|
361
|
+
"data-testid": "tec-enterprise-lrs",
|
|
362
|
+
children: [/* @__PURE__ */ A(U, {
|
|
363
|
+
icon: E,
|
|
364
|
+
title: "Automatic sync to your LRS / HRIS",
|
|
365
|
+
description: "Connect a Learning Record Store (or an LMS or HRIS that accepts xAPI) once, and new learner activity is sent to it as xAPI 1.0.3 statements every few minutes. The first sync sends the full history."
|
|
366
|
+
}), r && !e ? /* @__PURE__ */ j("div", {
|
|
367
|
+
className: "flex items-center gap-2 text-sm text-text-muted",
|
|
368
|
+
children: [/* @__PURE__ */ A(T, {
|
|
369
|
+
size: 15,
|
|
370
|
+
className: "animate-spin"
|
|
371
|
+
}), " Loading sync status…"]
|
|
372
|
+
}) : i ? /* @__PURE__ */ A(n, {
|
|
373
|
+
title: "Couldn't load the LRS sync",
|
|
374
|
+
error: i
|
|
375
|
+
}) : u ? /* @__PURE__ */ j("div", { children: [
|
|
376
|
+
/* @__PURE__ */ j("div", {
|
|
377
|
+
className: "flex flex-wrap items-center gap-3",
|
|
378
|
+
children: [/* @__PURE__ */ A(t, { status: u.state }), /* @__PURE__ */ A("span", {
|
|
379
|
+
className: "text-sm text-text-secondary",
|
|
380
|
+
role: "status",
|
|
381
|
+
children: p(u.state, u.consecutiveFailures)
|
|
382
|
+
})]
|
|
383
|
+
}),
|
|
384
|
+
/* @__PURE__ */ j("dl", {
|
|
385
|
+
className: "mt-4 grid gap-3 sm:grid-cols-3",
|
|
386
|
+
children: [
|
|
387
|
+
/* @__PURE__ */ A($, {
|
|
388
|
+
label: "Endpoint",
|
|
389
|
+
value: /* @__PURE__ */ A("span", {
|
|
390
|
+
className: "font-mono",
|
|
391
|
+
children: x ?? b?.name ?? "—"
|
|
392
|
+
})
|
|
393
|
+
}),
|
|
394
|
+
/* @__PURE__ */ A($, {
|
|
395
|
+
label: "Last sync",
|
|
396
|
+
value: X(u.lastSyncAt)
|
|
397
|
+
}),
|
|
398
|
+
/* @__PURE__ */ A($, {
|
|
399
|
+
label: "Last success",
|
|
400
|
+
value: X(u.lastSuccessAt)
|
|
401
|
+
}),
|
|
402
|
+
/* @__PURE__ */ A($, {
|
|
403
|
+
label: "Statements sent",
|
|
404
|
+
value: u.statementsPushed.toLocaleString()
|
|
405
|
+
}),
|
|
406
|
+
/* @__PURE__ */ A($, {
|
|
407
|
+
label: "Next sync",
|
|
408
|
+
value: X(u.nextAttemptAt)
|
|
409
|
+
}),
|
|
410
|
+
/* @__PURE__ */ A($, {
|
|
411
|
+
label: "Interval",
|
|
412
|
+
value: `Every ${u.intervalMinutes} min`
|
|
413
|
+
})
|
|
414
|
+
]
|
|
415
|
+
}),
|
|
416
|
+
u.lastError ? /* @__PURE__ */ j("div", {
|
|
417
|
+
className: "mt-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-text-primary",
|
|
418
|
+
"data-testid": "tec-lrs-last-error",
|
|
419
|
+
children: [/* @__PURE__ */ j("div", {
|
|
420
|
+
className: "text-xs font-medium text-status-error-text",
|
|
421
|
+
children: ["Last error · ", X(u.lastErrorAt)]
|
|
422
|
+
}), /* @__PURE__ */ A("div", {
|
|
423
|
+
className: "mt-1 break-words",
|
|
424
|
+
children: u.lastError
|
|
425
|
+
})]
|
|
426
|
+
}) : null,
|
|
427
|
+
/* @__PURE__ */ j("div", {
|
|
428
|
+
className: "mt-4 flex flex-wrap gap-2",
|
|
429
|
+
children: [/* @__PURE__ */ j(M, {
|
|
430
|
+
size: "sm",
|
|
431
|
+
onClick: () => void _(),
|
|
432
|
+
disabled: v !== null,
|
|
433
|
+
children: [v === "sync" ? /* @__PURE__ */ A(T, {
|
|
434
|
+
size: 15,
|
|
435
|
+
className: "animate-spin"
|
|
436
|
+
}) : /* @__PURE__ */ A(E, { size: 15 }), "Sync now"]
|
|
437
|
+
}), /* @__PURE__ */ j(M, {
|
|
438
|
+
size: "sm",
|
|
439
|
+
variant: "outline",
|
|
440
|
+
onClick: () => void g(u.connectionId),
|
|
441
|
+
disabled: v !== null,
|
|
442
|
+
children: [v === "disconnect" ? /* @__PURE__ */ A(T, {
|
|
443
|
+
size: 15,
|
|
444
|
+
className: "animate-spin"
|
|
445
|
+
}) : /* @__PURE__ */ A(k, { size: 15 }), "Disconnect"]
|
|
446
|
+
})]
|
|
447
|
+
})
|
|
448
|
+
] }) : /* @__PURE__ */ A(Q, {
|
|
449
|
+
busy: v === "connect",
|
|
450
|
+
onConnect: async (e) => {
|
|
451
|
+
let t = h(e.endpoint);
|
|
452
|
+
return !!await m({
|
|
453
|
+
name: f(t),
|
|
454
|
+
endpoint: t,
|
|
455
|
+
key: e.key.trim(),
|
|
456
|
+
secret: e.secret
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
})]
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
function te() {
|
|
463
|
+
return /* @__PURE__ */ A(N, {
|
|
235
464
|
className: "p-5",
|
|
236
|
-
children: /* @__PURE__ */
|
|
237
|
-
icon:
|
|
465
|
+
children: /* @__PURE__ */ A(U, {
|
|
466
|
+
icon: w,
|
|
238
467
|
title: "Single sign-on and user provisioning",
|
|
239
468
|
description: "SAML single sign-on and SCIM 2.0 user provisioning come from the Burdenoff platform identity service that TechnoSpam signs in with, so learners provisioned by your identity provider or HRIS arrive with the same account id used in certificates and xAPI statements."
|
|
240
469
|
})
|
|
241
470
|
});
|
|
242
471
|
}
|
|
243
|
-
function
|
|
244
|
-
return /* @__PURE__ */
|
|
472
|
+
function ne() {
|
|
473
|
+
return /* @__PURE__ */ j("div", {
|
|
245
474
|
className: "space-y-4",
|
|
246
475
|
children: [
|
|
247
|
-
/* @__PURE__ */
|
|
248
|
-
/* @__PURE__ */
|
|
249
|
-
/* @__PURE__ */
|
|
476
|
+
/* @__PURE__ */ A(W, {}),
|
|
477
|
+
/* @__PURE__ */ A(K, {}),
|
|
478
|
+
/* @__PURE__ */ A(ee, {}),
|
|
479
|
+
/* @__PURE__ */ A(te, {})
|
|
250
480
|
]
|
|
251
481
|
});
|
|
252
482
|
}
|
|
253
483
|
//#endregion
|
|
254
|
-
export {
|
|
484
|
+
export { ne as EnterpriseIntegrationsTab };
|
|
255
485
|
|
|
256
486
|
//# sourceMappingURL=EnterpriseIntegrationsTab.js.map
|