@ubkinfotech/tecaher-erp 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 (146) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +413 -0
  3. package/lib/commonjs/core/api/apiClient.js +38 -0
  4. package/lib/commonjs/core/api/endpoints.js +139 -0
  5. package/lib/commonjs/core/api/interceptor.js +64 -0
  6. package/lib/commonjs/core/auth/authContext.js +30 -0
  7. package/lib/commonjs/core/auth/authService.js +12 -0
  8. package/lib/commonjs/core/hooks/useApiQuery.js +26 -0
  9. package/lib/commonjs/core/provider/ERPProvider.js +63 -0
  10. package/lib/commonjs/core/provider/types.js +5 -0
  11. package/lib/commonjs/core/provider/useERP.js +17 -0
  12. package/lib/commonjs/core/types/api.js +1 -0
  13. package/lib/commonjs/index.js +110 -0
  14. package/lib/commonjs/modules/assignment/hooks/useAssignmentList.js +16 -0
  15. package/lib/commonjs/modules/assignment/hooks/useHomeworkDetails.js +16 -0
  16. package/lib/commonjs/modules/assignment/hooks/useHomeworkSubmissions.js +16 -0
  17. package/lib/commonjs/modules/assignment/screens/AssignmentScreen.js +2615 -0
  18. package/lib/commonjs/modules/assignment/services/assignmentService.js +75 -0
  19. package/lib/commonjs/modules/attendance/hooks/useAttendance.js +16 -0
  20. package/lib/commonjs/modules/attendance/screens/AttendanceScreen.js +866 -0
  21. package/lib/commonjs/modules/attendance/services/attendanceService.js +31 -0
  22. package/lib/commonjs/modules/leaveRequest/hooks/useLeaveRequests.js +16 -0
  23. package/lib/commonjs/modules/leaveRequest/screens/LeaveRequestScreen.js +991 -0
  24. package/lib/commonjs/modules/leaveRequest/services/leaveRequestService.js +42 -0
  25. package/lib/commonjs/modules/marks/hooks/useMarks.js +14 -0
  26. package/lib/commonjs/modules/marks/screens/MarksScreen.js +1621 -0
  27. package/lib/commonjs/modules/marks/services/marksService.js +71 -0
  28. package/lib/commonjs/modules/myAttendance/hooks/useMyAttendance.js +16 -0
  29. package/lib/commonjs/modules/myAttendance/screens/MyAttendanceScreen.js +357 -0
  30. package/lib/commonjs/modules/myAttendance/services/myAttendanceService.js +11 -0
  31. package/lib/commonjs/modules/notes/hooks/useNotes.js +16 -0
  32. package/lib/commonjs/modules/notes/screens/NotesScreen.js +1287 -0
  33. package/lib/commonjs/modules/notes/services/notesService.js +65 -0
  34. package/lib/commonjs/modules/noticeboard/hooks/useNoticeboard.js +16 -0
  35. package/lib/commonjs/modules/noticeboard/screens/NoticeBoardScreen.js +381 -0
  36. package/lib/commonjs/modules/noticeboard/services/noticeboardService.js +16 -0
  37. package/lib/commonjs/modules/notification/hooks/useNotifications.js +16 -0
  38. package/lib/commonjs/modules/notification/screens/NotificationScreen.js +186 -0
  39. package/lib/commonjs/modules/notification/services/notificationService.js +16 -0
  40. package/lib/commonjs/modules/promoteStudent/hooks/usePromoteStudent.js +16 -0
  41. package/lib/commonjs/modules/promoteStudent/screens/PromoteStudentScreen.js +644 -0
  42. package/lib/commonjs/modules/promoteStudent/services/promoteStudentService.js +36 -0
  43. package/lib/commonjs/modules/timetable/hooks/useTimeTable.js +14 -0
  44. package/lib/commonjs/modules/timetable/screens/TimeTableScreen.js +258 -0
  45. package/lib/commonjs/modules/timetable/services/timetableService.js +16 -0
  46. package/lib/commonjs/package.json +1 -0
  47. package/lib/commonjs/shared/empty-states/EmptyState.js +45 -0
  48. package/lib/commonjs/shared/empty-states/ErrorState.js +45 -0
  49. package/lib/commonjs/shared/loaders/LoadingState.js +25 -0
  50. package/lib/commonjs/shared/theme/theme.js +22 -0
  51. package/lib/module/core/api/apiClient.js +32 -0
  52. package/lib/module/core/api/endpoints.js +135 -0
  53. package/lib/module/core/api/interceptor.js +60 -0
  54. package/lib/module/core/auth/authContext.js +23 -0
  55. package/lib/module/core/auth/authService.js +8 -0
  56. package/lib/module/core/hooks/useApiQuery.js +21 -0
  57. package/lib/module/core/provider/ERPProvider.js +56 -0
  58. package/lib/module/core/provider/types.js +3 -0
  59. package/lib/module/core/provider/useERP.js +12 -0
  60. package/lib/module/core/types/api.js +1 -0
  61. package/lib/module/index.js +17 -0
  62. package/lib/module/modules/assignment/hooks/useAssignmentList.js +12 -0
  63. package/lib/module/modules/assignment/hooks/useHomeworkDetails.js +12 -0
  64. package/lib/module/modules/assignment/hooks/useHomeworkSubmissions.js +12 -0
  65. package/lib/module/modules/assignment/screens/AssignmentScreen.js +2609 -0
  66. package/lib/module/modules/assignment/services/assignmentService.js +62 -0
  67. package/lib/module/modules/attendance/hooks/useAttendance.js +12 -0
  68. package/lib/module/modules/attendance/screens/AttendanceScreen.js +860 -0
  69. package/lib/module/modules/attendance/services/attendanceService.js +23 -0
  70. package/lib/module/modules/leaveRequest/hooks/useLeaveRequests.js +12 -0
  71. package/lib/module/modules/leaveRequest/screens/LeaveRequestScreen.js +985 -0
  72. package/lib/module/modules/leaveRequest/services/leaveRequestService.js +35 -0
  73. package/lib/module/modules/marks/hooks/useMarks.js +10 -0
  74. package/lib/module/modules/marks/screens/MarksScreen.js +1615 -0
  75. package/lib/module/modules/marks/services/marksService.js +55 -0
  76. package/lib/module/modules/myAttendance/hooks/useMyAttendance.js +12 -0
  77. package/lib/module/modules/myAttendance/screens/MyAttendanceScreen.js +351 -0
  78. package/lib/module/modules/myAttendance/services/myAttendanceService.js +7 -0
  79. package/lib/module/modules/notes/hooks/useNotes.js +12 -0
  80. package/lib/module/modules/notes/screens/NotesScreen.js +1281 -0
  81. package/lib/module/modules/notes/services/notesService.js +54 -0
  82. package/lib/module/modules/noticeboard/hooks/useNoticeboard.js +12 -0
  83. package/lib/module/modules/noticeboard/screens/NoticeBoardScreen.js +375 -0
  84. package/lib/module/modules/noticeboard/services/noticeboardService.js +12 -0
  85. package/lib/module/modules/notification/hooks/useNotifications.js +12 -0
  86. package/lib/module/modules/notification/screens/NotificationScreen.js +180 -0
  87. package/lib/module/modules/notification/services/notificationService.js +12 -0
  88. package/lib/module/modules/promoteStudent/hooks/usePromoteStudent.js +12 -0
  89. package/lib/module/modules/promoteStudent/screens/PromoteStudentScreen.js +638 -0
  90. package/lib/module/modules/promoteStudent/services/promoteStudentService.js +27 -0
  91. package/lib/module/modules/timetable/hooks/useTimeTable.js +10 -0
  92. package/lib/module/modules/timetable/screens/TimeTableScreen.js +252 -0
  93. package/lib/module/modules/timetable/services/timetableService.js +11 -0
  94. package/lib/module/package.json +1 -0
  95. package/lib/module/shared/empty-states/EmptyState.js +40 -0
  96. package/lib/module/shared/empty-states/ErrorState.js +40 -0
  97. package/lib/module/shared/loaders/LoadingState.js +20 -0
  98. package/lib/module/shared/theme/theme.js +18 -0
  99. package/lib/typescript/core/api/apiClient.d.ts +14 -0
  100. package/lib/typescript/core/api/endpoints.d.ts +164 -0
  101. package/lib/typescript/core/api/interceptor.d.ts +4 -0
  102. package/lib/typescript/core/auth/authContext.d.ts +11 -0
  103. package/lib/typescript/core/auth/authService.d.ts +9 -0
  104. package/lib/typescript/core/hooks/useApiQuery.d.ts +7 -0
  105. package/lib/typescript/core/provider/ERPProvider.d.ts +14 -0
  106. package/lib/typescript/core/provider/types.d.ts +34 -0
  107. package/lib/typescript/core/provider/useERP.d.ts +3 -0
  108. package/lib/typescript/core/types/api.d.ts +11 -0
  109. package/lib/typescript/index.d.ts +18 -0
  110. package/lib/typescript/modules/assignment/hooks/useAssignmentList.d.ts +3 -0
  111. package/lib/typescript/modules/assignment/hooks/useHomeworkDetails.d.ts +4 -0
  112. package/lib/typescript/modules/assignment/hooks/useHomeworkSubmissions.d.ts +4 -0
  113. package/lib/typescript/modules/assignment/screens/AssignmentScreen.d.ts +9 -0
  114. package/lib/typescript/modules/assignment/services/assignmentService.d.ts +89 -0
  115. package/lib/typescript/modules/attendance/hooks/useAttendance.d.ts +3 -0
  116. package/lib/typescript/modules/attendance/screens/AttendanceScreen.d.ts +5 -0
  117. package/lib/typescript/modules/attendance/services/attendanceService.d.ts +33 -0
  118. package/lib/typescript/modules/leaveRequest/hooks/useLeaveRequests.d.ts +3 -0
  119. package/lib/typescript/modules/leaveRequest/screens/LeaveRequestScreen.d.ts +5 -0
  120. package/lib/typescript/modules/leaveRequest/services/leaveRequestService.d.ts +39 -0
  121. package/lib/typescript/modules/marks/hooks/useMarks.d.ts +2 -0
  122. package/lib/typescript/modules/marks/screens/MarksScreen.d.ts +6 -0
  123. package/lib/typescript/modules/marks/services/marksService.d.ts +150 -0
  124. package/lib/typescript/modules/myAttendance/hooks/useMyAttendance.d.ts +3 -0
  125. package/lib/typescript/modules/myAttendance/screens/MyAttendanceScreen.d.ts +5 -0
  126. package/lib/typescript/modules/myAttendance/services/myAttendanceService.d.ts +10 -0
  127. package/lib/typescript/modules/notes/hooks/useNotes.d.ts +3 -0
  128. package/lib/typescript/modules/notes/screens/NotesScreen.d.ts +8 -0
  129. package/lib/typescript/modules/notes/services/notesService.d.ts +58 -0
  130. package/lib/typescript/modules/noticeboard/hooks/useNoticeboard.d.ts +3 -0
  131. package/lib/typescript/modules/noticeboard/screens/NoticeBoardScreen.d.ts +5 -0
  132. package/lib/typescript/modules/noticeboard/services/noticeboardService.d.ts +17 -0
  133. package/lib/typescript/modules/notification/hooks/useNotifications.d.ts +3 -0
  134. package/lib/typescript/modules/notification/screens/NotificationScreen.d.ts +5 -0
  135. package/lib/typescript/modules/notification/services/notificationService.d.ts +8 -0
  136. package/lib/typescript/modules/promoteStudent/hooks/usePromoteStudent.d.ts +3 -0
  137. package/lib/typescript/modules/promoteStudent/screens/PromoteStudentScreen.d.ts +5 -0
  138. package/lib/typescript/modules/promoteStudent/services/promoteStudentService.d.ts +40 -0
  139. package/lib/typescript/modules/timetable/hooks/useTimeTable.d.ts +3 -0
  140. package/lib/typescript/modules/timetable/screens/TimeTableScreen.d.ts +5 -0
  141. package/lib/typescript/modules/timetable/services/timetableService.d.ts +14 -0
  142. package/lib/typescript/shared/empty-states/EmptyState.d.ts +6 -0
  143. package/lib/typescript/shared/empty-states/ErrorState.d.ts +6 -0
  144. package/lib/typescript/shared/loaders/LoadingState.d.ts +3 -0
  145. package/lib/typescript/shared/theme/theme.d.ts +17 -0
  146. package/package.json +89 -0
@@ -0,0 +1,860 @@
1
+ "use strict";
2
+
3
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
4
+ import { ActivityIndicator, Alert, FlatList, Modal, Pressable, SafeAreaView, StyleSheet, Switch, Text, TouchableOpacity, View } from 'react-native';
5
+ import { useERP } from "../../../core/provider/useERP.js";
6
+ import { EmptyState } from "../../../shared/empty-states/EmptyState.js";
7
+ import { LoadingState } from "../../../shared/loaders/LoadingState.js";
8
+ import { fetchAttendanceStudents, fetchTeacherClasses, fetchTeacherSections, notifyAttendance, submitAttendance } from "../services/attendanceService.js";
9
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
10
+ function normalizeOptions(result) {
11
+ if (!result) {
12
+ return [];
13
+ }
14
+ if (Array.isArray(result)) {
15
+ return result;
16
+ }
17
+ if (Array.isArray(result?.data)) {
18
+ return result.data;
19
+ }
20
+ if (Array.isArray(result?.data?.data)) {
21
+ return result.data.data;
22
+ }
23
+ return [];
24
+ }
25
+ function normalizeAttendancePayload(result) {
26
+ if (!result) {
27
+ return null;
28
+ }
29
+ const raw = result?.data ?? result;
30
+ if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
31
+ return raw;
32
+ }
33
+ return null;
34
+ }
35
+ function normalizeStudents(payload, mode) {
36
+ const list = Array.isArray(payload?.student) ? payload.student : [];
37
+ return list.map(item => {
38
+ const value = item?.attendence_type_id;
39
+ let next = value;
40
+ if (mode === 'take') {
41
+ next = value === null ? '4' : value !== undefined && value !== '' ? String(value) : '1';
42
+ } else {
43
+ next = value ? String(value) : '4';
44
+ }
45
+ return {
46
+ ...item,
47
+ attendence_type_id: next
48
+ };
49
+ });
50
+ }
51
+ function presentCountFromPayload(payload, students) {
52
+ const rawPresent = payload?.total?.[0]?.present;
53
+ if (rawPresent !== undefined && rawPresent !== null && rawPresent !== '') {
54
+ const value = Number(rawPresent);
55
+ if (Number.isFinite(value)) {
56
+ return value;
57
+ }
58
+ }
59
+ return students.filter(item => String(item?.attendence_type_id ?? '') === '1').length;
60
+ }
61
+ function formatDisplayDate(date) {
62
+ const parts = String(date ?? '').split('-');
63
+ if (parts.length !== 3) {
64
+ return date;
65
+ }
66
+ const [year, month, day] = parts;
67
+ const d = new Date(Number(year), Number(month) - 1, Number(day));
68
+ if (Number.isNaN(d.getTime())) {
69
+ return date;
70
+ }
71
+ return d.toLocaleDateString('en-US', {
72
+ day: '2-digit',
73
+ month: 'short',
74
+ year: 'numeric'
75
+ });
76
+ }
77
+ function todayYmd() {
78
+ const d = new Date();
79
+ const y = d.getFullYear();
80
+ const m = String(d.getMonth() + 1).padStart(2, '0');
81
+ const day = String(d.getDate()).padStart(2, '0');
82
+ return `${y}-${m}-${day}`;
83
+ }
84
+ function tryGetCalendar() {
85
+ try {
86
+ const mod = require('react-native-calendars');
87
+ return mod?.Calendar ?? mod?.default ?? mod;
88
+ } catch {
89
+ return null;
90
+ }
91
+ }
92
+ function SelectModal({
93
+ label,
94
+ value,
95
+ options,
96
+ placeholder,
97
+ onChange
98
+ }) {
99
+ const [open, setOpen] = useState(false);
100
+ return /*#__PURE__*/_jsxs(View, {
101
+ style: styles.field,
102
+ children: [/*#__PURE__*/_jsx(Text, {
103
+ style: styles.label,
104
+ children: label
105
+ }), /*#__PURE__*/_jsx(Pressable, {
106
+ onPress: () => setOpen(true),
107
+ style: styles.select,
108
+ children: /*#__PURE__*/_jsx(Text, {
109
+ style: styles.selectText,
110
+ children: value?.label ?? placeholder
111
+ })
112
+ }), /*#__PURE__*/_jsx(Modal, {
113
+ visible: open,
114
+ transparent: true,
115
+ animationType: "fade",
116
+ children: /*#__PURE__*/_jsx(Pressable, {
117
+ style: styles.modalOverlay,
118
+ onPress: () => setOpen(false),
119
+ children: /*#__PURE__*/_jsxs(Pressable, {
120
+ style: styles.modalCard,
121
+ onPress: () => {},
122
+ children: [/*#__PURE__*/_jsx(Text, {
123
+ style: styles.modalTitle,
124
+ children: label
125
+ }), /*#__PURE__*/_jsx(FlatList, {
126
+ data: options,
127
+ keyExtractor: (item, index) => `${item.value}-${index}`,
128
+ renderItem: ({
129
+ item
130
+ }) => /*#__PURE__*/_jsx(Pressable, {
131
+ style: styles.optionRow,
132
+ onPress: () => {
133
+ onChange(item);
134
+ setOpen(false);
135
+ },
136
+ children: /*#__PURE__*/_jsx(Text, {
137
+ style: styles.optionText,
138
+ children: item.label
139
+ })
140
+ }),
141
+ ListEmptyComponent: /*#__PURE__*/_jsx(Text, {
142
+ style: styles.optionEmpty,
143
+ children: "No options"
144
+ })
145
+ })]
146
+ })
147
+ })
148
+ })]
149
+ });
150
+ }
151
+ export function AttendanceScreen(props) {
152
+ const {
153
+ api
154
+ } = useERP();
155
+ const Calendar = useMemo(() => tryGetCalendar(), []);
156
+ const [mode, setMode] = useState('take');
157
+ const [classes, setClasses] = useState([]);
158
+ const [sections, setSections] = useState([]);
159
+ const [selectedClass, setSelectedClass] = useState(null);
160
+ const [selectedSection, setSelectedSection] = useState(null);
161
+ const [date, setDate] = useState(props.date ?? todayYmd());
162
+ const [calendarOpen, setCalendarOpen] = useState(false);
163
+ const [loadingFilters, setLoadingFilters] = useState(true);
164
+ const [searching, setSearching] = useState(false);
165
+ const [submitting, setSubmitting] = useState(false);
166
+ const [searched, setSearched] = useState(false);
167
+ const [attendanceData, setAttendanceData] = useState(null);
168
+ const [students, setStudents] = useState([]);
169
+ const [present, setPresent] = useState(0);
170
+ const [count, setCount] = useState(0);
171
+ const sessionId = props.sessionId;
172
+ const loadClasses = useCallback(async () => {
173
+ setLoadingFilters(true);
174
+ try {
175
+ const res = await fetchTeacherClasses(api);
176
+ setClasses(normalizeOptions(res));
177
+ } catch (error) {
178
+ Alert.alert('Error', String(error?.message ?? 'Could not load classes'));
179
+ } finally {
180
+ setLoadingFilters(false);
181
+ }
182
+ }, [api]);
183
+ useEffect(() => {
184
+ loadClasses().catch(() => {});
185
+ }, [loadClasses]);
186
+ useEffect(() => {
187
+ if (!classes.length || selectedClass || props.classId === undefined) {
188
+ return;
189
+ }
190
+ const found = classes.find(item => String(item.value) === String(props.classId));
191
+ if (found) {
192
+ setSelectedClass(found);
193
+ }
194
+ }, [classes, props.classId, selectedClass]);
195
+ useEffect(() => {
196
+ if (!selectedClass?.value) {
197
+ setSections([]);
198
+ setSelectedSection(null);
199
+ return;
200
+ }
201
+ fetchTeacherSections(api, {
202
+ classId: selectedClass.value
203
+ }).then(res => {
204
+ const next = normalizeOptions(res);
205
+ setSections(next);
206
+ if (props.sectionId !== undefined) {
207
+ const found = next.find(item => String(item.value) === String(props.sectionId));
208
+ if (found) {
209
+ setSelectedSection(found);
210
+ }
211
+ }
212
+ }).catch(error => {
213
+ Alert.alert('Error', String(error?.message ?? 'Could not load sections'));
214
+ });
215
+ }, [api, props.sectionId, selectedClass?.value]);
216
+ const runSearch = useCallback(async () => {
217
+ if (!selectedClass?.value) {
218
+ Alert.alert('Error', 'Please choose class');
219
+ return;
220
+ }
221
+ if (!selectedSection?.value) {
222
+ Alert.alert('Error', 'Please choose section');
223
+ return;
224
+ }
225
+ if (!sessionId) {
226
+ Alert.alert('Error', 'Pass sessionId to AttendanceScreen');
227
+ return;
228
+ }
229
+ setSearched(true);
230
+ setSearching(true);
231
+ try {
232
+ const res = await fetchAttendanceStudents(api, {
233
+ classId: selectedClass.value,
234
+ sectionId: selectedSection.value,
235
+ sessionId,
236
+ date
237
+ });
238
+ const payload = normalizeAttendancePayload(res);
239
+ const list = normalizeStudents(payload, mode);
240
+ setAttendanceData(payload);
241
+ setStudents(list);
242
+ setCount(list.length);
243
+ setPresent(presentCountFromPayload(payload, list));
244
+ if (!payload?.student?.length && !payload?.holiday && !payload?.week_off && payload?.sunday !== 'yes') {
245
+ Alert.alert('Error', 'No Data Found');
246
+ }
247
+ } catch (error) {
248
+ Alert.alert('Error', String(error?.message ?? 'Could not load attendance'));
249
+ setAttendanceData(null);
250
+ setStudents([]);
251
+ setCount(0);
252
+ setPresent(0);
253
+ } finally {
254
+ setSearching(false);
255
+ }
256
+ }, [api, date, mode, selectedClass?.value, selectedSection?.value, sessionId]);
257
+ useEffect(() => {
258
+ if (!searched || !attendanceData) {
259
+ return;
260
+ }
261
+ const list = normalizeStudents(attendanceData, mode);
262
+ setStudents(list);
263
+ setCount(list.length);
264
+ setPresent(presentCountFromPayload(attendanceData, list));
265
+ }, [attendanceData, mode, searched]);
266
+ const onToggleStudent = useCallback((studentId, item) => {
267
+ if (mode !== 'take') {
268
+ return;
269
+ }
270
+ setStudents(prev => {
271
+ const next = prev.map(student => {
272
+ if (String(student?.id) !== String(studentId)) {
273
+ return student;
274
+ }
275
+ const current = String(item?.attendence_type_id ?? '');
276
+ return {
277
+ ...student,
278
+ attendence_type_id: current === '1' ? '4' : '1'
279
+ };
280
+ });
281
+ setPresent(next.filter(student => String(student?.attendence_type_id ?? '') === '1').length);
282
+ return next;
283
+ });
284
+ }, [mode]);
285
+ const submitCurrentAttendance = useCallback(async () => {
286
+ if (!selectedClass?.value || !selectedSection?.value) {
287
+ Alert.alert('Error', 'Please choose class and section');
288
+ return;
289
+ }
290
+ if (!students.length) {
291
+ Alert.alert('Error', 'No students found');
292
+ return;
293
+ }
294
+ const payload = {
295
+ attendanceData: students,
296
+ attendanceDate: date,
297
+ class_id: selectedClass.value,
298
+ section_id: selectedSection.value
299
+ };
300
+ setSubmitting(true);
301
+ try {
302
+ const res = await submitAttendance(api, payload);
303
+ const response = res;
304
+ if (response?.Status && response.Status !== 'Success') {
305
+ Alert.alert('Failed', String(response?.Message ?? response?.Status ?? 'Request failed'));
306
+ return;
307
+ }
308
+ Alert.alert('Success', 'Attendance given successfully');
309
+ try {
310
+ await notifyAttendance(api, payload);
311
+ } catch {}
312
+ await runSearch();
313
+ } catch (error) {
314
+ Alert.alert('Failed', String(error?.message ?? 'Could not submit attendance'));
315
+ } finally {
316
+ setSubmitting(false);
317
+ }
318
+ }, [api, date, runSearch, selectedClass?.value, selectedSection?.value, students]);
319
+ const renderStudent = ({
320
+ item
321
+ }) => {
322
+ const isPresent = String(item?.attendence_type_id ?? '') === '1';
323
+ return /*#__PURE__*/_jsxs(View, {
324
+ style: styles.studentItem,
325
+ children: [/*#__PURE__*/_jsx(View, {
326
+ style: styles.rollCol,
327
+ children: /*#__PURE__*/_jsx(Text, {
328
+ style: styles.studentText,
329
+ children: String(item?.roll_no ?? '-')
330
+ })
331
+ }), /*#__PURE__*/_jsx(View, {
332
+ style: mode === 'take' ? styles.nameColTake : styles.nameColView,
333
+ children: /*#__PURE__*/_jsx(Text, {
334
+ style: styles.studentText,
335
+ children: String(item?.full_name ?? 'Student')
336
+ })
337
+ }), mode === 'take' ? /*#__PURE__*/_jsx(View, {
338
+ style: styles.statusColTake,
339
+ children: /*#__PURE__*/_jsx(Switch, {
340
+ value: isPresent,
341
+ onValueChange: () => onToggleStudent(String(item?.id ?? ''), item),
342
+ trackColor: {
343
+ false: '#EF4444',
344
+ true: '#22C55E'
345
+ },
346
+ thumbColor: "#FFFFFF"
347
+ })
348
+ }) : /*#__PURE__*/_jsx(View, {
349
+ style: styles.statusColView,
350
+ children: /*#__PURE__*/_jsx(View, {
351
+ style: [styles.statusBadge, isPresent ? styles.badgePresent : styles.badgeAbsent],
352
+ children: /*#__PURE__*/_jsx(Text, {
353
+ style: styles.statusBadgeText,
354
+ children: isPresent ? 'Present' : 'Absent'
355
+ })
356
+ })
357
+ })]
358
+ });
359
+ };
360
+ const isHoliday = !!attendanceData?.holiday;
361
+ const isWeeklyOff = !!attendanceData?.week_off || attendanceData?.sunday === 'yes';
362
+ const emptyMessage = attendanceData?.sunday === 'yes' ? 'Today is Sunday' : 'Weekly off';
363
+ if (loadingFilters) {
364
+ return /*#__PURE__*/_jsx(LoadingState, {});
365
+ }
366
+ return /*#__PURE__*/_jsxs(SafeAreaView, {
367
+ style: styles.root,
368
+ children: [/*#__PURE__*/_jsxs(View, {
369
+ style: styles.header,
370
+ children: [/*#__PURE__*/_jsx(Text, {
371
+ style: styles.headerTitle,
372
+ children: "Attendance"
373
+ }), /*#__PURE__*/_jsxs(View, {
374
+ style: styles.modeRow,
375
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
376
+ onPress: () => setMode('take'),
377
+ style: [styles.modeBtn, mode === 'take' ? styles.modeBtnActive : null],
378
+ children: /*#__PURE__*/_jsx(Text, {
379
+ style: [styles.modeBtnText, mode === 'take' ? styles.modeBtnTextActive : null],
380
+ children: "Take"
381
+ })
382
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
383
+ onPress: () => setMode('view'),
384
+ style: [styles.modeBtn, mode === 'view' ? styles.modeBtnActive : null],
385
+ children: /*#__PURE__*/_jsx(Text, {
386
+ style: [styles.modeBtnText, mode === 'view' ? styles.modeBtnTextActive : null],
387
+ children: "View"
388
+ })
389
+ })]
390
+ })]
391
+ }), /*#__PURE__*/_jsx(FlatList, {
392
+ data: !searched || searching || isHoliday || isWeeklyOff ? [] : students,
393
+ keyExtractor: (item, index) => String(item?.id ?? index),
394
+ renderItem: renderStudent,
395
+ contentContainerStyle: styles.content,
396
+ ListHeaderComponent: /*#__PURE__*/_jsxs(View, {
397
+ children: [/*#__PURE__*/_jsxs(View, {
398
+ style: styles.card,
399
+ children: [/*#__PURE__*/_jsx(Text, {
400
+ style: styles.cardTitle,
401
+ children: mode === 'take' ? 'Attendance Details' : 'Filter Criteria'
402
+ }), /*#__PURE__*/_jsxs(View, {
403
+ style: styles.filterRow,
404
+ children: [/*#__PURE__*/_jsx(View, {
405
+ style: styles.filterCol,
406
+ children: /*#__PURE__*/_jsx(SelectModal, {
407
+ label: "Class",
408
+ value: selectedClass,
409
+ options: classes,
410
+ placeholder: "Select class",
411
+ onChange: next => {
412
+ setSelectedClass(next);
413
+ setSelectedSection(null);
414
+ setAttendanceData(null);
415
+ setStudents([]);
416
+ setCount(0);
417
+ setPresent(0);
418
+ setSearched(false);
419
+ }
420
+ })
421
+ }), /*#__PURE__*/_jsx(View, {
422
+ style: styles.filterCol,
423
+ children: /*#__PURE__*/_jsx(SelectModal, {
424
+ label: "Section",
425
+ value: selectedSection,
426
+ options: sections,
427
+ placeholder: "Select section",
428
+ onChange: next => {
429
+ setSelectedSection(next);
430
+ setAttendanceData(null);
431
+ setStudents([]);
432
+ setCount(0);
433
+ setPresent(0);
434
+ setSearched(false);
435
+ }
436
+ })
437
+ })]
438
+ }), /*#__PURE__*/_jsxs(View, {
439
+ style: styles.field,
440
+ children: [/*#__PURE__*/_jsx(Text, {
441
+ style: styles.label,
442
+ children: "Date"
443
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
444
+ style: styles.dateBtn,
445
+ onPress: () => setCalendarOpen(true),
446
+ children: /*#__PURE__*/_jsx(Text, {
447
+ style: styles.dateBtnText,
448
+ children: formatDisplayDate(date)
449
+ })
450
+ })]
451
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
452
+ onPress: () => runSearch().catch(() => {}),
453
+ style: styles.searchBtn,
454
+ children: /*#__PURE__*/_jsx(Text, {
455
+ style: styles.searchBtnText,
456
+ children: "Search"
457
+ })
458
+ }), sessionId === undefined ? /*#__PURE__*/_jsx(Text, {
459
+ style: styles.helperText,
460
+ children: "Pass sessionId to AttendanceScreen"
461
+ }) : null]
462
+ }), searching ? /*#__PURE__*/_jsx(ActivityIndicator, {
463
+ style: styles.loader,
464
+ size: "large",
465
+ color: "#111827"
466
+ }) : null, searched && !searching && isHoliday ? /*#__PURE__*/_jsx(Text, {
467
+ style: styles.messageText,
468
+ children: "Today is Holiday"
469
+ }) : null, searched && !searching && !isHoliday && isWeeklyOff ? /*#__PURE__*/_jsx(Text, {
470
+ style: styles.messageText,
471
+ children: emptyMessage
472
+ }) : null, searched && !searching && !isHoliday && !isWeeklyOff && students.length > 0 ? /*#__PURE__*/_jsxs(_Fragment, {
473
+ children: [/*#__PURE__*/_jsxs(View, {
474
+ style: styles.summaryRow,
475
+ children: [/*#__PURE__*/_jsxs(View, {
476
+ style: styles.summaryCard,
477
+ children: [/*#__PURE__*/_jsx(Text, {
478
+ style: styles.summaryLabel,
479
+ children: "Total Present"
480
+ }), /*#__PURE__*/_jsx(Text, {
481
+ style: [styles.summaryValue, styles.presentText],
482
+ children: present
483
+ })]
484
+ }), /*#__PURE__*/_jsxs(View, {
485
+ style: styles.summaryCard,
486
+ children: [/*#__PURE__*/_jsx(Text, {
487
+ style: styles.summaryLabel,
488
+ children: "Total Absent"
489
+ }), /*#__PURE__*/_jsx(Text, {
490
+ style: [styles.summaryValue, styles.absentText],
491
+ children: Math.max(count - present, 0)
492
+ })]
493
+ })]
494
+ }), mode === 'take' ? /*#__PURE__*/_jsx(TouchableOpacity, {
495
+ style: [styles.submitBtn, submitting ? styles.submitBtnDisabled : null],
496
+ onPress: () => submitCurrentAttendance().catch(() => {}),
497
+ disabled: submitting,
498
+ children: submitting ? /*#__PURE__*/_jsx(ActivityIndicator, {
499
+ color: "#FFFFFF"
500
+ }) : /*#__PURE__*/_jsx(Text, {
501
+ style: styles.submitBtnText,
502
+ children: "Submit"
503
+ })
504
+ }) : null, /*#__PURE__*/_jsxs(View, {
505
+ style: styles.listHeader,
506
+ children: [/*#__PURE__*/_jsx(Text, {
507
+ style: [styles.listHeaderText, styles.rollCol],
508
+ children: "Roll No"
509
+ }), /*#__PURE__*/_jsx(Text, {
510
+ style: [styles.listHeaderText, mode === 'take' ? styles.nameColTake : styles.nameColView],
511
+ children: "Student Name"
512
+ }), /*#__PURE__*/_jsx(Text, {
513
+ style: [styles.listHeaderText, mode === 'take' ? styles.statusColTake : styles.statusColView, styles.listHeaderRight],
514
+ children: "Status"
515
+ })]
516
+ })]
517
+ }) : null]
518
+ }),
519
+ ListEmptyComponent: searched && !searching && !isHoliday && !isWeeklyOff && students.length === 0 ? /*#__PURE__*/_jsx(Text, {
520
+ style: styles.messageText,
521
+ children: "No Data Found"
522
+ }) : null
523
+ }), /*#__PURE__*/_jsx(Modal, {
524
+ visible: calendarOpen,
525
+ transparent: true,
526
+ animationType: "fade",
527
+ children: /*#__PURE__*/_jsx(Pressable, {
528
+ style: styles.modalOverlay,
529
+ onPress: () => setCalendarOpen(false),
530
+ children: /*#__PURE__*/_jsxs(Pressable, {
531
+ style: styles.modalCard,
532
+ onPress: () => {},
533
+ children: [/*#__PURE__*/_jsxs(View, {
534
+ style: styles.calendarHeader,
535
+ children: [/*#__PURE__*/_jsx(Text, {
536
+ style: styles.modalTitle,
537
+ children: "Select Date"
538
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
539
+ onPress: () => setCalendarOpen(false),
540
+ style: styles.closeBtn,
541
+ children: /*#__PURE__*/_jsx(Text, {
542
+ style: styles.closeBtnText,
543
+ children: "Close"
544
+ })
545
+ })]
546
+ }), Calendar ? /*#__PURE__*/_jsx(Calendar, {
547
+ maxDate: todayYmd(),
548
+ onDayPress: day => {
549
+ const next = String(day?.dateString ?? '');
550
+ if (!next) {
551
+ return;
552
+ }
553
+ setDate(next);
554
+ setCalendarOpen(false);
555
+ },
556
+ markedDates: {
557
+ [date]: {
558
+ selected: true,
559
+ selectedColor: '#111827'
560
+ }
561
+ }
562
+ }) : /*#__PURE__*/_jsx(EmptyState, {
563
+ title: "Calendar missing",
564
+ message: "Install react-native-calendars to enable date picker"
565
+ })]
566
+ })
567
+ })
568
+ })]
569
+ });
570
+ }
571
+ const styles = StyleSheet.create({
572
+ root: {
573
+ flex: 1,
574
+ backgroundColor: '#FFFFFF'
575
+ },
576
+ header: {
577
+ paddingHorizontal: 16,
578
+ paddingVertical: 12,
579
+ borderBottomWidth: 1,
580
+ borderBottomColor: '#E5E7EB',
581
+ flexDirection: 'row',
582
+ alignItems: 'center',
583
+ justifyContent: 'space-between',
584
+ gap: 12
585
+ },
586
+ headerTitle: {
587
+ fontSize: 18,
588
+ fontWeight: '700',
589
+ color: '#111827'
590
+ },
591
+ modeRow: {
592
+ flexDirection: 'row',
593
+ gap: 8
594
+ },
595
+ modeBtn: {
596
+ paddingHorizontal: 12,
597
+ paddingVertical: 8,
598
+ borderRadius: 999,
599
+ borderWidth: 1,
600
+ borderColor: '#D1D5DB',
601
+ backgroundColor: '#FFFFFF'
602
+ },
603
+ modeBtnActive: {
604
+ backgroundColor: '#111827',
605
+ borderColor: '#111827'
606
+ },
607
+ modeBtnText: {
608
+ fontSize: 12,
609
+ fontWeight: '700',
610
+ color: '#111827'
611
+ },
612
+ modeBtnTextActive: {
613
+ color: '#FFFFFF'
614
+ },
615
+ content: {
616
+ padding: 16,
617
+ gap: 12
618
+ },
619
+ card: {
620
+ borderWidth: 1,
621
+ borderColor: '#E5E7EB',
622
+ borderRadius: 16,
623
+ padding: 14,
624
+ backgroundColor: '#FFFFFF'
625
+ },
626
+ cardTitle: {
627
+ fontSize: 16,
628
+ fontWeight: '700',
629
+ color: '#111827',
630
+ marginBottom: 12
631
+ },
632
+ filterRow: {
633
+ flexDirection: 'row',
634
+ gap: 10
635
+ },
636
+ filterCol: {
637
+ flex: 1
638
+ },
639
+ field: {
640
+ marginTop: 10,
641
+ gap: 6
642
+ },
643
+ label: {
644
+ fontSize: 12,
645
+ fontWeight: '600',
646
+ color: '#6B7280'
647
+ },
648
+ select: {
649
+ borderWidth: 1,
650
+ borderColor: '#E5E7EB',
651
+ borderRadius: 12,
652
+ paddingHorizontal: 12,
653
+ paddingVertical: 12,
654
+ backgroundColor: '#FFFFFF'
655
+ },
656
+ selectText: {
657
+ fontSize: 14,
658
+ color: '#111827'
659
+ },
660
+ dateBtn: {
661
+ borderWidth: 1,
662
+ borderColor: '#E5E7EB',
663
+ borderRadius: 12,
664
+ paddingHorizontal: 12,
665
+ paddingVertical: 12,
666
+ backgroundColor: '#F9FAFB'
667
+ },
668
+ dateBtnText: {
669
+ fontSize: 14,
670
+ color: '#111827'
671
+ },
672
+ searchBtn: {
673
+ marginTop: 14,
674
+ paddingVertical: 12,
675
+ borderRadius: 12,
676
+ backgroundColor: '#111827',
677
+ alignItems: 'center',
678
+ justifyContent: 'center'
679
+ },
680
+ searchBtnText: {
681
+ fontSize: 14,
682
+ fontWeight: '700',
683
+ color: '#FFFFFF'
684
+ },
685
+ helperText: {
686
+ marginTop: 8,
687
+ fontSize: 12,
688
+ color: '#6B7280'
689
+ },
690
+ loader: {
691
+ marginVertical: 24
692
+ },
693
+ summaryRow: {
694
+ flexDirection: 'row',
695
+ gap: 12,
696
+ marginTop: 16
697
+ },
698
+ summaryCard: {
699
+ flex: 1,
700
+ borderWidth: 1,
701
+ borderColor: '#E5E7EB',
702
+ borderRadius: 14,
703
+ padding: 14,
704
+ backgroundColor: '#F9FAFB'
705
+ },
706
+ summaryLabel: {
707
+ fontSize: 12,
708
+ color: '#6B7280'
709
+ },
710
+ summaryValue: {
711
+ marginTop: 8,
712
+ fontSize: 22,
713
+ fontWeight: '800'
714
+ },
715
+ presentText: {
716
+ color: '#16A34A'
717
+ },
718
+ absentText: {
719
+ color: '#DC2626'
720
+ },
721
+ submitBtn: {
722
+ marginTop: 14,
723
+ paddingVertical: 13,
724
+ borderRadius: 12,
725
+ backgroundColor: '#2563EB',
726
+ alignItems: 'center',
727
+ justifyContent: 'center'
728
+ },
729
+ submitBtnDisabled: {
730
+ opacity: 0.7
731
+ },
732
+ submitBtnText: {
733
+ fontSize: 15,
734
+ fontWeight: '800',
735
+ color: '#FFFFFF'
736
+ },
737
+ listHeader: {
738
+ marginTop: 16,
739
+ paddingHorizontal: 10,
740
+ paddingVertical: 10,
741
+ borderRadius: 12,
742
+ backgroundColor: '#111827',
743
+ flexDirection: 'row',
744
+ alignItems: 'center'
745
+ },
746
+ listHeaderText: {
747
+ fontSize: 12,
748
+ fontWeight: '700',
749
+ color: '#FFFFFF'
750
+ },
751
+ listHeaderRight: {
752
+ textAlign: 'right'
753
+ },
754
+ studentItem: {
755
+ marginTop: 10,
756
+ borderWidth: 1,
757
+ borderColor: '#E5E7EB',
758
+ borderRadius: 12,
759
+ paddingHorizontal: 10,
760
+ paddingVertical: 12,
761
+ backgroundColor: '#FFFFFF',
762
+ flexDirection: 'row',
763
+ alignItems: 'center'
764
+ },
765
+ rollCol: {
766
+ width: '20%'
767
+ },
768
+ nameColTake: {
769
+ width: '60%'
770
+ },
771
+ statusColTake: {
772
+ width: '20%',
773
+ alignItems: 'flex-end'
774
+ },
775
+ nameColView: {
776
+ width: '50%'
777
+ },
778
+ statusColView: {
779
+ width: '30%',
780
+ alignItems: 'flex-end'
781
+ },
782
+ studentText: {
783
+ fontSize: 13,
784
+ color: '#111827'
785
+ },
786
+ statusBadge: {
787
+ minWidth: 82,
788
+ paddingHorizontal: 10,
789
+ paddingVertical: 7,
790
+ borderRadius: 999,
791
+ alignItems: 'center'
792
+ },
793
+ badgePresent: {
794
+ backgroundColor: '#DCFCE7'
795
+ },
796
+ badgeAbsent: {
797
+ backgroundColor: '#FEE2E2'
798
+ },
799
+ statusBadgeText: {
800
+ fontSize: 12,
801
+ fontWeight: '700',
802
+ color: '#111827'
803
+ },
804
+ messageText: {
805
+ marginTop: 16,
806
+ textAlign: 'center',
807
+ color: '#6B7280',
808
+ fontSize: 14
809
+ },
810
+ modalOverlay: {
811
+ flex: 1,
812
+ backgroundColor: 'rgba(0,0,0,0.45)',
813
+ justifyContent: 'center',
814
+ padding: 16
815
+ },
816
+ modalCard: {
817
+ backgroundColor: '#FFFFFF',
818
+ borderRadius: 16,
819
+ overflow: 'hidden',
820
+ maxHeight: '80%'
821
+ },
822
+ modalTitle: {
823
+ fontSize: 16,
824
+ fontWeight: '700',
825
+ color: '#111827'
826
+ },
827
+ calendarHeader: {
828
+ paddingHorizontal: 16,
829
+ paddingVertical: 12,
830
+ borderBottomWidth: 1,
831
+ borderBottomColor: '#E5E7EB',
832
+ flexDirection: 'row',
833
+ alignItems: 'center',
834
+ justifyContent: 'space-between'
835
+ },
836
+ closeBtn: {
837
+ paddingHorizontal: 10,
838
+ paddingVertical: 6,
839
+ borderRadius: 10,
840
+ backgroundColor: '#F3F4F6'
841
+ },
842
+ closeBtnText: {
843
+ fontSize: 12,
844
+ fontWeight: '700',
845
+ color: '#111827'
846
+ },
847
+ optionRow: {
848
+ paddingHorizontal: 16,
849
+ paddingVertical: 12
850
+ },
851
+ optionText: {
852
+ fontSize: 14,
853
+ color: '#111827'
854
+ },
855
+ optionEmpty: {
856
+ paddingHorizontal: 16,
857
+ paddingVertical: 12,
858
+ color: '#6B7280'
859
+ }
860
+ });