react-native-elearn-ui 0.1.23 → 0.1.28

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 (39) hide show
  1. package/lib/module/components/BottomSheet.js +134 -0
  2. package/lib/module/components/BottomSheet.js.map +1 -0
  3. package/lib/module/components/Icon.js +30 -0
  4. package/lib/module/components/Icon.js.map +1 -1
  5. package/lib/module/components/index.js +1 -0
  6. package/lib/module/components/index.js.map +1 -1
  7. package/lib/module/index.js +1 -1
  8. package/lib/module/index.js.map +1 -1
  9. package/lib/module/screens/index.js +1 -0
  10. package/lib/module/screens/index.js.map +1 -1
  11. package/lib/module/screens/qbank/QBankQuestionScreen.js +415 -0
  12. package/lib/module/screens/qbank/QBankQuestionScreen.js.map +1 -0
  13. package/lib/module/screens/qbank/QBankSubTopicScreen.js +55 -75
  14. package/lib/module/screens/qbank/QBankSubTopicScreen.js.map +1 -1
  15. package/lib/typescript/src/components/BottomSheet.d.ts +9 -0
  16. package/lib/typescript/src/components/BottomSheet.d.ts.map +1 -0
  17. package/lib/typescript/src/components/Icon.d.ts +1 -1
  18. package/lib/typescript/src/components/Icon.d.ts.map +1 -1
  19. package/lib/typescript/src/components/index.d.ts +1 -0
  20. package/lib/typescript/src/components/index.d.ts.map +1 -1
  21. package/lib/typescript/src/index.d.ts +1 -1
  22. package/lib/typescript/src/index.d.ts.map +1 -1
  23. package/lib/typescript/src/screens/index.d.ts +1 -0
  24. package/lib/typescript/src/screens/index.d.ts.map +1 -1
  25. package/lib/typescript/src/screens/qbank/QBankQuestionScreen.d.ts +16 -0
  26. package/lib/typescript/src/screens/qbank/QBankQuestionScreen.d.ts.map +1 -0
  27. package/lib/typescript/src/screens/qbank/QBankSubTopicScreen.d.ts +1 -0
  28. package/lib/typescript/src/screens/qbank/QBankSubTopicScreen.d.ts.map +1 -1
  29. package/lib/typescript/src/types/index.d.ts +15 -0
  30. package/lib/typescript/src/types/index.d.ts.map +1 -1
  31. package/package.json +1 -1
  32. package/src/components/BottomSheet.tsx +144 -0
  33. package/src/components/Icon.tsx +12 -2
  34. package/src/components/index.ts +1 -0
  35. package/src/index.tsx +1 -0
  36. package/src/screens/index.ts +1 -0
  37. package/src/screens/qbank/QBankQuestionScreen.tsx +393 -0
  38. package/src/screens/qbank/QBankSubTopicScreen.tsx +42 -61
  39. package/src/types/index.ts +17 -0
@@ -0,0 +1,393 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import {
3
+ View,
4
+ ScrollView,
5
+ StyleSheet,
6
+ TouchableOpacity,
7
+ StatusBar,
8
+ } from 'react-native';
9
+ import { SafeAreaView } from 'react-native-safe-area-context';
10
+ import { useTheme } from '../../context/ThemeContext';
11
+ import { Typography, Icon, Button } from '../../components';
12
+ import type { QBankQuestionParams, QBankOption } from '../../types';
13
+
14
+ interface QBankQuestionScreenProps {
15
+ params: QBankQuestionParams;
16
+ initialTimeSeconds?: number;
17
+ onTimeUp?: () => void;
18
+ onBack?: () => void;
19
+ onNext?: () => void;
20
+ onPrev?: () => void;
21
+ onPressOption?: (optionId: string) => void;
22
+ onPressReport?: () => void;
23
+ onPressUsers?: () => void;
24
+ }
25
+
26
+ const formatTime = (totalSeconds: number) => {
27
+ const minutes = Math.floor(totalSeconds / 60);
28
+ const seconds = totalSeconds % 60;
29
+ return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
30
+ };
31
+
32
+ export const QBankQuestionScreen: React.FC<QBankQuestionScreenProps> = ({
33
+ params,
34
+ initialTimeSeconds = 60,
35
+ onTimeUp,
36
+ onBack,
37
+ onNext,
38
+ onPrev,
39
+ onPressOption,
40
+ onPressReport,
41
+ onPressUsers,
42
+ }) => {
43
+ const theme = useTheme();
44
+ const [timeLeft, setTimeLeft] = useState(initialTimeSeconds);
45
+
46
+ useEffect(() => {
47
+ if (timeLeft <= 0) {
48
+ onTimeUp?.();
49
+ return;
50
+ }
51
+
52
+ const timer = setInterval(() => {
53
+ setTimeLeft((prev) => prev - 1);
54
+ }, 1000);
55
+
56
+ return () => clearInterval(timer);
57
+ }, [timeLeft, onTimeUp]);
58
+
59
+ const progressPercentage = (params.questionNumber / params.totalQuestions) * 100;
60
+
61
+ const renderOption = (option: QBankOption, index: number) => {
62
+ const letters = ['A', 'B', 'C', 'D', 'E', 'F'];
63
+ const letter = letters[index] || '';
64
+
65
+ let borderColor = '#E5E7EB'; // Default grey
66
+ let backgroundColor = '#FFFFFF';
67
+ let iconName: 'check-circle' | 'x-circle' | undefined;
68
+ let iconColor = theme.textSecondary;
69
+ let statusText = '';
70
+ let statusColor = '';
71
+
72
+ if (option.status === 'correct') {
73
+ borderColor = theme.success;
74
+ backgroundColor = '#F0FDF4'; // light green bg
75
+ iconName = 'check-circle';
76
+ iconColor = theme.success;
77
+ statusText = 'Correct';
78
+ statusColor = theme.success;
79
+ } else if (option.status === 'incorrect') {
80
+ borderColor = '#EF4444';
81
+ backgroundColor = '#FEF2F2'; // light red bg
82
+ iconName = 'x-circle';
83
+ iconColor = '#EF4444';
84
+ statusText = 'InCorrect'; // matching user image
85
+ statusColor = '#EF4444';
86
+ }
87
+
88
+ return (
89
+ <TouchableOpacity
90
+ key={option.id}
91
+ style={[styles.optionContainer, { borderColor, backgroundColor }]}
92
+ onPress={() => onPressOption?.(option.id)}
93
+ activeOpacity={0.7}
94
+ >
95
+ <View style={styles.optionContent}>
96
+ {iconName ? (
97
+ <Icon name={iconName} size={20} color={iconColor} />
98
+ ) : (
99
+ <View style={styles.emptyCircle} />
100
+ )}
101
+ <View style={styles.optionTextWrapper}>
102
+ <Typography variant="body" color={theme.textPrimary} style={styles.optionText}>
103
+ {letter}. {option.text}
104
+ </Typography>
105
+ {(option.status === 'correct' || option.status === 'incorrect') && (
106
+ <Typography variant="caption" color={statusColor} style={styles.optionStatusText}>
107
+ {statusText} <Typography variant="caption" color={theme.textSecondary}>[{option.percentage}%]</Typography>
108
+ </Typography>
109
+ )}
110
+ {option.status === 'default' && option.percentage !== undefined && (
111
+ <Typography variant="caption" color={theme.textSecondary} style={styles.optionStatusText}>
112
+ [{option.percentage}%]
113
+ </Typography>
114
+ )}
115
+ </View>
116
+ </View>
117
+ </TouchableOpacity>
118
+ );
119
+ };
120
+
121
+ return (
122
+ <SafeAreaView style={[styles.container, { backgroundColor: '#E5E7EB' }]} edges={['top', 'bottom']}>
123
+ <StatusBar barStyle="dark-content" backgroundColor="#FFFFFF" />
124
+
125
+ <View style={styles.card}>
126
+ {/* Header */}
127
+ <View style={styles.header}>
128
+ <TouchableOpacity onPress={onBack} style={styles.headerIconBtnLeft}>
129
+ <Icon name="chevron-left" size={24} color={theme.textPrimary} />
130
+ </TouchableOpacity>
131
+ <Typography variant="h3" color={theme.textPrimary} style={styles.headerTitle}>
132
+ {params.headerTitle}
133
+ </Typography>
134
+ <View style={styles.headerIconBtnRight} />
135
+ </View>
136
+
137
+ {/* Progress Bar */}
138
+ <View style={styles.progressTrack}>
139
+ <View style={[styles.progressFill, { width: `${progressPercentage}%`, backgroundColor: theme.success }]} />
140
+ </View>
141
+
142
+ <ScrollView
143
+ contentContainerStyle={styles.scrollContent}
144
+ showsVerticalScrollIndicator={false}
145
+ >
146
+ {/* Top Bar: Question Number & Timer */}
147
+ <View style={styles.topBar}>
148
+ <Typography variant="caption" bold color={theme.textSecondary} style={styles.questionNumber}>
149
+ QUESTION {params.questionNumber}/{params.totalQuestions}
150
+ </Typography>
151
+ <View style={styles.timerWrapper}>
152
+ <Icon name="clock" size={14} color={theme.textSecondary} />
153
+ <Typography variant="caption" color={theme.textSecondary} style={styles.timerText}>
154
+ {formatTime(timeLeft)}
155
+ </Typography>
156
+ </View>
157
+ </View>
158
+
159
+ {/* Question Text */}
160
+ <Typography variant="h3" bold color={theme.textPrimary} style={styles.questionText}>
161
+ {params.questionText}
162
+ </Typography>
163
+
164
+ {/* Options */}
165
+ <View style={styles.optionsList}>
166
+ {params.options.map((option, index) => renderOption(option, index))}
167
+ </View>
168
+
169
+ {/* Explanation Section */}
170
+ {params.explanation && (
171
+ <View style={styles.explanationContainer}>
172
+ <Typography variant="caption" bold color={theme.textPrimary} style={styles.explanationTitle}>
173
+ EXPLANATION
174
+ </Typography>
175
+ <Typography variant="body" color={theme.textSecondary} style={styles.explanationText}>
176
+ {params.explanation}
177
+ </Typography>
178
+
179
+ {params.referenceId && (
180
+ <View style={styles.referenceContainer}>
181
+ <Typography variant="caption" color={theme.textSecondary} style={styles.referenceText}>
182
+ {params.referenceId}
183
+ </Typography>
184
+ </View>
185
+ )}
186
+ </View>
187
+ )}
188
+ </ScrollView>
189
+
190
+ {/* Bottom Actions */}
191
+ <View style={styles.bottomActions}>
192
+ <View style={styles.leftActions}>
193
+ <TouchableOpacity style={styles.iconAction} onPress={onPressReport}>
194
+ <Icon name="alert-circle" size={20} color={theme.textSecondary} />
195
+ </TouchableOpacity>
196
+ <TouchableOpacity style={styles.iconAction} onPress={onPressUsers}>
197
+ <Icon name="users" size={20} color={theme.textSecondary} />
198
+ </TouchableOpacity>
199
+ </View>
200
+ <View style={styles.rightActions}>
201
+ <TouchableOpacity style={styles.prevBtn} onPress={onPrev}>
202
+ <Typography variant="body" bold color={theme.textPrimary}>
203
+ Prev
204
+ </Typography>
205
+ </TouchableOpacity>
206
+ <Button
207
+ title="Next"
208
+ onPress={onNext || (() => {})}
209
+ style={styles.nextBtn}
210
+ textStyle={styles.nextBtnText}
211
+ />
212
+ </View>
213
+ </View>
214
+
215
+ </View>
216
+ </SafeAreaView>
217
+ );
218
+ };
219
+
220
+ const styles = StyleSheet.create({
221
+ container: {
222
+ flex: 1,
223
+ padding: 16, // Gray margin around the card
224
+ },
225
+ card: {
226
+ flex: 1,
227
+ backgroundColor: '#FFFFFF',
228
+ borderRadius: 8,
229
+ overflow: 'hidden',
230
+ shadowColor: '#000',
231
+ shadowOffset: { width: 0, height: 2 },
232
+ shadowOpacity: 0.1,
233
+ shadowRadius: 4,
234
+ elevation: 4,
235
+ },
236
+ header: {
237
+ flexDirection: 'row',
238
+ alignItems: 'center',
239
+ paddingHorizontal: 16,
240
+ paddingVertical: 12,
241
+ },
242
+ headerIconBtnLeft: {
243
+ width: 40,
244
+ height: 40,
245
+ justifyContent: 'center',
246
+ alignItems: 'flex-start',
247
+ },
248
+ headerRightActions: {
249
+ flexDirection: 'row',
250
+ alignItems: 'center',
251
+ },
252
+ headerIconBtnRight: {
253
+ width: 40,
254
+ height: 40,
255
+ justifyContent: 'center',
256
+ alignItems: 'flex-end',
257
+ },
258
+ headerTitle: {
259
+ fontSize: 16,
260
+ fontWeight: '600',
261
+ flex: 1,
262
+ textAlign: 'center',
263
+ },
264
+ progressTrack: {
265
+ height: 4,
266
+ backgroundColor: '#E5E7EB',
267
+ width: '100%',
268
+ },
269
+ progressFill: {
270
+ height: '100%',
271
+ },
272
+ scrollContent: {
273
+ padding: 20,
274
+ paddingBottom: 40,
275
+ },
276
+ topBar: {
277
+ flexDirection: 'row',
278
+ justifyContent: 'space-between',
279
+ alignItems: 'center',
280
+ marginBottom: 20,
281
+ },
282
+ questionNumber: {
283
+ fontSize: 12,
284
+ letterSpacing: 0.5,
285
+ },
286
+ timerWrapper: {
287
+ flexDirection: 'row',
288
+ alignItems: 'center',
289
+ },
290
+ timerText: {
291
+ fontSize: 12,
292
+ marginLeft: 6,
293
+ },
294
+ questionText: {
295
+ fontSize: 16,
296
+ lineHeight: 24,
297
+ marginBottom: 24,
298
+ },
299
+ optionsList: {
300
+ marginBottom: 32,
301
+ },
302
+ optionContainer: {
303
+ borderWidth: 1,
304
+ borderRadius: 8,
305
+ padding: 16,
306
+ marginBottom: 12,
307
+ },
308
+ optionContent: {
309
+ flexDirection: 'row',
310
+ alignItems: 'flex-start',
311
+ },
312
+ emptyCircle: {
313
+ width: 20,
314
+ height: 20,
315
+ borderRadius: 10,
316
+ borderWidth: 1,
317
+ borderColor: '#D1D5DB',
318
+ },
319
+ optionTextWrapper: {
320
+ flex: 1,
321
+ marginLeft: 12,
322
+ },
323
+ optionText: {
324
+ fontSize: 14,
325
+ lineHeight: 20,
326
+ },
327
+ optionStatusText: {
328
+ marginTop: 6,
329
+ fontSize: 12,
330
+ },
331
+ explanationContainer: {
332
+ marginTop: 16,
333
+ },
334
+ explanationTitle: {
335
+ fontSize: 12,
336
+ marginBottom: 12,
337
+ },
338
+ explanationText: {
339
+ fontSize: 14,
340
+ lineHeight: 22,
341
+ },
342
+ referenceContainer: {
343
+ marginTop: 24,
344
+ paddingTop: 16,
345
+ borderTopWidth: 1,
346
+ borderTopColor: '#F3F4F6',
347
+ alignItems: 'center',
348
+ },
349
+ referenceText: {
350
+ fontSize: 12,
351
+ },
352
+ bottomActions: {
353
+ flexDirection: 'row',
354
+ justifyContent: 'space-between',
355
+ alignItems: 'center',
356
+ padding: 16,
357
+ borderTopWidth: 1,
358
+ borderTopColor: '#F3F4F6',
359
+ backgroundColor: '#FFFFFF',
360
+ },
361
+ leftActions: {
362
+ flexDirection: 'row',
363
+ alignItems: 'center',
364
+ },
365
+ iconAction: {
366
+ width: 36,
367
+ height: 36,
368
+ justifyContent: 'center',
369
+ alignItems: 'center',
370
+ marginRight: 8,
371
+ borderRadius: 18,
372
+ borderWidth: 1,
373
+ borderColor: '#E5E7EB',
374
+ },
375
+ rightActions: {
376
+ flexDirection: 'row',
377
+ alignItems: 'center',
378
+ },
379
+ prevBtn: {
380
+ marginRight: 20,
381
+ },
382
+ nextBtn: {
383
+ backgroundColor: '#10B981',
384
+ paddingHorizontal: 24,
385
+ paddingVertical: 10,
386
+ height: 'auto',
387
+ minWidth: 80,
388
+ },
389
+ nextBtnText: {
390
+ fontSize: 14,
391
+ fontWeight: '600',
392
+ },
393
+ });
@@ -6,12 +6,10 @@ import {
6
6
  TouchableOpacity,
7
7
  StatusBar,
8
8
  FlatList,
9
- Modal,
10
- TouchableWithoutFeedback,
11
9
  } from 'react-native';
12
10
  import { SafeAreaView } from 'react-native-safe-area-context';
13
11
  import { useTheme } from '../../context/ThemeContext';
14
- import { Typography, Icon } from '../../components';
12
+ import { Typography, Icon, BottomSheet } from '../../components';
15
13
  import type { QBankSubTopicParams } from '../../types';
16
14
 
17
15
  interface QBankSubTopicScreenProps {
@@ -21,6 +19,7 @@ interface QBankSubTopicScreenProps {
21
19
  onPressModule?: (moduleId: string) => void;
22
20
  onPressFilter?: (filter: string) => void;
23
21
  onPressSearch?: () => void;
22
+ isProUser?: boolean;
24
23
  }
25
24
 
26
25
  export const QBankSubTopicScreen: React.FC<QBankSubTopicScreenProps> = ({
@@ -30,6 +29,7 @@ export const QBankSubTopicScreen: React.FC<QBankSubTopicScreenProps> = ({
30
29
  onPressModule,
31
30
  onPressFilter,
32
31
  onPressSearch,
32
+ isProUser = false,
33
33
  }) => {
34
34
  const theme = useTheme();
35
35
  const [activeFilter, setActiveFilter] = useState(params.activeFilter || params.filters[0]);
@@ -193,25 +193,27 @@ export const QBankSubTopicScreen: React.FC<QBankSubTopicScreenProps> = ({
193
193
  </Typography>
194
194
  </View>
195
195
  )}
196
- {moduleItem.status === 'locked' && (
197
- <View style={styles.lockedBadge}>
198
- <Icon name="lock" size={12} color="#64748B" />
196
+ {moduleItem.status === 'locked' && !isProUser && (
197
+ <View style={styles.proBadge}>
198
+ <Typography variant="caption" bold color="#FFFFFF" style={{fontSize: 7}}>
199
+ PRO
200
+ </Typography>
199
201
  </View>
200
202
  )}
201
203
  {moduleItem.attemptStatus === 'resume' && (
202
- <Typography variant="caption" bold color="#F59E0B" style={{fontSize: 11}}>
204
+ <Typography variant="caption" bold color="#F59E0B" style={{fontSize: 9}}>
203
205
  RESUME
204
206
  </Typography>
205
207
  )}
206
208
  {moduleItem.attemptStatus === 'completed' && (
207
209
  <View style={{ alignItems: 'flex-end' }}>
208
210
  <View style={styles.completedBadge}>
209
- <Typography variant="caption" bold color="#FFFFFF" style={{fontSize: 8}}>
211
+ <Typography variant="caption" bold color="#FFFFFF" style={{fontSize: 7}}>
210
212
  COMPLETED
211
213
  </Typography>
212
214
  </View>
213
215
  {moduleItem.correctPercentage !== undefined && (
214
- <Typography variant="caption" bold color={theme.success} style={{fontSize: 12, marginTop: 4}}>
216
+ <Typography variant="caption" bold color={theme.success} style={{fontSize: 10, marginTop: 4}}>
215
217
  {moduleItem.correctPercentage}% Correct
216
218
  </Typography>
217
219
  )}
@@ -228,41 +230,34 @@ export const QBankSubTopicScreen: React.FC<QBankSubTopicScreenProps> = ({
228
230
  </ScrollView>
229
231
 
230
232
  {/* Table of Contents Bottom Sheet Modal */}
231
- <Modal
233
+ <BottomSheet
232
234
  visible={showTOC}
233
- transparent={true}
234
- animationType="slide"
235
- onRequestClose={() => setShowTOC(false)}
235
+ onClose={() => setShowTOC(false)}
236
+ height={400}
236
237
  >
237
- <TouchableOpacity style={styles.modalOverlay} activeOpacity={1} onPress={() => setShowTOC(false)}>
238
- <TouchableWithoutFeedback>
239
- <View style={styles.bottomSheet}>
240
- <View style={styles.sheetHeader}>
241
- <Typography variant="h3" bold color={theme.textPrimary}>
242
- Table of Contents
243
- </Typography>
244
- <TouchableOpacity onPress={() => setShowTOC(false)}>
245
- <Icon name="x" size={24} color={theme.textSecondary} />
246
- </TouchableOpacity>
247
- </View>
248
- <ScrollView style={styles.sheetScroll}>
249
- {params.sections.map((section) => (
250
- <TouchableOpacity
251
- key={section.id}
252
- style={styles.sheetItem}
253
- onPress={() => scrollToSection(section.id)}
254
- >
255
- <Typography variant="body" color={theme.textPrimary}>
256
- {section.title}
257
- </Typography>
258
- <Icon name="chevron-right" size={20} color={theme.textSecondary} />
259
- </TouchableOpacity>
260
- ))}
261
- </ScrollView>
262
- </View>
263
- </TouchableWithoutFeedback>
264
- </TouchableOpacity>
265
- </Modal>
238
+ <View style={styles.sheetHeader}>
239
+ <Typography variant="h3" bold color={theme.textPrimary}>
240
+ Table of Contents
241
+ </Typography>
242
+ <TouchableOpacity onPress={() => setShowTOC(false)}>
243
+ <Icon name="x" size={24} color={theme.textSecondary} />
244
+ </TouchableOpacity>
245
+ </View>
246
+ <ScrollView style={styles.sheetScroll}>
247
+ {params.sections.map((section) => (
248
+ <TouchableOpacity
249
+ key={section.id}
250
+ style={styles.sheetItem}
251
+ onPress={() => scrollToSection(section.id)}
252
+ >
253
+ <Typography variant="body" color={theme.textPrimary}>
254
+ {section.title}
255
+ </Typography>
256
+ <Icon name="chevron-right" size={20} color={theme.textSecondary} />
257
+ </TouchableOpacity>
258
+ ))}
259
+ </ScrollView>
260
+ </BottomSheet>
266
261
 
267
262
  </SafeAreaView>
268
263
  );
@@ -436,13 +431,11 @@ const styles = StyleSheet.create({
436
431
  borderColor: '#10B981',
437
432
  backgroundColor: '#FFFFFF',
438
433
  },
439
- lockedBadge: {
440
- width: 24,
441
- height: 24,
442
- borderRadius: 12,
443
- backgroundColor: '#F1F5F9',
444
- justifyContent: 'center',
445
- alignItems: 'center',
434
+ proBadge: {
435
+ paddingHorizontal: 6,
436
+ paddingVertical: 2,
437
+ borderRadius: 4,
438
+ backgroundColor: '#F59E0B',
446
439
  },
447
440
  completedBadge: {
448
441
  paddingHorizontal: 6,
@@ -450,18 +443,6 @@ const styles = StyleSheet.create({
450
443
  borderRadius: 4,
451
444
  backgroundColor: '#10B981',
452
445
  },
453
- modalOverlay: {
454
- flex: 1,
455
- backgroundColor: 'rgba(0,0,0,0.4)',
456
- justifyContent: 'flex-end',
457
- },
458
- bottomSheet: {
459
- backgroundColor: '#FFFFFF',
460
- borderTopLeftRadius: 20,
461
- borderTopRightRadius: 20,
462
- paddingBottom: 40,
463
- maxHeight: '70%',
464
- },
465
446
  sheetHeader: {
466
447
  flexDirection: 'row',
467
448
  justifyContent: 'space-between',
@@ -239,6 +239,23 @@ export interface QBankSubTopicSection {
239
239
  modules: QBankSubTopicModule[];
240
240
  }
241
241
 
242
+ export interface QBankOption {
243
+ id: string;
244
+ text: string;
245
+ status: 'default' | 'correct' | 'incorrect';
246
+ percentage?: number; // e.g. 20 for [20%]
247
+ }
248
+
249
+ export interface QBankQuestionParams {
250
+ headerTitle: string;
251
+ questionNumber: number;
252
+ totalQuestions: number;
253
+ questionText: string;
254
+ options: QBankOption[];
255
+ explanation?: string;
256
+ referenceId?: string;
257
+ }
258
+
242
259
  export interface QBankSubTopicParams {
243
260
  header: {
244
261
  title: string;