@burdenoff/microfe-buildmyiq 2026.705.13 → 2026.706.10

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 (67) hide show
  1. package/dist/buildmyiq/BuildmyiqRoot.d.ts.map +1 -1
  2. package/dist/buildmyiq/BuildmyiqRoot.js +57 -49
  3. package/dist/buildmyiq/BuildmyiqRoot.js.map +1 -1
  4. package/dist/buildmyiq/BuildmyiqRoutes.d.ts.map +1 -1
  5. package/dist/buildmyiq/BuildmyiqRoutes.js +15 -11
  6. package/dist/buildmyiq/BuildmyiqRoutes.js.map +1 -1
  7. package/dist/buildmyiq/components/Avatar.d.ts +15 -0
  8. package/dist/buildmyiq/components/Avatar.d.ts.map +1 -0
  9. package/dist/buildmyiq/components/Avatar.js +42 -0
  10. package/dist/buildmyiq/components/Avatar.js.map +1 -0
  11. package/dist/buildmyiq/components/Viz.d.ts +25 -0
  12. package/dist/buildmyiq/components/Viz.d.ts.map +1 -0
  13. package/dist/buildmyiq/components/Viz.js +78 -0
  14. package/dist/buildmyiq/components/Viz.js.map +1 -0
  15. package/dist/buildmyiq/components/index.d.ts +2 -0
  16. package/dist/buildmyiq/components/index.d.ts.map +1 -1
  17. package/dist/buildmyiq/components/index.js +2 -0
  18. package/dist/buildmyiq/pages/DashboardPage.d.ts.map +1 -1
  19. package/dist/buildmyiq/pages/DashboardPage.js +200 -98
  20. package/dist/buildmyiq/pages/DashboardPage.js.map +1 -1
  21. package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.d.ts.map +1 -1
  22. package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.js +262 -241
  23. package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.js.map +1 -1
  24. package/dist/buildmyiq/pages/assessments/AssessmentsPage.d.ts.map +1 -1
  25. package/dist/buildmyiq/pages/assessments/AssessmentsPage.js +142 -115
  26. package/dist/buildmyiq/pages/assessments/AssessmentsPage.js.map +1 -1
  27. package/dist/buildmyiq/pages/assessments/TakeAssessmentPage.d.ts.map +1 -1
  28. package/dist/buildmyiq/pages/assessments/TakeAssessmentPage.js +220 -156
  29. package/dist/buildmyiq/pages/assessments/TakeAssessmentPage.js.map +1 -1
  30. package/dist/buildmyiq/pages/authoring/AuthoringPage.d.ts.map +1 -1
  31. package/dist/buildmyiq/pages/authoring/AuthoringPage.js +51 -46
  32. package/dist/buildmyiq/pages/authoring/AuthoringPage.js.map +1 -1
  33. package/dist/buildmyiq/pages/authoring/CourseBuilderPage.d.ts.map +1 -1
  34. package/dist/buildmyiq/pages/authoring/CourseBuilderPage.js +581 -448
  35. package/dist/buildmyiq/pages/authoring/CourseBuilderPage.js.map +1 -1
  36. package/dist/buildmyiq/pages/catalog/CatalogPage.d.ts.map +1 -1
  37. package/dist/buildmyiq/pages/catalog/CatalogPage.js +52 -42
  38. package/dist/buildmyiq/pages/catalog/CatalogPage.js.map +1 -1
  39. package/dist/buildmyiq/pages/catalog/CourseDetailPage.d.ts.map +1 -1
  40. package/dist/buildmyiq/pages/catalog/CourseDetailPage.js +422 -301
  41. package/dist/buildmyiq/pages/catalog/CourseDetailPage.js.map +1 -1
  42. package/dist/buildmyiq/pages/certificates/CertificatesPage.d.ts.map +1 -1
  43. package/dist/buildmyiq/pages/certificates/CertificatesPage.js +237 -151
  44. package/dist/buildmyiq/pages/certificates/CertificatesPage.js.map +1 -1
  45. package/dist/buildmyiq/pages/cohorts/CohortsPage.d.ts.map +1 -1
  46. package/dist/buildmyiq/pages/cohorts/CohortsPage.js +219 -138
  47. package/dist/buildmyiq/pages/cohorts/CohortsPage.js.map +1 -1
  48. package/dist/buildmyiq/pages/insights/InsightsPage.d.ts.map +1 -1
  49. package/dist/buildmyiq/pages/insights/InsightsPage.js +276 -152
  50. package/dist/buildmyiq/pages/insights/InsightsPage.js.map +1 -1
  51. package/dist/buildmyiq/pages/learning/LessonPlayerPage.d.ts.map +1 -1
  52. package/dist/buildmyiq/pages/learning/LessonPlayerPage.js +237 -174
  53. package/dist/buildmyiq/pages/learning/LessonPlayerPage.js.map +1 -1
  54. package/dist/buildmyiq/pages/learning/MyLearningPage.d.ts.map +1 -1
  55. package/dist/buildmyiq/pages/learning/MyLearningPage.js +307 -169
  56. package/dist/buildmyiq/pages/learning/MyLearningPage.js.map +1 -1
  57. package/dist/buildmyiq/pages/paths/PathsPage.d.ts +4 -0
  58. package/dist/buildmyiq/pages/paths/PathsPage.d.ts.map +1 -0
  59. package/dist/buildmyiq/pages/paths/PathsPage.js +187 -0
  60. package/dist/buildmyiq/pages/paths/PathsPage.js.map +1 -0
  61. package/dist/buildmyiq/pages/skills/SkillsPage.d.ts.map +1 -1
  62. package/dist/buildmyiq/pages/skills/SkillsPage.js +233 -140
  63. package/dist/buildmyiq/pages/skills/SkillsPage.js.map +1 -1
  64. package/dist/buildmyiq/pages/submissions/SubmissionsPage.d.ts.map +1 -1
  65. package/dist/buildmyiq/pages/submissions/SubmissionsPage.js +379 -275
  66. package/dist/buildmyiq/pages/submissions/SubmissionsPage.js.map +1 -1
  67. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"SubmissionsPage.js","names":[],"sources":["../../../../src/buildmyiq/pages/submissions/SubmissionsPage.tsx"],"sourcesContent":["/**\n * SubmissionsPage — the workspace-wide grading queue.\n * @module buildmyiq/pages/submissions\n *\n * The educator surface for assessment submissions. Reads every submission in the\n * workspace via `useBuiSubmissions` (no `assessmentId` → all assessments),\n * filterable by status, and paginated by cursor. Clicking a row opens a grading\n * drawer that shows the learner's answers read-only (rendered safely from the\n * `answers` JSON blob) and — when the submission is awaiting grading — a grading\n * form (`useBuiGradeSubmission`: score, passed, feedback). Already-graded rows\n * show their result read-only with a \"Re-grade\" affordance.\n *\n * Every number is derived from live `bui*` data; the KPI row is explicitly scoped\n * to the current page (\"this page\") so it never overstates workspace totals.\n */\n\nimport { useMemo, useState } from 'react';\nimport type { FC, ReactNode } from 'react';\nimport { ClipboardList, Clock, CheckCircle2, Undo2, GraduationCap } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Switch,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n StatusBadge,\n DataTable,\n FilterTabs,\n Pagination,\n EmptyState,\n LoadingView,\n ErrorView,\n type Column,\n type FilterOption,\n} from '../../components';\nimport { useBuildmyiq } from '../../context';\nimport { useBuiSubmissions, useBuiGradeSubmission, useCursorPager } from '../../hooks';\nimport { formatDateTime } from '../../utils/formatters';\nimport type { BuiSubmission, BuiSubmissionStatus } from '../../types';\n\nconst PAGE_SIZE = 24;\n\ntype StatusFilter = BuiSubmissionStatus | 'ALL';\n\nconst STATUS_FILTERS: FilterOption<StatusFilter>[] = [\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/** Truncate an opaque id for compact display, keeping the full value in `title`. */\nconst shortId = (id: string): string => (id.length > 10 ? `${id.slice(0, 8)}…` : id);\n\nconst isPlainRecord = (v: unknown): v is Record<string, unknown> =>\n typeof v === 'object' && v !== null && !Array.isArray(v);\n\n/** Render a single answer value safely, whatever its JSON shape. */\nconst formatAnswerValue = (v: unknown): string => {\n if (v == null) return '—';\n if (typeof v === 'string') return v;\n if (typeof v === 'number' || typeof v === 'boolean') return String(v);\n return JSON.stringify(v, null, 2);\n};\n\n/** Read-only rendering of a submission's `answers` JSON blob. */\nconst AnswersView: FC<{ answers: unknown }> = ({ answers }) => {\n if (answers == null) {\n return <p className=\"text-sm text-text-secondary\">No answers were submitted.</p>;\n }\n if (isPlainRecord(answers)) {\n const entries = Object.entries(answers);\n if (entries.length === 0) {\n return <p className=\"text-sm text-text-secondary\">No answers were submitted.</p>;\n }\n return (\n <dl className=\"divide-y divide-border-seam/60\">\n {entries.map(([key, value]) => (\n <div key={key} className=\"grid gap-1 py-2 sm:grid-cols-3 sm:gap-3\">\n <dt className=\"break-words font-mono text-xs text-text-secondary sm:col-span-1\">\n {key}\n </dt>\n <dd className=\"whitespace-pre-wrap break-words text-sm text-text-primary sm:col-span-2\">\n {formatAnswerValue(value)}\n </dd>\n </div>\n ))}\n </dl>\n );\n }\n return (\n <pre className=\"overflow-x-auto whitespace-pre-wrap break-words text-xs text-text-primary\">\n {JSON.stringify(answers, null, 2)}\n </pre>\n );\n};\n\n/** A labelled read-only field for the submission meta / graded-result panels. */\nconst MetaField: FC<{ label: string; children: ReactNode }> = ({ label, children }) => (\n <div className=\"min-w-0\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">{label}</p>\n <div className=\"mt-0.5 text-sm text-text-primary\">{children}</div>\n </div>\n);\n\nexport const SubmissionsPage: FC = () => {\n const { currentUser } = useBuildmyiq();\n const [statusFilter, setStatusFilter] = useState<StatusFilter>('ALL');\n const pager = useCursorPager();\n\n const [selected, setSelected] = useState<BuiSubmission | null>(null);\n const [editMode, setEditMode] = useState(false);\n const [scoreInput, setScoreInput] = useState('');\n const [passed, setPassed] = useState(false);\n const [feedback, setFeedback] = useState('');\n const [formError, setFormError] = useState<string | null>(null);\n\n const { data, isLoading, isError, refetch } = useBuiSubmissions({\n status: statusFilter === 'ALL' ? undefined : statusFilter,\n cursor: pager.cursor,\n limit: PAGE_SIZE,\n });\n const gradeSubmission = useBuiGradeSubmission();\n\n const rows = useMemo<BuiSubmission[]>(() => data?.items ?? [], [data]);\n\n // Counts are scoped to the loaded page — labelled \"this page\" to stay honest.\n const counts = useMemo(() => {\n const acc = { total: rows.length, awaiting: 0, graded: 0, returned: 0 };\n for (const s of rows) {\n if (s.status === 'SUBMITTED' || s.status === 'GRADING') acc.awaiting += 1;\n else if (s.status === 'GRADED') acc.graded += 1;\n else if (s.status === 'RETURNED') acc.returned += 1;\n }\n return acc;\n }, [rows]);\n\n const openGrade = (s: BuiSubmission) => {\n setSelected(s);\n setScoreInput(s.score != null ? String(s.score) : '');\n setPassed(s.passed ?? false);\n setFeedback(s.feedback ?? '');\n setEditMode(false);\n setFormError(null);\n };\n\n const closeModal = () => {\n setSelected(null);\n setEditMode(false);\n setFormError(null);\n };\n\n const handleFilterChange = (value: StatusFilter) => {\n setStatusFilter(value);\n pager.reset();\n };\n\n const handleGrade = async () => {\n if (!selected) return;\n const trimmed = scoreInput.trim();\n const scoreNum = Number(trimmed);\n if (trimmed === '' || Number.isNaN(scoreNum)) {\n setFormError('Enter a numeric score.');\n return;\n }\n if (scoreNum < 0) {\n setFormError('Score cannot be negative.');\n return;\n }\n try {\n await gradeSubmission.mutateAsync({\n id: selected.id,\n input: { score: scoreNum, passed, feedback: feedback.trim() || undefined },\n });\n closeModal();\n } catch (err) {\n setFormError(err instanceof Error ? err.message : 'Failed to save grade.');\n }\n };\n\n const gradingAllowed =\n selected != null && (selected.status === 'SUBMITTED' || selected.status === 'GRADING');\n const alreadyGraded =\n selected != null && (selected.status === 'GRADED' || selected.status === 'RETURNED');\n const notSubmitted = selected != null && selected.status === 'IN_PROGRESS';\n const showGradeForm = selected != null && (gradingAllowed || editMode);\n\n const columns: Column<BuiSubmission>[] = [\n {\n key: 'learner',\n header: 'Learner',\n sortValue: (s) => s.learnerId,\n render: (s) =>\n s.learnerId === currentUser?.id ? (\n <span className=\"font-medium text-text-primary\">You</span>\n ) : (\n <span className=\"font-mono text-xs text-text-secondary\" title={s.learnerId}>\n {shortId(s.learnerId)}\n </span>\n ),\n },\n {\n key: 'assessment',\n header: 'Assessment',\n hideOnMobile: true,\n render: (s) => (\n <span className=\"font-mono text-xs text-text-secondary\" title={s.assessmentId}>\n {shortId(s.assessmentId)}\n </span>\n ),\n },\n {\n key: 'attempt',\n header: 'Attempt',\n align: 'right',\n hideOnMobile: true,\n sortValue: (s) => s.attempt,\n render: (s) => <span className=\"tabular-nums text-text-secondary\">#{s.attempt}</span>,\n },\n {\n key: 'status',\n header: 'Status',\n sortValue: (s) => s.status,\n render: (s) => <StatusBadge status={s.status} />,\n },\n {\n key: 'score',\n header: 'Score',\n align: 'right',\n sortValue: (s) => s.score ?? -1,\n render: (s) => (\n <span className=\"tabular-nums font-medium text-text-primary\">{s.score ?? '—'}</span>\n ),\n },\n {\n key: 'submittedAt',\n header: 'Submitted',\n hideOnMobile: true,\n sortValue: (s) => s.submittedAt ?? s.createdAt,\n render: (s) => (\n <span className=\"whitespace-nowrap text-text-secondary\">\n {formatDateTime(s.submittedAt)}\n </span>\n ),\n },\n ];\n\n return (\n <PageLayout\n title=\"Grading\"\n description=\"Review and grade assessment submissions across every course in your workspace.\"\n titleGradient\n aurora\n >\n <div className=\"flex flex-col gap-6\">\n {/* KPI row — scoped to the current page. */}\n <div className=\"grid grid-cols-2 gap-4 xl:grid-cols-4\">\n <MetricsCard\n icon={<ClipboardList className=\"h-5 w-5\" />}\n value={counts.total}\n label=\"Submissions (this page)\"\n featured\n />\n <MetricsCard\n icon={<Clock className=\"h-5 w-5\" />}\n value={counts.awaiting}\n label=\"Awaiting grade (this page)\"\n accent=\"warning\"\n />\n <MetricsCard\n icon={<CheckCircle2 className=\"h-5 w-5\" />}\n value={counts.graded}\n label=\"Graded (this page)\"\n accent=\"success\"\n />\n <MetricsCard\n icon={<Undo2 className=\"h-5 w-5\" />}\n value={counts.returned}\n label=\"Returned (this page)\"\n />\n </div>\n\n <FilterTabs options={STATUS_FILTERS} value={statusFilter} onChange={handleFilterChange} />\n\n {isLoading ? (\n <LoadingView label=\"Loading submissions…\" />\n ) : isError ? (\n <ErrorView\n title=\"Couldn't load submissions\"\n message=\"The grading queue failed to load.\"\n onRetry={() => refetch()}\n />\n ) : rows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<GraduationCap className=\"h-10 w-10\" />}\n title={statusFilter === 'ALL' ? 'No submissions yet' : 'Nothing in this status'}\n description={\n statusFilter === 'ALL'\n ? 'When learners submit assessments, their attempts will appear here for grading.'\n : 'No submissions currently match this status filter.'\n }\n />\n ) : (\n <div>\n <DataTable\n columns={columns}\n rows={rows}\n rowKey={(s) => s.id}\n onRowClick={openGrade}\n defaultSort={{ key: 'submittedAt', dir: 'desc' }}\n />\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={data?.pageInfo?.hasMore ?? false}\n onPageChange={(target) => pager.onPageChange(target, data?.pageInfo?.cursor)}\n />\n </div>\n )}\n </div>\n\n <ResponsiveDialog\n open={selected != null}\n onOpenChange={(open) => {\n if (!open) closeModal();\n }}\n >\n <ResponsiveDialogContent className=\"max-h-[88vh] overflow-y-auto sm:max-w-[640px]\">\n {selected && (\n <>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {gradingAllowed || editMode ? 'Grade submission' : 'Submission details'}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {selected.learnerId === currentUser?.id\n ? 'Your attempt'\n : `Learner ${shortId(selected.learnerId)}`}{' '}\n · attempt #{selected.attempt}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"grid gap-5 py-2\">\n {/* Meta */}\n <div className=\"grid grid-cols-2 gap-4 rounded-xl bg-bg-sunken/60 p-4 sm:grid-cols-3\">\n <MetaField label=\"Status\">\n <StatusBadge status={selected.status} />\n </MetaField>\n <MetaField label=\"Attempt\">\n <span className=\"tabular-nums\">#{selected.attempt}</span>\n </MetaField>\n <MetaField label=\"Submitted\">{formatDateTime(selected.submittedAt)}</MetaField>\n <MetaField label=\"Assessment\">\n <span className=\"font-mono text-xs\" title={selected.assessmentId}>\n {shortId(selected.assessmentId)}\n </span>\n </MetaField>\n {selected.enrollmentId && (\n <MetaField label=\"Enrollment\">\n <span className=\"font-mono text-xs\" title={selected.enrollmentId}>\n {shortId(selected.enrollmentId)}\n </span>\n </MetaField>\n )}\n {selected.gradedAt && (\n <MetaField label=\"Graded\">{formatDateTime(selected.gradedAt)}</MetaField>\n )}\n </div>\n\n {/* Learner answers (read-only) */}\n <div>\n <p className=\"mb-2 text-sm font-semibold text-text-primary\">Answers</p>\n <div className=\"max-h-64 overflow-y-auto rounded-xl border border-border-seam p-4\">\n <AnswersView answers={selected.answers} />\n </div>\n </div>\n\n {/* Not-yet-submitted note */}\n {notSubmitted && (\n <p className=\"rounded-lg bg-bg-sunken/60 px-4 py-3 text-sm text-text-secondary\">\n This attempt is still in progress — there is nothing to grade until the learner\n submits.\n </p>\n )}\n\n {/* Existing grade (read-only) with a re-grade affordance */}\n {alreadyGraded && !editMode && (\n <div className=\"rounded-xl border border-border-seam p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"grid grid-cols-2 gap-4\">\n <MetaField label=\"Score\">\n <span className=\"tabular-nums font-semibold\">\n {selected.score ?? '—'}\n </span>\n </MetaField>\n <MetaField label=\"Result\">\n {selected.passed == null ? (\n <span className=\"text-text-secondary\">—</span>\n ) : (\n <StatusBadge status={selected.passed ? 'GRADED' : 'RETURNED'} />\n )}\n </MetaField>\n </div>\n <Button variant=\"outline\" size=\"sm\" onClick={() => setEditMode(true)}>\n Re-grade\n </Button>\n </div>\n <div className=\"mt-3\">\n <MetaField label=\"Feedback\">\n {selected.feedback ? (\n <span className=\"whitespace-pre-wrap\">{selected.feedback}</span>\n ) : (\n <span className=\"text-text-secondary\">No feedback provided.</span>\n )}\n </MetaField>\n </div>\n </div>\n )}\n\n {/* Grading form */}\n {showGradeForm && (\n <div className=\"grid gap-4\">\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"grade-score\">Score</Label>\n <Input\n id=\"grade-score\"\n type=\"number\"\n min=\"0\"\n step=\"1\"\n value={scoreInput}\n onChange={(e) => setScoreInput(e.target.value)}\n placeholder=\"e.g. 85\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"grade-passed\">Result</Label>\n <div className=\"flex h-9 items-center gap-3\">\n <Switch id=\"grade-passed\" checked={passed} onCheckedChange={setPassed} />\n <span className=\"text-sm text-text-secondary\">\n {passed ? 'Passed' : 'Not passed'}\n </span>\n </div>\n </div>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"grade-feedback\">Feedback</Label>\n <Textarea\n id=\"grade-feedback\"\n value={feedback}\n onChange={(e) => setFeedback(e.target.value)}\n placeholder=\"Notes, corrections or encouragement for the learner (optional).\"\n rows={3}\n />\n </div>\n {formError && <p className=\"text-sm text-status-error-text\">{formError}</p>}\n </div>\n )}\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={closeModal}>\n {showGradeForm ? 'Cancel' : 'Close'}\n </Button>\n {showGradeForm && (\n <Button onClick={handleGrade} disabled={gradeSubmission.isPending}>\n {gradeSubmission.isPending\n ? 'Saving…'\n : alreadyGraded\n ? 'Update grade'\n : 'Save grade'}\n </Button>\n )}\n </ResponsiveDialogFooter>\n </>\n )}\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"SubmissionsPage.js","names":[],"sources":["../../../../src/buildmyiq/pages/submissions/SubmissionsPage.tsx"],"sourcesContent":["/**\n * SubmissionsPage — the workspace-wide grading queue.\n * @module buildmyiq/pages/submissions\n *\n * The educator surface for assessment submissions. Reads every submission in the\n * workspace via `useBuiSubmissions` (no `assessmentId` → all assessments),\n * filterable by status, and paginated by cursor. Clicking a row opens a grading\n * slide-over — a right-hand panel on desktop, a bottom-sheet on mobile — that\n * shows the learner's answers read-only (rendered safely from the `answers` JSON\n * blob) under a Submission / Details tab pair, plus a grading form\n * (`useBuiGradeSubmission`: score, passed, feedback). Already-graded rows show\n * their result read-only with a \"Re-grade\" affordance.\n *\n * Every number is derived from live `bui*` data; the KPI row is explicitly scoped\n * to the current page (\"this page\") so it never overstates workspace totals.\n * Learner identity is shown honestly: the signed-in educator's own attempts get\n * their real name + \"You\"; everyone else is a deterministic initials avatar keyed\n * off the opaque learner id — never a fabricated name or stock photo.\n */\n\nimport { useMemo, useState } from 'react';\nimport type { FC, ReactNode } from 'react';\nimport {\n ClipboardList,\n Clock,\n CheckCircle2,\n Undo2,\n GraduationCap,\n BookOpen,\n} from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Switch,\n Sheet,\n SheetContent,\n SheetHeader,\n SheetTitle,\n SheetDescription,\n SheetFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { useMediaQuery } from '@burdenoff/fe-libs/shared/hooks';\nimport {\n PageLayout,\n MetricsCard,\n StatusBadge,\n DataTable,\n Tabs,\n Pagination,\n EmptyState,\n LoadingView,\n ErrorView,\n Avatar,\n type Column,\n type TabItem,\n} from '../../components';\nimport { useBuildmyiq } from '../../context';\nimport { useBuiSubmissions, useBuiGradeSubmission, useCursorPager } from '../../hooks';\nimport { formatDate, formatDateTime } from '../../utils/formatters';\nimport type { BuiSubmission, BuiSubmissionStatus } from '../../types';\n\nconst PAGE_SIZE = 24;\n\ntype StatusFilter = BuiSubmissionStatus | 'ALL';\ntype PanelTab = 'submission' | 'details';\n\nconst STATUS_TABS: TabItem<StatusFilter>[] = [\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\nconst PANEL_TABS: TabItem<PanelTab>[] = [\n { value: 'submission', label: 'Submission' },\n { value: 'details', label: 'Details' },\n];\n\n/** Truncate an opaque id for compact display, keeping the full value in `title`. */\nconst shortId = (id: string): string => (id.length > 10 ? `${id.slice(0, 8)}…` : id);\n\n/** Time-only rendering (e.g. \"10:24 AM\") reusing the locale-aware date formatter. */\nconst formatClock = (date: string | null | undefined): string =>\n formatDate(date, { day: undefined, month: undefined, year: undefined, hour: 'numeric', minute: '2-digit' });\n\nconst isPlainRecord = (v: unknown): v is Record<string, unknown> =>\n typeof v === 'object' && v !== null && !Array.isArray(v);\n\n/** Render a single answer value safely, whatever its JSON shape. */\nconst formatAnswerValue = (v: unknown): string => {\n if (v == null) return '—';\n if (typeof v === 'string') return v;\n if (typeof v === 'number' || typeof v === 'boolean') return String(v);\n return JSON.stringify(v, null, 2);\n};\n\n/** Read-only rendering of a submission's `answers` JSON blob. */\nconst AnswersView: FC<{ answers: unknown }> = ({ answers }) => {\n if (answers == null) {\n return <p className=\"text-sm text-text-secondary\">No answers were submitted.</p>;\n }\n if (isPlainRecord(answers)) {\n const entries = Object.entries(answers);\n if (entries.length === 0) {\n return <p className=\"text-sm text-text-secondary\">No answers were submitted.</p>;\n }\n return (\n <ol className=\"grid gap-2.5\">\n {entries.map(([key, value], i) => (\n <li\n key={key}\n className=\"grid gap-1 rounded-lg border border-border-seam/70 bg-bg-surface/60 p-3 sm:grid-cols-[minmax(0,7rem)_1fr] sm:gap-3\"\n >\n <div className=\"flex items-baseline gap-2 sm:col-span-1\">\n <span className=\"shrink-0 text-xs font-semibold text-[var(--color-accent-teal)] tabular-nums\">\n Q{i + 1}\n </span>\n <span className=\"break-words font-mono text-xs text-text-secondary\">{key}</span>\n </div>\n <span className=\"whitespace-pre-wrap break-words text-sm text-text-primary sm:col-span-1\">\n {formatAnswerValue(value)}\n </span>\n </li>\n ))}\n </ol>\n );\n }\n return (\n <pre className=\"overflow-x-auto whitespace-pre-wrap break-words rounded-lg border border-border-seam/70 bg-bg-surface/60 p-3 text-xs text-text-primary\">\n {JSON.stringify(answers, null, 2)}\n </pre>\n );\n};\n\n/** A labelled read-only field for the submission meta / graded-result panels. */\nconst MetaField: FC<{ label: string; children: ReactNode }> = ({ label, children }) => (\n <div className=\"min-w-0\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">{label}</p>\n <div className=\"mt-0.5 text-sm text-text-primary\">{children}</div>\n </div>\n);\n\nexport const SubmissionsPage: FC = () => {\n const { currentUser } = useBuildmyiq();\n const isDesktop = useMediaQuery('(min-width: 768px)');\n const [statusFilter, setStatusFilter] = useState<StatusFilter>('ALL');\n const pager = useCursorPager();\n\n const [selected, setSelected] = useState<BuiSubmission | null>(null);\n const [panelTab, setPanelTab] = useState<PanelTab>('submission');\n const [editMode, setEditMode] = useState(false);\n const [scoreInput, setScoreInput] = useState('');\n const [passed, setPassed] = useState(false);\n const [feedback, setFeedback] = useState('');\n const [formError, setFormError] = useState<string | null>(null);\n\n const { data, isLoading, isError, refetch } = useBuiSubmissions({\n status: statusFilter === 'ALL' ? undefined : statusFilter,\n cursor: pager.cursor,\n limit: PAGE_SIZE,\n });\n const gradeSubmission = useBuiGradeSubmission();\n\n const rows = useMemo<BuiSubmission[]>(() => data?.items ?? [], [data]);\n\n // The signed-in educator's own display name (used only for their own rows).\n const meName = useMemo<string | undefined>(() => {\n if (!currentUser) return undefined;\n const full = [currentUser.firstName, currentUser.lastName].filter(Boolean).join(' ').trim();\n return currentUser.name?.trim() || full || currentUser.username || currentUser.email;\n }, [currentUser]);\n\n // Counts are scoped to the loaded page — labelled \"this page\" to stay honest.\n const counts = useMemo(() => {\n const acc = { total: rows.length, awaiting: 0, graded: 0, returned: 0 };\n for (const s of rows) {\n if (s.status === 'SUBMITTED' || s.status === 'GRADING') acc.awaiting += 1;\n else if (s.status === 'GRADED') acc.graded += 1;\n else if (s.status === 'RETURNED') acc.returned += 1;\n }\n return acc;\n }, [rows]);\n\n const isMe = (learnerId: string): boolean => learnerId === currentUser?.id;\n const learnerLabel = (learnerId: string): string =>\n isMe(learnerId) ? 'You' : `Learner ${shortId(learnerId)}`;\n\n const openGrade = (s: BuiSubmission) => {\n setSelected(s);\n setPanelTab('submission');\n setScoreInput(s.score != null ? String(s.score) : '');\n setPassed(s.passed ?? false);\n setFeedback(s.feedback ?? '');\n setEditMode(false);\n setFormError(null);\n };\n\n const closePanel = () => {\n setSelected(null);\n setEditMode(false);\n setFormError(null);\n };\n\n const handleFilterChange = (value: StatusFilter) => {\n setStatusFilter(value);\n pager.reset();\n };\n\n const handleGrade = async () => {\n if (!selected) return;\n const trimmed = scoreInput.trim();\n const scoreNum = Number(trimmed);\n if (trimmed === '' || Number.isNaN(scoreNum)) {\n setFormError('Enter a numeric score.');\n return;\n }\n if (scoreNum < 0) {\n setFormError('Score cannot be negative.');\n return;\n }\n try {\n await gradeSubmission.mutateAsync({\n id: selected.id,\n input: { score: scoreNum, passed, feedback: feedback.trim() || undefined },\n });\n closePanel();\n } catch (err) {\n setFormError(err instanceof Error ? err.message : 'Failed to save grade.');\n }\n };\n\n const gradingAllowed =\n selected != null && (selected.status === 'SUBMITTED' || selected.status === 'GRADING');\n const alreadyGraded =\n selected != null && (selected.status === 'GRADED' || selected.status === 'RETURNED');\n const notSubmitted = selected != null && selected.status === 'IN_PROGRESS';\n const showGradeForm = selected != null && (gradingAllowed || editMode);\n\n const columns: Column<BuiSubmission>[] = [\n {\n key: 'learner',\n header: 'Learner',\n sortValue: (s) => (isMe(s.learnerId) ? '' : s.learnerId),\n render: (s) => (\n <div className=\"flex items-center gap-3\">\n <Avatar\n size=\"sm\"\n name={isMe(s.learnerId) ? meName : undefined}\n seed={s.learnerId}\n src={isMe(s.learnerId) ? currentUser?.avatar : undefined}\n />\n <div className=\"min-w-0\">\n <span className=\"block truncate font-medium text-text-primary\" title={s.learnerId}>\n {isMe(s.learnerId) ? 'You' : shortId(s.learnerId)}\n </span>\n {isMe(s.learnerId) && meName && (\n <span className=\"block truncate text-xs text-text-secondary\">{meName}</span>\n )}\n </div>\n </div>\n ),\n },\n {\n key: 'assessment',\n header: 'Assessment',\n hideOnMobile: true,\n render: (s) => (\n <span className=\"inline-flex items-center gap-2 text-text-secondary\">\n <BookOpen className=\"h-4 w-4 shrink-0 opacity-70\" />\n <span className=\"font-mono text-xs\" title={s.assessmentId}>\n {shortId(s.assessmentId)}\n </span>\n </span>\n ),\n },\n {\n key: 'attempt',\n header: 'Attempt',\n align: 'right',\n hideOnMobile: true,\n sortValue: (s) => s.attempt,\n render: (s) => <span className=\"tabular-nums text-text-secondary\">#{s.attempt}</span>,\n },\n {\n key: 'status',\n header: 'Status',\n sortValue: (s) => s.status,\n render: (s) => <StatusBadge status={s.status} />,\n },\n {\n key: 'score',\n header: 'Score',\n align: 'right',\n sortValue: (s) => s.score ?? -1,\n render: (s) =>\n s.score == null ? (\n <span className=\"text-text-secondary\">—</span>\n ) : (\n <span className=\"tabular-nums font-semibold text-text-primary\">{s.score}</span>\n ),\n },\n {\n key: 'submittedAt',\n header: 'Submitted',\n align: 'right',\n hideOnMobile: true,\n sortValue: (s) => s.submittedAt ?? s.createdAt,\n render: (s) => (\n <div className=\"whitespace-nowrap text-right\">\n <div className=\"text-text-primary\">{formatDate(s.submittedAt)}</div>\n {s.submittedAt && (\n <div className=\"text-xs text-text-secondary\">{formatClock(s.submittedAt)}</div>\n )}\n </div>\n ),\n },\n ];\n\n return (\n <PageLayout\n title=\"Grading\"\n description=\"Review and grade assessment submissions across every course in your workspace.\"\n titleGradient\n aurora\n >\n <div className=\"flex flex-col gap-6\">\n {/* KPI row — scoped to the current page. */}\n <div className=\"grid grid-cols-2 gap-4 xl:grid-cols-4\">\n <MetricsCard\n icon={<ClipboardList className=\"h-5 w-5\" />}\n value={counts.total}\n label=\"Submissions (this page)\"\n featured\n />\n <MetricsCard\n icon={<Clock className=\"h-5 w-5\" />}\n value={counts.awaiting}\n label=\"Awaiting grade (this page)\"\n accent=\"warning\"\n />\n <MetricsCard\n icon={<CheckCircle2 className=\"h-5 w-5\" />}\n value={counts.graded}\n label=\"Graded (this page)\"\n accent=\"success\"\n />\n <MetricsCard\n icon={<Undo2 className=\"h-5 w-5\" />}\n value={counts.returned}\n label=\"Returned (this page)\"\n />\n </div>\n\n <Tabs items={STATUS_TABS} value={statusFilter} onChange={handleFilterChange} />\n\n {isLoading ? (\n <LoadingView label=\"Loading submissions…\" />\n ) : isError ? (\n <ErrorView\n title=\"Couldn't load submissions\"\n message=\"The grading queue failed to load.\"\n onRetry={() => refetch()}\n />\n ) : rows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<GraduationCap className=\"h-10 w-10\" />}\n title={statusFilter === 'ALL' ? 'No submissions yet' : 'Nothing in this status'}\n description={\n statusFilter === 'ALL'\n ? 'When learners submit assessments, their attempts will appear here for grading.'\n : 'No submissions currently match this status filter.'\n }\n />\n ) : (\n <div className=\"flex flex-col gap-4\">\n <DataTable\n columns={columns}\n rows={rows}\n rowKey={(s) => s.id}\n onRowClick={openGrade}\n defaultSort={{ key: 'submittedAt', dir: 'desc' }}\n />\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={data?.pageInfo?.hasMore ?? false}\n onPageChange={(target) => pager.onPageChange(target, data?.pageInfo?.cursor)}\n />\n </div>\n )}\n </div>\n\n {/* Grading slide-over — right panel on desktop, bottom-sheet on mobile. */}\n <Sheet\n open={selected != null}\n onOpenChange={(open) => {\n if (!open) closePanel();\n }}\n >\n <SheetContent\n side={isDesktop ? 'right' : 'bottom'}\n className={\n isDesktop\n ? 'flex w-full max-w-none flex-col gap-0 p-0 sm:max-w-xl'\n : 'flex max-h-[92vh] w-full flex-col gap-0 rounded-t-2xl p-0'\n }\n >\n {selected && (\n <>\n <SheetHeader className=\"border-b border-border-seam p-5 pr-12\">\n <div className=\"flex items-center gap-3\">\n <Avatar\n size=\"md\"\n name={isMe(selected.learnerId) ? meName : undefined}\n seed={selected.learnerId}\n src={isMe(selected.learnerId) ? currentUser?.avatar : undefined}\n />\n <div className=\"min-w-0\">\n <SheetTitle className=\"truncate text-lg\">\n {learnerLabel(selected.learnerId)}\n </SheetTitle>\n <SheetDescription\n className=\"truncate font-mono text-xs\"\n title={selected.assessmentId}\n >\n Assessment {shortId(selected.assessmentId)}\n </SheetDescription>\n </div>\n </div>\n <p className=\"text-xs text-text-secondary\">\n Attempt #{selected.attempt} · Submitted {formatDateTime(selected.submittedAt)}\n </p>\n <div className=\"pt-1\">\n <StatusBadge status={selected.status} />\n </div>\n </SheetHeader>\n\n <div className=\"flex-1 overflow-y-auto px-5 py-4\">\n <Tabs items={PANEL_TABS} value={panelTab} onChange={setPanelTab} className=\"mb-4\" />\n\n {panelTab === 'submission' ? (\n <div>\n <p className=\"mb-2 text-sm font-semibold text-text-primary\">\n Answers{' '}\n <span className=\"font-normal text-text-secondary\">(read-only)</span>\n </p>\n <AnswersView answers={selected.answers} />\n </div>\n ) : (\n <div className=\"grid grid-cols-2 gap-4 rounded-xl bg-bg-sunken/60 p-4\">\n <MetaField label=\"Status\">\n <StatusBadge status={selected.status} />\n </MetaField>\n <MetaField label=\"Attempt\">\n <span className=\"tabular-nums\">#{selected.attempt}</span>\n </MetaField>\n <MetaField label=\"Submitted\">\n {formatDateTime(selected.submittedAt)}\n </MetaField>\n <MetaField label=\"Assessment\">\n <span className=\"font-mono text-xs\" title={selected.assessmentId}>\n {shortId(selected.assessmentId)}\n </span>\n </MetaField>\n {selected.enrollmentId && (\n <MetaField label=\"Enrollment\">\n <span className=\"font-mono text-xs\" title={selected.enrollmentId}>\n {shortId(selected.enrollmentId)}\n </span>\n </MetaField>\n )}\n {selected.gradedAt && (\n <MetaField label=\"Graded\">{formatDateTime(selected.gradedAt)}</MetaField>\n )}\n </div>\n )}\n\n {/* Grade / result — persistent below the tab content. */}\n <div className=\"mt-6 border-t border-border-seam pt-5\">\n <p className=\"mb-3 text-sm font-semibold text-text-primary\">Grade</p>\n\n {notSubmitted && (\n <p className=\"rounded-lg bg-bg-sunken/60 px-4 py-3 text-sm text-text-secondary\">\n This attempt is still in progress — there is nothing to grade until the\n learner submits.\n </p>\n )}\n\n {alreadyGraded && !editMode && (\n <div className=\"rounded-xl border border-border-seam p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"grid grid-cols-2 gap-4\">\n <MetaField label=\"Score\">\n <span className=\"tabular-nums font-semibold\">\n {selected.score ?? '—'}\n </span>\n </MetaField>\n <MetaField label=\"Result\">\n {selected.passed == null ? (\n <span className=\"text-text-secondary\">—</span>\n ) : (\n <StatusBadge status={selected.passed ? 'GRADED' : 'RETURNED'} />\n )}\n </MetaField>\n </div>\n <Button variant=\"outline\" size=\"sm\" onClick={() => setEditMode(true)}>\n Re-grade\n </Button>\n </div>\n <div className=\"mt-3\">\n <MetaField label=\"Feedback\">\n {selected.feedback ? (\n <span className=\"whitespace-pre-wrap\">{selected.feedback}</span>\n ) : (\n <span className=\"text-text-secondary\">No feedback provided.</span>\n )}\n </MetaField>\n </div>\n </div>\n )}\n\n {showGradeForm && (\n <div className=\"grid gap-4\">\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"grade-score\">Score</Label>\n <Input\n id=\"grade-score\"\n type=\"number\"\n min=\"0\"\n step=\"1\"\n value={scoreInput}\n onChange={(e) => setScoreInput(e.target.value)}\n placeholder=\"e.g. 85\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"grade-passed\">Result</Label>\n <div className=\"flex h-10 items-center gap-3\">\n <Switch\n id=\"grade-passed\"\n checked={passed}\n onCheckedChange={setPassed}\n />\n <span className=\"text-sm text-text-secondary\">\n {passed ? 'Passed' : 'Not passed'}\n </span>\n </div>\n </div>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"grade-feedback\">Feedback</Label>\n <Textarea\n id=\"grade-feedback\"\n value={feedback}\n onChange={(e) => setFeedback(e.target.value)}\n placeholder=\"Notes, corrections or encouragement for the learner (optional).\"\n rows={4}\n />\n </div>\n {formError && (\n <p className=\"text-sm text-status-error-text\">{formError}</p>\n )}\n </div>\n )}\n </div>\n </div>\n\n <SheetFooter className=\"flex-row justify-end gap-3 border-t border-border-seam p-4\">\n <Button variant=\"outline\" onClick={closePanel}>\n {showGradeForm ? 'Cancel' : 'Close'}\n </Button>\n {showGradeForm && (\n <Button onClick={handleGrade} disabled={gradeSubmission.isPending}>\n {gradeSubmission.isPending\n ? 'Saving…'\n : alreadyGraded\n ? 'Update grade'\n : 'Grade submission'}\n </Button>\n )}\n </SheetFooter>\n </>\n )}\n </SheetContent>\n </Sheet>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-buildmyiq",
3
- "version": "2026.705.13",
3
+ "version": "2026.706.10",
4
4
  "description": "Burdenoff BuildMyIQ Microfrontend — education, learning & development",
5
5
  "type": "module",
6
6
  "files": [