@schoolio/player 1.0.0

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.
package/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # @schoolio/player
2
+
3
+ A React component for loading and playing quizzes from Quiz Engine.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @schoolio/player
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```tsx
14
+ import { QuizPlayer } from '@schoolio/player';
15
+
16
+ function MyQuizPage() {
17
+ return (
18
+ <QuizPlayer
19
+ quizId="QZ123"
20
+ lessonId="L12"
21
+ assignLessonId="assignLesson123"
22
+ courseId="course456"
23
+ childId="child789"
24
+ parentId="parent012"
25
+ apiBaseUrl="https://your-quiz-engine-url.com"
26
+ onComplete={(result) => {
27
+ console.log('Quiz completed!', result);
28
+ // result contains: attemptId, score, correctAnswers, totalQuestions, answers, timeSpentSeconds
29
+ }}
30
+ onError={(error) => {
31
+ console.error('Quiz error:', error);
32
+ }}
33
+ onProgress={(progress) => {
34
+ console.log(`Question ${progress.currentQuestion} of ${progress.totalQuestions}`);
35
+ }}
36
+ />
37
+ );
38
+ }
39
+ ```
40
+
41
+ ## Props
42
+
43
+ | Prop | Type | Required | Description |
44
+ |------|------|----------|-------------|
45
+ | `quizId` | string | Yes | The ID of the quiz to load |
46
+ | `lessonId` | string | Yes | Lesson ID from your app |
47
+ | `assignLessonId` | string | Yes | Assigned lesson ID for tracking |
48
+ | `courseId` | string | Yes | Course ID for tracking |
49
+ | `childId` | string | Yes | Student/child ID |
50
+ | `parentId` | string | Yes | Parent ID |
51
+ | `apiBaseUrl` | string | Yes | Base URL of your Quiz Engine API |
52
+ | `authToken` | string | No | Optional auth token for API requests |
53
+ | `onComplete` | function | No | Callback when quiz is completed |
54
+ | `onError` | function | No | Callback when an error occurs |
55
+ | `onProgress` | function | No | Callback on question navigation |
56
+ | `className` | string | No | CSS class for the container |
57
+
58
+ ## Quiz Result
59
+
60
+ When the quiz is completed, the `onComplete` callback receives:
61
+
62
+ ```typescript
63
+ interface QuizResult {
64
+ attemptId: string; // Unique attempt ID
65
+ score: number; // Percentage score (0-100)
66
+ correctAnswers: number; // Number of correct answers
67
+ totalQuestions: number; // Total questions in quiz
68
+ answers: QuizAnswerDetail[]; // Detailed answer data
69
+ timeSpentSeconds: number; // Time taken to complete
70
+ }
71
+ ```
72
+
73
+ ## Attempt Tracking
74
+
75
+ The component automatically:
76
+ - Creates a new attempt when started
77
+ - Saves progress as the student answers questions
78
+ - Resumes from where the student left off if they return
79
+ - Tracks attempt number (1st, 2nd, 3rd attempt, etc.)
80
+ - Records time spent on the quiz
81
+
82
+ ## API Endpoints
83
+
84
+ The component expects these endpoints on your Quiz Engine:
85
+
86
+ - `GET /api/external/quizzes/:id` - Get quiz by ID
87
+ - `POST /api/external/quiz-attempts` - Create new attempt
88
+ - `PATCH /api/external/quiz-attempts/:id` - Update attempt
89
+ - `GET /api/external/quiz-attempts/:id` - Get attempt by ID
90
+ - `GET /api/external/quiz-attempts?assignLessonId=X&childId=Y` - Get attempts
91
+
92
+ ## Customization
93
+
94
+ You can provide custom styling via the `className` prop and CSS:
95
+
96
+ ```css
97
+ .my-quiz-player {
98
+ /* Custom container styles */
99
+ }
100
+
101
+ .my-quiz-player .quiz-question {
102
+ /* Custom question styles */
103
+ }
104
+ ```
105
+
106
+ ## Using the API Client Directly
107
+
108
+ For more control, you can use the API client directly:
109
+
110
+ ```typescript
111
+ import { QuizApiClient } from '@schoolio/player';
112
+
113
+ const client = new QuizApiClient({
114
+ baseUrl: 'https://your-quiz-engine-url.com',
115
+ authToken: 'optional-auth-token',
116
+ });
117
+
118
+ // Fetch a quiz
119
+ const quiz = await client.getQuiz('quiz-id');
120
+
121
+ // Create an attempt
122
+ const attempt = await client.createAttempt({
123
+ quizId: 'quiz-id',
124
+ lessonId: 'lesson-id',
125
+ assignLessonId: 'assign-lesson-id',
126
+ courseId: 'course-id',
127
+ childId: 'child-id',
128
+ parentId: 'parent-id',
129
+ });
130
+
131
+ // Update attempt with answers
132
+ await client.updateAttempt(attempt.id, {
133
+ answers: [...],
134
+ status: 'completed',
135
+ score: 80,
136
+ correctAnswers: 8,
137
+ timeSpentSeconds: 300,
138
+ });
139
+ ```
140
+
141
+ ## License
142
+
143
+ MIT
@@ -0,0 +1,146 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ type QuestionType = "single" | "multiple" | "true-false" | "free" | "essay" | "fill" | "sorting" | "matrix" | "assessment";
4
+ interface QuizQuestion {
5
+ id: string;
6
+ question: string;
7
+ type: QuestionType;
8
+ points: number;
9
+ explanation?: string;
10
+ hint?: string;
11
+ imageUrl?: string;
12
+ options?: string[];
13
+ correctAnswer?: string | string[] | Record<string, string>;
14
+ blanks?: string[];
15
+ items?: string[];
16
+ correctOrder?: number[];
17
+ leftItems?: string[];
18
+ rightItems?: string[];
19
+ correctMatches?: Record<string, string>;
20
+ scaleType?: "likert" | "rating" | "yes-no";
21
+ scaleMin?: number;
22
+ scaleMax?: number;
23
+ }
24
+ interface Quiz {
25
+ id: string;
26
+ title: string;
27
+ grade: number;
28
+ questions: QuizQuestion[];
29
+ status: string;
30
+ }
31
+ interface QuizAnswerDetail {
32
+ questionId: string;
33
+ questionText: string;
34
+ questionType: QuestionType;
35
+ points: number;
36
+ pointsEarned: number;
37
+ selectedAnswer?: string | string[] | number[] | Record<string, string> | number;
38
+ correctAnswer?: string | string[] | Record<string, string>;
39
+ isCorrect: boolean;
40
+ explanation?: string;
41
+ hint?: string;
42
+ }
43
+ type AttemptStatus = "in_progress" | "completed" | "abandoned";
44
+ interface ExternalQuizAttempt {
45
+ id: string;
46
+ quizId: string;
47
+ lessonId: string;
48
+ assignLessonId: string;
49
+ courseId: string;
50
+ childId: string;
51
+ parentId: string;
52
+ attemptNumber: number;
53
+ status: AttemptStatus;
54
+ score?: number;
55
+ correctAnswers?: number;
56
+ totalQuestions: number;
57
+ answers: QuizAnswerDetail[];
58
+ startedAt: string;
59
+ completedAt?: string;
60
+ timeSpentSeconds?: number;
61
+ }
62
+ interface QuizPlayerProps {
63
+ quizId: string;
64
+ lessonId: string;
65
+ assignLessonId: string;
66
+ courseId: string;
67
+ childId: string;
68
+ parentId: string;
69
+ apiBaseUrl: string;
70
+ authToken?: string;
71
+ onComplete?: (result: QuizResult) => void;
72
+ onError?: (error: Error) => void;
73
+ onProgress?: (progress: QuizProgress) => void;
74
+ className?: string;
75
+ styles?: QuizPlayerStyles;
76
+ }
77
+ interface QuizResult {
78
+ attemptId: string;
79
+ score: number;
80
+ correctAnswers: number;
81
+ totalQuestions: number;
82
+ answers: QuizAnswerDetail[];
83
+ timeSpentSeconds: number;
84
+ }
85
+ interface QuizProgress {
86
+ currentQuestion: number;
87
+ totalQuestions: number;
88
+ answeredQuestions: number;
89
+ }
90
+ interface QuizPlayerStyles {
91
+ containerClassName?: string;
92
+ questionClassName?: string;
93
+ optionClassName?: string;
94
+ buttonClassName?: string;
95
+ progressClassName?: string;
96
+ }
97
+
98
+ declare function QuizPlayer({ quizId, lessonId, assignLessonId, courseId, childId, parentId, apiBaseUrl, authToken, onComplete, onError, onProgress, className, }: QuizPlayerProps): react_jsx_runtime.JSX.Element;
99
+
100
+ interface ApiClientConfig {
101
+ baseUrl: string;
102
+ authToken?: string;
103
+ }
104
+ declare class QuizApiClient {
105
+ private baseUrl;
106
+ private authToken?;
107
+ constructor(config: ApiClientConfig);
108
+ private request;
109
+ getQuiz(quizId: string): Promise<Quiz>;
110
+ createAttempt(params: {
111
+ quizId: string;
112
+ lessonId: string;
113
+ assignLessonId: string;
114
+ courseId: string;
115
+ childId: string;
116
+ parentId: string;
117
+ }): Promise<ExternalQuizAttempt>;
118
+ updateAttempt(attemptId: string, data: {
119
+ answers?: QuizAnswerDetail[];
120
+ status?: 'in_progress' | 'completed' | 'abandoned';
121
+ score?: number;
122
+ correctAnswers?: number;
123
+ timeSpentSeconds?: number;
124
+ }): Promise<ExternalQuizAttempt>;
125
+ getAttempt(attemptId: string): Promise<ExternalQuizAttempt>;
126
+ getAttempts(params: {
127
+ assignLessonId?: string;
128
+ childId?: string;
129
+ quizId?: string;
130
+ }): Promise<ExternalQuizAttempt[]>;
131
+ }
132
+
133
+ declare function checkAnswer(question: QuizQuestion, selectedAnswer: unknown): {
134
+ isCorrect: boolean;
135
+ pointsEarned: number;
136
+ };
137
+ declare function createAnswerDetail(question: QuizQuestion, selectedAnswer: unknown): QuizAnswerDetail;
138
+ declare function calculateScore(answers: QuizAnswerDetail[]): {
139
+ score: number;
140
+ correctAnswers: number;
141
+ totalPoints: number;
142
+ earnedPoints: number;
143
+ };
144
+ declare function formatTime(seconds: number): string;
145
+
146
+ export { type ApiClientConfig, type AttemptStatus, type ExternalQuizAttempt, type QuestionType, type Quiz, type QuizAnswerDetail, QuizApiClient, QuizPlayer, type QuizPlayerProps, type QuizPlayerStyles, type QuizProgress, type QuizQuestion, type QuizResult, calculateScore, checkAnswer, createAnswerDetail, formatTime };
@@ -0,0 +1,146 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ type QuestionType = "single" | "multiple" | "true-false" | "free" | "essay" | "fill" | "sorting" | "matrix" | "assessment";
4
+ interface QuizQuestion {
5
+ id: string;
6
+ question: string;
7
+ type: QuestionType;
8
+ points: number;
9
+ explanation?: string;
10
+ hint?: string;
11
+ imageUrl?: string;
12
+ options?: string[];
13
+ correctAnswer?: string | string[] | Record<string, string>;
14
+ blanks?: string[];
15
+ items?: string[];
16
+ correctOrder?: number[];
17
+ leftItems?: string[];
18
+ rightItems?: string[];
19
+ correctMatches?: Record<string, string>;
20
+ scaleType?: "likert" | "rating" | "yes-no";
21
+ scaleMin?: number;
22
+ scaleMax?: number;
23
+ }
24
+ interface Quiz {
25
+ id: string;
26
+ title: string;
27
+ grade: number;
28
+ questions: QuizQuestion[];
29
+ status: string;
30
+ }
31
+ interface QuizAnswerDetail {
32
+ questionId: string;
33
+ questionText: string;
34
+ questionType: QuestionType;
35
+ points: number;
36
+ pointsEarned: number;
37
+ selectedAnswer?: string | string[] | number[] | Record<string, string> | number;
38
+ correctAnswer?: string | string[] | Record<string, string>;
39
+ isCorrect: boolean;
40
+ explanation?: string;
41
+ hint?: string;
42
+ }
43
+ type AttemptStatus = "in_progress" | "completed" | "abandoned";
44
+ interface ExternalQuizAttempt {
45
+ id: string;
46
+ quizId: string;
47
+ lessonId: string;
48
+ assignLessonId: string;
49
+ courseId: string;
50
+ childId: string;
51
+ parentId: string;
52
+ attemptNumber: number;
53
+ status: AttemptStatus;
54
+ score?: number;
55
+ correctAnswers?: number;
56
+ totalQuestions: number;
57
+ answers: QuizAnswerDetail[];
58
+ startedAt: string;
59
+ completedAt?: string;
60
+ timeSpentSeconds?: number;
61
+ }
62
+ interface QuizPlayerProps {
63
+ quizId: string;
64
+ lessonId: string;
65
+ assignLessonId: string;
66
+ courseId: string;
67
+ childId: string;
68
+ parentId: string;
69
+ apiBaseUrl: string;
70
+ authToken?: string;
71
+ onComplete?: (result: QuizResult) => void;
72
+ onError?: (error: Error) => void;
73
+ onProgress?: (progress: QuizProgress) => void;
74
+ className?: string;
75
+ styles?: QuizPlayerStyles;
76
+ }
77
+ interface QuizResult {
78
+ attemptId: string;
79
+ score: number;
80
+ correctAnswers: number;
81
+ totalQuestions: number;
82
+ answers: QuizAnswerDetail[];
83
+ timeSpentSeconds: number;
84
+ }
85
+ interface QuizProgress {
86
+ currentQuestion: number;
87
+ totalQuestions: number;
88
+ answeredQuestions: number;
89
+ }
90
+ interface QuizPlayerStyles {
91
+ containerClassName?: string;
92
+ questionClassName?: string;
93
+ optionClassName?: string;
94
+ buttonClassName?: string;
95
+ progressClassName?: string;
96
+ }
97
+
98
+ declare function QuizPlayer({ quizId, lessonId, assignLessonId, courseId, childId, parentId, apiBaseUrl, authToken, onComplete, onError, onProgress, className, }: QuizPlayerProps): react_jsx_runtime.JSX.Element;
99
+
100
+ interface ApiClientConfig {
101
+ baseUrl: string;
102
+ authToken?: string;
103
+ }
104
+ declare class QuizApiClient {
105
+ private baseUrl;
106
+ private authToken?;
107
+ constructor(config: ApiClientConfig);
108
+ private request;
109
+ getQuiz(quizId: string): Promise<Quiz>;
110
+ createAttempt(params: {
111
+ quizId: string;
112
+ lessonId: string;
113
+ assignLessonId: string;
114
+ courseId: string;
115
+ childId: string;
116
+ parentId: string;
117
+ }): Promise<ExternalQuizAttempt>;
118
+ updateAttempt(attemptId: string, data: {
119
+ answers?: QuizAnswerDetail[];
120
+ status?: 'in_progress' | 'completed' | 'abandoned';
121
+ score?: number;
122
+ correctAnswers?: number;
123
+ timeSpentSeconds?: number;
124
+ }): Promise<ExternalQuizAttempt>;
125
+ getAttempt(attemptId: string): Promise<ExternalQuizAttempt>;
126
+ getAttempts(params: {
127
+ assignLessonId?: string;
128
+ childId?: string;
129
+ quizId?: string;
130
+ }): Promise<ExternalQuizAttempt[]>;
131
+ }
132
+
133
+ declare function checkAnswer(question: QuizQuestion, selectedAnswer: unknown): {
134
+ isCorrect: boolean;
135
+ pointsEarned: number;
136
+ };
137
+ declare function createAnswerDetail(question: QuizQuestion, selectedAnswer: unknown): QuizAnswerDetail;
138
+ declare function calculateScore(answers: QuizAnswerDetail[]): {
139
+ score: number;
140
+ correctAnswers: number;
141
+ totalPoints: number;
142
+ earnedPoints: number;
143
+ };
144
+ declare function formatTime(seconds: number): string;
145
+
146
+ export { type ApiClientConfig, type AttemptStatus, type ExternalQuizAttempt, type QuestionType, type Quiz, type QuizAnswerDetail, QuizApiClient, QuizPlayer, type QuizPlayerProps, type QuizPlayerStyles, type QuizProgress, type QuizQuestion, type QuizResult, calculateScore, checkAnswer, createAnswerDetail, formatTime };