@burdenoff/microfe-buildmyiq 2026.715.1 → 2026.715.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BuildmyiqAdminRoot.d.ts","sourceRoot":"","sources":["../../src/buildmyiq/BuildmyiqAdminRoot.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAwBhC,OAAO,KAAK,EACV,kBAAkB,EAQnB,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,uBAAwB,SAAQ,kBAAkB;IACjE,0GAA0G;IAC1G,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAkoBD,eAAO,MAAM,kBAAkB,EAAE,EAAE,CAAC,uBAAuB,
|
|
1
|
+
{"version":3,"file":"BuildmyiqAdminRoot.d.ts","sourceRoot":"","sources":["../../src/buildmyiq/BuildmyiqAdminRoot.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAwBhC,OAAO,KAAK,EACV,kBAAkB,EAQnB,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,uBAAwB,SAAQ,kBAAkB;IACjE,0GAA0G;IAC1G,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAkoBD,eAAO,MAAM,kBAAkB,EAAE,EAAE,CAAC,uBAAuB,CAkB1D,CAAC;AAEF,OAAO,EAAE,kBAAkB,IAAI,kBAAkB,EAAE,CAAC"}
|
|
@@ -577,7 +577,7 @@ function q() {
|
|
|
577
577
|
});
|
|
578
578
|
}
|
|
579
579
|
var J = (t) => {
|
|
580
|
-
let
|
|
580
|
+
let n = t;
|
|
581
581
|
return /* @__PURE__ */ N(T, {
|
|
582
582
|
client: I,
|
|
583
583
|
children: /* @__PURE__ */ N(E, {
|
|
@@ -585,7 +585,7 @@ var J = (t) => {
|
|
|
585
585
|
gateway: "global",
|
|
586
586
|
defaultEnabled: !0,
|
|
587
587
|
children: /* @__PURE__ */ N(e, {
|
|
588
|
-
...
|
|
588
|
+
...n,
|
|
589
589
|
children: /* @__PURE__ */ N(q, {})
|
|
590
590
|
})
|
|
591
591
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BuildmyiqAdminRoot.js","names":[],"sources":["../../src/buildmyiq/BuildmyiqAdminRoot.tsx"],"sourcesContent":["/**\n * BuildMyIQ Admin Root\n * @module buildmyiq\n *\n * Product-specific admin surface mounted inside `admin-app` under\n * `/admin/buildmyiq`. It exposes three core governance operations:\n *\n * 1. Course / assessment administration — list, publish, archive courses and\n * inspect assessments per course.\n * 2. Enrollment overview — workspace-wide enrollment list with status/progress.\n * 3. Submission review — graded/ungraded submission queue.\n *\n * The panel reuses the real `wspace-buildmyiq-svc` GraphQL operations already\n * consumed by the learner/author surfaces (`buiListCourses`, `buiListEnrollments`,\n * `buiListSubmissions`, `buiListAssessments`, `buiPublishCourse`, `buiArchiveCourse`).\n *\n * Remaining backend gaps (documented inline):\n * - Admin-scoped bulk mutations (bulk archive, bulk transition enrollment).\n * - Cross-workspace/admin analytics aggregations.\n * - Submission review detail + rubric grading UI.\n */\n\nimport type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { BookOpen, ClipboardCheck, Users, CheckSquare, GraduationCap } from 'lucide-react';\nimport { BuildmyiqProvider } from './context/BuildmyiqContext';\nimport { PageLayout } from './components/PageLayout';\nimport { Tabs } from './components/Tabs';\nimport { DataTable, type Column } from './components/DataTable';\nimport { StatusBadge } from './components/StatusBadge';\nimport { LoadingView, ErrorView, Section } from './components/StateViews';\nimport { EmptyState } from './components/EmptyState';\nimport { Pagination } from './components/Pagination';\nimport { FilterTabs } from './components/FilterTabs';\nimport {\n useBuiCourses,\n useBuiAssessments,\n useBuiEnrollments,\n useBuiSubmissions,\n useBuiPublishCourse,\n useBuiArchiveCourse,\n} from './hooks/useBuildmyiqApi';\nimport { formatDate, formatDateTime, formatPercent, formatNumber, formatHours } from './utils';\nimport type {\n BuildmyiqRootProps,\n BuiCourse,\n BuiCourseStatus,\n BuiEnrollment,\n BuiEnrollmentStatus,\n BuiSubmission,\n BuiSubmissionStatus,\n BuiAssessment,\n} from './types';\n\nexport interface BuildmyiqAdminRootProps extends BuildmyiqRootProps {\n /** Workspace token is supplied by admin-app for workspace-scoped MFEs; not used directly by BUI hooks. */\n workspaceToken?: string;\n}\n\ntype AdminTab = 'courses' | 'assessments' | 'enrollments' | 'submissions';\n\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 30 * 1000,\n retry: 1,\n refetchOnWindowFocus: false,\n },\n },\n});\n\nconst PAGE_SIZE = 12;\n\nconst COURSE_STATUS_OPTIONS: { value: 'ALL' | BuiCourseStatus; label: string }[] = [\n { value: 'ALL', label: 'All' },\n { value: 'DRAFT', label: 'Draft' },\n { value: 'IN_REVIEW', label: 'In Review' },\n { value: 'PUBLISHED', label: 'Published' },\n { value: 'ARCHIVED', label: 'Archived' },\n];\n\nconst ENROLLMENT_STATUS_OPTIONS: { value: 'ALL' | BuiEnrollmentStatus; label: string }[] = [\n { value: 'ALL', label: 'All' },\n { value: 'ENROLLED', label: 'Enrolled' },\n { value: 'IN_PROGRESS', label: 'In Progress' },\n { value: 'COMPLETED', label: 'Completed' },\n { value: 'DROPPED', label: 'Dropped' },\n { value: 'EXPIRED', label: 'Expired' },\n];\n\nconst SUBMISSION_STATUS_OPTIONS: { value: 'ALL' | BuiSubmissionStatus; label: string }[] = [\n { value: 'ALL', label: 'All' },\n { value: 'SUBMITTED', label: 'Submitted' },\n { value: 'GRADING', label: 'Grading' },\n { value: 'GRADED', label: 'Graded' },\n { value: 'RETURNED', label: 'Returned' },\n];\n\n// ============================================================================\n// Course administration\n// ============================================================================\n\nfunction CourseActions({ course }: { course: BuiCourse }) {\n const publish = useBuiPublishCourse();\n const archive = useBuiArchiveCourse();\n\n return (\n <div className=\"flex flex-wrap items-center gap-2\">\n {course.status === 'DRAFT' && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => publish.mutate(course.id)}\n disabled={publish.isPending}\n >\n Publish\n </Button>\n )}\n {course.status !== 'ARCHIVED' && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => archive.mutate(course.id)}\n disabled={archive.isPending}\n >\n Archive\n </Button>\n )}\n {course.status === 'ARCHIVED' && (\n <span className=\"text-xs text-text-secondary\">Archived</span>\n )}\n </div>\n );\n}\n\nfunction CoursesPanel() {\n const [status, setStatus] = useState<'ALL' | BuiCourseStatus>('ALL');\n const [page, setPage] = useState(0);\n const [cursors, setCursors] = useState<string[]>([]);\n\n const cursor = page > 0 ? cursors[page - 1] : undefined;\n const { data, isLoading, error, refetch } = useBuiCourses({\n status: status === 'ALL' ? undefined : status,\n cursor,\n limit: PAGE_SIZE,\n });\n\n const columns = useMemo<Column<BuiCourse>[]>(\n () => [\n {\n key: 'title',\n header: 'Course',\n render: (c) => (\n <div className=\"min-w-0\">\n <p className=\"font-medium text-text-primary truncate\">{c.title}</p>\n <p className=\"text-xs text-text-secondary\">{c.slug}</p>\n </div>\n ),\n sortValue: (c) => c.title.toLowerCase(),\n },\n {\n key: 'status',\n header: 'Status',\n render: (c) => <StatusBadge status={c.status} />,\n },\n {\n key: 'level',\n header: 'Level',\n render: (c) => <StatusBadge status={c.level} />,\n },\n {\n key: 'lessons',\n header: 'Lessons',\n align: 'right',\n render: (c) => formatNumber(c.lessons?.length ?? 0),\n sortValue: (c) => c.lessons?.length ?? 0,\n },\n {\n key: 'hours',\n header: 'Est. Hours',\n align: 'right',\n render: (c) => formatHours(c.estimatedHours),\n sortValue: (c) => c.estimatedHours,\n },\n {\n key: 'public',\n header: 'Visibility',\n render: (c) => (c.isPublic ? 'Public' : 'Private'),\n },\n {\n key: 'created',\n header: 'Created',\n render: (c) => formatDate(c.createdAt),\n sortValue: (c) => new Date(c.createdAt).getTime(),\n },\n {\n key: 'actions',\n header: 'Actions',\n align: 'right',\n render: (c) => <CourseActions course={c} />,\n },\n ],\n []\n );\n\n const handlePageChange = (nextPage: number) => {\n if (nextPage > page && data?.pageInfo.cursor) {\n setCursors((prev) => [...prev, data.pageInfo.cursor as string]);\n } else if (nextPage < page) {\n setCursors((prev) => prev.slice(0, -1));\n }\n setPage(nextPage);\n };\n\n if (isLoading) return <LoadingView label=\"Loading courses…\" />;\n if (error)\n return <ErrorView title=\"Could not load courses\" message={error.message} onRetry={refetch} />;\n\n const rows = data?.items ?? [];\n\n return (\n <Section title=\"Course catalog\" description=\"Publish, archive and inspect courses.\">\n <div className=\"mb-4\">\n <FilterTabs\n options={COURSE_STATUS_OPTIONS}\n value={status}\n onChange={(v) => {\n setStatus(v);\n setPage(0);\n setCursors([]);\n }}\n />\n </div>\n {rows.length === 0 ? (\n <EmptyState\n icon={<BookOpen size={40} />}\n title=\"No courses found\"\n description={`There are no ${status === 'ALL' ? '' : status.toLowerCase()} courses in this workspace.`}\n />\n ) : (\n <>\n <DataTable\n columns={columns}\n rows={rows}\n rowKey={(c) => c.id}\n defaultSort={{ key: 'created', dir: 'desc' }}\n />\n <Pagination\n page={page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={data?.pageInfo.hasMore ?? false}\n onPageChange={handlePageChange}\n />\n </>\n )}\n </Section>\n );\n}\n\n// ============================================================================\n// Assessment administration\n// ============================================================================\n\nfunction AssessmentsPanel() {\n const [selectedCourseId, setSelectedCourseId] = useState<string>('');\n\n const {\n data: coursesData,\n isLoading: coursesLoading,\n error: coursesError,\n } = useBuiCourses({\n status: 'PUBLISHED',\n limit: 100,\n });\n\n const {\n data: assessments,\n isLoading: assessmentsLoading,\n error: assessmentsError,\n } = useBuiAssessments(selectedCourseId || undefined);\n\n const columns = useMemo<Column<BuiAssessment>[]>(\n () => [\n {\n key: 'title',\n header: 'Assessment',\n render: (a) => (\n <div className=\"min-w-0\">\n <p className=\"font-medium text-text-primary truncate\">{a.title}</p>\n <p className=\"text-xs text-text-secondary\">{a.type}</p>\n </div>\n ),\n sortValue: (a) => a.title.toLowerCase(),\n },\n {\n key: 'status',\n header: 'Status',\n render: (a) => <StatusBadge status={a.status} />,\n },\n {\n key: 'questions',\n header: 'Questions',\n align: 'right',\n render: (a) => formatNumber(a.questions?.length ?? 0),\n sortValue: (a) => a.questions?.length ?? 0,\n },\n {\n key: 'passing',\n header: 'Passing Score',\n align: 'right',\n render: (a) => formatPercent(a.passingScore),\n },\n {\n key: 'attempts',\n header: 'Max Attempts',\n align: 'right',\n render: (a) => formatNumber(a.maxAttempts),\n },\n {\n key: 'time',\n header: 'Time Limit',\n align: 'right',\n render: (a) => (a.timeLimitMin > 0 ? `${a.timeLimitMin} min` : '—'),\n },\n ],\n []\n );\n\n if (coursesLoading) return <LoadingView label=\"Loading courses…\" />;\n if (coursesError)\n return <ErrorView title=\"Could not load courses\" message={coursesError.message} />;\n\n const courses = coursesData?.items ?? [];\n\n return (\n <Section\n title=\"Assessments\"\n description=\"Inspect assessments attached to a published course.\"\n action={\n <div className=\"min-w-[16rem]\">\n <label htmlFor=\"bui-admin-course-select\" className=\"sr-only\">\n Select course\n </label>\n <select\n id=\"bui-admin-course-select\"\n value={selectedCourseId}\n onChange={(e) => setSelectedCourseId(e.target.value)}\n className=\"w-full rounded-lg border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n >\n <option value=\"\">Choose a course…</option>\n {courses.map((c) => (\n <option key={c.id} value={c.id}>\n {c.title}\n </option>\n ))}\n </select>\n </div>\n }\n >\n {!selectedCourseId ? (\n <EmptyState\n icon={<ClipboardCheck size={40} />}\n title=\"Select a course\"\n description=\"Choose a published course above to view its assessments.\"\n />\n ) : assessmentsLoading ? (\n <LoadingView label=\"Loading assessments…\" />\n ) : assessmentsError ? (\n <ErrorView title=\"Could not load assessments\" message={assessmentsError.message} />\n ) : assessments?.length === 0 ? (\n <EmptyState\n icon={<ClipboardCheck size={40} />}\n title=\"No assessments\"\n description=\"This course has no assessments yet.\"\n />\n ) : (\n <DataTable\n columns={columns}\n rows={assessments ?? []}\n rowKey={(a) => a.id}\n defaultSort={{ key: 'title', dir: 'asc' }}\n />\n )}\n </Section>\n );\n}\n\n// ============================================================================\n// Enrollment overview\n// ============================================================================\n\nfunction EnrollmentsPanel() {\n const [status, setStatus] = useState<'ALL' | BuiEnrollmentStatus>('ALL');\n const [page, setPage] = useState(0);\n const [cursors, setCursors] = useState<string[]>([]);\n\n const cursor = page > 0 ? cursors[page - 1] : undefined;\n const { data, isLoading, error, refetch } = useBuiEnrollments({\n status: status === 'ALL' ? undefined : status,\n cursor,\n limit: PAGE_SIZE,\n });\n\n const columns = useMemo<Column<BuiEnrollment>[]>(\n () => [\n {\n key: 'learner',\n header: 'Learner',\n render: (e) => <span className=\"font-mono text-xs text-text-secondary\">{e.learnerId}</span>,\n },\n {\n key: 'course',\n header: 'Course ID',\n render: (e) => <span className=\"font-mono text-xs text-text-secondary\">{e.courseId}</span>,\n },\n {\n key: 'status',\n header: 'Status',\n render: (e) => <StatusBadge status={e.status} />,\n },\n {\n key: 'progress',\n header: 'Progress',\n align: 'right',\n render: (e) => formatPercent(e.progressPct),\n sortValue: (e) => e.progressPct,\n },\n {\n key: 'cohort',\n header: 'Cohort',\n render: (e) => e.cohort?.name ?? '—',\n },\n {\n key: 'enrolled',\n header: 'Enrolled',\n render: (e) => formatDateTime(e.enrolledAt),\n sortValue: (e) => new Date(e.enrolledAt).getTime(),\n hideOnMobile: true,\n },\n {\n key: 'lastActive',\n header: 'Last Active',\n render: (e) => (e.lastActiveAt ? formatRelativeTime(e.lastActiveAt) : '—'),\n sortValue: (e) => (e.lastActiveAt ? new Date(e.lastActiveAt).getTime() : 0),\n hideOnMobile: true,\n },\n ],\n []\n );\n\n const handlePageChange = (nextPage: number) => {\n if (nextPage > page && data?.pageInfo.cursor) {\n setCursors((prev) => [...prev, data.pageInfo.cursor as string]);\n } else if (nextPage < page) {\n setCursors((prev) => prev.slice(0, -1));\n }\n setPage(nextPage);\n };\n\n if (isLoading) return <LoadingView label=\"Loading enrollments…\" />;\n if (error)\n return (\n <ErrorView title=\"Could not load enrollments\" message={error.message} onRetry={refetch} />\n );\n\n const rows = data?.items ?? [];\n\n return (\n <Section title=\"Enrollments\" description=\"Workspace-wide learner enrollment overview.\">\n <div className=\"mb-4\">\n <FilterTabs\n options={ENROLLMENT_STATUS_OPTIONS}\n value={status}\n onChange={(v) => {\n setStatus(v);\n setPage(0);\n setCursors([]);\n }}\n />\n </div>\n {rows.length === 0 ? (\n <EmptyState\n icon={<Users size={40} />}\n title=\"No enrollments\"\n description={`There are no ${status === 'ALL' ? '' : status.toLowerCase()} enrollments in this workspace.`}\n />\n ) : (\n <>\n <DataTable\n columns={columns}\n rows={rows}\n rowKey={(e) => e.id}\n defaultSort={{ key: 'enrolled', dir: 'desc' }}\n />\n <Pagination\n page={page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={data?.pageInfo.hasMore ?? false}\n onPageChange={handlePageChange}\n />\n </>\n )}\n </Section>\n );\n}\n\n// Need formatRelativeTime for lastActive in enrollments; import it.\nfunction formatRelativeTime(date: string | Date): string {\n const d = typeof date === 'string' ? new Date(date) : date;\n const now = new Date();\n const diffMs = d.getTime() - now.getTime();\n const future = diffMs >= 0;\n const absSeconds = Math.floor(Math.abs(diffMs) / 1000);\n const minutes = Math.floor(absSeconds / 60);\n const hours = Math.floor(minutes / 60);\n const days = Math.floor(hours / 24);\n const phrase = (value: number, unit: string) => {\n const label = `${value} ${unit}${value !== 1 ? 's' : ''}`;\n return future ? `in ${label}` : `${label} ago`;\n };\n if (absSeconds < 60) return future ? 'soon' : 'just now';\n if (minutes < 60) return phrase(minutes, 'min');\n if (hours < 24) return phrase(hours, 'hour');\n return phrase(days, 'day');\n}\n\n// ============================================================================\n// Submission review\n// ============================================================================\n\nfunction SubmissionsPanel() {\n const [status, setStatus] = useState<'ALL' | BuiSubmissionStatus>('ALL');\n const [page, setPage] = useState(0);\n const [cursors, setCursors] = useState<string[]>([]);\n\n const cursor = page > 0 ? cursors[page - 1] : undefined;\n const { data, isLoading, error, refetch } = useBuiSubmissions({\n status: status === 'ALL' ? undefined : status,\n cursor,\n limit: PAGE_SIZE,\n });\n\n const columns = useMemo<Column<BuiSubmission>[]>(\n () => [\n {\n key: 'learner',\n header: 'Learner',\n render: (s) => <span className=\"font-mono text-xs text-text-secondary\">{s.learnerId}</span>,\n },\n {\n key: 'assessment',\n header: 'Assessment ID',\n render: (s) => (\n <span className=\"font-mono text-xs text-text-secondary\">{s.assessmentId}</span>\n ),\n },\n {\n key: 'attempt',\n header: 'Attempt',\n align: 'right',\n render: (s) => formatNumber(s.attempt),\n },\n {\n key: 'status',\n header: 'Status',\n render: (s) => <StatusBadge status={s.status} />,\n },\n {\n key: 'score',\n header: 'Score',\n align: 'right',\n render: (s) => (s.score == null ? '—' : formatPercent(s.score)),\n sortValue: (s) => s.score ?? -1,\n },\n {\n key: 'passed',\n header: 'Passed',\n align: 'center',\n render: (s) => (s.passed == null ? '—' : s.passed ? 'Yes' : 'No'),\n },\n {\n key: 'submitted',\n header: 'Submitted',\n render: (s) => (s.submittedAt ? formatDateTime(s.submittedAt) : '—'),\n sortValue: (s) => (s.submittedAt ? new Date(s.submittedAt).getTime() : 0),\n hideOnMobile: true,\n },\n {\n key: 'actions',\n header: 'Review',\n align: 'right',\n render: () => (\n <Button variant=\"ghost\" size=\"sm\" disabled>\n Open\n </Button>\n ),\n },\n ],\n []\n );\n\n const handlePageChange = (nextPage: number) => {\n if (nextPage > page && data?.pageInfo.cursor) {\n setCursors((prev) => [...prev, data.pageInfo.cursor as string]);\n } else if (nextPage < page) {\n setCursors((prev) => prev.slice(0, -1));\n }\n setPage(nextPage);\n };\n\n if (isLoading) return <LoadingView label=\"Loading submissions…\" />;\n if (error)\n return (\n <ErrorView title=\"Could not load submissions\" message={error.message} onRetry={refetch} />\n );\n\n const rows = data?.items ?? [];\n\n return (\n <Section title=\"Submission review\" description=\"Review learner submissions and grading status.\">\n <div className=\"mb-4\">\n <FilterTabs\n options={SUBMISSION_STATUS_OPTIONS}\n value={status}\n onChange={(v) => {\n setStatus(v);\n setPage(0);\n setCursors([]);\n }}\n />\n </div>\n {rows.length === 0 ? (\n <EmptyState\n icon={<CheckSquare size={40} />}\n title=\"No submissions\"\n description={`There are no ${status === 'ALL' ? '' : status.toLowerCase()} submissions in this workspace.`}\n />\n ) : (\n <>\n <DataTable\n columns={columns}\n rows={rows}\n rowKey={(s) => s.id}\n defaultSort={{ key: 'submitted', dir: 'desc' }}\n />\n <Pagination\n page={page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={data?.pageInfo.hasMore ?? false}\n onPageChange={handlePageChange}\n />\n </>\n )}\n </Section>\n );\n}\n\n// ============================================================================\n// Root\n// ============================================================================\n\nfunction BuildmyiqAdminDashboard() {\n const [activeTab, setActiveTab] = useState<AdminTab>('courses');\n\n const tabs = useMemo(\n () => [\n { value: 'courses' as const, label: 'Courses', icon: <BookOpen size={18} /> },\n { value: 'assessments' as const, label: 'Assessments', icon: <ClipboardCheck size={18} /> },\n { value: 'enrollments' as const, label: 'Enrollments', icon: <Users size={18} /> },\n { value: 'submissions' as const, label: 'Submissions', icon: <CheckSquare size={18} /> },\n ],\n []\n );\n\n return (\n <PageLayout\n title=\"BuildMyIQ Admin\"\n description=\"Course, assessment, enrollment and submission governance.\"\n actions={\n <span className=\"inline-flex items-center gap-2 text-sm text-text-secondary\">\n <GraduationCap size={18} />\n Workspace-scoped\n </span>\n }\n >\n <Tabs items={tabs} value={activeTab} onChange={(v) => setActiveTab(v)} />\n <div className=\"mt-6\">\n {activeTab === 'courses' && <CoursesPanel />}\n {activeTab === 'assessments' && <AssessmentsPanel />}\n {activeTab === 'enrollments' && <EnrollmentsPanel />}\n {activeTab === 'submissions' && <SubmissionsPanel />}\n </div>\n </PageLayout>\n );\n}\n\nexport const BuildmyiqAdminRoot: FC<BuildmyiqAdminRootProps> = (props) => {\n const { workspaceToken: _workspaceToken, ...providerProps } = props;\n\n return (\n <QueryClientProvider client={queryClient}>\n <FeatureFlagProvider\n workspaceId={props.workspaceId ?? null}\n gateway=\"global\"\n defaultEnabled={true}\n >\n <BuildmyiqProvider {...providerProps}>\n <BuildmyiqAdminDashboard />\n </BuildmyiqProvider>\n </FeatureFlagProvider>\n </QueryClientProvider>\n );\n};\n\nexport { BuildmyiqAdminRoot as BuildMyIQAdminRoot };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BuildmyiqAdminRoot.js","names":[],"sources":["../../src/buildmyiq/BuildmyiqAdminRoot.tsx"],"sourcesContent":["/**\n * BuildMyIQ Admin Root\n * @module buildmyiq\n *\n * Product-specific admin surface mounted inside `admin-app` under\n * `/admin/buildmyiq`. It exposes three core governance operations:\n *\n * 1. Course / assessment administration — list, publish, archive courses and\n * inspect assessments per course.\n * 2. Enrollment overview — workspace-wide enrollment list with status/progress.\n * 3. Submission review — graded/ungraded submission queue.\n *\n * The panel reuses the real `wspace-buildmyiq-svc` GraphQL operations already\n * consumed by the learner/author surfaces (`buiListCourses`, `buiListEnrollments`,\n * `buiListSubmissions`, `buiListAssessments`, `buiPublishCourse`, `buiArchiveCourse`).\n *\n * Remaining backend gaps (documented inline):\n * - Admin-scoped bulk mutations (bulk archive, bulk transition enrollment).\n * - Cross-workspace/admin analytics aggregations.\n * - Submission review detail + rubric grading UI.\n */\n\nimport type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { BookOpen, ClipboardCheck, Users, CheckSquare, GraduationCap } from 'lucide-react';\nimport { BuildmyiqProvider } from './context/BuildmyiqContext';\nimport { PageLayout } from './components/PageLayout';\nimport { Tabs } from './components/Tabs';\nimport { DataTable, type Column } from './components/DataTable';\nimport { StatusBadge } from './components/StatusBadge';\nimport { LoadingView, ErrorView, Section } from './components/StateViews';\nimport { EmptyState } from './components/EmptyState';\nimport { Pagination } from './components/Pagination';\nimport { FilterTabs } from './components/FilterTabs';\nimport {\n useBuiCourses,\n useBuiAssessments,\n useBuiEnrollments,\n useBuiSubmissions,\n useBuiPublishCourse,\n useBuiArchiveCourse,\n} from './hooks/useBuildmyiqApi';\nimport { formatDate, formatDateTime, formatPercent, formatNumber, formatHours } from './utils';\nimport type {\n BuildmyiqRootProps,\n BuiCourse,\n BuiCourseStatus,\n BuiEnrollment,\n BuiEnrollmentStatus,\n BuiSubmission,\n BuiSubmissionStatus,\n BuiAssessment,\n} from './types';\n\nexport interface BuildmyiqAdminRootProps extends BuildmyiqRootProps {\n /** Workspace token is supplied by admin-app for workspace-scoped MFEs; not used directly by BUI hooks. */\n workspaceToken?: string;\n}\n\ntype AdminTab = 'courses' | 'assessments' | 'enrollments' | 'submissions';\n\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 30 * 1000,\n retry: 1,\n refetchOnWindowFocus: false,\n },\n },\n});\n\nconst PAGE_SIZE = 12;\n\nconst COURSE_STATUS_OPTIONS: { value: 'ALL' | BuiCourseStatus; label: string }[] = [\n { value: 'ALL', label: 'All' },\n { value: 'DRAFT', label: 'Draft' },\n { value: 'IN_REVIEW', label: 'In Review' },\n { value: 'PUBLISHED', label: 'Published' },\n { value: 'ARCHIVED', label: 'Archived' },\n];\n\nconst ENROLLMENT_STATUS_OPTIONS: { value: 'ALL' | BuiEnrollmentStatus; label: string }[] = [\n { value: 'ALL', label: 'All' },\n { value: 'ENROLLED', label: 'Enrolled' },\n { value: 'IN_PROGRESS', label: 'In Progress' },\n { value: 'COMPLETED', label: 'Completed' },\n { value: 'DROPPED', label: 'Dropped' },\n { value: 'EXPIRED', label: 'Expired' },\n];\n\nconst SUBMISSION_STATUS_OPTIONS: { value: 'ALL' | BuiSubmissionStatus; label: string }[] = [\n { value: 'ALL', label: 'All' },\n { value: 'SUBMITTED', label: 'Submitted' },\n { value: 'GRADING', label: 'Grading' },\n { value: 'GRADED', label: 'Graded' },\n { value: 'RETURNED', label: 'Returned' },\n];\n\n// ============================================================================\n// Course administration\n// ============================================================================\n\nfunction CourseActions({ course }: { course: BuiCourse }) {\n const publish = useBuiPublishCourse();\n const archive = useBuiArchiveCourse();\n\n return (\n <div className=\"flex flex-wrap items-center gap-2\">\n {course.status === 'DRAFT' && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => publish.mutate(course.id)}\n disabled={publish.isPending}\n >\n Publish\n </Button>\n )}\n {course.status !== 'ARCHIVED' && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => archive.mutate(course.id)}\n disabled={archive.isPending}\n >\n Archive\n </Button>\n )}\n {course.status === 'ARCHIVED' && (\n <span className=\"text-xs text-text-secondary\">Archived</span>\n )}\n </div>\n );\n}\n\nfunction CoursesPanel() {\n const [status, setStatus] = useState<'ALL' | BuiCourseStatus>('ALL');\n const [page, setPage] = useState(0);\n const [cursors, setCursors] = useState<string[]>([]);\n\n const cursor = page > 0 ? cursors[page - 1] : undefined;\n const { data, isLoading, error, refetch } = useBuiCourses({\n status: status === 'ALL' ? undefined : status,\n cursor,\n limit: PAGE_SIZE,\n });\n\n const columns = useMemo<Column<BuiCourse>[]>(\n () => [\n {\n key: 'title',\n header: 'Course',\n render: (c) => (\n <div className=\"min-w-0\">\n <p className=\"font-medium text-text-primary truncate\">{c.title}</p>\n <p className=\"text-xs text-text-secondary\">{c.slug}</p>\n </div>\n ),\n sortValue: (c) => c.title.toLowerCase(),\n },\n {\n key: 'status',\n header: 'Status',\n render: (c) => <StatusBadge status={c.status} />,\n },\n {\n key: 'level',\n header: 'Level',\n render: (c) => <StatusBadge status={c.level} />,\n },\n {\n key: 'lessons',\n header: 'Lessons',\n align: 'right',\n render: (c) => formatNumber(c.lessons?.length ?? 0),\n sortValue: (c) => c.lessons?.length ?? 0,\n },\n {\n key: 'hours',\n header: 'Est. Hours',\n align: 'right',\n render: (c) => formatHours(c.estimatedHours),\n sortValue: (c) => c.estimatedHours,\n },\n {\n key: 'public',\n header: 'Visibility',\n render: (c) => (c.isPublic ? 'Public' : 'Private'),\n },\n {\n key: 'created',\n header: 'Created',\n render: (c) => formatDate(c.createdAt),\n sortValue: (c) => new Date(c.createdAt).getTime(),\n },\n {\n key: 'actions',\n header: 'Actions',\n align: 'right',\n render: (c) => <CourseActions course={c} />,\n },\n ],\n []\n );\n\n const handlePageChange = (nextPage: number) => {\n if (nextPage > page && data?.pageInfo.cursor) {\n setCursors((prev) => [...prev, data.pageInfo.cursor as string]);\n } else if (nextPage < page) {\n setCursors((prev) => prev.slice(0, -1));\n }\n setPage(nextPage);\n };\n\n if (isLoading) return <LoadingView label=\"Loading courses…\" />;\n if (error)\n return <ErrorView title=\"Could not load courses\" message={error.message} onRetry={refetch} />;\n\n const rows = data?.items ?? [];\n\n return (\n <Section title=\"Course catalog\" description=\"Publish, archive and inspect courses.\">\n <div className=\"mb-4\">\n <FilterTabs\n options={COURSE_STATUS_OPTIONS}\n value={status}\n onChange={(v) => {\n setStatus(v);\n setPage(0);\n setCursors([]);\n }}\n />\n </div>\n {rows.length === 0 ? (\n <EmptyState\n icon={<BookOpen size={40} />}\n title=\"No courses found\"\n description={`There are no ${status === 'ALL' ? '' : status.toLowerCase()} courses in this workspace.`}\n />\n ) : (\n <>\n <DataTable\n columns={columns}\n rows={rows}\n rowKey={(c) => c.id}\n defaultSort={{ key: 'created', dir: 'desc' }}\n />\n <Pagination\n page={page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={data?.pageInfo.hasMore ?? false}\n onPageChange={handlePageChange}\n />\n </>\n )}\n </Section>\n );\n}\n\n// ============================================================================\n// Assessment administration\n// ============================================================================\n\nfunction AssessmentsPanel() {\n const [selectedCourseId, setSelectedCourseId] = useState<string>('');\n\n const {\n data: coursesData,\n isLoading: coursesLoading,\n error: coursesError,\n } = useBuiCourses({\n status: 'PUBLISHED',\n limit: 100,\n });\n\n const {\n data: assessments,\n isLoading: assessmentsLoading,\n error: assessmentsError,\n } = useBuiAssessments(selectedCourseId || undefined);\n\n const columns = useMemo<Column<BuiAssessment>[]>(\n () => [\n {\n key: 'title',\n header: 'Assessment',\n render: (a) => (\n <div className=\"min-w-0\">\n <p className=\"font-medium text-text-primary truncate\">{a.title}</p>\n <p className=\"text-xs text-text-secondary\">{a.type}</p>\n </div>\n ),\n sortValue: (a) => a.title.toLowerCase(),\n },\n {\n key: 'status',\n header: 'Status',\n render: (a) => <StatusBadge status={a.status} />,\n },\n {\n key: 'questions',\n header: 'Questions',\n align: 'right',\n render: (a) => formatNumber(a.questions?.length ?? 0),\n sortValue: (a) => a.questions?.length ?? 0,\n },\n {\n key: 'passing',\n header: 'Passing Score',\n align: 'right',\n render: (a) => formatPercent(a.passingScore),\n },\n {\n key: 'attempts',\n header: 'Max Attempts',\n align: 'right',\n render: (a) => formatNumber(a.maxAttempts),\n },\n {\n key: 'time',\n header: 'Time Limit',\n align: 'right',\n render: (a) => (a.timeLimitMin > 0 ? `${a.timeLimitMin} min` : '—'),\n },\n ],\n []\n );\n\n if (coursesLoading) return <LoadingView label=\"Loading courses…\" />;\n if (coursesError)\n return <ErrorView title=\"Could not load courses\" message={coursesError.message} />;\n\n const courses = coursesData?.items ?? [];\n\n return (\n <Section\n title=\"Assessments\"\n description=\"Inspect assessments attached to a published course.\"\n action={\n <div className=\"min-w-[16rem]\">\n <label htmlFor=\"bui-admin-course-select\" className=\"sr-only\">\n Select course\n </label>\n <select\n id=\"bui-admin-course-select\"\n value={selectedCourseId}\n onChange={(e) => setSelectedCourseId(e.target.value)}\n className=\"w-full rounded-lg border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n >\n <option value=\"\">Choose a course…</option>\n {courses.map((c) => (\n <option key={c.id} value={c.id}>\n {c.title}\n </option>\n ))}\n </select>\n </div>\n }\n >\n {!selectedCourseId ? (\n <EmptyState\n icon={<ClipboardCheck size={40} />}\n title=\"Select a course\"\n description=\"Choose a published course above to view its assessments.\"\n />\n ) : assessmentsLoading ? (\n <LoadingView label=\"Loading assessments…\" />\n ) : assessmentsError ? (\n <ErrorView title=\"Could not load assessments\" message={assessmentsError.message} />\n ) : assessments?.length === 0 ? (\n <EmptyState\n icon={<ClipboardCheck size={40} />}\n title=\"No assessments\"\n description=\"This course has no assessments yet.\"\n />\n ) : (\n <DataTable\n columns={columns}\n rows={assessments ?? []}\n rowKey={(a) => a.id}\n defaultSort={{ key: 'title', dir: 'asc' }}\n />\n )}\n </Section>\n );\n}\n\n// ============================================================================\n// Enrollment overview\n// ============================================================================\n\nfunction EnrollmentsPanel() {\n const [status, setStatus] = useState<'ALL' | BuiEnrollmentStatus>('ALL');\n const [page, setPage] = useState(0);\n const [cursors, setCursors] = useState<string[]>([]);\n\n const cursor = page > 0 ? cursors[page - 1] : undefined;\n const { data, isLoading, error, refetch } = useBuiEnrollments({\n status: status === 'ALL' ? undefined : status,\n cursor,\n limit: PAGE_SIZE,\n });\n\n const columns = useMemo<Column<BuiEnrollment>[]>(\n () => [\n {\n key: 'learner',\n header: 'Learner',\n render: (e) => <span className=\"font-mono text-xs text-text-secondary\">{e.learnerId}</span>,\n },\n {\n key: 'course',\n header: 'Course ID',\n render: (e) => <span className=\"font-mono text-xs text-text-secondary\">{e.courseId}</span>,\n },\n {\n key: 'status',\n header: 'Status',\n render: (e) => <StatusBadge status={e.status} />,\n },\n {\n key: 'progress',\n header: 'Progress',\n align: 'right',\n render: (e) => formatPercent(e.progressPct),\n sortValue: (e) => e.progressPct,\n },\n {\n key: 'cohort',\n header: 'Cohort',\n render: (e) => e.cohort?.name ?? '—',\n },\n {\n key: 'enrolled',\n header: 'Enrolled',\n render: (e) => formatDateTime(e.enrolledAt),\n sortValue: (e) => new Date(e.enrolledAt).getTime(),\n hideOnMobile: true,\n },\n {\n key: 'lastActive',\n header: 'Last Active',\n render: (e) => (e.lastActiveAt ? formatRelativeTime(e.lastActiveAt) : '—'),\n sortValue: (e) => (e.lastActiveAt ? new Date(e.lastActiveAt).getTime() : 0),\n hideOnMobile: true,\n },\n ],\n []\n );\n\n const handlePageChange = (nextPage: number) => {\n if (nextPage > page && data?.pageInfo.cursor) {\n setCursors((prev) => [...prev, data.pageInfo.cursor as string]);\n } else if (nextPage < page) {\n setCursors((prev) => prev.slice(0, -1));\n }\n setPage(nextPage);\n };\n\n if (isLoading) return <LoadingView label=\"Loading enrollments…\" />;\n if (error)\n return (\n <ErrorView title=\"Could not load enrollments\" message={error.message} onRetry={refetch} />\n );\n\n const rows = data?.items ?? [];\n\n return (\n <Section title=\"Enrollments\" description=\"Workspace-wide learner enrollment overview.\">\n <div className=\"mb-4\">\n <FilterTabs\n options={ENROLLMENT_STATUS_OPTIONS}\n value={status}\n onChange={(v) => {\n setStatus(v);\n setPage(0);\n setCursors([]);\n }}\n />\n </div>\n {rows.length === 0 ? (\n <EmptyState\n icon={<Users size={40} />}\n title=\"No enrollments\"\n description={`There are no ${status === 'ALL' ? '' : status.toLowerCase()} enrollments in this workspace.`}\n />\n ) : (\n <>\n <DataTable\n columns={columns}\n rows={rows}\n rowKey={(e) => e.id}\n defaultSort={{ key: 'enrolled', dir: 'desc' }}\n />\n <Pagination\n page={page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={data?.pageInfo.hasMore ?? false}\n onPageChange={handlePageChange}\n />\n </>\n )}\n </Section>\n );\n}\n\n// Need formatRelativeTime for lastActive in enrollments; import it.\nfunction formatRelativeTime(date: string | Date): string {\n const d = typeof date === 'string' ? new Date(date) : date;\n const now = new Date();\n const diffMs = d.getTime() - now.getTime();\n const future = diffMs >= 0;\n const absSeconds = Math.floor(Math.abs(diffMs) / 1000);\n const minutes = Math.floor(absSeconds / 60);\n const hours = Math.floor(minutes / 60);\n const days = Math.floor(hours / 24);\n const phrase = (value: number, unit: string) => {\n const label = `${value} ${unit}${value !== 1 ? 's' : ''}`;\n return future ? `in ${label}` : `${label} ago`;\n };\n if (absSeconds < 60) return future ? 'soon' : 'just now';\n if (minutes < 60) return phrase(minutes, 'min');\n if (hours < 24) return phrase(hours, 'hour');\n return phrase(days, 'day');\n}\n\n// ============================================================================\n// Submission review\n// ============================================================================\n\nfunction SubmissionsPanel() {\n const [status, setStatus] = useState<'ALL' | BuiSubmissionStatus>('ALL');\n const [page, setPage] = useState(0);\n const [cursors, setCursors] = useState<string[]>([]);\n\n const cursor = page > 0 ? cursors[page - 1] : undefined;\n const { data, isLoading, error, refetch } = useBuiSubmissions({\n status: status === 'ALL' ? undefined : status,\n cursor,\n limit: PAGE_SIZE,\n });\n\n const columns = useMemo<Column<BuiSubmission>[]>(\n () => [\n {\n key: 'learner',\n header: 'Learner',\n render: (s) => <span className=\"font-mono text-xs text-text-secondary\">{s.learnerId}</span>,\n },\n {\n key: 'assessment',\n header: 'Assessment ID',\n render: (s) => (\n <span className=\"font-mono text-xs text-text-secondary\">{s.assessmentId}</span>\n ),\n },\n {\n key: 'attempt',\n header: 'Attempt',\n align: 'right',\n render: (s) => formatNumber(s.attempt),\n },\n {\n key: 'status',\n header: 'Status',\n render: (s) => <StatusBadge status={s.status} />,\n },\n {\n key: 'score',\n header: 'Score',\n align: 'right',\n render: (s) => (s.score == null ? '—' : formatPercent(s.score)),\n sortValue: (s) => s.score ?? -1,\n },\n {\n key: 'passed',\n header: 'Passed',\n align: 'center',\n render: (s) => (s.passed == null ? '—' : s.passed ? 'Yes' : 'No'),\n },\n {\n key: 'submitted',\n header: 'Submitted',\n render: (s) => (s.submittedAt ? formatDateTime(s.submittedAt) : '—'),\n sortValue: (s) => (s.submittedAt ? new Date(s.submittedAt).getTime() : 0),\n hideOnMobile: true,\n },\n {\n key: 'actions',\n header: 'Review',\n align: 'right',\n render: () => (\n <Button variant=\"ghost\" size=\"sm\" disabled>\n Open\n </Button>\n ),\n },\n ],\n []\n );\n\n const handlePageChange = (nextPage: number) => {\n if (nextPage > page && data?.pageInfo.cursor) {\n setCursors((prev) => [...prev, data.pageInfo.cursor as string]);\n } else if (nextPage < page) {\n setCursors((prev) => prev.slice(0, -1));\n }\n setPage(nextPage);\n };\n\n if (isLoading) return <LoadingView label=\"Loading submissions…\" />;\n if (error)\n return (\n <ErrorView title=\"Could not load submissions\" message={error.message} onRetry={refetch} />\n );\n\n const rows = data?.items ?? [];\n\n return (\n <Section title=\"Submission review\" description=\"Review learner submissions and grading status.\">\n <div className=\"mb-4\">\n <FilterTabs\n options={SUBMISSION_STATUS_OPTIONS}\n value={status}\n onChange={(v) => {\n setStatus(v);\n setPage(0);\n setCursors([]);\n }}\n />\n </div>\n {rows.length === 0 ? (\n <EmptyState\n icon={<CheckSquare size={40} />}\n title=\"No submissions\"\n description={`There are no ${status === 'ALL' ? '' : status.toLowerCase()} submissions in this workspace.`}\n />\n ) : (\n <>\n <DataTable\n columns={columns}\n rows={rows}\n rowKey={(s) => s.id}\n defaultSort={{ key: 'submitted', dir: 'desc' }}\n />\n <Pagination\n page={page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={data?.pageInfo.hasMore ?? false}\n onPageChange={handlePageChange}\n />\n </>\n )}\n </Section>\n );\n}\n\n// ============================================================================\n// Root\n// ============================================================================\n\nfunction BuildmyiqAdminDashboard() {\n const [activeTab, setActiveTab] = useState<AdminTab>('courses');\n\n const tabs = useMemo(\n () => [\n { value: 'courses' as const, label: 'Courses', icon: <BookOpen size={18} /> },\n { value: 'assessments' as const, label: 'Assessments', icon: <ClipboardCheck size={18} /> },\n { value: 'enrollments' as const, label: 'Enrollments', icon: <Users size={18} /> },\n { value: 'submissions' as const, label: 'Submissions', icon: <CheckSquare size={18} /> },\n ],\n []\n );\n\n return (\n <PageLayout\n title=\"BuildMyIQ Admin\"\n description=\"Course, assessment, enrollment and submission governance.\"\n actions={\n <span className=\"inline-flex items-center gap-2 text-sm text-text-secondary\">\n <GraduationCap size={18} />\n Workspace-scoped\n </span>\n }\n >\n <Tabs items={tabs} value={activeTab} onChange={(v) => setActiveTab(v)} />\n <div className=\"mt-6\">\n {activeTab === 'courses' && <CoursesPanel />}\n {activeTab === 'assessments' && <AssessmentsPanel />}\n {activeTab === 'enrollments' && <EnrollmentsPanel />}\n {activeTab === 'submissions' && <SubmissionsPanel />}\n </div>\n </PageLayout>\n );\n}\n\nexport const BuildmyiqAdminRoot: FC<BuildmyiqAdminRootProps> = (props) => {\n // Strip the admin-app workspace token before passing props to the BUI\n // provider; the BUI hooks use the shell's workspace-token provider instead.\n const providerProps: BuildmyiqRootProps = props;\n\n return (\n <QueryClientProvider client={queryClient}>\n <FeatureFlagProvider\n workspaceId={props.workspaceId ?? null}\n gateway=\"global\"\n defaultEnabled={true}\n >\n <BuildmyiqProvider {...providerProps}>\n <BuildmyiqAdminDashboard />\n </BuildmyiqProvider>\n </FeatureFlagProvider>\n </QueryClientProvider>\n );\n};\n\nexport { BuildmyiqAdminRoot as BuildMyIQAdminRoot };\n"],"mappings":"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"}
|