Public Access
feat: replace screen state machine with React Router v7 URL routing
Release Pipeline / build-and-release (push) Successful in 5m24s
Release Pipeline / build-and-release (push) Successful in 5m24s
- Install react-router-dom - Add slugify utility with full TDD test suite (7 tests) - Wrap app in BrowserRouter in main.tsx - Add optional onNavigate prop to ExamRunner.goTo - Create ExamRoute component (deep-link restore + back/forward sync) - Migrate App.tsx: replace screen useState with <Routes>, all setScreen() calls replaced with navigate(), add activateExam for deep-link activation, use useLocation for verify/reset-password special layouts - Remove unused AppScreen type from types.ts
This commit is contained in:
1 parent
e97d90e316
commit
4eeacc38c5
10 files changed
+796
-367
No files matched your search
+82
-83
@@ -1,125 +1,124 @@
|
||||
// src/types.ts
|
||||
|
||||
export type QuestionType =
|
||||
| 'multiple-choice'
|
||||
| 'multiple-select'
|
||||
| 'short-answer'
|
||||
| 'translation'
|
||||
| 'math-input'
|
||||
| 'essay'
|
||||
| 'match'
|
||||
| 'order'
|
||||
| "multiple-choice"
|
||||
| "multiple-select"
|
||||
| "short-answer"
|
||||
| "translation"
|
||||
| "math-input"
|
||||
| "essay"
|
||||
| "match"
|
||||
| "order";
|
||||
|
||||
export interface Choice {
|
||||
id: string
|
||||
text: string
|
||||
id: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface MatchItem {
|
||||
id: string
|
||||
text: string
|
||||
id: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface QuestionImage {
|
||||
src: string
|
||||
alt?: string
|
||||
caption?: string
|
||||
src: string;
|
||||
alt?: string;
|
||||
caption?: string;
|
||||
}
|
||||
|
||||
export interface QuestionAudio {
|
||||
src: string
|
||||
src: string;
|
||||
}
|
||||
|
||||
export interface QuestionCode {
|
||||
language: string
|
||||
content: string
|
||||
language: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface QuestionTable {
|
||||
headers: string[]
|
||||
rows: string[][]
|
||||
headers: string[];
|
||||
rows: string[][];
|
||||
}
|
||||
|
||||
export interface Question {
|
||||
id: string
|
||||
type: QuestionType
|
||||
category?: string
|
||||
points?: number
|
||||
prompt: string
|
||||
explanation?: string
|
||||
image?: QuestionImage
|
||||
audio?: QuestionAudio
|
||||
code?: QuestionCode
|
||||
table?: QuestionTable
|
||||
svg?: string
|
||||
answer?: string | string[] | Record<string, string>
|
||||
acceptedForms?: string[]
|
||||
caseSensitive?: boolean
|
||||
ignorePunctuation?: boolean
|
||||
ignoreAccents?: boolean
|
||||
sourceLanguage?: string
|
||||
targetLanguage?: string
|
||||
choices?: Choice[]
|
||||
minWords?: number
|
||||
rubric?: string[]
|
||||
left?: MatchItem[]
|
||||
right?: MatchItem[]
|
||||
items?: MatchItem[]
|
||||
id: string;
|
||||
type: QuestionType;
|
||||
category?: string;
|
||||
points?: number;
|
||||
prompt: string;
|
||||
explanation?: string;
|
||||
image?: QuestionImage;
|
||||
audio?: QuestionAudio;
|
||||
code?: QuestionCode;
|
||||
table?: QuestionTable;
|
||||
svg?: string;
|
||||
answer?: string | string[] | Record<string, string>;
|
||||
acceptedForms?: string[];
|
||||
caseSensitive?: boolean;
|
||||
ignorePunctuation?: boolean;
|
||||
ignoreAccents?: boolean;
|
||||
sourceLanguage?: string;
|
||||
targetLanguage?: string;
|
||||
choices?: Choice[];
|
||||
minWords?: number;
|
||||
rubric?: string[];
|
||||
left?: MatchItem[];
|
||||
right?: MatchItem[];
|
||||
items?: MatchItem[];
|
||||
}
|
||||
|
||||
export interface Exam {
|
||||
title: string
|
||||
subtitle?: string
|
||||
author?: string
|
||||
timeLimitMinutes?: number | null
|
||||
feedbackMode?: 'end' | 'immediate'
|
||||
passingScore?: number
|
||||
categories?: string[]
|
||||
questions: Question[]
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
author?: string;
|
||||
timeLimitMinutes?: number | null;
|
||||
feedbackMode?: "end" | "immediate";
|
||||
passingScore?: number;
|
||||
categories?: string[];
|
||||
questions: Question[];
|
||||
}
|
||||
|
||||
export type FeedbackMode = 'immediate' | 'end'
|
||||
export type FeedbackMode = "immediate" | "end";
|
||||
|
||||
export interface ExamState {
|
||||
currentIdx: number
|
||||
answers: Record<string, unknown>
|
||||
flags: Record<string, boolean>
|
||||
notes: string
|
||||
revealed?: Record<string, boolean>
|
||||
startedAt: number
|
||||
completedAt?: number
|
||||
feedbackMode: FeedbackMode
|
||||
currentIdx: number;
|
||||
answers: Record<string, unknown>;
|
||||
flags: Record<string, boolean>;
|
||||
notes: string;
|
||||
revealed?: Record<string, boolean>;
|
||||
startedAt: number;
|
||||
completedAt?: number;
|
||||
feedbackMode: FeedbackMode;
|
||||
}
|
||||
|
||||
export interface HistoryEntry {
|
||||
id: string
|
||||
title: string
|
||||
completedAt: number
|
||||
elapsedMs: number
|
||||
gradedCount: number
|
||||
correctCount: number
|
||||
totalPoints: number
|
||||
scorePoints: number
|
||||
passingScore: number
|
||||
exam: Exam
|
||||
state: ExamState
|
||||
id: string;
|
||||
title: string;
|
||||
completedAt: number;
|
||||
elapsedMs: number;
|
||||
gradedCount: number;
|
||||
correctCount: number;
|
||||
totalPoints: number;
|
||||
scorePoints: number;
|
||||
passingScore: number;
|
||||
exam: Exam;
|
||||
state: ExamState;
|
||||
}
|
||||
|
||||
export type GradeStatus = 'correct' | 'wrong' | 'ungraded' | 'unanswered'
|
||||
export type GradeStatus = "correct" | "wrong" | "ungraded" | "unanswered";
|
||||
|
||||
export interface GradeResult {
|
||||
status: GradeStatus
|
||||
correct: boolean
|
||||
points: number
|
||||
status: GradeStatus;
|
||||
correct: boolean;
|
||||
points: number;
|
||||
}
|
||||
|
||||
export type AppScreen = 'landing' | 'exam' | 'results' | 'review' | 'verify' | 'reset-password' | 'admin'
|
||||
export type ThemeMode = 'light' | 'dark'
|
||||
export type FontSize = 'small' | 'medium' | 'large'
|
||||
export type FeedbackTweak = 'auto' | 'immediate' | 'end'
|
||||
export type ThemeMode = "light" | "dark";
|
||||
export type FontSize = "small" | "medium" | "large";
|
||||
export type FeedbackTweak = "auto" | "immediate" | "end";
|
||||
|
||||
export interface Tweaks {
|
||||
theme: ThemeMode
|
||||
fontSize: FontSize
|
||||
feedbackMode: FeedbackTweak
|
||||
theme: ThemeMode;
|
||||
fontSize: FontSize;
|
||||
feedbackMode: FeedbackTweak;
|
||||
}
|
||||
Reference in new issue
Block a user