@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,1287 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.NotesScreen = NotesScreen;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _useERP = require("../../../core/provider/useERP");
10
+ var _EmptyState = require("../../../shared/empty-states/EmptyState");
11
+ var _ErrorState = require("../../../shared/empty-states/ErrorState");
12
+ var _LoadingState = require("../../../shared/loaders/LoadingState");
13
+ var _notesService = require("../services/notesService");
14
+ var _jsxRuntime = require("react/jsx-runtime");
15
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
16
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
17
+ function normalizeList(result) {
18
+ if (Array.isArray(result)) return result;
19
+ if (Array.isArray(result?.data)) return result.data;
20
+ if (Array.isArray(result?.data?.data)) return result.data.data;
21
+ return [];
22
+ }
23
+ function normalizeOptions(result) {
24
+ const list = normalizeList(result?.data ?? result);
25
+ return list.map((item, index) => ({
26
+ label: String(item?.label ?? item?.title ?? item?.name ?? `Option ${index + 1}`),
27
+ value: item?.value ?? item?.id ?? index + 1,
28
+ raw: item
29
+ }));
30
+ }
31
+ function resolveNoteUrl(baseUrl, file) {
32
+ if (!file) return '';
33
+ if (/^https?:\/\//i.test(file)) return file;
34
+ const base = String(baseUrl ?? '').replace(/\/?$/, '/');
35
+ const normalized = String(file).replace(/^\/+/, '');
36
+ return `${base}student/note/${normalized}`;
37
+ }
38
+ function isImageUrl(file) {
39
+ const lower = String(file ?? '').toLowerCase();
40
+ return lower.endsWith('.png') || lower.endsWith('.jpg') || lower.endsWith('.jpeg') || lower.endsWith('.webp') || lower.endsWith('.gif');
41
+ }
42
+ function fileBaseName(path) {
43
+ const raw = String(path ?? '');
44
+ const cleaned = raw.split('?')[0] ?? raw;
45
+ const parts = cleaned.split(/[\\/]/);
46
+ return parts[parts.length - 1] || raw;
47
+ }
48
+ function ensureFileUri(path) {
49
+ return path.startsWith('file://') ? path : `file://${path}`;
50
+ }
51
+ function tryGetImageViewer() {
52
+ try {
53
+ const mod = require('react-native-image-viewing');
54
+ return mod?.default ?? mod;
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+ function tryGetPdf() {
60
+ try {
61
+ const mod = require('react-native-pdf');
62
+ return mod?.default ?? mod;
63
+ } catch {
64
+ return null;
65
+ }
66
+ }
67
+ function tryGetRNFS() {
68
+ try {
69
+ return require('react-native-fs');
70
+ } catch {
71
+ return null;
72
+ }
73
+ }
74
+ function tryGetFileViewer() {
75
+ try {
76
+ return require('react-native-file-viewer');
77
+ } catch {
78
+ return null;
79
+ }
80
+ }
81
+ async function downloadPdfToLocal(args) {
82
+ const safeName = fileBaseName(args.fileName || `document-${Date.now()}.pdf`) || `document-${Date.now()}.pdf`;
83
+ const target = `${args.RNFS.CachesDirectoryPath}/${safeName}`;
84
+ const result = await args.RNFS.downloadFile({
85
+ fromUrl: args.url,
86
+ toFile: target,
87
+ headers: args.headers ?? {}
88
+ }).promise;
89
+ if (result?.statusCode && result.statusCode >= 400) {
90
+ throw new Error(`PDF download failed with status ${result.statusCode}`);
91
+ }
92
+ const exists = await args.RNFS.exists(target);
93
+ if (!exists) {
94
+ throw new Error('Downloaded PDF file is missing');
95
+ }
96
+ return ensureFileUri(target);
97
+ }
98
+ function SelectField({
99
+ label,
100
+ value,
101
+ options,
102
+ placeholder,
103
+ onChange
104
+ }) {
105
+ const [open, setOpen] = (0, _react.useState)(false);
106
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
107
+ style: styles.field,
108
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
109
+ style: styles.label,
110
+ children: label
111
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
112
+ style: styles.select,
113
+ onPress: () => setOpen(true),
114
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
115
+ style: styles.selectText,
116
+ children: value?.label ?? placeholder
117
+ })
118
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
119
+ visible: open,
120
+ transparent: true,
121
+ animationType: "fade",
122
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
123
+ style: styles.modalOverlay,
124
+ onPress: () => setOpen(false),
125
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
126
+ style: styles.modalCard,
127
+ onPress: () => {},
128
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
129
+ style: styles.modalTitle,
130
+ children: label
131
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.FlatList, {
132
+ data: options,
133
+ keyExtractor: (item, index) => `${item.value}-${index}`,
134
+ renderItem: ({
135
+ item
136
+ }) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
137
+ style: styles.optionRow,
138
+ onPress: () => {
139
+ onChange(item);
140
+ setOpen(false);
141
+ },
142
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
143
+ style: styles.optionText,
144
+ children: item.label
145
+ })
146
+ }),
147
+ ListEmptyComponent: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
148
+ style: styles.optionEmpty,
149
+ children: "No options"
150
+ })
151
+ })]
152
+ })
153
+ })
154
+ })]
155
+ });
156
+ }
157
+ function NoteEditor(props) {
158
+ const {
159
+ api,
160
+ fileBaseUrl,
161
+ authToken,
162
+ getAuthToken,
163
+ schoolCode
164
+ } = (0, _useERP.useERP)();
165
+ const ImageView = (0, _react.useMemo)(() => tryGetImageViewer(), []);
166
+ const Pdf = (0, _react.useMemo)(() => tryGetPdf(), []);
167
+ const RNFS = (0, _react.useMemo)(() => tryGetRNFS(), []);
168
+ const FileViewer = (0, _react.useMemo)(() => tryGetFileViewer(), []);
169
+ const [loading, setLoading] = (0, _react.useState)(props.mode === 'edit');
170
+ const [saving, setSaving] = (0, _react.useState)(false);
171
+ const [classes, setClasses] = (0, _react.useState)([]);
172
+ const [sections, setSections] = (0, _react.useState)([]);
173
+ const [subjects, setSubjects] = (0, _react.useState)([]);
174
+ const [title, setTitle] = (0, _react.useState)('');
175
+ const [description, setDescription] = (0, _react.useState)('');
176
+ const [status, setStatus] = (0, _react.useState)('publish');
177
+ const [classOpt, setClassOpt] = (0, _react.useState)(null);
178
+ const [sectionOpt, setSectionOpt] = (0, _react.useState)(null);
179
+ const [subjectOpt, setSubjectOpt] = (0, _react.useState)(null);
180
+ const [file, setFile] = (0, _react.useState)('');
181
+ const [viewerOpen, setViewerOpen] = (0, _react.useState)(false);
182
+ const [pdfVisible, setPdfVisible] = (0, _react.useState)(false);
183
+ const [pdfUri, setPdfUri] = (0, _react.useState)('');
184
+ const [pdfHeaders, setPdfHeaders] = (0, _react.useState)({});
185
+ const [pdfLoading, setPdfLoading] = (0, _react.useState)(false);
186
+ const [downloading, setDownloading] = (0, _react.useState)(false);
187
+ const resolveHeaders = (0, _react.useCallback)(async () => {
188
+ const nextToken = (await getAuthToken?.()) ?? authToken;
189
+ return {
190
+ ...(nextToken ? {
191
+ Authorization: nextToken
192
+ } : {}),
193
+ ...(schoolCode ? {
194
+ school_code: schoolCode
195
+ } : {})
196
+ };
197
+ }, [authToken, getAuthToken, schoolCode]);
198
+ (0, _react.useEffect)(() => {
199
+ (0, _notesService.fetchNotesClasses)(api).then(res => {
200
+ const classOptions = normalizeOptions(res);
201
+ setClasses(classOptions);
202
+ }).catch(() => {});
203
+ }, [api]);
204
+ (0, _react.useEffect)(() => {
205
+ if (!classOpt?.value) {
206
+ setSections([]);
207
+ setSubjects([]);
208
+ return;
209
+ }
210
+ (0, _notesService.fetchNotesSections)(api, {
211
+ classId: classOpt.value
212
+ }).then(res => setSections(normalizeOptions(res))).catch(() => {});
213
+ if (props.sessionId !== undefined) {
214
+ (0, _notesService.fetchNotesSubjects)(api, {
215
+ classId: classOpt.value,
216
+ sessionId: props.sessionId
217
+ }).then(res => setSubjects(normalizeOptions(res))).catch(() => {});
218
+ }
219
+ }, [api, classOpt?.value, props.sessionId]);
220
+ (0, _react.useEffect)(() => {
221
+ if (props.mode !== 'edit' || !props.noteId) {
222
+ return;
223
+ }
224
+ const load = async () => {
225
+ try {
226
+ setLoading(true);
227
+ const res = await (0, _notesService.fetchNoteDetails)(api, {
228
+ noteId: props.noteId
229
+ });
230
+ const data = res?.data ?? res ?? {};
231
+ setTitle(String(data?.title ?? ''));
232
+ setDescription(String(data?.description ?? data?.remark ?? ''));
233
+ setStatus(String(data?.status ?? 'publish').toLowerCase() === 'unpublish' ? 'unpublish' : 'publish');
234
+ setFile(String(data?.file ?? ''));
235
+ const classId = data?.class_id ?? props.defaults.classId;
236
+ const sectionId = data?.section_id ?? props.defaults.sectionId;
237
+ const subjectId = data?.sub_id ?? data?.subject_id ?? props.defaults.subjectId;
238
+ if (classId !== undefined) {
239
+ const nextClass = normalizeOptions(await (0, _notesService.fetchNotesClasses)(api)).find(it => String(it.value) === String(classId)) ?? null;
240
+ setClasses(normalizeOptions(await (0, _notesService.fetchNotesClasses)(api)));
241
+ setClassOpt(nextClass);
242
+ if (nextClass) {
243
+ const sectionOptions = normalizeOptions(await (0, _notesService.fetchNotesSections)(api, {
244
+ classId: nextClass.value
245
+ }));
246
+ setSections(sectionOptions);
247
+ setSectionOpt(sectionOptions.find(it => String(it.value) === String(sectionId)) ?? null);
248
+ if (props.sessionId !== undefined) {
249
+ const subjectOptions = normalizeOptions(await (0, _notesService.fetchNotesSubjects)(api, {
250
+ classId: nextClass.value,
251
+ sessionId: props.sessionId
252
+ }));
253
+ setSubjects(subjectOptions);
254
+ setSubjectOpt(subjectOptions.find(it => String(it.value) === String(subjectId)) ?? null);
255
+ }
256
+ }
257
+ }
258
+ } catch (e) {
259
+ _reactNative.Alert.alert('Error', String(e?.message ?? 'Could not load note details'));
260
+ } finally {
261
+ setLoading(false);
262
+ }
263
+ };
264
+ load().catch(() => {});
265
+ }, [api, props.defaults.classId, props.defaults.sectionId, props.defaults.subjectId, props.mode, props.noteId, props.sessionId]);
266
+ (0, _react.useEffect)(() => {
267
+ if (props.mode !== 'create' || !classes.length) return;
268
+ if (props.defaults.classId !== undefined && !classOpt) {
269
+ setClassOpt(classes.find(it => String(it.value) === String(props.defaults.classId)) ?? null);
270
+ }
271
+ }, [classOpt, classes, props.defaults.classId, props.mode]);
272
+ (0, _react.useEffect)(() => {
273
+ if (!sections.length || props.mode !== 'create' || sectionOpt) return;
274
+ if (props.defaults.sectionId !== undefined) {
275
+ setSectionOpt(sections.find(it => String(it.value) === String(props.defaults.sectionId)) ?? null);
276
+ }
277
+ }, [props.defaults.sectionId, props.mode, sectionOpt, sections]);
278
+ const pickDocument = (0, _react.useCallback)(async () => {
279
+ let dp = null;
280
+ try {
281
+ dp = require('react-native-document-picker');
282
+ } catch {}
283
+ if (!dp) return;
284
+ const selected = await dp.pickSingle({
285
+ presentationStyle: 'fullScreen',
286
+ type: [dp.types.pdf, dp.types.images]
287
+ });
288
+ const upload = {
289
+ uri: selected.uri,
290
+ name: selected.name ?? 'file',
291
+ type: selected.type ?? 'application/octet-stream'
292
+ };
293
+ setSaving(true);
294
+ try {
295
+ const res = await (0, _notesService.uploadNoteFile)(api, upload);
296
+ if (res?.Status === 'Success' && res?.data) {
297
+ setFile(String(res.data));
298
+ } else {
299
+ _reactNative.Alert.alert('Error', 'File not uploaded');
300
+ }
301
+ } finally {
302
+ setSaving(false);
303
+ }
304
+ }, [api]);
305
+ const pickCamera = (0, _react.useCallback)(async () => {
306
+ let picker = null;
307
+ try {
308
+ picker = require('react-native-image-picker');
309
+ } catch {}
310
+ if (!picker?.launchCamera) return;
311
+ const res = await new Promise(resolve => {
312
+ picker.launchCamera({
313
+ mediaType: 'photo',
314
+ saveToPhotos: true,
315
+ includeBase64: false
316
+ }, r => resolve(r));
317
+ });
318
+ const asset = res?.assets?.[0];
319
+ if (!asset?.uri) return;
320
+ const upload = {
321
+ uri: asset.uri,
322
+ name: asset.fileName ?? 'camera.jpg',
323
+ type: asset.type ?? 'image/jpeg'
324
+ };
325
+ setSaving(true);
326
+ try {
327
+ const response = await (0, _notesService.uploadNoteFile)(api, upload);
328
+ if (response?.Status === 'Success' && response?.data) {
329
+ setFile(String(response.data));
330
+ } else {
331
+ _reactNative.Alert.alert('Error', 'File not uploaded');
332
+ }
333
+ } finally {
334
+ setSaving(false);
335
+ }
336
+ }, [api]);
337
+ const viewAttachment = (0, _react.useCallback)(async () => {
338
+ if (!file) {
339
+ _reactNative.Alert.alert('No Attachment');
340
+ return;
341
+ }
342
+ const url = resolveNoteUrl(fileBaseUrl, file);
343
+ const headers = await resolveHeaders();
344
+ if (ImageView && isImageUrl(file)) {
345
+ setViewerOpen(true);
346
+ return;
347
+ }
348
+ const isPdf = String(file).toLowerCase().endsWith('.pdf') || url.toLowerCase().endsWith('.pdf');
349
+ if (Pdf && isPdf) {
350
+ if (!RNFS) {
351
+ setPdfHeaders(headers);
352
+ setPdfUri(url);
353
+ setPdfVisible(true);
354
+ return;
355
+ }
356
+ setDownloading(true);
357
+ setPdfLoading(true);
358
+ try {
359
+ const local = await downloadPdfToLocal({
360
+ RNFS,
361
+ url,
362
+ fileName: fileBaseName(file),
363
+ headers
364
+ });
365
+ setPdfHeaders({});
366
+ setPdfUri(local);
367
+ setPdfVisible(true);
368
+ } catch (e) {
369
+ _reactNative.Alert.alert('Error', String(e?.message ?? 'Could not open PDF'));
370
+ } finally {
371
+ setDownloading(false);
372
+ setPdfLoading(false);
373
+ }
374
+ return;
375
+ }
376
+ if (!RNFS || !FileViewer) {
377
+ _reactNative.Alert.alert('Viewer Missing', 'Install react-native-fs and react-native-file-viewer.');
378
+ return;
379
+ }
380
+ setDownloading(true);
381
+ try {
382
+ const localFile = `${RNFS.DocumentDirectoryPath}/${fileBaseName(file)}`;
383
+ await RNFS.downloadFile({
384
+ fromUrl: url,
385
+ toFile: localFile,
386
+ headers
387
+ }).promise;
388
+ await FileViewer.open(localFile, {
389
+ showOpenWithDialog: true
390
+ });
391
+ } catch {
392
+ _reactNative.Alert.alert('Error', 'Could not view the file.');
393
+ } finally {
394
+ setDownloading(false);
395
+ }
396
+ }, [FileViewer, ImageView, Pdf, RNFS, file, fileBaseUrl, resolveHeaders]);
397
+ const submit = (0, _react.useCallback)(async () => {
398
+ if (!title || !description || !classOpt?.value || !sectionOpt?.value || !subjectOpt?.value || !file) {
399
+ _reactNative.Alert.alert('Error', 'Please enter all the details');
400
+ return;
401
+ }
402
+ const payload = {
403
+ id: props.noteId ?? undefined,
404
+ title,
405
+ description,
406
+ remark: description,
407
+ class_id: classOpt.value,
408
+ section_id: sectionOpt.value,
409
+ sub_id: subjectOpt.value,
410
+ file,
411
+ status
412
+ };
413
+ setSaving(true);
414
+ try {
415
+ const res = props.mode === 'create' ? await (0, _notesService.createNote)(api, payload) : await (0, _notesService.updateNote)(api, {
416
+ ...payload,
417
+ id: props.noteId ?? payload.id
418
+ });
419
+ const response = res;
420
+ if (response?.Status && response.Status !== 'Success') {
421
+ _reactNative.Alert.alert('Failed', String(response?.msg ?? 'Request failed'));
422
+ return;
423
+ }
424
+ _reactNative.Alert.alert('Success', props.mode === 'create' ? 'Note created successfully' : 'Note updated successfully');
425
+ props.onDone();
426
+ } catch (e) {
427
+ _reactNative.Alert.alert('Error', String(e?.message ?? 'Could not save note'));
428
+ } finally {
429
+ setSaving(false);
430
+ }
431
+ }, [api, classOpt?.value, description, file, props, sectionOpt?.value, status, subjectOpt?.value, title]);
432
+ if (loading) {
433
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_LoadingState.LoadingState, {});
434
+ }
435
+ const fileUrl = file ? resolveNoteUrl(fileBaseUrl, file) : '';
436
+ const headers = authToken || schoolCode ? {
437
+ ...(authToken ? {
438
+ Authorization: authToken
439
+ } : {}),
440
+ ...(schoolCode ? {
441
+ school_code: schoolCode
442
+ } : {})
443
+ } : undefined;
444
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.SafeAreaView, {
445
+ style: styles.root,
446
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
447
+ contentContainerStyle: styles.content,
448
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
449
+ style: styles.headerRow,
450
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
451
+ style: styles.title,
452
+ children: props.mode === 'create' ? 'Create Note' : 'Edit Note'
453
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
454
+ onPress: props.onCancel,
455
+ style: styles.secondaryBtn,
456
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
457
+ style: styles.secondaryBtnText,
458
+ children: "Back"
459
+ })
460
+ })]
461
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
462
+ style: styles.card,
463
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
464
+ style: styles.field,
465
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
466
+ style: styles.label,
467
+ children: "Title"
468
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
469
+ value: title,
470
+ onChangeText: setTitle,
471
+ style: styles.input,
472
+ placeholder: "Enter title",
473
+ placeholderTextColor: "#9CA3AF"
474
+ })]
475
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
476
+ style: styles.field,
477
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
478
+ style: styles.label,
479
+ children: "Description"
480
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
481
+ value: description,
482
+ onChangeText: setDescription,
483
+ style: [styles.input, styles.textarea],
484
+ placeholder: "Enter description",
485
+ placeholderTextColor: "#9CA3AF",
486
+ multiline: true
487
+ })]
488
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SelectField, {
489
+ label: "Class",
490
+ value: classOpt,
491
+ options: classes,
492
+ placeholder: "Select class",
493
+ onChange: setClassOpt
494
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SelectField, {
495
+ label: "Section",
496
+ value: sectionOpt,
497
+ options: sections,
498
+ placeholder: "Select section",
499
+ onChange: setSectionOpt
500
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SelectField, {
501
+ label: "Subject",
502
+ value: subjectOpt,
503
+ options: subjects,
504
+ placeholder: props.sessionId === undefined ? 'Provide sessionId' : 'Select subject',
505
+ onChange: setSubjectOpt
506
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
507
+ style: styles.field,
508
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
509
+ style: styles.label,
510
+ children: "Status"
511
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
512
+ style: styles.row,
513
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
514
+ style: [styles.chip, status === 'publish' ? styles.chipActive : null],
515
+ onPress: () => setStatus('publish'),
516
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
517
+ style: [styles.chipText, status === 'publish' ? styles.chipTextActive : null],
518
+ children: "Publish"
519
+ })
520
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
521
+ style: [styles.chip, status === 'unpublish' ? styles.chipActive : null],
522
+ onPress: () => setStatus('unpublish'),
523
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
524
+ style: [styles.chipText, status === 'unpublish' ? styles.chipTextActive : null],
525
+ children: "Unpublish"
526
+ })
527
+ })]
528
+ })]
529
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
530
+ style: styles.field,
531
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
532
+ style: styles.label,
533
+ children: "Attachment"
534
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
535
+ style: styles.row,
536
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
537
+ style: styles.secondaryBtn,
538
+ onPress: () => pickDocument().catch(() => {}),
539
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
540
+ style: styles.secondaryBtnText,
541
+ children: file ? 'Change File' : 'Upload File'
542
+ })
543
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
544
+ style: styles.secondaryBtn,
545
+ onPress: () => pickCamera().catch(() => {}),
546
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
547
+ style: styles.secondaryBtnText,
548
+ children: "Camera"
549
+ })
550
+ })]
551
+ }), file ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
552
+ style: styles.fileText,
553
+ children: ["Selected: ", fileBaseName(file)]
554
+ }) : null, file ? isImageUrl(file) ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
555
+ style: styles.previewWrap,
556
+ onPress: () => setViewerOpen(true),
557
+ activeOpacity: 0.9,
558
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, {
559
+ source: {
560
+ uri: fileUrl,
561
+ headers
562
+ },
563
+ style: styles.previewImage,
564
+ resizeMode: "contain"
565
+ })
566
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
567
+ style: styles.documentBtn,
568
+ onPress: () => viewAttachment().catch(() => {}),
569
+ disabled: downloading,
570
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
571
+ style: styles.documentBtnText,
572
+ children: downloading ? 'Opening Document...' : 'View Document'
573
+ })
574
+ }) : null]
575
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
576
+ style: styles.primaryBtn,
577
+ onPress: () => submit().catch(() => {}),
578
+ disabled: saving,
579
+ children: saving ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
580
+ color: "#FFFFFF"
581
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
582
+ style: styles.primaryBtnText,
583
+ children: props.mode === 'create' ? 'Create Note' : 'Update Note'
584
+ })
585
+ })]
586
+ })]
587
+ }), ImageView && file && isImageUrl(file) ? /*#__PURE__*/(0, _jsxRuntime.jsx)(ImageView, {
588
+ images: [{
589
+ uri: fileUrl,
590
+ headers
591
+ }],
592
+ imageIndex: 0,
593
+ visible: viewerOpen,
594
+ onRequestClose: () => setViewerOpen(false)
595
+ }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
596
+ visible: pdfVisible,
597
+ animationType: "slide",
598
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.SafeAreaView, {
599
+ style: styles.root,
600
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
601
+ style: styles.headerRow,
602
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
603
+ style: styles.title,
604
+ children: "PDF Viewer"
605
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
606
+ onPress: () => {
607
+ setPdfVisible(false);
608
+ setPdfUri('');
609
+ setPdfHeaders({});
610
+ },
611
+ style: styles.secondaryBtn,
612
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
613
+ style: styles.secondaryBtnText,
614
+ children: "Close"
615
+ })
616
+ })]
617
+ }), pdfLoading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_LoadingState.LoadingState, {}) : Pdf && pdfUri ? /*#__PURE__*/(0, _jsxRuntime.jsx)(Pdf, {
618
+ source: {
619
+ uri: pdfUri,
620
+ cache: true,
621
+ headers: pdfHeaders
622
+ },
623
+ style: {
624
+ flex: 1
625
+ }
626
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_EmptyState.EmptyState, {
627
+ title: "PDF Viewer Missing"
628
+ })]
629
+ })
630
+ })]
631
+ });
632
+ }
633
+ function NotesScreen(props) {
634
+ const {
635
+ api,
636
+ fileBaseUrl,
637
+ authToken,
638
+ getAuthToken,
639
+ schoolCode
640
+ } = (0, _useERP.useERP)();
641
+ const ImageView = (0, _react.useMemo)(() => tryGetImageViewer(), []);
642
+ const Pdf = (0, _react.useMemo)(() => tryGetPdf(), []);
643
+ const RNFS = (0, _react.useMemo)(() => tryGetRNFS(), []);
644
+ const FileViewer = (0, _react.useMemo)(() => tryGetFileViewer(), []);
645
+ const [mode, setMode] = (0, _react.useState)('list');
646
+ const [editNoteId, setEditNoteId] = (0, _react.useState)(null);
647
+ const [classes, setClasses] = (0, _react.useState)([]);
648
+ const [sections, setSections] = (0, _react.useState)([]);
649
+ const [subjects, setSubjects] = (0, _react.useState)([]);
650
+ const [classOpt, setClassOpt] = (0, _react.useState)(null);
651
+ const [sectionOpt, setSectionOpt] = (0, _react.useState)(null);
652
+ const [subjectOpt, setSubjectOpt] = (0, _react.useState)(null);
653
+ const [items, setItems] = (0, _react.useState)([]);
654
+ const [page, setPage] = (0, _react.useState)(props.page ?? 1);
655
+ const [totalPages, setTotalPages] = (0, _react.useState)(1);
656
+ const [loading, setLoading] = (0, _react.useState)(true);
657
+ const [refreshing, setRefreshing] = (0, _react.useState)(false);
658
+ const [loadingMore, setLoadingMore] = (0, _react.useState)(false);
659
+ const [error, setError] = (0, _react.useState)(null);
660
+ const [selectedNote, setSelectedNote] = (0, _react.useState)(null);
661
+ const [viewerOpen, setViewerOpen] = (0, _react.useState)(false);
662
+ const [pdfVisible, setPdfVisible] = (0, _react.useState)(false);
663
+ const [pdfUri, setPdfUri] = (0, _react.useState)('');
664
+ const [pdfHeaders, setPdfHeaders] = (0, _react.useState)({});
665
+ const [pdfLoading, setPdfLoading] = (0, _react.useState)(false);
666
+ const [downloading, setDownloading] = (0, _react.useState)(false);
667
+ const resolveHeaders = (0, _react.useCallback)(async () => {
668
+ const nextToken = (await getAuthToken?.()) ?? authToken;
669
+ return {
670
+ ...(nextToken ? {
671
+ Authorization: nextToken
672
+ } : {}),
673
+ ...(schoolCode ? {
674
+ school_code: schoolCode
675
+ } : {})
676
+ };
677
+ }, [authToken, getAuthToken, schoolCode]);
678
+ (0, _react.useEffect)(() => {
679
+ (0, _notesService.fetchNotesClasses)(api).then(res => {
680
+ const opts = normalizeOptions(res);
681
+ setClasses(opts);
682
+ if (props.classId !== undefined) {
683
+ setClassOpt(opts.find(it => String(it.value) === String(props.classId)) ?? null);
684
+ }
685
+ }).catch(e => setError(String(e?.message ?? 'Could not load classes')));
686
+ }, [api, props.classId]);
687
+ (0, _react.useEffect)(() => {
688
+ if (!classOpt?.value) {
689
+ setSections([]);
690
+ setSubjects([]);
691
+ setSectionOpt(props.sectionId === undefined ? null : sectionOpt);
692
+ return;
693
+ }
694
+ (0, _notesService.fetchNotesSections)(api, {
695
+ classId: classOpt.value
696
+ }).then(res => {
697
+ const opts = normalizeOptions(res);
698
+ setSections(opts);
699
+ if (props.sectionId !== undefined) {
700
+ setSectionOpt(opts.find(it => String(it.value) === String(props.sectionId)) ?? null);
701
+ }
702
+ }).catch(() => {});
703
+ if (props.sessionId !== undefined) {
704
+ (0, _notesService.fetchNotesSubjects)(api, {
705
+ classId: classOpt.value,
706
+ sessionId: props.sessionId
707
+ }).then(res => setSubjects(normalizeOptions(res))).catch(() => {});
708
+ }
709
+ }, [api, classOpt?.value, props.sectionId, props.sessionId]);
710
+ const loadList = (0, _react.useCallback)(async (nextPage, reset = false) => {
711
+ try {
712
+ if (reset) {
713
+ setRefreshing(true);
714
+ } else if (nextPage === (props.page ?? 1) && !items.length) {
715
+ setLoading(true);
716
+ } else {
717
+ setLoadingMore(true);
718
+ }
719
+ setError(null);
720
+ const res = await (0, _notesService.fetchNotesList)(api, {
721
+ page: nextPage,
722
+ perPage: props.perPage,
723
+ classId: classOpt?.value,
724
+ sectionId: sectionOpt?.value,
725
+ subjectId: subjectOpt?.value
726
+ });
727
+ const list = normalizeList(res?.data ?? res);
728
+ setItems(prev => reset ? list : [...prev, ...list]);
729
+ setPage(nextPage);
730
+ setTotalPages(Number(res?.pagination?.total_pages ?? 1));
731
+ } catch (e) {
732
+ setError(String(e?.message ?? 'Could not load notes'));
733
+ } finally {
734
+ setLoading(false);
735
+ setRefreshing(false);
736
+ setLoadingMore(false);
737
+ }
738
+ }, [api, classOpt?.value, items.length, props.page, props.perPage, sectionOpt?.value, subjectOpt?.value]);
739
+ (0, _react.useEffect)(() => {
740
+ if (mode !== 'list') return;
741
+ loadList(props.page ?? 1, true).catch(() => {});
742
+ }, [loadList, mode, props.page]);
743
+ const viewNoteAttachment = (0, _react.useCallback)(async () => {
744
+ const file = String(selectedNote?.file ?? '');
745
+ if (!file) {
746
+ _reactNative.Alert.alert('No Attachment');
747
+ return;
748
+ }
749
+ const url = resolveNoteUrl(fileBaseUrl, file);
750
+ const headers = await resolveHeaders();
751
+ if (ImageView && isImageUrl(file)) {
752
+ setViewerOpen(true);
753
+ return;
754
+ }
755
+ const isPdf = String(file).toLowerCase().endsWith('.pdf') || url.toLowerCase().endsWith('.pdf');
756
+ if (Pdf && isPdf) {
757
+ if (!RNFS) {
758
+ setPdfHeaders(headers);
759
+ setPdfUri(url);
760
+ setPdfVisible(true);
761
+ return;
762
+ }
763
+ setDownloading(true);
764
+ setPdfLoading(true);
765
+ try {
766
+ const local = await downloadPdfToLocal({
767
+ RNFS,
768
+ url,
769
+ fileName: fileBaseName(file),
770
+ headers
771
+ });
772
+ setPdfHeaders({});
773
+ setPdfUri(local);
774
+ setPdfVisible(true);
775
+ } catch (e) {
776
+ _reactNative.Alert.alert('Error', String(e?.message ?? 'Could not open PDF'));
777
+ } finally {
778
+ setDownloading(false);
779
+ setPdfLoading(false);
780
+ }
781
+ return;
782
+ }
783
+ if (!RNFS || !FileViewer) {
784
+ _reactNative.Alert.alert('Viewer Missing', 'Install react-native-fs and react-native-file-viewer.');
785
+ return;
786
+ }
787
+ setDownloading(true);
788
+ try {
789
+ const localFile = `${RNFS.DocumentDirectoryPath}/${fileBaseName(file)}`;
790
+ await RNFS.downloadFile({
791
+ fromUrl: url,
792
+ toFile: localFile,
793
+ headers
794
+ }).promise;
795
+ await FileViewer.open(localFile, {
796
+ showOpenWithDialog: true
797
+ });
798
+ } catch {
799
+ _reactNative.Alert.alert('Error', 'Could not view the file.');
800
+ } finally {
801
+ setDownloading(false);
802
+ }
803
+ }, [FileViewer, ImageView, Pdf, RNFS, fileBaseUrl, resolveHeaders, selectedNote?.file]);
804
+ if (mode === 'create') {
805
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(NoteEditor, {
806
+ mode: "create",
807
+ sessionId: props.sessionId,
808
+ defaults: {
809
+ classId: classOpt?.value ?? props.classId,
810
+ sectionId: sectionOpt?.value ?? props.sectionId,
811
+ subjectId: subjectOpt?.value
812
+ },
813
+ onDone: () => {
814
+ setMode('list');
815
+ loadList(props.page ?? 1, true).catch(() => {});
816
+ },
817
+ onCancel: () => setMode('list')
818
+ });
819
+ }
820
+ if (mode === 'edit' && editNoteId !== null) {
821
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(NoteEditor, {
822
+ mode: "edit",
823
+ noteId: editNoteId,
824
+ sessionId: props.sessionId,
825
+ defaults: {
826
+ classId: classOpt?.value ?? props.classId,
827
+ sectionId: sectionOpt?.value ?? props.sectionId,
828
+ subjectId: subjectOpt?.value
829
+ },
830
+ onDone: () => {
831
+ setMode('list');
832
+ setEditNoteId(null);
833
+ loadList(props.page ?? 1, true).catch(() => {});
834
+ },
835
+ onCancel: () => {
836
+ setMode('list');
837
+ setEditNoteId(null);
838
+ }
839
+ });
840
+ }
841
+ if (loading && !items.length) {
842
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_LoadingState.LoadingState, {});
843
+ }
844
+ if (error && !items.length) {
845
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ErrorState.ErrorState, {
846
+ message: error
847
+ });
848
+ }
849
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.SafeAreaView, {
850
+ style: styles.root,
851
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.FlatList, {
852
+ data: items,
853
+ keyExtractor: (item, index) => String(item?.id ?? index),
854
+ contentContainerStyle: styles.content,
855
+ refreshControl: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.RefreshControl, {
856
+ refreshing: refreshing,
857
+ onRefresh: () => {
858
+ loadList(props.page ?? 1, true).catch(() => {});
859
+ },
860
+ tintColor: "#1D4ED8"
861
+ }),
862
+ ListHeaderComponent: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
863
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
864
+ style: styles.headerRow,
865
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
866
+ style: styles.title,
867
+ children: "Notes"
868
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
869
+ style: styles.primaryBtnCompact,
870
+ onPress: () => setMode('create'),
871
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
872
+ style: styles.primaryBtnText,
873
+ children: "Create"
874
+ })
875
+ })]
876
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
877
+ style: styles.card,
878
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(SelectField, {
879
+ label: "Class",
880
+ value: classOpt,
881
+ options: classes,
882
+ placeholder: "All classes",
883
+ onChange: next => {
884
+ setClassOpt(next);
885
+ setSectionOpt(null);
886
+ setSubjectOpt(null);
887
+ }
888
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SelectField, {
889
+ label: "Section",
890
+ value: sectionOpt,
891
+ options: sections,
892
+ placeholder: "All sections",
893
+ onChange: setSectionOpt
894
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SelectField, {
895
+ label: "Subject",
896
+ value: subjectOpt,
897
+ options: subjects,
898
+ placeholder: props.sessionId === undefined ? 'Provide sessionId' : 'All subjects',
899
+ onChange: setSubjectOpt
900
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
901
+ style: styles.primaryBtn,
902
+ onPress: () => loadList(props.page ?? 1, true).catch(() => {}),
903
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
904
+ style: styles.primaryBtnText,
905
+ children: "Search"
906
+ })
907
+ })]
908
+ })]
909
+ }),
910
+ renderItem: ({
911
+ item
912
+ }) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
913
+ style: styles.card,
914
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
915
+ style: styles.noteTitle,
916
+ children: String(item?.title ?? 'Note')
917
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
918
+ style: styles.noteDesc,
919
+ numberOfLines: 3,
920
+ children: String(item?.description ?? item?.remark ?? '-')
921
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
922
+ style: styles.noteMeta,
923
+ children: String(item?.created_at ?? '')
924
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
925
+ style: styles.row,
926
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
927
+ style: styles.secondaryBtn,
928
+ onPress: () => setSelectedNote(item),
929
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
930
+ style: styles.secondaryBtnText,
931
+ children: "View"
932
+ })
933
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
934
+ style: styles.secondaryBtn,
935
+ onPress: () => {
936
+ setEditNoteId(item?.id ?? null);
937
+ setMode('edit');
938
+ },
939
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
940
+ style: styles.secondaryBtnText,
941
+ children: "Edit"
942
+ })
943
+ })]
944
+ })]
945
+ }),
946
+ ListEmptyComponent: /*#__PURE__*/(0, _jsxRuntime.jsx)(_EmptyState.EmptyState, {
947
+ title: "No Data Found"
948
+ }),
949
+ onEndReached: () => {
950
+ if (!loadingMore && page < totalPages) {
951
+ loadList(page + 1).catch(() => {});
952
+ }
953
+ },
954
+ onEndReachedThreshold: 0.4,
955
+ ListFooterComponent: loadingMore ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
956
+ color: "#1D4ED8"
957
+ }) : null
958
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
959
+ visible: selectedNote !== null,
960
+ transparent: true,
961
+ animationType: "slide",
962
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
963
+ style: styles.modalOverlay,
964
+ onPress: () => setSelectedNote(null),
965
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
966
+ style: styles.detailCard,
967
+ onPress: () => {},
968
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
969
+ style: styles.headerRow,
970
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
971
+ style: styles.title,
972
+ children: "Note Details"
973
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
974
+ onPress: () => setSelectedNote(null),
975
+ style: styles.secondaryBtn,
976
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
977
+ style: styles.secondaryBtnText,
978
+ children: "Close"
979
+ })
980
+ })]
981
+ }), selectedNote ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
982
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
983
+ style: styles.noteTitle,
984
+ children: String(selectedNote?.title ?? '')
985
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
986
+ style: styles.noteMeta,
987
+ children: String(selectedNote?.created_at ?? '')
988
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
989
+ style: styles.noteDescFull,
990
+ children: String(selectedNote?.description ?? selectedNote?.remark ?? '-')
991
+ }), selectedNote?.file ? isImageUrl(String(selectedNote.file)) ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
992
+ style: styles.previewWrap,
993
+ onPress: () => setViewerOpen(true),
994
+ activeOpacity: 0.9,
995
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, {
996
+ source: {
997
+ uri: resolveNoteUrl(fileBaseUrl, String(selectedNote.file)),
998
+ headers: authToken || schoolCode ? {
999
+ ...(authToken ? {
1000
+ Authorization: authToken
1001
+ } : {}),
1002
+ ...(schoolCode ? {
1003
+ school_code: schoolCode
1004
+ } : {})
1005
+ } : undefined
1006
+ },
1007
+ style: styles.previewImage,
1008
+ resizeMode: "contain"
1009
+ })
1010
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1011
+ style: styles.documentBtn,
1012
+ onPress: () => viewNoteAttachment().catch(() => {}),
1013
+ disabled: downloading,
1014
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1015
+ style: styles.documentBtnText,
1016
+ children: downloading ? 'Opening Document...' : 'View Document'
1017
+ })
1018
+ }) : null]
1019
+ }) : null]
1020
+ })
1021
+ })
1022
+ }), ImageView && selectedNote?.file && isImageUrl(String(selectedNote.file)) ? /*#__PURE__*/(0, _jsxRuntime.jsx)(ImageView, {
1023
+ images: [{
1024
+ uri: resolveNoteUrl(fileBaseUrl, String(selectedNote.file))
1025
+ }],
1026
+ imageIndex: 0,
1027
+ visible: viewerOpen,
1028
+ onRequestClose: () => setViewerOpen(false)
1029
+ }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
1030
+ visible: pdfVisible,
1031
+ animationType: "slide",
1032
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.SafeAreaView, {
1033
+ style: styles.root,
1034
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1035
+ style: styles.headerRow,
1036
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1037
+ style: styles.title,
1038
+ children: "PDF Viewer"
1039
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1040
+ onPress: () => {
1041
+ setPdfVisible(false);
1042
+ setPdfUri('');
1043
+ setPdfHeaders({});
1044
+ },
1045
+ style: styles.secondaryBtn,
1046
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1047
+ style: styles.secondaryBtnText,
1048
+ children: "Close"
1049
+ })
1050
+ })]
1051
+ }), pdfLoading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_LoadingState.LoadingState, {}) : Pdf && pdfUri ? /*#__PURE__*/(0, _jsxRuntime.jsx)(Pdf, {
1052
+ source: {
1053
+ uri: pdfUri,
1054
+ cache: true,
1055
+ headers: pdfHeaders
1056
+ },
1057
+ style: {
1058
+ flex: 1
1059
+ }
1060
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_EmptyState.EmptyState, {
1061
+ title: "PDF Viewer Missing"
1062
+ })]
1063
+ })
1064
+ })]
1065
+ });
1066
+ }
1067
+ const styles = _reactNative.StyleSheet.create({
1068
+ root: {
1069
+ flex: 1,
1070
+ backgroundColor: '#F9FAFB'
1071
+ },
1072
+ content: {
1073
+ padding: 16,
1074
+ paddingBottom: 32
1075
+ },
1076
+ headerRow: {
1077
+ flexDirection: 'row',
1078
+ alignItems: 'center',
1079
+ justifyContent: 'space-between',
1080
+ gap: 12,
1081
+ marginBottom: 12
1082
+ },
1083
+ title: {
1084
+ fontSize: 20,
1085
+ fontWeight: '700',
1086
+ color: '#111827'
1087
+ },
1088
+ card: {
1089
+ backgroundColor: '#FFFFFF',
1090
+ borderWidth: 1,
1091
+ borderColor: '#E5E7EB',
1092
+ borderRadius: 14,
1093
+ padding: 14,
1094
+ marginBottom: 12
1095
+ },
1096
+ field: {
1097
+ marginBottom: 12
1098
+ },
1099
+ label: {
1100
+ fontSize: 13,
1101
+ fontWeight: '600',
1102
+ color: '#1F2937',
1103
+ marginBottom: 6
1104
+ },
1105
+ select: {
1106
+ borderWidth: 1,
1107
+ borderColor: '#D1D5DB',
1108
+ borderRadius: 10,
1109
+ backgroundColor: '#FFFFFF',
1110
+ paddingHorizontal: 12,
1111
+ paddingVertical: 12
1112
+ },
1113
+ selectText: {
1114
+ fontSize: 14,
1115
+ color: '#111827'
1116
+ },
1117
+ input: {
1118
+ borderWidth: 1,
1119
+ borderColor: '#D1D5DB',
1120
+ borderRadius: 10,
1121
+ backgroundColor: '#FFFFFF',
1122
+ paddingHorizontal: 12,
1123
+ paddingVertical: 12,
1124
+ fontSize: 14,
1125
+ color: '#111827'
1126
+ },
1127
+ textarea: {
1128
+ minHeight: 120,
1129
+ textAlignVertical: 'top'
1130
+ },
1131
+ row: {
1132
+ flexDirection: 'row',
1133
+ alignItems: 'center',
1134
+ gap: 10,
1135
+ flexWrap: 'wrap'
1136
+ },
1137
+ primaryBtn: {
1138
+ backgroundColor: '#1D4ED8',
1139
+ borderRadius: 12,
1140
+ paddingVertical: 14,
1141
+ alignItems: 'center',
1142
+ justifyContent: 'center',
1143
+ marginTop: 6
1144
+ },
1145
+ primaryBtnCompact: {
1146
+ backgroundColor: '#1D4ED8',
1147
+ borderRadius: 10,
1148
+ paddingHorizontal: 16,
1149
+ paddingVertical: 10,
1150
+ alignItems: 'center',
1151
+ justifyContent: 'center'
1152
+ },
1153
+ primaryBtnText: {
1154
+ color: '#FFFFFF',
1155
+ fontSize: 14,
1156
+ fontWeight: '700'
1157
+ },
1158
+ secondaryBtn: {
1159
+ borderWidth: 1,
1160
+ borderColor: '#BFDBFE',
1161
+ backgroundColor: '#EFF6FF',
1162
+ borderRadius: 10,
1163
+ paddingHorizontal: 14,
1164
+ paddingVertical: 10
1165
+ },
1166
+ secondaryBtnText: {
1167
+ fontSize: 14,
1168
+ fontWeight: '700',
1169
+ color: '#1D4ED8'
1170
+ },
1171
+ chip: {
1172
+ flex: 1,
1173
+ minWidth: 120,
1174
+ borderWidth: 1,
1175
+ borderColor: '#BFDBFE',
1176
+ backgroundColor: '#EFF6FF',
1177
+ paddingVertical: 10,
1178
+ borderRadius: 10,
1179
+ alignItems: 'center'
1180
+ },
1181
+ chipActive: {
1182
+ backgroundColor: '#1D4ED8',
1183
+ borderColor: '#1D4ED8'
1184
+ },
1185
+ chipText: {
1186
+ fontSize: 14,
1187
+ fontWeight: '700',
1188
+ color: '#1D4ED8'
1189
+ },
1190
+ chipTextActive: {
1191
+ color: '#FFFFFF'
1192
+ },
1193
+ fileText: {
1194
+ fontSize: 13,
1195
+ color: '#374151',
1196
+ marginTop: 8
1197
+ },
1198
+ previewWrap: {
1199
+ marginTop: 12,
1200
+ borderWidth: 1,
1201
+ borderColor: '#E5E7EB',
1202
+ borderRadius: 12,
1203
+ overflow: 'hidden',
1204
+ backgroundColor: '#F9FAFB',
1205
+ height: 220
1206
+ },
1207
+ previewImage: {
1208
+ width: '100%',
1209
+ height: '100%'
1210
+ },
1211
+ documentBtn: {
1212
+ marginTop: 12,
1213
+ borderWidth: 1,
1214
+ borderStyle: 'dashed',
1215
+ borderColor: '#1D4ED8',
1216
+ borderRadius: 12,
1217
+ paddingVertical: 16,
1218
+ alignItems: 'center'
1219
+ },
1220
+ documentBtnText: {
1221
+ fontSize: 15,
1222
+ fontWeight: '700',
1223
+ color: '#1D4ED8'
1224
+ },
1225
+ noteTitle: {
1226
+ fontSize: 16,
1227
+ fontWeight: '700',
1228
+ color: '#111827',
1229
+ marginBottom: 6
1230
+ },
1231
+ noteDesc: {
1232
+ fontSize: 14,
1233
+ lineHeight: 20,
1234
+ color: '#374151',
1235
+ marginBottom: 8
1236
+ },
1237
+ noteDescFull: {
1238
+ fontSize: 14,
1239
+ lineHeight: 22,
1240
+ color: '#374151',
1241
+ marginTop: 10
1242
+ },
1243
+ noteMeta: {
1244
+ fontSize: 12,
1245
+ color: '#6B7280',
1246
+ marginBottom: 10
1247
+ },
1248
+ modalOverlay: {
1249
+ flex: 1,
1250
+ backgroundColor: 'rgba(0,0,0,0.35)',
1251
+ justifyContent: 'center',
1252
+ padding: 16
1253
+ },
1254
+ modalCard: {
1255
+ backgroundColor: '#FFFFFF',
1256
+ borderRadius: 14,
1257
+ maxHeight: '75%',
1258
+ padding: 16
1259
+ },
1260
+ detailCard: {
1261
+ backgroundColor: '#FFFFFF',
1262
+ borderRadius: 14,
1263
+ maxHeight: '85%',
1264
+ padding: 16
1265
+ },
1266
+ modalTitle: {
1267
+ fontSize: 16,
1268
+ fontWeight: '700',
1269
+ color: '#111827',
1270
+ marginBottom: 12
1271
+ },
1272
+ optionRow: {
1273
+ paddingVertical: 12,
1274
+ borderBottomWidth: 1,
1275
+ borderBottomColor: '#F3F4F6'
1276
+ },
1277
+ optionText: {
1278
+ fontSize: 14,
1279
+ color: '#111827'
1280
+ },
1281
+ optionEmpty: {
1282
+ fontSize: 14,
1283
+ color: '#6B7280',
1284
+ textAlign: 'center',
1285
+ paddingVertical: 16
1286
+ }
1287
+ });