react-native-elearn-ui 0.1.28 → 0.1.32
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/lib/module/components/Icon.js +9 -0
- package/lib/module/components/Icon.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/screens/index.js +2 -0
- package/lib/module/screens/index.js.map +1 -1
- package/lib/module/screens/qbank/QBankOverviewScreen.js +435 -0
- package/lib/module/screens/qbank/QBankOverviewScreen.js.map +1 -0
- package/lib/module/screens/qbank/QBankQuestionScreen.js +181 -123
- package/lib/module/screens/qbank/QBankQuestionScreen.js.map +1 -1
- package/lib/module/screens/qbank/QBankSuccessScreen.js +336 -0
- package/lib/module/screens/qbank/QBankSuccessScreen.js.map +1 -0
- package/lib/typescript/src/components/Icon.d.ts +1 -1
- package/lib/typescript/src/components/Icon.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/screens/index.d.ts +2 -0
- package/lib/typescript/src/screens/index.d.ts.map +1 -1
- package/lib/typescript/src/screens/qbank/QBankOverviewScreen.d.ts +4 -0
- package/lib/typescript/src/screens/qbank/QBankOverviewScreen.d.ts.map +1 -0
- package/lib/typescript/src/screens/qbank/QBankQuestionScreen.d.ts +7 -9
- package/lib/typescript/src/screens/qbank/QBankQuestionScreen.d.ts.map +1 -1
- package/lib/typescript/src/screens/qbank/QBankSuccessScreen.d.ts +4 -0
- package/lib/typescript/src/screens/qbank/QBankSuccessScreen.d.ts.map +1 -0
- package/lib/typescript/src/types/index.d.ts +39 -7
- package/lib/typescript/src/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/Icon.tsx +6 -1
- package/src/index.tsx +1 -0
- package/src/screens/index.ts +2 -0
- package/src/screens/qbank/QBankOverviewScreen.tsx +392 -0
- package/src/screens/qbank/QBankQuestionScreen.tsx +164 -109
- package/src/screens/qbank/QBankSuccessScreen.tsx +292 -0
- package/src/types/index.ts +43 -7
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
ScrollView,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
TouchableOpacity,
|
|
7
|
+
TextInput,
|
|
8
|
+
KeyboardAvoidingView,
|
|
9
|
+
Platform,
|
|
10
|
+
} from 'react-native';
|
|
11
|
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
12
|
+
import { useTheme } from '../../context/ThemeContext';
|
|
13
|
+
import { Typography, Icon, Button } from '../../components';
|
|
14
|
+
import type { QBankSuccessScreenProps } from '../../types';
|
|
15
|
+
|
|
16
|
+
export const QBankSuccessScreen: React.FC<QBankSuccessScreenProps> = ({
|
|
17
|
+
moduleTitle,
|
|
18
|
+
completedDate,
|
|
19
|
+
totalQuestions,
|
|
20
|
+
correctCount,
|
|
21
|
+
incorrectCount,
|
|
22
|
+
missedCount,
|
|
23
|
+
onBack,
|
|
24
|
+
onSubmitFeedback,
|
|
25
|
+
}) => {
|
|
26
|
+
const theme = useTheme();
|
|
27
|
+
const [rating, setRating] = useState(0);
|
|
28
|
+
const [feedback, setFeedback] = useState('');
|
|
29
|
+
|
|
30
|
+
const percentage = Math.round((correctCount / totalQuestions) * 100) || 0;
|
|
31
|
+
|
|
32
|
+
// Calculate segments for the progress bar (percentages of the width)
|
|
33
|
+
const correctWidth = (correctCount / totalQuestions) * 100;
|
|
34
|
+
const incorrectWidth = (incorrectCount / totalQuestions) * 100;
|
|
35
|
+
const missedWidth = (missedCount / totalQuestions) * 100;
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
|
|
39
|
+
<KeyboardAvoidingView
|
|
40
|
+
style={styles.container}
|
|
41
|
+
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
|
42
|
+
>
|
|
43
|
+
<ScrollView contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={false}>
|
|
44
|
+
{/* Header */}
|
|
45
|
+
<View style={styles.header}>
|
|
46
|
+
<TouchableOpacity onPress={onBack} style={styles.headerIconBtnLeft}>
|
|
47
|
+
<Icon name="chevron-left" size={24} color={theme.textPrimary} />
|
|
48
|
+
</TouchableOpacity>
|
|
49
|
+
</View>
|
|
50
|
+
|
|
51
|
+
{/* Success Icon & Title */}
|
|
52
|
+
<View style={styles.successHeader}>
|
|
53
|
+
<View style={styles.iconCircle}>
|
|
54
|
+
<Icon name="check" size={40} color="#FFFFFF" />
|
|
55
|
+
</View>
|
|
56
|
+
<Typography variant="h2" bold color={theme.textPrimary} style={styles.successTitle}>
|
|
57
|
+
Module Completed
|
|
58
|
+
</Typography>
|
|
59
|
+
</View>
|
|
60
|
+
|
|
61
|
+
{/* Summary Card */}
|
|
62
|
+
<View style={styles.card}>
|
|
63
|
+
<View style={styles.cardHeader}>
|
|
64
|
+
<Typography variant="h3" bold color={theme.textPrimary}>
|
|
65
|
+
{moduleTitle}
|
|
66
|
+
</Typography>
|
|
67
|
+
<Typography variant="caption" color={theme.textSecondary} style={styles.dateText}>
|
|
68
|
+
Completed on {completedDate}
|
|
69
|
+
</Typography>
|
|
70
|
+
</View>
|
|
71
|
+
|
|
72
|
+
<View style={styles.divider} />
|
|
73
|
+
|
|
74
|
+
<View style={styles.statsSection}>
|
|
75
|
+
<Typography variant="caption" color={theme.textSecondary} style={styles.totalText}>
|
|
76
|
+
Total Question {totalQuestions}
|
|
77
|
+
</Typography>
|
|
78
|
+
|
|
79
|
+
<View style={styles.statsRow}>
|
|
80
|
+
<View style={styles.statsLabels}>
|
|
81
|
+
<Typography variant="caption" color={theme.success} style={styles.statLabel}>
|
|
82
|
+
{String(correctCount).padStart(2, '0')} Correct
|
|
83
|
+
</Typography>
|
|
84
|
+
<Typography variant="caption" color="#EF4444" style={styles.statLabel}>
|
|
85
|
+
{String(incorrectCount).padStart(2, '0')} Incorrect
|
|
86
|
+
</Typography>
|
|
87
|
+
<Typography variant="caption" color="#F59E0B" style={styles.statLabel}>
|
|
88
|
+
{String(missedCount).padStart(2, '0')} Missed
|
|
89
|
+
</Typography>
|
|
90
|
+
</View>
|
|
91
|
+
|
|
92
|
+
<View style={styles.percentageBlock}>
|
|
93
|
+
<Typography variant="h3" bold color={theme.textPrimary}>
|
|
94
|
+
{percentage}%
|
|
95
|
+
</Typography>
|
|
96
|
+
<Typography variant="caption" color={theme.textSecondary} style={{fontSize: 10}}>
|
|
97
|
+
Correct
|
|
98
|
+
</Typography>
|
|
99
|
+
</View>
|
|
100
|
+
</View>
|
|
101
|
+
|
|
102
|
+
{/* Multi-color Progress Bar */}
|
|
103
|
+
<View style={styles.progressBarContainer}>
|
|
104
|
+
{correctWidth > 0 && <View style={[styles.progressSegment, { width: `${correctWidth}%`, backgroundColor: theme.success }]} />}
|
|
105
|
+
{incorrectWidth > 0 && <View style={[styles.progressSegment, { width: `${incorrectWidth}%`, backgroundColor: '#EF4444' }]} />}
|
|
106
|
+
{missedWidth > 0 && <View style={[styles.progressSegment, { width: `${missedWidth}%`, backgroundColor: '#F59E0B' }]} />}
|
|
107
|
+
{/* Fallback empty bar if no questions */}
|
|
108
|
+
{totalQuestions === 0 && <View style={[styles.progressSegment, { width: '100%', backgroundColor: '#E5E7EB' }]} />}
|
|
109
|
+
</View>
|
|
110
|
+
</View>
|
|
111
|
+
</View>
|
|
112
|
+
|
|
113
|
+
{/* Feedback Section */}
|
|
114
|
+
<View style={styles.feedbackSection}>
|
|
115
|
+
<Typography variant="h3" bold color={theme.textPrimary} style={styles.feedbackTitle}>
|
|
116
|
+
How was your QBank{'\n'}taking experience?
|
|
117
|
+
</Typography>
|
|
118
|
+
<Typography variant="body" color={theme.textSecondary} style={styles.feedbackSubtitle}>
|
|
119
|
+
Your feedback will help us improve your QBank experience
|
|
120
|
+
</Typography>
|
|
121
|
+
|
|
122
|
+
<View style={styles.starsContainer}>
|
|
123
|
+
{[1, 2, 3, 4, 5].map((star) => (
|
|
124
|
+
<TouchableOpacity key={star} onPress={() => setRating(star)}>
|
|
125
|
+
<Icon
|
|
126
|
+
name="star"
|
|
127
|
+
size={36}
|
|
128
|
+
color={star <= rating ? '#EAB308' : '#D1D5DB'}
|
|
129
|
+
/>
|
|
130
|
+
</TouchableOpacity>
|
|
131
|
+
))}
|
|
132
|
+
</View>
|
|
133
|
+
|
|
134
|
+
<TextInput
|
|
135
|
+
style={styles.textInput}
|
|
136
|
+
placeholder="Type Your Feedback Here"
|
|
137
|
+
placeholderTextColor="#9CA3AF"
|
|
138
|
+
multiline
|
|
139
|
+
numberOfLines={4}
|
|
140
|
+
value={feedback}
|
|
141
|
+
onChangeText={setFeedback}
|
|
142
|
+
/>
|
|
143
|
+
|
|
144
|
+
<Button
|
|
145
|
+
title="Submit"
|
|
146
|
+
onPress={() => onSubmitFeedback?.(rating, feedback)}
|
|
147
|
+
style={styles.submitBtn}
|
|
148
|
+
/>
|
|
149
|
+
</View>
|
|
150
|
+
</ScrollView>
|
|
151
|
+
</KeyboardAvoidingView>
|
|
152
|
+
</SafeAreaView>
|
|
153
|
+
);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const styles = StyleSheet.create({
|
|
157
|
+
container: {
|
|
158
|
+
flex: 1,
|
|
159
|
+
backgroundColor: '#F9FAFB',
|
|
160
|
+
},
|
|
161
|
+
scrollContent: {
|
|
162
|
+
padding: 20,
|
|
163
|
+
paddingBottom: 40,
|
|
164
|
+
},
|
|
165
|
+
header: {
|
|
166
|
+
marginBottom: 20,
|
|
167
|
+
},
|
|
168
|
+
headerIconBtnLeft: {
|
|
169
|
+
width: 40,
|
|
170
|
+
height: 40,
|
|
171
|
+
justifyContent: 'center',
|
|
172
|
+
alignItems: 'flex-start',
|
|
173
|
+
},
|
|
174
|
+
successHeader: {
|
|
175
|
+
alignItems: 'center',
|
|
176
|
+
marginBottom: 32,
|
|
177
|
+
},
|
|
178
|
+
iconCircle: {
|
|
179
|
+
width: 80,
|
|
180
|
+
height: 80,
|
|
181
|
+
borderRadius: 40,
|
|
182
|
+
backgroundColor: '#10B981', // Success green
|
|
183
|
+
justifyContent: 'center',
|
|
184
|
+
alignItems: 'center',
|
|
185
|
+
shadowColor: '#10B981',
|
|
186
|
+
shadowOffset: { width: 0, height: 4 },
|
|
187
|
+
shadowOpacity: 0.3,
|
|
188
|
+
shadowRadius: 8,
|
|
189
|
+
elevation: 8,
|
|
190
|
+
marginBottom: 16,
|
|
191
|
+
},
|
|
192
|
+
successTitle: {
|
|
193
|
+
fontSize: 20,
|
|
194
|
+
},
|
|
195
|
+
card: {
|
|
196
|
+
backgroundColor: '#FFFFFF',
|
|
197
|
+
borderRadius: 12,
|
|
198
|
+
borderWidth: 1,
|
|
199
|
+
borderColor: '#F3F4F6',
|
|
200
|
+
shadowColor: '#000',
|
|
201
|
+
shadowOffset: { width: 0, height: 2 },
|
|
202
|
+
shadowOpacity: 0.05,
|
|
203
|
+
shadowRadius: 8,
|
|
204
|
+
elevation: 2,
|
|
205
|
+
marginBottom: 40,
|
|
206
|
+
},
|
|
207
|
+
cardHeader: {
|
|
208
|
+
padding: 16,
|
|
209
|
+
},
|
|
210
|
+
dateText: {
|
|
211
|
+
marginTop: 4,
|
|
212
|
+
},
|
|
213
|
+
divider: {
|
|
214
|
+
height: 1,
|
|
215
|
+
backgroundColor: '#F3F4F6',
|
|
216
|
+
},
|
|
217
|
+
statsSection: {
|
|
218
|
+
padding: 16,
|
|
219
|
+
},
|
|
220
|
+
totalText: {
|
|
221
|
+
marginBottom: 8,
|
|
222
|
+
},
|
|
223
|
+
statsRow: {
|
|
224
|
+
flexDirection: 'row',
|
|
225
|
+
justifyContent: 'space-between',
|
|
226
|
+
alignItems: 'flex-end',
|
|
227
|
+
marginBottom: 12,
|
|
228
|
+
},
|
|
229
|
+
statsLabels: {
|
|
230
|
+
flexDirection: 'row',
|
|
231
|
+
alignItems: 'center',
|
|
232
|
+
flex: 1,
|
|
233
|
+
flexWrap: 'wrap',
|
|
234
|
+
},
|
|
235
|
+
statLabel: {
|
|
236
|
+
marginRight: 16,
|
|
237
|
+
fontSize: 12,
|
|
238
|
+
marginBottom: 4,
|
|
239
|
+
},
|
|
240
|
+
percentageBlock: {
|
|
241
|
+
alignItems: 'center',
|
|
242
|
+
justifyContent: 'center',
|
|
243
|
+
},
|
|
244
|
+
progressBarContainer: {
|
|
245
|
+
height: 8,
|
|
246
|
+
backgroundColor: '#E5E7EB',
|
|
247
|
+
borderRadius: 4,
|
|
248
|
+
flexDirection: 'row',
|
|
249
|
+
overflow: 'hidden',
|
|
250
|
+
},
|
|
251
|
+
progressSegment: {
|
|
252
|
+
height: '100%',
|
|
253
|
+
},
|
|
254
|
+
feedbackSection: {
|
|
255
|
+
alignItems: 'center',
|
|
256
|
+
},
|
|
257
|
+
feedbackTitle: {
|
|
258
|
+
textAlign: 'center',
|
|
259
|
+
fontSize: 18,
|
|
260
|
+
marginBottom: 8,
|
|
261
|
+
lineHeight: 26,
|
|
262
|
+
},
|
|
263
|
+
feedbackSubtitle: {
|
|
264
|
+
textAlign: 'center',
|
|
265
|
+
fontSize: 12,
|
|
266
|
+
marginBottom: 24,
|
|
267
|
+
paddingHorizontal: 20,
|
|
268
|
+
},
|
|
269
|
+
starsContainer: {
|
|
270
|
+
flexDirection: 'row',
|
|
271
|
+
justifyContent: 'center',
|
|
272
|
+
gap: 12, // Space between stars
|
|
273
|
+
marginBottom: 32,
|
|
274
|
+
},
|
|
275
|
+
textInput: {
|
|
276
|
+
width: '100%',
|
|
277
|
+
height: 120,
|
|
278
|
+
backgroundColor: '#FFFFFF',
|
|
279
|
+
borderWidth: 1,
|
|
280
|
+
borderColor: '#10B981', // Green border as in design
|
|
281
|
+
borderRadius: 8,
|
|
282
|
+
padding: 16,
|
|
283
|
+
fontSize: 14,
|
|
284
|
+
color: '#1F2937',
|
|
285
|
+
marginBottom: 24,
|
|
286
|
+
},
|
|
287
|
+
submitBtn: {
|
|
288
|
+
backgroundColor: '#10B981',
|
|
289
|
+
paddingHorizontal: 40,
|
|
290
|
+
borderRadius: 8,
|
|
291
|
+
},
|
|
292
|
+
});
|
package/src/types/index.ts
CHANGED
|
@@ -239,23 +239,59 @@ export interface QBankSubTopicSection {
|
|
|
239
239
|
modules: QBankSubTopicModule[];
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
-
export interface
|
|
242
|
+
export interface QBankExamOption {
|
|
243
243
|
id: string;
|
|
244
244
|
text: string;
|
|
245
|
-
|
|
245
|
+
isCorrect: boolean;
|
|
246
246
|
percentage?: number; // e.g. 20 for [20%]
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
-
export interface
|
|
250
|
-
|
|
251
|
-
questionNumber: number;
|
|
252
|
-
totalQuestions: number;
|
|
249
|
+
export interface QBankExamQuestion {
|
|
250
|
+
id: string;
|
|
253
251
|
questionText: string;
|
|
254
|
-
options:
|
|
252
|
+
options: QBankExamOption[];
|
|
255
253
|
explanation?: string;
|
|
256
254
|
referenceId?: string;
|
|
257
255
|
}
|
|
258
256
|
|
|
257
|
+
export interface QBankExamData {
|
|
258
|
+
id: string;
|
|
259
|
+
title: string;
|
|
260
|
+
accessType: 'free' | 'pro';
|
|
261
|
+
timeLimitSeconds?: number;
|
|
262
|
+
questions: QBankExamQuestion[];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export interface QBankSuccessScreenProps {
|
|
266
|
+
moduleTitle: string;
|
|
267
|
+
completedDate: string;
|
|
268
|
+
totalQuestions: number;
|
|
269
|
+
correctCount: number;
|
|
270
|
+
incorrectCount: number;
|
|
271
|
+
missedCount: number;
|
|
272
|
+
onBack?: () => void;
|
|
273
|
+
onSubmitFeedback?: (rating: number, feedback: string) => void;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export interface QBankOverviewQuestion {
|
|
277
|
+
id: string;
|
|
278
|
+
number: number;
|
|
279
|
+
title: string;
|
|
280
|
+
status: 'correct' | 'incorrect' | 'missed';
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export interface QBankOverviewScreenProps {
|
|
284
|
+
topicTitle: string;
|
|
285
|
+
moduleTitle: string;
|
|
286
|
+
totalQuestions: number;
|
|
287
|
+
correctCount: number;
|
|
288
|
+
incorrectCount: number;
|
|
289
|
+
missedCount: number;
|
|
290
|
+
questions: QBankOverviewQuestion[];
|
|
291
|
+
onBack?: () => void;
|
|
292
|
+
onPressQuestion?: (questionId: string) => void;
|
|
293
|
+
}
|
|
294
|
+
|
|
259
295
|
export interface QBankSubTopicParams {
|
|
260
296
|
header: {
|
|
261
297
|
title: string;
|