feat(exam): Assessment dashboard + /exam-marker route; remove old CCExamMarker (S4-8)
- New examRepository (R2.1 seam): the only module talking to /api/exam (Supabase JWT → Bearer, axios to API_BASE). list/get/create/archive templates. - ExamDashboardPage (/exam-marker): lists institute templates, create dialog, archive; cards link to setup (S4-9). Wrapped in ErrorBoundary (R6.4). - exam.types.ts mirrors the API contract. - Dashboard 'Exam Marker' quick action (top-level discovery, R1.3/R6.1). Note: the in-canvas TeacherNavigation is unsuitable for a nav section (it's the worker prev/next tab bar) — flagged; used the dashboard entry instead. - R1.1 removal: deleted src/pages/tldraw/CCExamMarker/ (old 3-PDF viewer) and the now-dead CCExamMarkerPanel + its wiring in CCPanel/BasePanel (examMarkerProps was only ever passed by the deleted page). - Fixed pre-existing broken AppRoutes test (missing TimetableListPage mock export). Build green (vite); AppRoutes route tests pass; typecheck clean for new files. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
434643596e
commit
29554ebdbd
@@ -1,5 +1,4 @@
|
||||
import React, { useEffect, useRef, useMemo, useState } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { TldrawUiButton } from '@tldraw/tldraw';
|
||||
import { CCShapesPanel } from './CCShapesPanel';
|
||||
import { CCSlidesPanel } from './CCSlidesPanel';
|
||||
@@ -7,7 +6,6 @@ import { CCFilesPanel } from './CCFilesPanel';
|
||||
import { CCCabinetsPanel } from './CCCabinetsPanel';
|
||||
import { CCYoutubePanel } from './CCYoutubePanel';
|
||||
import { CCGraphPanel } from './CCGraphPanel';
|
||||
import { CCExamMarkerPanel } from './CCExamMarkerPanel';
|
||||
import { CCSearchPanel } from './CCSearchPanel';
|
||||
import { CCGraphNavPanel } from './navigation/CCGraphNavPanel';
|
||||
import { CCTranscriptionPanel } from './CCTranscriptionPanel';
|
||||
@@ -31,16 +29,12 @@ export const PANEL_TYPES = {
|
||||
{ id: 'graph', label: 'Graph', order: 60 },
|
||||
{ id: 'search', label: 'Search', order: 70 },
|
||||
],
|
||||
examMarker: [
|
||||
{ id: 'exam-marker', label: 'Exam Marker', order: 10 },
|
||||
],
|
||||
} as const;
|
||||
|
||||
export type PanelType = typeof PANEL_TYPES.default[number]['id'] | typeof PANEL_TYPES.examMarker[number]['id'];
|
||||
export type PanelType = typeof PANEL_TYPES.default[number]['id'];
|
||||
|
||||
interface BasePanelProps {
|
||||
initialPanelType?: PanelType;
|
||||
examMarkerProps?: React.ComponentProps<typeof CCExamMarkerPanel>;
|
||||
isExpanded?: boolean;
|
||||
isPinned?: boolean;
|
||||
onExpandedChange?: (expanded: boolean) => void;
|
||||
@@ -125,16 +119,6 @@ function getIconSvg(panelId: PanelType, size = '1.25rem') {
|
||||
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z" />
|
||||
</svg>
|
||||
);
|
||||
case 'exam-marker':
|
||||
return (
|
||||
<svg {...common}>
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
||||
<polyline points="14 2 14 8 20 8" />
|
||||
<line x1="16" y1="13" x2="8" y2="13" />
|
||||
<line x1="16" y1="17" x2="8" y2="17" />
|
||||
<polyline points="10 9 9 9 8 9" />
|
||||
</svg>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<svg {...common}>
|
||||
@@ -152,7 +136,6 @@ function isDarkMode() {
|
||||
|
||||
export const BasePanel: React.FC<BasePanelProps> = ({
|
||||
initialPanelType = 'files',
|
||||
examMarkerProps,
|
||||
isExpanded: controlledIsExpanded,
|
||||
isPinned: controlledIsPinned,
|
||||
onExpandedChange,
|
||||
@@ -160,7 +143,6 @@ export const BasePanel: React.FC<BasePanelProps> = ({
|
||||
isMenuOpen = false,
|
||||
onMenuOpenChange = () => {},
|
||||
}) => {
|
||||
const location = useLocation();
|
||||
const { tldrawPreferences } = useTLDraw();
|
||||
const [prefersDarkMode, setPrefersDarkMode] = useState(isDarkMode());
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
@@ -177,12 +159,9 @@ export const BasePanel: React.FC<BasePanelProps> = ({
|
||||
? 'dark'
|
||||
: 'light';
|
||||
|
||||
const isExamMarkerRoute = location.pathname === '/exam-marker';
|
||||
const availablePanels = isExamMarkerRoute ? PANEL_TYPES.examMarker : PANEL_TYPES.default;
|
||||
const availablePanels = PANEL_TYPES.default;
|
||||
|
||||
const [currentPanelType, setCurrentPanelType] = useState<PanelType>(
|
||||
isExamMarkerRoute ? 'exam-marker' : initialPanelType
|
||||
);
|
||||
const [currentPanelType, setCurrentPanelType] = useState<PanelType>(initialPanelType);
|
||||
|
||||
const [internalIsExpanded, setInternalIsExpanded] = useState(true);
|
||||
const [internalIsPinned, setInternalIsPinned] = useState(true);
|
||||
@@ -223,10 +202,6 @@ export const BasePanel: React.FC<BasePanelProps> = ({
|
||||
}, [isExpanded, isPinned]);
|
||||
|
||||
const renderCurrentPanel = () => {
|
||||
if (isExamMarkerRoute && currentPanelType === 'exam-marker') {
|
||||
return examMarkerProps ? <CCExamMarkerPanel {...examMarkerProps} /> : null;
|
||||
}
|
||||
|
||||
switch (currentPanelType) {
|
||||
case 'cabinets':
|
||||
return <CCCabinetsPanel />;
|
||||
@@ -331,7 +306,6 @@ export const BasePanel: React.FC<BasePanelProps> = ({
|
||||
{type.id === 'search' && 'Search through your content'}
|
||||
{type.id === 'navigation' && 'Navigate through different contexts'}
|
||||
{type.id === 'node-snapshot' && 'Manage node snapshots'}
|
||||
{type.id === 'exam-marker' && 'Mark and grade exams'}
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user