react-native-elearn-ui 0.1.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.
Files changed (100) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +37 -0
  3. package/lib/module/components/Button.js +82 -0
  4. package/lib/module/components/Button.js.map +1 -0
  5. package/lib/module/components/Card.js +49 -0
  6. package/lib/module/components/Card.js.map +1 -0
  7. package/lib/module/components/CircularProgress.js +80 -0
  8. package/lib/module/components/CircularProgress.js.map +1 -0
  9. package/lib/module/components/Icon.js +268 -0
  10. package/lib/module/components/Icon.js.map +1 -0
  11. package/lib/module/components/ProgressBar.js +39 -0
  12. package/lib/module/components/ProgressBar.js.map +1 -0
  13. package/lib/module/components/Typography.js +102 -0
  14. package/lib/module/components/Typography.js.map +1 -0
  15. package/lib/module/components/index.js +10 -0
  16. package/lib/module/components/index.js.map +1 -0
  17. package/lib/module/context/ElearnConfigContext.js +76 -0
  18. package/lib/module/context/ElearnConfigContext.js.map +1 -0
  19. package/lib/module/context/ThemeContext.js +45 -0
  20. package/lib/module/context/ThemeContext.js.map +1 -0
  21. package/lib/module/index.js +13 -0
  22. package/lib/module/index.js.map +1 -0
  23. package/lib/module/multiply.js +6 -0
  24. package/lib/module/multiply.js.map +1 -0
  25. package/lib/module/package.json +1 -0
  26. package/lib/module/screens/HomeScreen.js +330 -0
  27. package/lib/module/screens/HomeScreen.js.map +1 -0
  28. package/lib/module/screens/QbankScreen.js +200 -0
  29. package/lib/module/screens/QbankScreen.js.map +1 -0
  30. package/lib/module/screens/QuestionScreen.js +383 -0
  31. package/lib/module/screens/QuestionScreen.js.map +1 -0
  32. package/lib/module/screens/QuizStartScreen.js +230 -0
  33. package/lib/module/screens/QuizStartScreen.js.map +1 -0
  34. package/lib/module/screens/ResultScreen.js +318 -0
  35. package/lib/module/screens/ResultScreen.js.map +1 -0
  36. package/lib/module/screens/TopicListScreen.js +222 -0
  37. package/lib/module/screens/TopicListScreen.js.map +1 -0
  38. package/lib/module/screens/index.js +9 -0
  39. package/lib/module/screens/index.js.map +1 -0
  40. package/lib/module/types/index.js +2 -0
  41. package/lib/module/types/index.js.map +1 -0
  42. package/lib/typescript/package.json +1 -0
  43. package/lib/typescript/src/components/Button.d.ts +14 -0
  44. package/lib/typescript/src/components/Button.d.ts.map +1 -0
  45. package/lib/typescript/src/components/Card.d.ts +12 -0
  46. package/lib/typescript/src/components/Card.d.ts.map +1 -0
  47. package/lib/typescript/src/components/CircularProgress.d.ts +15 -0
  48. package/lib/typescript/src/components/CircularProgress.d.ts.map +1 -0
  49. package/lib/typescript/src/components/Icon.d.ts +11 -0
  50. package/lib/typescript/src/components/Icon.d.ts.map +1 -0
  51. package/lib/typescript/src/components/ProgressBar.d.ts +11 -0
  52. package/lib/typescript/src/components/ProgressBar.d.ts.map +1 -0
  53. package/lib/typescript/src/components/Typography.d.ts +12 -0
  54. package/lib/typescript/src/components/Typography.d.ts.map +1 -0
  55. package/lib/typescript/src/components/index.d.ts +9 -0
  56. package/lib/typescript/src/components/index.d.ts.map +1 -0
  57. package/lib/typescript/src/context/ElearnConfigContext.d.ts +22 -0
  58. package/lib/typescript/src/context/ElearnConfigContext.d.ts.map +1 -0
  59. package/lib/typescript/src/context/ThemeContext.d.ts +11 -0
  60. package/lib/typescript/src/context/ThemeContext.d.ts.map +1 -0
  61. package/lib/typescript/src/index.d.ts +6 -0
  62. package/lib/typescript/src/index.d.ts.map +1 -0
  63. package/lib/typescript/src/multiply.d.ts +2 -0
  64. package/lib/typescript/src/multiply.d.ts.map +1 -0
  65. package/lib/typescript/src/screens/HomeScreen.d.ts +3 -0
  66. package/lib/typescript/src/screens/HomeScreen.d.ts.map +1 -0
  67. package/lib/typescript/src/screens/QbankScreen.d.ts +3 -0
  68. package/lib/typescript/src/screens/QbankScreen.d.ts.map +1 -0
  69. package/lib/typescript/src/screens/QuestionScreen.d.ts +11 -0
  70. package/lib/typescript/src/screens/QuestionScreen.d.ts.map +1 -0
  71. package/lib/typescript/src/screens/QuizStartScreen.d.ts +10 -0
  72. package/lib/typescript/src/screens/QuizStartScreen.d.ts.map +1 -0
  73. package/lib/typescript/src/screens/ResultScreen.d.ts +11 -0
  74. package/lib/typescript/src/screens/ResultScreen.d.ts.map +1 -0
  75. package/lib/typescript/src/screens/TopicListScreen.d.ts +9 -0
  76. package/lib/typescript/src/screens/TopicListScreen.d.ts.map +1 -0
  77. package/lib/typescript/src/screens/index.d.ts +7 -0
  78. package/lib/typescript/src/screens/index.d.ts.map +1 -0
  79. package/lib/typescript/src/types/index.d.ts +107 -0
  80. package/lib/typescript/src/types/index.d.ts.map +1 -0
  81. package/package.json +121 -0
  82. package/src/components/Button.tsx +101 -0
  83. package/src/components/Card.tsx +57 -0
  84. package/src/components/CircularProgress.tsx +93 -0
  85. package/src/components/Icon.tsx +338 -0
  86. package/src/components/ProgressBar.tsx +47 -0
  87. package/src/components/Typography.tsx +112 -0
  88. package/src/components/index.ts +8 -0
  89. package/src/context/ElearnConfigContext.tsx +90 -0
  90. package/src/context/ThemeContext.tsx +37 -0
  91. package/src/index.tsx +33 -0
  92. package/src/multiply.tsx +3 -0
  93. package/src/screens/HomeScreen.tsx +291 -0
  94. package/src/screens/QbankScreen.tsx +176 -0
  95. package/src/screens/QuestionScreen.tsx +402 -0
  96. package/src/screens/QuizStartScreen.tsx +215 -0
  97. package/src/screens/ResultScreen.tsx +303 -0
  98. package/src/screens/TopicListScreen.tsx +200 -0
  99. package/src/screens/index.ts +6 -0
  100. package/src/types/index.ts +122 -0
@@ -0,0 +1,402 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import {
3
+ View,
4
+ ScrollView,
5
+ StyleSheet,
6
+ TouchableOpacity,
7
+ SafeAreaView,
8
+ StatusBar,
9
+ Image,
10
+ } from 'react-native';
11
+ import type { Question, QuizResults } from '../types';
12
+ import { useElearnConfig } from '../context/ElearnConfigContext';
13
+ import { useTheme } from '../context/ThemeContext';
14
+ import { Typography, Card, Button, Icon, ProgressBar } from '../components';
15
+
16
+ interface QuestionScreenProps {
17
+ quizId: string;
18
+ questions: Question[];
19
+ title: string;
20
+ onClose: () => void;
21
+ }
22
+
23
+ export const QuestionScreen: React.FC<QuestionScreenProps> = ({
24
+ quizId,
25
+ questions,
26
+ title,
27
+ onClose,
28
+ }) => {
29
+ const { callbacks, getString } = useElearnConfig();
30
+ const theme = useTheme();
31
+
32
+ const [currentIndex, setCurrentIndex] = useState(0);
33
+ const [selectedOption, setSelectedOption] = useState<number | null>(null);
34
+ const [isAnswered, setIsAnswered] = useState(false);
35
+ const [userAnswers, setUserAnswers] = useState<{ [questionId: string]: number }>({});
36
+ const [correctCount, setCorrectCount] = useState(0);
37
+ const [startTime] = useState(Date.now());
38
+ const [seconds, setSeconds] = useState(0);
39
+
40
+ // Timer effect
41
+ useEffect(() => {
42
+ const timer = setInterval(() => {
43
+ setSeconds((prev) => prev + 1);
44
+ }, 1000);
45
+ return () => clearInterval(timer);
46
+ }, []);
47
+
48
+ if (!questions || questions.length === 0) {
49
+ return (
50
+ <SafeAreaView style={[styles.safeArea, { backgroundColor: theme.background }]}>
51
+ <View style={styles.errorContainer}>
52
+ <Typography variant="h2" bold>No questions available</Typography>
53
+ <Button title="Go Back" onPress={onClose} style={styles.errorButton} />
54
+ </View>
55
+ </SafeAreaView>
56
+ );
57
+ }
58
+
59
+ const currentQuestion = questions[currentIndex]!;
60
+ const optionPrefixes = ['A', 'B', 'C', 'D', 'E', 'F'];
61
+
62
+ const formatTime = (totalSeconds: number) => {
63
+ const mins = Math.floor(totalSeconds / 60);
64
+ const secs = totalSeconds % 60;
65
+ return `${mins}:${secs.toString().padStart(2, '0')}`;
66
+ };
67
+
68
+ const handleOptionPress = (index: number) => {
69
+ if (isAnswered) return; // Prevent changing answer after selection
70
+
71
+ setSelectedOption(index);
72
+ setIsAnswered(true);
73
+
74
+ const updatedAnswers = { ...userAnswers, [currentQuestion.id]: index };
75
+ setUserAnswers(updatedAnswers);
76
+
77
+ const isCorrect = index === currentQuestion.correctOptionIndex;
78
+ if (isCorrect) {
79
+ setCorrectCount((prev) => prev + 1);
80
+ }
81
+ };
82
+
83
+ const handleNext = () => {
84
+ if (currentIndex < questions.length - 1) {
85
+ setCurrentIndex((prev) => prev + 1);
86
+ setSelectedOption(null);
87
+ setIsAnswered(false);
88
+ } else {
89
+ // Last question - finish quiz
90
+ const totalTimeSeconds = Math.round((Date.now() - startTime) / 1000);
91
+ const wrongCount = questions.length - correctCount;
92
+ const accuracy = Math.round((correctCount / questions.length) * 100);
93
+
94
+ const results: QuizResults = {
95
+ quizId,
96
+ totalQuestions: questions.length,
97
+ correctCount,
98
+ wrongCount,
99
+ skippedCount: 0,
100
+ accuracy,
101
+ timeSpentSeconds: totalTimeSeconds,
102
+ userAnswers,
103
+ };
104
+
105
+ callbacks.onFinishQuiz?.(quizId, results);
106
+ }
107
+ };
108
+
109
+ const progress = (currentIndex + 1) / questions.length;
110
+
111
+ return (
112
+ <SafeAreaView style={[styles.safeArea, { backgroundColor: theme.background }]}>
113
+ <StatusBar barStyle="dark-content" />
114
+
115
+ {/* Header */}
116
+ <View style={styles.header}>
117
+ <TouchableOpacity onPress={onClose} style={styles.closeButton}>
118
+ <Icon name="x-circle" size={24} color={theme.textSecondary} />
119
+ </TouchableOpacity>
120
+
121
+ <View style={styles.headerCenter}>
122
+ <Typography variant="h3" bold numberOfLines={1}>
123
+ {title}
124
+ </Typography>
125
+ </View>
126
+
127
+ <View style={styles.timerRow}>
128
+ <Icon name="clock" size={16} color={theme.textSecondary} />
129
+ <Typography variant="caption" bold color={theme.textSecondary} style={styles.timerText}>
130
+ {formatTime(seconds)}
131
+ </Typography>
132
+ </View>
133
+ </View>
134
+
135
+ {/* Progress Bar */}
136
+ <ProgressBar progress={progress} height={4} style={styles.progressTracker} />
137
+
138
+ <ScrollView contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={false}>
139
+
140
+ {/* Question Info */}
141
+ <View style={styles.questionMeta}>
142
+ <Typography variant="caption" color={theme.primary} bold>
143
+ {getString('questionLabel').toUpperCase()} {currentIndex + 1} OF {questions.length}
144
+ </Typography>
145
+ </View>
146
+
147
+ {/* Question Text */}
148
+ <Typography variant="h2" bold style={styles.questionText}>
149
+ {currentQuestion.text}
150
+ </Typography>
151
+
152
+ {/* Optional Diagram Image */}
153
+ {currentQuestion.imageUrl && (
154
+ <Image
155
+ source={{ uri: currentQuestion.imageUrl }}
156
+ style={styles.questionImage}
157
+ resizeMode="contain"
158
+ />
159
+ )}
160
+
161
+ {/* Options List */}
162
+ <View style={styles.optionsList}>
163
+ {currentQuestion.options.map((option, idx) => {
164
+ const isSelected = selectedOption === idx;
165
+ const isCorrectAnswer = idx === currentQuestion.correctOptionIndex;
166
+
167
+ let optionBg = theme.cardBackground;
168
+ let optionBorder = theme.border;
169
+ let showFeedbackIcon: 'check' | 'cross' | null = null;
170
+
171
+ if (isAnswered) {
172
+ if (isCorrectAnswer) {
173
+ // Highlight correct answer in green
174
+ optionBg = 'rgba(16, 185, 129, 0.08)';
175
+ optionBorder = theme.success;
176
+ showFeedbackIcon = 'check';
177
+ } else if (isSelected) {
178
+ // Highlight selected incorrect answer in red
179
+ optionBg = 'rgba(239, 68, 68, 0.08)';
180
+ optionBorder = theme.danger;
181
+ showFeedbackIcon = 'cross';
182
+ }
183
+ } else if (isSelected) {
184
+ // Active highlight before submission
185
+ optionBg = theme.secondary;
186
+ optionBorder = theme.primary;
187
+ }
188
+
189
+ return (
190
+ <TouchableOpacity
191
+ key={idx}
192
+ activeOpacity={0.8}
193
+ onPress={() => handleOptionPress(idx)}
194
+ disabled={isAnswered}
195
+ style={[
196
+ styles.optionCard,
197
+ {
198
+ backgroundColor: optionBg,
199
+ borderColor: optionBorder,
200
+ },
201
+ ]}
202
+ >
203
+ <View style={styles.optionRow}>
204
+ <View
205
+ style={[
206
+ styles.prefixCircle,
207
+ {
208
+ backgroundColor: isSelected ? theme.primary : theme.secondary,
209
+ },
210
+ isAnswered && isCorrectAnswer && { backgroundColor: theme.success },
211
+ isAnswered && isSelected && !isCorrectAnswer && { backgroundColor: theme.danger },
212
+ ]}
213
+ >
214
+ <Typography
215
+ variant="caption"
216
+ bold
217
+ color={
218
+ isSelected || (isAnswered && isCorrectAnswer)
219
+ ? theme.textInverse
220
+ : theme.primary
221
+ }
222
+ >
223
+ {optionPrefixes[idx] || idx + 1}
224
+ </Typography>
225
+ </View>
226
+
227
+ <Typography
228
+ variant="body"
229
+ color={theme.textPrimary}
230
+ style={styles.optionText}
231
+ >
232
+ {option}
233
+ </Typography>
234
+
235
+ {isAnswered && showFeedbackIcon && (
236
+ <View style={styles.feedbackIcon}>
237
+ <Icon
238
+ name={showFeedbackIcon === 'check' ? 'check-circle' : 'x-circle'}
239
+ size={20}
240
+ color={showFeedbackIcon === 'check' ? theme.success : theme.danger}
241
+ />
242
+ </View>
243
+ )}
244
+ </View>
245
+ </TouchableOpacity>
246
+ );
247
+ })}
248
+ </View>
249
+
250
+ {/* Explanation Card (Appears after answer selection) */}
251
+ {isAnswered && (
252
+ <Card bordered style={styles.explanationCard}>
253
+ <View style={styles.explanationHeader}>
254
+ <Icon name="info" size={20} color={theme.primary} />
255
+ <Typography variant="h3" bold color={theme.primary} style={styles.explanationTitle}>
256
+ {getString('explanationLabel')}
257
+ </Typography>
258
+ </View>
259
+ <Typography variant="body" color={theme.textPrimary} style={styles.explanationBody}>
260
+ {currentQuestion.explanation}
261
+ </Typography>
262
+ </Card>
263
+ )}
264
+
265
+ </ScrollView>
266
+
267
+ {/* Next/Submit Button Panel */}
268
+ {isAnswered && (
269
+ <View style={[styles.footer, { backgroundColor: theme.cardBackground, borderTopColor: theme.border }]}>
270
+ <Button
271
+ title={
272
+ currentIndex === questions.length - 1
273
+ ? getString('submitButtonLabel')
274
+ : getString('nextButtonLabel')
275
+ }
276
+ onPress={handleNext}
277
+ variant="primary"
278
+ style={styles.nextButton}
279
+ />
280
+ </View>
281
+ )}
282
+ </SafeAreaView>
283
+ );
284
+ };
285
+
286
+ const styles = StyleSheet.create({
287
+ safeArea: {
288
+ flex: 1,
289
+ },
290
+ header: {
291
+ flexDirection: 'row',
292
+ justifyContent: 'space-between',
293
+ alignItems: 'center',
294
+ paddingHorizontal: 16,
295
+ paddingVertical: 12,
296
+ },
297
+ closeButton: {
298
+ padding: 4,
299
+ },
300
+ headerCenter: {
301
+ flex: 1,
302
+ alignItems: 'center',
303
+ marginHorizontal: 12,
304
+ },
305
+ timerRow: {
306
+ flexDirection: 'row',
307
+ alignItems: 'center',
308
+ paddingHorizontal: 10,
309
+ paddingVertical: 6,
310
+ borderRadius: 12,
311
+ backgroundColor: '#F1F5F9',
312
+ },
313
+ timerText: {
314
+ marginLeft: 6,
315
+ },
316
+ progressTracker: {
317
+ borderRadius: 0,
318
+ },
319
+ scrollContent: {
320
+ paddingHorizontal: 20,
321
+ paddingTop: 16,
322
+ paddingBottom: 40,
323
+ },
324
+ questionMeta: {
325
+ marginBottom: 8,
326
+ },
327
+ questionText: {
328
+ fontSize: 18,
329
+ lineHeight: 24,
330
+ marginBottom: 16,
331
+ },
332
+ questionImage: {
333
+ width: '100%',
334
+ height: 180,
335
+ borderRadius: 12,
336
+ marginBottom: 20,
337
+ backgroundColor: '#F8FAFC',
338
+ },
339
+ optionsList: {
340
+ marginBottom: 20,
341
+ },
342
+ optionCard: {
343
+ borderWidth: 1.5,
344
+ borderRadius: 14,
345
+ padding: 14,
346
+ marginBottom: 12,
347
+ },
348
+ optionRow: {
349
+ flexDirection: 'row',
350
+ alignItems: 'center',
351
+ },
352
+ prefixCircle: {
353
+ width: 28,
354
+ height: 28,
355
+ borderRadius: 14,
356
+ justifyContent: 'center',
357
+ alignItems: 'center',
358
+ marginRight: 12,
359
+ },
360
+ optionText: {
361
+ flex: 1,
362
+ lineHeight: 20,
363
+ },
364
+ feedbackIcon: {
365
+ marginLeft: 8,
366
+ },
367
+ explanationCard: {
368
+ padding: 20,
369
+ borderRadius: 16,
370
+ marginTop: 12,
371
+ borderWidth: 1,
372
+ borderStyle: 'dashed',
373
+ },
374
+ explanationHeader: {
375
+ flexDirection: 'row',
376
+ alignItems: 'center',
377
+ marginBottom: 10,
378
+ },
379
+ explanationTitle: {
380
+ marginLeft: 8,
381
+ },
382
+ explanationBody: {
383
+ lineHeight: 20,
384
+ },
385
+ footer: {
386
+ paddingHorizontal: 20,
387
+ paddingVertical: 14,
388
+ borderTopWidth: 1,
389
+ },
390
+ nextButton: {
391
+ width: '100%',
392
+ },
393
+ errorContainer: {
394
+ flex: 1,
395
+ justifyContent: 'center',
396
+ alignItems: 'center',
397
+ padding: 24,
398
+ },
399
+ errorButton: {
400
+ marginTop: 16,
401
+ },
402
+ });
@@ -0,0 +1,215 @@
1
+ import React from 'react';
2
+ import {
3
+ View,
4
+ StyleSheet,
5
+ TouchableOpacity,
6
+ SafeAreaView,
7
+ StatusBar,
8
+ } from 'react-native';
9
+ import type { Topic, Course } from '../types';
10
+ import { useElearnConfig } from '../context/ElearnConfigContext';
11
+ import { useTheme } from '../context/ThemeContext';
12
+ import { Typography, Card, Button, Icon } from '../components';
13
+
14
+ interface QuizStartScreenProps {
15
+ course: Course;
16
+ topic: Topic;
17
+ onBack: () => void;
18
+ }
19
+
20
+ export const QuizStartScreen: React.FC<QuizStartScreenProps> = ({
21
+ course,
22
+ topic,
23
+ onBack,
24
+ }) => {
25
+ const { callbacks, getString } = useElearnConfig();
26
+ const theme = useTheme();
27
+
28
+ const handleStart = () => {
29
+ if (callbacks.onStartQuiz && topic.questions) {
30
+ callbacks.onStartQuiz(topic.id, topic.questions, topic.title);
31
+ }
32
+ };
33
+
34
+ const infoItems = [
35
+ 'Immediate explanation display',
36
+ 'Interactive choice evaluation',
37
+ 'Self-paced learning structure',
38
+ 'Detailed performance scorecard',
39
+ ];
40
+
41
+ return (
42
+ <SafeAreaView style={[styles.safeArea, { backgroundColor: theme.background }]}>
43
+ <StatusBar barStyle="dark-content" />
44
+
45
+ {/* Header */}
46
+ <View style={styles.header}>
47
+ <TouchableOpacity
48
+ onPress={onBack}
49
+ style={[styles.backButton, { borderColor: theme.border }]}
50
+ >
51
+ <Icon name="arrow-left" size={20} color={theme.textPrimary} />
52
+ </TouchableOpacity>
53
+ <Typography variant="h3" bold style={styles.headerTitle} numberOfLines={1}>
54
+ {course.title}
55
+ </Typography>
56
+ <View style={styles.headerRightPlaceholder} />
57
+ </View>
58
+
59
+ <View style={styles.container}>
60
+ <View style={styles.content}>
61
+
62
+ {/* Main Info Card */}
63
+ <Card bordered style={styles.mainCard}>
64
+ <View style={[styles.badge, { backgroundColor: theme.secondary }]}>
65
+ <Typography variant="caption" color={theme.primary} bold>
66
+ {course.title.toUpperCase()}
67
+ </Typography>
68
+ </View>
69
+ <Typography variant="h1" bold style={styles.title}>
70
+ {topic.title}
71
+ </Typography>
72
+ <Typography variant="body" color={theme.textSecondary} style={styles.desc}>
73
+ Practice questions to master this topic. Dynamic evaluation will track your correctness and accuracy instantly.
74
+ </Typography>
75
+
76
+ <View style={styles.questionsStatsRow}>
77
+ <Icon name="book-open" size={20} color={theme.primary} />
78
+ <Typography variant="body" bold style={styles.statsText}>
79
+ {topic.totalQuestions} Questions Available
80
+ </Typography>
81
+ </View>
82
+ </Card>
83
+
84
+ {/* Guidelines / Info Section */}
85
+ <View style={styles.infoSection}>
86
+ <Typography variant="h2" bold style={styles.infoTitle}>
87
+ {getString('topicsAndItemsLabel')}
88
+ </Typography>
89
+
90
+ <View style={styles.guidelinesList}>
91
+ {infoItems.map((item, idx) => (
92
+ <View key={idx} style={styles.infoItemRow}>
93
+ <View style={styles.bulletContainer}>
94
+ <Icon name="check-circle" size={18} color={theme.primary} />
95
+ </View>
96
+ <Typography variant="body" color={theme.textSecondary} style={styles.infoItemText}>
97
+ {item}
98
+ </Typography>
99
+ </View>
100
+ ))}
101
+ </View>
102
+ </View>
103
+
104
+ </View>
105
+
106
+ {/* Footer Launch Button */}
107
+ <View style={[styles.footer, { backgroundColor: theme.cardBackground, borderTopColor: theme.border }]}>
108
+ <Button
109
+ title={getString('startQuizButton')}
110
+ onPress={handleStart}
111
+ variant="primary"
112
+ style={styles.startButton}
113
+ disabled={!topic.questions || topic.questions.length === 0}
114
+ />
115
+ </View>
116
+ </View>
117
+ </SafeAreaView>
118
+ );
119
+ };
120
+
121
+ const styles = StyleSheet.create({
122
+ safeArea: {
123
+ flex: 1,
124
+ },
125
+ header: {
126
+ flexDirection: 'row',
127
+ justifyContent: 'space-between',
128
+ alignItems: 'center',
129
+ paddingHorizontal: 20,
130
+ paddingVertical: 12,
131
+ },
132
+ backButton: {
133
+ width: 40,
134
+ height: 40,
135
+ borderRadius: 20,
136
+ borderWidth: 1,
137
+ justifyContent: 'center',
138
+ alignItems: 'center',
139
+ },
140
+ headerTitle: {
141
+ flex: 1,
142
+ textAlign: 'center',
143
+ marginHorizontal: 12,
144
+ },
145
+ headerRightPlaceholder: {
146
+ width: 40,
147
+ },
148
+ container: {
149
+ flex: 1,
150
+ justifyContent: 'space-between',
151
+ },
152
+ content: {
153
+ flex: 1,
154
+ paddingHorizontal: 20,
155
+ paddingTop: 12,
156
+ },
157
+ mainCard: {
158
+ padding: 24,
159
+ borderRadius: 20,
160
+ },
161
+ badge: {
162
+ alignSelf: 'flex-start',
163
+ paddingHorizontal: 10,
164
+ paddingVertical: 4,
165
+ borderRadius: 8,
166
+ marginBottom: 12,
167
+ },
168
+ title: {
169
+ fontSize: 22,
170
+ lineHeight: 28,
171
+ marginBottom: 8,
172
+ },
173
+ desc: {
174
+ lineHeight: 20,
175
+ marginBottom: 16,
176
+ },
177
+ questionsStatsRow: {
178
+ flexDirection: 'row',
179
+ alignItems: 'center',
180
+ marginTop: 8,
181
+ },
182
+ statsText: {
183
+ marginLeft: 8,
184
+ },
185
+ infoSection: {
186
+ marginTop: 28,
187
+ },
188
+ infoTitle: {
189
+ marginBottom: 16,
190
+ },
191
+ guidelinesList: {
192
+ marginTop: 4,
193
+ },
194
+ infoItemRow: {
195
+ flexDirection: 'row',
196
+ alignItems: 'flex-start',
197
+ marginBottom: 14,
198
+ },
199
+ bulletContainer: {
200
+ marginTop: 1,
201
+ },
202
+ infoItemText: {
203
+ marginLeft: 12,
204
+ flex: 1,
205
+ lineHeight: 18,
206
+ },
207
+ footer: {
208
+ paddingHorizontal: 20,
209
+ paddingVertical: 16,
210
+ borderTopWidth: 1,
211
+ },
212
+ startButton: {
213
+ width: '100%',
214
+ },
215
+ });