react-native-elearn-ui 0.1.4 → 0.1.6

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/Icon.js +97 -0
  2. package/lib/module/components/Icon.js.map +1 -1
  3. package/lib/module/screens/HomeScreen.js +22 -9
  4. package/lib/module/screens/HomeScreen.js.map +1 -1
  5. package/lib/module/screens/QbankScreen.js +255 -101
  6. package/lib/module/screens/QbankScreen.js.map +1 -1
  7. package/lib/module/screens/QuestionScreen.js +222 -181
  8. package/lib/module/screens/QuestionScreen.js.map +1 -1
  9. package/lib/module/screens/QuizStartScreen.js +137 -133
  10. package/lib/module/screens/QuizStartScreen.js.map +1 -1
  11. package/lib/module/screens/ResultScreen.js +3 -2
  12. package/lib/module/screens/ResultScreen.js.map +1 -1
  13. package/lib/module/screens/TopicListScreen.js +287 -137
  14. package/lib/module/screens/TopicListScreen.js.map +1 -1
  15. package/lib/typescript/src/components/Icon.d.ts +1 -1
  16. package/lib/typescript/src/components/Icon.d.ts.map +1 -1
  17. package/lib/typescript/src/screens/HomeScreen.d.ts +1 -0
  18. package/lib/typescript/src/screens/HomeScreen.d.ts.map +1 -1
  19. package/lib/typescript/src/screens/QbankScreen.d.ts +5 -1
  20. package/lib/typescript/src/screens/QbankScreen.d.ts.map +1 -1
  21. package/lib/typescript/src/screens/QuestionScreen.d.ts +1 -0
  22. package/lib/typescript/src/screens/QuestionScreen.d.ts.map +1 -1
  23. package/lib/typescript/src/screens/QuizStartScreen.d.ts +1 -0
  24. package/lib/typescript/src/screens/QuizStartScreen.d.ts.map +1 -1
  25. package/lib/typescript/src/screens/ResultScreen.d.ts +1 -0
  26. package/lib/typescript/src/screens/ResultScreen.d.ts.map +1 -1
  27. package/lib/typescript/src/screens/TopicListScreen.d.ts +1 -0
  28. package/lib/typescript/src/screens/TopicListScreen.d.ts.map +1 -1
  29. package/lib/typescript/src/types/index.d.ts +22 -0
  30. package/lib/typescript/src/types/index.d.ts.map +1 -1
  31. package/package.json +1 -1
  32. package/src/components/Icon.tsx +125 -1
  33. package/src/screens/HomeScreen.tsx +77 -51
  34. package/src/screens/QbankScreen.tsx +209 -79
  35. package/src/screens/QuestionScreen.tsx +185 -178
  36. package/src/screens/QuizStartScreen.tsx +127 -117
  37. package/src/screens/ResultScreen.tsx +13 -9
  38. package/src/screens/TopicListScreen.tsx +271 -116
  39. package/src/types/index.ts +10 -0
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useState } from 'react';
2
2
  import {
3
3
  View,
4
4
  ScrollView,
@@ -7,105 +7,193 @@ import {
7
7
  SafeAreaView,
8
8
  StatusBar,
9
9
  } from 'react-native';
10
- import type { Course } from '../types';
10
+ import type { Course, Topic } from '../types';
11
11
  import { useElearnConfig } from '../context/ElearnConfigContext';
12
12
  import { useTheme } from '../context/ThemeContext';
13
- import { Typography, Card, ProgressBar, Icon } from '../components';
13
+ import { Typography, Card, Icon } from '../components';
14
14
 
15
15
  interface TopicListScreenProps {
16
16
  course: Course;
17
17
  onBack: () => void;
18
+ hideHeader?: boolean;
18
19
  }
19
20
 
20
21
  export const TopicListScreen: React.FC<TopicListScreenProps> = ({
21
22
  course,
22
23
  onBack,
24
+ hideHeader = false,
23
25
  }) => {
24
- const { callbacks, getString } = useElearnConfig();
26
+ const { callbacks } = useElearnConfig();
25
27
  const theme = useTheme();
26
28
 
29
+ // Active filter tab
30
+ const [activeTab, setActiveTab] = useState('All');
31
+ const tabs = ['All', 'New', 'Completed', 'Paused', 'Not Started'];
32
+
33
+ // Group topics by category
34
+ const categoriesMap: { [key: string]: Topic[] } = {};
35
+ course.topics.forEach((topic) => {
36
+ const cat = topic.category || 'GENERAL';
37
+ if (!categoriesMap[cat]) {
38
+ categoriesMap[cat] = [];
39
+ }
40
+ categoriesMap[cat].push(topic);
41
+ });
42
+
43
+ const categories = Object.keys(categoriesMap);
44
+
45
+ // Keep a global sequential counter for the timeline nodes
46
+ let globalIndex = 0;
47
+
27
48
  return (
28
49
  <SafeAreaView style={[styles.safeArea, { backgroundColor: theme.background }]}>
29
50
  <StatusBar barStyle="dark-content" />
30
51
 
31
52
  {/* Header */}
32
- <View style={styles.header}>
33
- <TouchableOpacity
34
- onPress={onBack}
35
- style={[styles.backButton, { borderColor: theme.border }]}
36
- >
37
- <Icon name="arrow-left" size={20} color={theme.textPrimary} />
38
- </TouchableOpacity>
39
- <Typography variant="h3" bold style={styles.headerTitle} numberOfLines={1}>
40
- {course.title}
41
- </Typography>
42
- <View style={styles.headerRightPlaceholder} />
43
- </View>
53
+ {!hideHeader && (
54
+ <View style={styles.header}>
55
+ <TouchableOpacity onPress={onBack} style={styles.backButton}>
56
+ <Icon name="arrow-left" size={20} color={theme.textPrimary} />
57
+ </TouchableOpacity>
58
+ <Typography variant="h2" bold style={styles.headerTitle}>
59
+ QBank
60
+ </Typography>
61
+ <TouchableOpacity style={styles.searchButton}>
62
+ <Icon name="search" size={20} color={theme.textPrimary} />
63
+ </TouchableOpacity>
64
+ </View>
65
+ )}
44
66
 
45
67
  <ScrollView contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={false}>
46
68
 
47
- {/* Subject Summary Card */}
48
- <Card style={[styles.summaryCard, { backgroundColor: theme.primary }]}>
49
- <Typography variant="h2" bold color="#FFFFFF">
69
+ {/* Subject Header Suffix */}
70
+ <View style={styles.subjectHeader}>
71
+ <Typography variant="h1" bold color={theme.textPrimary} style={styles.subjectTitle}>
50
72
  {course.title}
51
73
  </Typography>
52
- {course.description && (
53
- <Typography variant="caption" color="rgba(255, 255, 255, 0.8)" style={styles.summaryDesc}>
54
- {course.description}
55
- </Typography>
56
- )}
57
- <View style={styles.progressSummaryContainer}>
58
- <View style={styles.progressSummaryText}>
59
- <Typography variant="caption" color="#FFFFFF">
60
- Overall Subject Progress
61
- </Typography>
62
- <Typography variant="label" bold color="#FFFFFF">
63
- {course.completedQuestions}/{course.totalQuestions} Questions answered
64
- </Typography>
65
- </View>
66
- <Typography variant="h2" bold color="#FFFFFF">
67
- {Math.round(course.progress * 100)}%
68
- </Typography>
69
- </View>
70
- <ProgressBar progress={course.progress} color="#FFFFFF" style={styles.summaryProgressBar} />
71
- </Card>
72
-
73
- {/* List of Topics */}
74
- <View style={styles.sectionHeader}>
75
- <Typography variant="h2" bold color={theme.textPrimary}>
76
- {getString('topicsLabel')} ({course.topics.length})
74
+ <Typography variant="body" color={theme.textSecondary}>
75
+ {course.topics.length} Modules
77
76
  </Typography>
78
77
  </View>
79
78
 
80
- <View style={styles.topicsList}>
81
- {course.topics.map((topic) => {
82
- const pct = Math.round(topic.progress * 100);
79
+ {/* Horizontal Filters Tabs Bar */}
80
+ <View style={styles.tabsContainer}>
81
+ <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.tabsScroll}>
82
+ {tabs.map((tab) => {
83
+ const isActive = activeTab === tab;
84
+ return (
85
+ <TouchableOpacity
86
+ key={tab}
87
+ onPress={() => setActiveTab(tab)}
88
+ style={[
89
+ styles.tabButton,
90
+ isActive
91
+ ? { backgroundColor: theme.textPrimary }
92
+ : { borderColor: theme.border, borderWidth: 1 }
93
+ ]}
94
+ >
95
+ <Typography
96
+ variant="caption"
97
+ bold
98
+ color={isActive ? '#FFFFFF' : theme.textSecondary}
99
+ >
100
+ {tab}
101
+ </Typography>
102
+ </TouchableOpacity>
103
+ );
104
+ })}
105
+ </ScrollView>
106
+ </View>
83
107
 
108
+ {/* Timeline of Chapters Categories */}
109
+ <View style={styles.listContainer}>
110
+ {categories.map((category) => {
111
+ const list = categoriesMap[category] || [];
112
+
84
113
  return (
85
- <TouchableOpacity
86
- key={topic.id}
87
- activeOpacity={0.8}
88
- onPress={() => callbacks.onSelectTopic?.(course, topic)}
89
- >
90
- <Card bordered style={styles.topicCard}>
91
- <View style={styles.topicCardHeader}>
92
- <View style={styles.topicTitleSection}>
93
- <Typography variant="h3" bold color={theme.textPrimary}>
94
- {topic.title}
95
- </Typography>
96
- <Typography variant="caption" color={theme.textSecondary}>
97
- {topic.completedQuestions}/{topic.totalQuestions} {getString('questionsCountLabel')}
98
- </Typography>
99
- </View>
100
- <View style={styles.pctBadge}>
101
- <Typography variant="caption" bold color={theme.primary}>
102
- {pct}%
103
- </Typography>
104
- </View>
105
- </View>
106
- <ProgressBar progress={topic.progress} style={styles.topicProgressBar} />
107
- </Card>
108
- </TouchableOpacity>
114
+ <View key={category} style={styles.categoryBlock}>
115
+ {/* Category Section Title */}
116
+ <Typography variant="caption" bold color={theme.textSecondary} style={styles.categoryTitle}>
117
+ {category.toUpperCase()}
118
+ </Typography>
119
+
120
+ {/* Timeline Items */}
121
+ <View style={styles.categoryItems}>
122
+ {list.map((topic) => {
123
+ globalIndex++;
124
+ const currentIndex = globalIndex;
125
+ const isLastInCourse = currentIndex === course.topics.length;
126
+
127
+ return (
128
+ <View key={topic.id} style={styles.timelineItemRow}>
129
+
130
+ {/* Left Side Timeline Connectors */}
131
+ <View style={styles.timelineLeftColumn}>
132
+ {/* Top Line connector */}
133
+ {currentIndex > 1 && <View style={[styles.timelineLine, { height: '50%', top: 0 }]} />}
134
+
135
+ {/* Bottom Line connector */}
136
+ {!isLastInCourse && <View style={[styles.timelineLine, { height: '100%', top: 16 }]} />}
137
+
138
+ {/* Sequential Number Circle */}
139
+ <View style={[styles.numberNode, { backgroundColor: '#8C9BAB' }]}>
140
+ <Typography variant="caption" bold color="#FFFFFF" style={styles.nodeText}>
141
+ {currentIndex}
142
+ </Typography>
143
+ </View>
144
+ </View>
145
+
146
+ {/* Right Side Info Card */}
147
+ <TouchableOpacity
148
+ activeOpacity={0.8}
149
+ onPress={() => callbacks.onSelectTopic?.(course, topic)}
150
+ style={styles.cardTouchable}
151
+ >
152
+ <Card bordered style={styles.topicCard}>
153
+ <View style={styles.cardMainRow}>
154
+
155
+ {/* Left Text details */}
156
+ <View style={styles.cardInfoCol}>
157
+ <Typography variant="h3" bold color={theme.textPrimary} style={styles.cardTitle}>
158
+ {topic.title}
159
+ </Typography>
160
+
161
+ <View style={styles.ratingRow}>
162
+ <Icon name="star" size={14} color="#EAB308" />
163
+ <Typography variant="caption" bold color={theme.textPrimary} style={styles.ratingVal}>
164
+ {topic.rating || '4.5'}
165
+ </Typography>
166
+ <View style={styles.bulletSeparator} />
167
+ <Typography variant="caption" color={theme.textSecondary}>
168
+ {topic.totalQuestions} Questions
169
+ </Typography>
170
+ </View>
171
+ </View>
172
+
173
+ {/* Right Badge / Lock option */}
174
+ <View style={styles.cardBadgeCol}>
175
+ {topic.isFree ? (
176
+ <View style={[styles.freeBadge, { borderColor: theme.primary }]}>
177
+ <Typography variant="caption" bold color={theme.primary} style={styles.freeBadgeText}>
178
+ FREE
179
+ </Typography>
180
+ </View>
181
+ ) : topic.isLocked ? (
182
+ <View style={styles.lockContainer}>
183
+ <Icon name="lock" size={14} color={theme.textSecondary} />
184
+ </View>
185
+ ) : null}
186
+ </View>
187
+
188
+ </View>
189
+ </Card>
190
+ </TouchableOpacity>
191
+
192
+ </View>
193
+ );
194
+ })}
195
+ </View>
196
+ </View>
109
197
  );
110
198
  })}
111
199
  </View>
@@ -123,78 +211,145 @@ const styles = StyleSheet.create({
123
211
  flexDirection: 'row',
124
212
  justifyContent: 'space-between',
125
213
  alignItems: 'center',
126
- paddingHorizontal: 20,
214
+ paddingHorizontal: 16,
127
215
  paddingVertical: 12,
128
216
  },
129
217
  backButton: {
130
- width: 40,
131
- height: 40,
132
- borderRadius: 20,
133
- borderWidth: 1,
134
- justifyContent: 'center',
135
- alignItems: 'center',
218
+ padding: 6,
136
219
  },
137
220
  headerTitle: {
138
- flex: 1,
139
- textAlign: 'center',
140
- marginHorizontal: 12,
221
+ fontSize: 18,
222
+ fontWeight: '700',
141
223
  },
142
- headerRightPlaceholder: {
143
- width: 40,
224
+ searchButton: {
225
+ padding: 6,
144
226
  },
145
227
  scrollContent: {
146
- paddingHorizontal: 20,
147
- paddingBottom: 24,
228
+ paddingHorizontal: 16,
229
+ paddingBottom: 32,
148
230
  },
149
- summaryCard: {
150
- padding: 20,
151
- borderRadius: 20,
231
+ subjectHeader: {
152
232
  marginTop: 12,
233
+ marginBottom: 16,
153
234
  },
154
- summaryDesc: {
155
- marginTop: 6,
235
+ subjectTitle: {
236
+ fontSize: 22,
237
+ marginBottom: 2,
156
238
  },
157
- progressSummaryContainer: {
158
- flexDirection: 'row',
159
- justifyContent: 'space-between',
160
- alignItems: 'flex-end',
161
- marginTop: 20,
162
- marginBottom: 8,
239
+ tabsContainer: {
240
+ marginBottom: 16,
163
241
  },
164
- progressSummaryText: {
165
- flex: 1,
242
+ tabsScroll: {
243
+ paddingVertical: 4,
244
+ },
245
+ tabButton: {
246
+ paddingHorizontal: 16,
247
+ paddingVertical: 6,
248
+ borderRadius: 20,
249
+ marginRight: 10,
250
+ },
251
+ listContainer: {
252
+ marginTop: 8,
253
+ },
254
+ categoryBlock: {
255
+ marginBottom: 24,
256
+ },
257
+ categoryTitle: {
258
+ fontSize: 11,
259
+ letterSpacing: 0.8,
260
+ marginBottom: 16,
166
261
  },
167
- summaryProgressBar: {
168
- backgroundColor: 'rgba(255, 255, 255, 0.25)',
262
+ categoryItems: {
263
+ marginTop: 2,
169
264
  },
170
- sectionHeader: {
171
- marginTop: 24,
265
+ timelineItemRow: {
266
+ flexDirection: 'row',
267
+ alignItems: 'stretch',
172
268
  marginBottom: 12,
269
+ minHeight: 80,
270
+ },
271
+ timelineLeftColumn: {
272
+ width: 36,
273
+ alignItems: 'center',
274
+ justifyContent: 'flex-start',
275
+ paddingTop: 12,
276
+ position: 'relative',
277
+ },
278
+ timelineLine: {
279
+ position: 'absolute',
280
+ left: 17, // center of the 36 width
281
+ width: 2,
282
+ backgroundColor: '#E2E8F0',
283
+ zIndex: -1,
284
+ },
285
+ numberNode: {
286
+ width: 24,
287
+ height: 24,
288
+ borderRadius: 12,
289
+ justifyContent: 'center',
290
+ alignItems: 'center',
291
+ zIndex: 1,
173
292
  },
174
- topicsList: {
175
- marginTop: 4,
293
+ nodeText: {
294
+ fontSize: 11,
295
+ },
296
+ cardTouchable: {
297
+ flex: 1,
176
298
  },
177
299
  topicCard: {
178
- padding: 16,
179
- marginBottom: 12,
300
+ padding: 12,
301
+ borderRadius: 12,
302
+ flex: 1,
180
303
  },
181
- topicCardHeader: {
304
+ cardMainRow: {
182
305
  flexDirection: 'row',
183
306
  justifyContent: 'space-between',
184
307
  alignItems: 'center',
185
- marginBottom: 12,
308
+ flex: 1,
186
309
  },
187
- topicTitleSection: {
310
+ cardInfoCol: {
188
311
  flex: 1,
189
- paddingRight: 12,
312
+ paddingRight: 8,
190
313
  },
191
- pctBadge: {
314
+ cardTitle: {
315
+ fontSize: 14,
316
+ lineHeight: 18,
317
+ marginBottom: 6,
318
+ },
319
+ ratingRow: {
320
+ flexDirection: 'row',
321
+ alignItems: 'center',
322
+ },
323
+ ratingVal: {
324
+ fontSize: 11,
325
+ marginLeft: 4,
326
+ },
327
+ bulletSeparator: {
328
+ width: 3,
329
+ height: 3,
330
+ borderRadius: 1.5,
331
+ backgroundColor: '#94A3B8',
332
+ marginHorizontal: 6,
333
+ },
334
+ cardBadgeCol: {
335
+ justifyContent: 'center',
336
+ alignItems: 'flex-end',
337
+ },
338
+ freeBadge: {
339
+ borderWidth: 1.5,
340
+ borderRadius: 6,
192
341
  paddingHorizontal: 8,
193
- paddingVertical: 4,
194
- borderRadius: 8,
195
- backgroundColor: 'rgba(0, 122, 135, 0.1)', // Dynamic overlay of primary color
342
+ paddingVertical: 3,
196
343
  },
197
- topicProgressBar: {
198
- height: 6,
344
+ freeBadgeText: {
345
+ fontSize: 9,
346
+ },
347
+ lockContainer: {
348
+ width: 24,
349
+ height: 24,
350
+ borderRadius: 12,
351
+ backgroundColor: '#F1F5F9',
352
+ justifyContent: 'center',
353
+ alignItems: 'center',
199
354
  },
200
355
  });
@@ -72,6 +72,10 @@ export interface Topic {
72
72
  completedQuestions: number;
73
73
  progress: number; // 0 to 1
74
74
  questions?: Question[];
75
+ category?: string; // e.g. "NEURO ANATOMY"
76
+ rating?: number; // e.g. 4.5
77
+ isFree?: boolean;
78
+ isLocked?: boolean;
75
79
  }
76
80
 
77
81
  export interface Course {
@@ -178,4 +182,10 @@ export interface HomeScreenParams {
178
182
  text: string;
179
183
  buttonText: string;
180
184
  };
185
+ sectionHeaders?: {
186
+ quickLinks?: { title?: string; show?: boolean };
187
+ dailyMcq?: { title?: string; show?: boolean };
188
+ getStarted?: { title?: string; show?: boolean };
189
+ continueLearning?: { title?: string; show?: boolean };
190
+ };
181
191
  }