@burdenoff/microfe-technospam 2026.1102.4 → 2026.1103.1

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.
Files changed (35) hide show
  1. package/dist/generated/wspace-types.d.ts +397 -8
  2. package/dist/generated/wspace-types.d.ts.map +1 -1
  3. package/dist/stories/fixtures/technospamFixtures.d.ts.map +1 -1
  4. package/dist/technospam/api/canonicalLearning.d.ts +26 -1
  5. package/dist/technospam/api/canonicalLearning.d.ts.map +1 -1
  6. package/dist/technospam/api/canonicalLearning.js +31 -21
  7. package/dist/technospam/api/canonicalLearning.js.map +1 -1
  8. package/dist/technospam/api/technospamApi.d.ts +131 -13
  9. package/dist/technospam/api/technospamApi.d.ts.map +1 -1
  10. package/dist/technospam/api/technospamApi.js +575 -356
  11. package/dist/technospam/api/technospamApi.js.map +1 -1
  12. package/dist/technospam/components/StatusBadge.d.ts.map +1 -1
  13. package/dist/technospam/components/StatusBadge.js +4 -0
  14. package/dist/technospam/components/StatusBadge.js.map +1 -1
  15. package/dist/technospam/components/admin/AssessmentsAdminTab.js +1 -1
  16. package/dist/technospam/components/admin/AssessmentsAdminTab.js.map +1 -1
  17. package/dist/technospam/components/admin/EnterpriseIntegrationsTab.d.ts +2 -1
  18. package/dist/technospam/components/admin/EnterpriseIntegrationsTab.d.ts.map +1 -1
  19. package/dist/technospam/components/admin/EnterpriseIntegrationsTab.js +325 -95
  20. package/dist/technospam/components/admin/EnterpriseIntegrationsTab.js.map +1 -1
  21. package/dist/technospam/lib/enterprise.d.ts +20 -0
  22. package/dist/technospam/lib/enterprise.d.ts.map +1 -1
  23. package/dist/technospam/lib/enterprise.js +33 -1
  24. package/dist/technospam/lib/enterprise.js.map +1 -1
  25. package/dist/technospam/pages/AdminAdminDashboardPagePage.js +2 -2
  26. package/dist/technospam/pages/AdminAdminDashboardPagePage.js.map +1 -1
  27. package/dist/technospam/pages/AnalyticsAnalyticsPagePage.js +1 -1
  28. package/dist/technospam/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
  29. package/dist/technospam/pages/CoursesCourseDetailsPagePage.js +1 -1
  30. package/dist/technospam/pages/CoursesCourseDetailsPagePage.js.map +1 -1
  31. package/dist/technospam/pages/CoursesCoursesPagePage.js +1 -1
  32. package/dist/technospam/pages/CoursesCoursesPagePage.js.map +1 -1
  33. package/dist/technospam/pages/DashboardDashboardPagePage.js +1 -1
  34. package/dist/technospam/pages/DashboardDashboardPagePage.js.map +1 -1
  35. package/package.json +1 -1
@@ -1 +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
+ {"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?.courses?.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, and a pointer to platform SSO/SCIM.
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":"AAmRA;;;GAGG;AACH,wBAAgB,yBAAyB,gCAQxC"}
1
+ {"version":3,"file":"EnterpriseIntegrationsTab.d.ts","sourceRoot":"","sources":["../../../../src/technospam/components/admin/EnterpriseIntegrationsTab.tsx"],"names":[],"mappings":"AA0gBA;;;;GAIG;AACH,wBAAgB,yBAAyB,gCASxC"}