@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,2615 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.AssignmentScreen = AssignmentScreen;
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 _useAssignmentList = require("../hooks/useAssignmentList");
14
+ var _useHomeworkDetails = require("../hooks/useHomeworkDetails");
15
+ var _useHomeworkSubmissions = require("../hooks/useHomeworkSubmissions");
16
+ var _assignmentService = require("../services/assignmentService");
17
+ var _jsxRuntime = require("react/jsx-runtime");
18
+ 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); }
19
+ 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; }
20
+ function normalizeOptions(result) {
21
+ if (!result) {
22
+ return [];
23
+ }
24
+ if (Array.isArray(result)) {
25
+ return result;
26
+ }
27
+ if (Array.isArray(result?.data)) {
28
+ return result.data;
29
+ }
30
+ if (Array.isArray(result?.data?.data)) {
31
+ return result.data.data;
32
+ }
33
+ return [];
34
+ }
35
+ function normalizeAssignments(result) {
36
+ const raw = result?.data ?? result;
37
+ if (Array.isArray(raw)) {
38
+ return raw;
39
+ }
40
+ if (Array.isArray(raw?.data)) {
41
+ return raw.data;
42
+ }
43
+ if (Array.isArray(raw?.data?.data)) {
44
+ return raw.data.data;
45
+ }
46
+ return [];
47
+ }
48
+ function normalizeHomeworkDetail(result) {
49
+ if (!result) return null;
50
+ const raw = result?.data ?? result;
51
+ if (Array.isArray(raw)) {
52
+ return raw[0] ?? null;
53
+ }
54
+ if (Array.isArray(raw?.data)) {
55
+ return raw.data[0] ?? null;
56
+ }
57
+ if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
58
+ if (raw?.data && typeof raw.data === 'object') {
59
+ return raw.data;
60
+ }
61
+ return raw;
62
+ }
63
+ return null;
64
+ }
65
+ function detailValue(source, keys, fallback) {
66
+ for (const key of keys) {
67
+ const value = source?.[key];
68
+ if (value !== undefined && value !== null && value !== '') {
69
+ return value;
70
+ }
71
+ }
72
+ return fallback;
73
+ }
74
+ function SelectModal({
75
+ label,
76
+ value,
77
+ options,
78
+ placeholder,
79
+ onChange
80
+ }) {
81
+ const [open, setOpen] = (0, _react.useState)(false);
82
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
83
+ style: styles.field,
84
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
85
+ style: styles.label,
86
+ children: label
87
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
88
+ onPress: () => setOpen(true),
89
+ style: styles.select,
90
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
91
+ style: styles.selectText,
92
+ children: value?.label ?? placeholder
93
+ })
94
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
95
+ visible: open,
96
+ transparent: true,
97
+ animationType: "fade",
98
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
99
+ style: styles.modalBackdrop,
100
+ onPress: () => setOpen(false),
101
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
102
+ style: styles.modalCard,
103
+ onPress: () => {},
104
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
105
+ style: styles.modalTitle,
106
+ children: label
107
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.FlatList, {
108
+ data: options,
109
+ keyExtractor: (it, idx) => `${it.value}-${idx}`,
110
+ renderItem: ({
111
+ item
112
+ }) => {
113
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
114
+ style: styles.optionRow,
115
+ onPress: () => {
116
+ onChange(item);
117
+ setOpen(false);
118
+ },
119
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
120
+ style: styles.optionText,
121
+ children: item.label
122
+ })
123
+ });
124
+ },
125
+ ListEmptyComponent: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
126
+ style: styles.optionEmpty,
127
+ children: "No options"
128
+ })
129
+ })]
130
+ })
131
+ })
132
+ })]
133
+ });
134
+ }
135
+ function resolveFileBaseUrl(args) {
136
+ if (args.fileBaseUrl) {
137
+ return args.fileBaseUrl.endsWith('/') ? args.fileBaseUrl : `${args.fileBaseUrl}/`;
138
+ }
139
+ const base = args.baseUrl;
140
+ if (base.includes('/api/')) {
141
+ return base.replace('/api/', '/uploads/');
142
+ }
143
+ if (base.endsWith('/api')) {
144
+ return `${base.slice(0, -3)}/uploads/`;
145
+ }
146
+ return base.endsWith('/') ? `${base}uploads/` : `${base}/uploads/`;
147
+ }
148
+ function AssignmentScreen(props) {
149
+ const {
150
+ api,
151
+ baseUrl,
152
+ fileBaseUrl
153
+ } = (0, _useERP.useERP)();
154
+ const downloadsBaseUrl = (0, _react.useMemo)(() => {
155
+ return resolveFileBaseUrl({
156
+ baseUrl,
157
+ fileBaseUrl
158
+ });
159
+ }, [baseUrl, fileBaseUrl]);
160
+ const [mode, setMode] = (0, _react.useState)('list');
161
+ const [selectedHomework, setSelectedHomework] = (0, _react.useState)(null);
162
+ const [submissionsOpen, setSubmissionsOpen] = (0, _react.useState)(false);
163
+ const [editHomeworkId, setEditHomeworkId] = (0, _react.useState)(null);
164
+ const [filterClass, setFilterClass] = (0, _react.useState)(null);
165
+ const [filterSection, setFilterSection] = (0, _react.useState)(null);
166
+ const [filterSubject, setFilterSubject] = (0, _react.useState)(null);
167
+ const [classes, setClasses] = (0, _react.useState)([]);
168
+ const [sections, setSections] = (0, _react.useState)([]);
169
+ const [subjects, setSubjects] = (0, _react.useState)([]);
170
+ const listParams = (0, _react.useMemo)(() => {
171
+ return {
172
+ page: props.page ?? 1,
173
+ perPage: props.perPage ?? 10,
174
+ classId: props.classId ?? filterClass?.value,
175
+ sectionId: props.sectionId ?? filterSection?.value,
176
+ subjectId: props.subjectId ?? filterSubject?.value
177
+ };
178
+ }, [filterClass?.value, filterSection?.value, filterSubject?.value, props.classId, props.page, props.perPage, props.sectionId, props.subjectId]);
179
+ const {
180
+ data,
181
+ isLoading,
182
+ error,
183
+ refetch,
184
+ isFetching
185
+ } = (0, _useAssignmentList.useAssignmentList)(listParams);
186
+ const assignments = (0, _react.useMemo)(() => normalizeAssignments(data), [data]);
187
+ const submissionsQuery = (0, _useHomeworkSubmissions.useHomeworkSubmissions)(selectedHomework?.id !== undefined && selectedHomework?.id !== null ? {
188
+ homeworkId: selectedHomework.id
189
+ } : undefined);
190
+ const homeworkDetailsQuery = (0, _useHomeworkDetails.useHomeworkDetails)(editHomeworkId !== null ? {
191
+ homeworkId: editHomeworkId
192
+ } : undefined);
193
+ const loadClasses = (0, _react.useCallback)(async () => {
194
+ const res = await (0, _assignmentService.fetchTeacherClasses)(api);
195
+ setClasses(normalizeOptions(res));
196
+ }, [api]);
197
+ (0, _react.useEffect)(() => {
198
+ loadClasses().catch(() => {});
199
+ }, [loadClasses]);
200
+ (0, _react.useEffect)(() => {
201
+ if (!filterClass?.value) {
202
+ setSections([]);
203
+ setSubjects([]);
204
+ setFilterSection(null);
205
+ setFilterSubject(null);
206
+ return;
207
+ }
208
+ (0, _assignmentService.fetchTeacherSections)(api, {
209
+ classId: filterClass.value
210
+ }).then(res => {
211
+ setSections(normalizeOptions(res));
212
+ }).catch(() => {});
213
+ if (props.sessionId !== undefined) {
214
+ (0, _assignmentService.fetchTeacherSubjectsByClass)(api, {
215
+ classId: filterClass.value,
216
+ sessionId: props.sessionId
217
+ }).then(res => {
218
+ setSubjects(normalizeOptions(res));
219
+ }).catch(() => {});
220
+ } else {
221
+ setSubjects([]);
222
+ setFilterSubject(null);
223
+ }
224
+ }, [api, filterClass?.value, props.sessionId]);
225
+ const headerRight = (0, _react.useMemo)(() => {
226
+ if (mode === 'list') {
227
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
228
+ onPress: () => setMode('create'),
229
+ style: styles.primaryBtn,
230
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
231
+ style: styles.primaryBtnText,
232
+ children: "Create"
233
+ })
234
+ });
235
+ }
236
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
237
+ onPress: () => setMode('list'),
238
+ style: styles.secondaryBtn,
239
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
240
+ style: styles.secondaryBtnText,
241
+ children: "Back"
242
+ })
243
+ });
244
+ }, [mode]);
245
+ if (mode === 'create') {
246
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(CreateAssignmentView, {
247
+ sessionId: props.sessionId,
248
+ classes: classes,
249
+ api: api,
250
+ downloadsBaseUrl: downloadsBaseUrl,
251
+ onDone: () => {
252
+ setMode('list');
253
+ refetch();
254
+ }
255
+ });
256
+ }
257
+ if (mode === 'edit') {
258
+ if (editHomeworkId === null) {
259
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ErrorState.ErrorState, {
260
+ message: "Missing homework id"
261
+ });
262
+ }
263
+ if (homeworkDetailsQuery.isLoading) {
264
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_LoadingState.LoadingState, {});
265
+ }
266
+ if (homeworkDetailsQuery.error) {
267
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ErrorState.ErrorState, {
268
+ message: homeworkDetailsQuery.error.message
269
+ });
270
+ }
271
+ const detail = normalizeHomeworkDetail(homeworkDetailsQuery.data);
272
+ if (!detail) {
273
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ErrorState.ErrorState, {
274
+ message: "Unable to load homework details"
275
+ });
276
+ }
277
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(EditAssignmentView, {
278
+ api: api,
279
+ sessionId: props.sessionId,
280
+ classes: classes,
281
+ downloadsBaseUrl: downloadsBaseUrl,
282
+ initial: detail,
283
+ onDone: () => {
284
+ setMode('list');
285
+ setEditHomeworkId(null);
286
+ refetch();
287
+ },
288
+ onCancel: () => {
289
+ setMode('list');
290
+ setEditHomeworkId(null);
291
+ }
292
+ });
293
+ }
294
+ if (isLoading) {
295
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_LoadingState.LoadingState, {});
296
+ }
297
+ if (error) {
298
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ErrorState.ErrorState, {
299
+ message: error.message
300
+ });
301
+ }
302
+ const closeSubmissions = () => {
303
+ setSubmissionsOpen(false);
304
+ };
305
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.SafeAreaView, {
306
+ style: styles.root,
307
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
308
+ style: styles.header,
309
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
310
+ style: styles.headerTitle,
311
+ children: "Assignments"
312
+ }), headerRight]
313
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
314
+ style: styles.filters,
315
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(SelectModal, {
316
+ label: "Class",
317
+ value: filterClass,
318
+ options: classes,
319
+ placeholder: "Select class",
320
+ onChange: next => setFilterClass(next)
321
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SelectModal, {
322
+ label: "Section",
323
+ value: filterSection,
324
+ options: sections,
325
+ placeholder: "Select section",
326
+ onChange: next => setFilterSection(next)
327
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SelectModal, {
328
+ label: "Subject",
329
+ value: filterSubject,
330
+ options: subjects,
331
+ placeholder: props.sessionId === undefined ? 'Provide sessionId' : 'Select subject',
332
+ onChange: next => setFilterSubject(next)
333
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
334
+ style: styles.secondaryBtn,
335
+ onPress: () => {
336
+ refetch();
337
+ },
338
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
339
+ style: styles.secondaryBtnText,
340
+ children: isFetching ? 'Refreshing...' : 'Apply'
341
+ })
342
+ })]
343
+ }), assignments.length === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_EmptyState.EmptyState, {}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.FlatList, {
344
+ data: assignments,
345
+ keyExtractor: (it, idx) => `${it?.id ?? idx}`,
346
+ contentContainerStyle: styles.list,
347
+ renderItem: ({
348
+ item
349
+ }) => {
350
+ const total = item?.total_submission !== undefined && item?.total_submission !== null ? Number(item.total_submission) : 0;
351
+ const canView = Number.isFinite(total) && total > 0;
352
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
353
+ style: styles.card,
354
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
355
+ style: styles.cardTop,
356
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
357
+ style: styles.cardTitle,
358
+ numberOfLines: 1,
359
+ children: item?.title ?? item?.homework_title ?? 'Assignment'
360
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
361
+ style: styles.cardActions,
362
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
363
+ onPress: () => {
364
+ if (item.id === undefined || item.id === null) return;
365
+ setEditHomeworkId(item.id);
366
+ setMode('edit');
367
+ },
368
+ style: styles.editBtn,
369
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
370
+ style: styles.editBtnText,
371
+ children: "Edit"
372
+ })
373
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
374
+ onPress: () => {
375
+ if (!canView) return;
376
+ setSelectedHomework(item);
377
+ setSubmissionsOpen(true);
378
+ },
379
+ activeOpacity: canView ? 0.8 : 1,
380
+ style: [styles.viewBtn, canView ? styles.viewBtnActive : styles.viewBtnDisabled],
381
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
382
+ style: [styles.viewBtnText, canView ? styles.viewBtnTextActive : styles.viewBtnTextDisabled],
383
+ children: ["View ", Number.isFinite(total) ? String(total) : '0']
384
+ })
385
+ })]
386
+ })]
387
+ }), item?.description ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
388
+ style: styles.cardSub,
389
+ children: item.description
390
+ }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
391
+ style: styles.cardMeta,
392
+ children: [item?.class_name ? `Class: ${item.class_name}` : null, item?.section_name ? `Section: ${item.section_name}` : null, item?.subject_name ? `Subject: ${item.subject_name}` : null].filter(Boolean).join(' • ')
393
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
394
+ style: styles.cardMeta,
395
+ children: [item?.homew_date ? `From: ${item.homew_date}` : null, item?.submission_date ? `To: ${item.submission_date}` : null, item?.status ? `Status: ${item.status}` : null].filter(Boolean).join(' • ')
396
+ })]
397
+ });
398
+ }
399
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
400
+ animationType: "fade",
401
+ transparent: true,
402
+ visible: submissionsOpen,
403
+ onRequestClose: closeSubmissions,
404
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
405
+ style: styles.modalOverlay,
406
+ onPress: closeSubmissions,
407
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
408
+ style: styles.sheetCard,
409
+ onPress: () => {},
410
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
411
+ style: styles.modalHeader,
412
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
413
+ style: styles.modalHeaderTitle,
414
+ numberOfLines: 1,
415
+ children: "Submissions"
416
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
417
+ onPress: closeSubmissions,
418
+ style: styles.modalClose,
419
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
420
+ style: styles.modalCloseText,
421
+ children: "Close"
422
+ })
423
+ })]
424
+ }), submissionsQuery.isLoading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_LoadingState.LoadingState, {}) : submissionsQuery.error ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_ErrorState.ErrorState, {
425
+ message: submissionsQuery.error.message
426
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(HomeworkSubmissionsList, {
427
+ data: submissionsQuery.data,
428
+ api: api,
429
+ downloadsBaseUrl: downloadsBaseUrl
430
+ })]
431
+ })
432
+ })
433
+ })]
434
+ });
435
+ }
436
+ function normalizeSubmissions(result) {
437
+ if (!result) return [];
438
+ const raw = result?.data ?? result;
439
+ if (Array.isArray(raw)) return raw;
440
+ if (Array.isArray(raw?.data)) return raw.data;
441
+ if (Array.isArray(raw?.data?.data)) return raw.data.data;
442
+ return [];
443
+ }
444
+ function isImageUrl(url) {
445
+ const lower = url.toLowerCase();
446
+ return lower.endsWith('.png') || lower.endsWith('.jpg') || lower.endsWith('.jpeg') || lower.endsWith('.webp') || lower.endsWith('.gif');
447
+ }
448
+ function fileBaseName(path) {
449
+ const raw = String(path ?? '');
450
+ const cleaned = raw.split('?')[0] ?? raw;
451
+ const parts = cleaned.split(/[\\/]/);
452
+ return parts[parts.length - 1] || raw;
453
+ }
454
+ function splitFileCsv(raw) {
455
+ if (!raw) return [];
456
+ return String(raw).split(',').map(s => s.trim()).filter(Boolean);
457
+ }
458
+ function ensureFileUri(path) {
459
+ if (!path) {
460
+ return '';
461
+ }
462
+ return path.startsWith('file://') ? path : `file://${path}`;
463
+ }
464
+ function getUploadPath(value) {
465
+ const candidates = [value?.file, value?.document, value?.image, value?.path, value?.file_path, value?.filePath, value?.document_path, value?.documentPath, value?.image_path, value?.imagePath, value?.url, value?.file_url, value?.fileUrl];
466
+ for (const candidate of candidates) {
467
+ if (typeof candidate === 'string' && candidate.trim()) {
468
+ return candidate.trim();
469
+ }
470
+ }
471
+ return '';
472
+ }
473
+ function resolveUploadUrl(downloadsBaseUrl, rawPath) {
474
+ const raw = String(rawPath ?? '').trim();
475
+ if (!raw) {
476
+ return '';
477
+ }
478
+ if (/^https?:\/\//i.test(raw)) {
479
+ return encodeURI(raw);
480
+ }
481
+ const cleaned = raw.replace(/\\/g, '/').replace(/^\/+/, '');
482
+ const uploadsRoot = downloadsBaseUrl.replace(/uploads\/?$/i, '');
483
+ if (cleaned.startsWith('uploads/')) {
484
+ return encodeURI(`${uploadsRoot}${cleaned}`);
485
+ }
486
+ if (cleaned.startsWith('student/homework/')) {
487
+ return encodeURI(`${downloadsBaseUrl}${cleaned}`);
488
+ }
489
+ if (cleaned.startsWith('homework/')) {
490
+ return encodeURI(`${downloadsBaseUrl}student/${cleaned}`);
491
+ }
492
+ return encodeURI(`${downloadsBaseUrl}student/homework/${cleaned}`);
493
+ }
494
+ async function downloadPdfToLocal(args) {
495
+ const fallbackName = args.fileName || `document-${Date.now()}.pdf`;
496
+ const safeName = fallbackName.toLowerCase().endsWith('.pdf') ? fallbackName : `${fallbackName}.pdf`;
497
+ const targetPath = `${args.RNFS.CachesDirectoryPath}/${safeName}`;
498
+ const result = await args.RNFS.downloadFile({
499
+ fromUrl: args.url,
500
+ toFile: targetPath,
501
+ headers: args.headers,
502
+ background: true,
503
+ discretionary: true
504
+ }).promise;
505
+ if (result?.statusCode && result.statusCode >= 400) {
506
+ throw new Error(`PDF download failed with status ${result.statusCode}`);
507
+ }
508
+ const exists = await args.RNFS.exists(targetPath);
509
+ if (!exists) {
510
+ throw new Error('Downloaded PDF file is missing');
511
+ }
512
+ return ensureFileUri(targetPath);
513
+ }
514
+ async function resolveViewerHeaders(args) {
515
+ const token = args.authToken ?? (args.getAuthToken ? await args.getAuthToken() : null);
516
+ const headers = {};
517
+ if (token) {
518
+ headers.Authorization = token;
519
+ }
520
+ if (args.schoolCode) {
521
+ headers.school_code = args.schoolCode;
522
+ }
523
+ return headers;
524
+ }
525
+ function tryGetImageViewer() {
526
+ try {
527
+ const mod = require('react-native-image-viewing');
528
+ return mod?.default ?? mod;
529
+ } catch {
530
+ return null;
531
+ }
532
+ }
533
+ function tryGetCalendar() {
534
+ try {
535
+ const mod = require('react-native-calendars');
536
+ return mod?.Calendar ?? mod?.default ?? mod;
537
+ } catch {
538
+ return null;
539
+ }
540
+ }
541
+ function tryGetRNFS() {
542
+ try {
543
+ return require('react-native-fs');
544
+ } catch {
545
+ return null;
546
+ }
547
+ }
548
+ function tryGetFileViewer() {
549
+ try {
550
+ const mod = require('react-native-file-viewer');
551
+ return mod?.default ?? mod;
552
+ } catch {
553
+ return null;
554
+ }
555
+ }
556
+ function tryGetPdf() {
557
+ try {
558
+ const mod = require('react-native-pdf');
559
+ return mod?.default ?? mod;
560
+ } catch {
561
+ return null;
562
+ }
563
+ }
564
+ function toYmd(d) {
565
+ const y = d.getFullYear();
566
+ const m = String(d.getMonth() + 1).padStart(2, '0');
567
+ const day = String(d.getDate()).padStart(2, '0');
568
+ return `${y}-${m}-${day}`;
569
+ }
570
+ function addDays(d, days) {
571
+ const next = new Date(d);
572
+ next.setDate(next.getDate() + days);
573
+ return next;
574
+ }
575
+ function rangeMarkedDates(start, end) {
576
+ if (!start || !end) return {};
577
+ const s = new Date(start);
578
+ const e = new Date(end);
579
+ if (Number.isNaN(s.getTime()) || Number.isNaN(e.getTime())) return {};
580
+ const from = s <= e ? s : e;
581
+ const to = s <= e ? e : s;
582
+ const out = {};
583
+ let cur = new Date(from);
584
+ while (cur <= to) {
585
+ const key = toYmd(cur);
586
+ out[key] = {
587
+ color: '#111827',
588
+ textColor: 'white'
589
+ };
590
+ cur = addDays(cur, 1);
591
+ }
592
+ const startKey = toYmd(from);
593
+ const endKey = toYmd(to);
594
+ out[startKey] = {
595
+ startingDay: true,
596
+ color: '#111827',
597
+ textColor: 'white'
598
+ };
599
+ out[endKey] = {
600
+ endingDay: true,
601
+ color: '#111827',
602
+ textColor: 'white'
603
+ };
604
+ return out;
605
+ }
606
+ function HomeworkSubmissionsList({
607
+ data,
608
+ api,
609
+ downloadsBaseUrl
610
+ }) {
611
+ const {
612
+ authToken,
613
+ getAuthToken,
614
+ schoolCode
615
+ } = (0, _useERP.useERP)();
616
+ const items = (0, _react.useMemo)(() => normalizeSubmissions(data), [data]);
617
+ const ImageView = (0, _react.useMemo)(() => tryGetImageViewer(), []);
618
+ const Pdf = (0, _react.useMemo)(() => tryGetPdf(), []);
619
+ const RNFS = (0, _react.useMemo)(() => tryGetRNFS(), []);
620
+ const FileViewer = (0, _react.useMemo)(() => tryGetFileViewer(), []);
621
+ const [viewerOpen, setViewerOpen] = (0, _react.useState)(false);
622
+ const [viewerIndex, setViewerIndex] = (0, _react.useState)(0);
623
+ const [viewerImages, setViewerImages] = (0, _react.useState)([]);
624
+ const [pdfVisible, setPdfVisible] = (0, _react.useState)(false);
625
+ const [pdfUri, setPdfUri] = (0, _react.useState)('');
626
+ const [pdfHeaders, setPdfHeaders] = (0, _react.useState)({});
627
+ const [pdfLoading, setPdfLoading] = (0, _react.useState)(false);
628
+ const [drafts, setDrafts] = (0, _react.useState)({});
629
+ const [saving, setSaving] = (0, _react.useState)(false);
630
+ const [downloadingId, setDownloadingId] = (0, _react.useState)(null);
631
+ if (!items.length) {
632
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_EmptyState.EmptyState, {
633
+ title: "No Submission"
634
+ });
635
+ }
636
+ const openUploads = async item => {
637
+ const list = Array.isArray(item?.submitted_homework) ? item.submitted_homework : [];
638
+ const uploads = list.map(el => {
639
+ const rawPath = getUploadPath(el);
640
+ return {
641
+ rawPath,
642
+ url: resolveUploadUrl(downloadsBaseUrl, rawPath)
643
+ };
644
+ }).filter(entry => entry.rawPath && entry.url);
645
+ const urls = uploads.map(entry => entry.url);
646
+ if (!urls.length) {
647
+ _reactNative.Alert.alert('No Uploads');
648
+ return;
649
+ }
650
+ const headers = await resolveViewerHeaders({
651
+ authToken,
652
+ getAuthToken,
653
+ schoolCode
654
+ });
655
+ const imageUrls = uploads.filter(entry => {
656
+ return isImageUrl(entry.url) || isImageUrl(entry.rawPath);
657
+ }).map(entry => entry.url);
658
+ if (ImageView && imageUrls.length) {
659
+ setViewerImages(imageUrls.map(uri => ({
660
+ uri,
661
+ headers: Object.keys(headers).length ? headers : undefined
662
+ })));
663
+ setViewerIndex(0);
664
+ setViewerOpen(true);
665
+ return;
666
+ }
667
+ const firstUpload = uploads[0];
668
+ const firstUrl = firstUpload?.url ?? '';
669
+ const firstFile = firstUpload?.rawPath ?? '';
670
+ const isPdf = firstUrl.toLowerCase().endsWith('.pdf') || firstFile.toLowerCase().endsWith('.pdf');
671
+ if (Pdf && isPdf) {
672
+ if (!RNFS) {
673
+ setPdfUri(firstUrl);
674
+ setPdfHeaders(headers);
675
+ setPdfVisible(true);
676
+ return;
677
+ }
678
+ setDownloadingId(String(item?.id ?? 'download'));
679
+ setPdfLoading(true);
680
+ try {
681
+ const localPdf = await downloadPdfToLocal({
682
+ RNFS,
683
+ url: firstUrl,
684
+ fileName: fileBaseName(firstFile || 'document.pdf'),
685
+ headers
686
+ });
687
+ setPdfHeaders({});
688
+ setPdfUri(localPdf);
689
+ setPdfVisible(true);
690
+ } catch (error) {
691
+ _reactNative.Alert.alert('Error', error?.message ?? 'Could not open PDF.');
692
+ } finally {
693
+ setPdfLoading(false);
694
+ setDownloadingId(null);
695
+ }
696
+ return;
697
+ }
698
+ if (!RNFS || !FileViewer) {
699
+ _reactNative.Alert.alert('Viewer Missing', 'Install react-native-fs and react-native-file-viewer to open documents inside the app.');
700
+ return;
701
+ }
702
+ setDownloadingId(String(item?.id ?? 'download'));
703
+ try {
704
+ let fileName = fileBaseName(firstFile || 'document');
705
+ const hasExtension = fileName.includes('.') && (fileName.split('.').pop()?.length ?? 0) > 0;
706
+ if (!hasExtension && firstUrl.startsWith('http')) {
707
+ try {
708
+ const response = await fetch(firstUrl, {
709
+ method: 'HEAD'
710
+ });
711
+ const contentType = response.headers.get('content-type') ?? '';
712
+ if (contentType.includes('pdf')) fileName += '.pdf';else if (contentType.includes('word') || contentType.includes('doc')) fileName += '.docx';else if (contentType.includes('sheet') || contentType.includes('excel')) fileName += '.xlsx';else fileName += '.pdf';
713
+ } catch {
714
+ fileName += '.pdf';
715
+ }
716
+ }
717
+ const localFile = `${RNFS.DocumentDirectoryPath}/${fileName}`;
718
+ await RNFS.downloadFile({
719
+ fromUrl: firstUrl,
720
+ toFile: localFile,
721
+ headers
722
+ }).promise;
723
+ await FileViewer.open(localFile, {
724
+ showOpenWithDialog: true
725
+ });
726
+ } catch {
727
+ _reactNative.Alert.alert('Error', 'Could not view the file.');
728
+ } finally {
729
+ setDownloadingId(null);
730
+ }
731
+ };
732
+ const setDraft = (id, patch) => {
733
+ setDrafts(prev => {
734
+ const key = String(id);
735
+ const next = {
736
+ ...(prev[key] ?? {}),
737
+ ...patch
738
+ };
739
+ return {
740
+ ...prev,
741
+ [key]: next
742
+ };
743
+ });
744
+ };
745
+ const giveMarks = async () => {
746
+ const payload = Object.entries(drafts).map(([id, v]) => {
747
+ return {
748
+ id,
749
+ number: v.number,
750
+ remark: v.remark
751
+ };
752
+ }).filter(it => it.number !== undefined || it.remark !== undefined);
753
+ if (!payload.length) {
754
+ _reactNative.Alert.alert('Error', 'Please fill any field');
755
+ return;
756
+ }
757
+ setSaving(true);
758
+ try {
759
+ const res = await (0, _assignmentService.giveMarksToHomework)(api, payload);
760
+ _reactNative.Alert.alert(res?.Status ?? 'Success');
761
+ } catch (e) {
762
+ _reactNative.Alert.alert('Error', e?.message ?? 'Failed');
763
+ } finally {
764
+ setSaving(false);
765
+ }
766
+ };
767
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
768
+ style: {
769
+ flex: 1
770
+ },
771
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.FlatList, {
772
+ data: items,
773
+ keyExtractor: (it, idx) => String(it?.id ?? idx),
774
+ contentContainerStyle: styles.submissionList,
775
+ renderItem: ({
776
+ item
777
+ }) => {
778
+ const roll = item?.roll_no ?? item?.roll ?? '-';
779
+ const studentName = item?.full_name ?? item?.student_name ?? 'Student';
780
+ const submittedAt = item?.submission_date ?? item?.date ?? '-';
781
+ const marksValue = drafts[String(item?.id)]?.number ?? item?.number ?? '';
782
+ const remarkValue = drafts[String(item?.id)]?.remark ?? item?.remark ?? '';
783
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
784
+ style: styles.submissionCard,
785
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
786
+ style: styles.submissionHeaderRow,
787
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
788
+ style: styles.submissionName,
789
+ numberOfLines: 1,
790
+ children: studentName
791
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
792
+ style: styles.submissionMeta,
793
+ numberOfLines: 1,
794
+ children: ["Roll: ", String(roll)]
795
+ })]
796
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
797
+ style: styles.submissionMeta,
798
+ numberOfLines: 1,
799
+ children: submittedAt
800
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
801
+ style: styles.submissionInputRow,
802
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
803
+ style: {
804
+ flex: 1
805
+ },
806
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
807
+ style: styles.label,
808
+ children: "Marks"
809
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
810
+ keyboardType: "numeric",
811
+ value: String(marksValue),
812
+ onChangeText: val => setDraft(item.id, {
813
+ number: val
814
+ }),
815
+ style: styles.smallInput,
816
+ placeholderTextColor: "#9CA3AF"
817
+ })]
818
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
819
+ style: {
820
+ flex: 2
821
+ },
822
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
823
+ style: styles.label,
824
+ children: "Remark"
825
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
826
+ value: String(remarkValue),
827
+ onChangeText: val => setDraft(item.id, {
828
+ remark: val
829
+ }),
830
+ style: styles.smallInput,
831
+ placeholderTextColor: "#9CA3AF"
832
+ })]
833
+ })]
834
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
835
+ onPress: () => {
836
+ openUploads(item).catch(() => {});
837
+ },
838
+ style: styles.uploadBtn,
839
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
840
+ style: styles.uploadBtnText,
841
+ children: downloadingId === String(item?.id ?? '') ? 'Opening...' : 'View Uploads'
842
+ })
843
+ })]
844
+ });
845
+ }
846
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
847
+ style: styles.submissionFooter,
848
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
849
+ onPress: () => {
850
+ giveMarks().catch(() => {});
851
+ },
852
+ style: styles.giveMarksBtn,
853
+ disabled: saving,
854
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
855
+ style: styles.giveMarksBtnText,
856
+ children: saving ? 'Saving...' : 'Give Marks'
857
+ })
858
+ })
859
+ }), ImageView ? /*#__PURE__*/(0, _jsxRuntime.jsx)(ImageView, {
860
+ images: viewerImages,
861
+ imageIndex: viewerIndex,
862
+ visible: viewerOpen,
863
+ onRequestClose: () => setViewerOpen(false)
864
+ }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
865
+ visible: pdfVisible,
866
+ animationType: "slide",
867
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.SafeAreaView, {
868
+ style: styles.pdfRoot,
869
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
870
+ style: styles.pdfHeader,
871
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
872
+ style: styles.pdfTitle,
873
+ children: "Document Viewer"
874
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
875
+ onPress: () => {
876
+ setPdfVisible(false);
877
+ setPdfUri('');
878
+ setPdfHeaders({});
879
+ },
880
+ style: styles.modalClose,
881
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
882
+ style: styles.modalCloseText,
883
+ children: "Close"
884
+ })
885
+ })]
886
+ }), pdfLoading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_LoadingState.LoadingState, {}) : Pdf && pdfUri ? /*#__PURE__*/(0, _jsxRuntime.jsx)(Pdf, {
887
+ source: {
888
+ uri: pdfUri,
889
+ cache: true,
890
+ headers: pdfHeaders
891
+ },
892
+ style: styles.pdfViewer,
893
+ onError: error => {
894
+ _reactNative.Alert.alert('Error', error?.message ?? 'Could not open PDF.');
895
+ }
896
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_EmptyState.EmptyState, {
897
+ title: "PDF Viewer Missing"
898
+ })]
899
+ })
900
+ })]
901
+ });
902
+ }
903
+ function CreateAssignmentView({
904
+ api,
905
+ sessionId,
906
+ classes,
907
+ downloadsBaseUrl,
908
+ onDone
909
+ }) {
910
+ const {
911
+ authToken,
912
+ getAuthToken,
913
+ schoolCode
914
+ } = (0, _useERP.useERP)();
915
+ const ImageView = (0, _react.useMemo)(() => tryGetImageViewer(), []);
916
+ const Pdf = (0, _react.useMemo)(() => tryGetPdf(), []);
917
+ const RNFS = (0, _react.useMemo)(() => tryGetRNFS(), []);
918
+ const FileViewer = (0, _react.useMemo)(() => tryGetFileViewer(), []);
919
+ const [viewerOpen, setViewerOpen] = (0, _react.useState)(false);
920
+ const [viewerIndex, setViewerIndex] = (0, _react.useState)(0);
921
+ const [viewerImages, setViewerImages] = (0, _react.useState)([]);
922
+ const [pdfVisible, setPdfVisible] = (0, _react.useState)(false);
923
+ const [pdfUri, setPdfUri] = (0, _react.useState)('');
924
+ const [pdfHeaders, setPdfHeaders] = (0, _react.useState)({});
925
+ const [pdfLoading, setPdfLoading] = (0, _react.useState)(false);
926
+ const [isDownloading, setIsDownloading] = (0, _react.useState)(false);
927
+ const [busy, setBusy] = (0, _react.useState)(false);
928
+ const [title, setTitle] = (0, _react.useState)('');
929
+ const [description, setDescription] = (0, _react.useState)('');
930
+ const [classOpt, setClassOpt] = (0, _react.useState)(null);
931
+ const [sectionOpt, setSectionOpt] = (0, _react.useState)(null);
932
+ const [subjectOpt, setSubjectOpt] = (0, _react.useState)(null);
933
+ const [sections, setSections] = (0, _react.useState)([]);
934
+ const [subjects, setSubjects] = (0, _react.useState)([]);
935
+ const [homewDate, setHomewDate] = (0, _react.useState)('');
936
+ const [submissionDate, setSubmissionDate] = (0, _react.useState)('');
937
+ const [status, setStatus] = (0, _react.useState)('publish');
938
+ const [fileList, setFileList] = (0, _react.useState)([]);
939
+ const [activeFile, setActiveFile] = (0, _react.useState)('');
940
+ const [fileError, setFileError] = (0, _react.useState)(null);
941
+ (0, _react.useEffect)(() => {
942
+ if (!classOpt?.value) {
943
+ setSections([]);
944
+ setSubjects([]);
945
+ setSectionOpt(null);
946
+ setSubjectOpt(null);
947
+ return;
948
+ }
949
+ (0, _assignmentService.fetchTeacherSections)(api, {
950
+ classId: classOpt.value
951
+ }).then(res => {
952
+ setSections(normalizeOptions(res));
953
+ }).catch(() => {});
954
+ if (sessionId !== undefined) {
955
+ (0, _assignmentService.fetchTeacherSubjectsByClass)(api, {
956
+ classId: classOpt.value,
957
+ sessionId
958
+ }).then(res => {
959
+ setSubjects(normalizeOptions(res));
960
+ }).catch(() => {});
961
+ } else {
962
+ setSubjects([]);
963
+ setSubjectOpt(null);
964
+ }
965
+ }, [api, classOpt?.value, sessionId]);
966
+ const viewAttachment = (0, _react.useCallback)(async rawFile => {
967
+ const file = String(rawFile ?? '');
968
+ if (!file) {
969
+ _reactNative.Alert.alert('No Attachment');
970
+ return;
971
+ }
972
+ const url = resolveUploadUrl(downloadsBaseUrl, file);
973
+ const headers = await resolveViewerHeaders({
974
+ authToken,
975
+ getAuthToken,
976
+ schoolCode
977
+ });
978
+ const isImage = isImageUrl(url) || isImageUrl(file);
979
+ if (ImageView && isImage) {
980
+ const imageFiles = fileList.filter(p => {
981
+ const u = resolveUploadUrl(downloadsBaseUrl, p);
982
+ return isImageUrl(u) || isImageUrl(p);
983
+ });
984
+ const imageUrls = imageFiles.map(p => resolveUploadUrl(downloadsBaseUrl, p));
985
+ const idx = imageFiles.findIndex(p => String(p) === String(file));
986
+ setViewerImages(imageUrls.map(uri => ({
987
+ uri,
988
+ headers: Object.keys(headers).length ? headers : undefined
989
+ })));
990
+ setViewerIndex(Math.max(0, idx));
991
+ setViewerOpen(true);
992
+ return;
993
+ }
994
+ const isPdf = url.toLowerCase().endsWith('.pdf') || String(file).toLowerCase().endsWith('.pdf');
995
+ if (Pdf && isPdf) {
996
+ if (!RNFS) {
997
+ setPdfUri(url);
998
+ setPdfHeaders(headers);
999
+ setPdfVisible(true);
1000
+ return;
1001
+ }
1002
+ setIsDownloading(true);
1003
+ setPdfLoading(true);
1004
+ try {
1005
+ const localPdf = await downloadPdfToLocal({
1006
+ RNFS,
1007
+ url,
1008
+ fileName: fileBaseName(file || 'document.pdf'),
1009
+ headers
1010
+ });
1011
+ setPdfHeaders({});
1012
+ setPdfUri(localPdf);
1013
+ setPdfVisible(true);
1014
+ } catch (error) {
1015
+ _reactNative.Alert.alert('Error', error?.message ?? 'Could not open PDF.');
1016
+ } finally {
1017
+ setPdfLoading(false);
1018
+ setIsDownloading(false);
1019
+ }
1020
+ return;
1021
+ }
1022
+ if (!RNFS || !FileViewer) {
1023
+ _reactNative.Alert.alert('Viewer Missing', 'Install react-native-fs and react-native-file-viewer to open documents inside the app.');
1024
+ return;
1025
+ }
1026
+ setIsDownloading(true);
1027
+ try {
1028
+ let name = fileBaseName(file) || 'document';
1029
+ const hasExtension = name.includes('.') && (name.split('.').pop()?.length ?? 0) > 0;
1030
+ if (!hasExtension && url.startsWith('http')) {
1031
+ try {
1032
+ const response = await fetch(url, {
1033
+ method: 'HEAD'
1034
+ });
1035
+ const contentType = response.headers.get('content-type') ?? '';
1036
+ if (contentType.includes('pdf')) name += '.pdf';else if (contentType.includes('word') || contentType.includes('doc')) name += '.docx';else if (contentType.includes('sheet') || contentType.includes('excel')) name += '.xlsx';else if (contentType.includes('jpeg') || contentType.includes('jpg')) name += '.jpg';else if (contentType.includes('png')) name += '.png';else name += '.pdf';
1037
+ } catch {
1038
+ name += '.pdf';
1039
+ }
1040
+ }
1041
+ const localFile = `${RNFS.DocumentDirectoryPath}/${name}`;
1042
+ await RNFS.downloadFile({
1043
+ fromUrl: url,
1044
+ toFile: localFile,
1045
+ headers
1046
+ }).promise;
1047
+ await FileViewer.open(localFile, {
1048
+ showOpenWithDialog: true
1049
+ });
1050
+ } catch {
1051
+ _reactNative.Alert.alert('Error', 'Could not view the file.');
1052
+ } finally {
1053
+ setIsDownloading(false);
1054
+ }
1055
+ }, [FileViewer, ImageView, Pdf, RNFS, authToken, downloadsBaseUrl, fileList, getAuthToken, schoolCode]);
1056
+ const pickFromCamera = (0, _react.useCallback)(async () => {
1057
+ let picker = null;
1058
+ try {
1059
+ picker = require('react-native-image-picker');
1060
+ } catch {}
1061
+ if (!picker?.launchCamera) {
1062
+ _reactNative.Alert.alert('Error', 'Camera picker is not available');
1063
+ return;
1064
+ }
1065
+ const res = await new Promise(resolve => {
1066
+ picker.launchCamera({
1067
+ mediaType: 'photo',
1068
+ saveToPhotos: true,
1069
+ includeBase64: false
1070
+ }, r => resolve(r));
1071
+ });
1072
+ const asset = res?.assets?.[0];
1073
+ if (!asset?.uri) return;
1074
+ const file = {
1075
+ uri: asset.uri,
1076
+ name: asset.fileName ?? 'camera.jpg',
1077
+ type: asset.type ?? 'image/jpeg'
1078
+ };
1079
+ setFileError(null);
1080
+ setBusy(true);
1081
+ try {
1082
+ const uploaded = await (0, _assignmentService.uploadAssignmentFile)(api, file);
1083
+ if (uploaded?.Status === 'Success' && uploaded.data) {
1084
+ const nextPath = String(uploaded.data);
1085
+ setFileList(prev => [...prev, nextPath]);
1086
+ setActiveFile(prev => prev || nextPath);
1087
+ } else {
1088
+ setFileError('File upload failed');
1089
+ }
1090
+ } finally {
1091
+ setBusy(false);
1092
+ }
1093
+ }, [api]);
1094
+ const pickFiles = (0, _react.useCallback)(async () => {
1095
+ let dp = null;
1096
+ try {
1097
+ dp = require('react-native-document-picker');
1098
+ } catch {}
1099
+ if (!dp) {
1100
+ return;
1101
+ }
1102
+ const selected = await dp.pick({
1103
+ presentationStyle: 'fullScreen',
1104
+ allowMultiSelection: true,
1105
+ type: [dp.types.pdf, dp.types.images]
1106
+ });
1107
+ const picks = Array.isArray(selected) ? selected : [selected];
1108
+ const allowed = ['pdf', 'png', 'jpg', 'jpeg', 'webp'];
1109
+ const files = picks.map(it => ({
1110
+ uri: it.uri,
1111
+ name: it.name ?? 'file',
1112
+ type: it.type ?? 'application/octet-stream'
1113
+ })).filter(it => !!it.uri);
1114
+ const invalid = files.find(it => {
1115
+ const ext = String(it.name).split('.').pop()?.toLowerCase();
1116
+ return ext ? !allowed.includes(ext) : false;
1117
+ });
1118
+ if (invalid) {
1119
+ setFileError('Only PDF or image files are allowed');
1120
+ return;
1121
+ }
1122
+ setFileError(null);
1123
+ setBusy(true);
1124
+ try {
1125
+ const uploaded = [];
1126
+ for (const f of files) {
1127
+ const res = await (0, _assignmentService.uploadAssignmentFile)(api, f);
1128
+ if (res?.Status === 'Success' && res.data) {
1129
+ uploaded.push(String(res.data));
1130
+ }
1131
+ }
1132
+ if (!uploaded.length) {
1133
+ setFileError('File upload failed');
1134
+ return;
1135
+ }
1136
+ setFileList(prev => [...prev, ...uploaded]);
1137
+ setActiveFile(prev => prev ? prev : uploaded[0] ?? '');
1138
+ } finally {
1139
+ setBusy(false);
1140
+ }
1141
+ }, [api]);
1142
+ const submit = (0, _react.useCallback)(async () => {
1143
+ if (!title || !description || !classOpt?.value || !sectionOpt?.value || !subjectOpt?.value || !homewDate || !submissionDate || !fileList.length) {
1144
+ if (!fileList.length) {
1145
+ setFileError('File is required');
1146
+ }
1147
+ return;
1148
+ }
1149
+ const fileCsv = fileList.join(',');
1150
+ const payload = {
1151
+ title,
1152
+ description,
1153
+ class_id: classOpt.value,
1154
+ section_id: sectionOpt.value,
1155
+ sub_id: subjectOpt.value,
1156
+ homew_date: homewDate,
1157
+ submission_date: submissionDate,
1158
+ status,
1159
+ file: fileCsv
1160
+ };
1161
+ setBusy(true);
1162
+ try {
1163
+ await (0, _assignmentService.createAssignment)(api, payload);
1164
+ onDone();
1165
+ } finally {
1166
+ setBusy(false);
1167
+ }
1168
+ }, [api, classOpt?.value, description, homewDate, onDone, sectionOpt?.value, status, subjectOpt?.value, submissionDate, title, fileList]);
1169
+ const canPickSubject = sessionId !== undefined;
1170
+ const canSubmit = canPickSubject && fileList.length > 0 && !busy;
1171
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.SafeAreaView, {
1172
+ style: styles.root,
1173
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1174
+ style: styles.header,
1175
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1176
+ style: styles.headerTitle,
1177
+ children: "Create Assignment"
1178
+ })
1179
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
1180
+ contentContainerStyle: styles.form,
1181
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1182
+ style: styles.field,
1183
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1184
+ style: styles.label,
1185
+ children: "Title"
1186
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
1187
+ value: title,
1188
+ onChangeText: setTitle,
1189
+ placeholder: "Enter title",
1190
+ placeholderTextColor: "#9CA3AF",
1191
+ style: styles.input
1192
+ })]
1193
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1194
+ style: styles.field,
1195
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1196
+ style: styles.label,
1197
+ children: "Description"
1198
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
1199
+ value: description,
1200
+ onChangeText: setDescription,
1201
+ placeholder: "Enter description",
1202
+ placeholderTextColor: "#9CA3AF",
1203
+ style: [styles.input, styles.textarea],
1204
+ multiline: true
1205
+ })]
1206
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SelectModal, {
1207
+ label: "Class",
1208
+ value: classOpt,
1209
+ options: classes,
1210
+ placeholder: "Select class",
1211
+ onChange: setClassOpt
1212
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SelectModal, {
1213
+ label: "Section",
1214
+ value: sectionOpt,
1215
+ options: sections,
1216
+ placeholder: "Select section",
1217
+ onChange: setSectionOpt
1218
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SelectModal, {
1219
+ label: "Subject",
1220
+ value: subjectOpt,
1221
+ options: subjects,
1222
+ placeholder: canPickSubject ? 'Select subject' : 'Provide sessionId',
1223
+ onChange: setSubjectOpt
1224
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1225
+ style: styles.field,
1226
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1227
+ style: styles.label,
1228
+ children: "Homework Date (YYYY-MM-DD)"
1229
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
1230
+ value: homewDate,
1231
+ onChangeText: setHomewDate,
1232
+ placeholder: "2026-01-31",
1233
+ placeholderTextColor: "#9CA3AF",
1234
+ style: styles.input
1235
+ })]
1236
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1237
+ style: styles.field,
1238
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1239
+ style: styles.label,
1240
+ children: "Submission Date (YYYY-MM-DD)"
1241
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
1242
+ value: submissionDate,
1243
+ onChangeText: setSubmissionDate,
1244
+ placeholder: "2026-02-05",
1245
+ placeholderTextColor: "#9CA3AF",
1246
+ style: styles.input
1247
+ })]
1248
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1249
+ style: styles.field,
1250
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1251
+ style: styles.label,
1252
+ children: "Status"
1253
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1254
+ style: styles.row,
1255
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1256
+ style: [styles.chip, status === 'publish' ? styles.chipActive : null],
1257
+ onPress: () => setStatus('publish'),
1258
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1259
+ style: [styles.chipText, status === 'publish' ? styles.chipTextActive : null],
1260
+ children: "Publish"
1261
+ })
1262
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1263
+ style: [styles.chip, status === 'unpublish' ? styles.chipActive : null],
1264
+ onPress: () => setStatus('unpublish'),
1265
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1266
+ style: [styles.chipText, status === 'unpublish' ? styles.chipTextActive : null],
1267
+ children: "Unpublish"
1268
+ })
1269
+ })]
1270
+ })]
1271
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1272
+ style: styles.field,
1273
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1274
+ style: styles.label,
1275
+ children: "Attachment"
1276
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1277
+ style: styles.row,
1278
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1279
+ style: styles.secondaryBtn,
1280
+ onPress: () => {
1281
+ pickFiles().catch(() => {});
1282
+ },
1283
+ disabled: busy,
1284
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1285
+ style: styles.secondaryBtnText,
1286
+ children: "Gallery / File"
1287
+ })
1288
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1289
+ style: styles.secondaryBtn,
1290
+ onPress: () => {
1291
+ pickFromCamera().catch(() => {});
1292
+ },
1293
+ disabled: busy,
1294
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1295
+ style: styles.secondaryBtnText,
1296
+ children: "Camera"
1297
+ })
1298
+ })]
1299
+ }), fileList.length ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
1300
+ style: styles.fileSelectedText,
1301
+ children: ["Selected: ", fileList.length, " files"]
1302
+ }) : null, fileList.length ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1303
+ style: {
1304
+ gap: 8
1305
+ },
1306
+ children: fileList.map((p, idx) => {
1307
+ const name = fileBaseName(p);
1308
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1309
+ style: {
1310
+ flexDirection: 'row',
1311
+ alignItems: 'center',
1312
+ justifyContent: 'space-between',
1313
+ paddingVertical: 6,
1314
+ borderBottomWidth: 1,
1315
+ borderBottomColor: '#E5E7EB',
1316
+ gap: 10
1317
+ },
1318
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1319
+ style: {
1320
+ flex: 1,
1321
+ color: '#111827'
1322
+ },
1323
+ numberOfLines: 1,
1324
+ children: name
1325
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1326
+ style: {
1327
+ flexDirection: 'row',
1328
+ alignItems: 'center',
1329
+ gap: 10
1330
+ },
1331
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
1332
+ onPress: () => viewAttachment(p).catch(() => {}),
1333
+ style: {
1334
+ flexDirection: 'row',
1335
+ alignItems: 'center',
1336
+ gap: 6
1337
+ },
1338
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1339
+ style: {
1340
+ width: 22,
1341
+ height: 22,
1342
+ borderRadius: 11,
1343
+ backgroundColor: '#DBEAFE',
1344
+ alignItems: 'center',
1345
+ justifyContent: 'center'
1346
+ },
1347
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1348
+ style: {
1349
+ color: '#1D4ED8',
1350
+ fontWeight: '800'
1351
+ },
1352
+ children: "V"
1353
+ })
1354
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1355
+ style: {
1356
+ color: '#1D4ED8',
1357
+ fontWeight: '700'
1358
+ },
1359
+ children: "View"
1360
+ })]
1361
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
1362
+ onPress: () => {
1363
+ setFileList(prev => {
1364
+ const next = prev.filter((_, i) => i !== idx);
1365
+ setActiveFile(cur => String(cur) === String(p) ? next[0] ?? '' : cur);
1366
+ return next;
1367
+ });
1368
+ },
1369
+ style: {
1370
+ flexDirection: 'row',
1371
+ alignItems: 'center',
1372
+ gap: 6
1373
+ },
1374
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1375
+ style: {
1376
+ width: 22,
1377
+ height: 22,
1378
+ borderRadius: 11,
1379
+ backgroundColor: '#FEE2E2',
1380
+ alignItems: 'center',
1381
+ justifyContent: 'center'
1382
+ },
1383
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1384
+ style: {
1385
+ color: '#B91C1C',
1386
+ fontWeight: '800'
1387
+ },
1388
+ children: "\xD7"
1389
+ })
1390
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1391
+ style: {
1392
+ color: '#B91C1C',
1393
+ fontWeight: '700'
1394
+ },
1395
+ children: "Remove"
1396
+ })]
1397
+ })]
1398
+ })]
1399
+ }, `${p}-${idx}`);
1400
+ })
1401
+ }) : null, (() => {
1402
+ const currentFile = activeFile || fileList[0] || '';
1403
+ const currentUrl = currentFile ? resolveUploadUrl(downloadsBaseUrl, currentFile) : '';
1404
+ const isImage = currentUrl ? isImageUrl(currentUrl) || isImageUrl(currentFile) : false;
1405
+ return currentUrl ? isImage ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1406
+ style: styles.attachmentPreview,
1407
+ onPress: () => viewAttachment(currentFile).catch(() => {}),
1408
+ activeOpacity: 0.9,
1409
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, {
1410
+ source: {
1411
+ uri: currentUrl,
1412
+ headers: authToken || schoolCode ? {
1413
+ ...(authToken ? {
1414
+ Authorization: authToken
1415
+ } : {}),
1416
+ ...(schoolCode ? {
1417
+ school_code: schoolCode
1418
+ } : {})
1419
+ } : undefined
1420
+ },
1421
+ style: styles.attachmentPreviewImage,
1422
+ resizeMode: "contain"
1423
+ })
1424
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1425
+ style: styles.viewDocumentBtn,
1426
+ onPress: () => viewAttachment(currentFile).catch(() => {}),
1427
+ disabled: isDownloading,
1428
+ activeOpacity: 0.9,
1429
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1430
+ style: styles.viewDocumentBtnText,
1431
+ children: isDownloading ? 'Opening Document...' : 'View Document'
1432
+ })
1433
+ }) : null;
1434
+ })(), fileError ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1435
+ style: styles.errorText,
1436
+ children: fileError
1437
+ }) : null]
1438
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1439
+ onPress: () => {
1440
+ submit().catch(() => {});
1441
+ },
1442
+ style: styles.primaryBtn,
1443
+ disabled: !canSubmit,
1444
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1445
+ style: styles.primaryBtnText,
1446
+ children: busy ? 'Please wait...' : 'Submit'
1447
+ })
1448
+ }), !canPickSubject ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1449
+ style: styles.helperText,
1450
+ children: "Pass sessionId to AssignmentScreen to load subjects."
1451
+ }) : null]
1452
+ }), ImageView ? /*#__PURE__*/(0, _jsxRuntime.jsx)(ImageView, {
1453
+ images: viewerImages,
1454
+ imageIndex: viewerIndex,
1455
+ visible: viewerOpen,
1456
+ onRequestClose: () => setViewerOpen(false)
1457
+ }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
1458
+ visible: pdfVisible,
1459
+ animationType: "slide",
1460
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.SafeAreaView, {
1461
+ style: styles.pdfRoot,
1462
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1463
+ style: styles.pdfHeader,
1464
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1465
+ style: styles.pdfTitle,
1466
+ children: "PDF Viewer"
1467
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1468
+ onPress: () => {
1469
+ setPdfVisible(false);
1470
+ setPdfUri('');
1471
+ setPdfHeaders({});
1472
+ },
1473
+ style: styles.modalClose,
1474
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1475
+ style: styles.modalCloseText,
1476
+ children: "Close"
1477
+ })
1478
+ })]
1479
+ }), pdfLoading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_LoadingState.LoadingState, {}) : Pdf && pdfUri ? /*#__PURE__*/(0, _jsxRuntime.jsx)(Pdf, {
1480
+ source: {
1481
+ uri: pdfUri,
1482
+ cache: true,
1483
+ headers: pdfHeaders
1484
+ },
1485
+ style: styles.pdfViewer,
1486
+ onError: error => {
1487
+ _reactNative.Alert.alert('Error', error?.message ?? 'Could not open PDF.');
1488
+ }
1489
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_EmptyState.EmptyState, {
1490
+ title: "PDF Viewer Missing"
1491
+ })]
1492
+ })
1493
+ })]
1494
+ });
1495
+ }
1496
+ function EditAssignmentView({
1497
+ api,
1498
+ sessionId,
1499
+ classes,
1500
+ downloadsBaseUrl,
1501
+ initial,
1502
+ onDone,
1503
+ onCancel
1504
+ }) {
1505
+ const {
1506
+ authToken,
1507
+ getAuthToken,
1508
+ schoolCode
1509
+ } = (0, _useERP.useERP)();
1510
+ const ImageView = (0, _react.useMemo)(() => tryGetImageViewer(), []);
1511
+ const Pdf = (0, _react.useMemo)(() => tryGetPdf(), []);
1512
+ const Calendar = (0, _react.useMemo)(() => tryGetCalendar(), []);
1513
+ const RNFS = (0, _react.useMemo)(() => tryGetRNFS(), []);
1514
+ const FileViewer = (0, _react.useMemo)(() => tryGetFileViewer(), []);
1515
+ const [viewerOpen, setViewerOpen] = (0, _react.useState)(false);
1516
+ const [viewerIndex, setViewerIndex] = (0, _react.useState)(0);
1517
+ const [viewerImages, setViewerImages] = (0, _react.useState)([]);
1518
+ const [pdfVisible, setPdfVisible] = (0, _react.useState)(false);
1519
+ const [pdfUri, setPdfUri] = (0, _react.useState)('');
1520
+ const [pdfHeaders, setPdfHeaders] = (0, _react.useState)({});
1521
+ const [pdfLoading, setPdfLoading] = (0, _react.useState)(false);
1522
+ const [calendarOpen, setCalendarOpen] = (0, _react.useState)(false);
1523
+ const [dateMode, setDateMode] = (0, _react.useState)('start');
1524
+ const [isDownloading, setIsDownloading] = (0, _react.useState)(false);
1525
+ const homeworkId = detailValue(initial, ['id', 'homework_id', 'homeworkId']);
1526
+ const initialTitle = String(detailValue(initial, ['title', 'homework_title'], '') ?? '');
1527
+ const initialDescription = String(detailValue(initial, ['description', 'desc', 'homework_description'], '') ?? '');
1528
+ const initialClassId = detailValue(initial, ['class_id', 'classId', 'class']);
1529
+ const initialSectionId = detailValue(initial, ['section_id', 'sectionId', 'section']);
1530
+ const initialSubjectId = detailValue(initial, ['sub_id', 'subject_id', 'subjectId', 'subject']);
1531
+ const initialHomewDate = String(detailValue(initial, ['homew_date', 'homework_date', 'homeworkDate'], '') ?? '');
1532
+ const initialSubmissionDate = String(detailValue(initial, ['submission_date', 'submit_date', 'submissionDate'], '') ?? '');
1533
+ const initialStatus = initial?.status === 'unpublish' ? 'unpublish' : 'publish';
1534
+ const existingFile = String(detailValue(initial, ['document', 'file', 'attachment', 'document_path'], '') ?? '');
1535
+ const initialFiles = splitFileCsv(existingFile);
1536
+ const [busy, setBusy] = (0, _react.useState)(false);
1537
+ const [fileError, setFileError] = (0, _react.useState)(null);
1538
+ const [fileList, setFileList] = (0, _react.useState)(() => initialFiles);
1539
+ const [activeFile, setActiveFile] = (0, _react.useState)(() => initialFiles[0] ?? '');
1540
+ const [title, setTitle] = (0, _react.useState)(initialTitle);
1541
+ const [description, setDescription] = (0, _react.useState)(initialDescription);
1542
+ const [classOpt, setClassOpt] = (0, _react.useState)(null);
1543
+ const [sectionOpt, setSectionOpt] = (0, _react.useState)(null);
1544
+ const [subjectOpt, setSubjectOpt] = (0, _react.useState)(null);
1545
+ const [sections, setSections] = (0, _react.useState)([]);
1546
+ const [subjects, setSubjects] = (0, _react.useState)([]);
1547
+ const [homewDate, setHomewDate] = (0, _react.useState)(initialHomewDate);
1548
+ const [submissionDate, setSubmissionDate] = (0, _react.useState)(initialSubmissionDate);
1549
+ const [status, setStatus] = (0, _react.useState)(initialStatus);
1550
+ const [markedDates, setMarkedDates] = (0, _react.useState)(() => {
1551
+ return rangeMarkedDates(initialHomewDate, initialSubmissionDate);
1552
+ });
1553
+ (0, _react.useEffect)(() => {
1554
+ if (!classes.length) return;
1555
+ if (classOpt) return;
1556
+ const found = classes.find(it => String(it.value) === String(initialClassId));
1557
+ if (found) {
1558
+ setClassOpt(found);
1559
+ }
1560
+ }, [classOpt, classes, initialClassId]);
1561
+ (0, _react.useEffect)(() => {
1562
+ if (!classOpt?.value) {
1563
+ setSections([]);
1564
+ setSubjects([]);
1565
+ setSectionOpt(null);
1566
+ setSubjectOpt(null);
1567
+ return;
1568
+ }
1569
+ (0, _assignmentService.fetchTeacherSections)(api, {
1570
+ classId: classOpt.value
1571
+ }).then(res => {
1572
+ const opts = normalizeOptions(res);
1573
+ setSections(opts);
1574
+ const found = opts.find(it => String(it.value) === String(initialSectionId));
1575
+ if (found) {
1576
+ setSectionOpt(found);
1577
+ }
1578
+ }).catch(() => {});
1579
+ if (sessionId !== undefined) {
1580
+ (0, _assignmentService.fetchTeacherSubjectsByClass)(api, {
1581
+ classId: classOpt.value,
1582
+ sessionId
1583
+ }).then(res => {
1584
+ const opts = normalizeOptions(res);
1585
+ setSubjects(opts);
1586
+ const found = opts.find(it => String(it.value) === String(initialSubjectId));
1587
+ if (found) {
1588
+ setSubjectOpt(found);
1589
+ }
1590
+ }).catch(() => {});
1591
+ } else {
1592
+ setSubjects([]);
1593
+ setSubjectOpt(null);
1594
+ }
1595
+ }, [api, classOpt?.value, initialSectionId, initialSubjectId, sessionId]);
1596
+ const viewAttachment = (0, _react.useCallback)(async rawFile => {
1597
+ const file = String(rawFile ?? activeFile ?? fileList[0] ?? '');
1598
+ if (!file) {
1599
+ _reactNative.Alert.alert('No Attachment');
1600
+ return;
1601
+ }
1602
+ const url = resolveUploadUrl(downloadsBaseUrl, file);
1603
+ const headers = await resolveViewerHeaders({
1604
+ authToken,
1605
+ getAuthToken,
1606
+ schoolCode
1607
+ });
1608
+ const isImage = isImageUrl(url) || isImageUrl(file);
1609
+ if (ImageView && isImage) {
1610
+ const imageFiles = fileList.filter(p => {
1611
+ const u = resolveUploadUrl(downloadsBaseUrl, p);
1612
+ return isImageUrl(u) || isImageUrl(p);
1613
+ });
1614
+ const imageUrls = imageFiles.map(p => resolveUploadUrl(downloadsBaseUrl, p));
1615
+ const idx = imageFiles.findIndex(p => String(p) === String(file));
1616
+ setViewerImages(imageUrls.map(uri => ({
1617
+ uri,
1618
+ headers: Object.keys(headers).length ? headers : undefined
1619
+ })));
1620
+ setViewerIndex(Math.max(0, idx));
1621
+ setViewerOpen(true);
1622
+ return;
1623
+ }
1624
+ const isPdf = url.toLowerCase().endsWith('.pdf') || String(file).toLowerCase().endsWith('.pdf');
1625
+ if (Pdf && isPdf) {
1626
+ if (!RNFS) {
1627
+ setPdfUri(url);
1628
+ setPdfHeaders(headers);
1629
+ setPdfVisible(true);
1630
+ return;
1631
+ }
1632
+ setIsDownloading(true);
1633
+ setPdfLoading(true);
1634
+ try {
1635
+ const localPdf = await downloadPdfToLocal({
1636
+ RNFS,
1637
+ url,
1638
+ fileName: fileBaseName(file || 'document.pdf'),
1639
+ headers
1640
+ });
1641
+ setPdfHeaders({});
1642
+ setPdfUri(localPdf);
1643
+ setPdfVisible(true);
1644
+ } catch (error) {
1645
+ _reactNative.Alert.alert('Error', error?.message ?? 'Could not open PDF.');
1646
+ } finally {
1647
+ setPdfLoading(false);
1648
+ setIsDownloading(false);
1649
+ }
1650
+ return;
1651
+ }
1652
+ if (!RNFS || !FileViewer) {
1653
+ _reactNative.Alert.alert('Viewer Missing', 'Install react-native-fs and react-native-file-viewer to open documents inside the app.');
1654
+ return;
1655
+ }
1656
+ setIsDownloading(true);
1657
+ try {
1658
+ let fileName = fileBaseName(file) || 'document';
1659
+ const hasExtension = fileName.includes('.') && (fileName.split('.').pop()?.length ?? 0) > 0;
1660
+ if (!hasExtension && url.startsWith('http')) {
1661
+ try {
1662
+ const response = await fetch(url, {
1663
+ method: 'HEAD'
1664
+ });
1665
+ const contentType = response.headers.get('content-type') ?? '';
1666
+ if (contentType.includes('pdf')) fileName += '.pdf';else if (contentType.includes('word') || contentType.includes('doc')) fileName += '.docx';else if (contentType.includes('sheet') || contentType.includes('excel')) fileName += '.xlsx';else if (contentType.includes('jpeg') || contentType.includes('jpg')) fileName += '.jpg';else if (contentType.includes('png')) fileName += '.png';else fileName += '.pdf';
1667
+ } catch {
1668
+ fileName += '.pdf';
1669
+ }
1670
+ }
1671
+ const localFile = `${RNFS.DocumentDirectoryPath}/${fileName}`;
1672
+ await RNFS.downloadFile({
1673
+ fromUrl: url,
1674
+ toFile: localFile,
1675
+ headers
1676
+ }).promise;
1677
+ await FileViewer.open(localFile, {
1678
+ showOpenWithDialog: true
1679
+ });
1680
+ } catch {
1681
+ _reactNative.Alert.alert('Error', 'Could not view the file.');
1682
+ } finally {
1683
+ setIsDownloading(false);
1684
+ }
1685
+ }, [FileViewer, ImageView, Pdf, RNFS, activeFile, authToken, downloadsBaseUrl, fileList, getAuthToken, schoolCode]);
1686
+ const pickFromCamera = (0, _react.useCallback)(async () => {
1687
+ let picker = null;
1688
+ try {
1689
+ picker = require('react-native-image-picker');
1690
+ } catch {}
1691
+ if (!picker?.launchCamera) {
1692
+ _reactNative.Alert.alert('Error', 'Camera picker is not available');
1693
+ return;
1694
+ }
1695
+ const res = await new Promise(resolve => {
1696
+ picker.launchCamera({
1697
+ mediaType: 'photo',
1698
+ saveToPhotos: true,
1699
+ includeBase64: false
1700
+ }, r => resolve(r));
1701
+ });
1702
+ const asset = res?.assets?.[0];
1703
+ if (!asset?.uri) return;
1704
+ const file = {
1705
+ uri: asset.uri,
1706
+ name: asset.fileName ?? 'camera.jpg',
1707
+ type: asset.type ?? 'image/jpeg'
1708
+ };
1709
+ setFileError(null);
1710
+ setBusy(true);
1711
+ try {
1712
+ const uploaded = await (0, _assignmentService.uploadAssignmentFile)(api, file);
1713
+ if (uploaded?.Status === 'Success' && uploaded.data) {
1714
+ const nextPath = String(uploaded.data);
1715
+ setFileList(prev => [...prev, nextPath]);
1716
+ setActiveFile(prev => prev || nextPath);
1717
+ } else {
1718
+ setFileError('File upload failed');
1719
+ }
1720
+ } finally {
1721
+ setBusy(false);
1722
+ }
1723
+ }, [api]);
1724
+ const pickFiles = (0, _react.useCallback)(async () => {
1725
+ let dp = null;
1726
+ try {
1727
+ dp = require('react-native-document-picker');
1728
+ } catch {}
1729
+ if (!dp) {
1730
+ return;
1731
+ }
1732
+ const allowed = ['pdf', 'png', 'jpg', 'jpeg', 'webp'];
1733
+ const selected = await dp.pick({
1734
+ presentationStyle: 'fullScreen',
1735
+ allowMultiSelection: true,
1736
+ type: [dp.types.pdf, dp.types.images]
1737
+ });
1738
+ const picks = Array.isArray(selected) ? selected : [selected];
1739
+ const files = picks.map(it => ({
1740
+ uri: it.uri,
1741
+ name: it.name ?? 'file',
1742
+ type: it.type ?? 'application/octet-stream'
1743
+ })).filter(it => !!it.uri);
1744
+ const invalid = files.find(it => {
1745
+ const ext = String(it.name).split('.').pop()?.toLowerCase();
1746
+ return ext ? !allowed.includes(ext) : false;
1747
+ });
1748
+ if (invalid) {
1749
+ setFileError('Only PDF or image files are allowed');
1750
+ return;
1751
+ }
1752
+ setFileError(null);
1753
+ setBusy(true);
1754
+ try {
1755
+ const uploaded = [];
1756
+ for (const f of files) {
1757
+ const res = await (0, _assignmentService.uploadAssignmentFile)(api, f);
1758
+ if (res?.Status === 'Success' && res.data) {
1759
+ uploaded.push(String(res.data));
1760
+ }
1761
+ }
1762
+ if (!uploaded.length) {
1763
+ setFileError('File upload failed');
1764
+ return;
1765
+ }
1766
+ setFileList(prev => [...prev, ...uploaded]);
1767
+ setActiveFile(prev => prev ? prev : uploaded[0] ?? '');
1768
+ } finally {
1769
+ setBusy(false);
1770
+ }
1771
+ }, [api]);
1772
+ const submit = (0, _react.useCallback)(async () => {
1773
+ const fileToSend = fileList.length ? fileList.join(',') : undefined;
1774
+ const missing = [];
1775
+ if (!homeworkId) missing.push('homework id');
1776
+ if (!title) missing.push('title');
1777
+ if (!description) missing.push('description');
1778
+ if (!classOpt?.value) missing.push('class');
1779
+ if (!sectionOpt?.value) missing.push('section');
1780
+ if (!subjectOpt?.value) missing.push('subject');
1781
+ if (!homewDate) missing.push('homework date');
1782
+ if (!submissionDate) missing.push('submission date');
1783
+ if (!fileToSend) missing.push('file');
1784
+ if (missing.length) {
1785
+ if (!fileToSend) {
1786
+ setFileError('File is required');
1787
+ }
1788
+ _reactNative.Alert.alert('Missing Fields', `Please fill: ${missing.join(', ')}`);
1789
+ return;
1790
+ }
1791
+ setBusy(true);
1792
+ try {
1793
+ const res = await (0, _assignmentService.updateAssignment)(api, {
1794
+ id: homeworkId,
1795
+ title,
1796
+ description,
1797
+ class_id: classOpt.value,
1798
+ section_id: sectionOpt.value,
1799
+ sub_id: subjectOpt.value,
1800
+ homew_date: homewDate,
1801
+ submission_date: submissionDate,
1802
+ status,
1803
+ file: fileToSend
1804
+ });
1805
+ const response = res;
1806
+ if (response?.Status && response.Status !== 'Success') {
1807
+ _reactNative.Alert.alert('Update Failed', String(response?.msg ?? response?.Status ?? 'Request failed'));
1808
+ return;
1809
+ }
1810
+ _reactNative.Alert.alert('Success', String(response?.msg ?? 'Assignment updated successfully'));
1811
+ onDone();
1812
+ } catch (error) {
1813
+ _reactNative.Alert.alert('Update Failed', String(error?.message ?? 'Could not update assignment'));
1814
+ } finally {
1815
+ setBusy(false);
1816
+ }
1817
+ }, [api, classOpt?.value, description, fileList, homewDate, homeworkId, onDone, sectionOpt?.value, status, subjectOpt?.value, submissionDate, title]);
1818
+ const canPickSubject = sessionId !== undefined;
1819
+ const canSubmit = canPickSubject && fileList.length > 0 && !busy;
1820
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.SafeAreaView, {
1821
+ style: styles.root,
1822
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1823
+ style: styles.header,
1824
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1825
+ style: styles.headerTitle,
1826
+ children: "Edit Assignment"
1827
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1828
+ onPress: onCancel,
1829
+ style: styles.secondaryBtn,
1830
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1831
+ style: styles.secondaryBtnText,
1832
+ children: "Back"
1833
+ })
1834
+ })]
1835
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
1836
+ contentContainerStyle: styles.form,
1837
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1838
+ style: styles.field,
1839
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1840
+ style: styles.label,
1841
+ children: "Title"
1842
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
1843
+ value: title,
1844
+ onChangeText: setTitle,
1845
+ placeholder: "Enter title",
1846
+ placeholderTextColor: "#9CA3AF",
1847
+ style: styles.input
1848
+ })]
1849
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1850
+ style: styles.field,
1851
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1852
+ style: styles.label,
1853
+ children: "Description"
1854
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
1855
+ value: description,
1856
+ onChangeText: setDescription,
1857
+ placeholder: "Enter description",
1858
+ placeholderTextColor: "#9CA3AF",
1859
+ style: [styles.input, styles.textarea],
1860
+ multiline: true
1861
+ })]
1862
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SelectModal, {
1863
+ label: "Class",
1864
+ value: classOpt,
1865
+ options: classes,
1866
+ placeholder: "Select class",
1867
+ onChange: setClassOpt
1868
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SelectModal, {
1869
+ label: "Section",
1870
+ value: sectionOpt,
1871
+ options: sections,
1872
+ placeholder: "Select section",
1873
+ onChange: setSectionOpt
1874
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SelectModal, {
1875
+ label: "Subject",
1876
+ value: subjectOpt,
1877
+ options: subjects,
1878
+ placeholder: canPickSubject ? 'Select subject' : 'Provide sessionId',
1879
+ onChange: setSubjectOpt
1880
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1881
+ style: styles.field,
1882
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1883
+ style: styles.label,
1884
+ children: "Homework Date"
1885
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1886
+ style: styles.secondaryBtn,
1887
+ onPress: () => {
1888
+ setDateMode('start');
1889
+ setCalendarOpen(true);
1890
+ },
1891
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1892
+ style: styles.secondaryBtnText,
1893
+ children: homewDate || 'Select start date'
1894
+ })
1895
+ })]
1896
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1897
+ style: styles.field,
1898
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1899
+ style: styles.label,
1900
+ children: "Submission Date"
1901
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1902
+ style: styles.secondaryBtn,
1903
+ onPress: () => {
1904
+ setDateMode('end');
1905
+ setCalendarOpen(true);
1906
+ },
1907
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1908
+ style: styles.secondaryBtnText,
1909
+ children: submissionDate || 'Select end date'
1910
+ })
1911
+ })]
1912
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1913
+ style: styles.field,
1914
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1915
+ style: styles.label,
1916
+ children: "Status"
1917
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1918
+ style: styles.row,
1919
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1920
+ style: [styles.chip, status === 'publish' ? styles.chipActive : null],
1921
+ onPress: () => setStatus('publish'),
1922
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1923
+ style: [styles.chipText, status === 'publish' ? styles.chipTextActive : null],
1924
+ children: "Publish"
1925
+ })
1926
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1927
+ style: [styles.chip, status === 'unpublish' ? styles.chipActive : null],
1928
+ onPress: () => setStatus('unpublish'),
1929
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1930
+ style: [styles.chipText, status === 'unpublish' ? styles.chipTextActive : null],
1931
+ children: "Unpublish"
1932
+ })
1933
+ })]
1934
+ })]
1935
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1936
+ style: styles.field,
1937
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1938
+ style: styles.label,
1939
+ children: "Attachment"
1940
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1941
+ style: styles.row,
1942
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1943
+ style: styles.secondaryBtn,
1944
+ onPress: () => {
1945
+ pickFiles().catch(() => {});
1946
+ },
1947
+ disabled: busy,
1948
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1949
+ style: styles.secondaryBtnText,
1950
+ children: "Add More Files"
1951
+ })
1952
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1953
+ style: styles.secondaryBtn,
1954
+ onPress: () => {
1955
+ pickFromCamera().catch(() => {});
1956
+ },
1957
+ disabled: busy,
1958
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1959
+ style: styles.secondaryBtnText,
1960
+ children: "Camera"
1961
+ })
1962
+ })]
1963
+ }), fileList.length ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
1964
+ style: styles.fileSelectedText,
1965
+ children: ["Selected: ", fileList.length, " files"]
1966
+ }) : null, fileList.length ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1967
+ style: {
1968
+ gap: 8
1969
+ },
1970
+ children: fileList.map((p, idx) => {
1971
+ const name = fileBaseName(p);
1972
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1973
+ style: {
1974
+ flexDirection: 'row',
1975
+ alignItems: 'center',
1976
+ justifyContent: 'space-between',
1977
+ paddingVertical: 6,
1978
+ borderBottomWidth: 1,
1979
+ borderBottomColor: '#E5E7EB',
1980
+ gap: 10
1981
+ },
1982
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1983
+ style: {
1984
+ flex: 1,
1985
+ color: '#111827'
1986
+ },
1987
+ numberOfLines: 1,
1988
+ children: name
1989
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1990
+ style: {
1991
+ flexDirection: 'row',
1992
+ alignItems: 'center',
1993
+ gap: 10
1994
+ },
1995
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
1996
+ onPress: () => viewAttachment(p).catch(() => {}),
1997
+ style: {
1998
+ flexDirection: 'row',
1999
+ alignItems: 'center',
2000
+ gap: 6
2001
+ },
2002
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
2003
+ style: {
2004
+ width: 22,
2005
+ height: 22,
2006
+ borderRadius: 11,
2007
+ backgroundColor: '#DBEAFE',
2008
+ alignItems: 'center',
2009
+ justifyContent: 'center'
2010
+ },
2011
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
2012
+ style: {
2013
+ color: '#1D4ED8',
2014
+ fontWeight: '800'
2015
+ },
2016
+ children: "V"
2017
+ })
2018
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
2019
+ style: {
2020
+ color: '#1D4ED8',
2021
+ fontWeight: '700'
2022
+ },
2023
+ children: "View"
2024
+ })]
2025
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
2026
+ onPress: () => {
2027
+ setFileList(prev => {
2028
+ const next = prev.filter((_, i) => i !== idx);
2029
+ setActiveFile(cur => String(cur) === String(p) ? next[0] ?? '' : cur);
2030
+ return next;
2031
+ });
2032
+ },
2033
+ style: {
2034
+ flexDirection: 'row',
2035
+ alignItems: 'center',
2036
+ gap: 6
2037
+ },
2038
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
2039
+ style: {
2040
+ width: 22,
2041
+ height: 22,
2042
+ borderRadius: 11,
2043
+ backgroundColor: '#FEE2E2',
2044
+ alignItems: 'center',
2045
+ justifyContent: 'center'
2046
+ },
2047
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
2048
+ style: {
2049
+ color: '#B91C1C',
2050
+ fontWeight: '800'
2051
+ },
2052
+ children: "\xD7"
2053
+ })
2054
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
2055
+ style: {
2056
+ color: '#B91C1C',
2057
+ fontWeight: '700'
2058
+ },
2059
+ children: "Remove"
2060
+ })]
2061
+ })]
2062
+ })]
2063
+ }, `${p}-${idx}`);
2064
+ })
2065
+ }) : null, (() => {
2066
+ const currentFile = activeFile || fileList[0] || '';
2067
+ const currentUrl = currentFile ? resolveUploadUrl(downloadsBaseUrl, currentFile) : '';
2068
+ const isImage = currentUrl ? isImageUrl(currentUrl) || isImageUrl(currentFile) : false;
2069
+ return currentUrl ? isImage ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
2070
+ style: styles.attachmentPreview,
2071
+ onPress: () => viewAttachment(currentFile).catch(() => {}),
2072
+ activeOpacity: 0.9,
2073
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, {
2074
+ source: {
2075
+ uri: currentUrl,
2076
+ headers: authToken || schoolCode ? {
2077
+ ...(authToken ? {
2078
+ Authorization: authToken
2079
+ } : {}),
2080
+ ...(schoolCode ? {
2081
+ school_code: schoolCode
2082
+ } : {})
2083
+ } : undefined
2084
+ },
2085
+ style: styles.attachmentPreviewImage,
2086
+ resizeMode: "contain"
2087
+ })
2088
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
2089
+ style: styles.viewDocumentBtn,
2090
+ onPress: () => viewAttachment(currentFile).catch(() => {}),
2091
+ disabled: isDownloading,
2092
+ activeOpacity: 0.9,
2093
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
2094
+ style: styles.viewDocumentBtnText,
2095
+ children: isDownloading ? 'Opening Document...' : 'View Document'
2096
+ })
2097
+ }) : null;
2098
+ })(), fileError ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
2099
+ style: styles.errorText,
2100
+ children: fileError
2101
+ }) : null]
2102
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
2103
+ onPress: () => {
2104
+ submit().catch(() => {});
2105
+ },
2106
+ style: styles.primaryBtn,
2107
+ disabled: !canSubmit,
2108
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
2109
+ style: styles.primaryBtnText,
2110
+ children: busy ? 'Please wait...' : 'Update'
2111
+ })
2112
+ }), !canPickSubject ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
2113
+ style: styles.helperText,
2114
+ children: "Pass sessionId to AssignmentScreen to load subjects."
2115
+ }) : null]
2116
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
2117
+ visible: calendarOpen,
2118
+ transparent: true,
2119
+ animationType: "fade",
2120
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
2121
+ style: styles.modalOverlay,
2122
+ onPress: () => setCalendarOpen(false),
2123
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
2124
+ style: styles.sheetCard,
2125
+ onPress: () => {},
2126
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
2127
+ style: styles.modalHeader,
2128
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
2129
+ style: styles.modalHeaderTitle,
2130
+ children: dateMode === 'start' ? 'Select start date' : 'Select end date'
2131
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
2132
+ onPress: () => setCalendarOpen(false),
2133
+ style: styles.modalClose,
2134
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
2135
+ style: styles.modalCloseText,
2136
+ children: "Close"
2137
+ })
2138
+ })]
2139
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
2140
+ style: {
2141
+ flex: 1
2142
+ },
2143
+ children: Calendar ? /*#__PURE__*/(0, _jsxRuntime.jsx)(Calendar, {
2144
+ markingType: "period",
2145
+ markedDates: markedDates,
2146
+ onDayPress: day => {
2147
+ const d = String(day?.dateString ?? '');
2148
+ if (!d) return;
2149
+ if (dateMode === 'start') {
2150
+ setHomewDate(d);
2151
+ const next = rangeMarkedDates(d, submissionDate || d);
2152
+ setMarkedDates(next);
2153
+ } else {
2154
+ setSubmissionDate(d);
2155
+ const next = rangeMarkedDates(homewDate || d, d);
2156
+ setMarkedDates(next);
2157
+ }
2158
+ setCalendarOpen(false);
2159
+ }
2160
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_EmptyState.EmptyState, {
2161
+ title: "Calendar missing",
2162
+ message: "Install react-native-calendars to enable date picker"
2163
+ })
2164
+ })]
2165
+ })
2166
+ })
2167
+ }), ImageView ? /*#__PURE__*/(0, _jsxRuntime.jsx)(ImageView, {
2168
+ images: viewerImages,
2169
+ imageIndex: viewerIndex,
2170
+ visible: viewerOpen,
2171
+ onRequestClose: () => setViewerOpen(false)
2172
+ }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
2173
+ visible: pdfVisible,
2174
+ animationType: "slide",
2175
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.SafeAreaView, {
2176
+ style: styles.pdfRoot,
2177
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
2178
+ style: styles.pdfHeader,
2179
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
2180
+ style: styles.pdfTitle,
2181
+ children: "PDF Viewer"
2182
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
2183
+ onPress: () => {
2184
+ setPdfVisible(false);
2185
+ setPdfUri('');
2186
+ setPdfHeaders({});
2187
+ },
2188
+ style: styles.modalClose,
2189
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
2190
+ style: styles.modalCloseText,
2191
+ children: "Close"
2192
+ })
2193
+ })]
2194
+ }), pdfLoading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_LoadingState.LoadingState, {}) : Pdf && pdfUri ? /*#__PURE__*/(0, _jsxRuntime.jsx)(Pdf, {
2195
+ source: {
2196
+ uri: pdfUri,
2197
+ cache: true,
2198
+ headers: pdfHeaders
2199
+ },
2200
+ style: styles.pdfViewer,
2201
+ onError: error => {
2202
+ _reactNative.Alert.alert('Error', error?.message ?? 'Could not open PDF.');
2203
+ }
2204
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_EmptyState.EmptyState, {
2205
+ title: "PDF Viewer Missing"
2206
+ })]
2207
+ })
2208
+ })]
2209
+ });
2210
+ }
2211
+ const styles = _reactNative.StyleSheet.create({
2212
+ root: {
2213
+ flex: 1,
2214
+ backgroundColor: '#FFFFFF'
2215
+ },
2216
+ header: {
2217
+ paddingHorizontal: 16,
2218
+ paddingVertical: 12,
2219
+ borderBottomWidth: 1,
2220
+ borderBottomColor: '#E5E7EB',
2221
+ flexDirection: 'row',
2222
+ justifyContent: 'space-between',
2223
+ alignItems: 'center'
2224
+ },
2225
+ headerTitle: {
2226
+ fontSize: 18,
2227
+ fontWeight: '600',
2228
+ color: '#111827'
2229
+ },
2230
+ filters: {
2231
+ paddingHorizontal: 16,
2232
+ paddingTop: 12,
2233
+ paddingBottom: 6,
2234
+ gap: 10
2235
+ },
2236
+ list: {
2237
+ padding: 16,
2238
+ gap: 12
2239
+ },
2240
+ card: {
2241
+ borderWidth: 1,
2242
+ borderColor: '#E5E7EB',
2243
+ borderRadius: 12,
2244
+ padding: 12,
2245
+ backgroundColor: '#FFFFFF'
2246
+ },
2247
+ cardTop: {
2248
+ flexDirection: 'row',
2249
+ alignItems: 'center',
2250
+ justifyContent: 'space-between',
2251
+ gap: 10
2252
+ },
2253
+ cardActions: {
2254
+ flexDirection: 'row',
2255
+ alignItems: 'center',
2256
+ gap: 10
2257
+ },
2258
+ editBtn: {
2259
+ paddingHorizontal: 10,
2260
+ paddingVertical: 6,
2261
+ borderRadius: 999,
2262
+ borderWidth: 1,
2263
+ borderColor: '#E5E7EB',
2264
+ backgroundColor: '#F3F4F6'
2265
+ },
2266
+ editBtnText: {
2267
+ fontSize: 12,
2268
+ fontWeight: '700',
2269
+ color: '#111827'
2270
+ },
2271
+ cardTitle: {
2272
+ fontSize: 16,
2273
+ fontWeight: '600',
2274
+ color: '#111827'
2275
+ },
2276
+ cardSub: {
2277
+ marginTop: 6,
2278
+ fontSize: 13,
2279
+ color: '#374151'
2280
+ },
2281
+ cardMeta: {
2282
+ marginTop: 6,
2283
+ fontSize: 12,
2284
+ color: '#6B7280'
2285
+ },
2286
+ viewBtn: {
2287
+ paddingHorizontal: 10,
2288
+ paddingVertical: 6,
2289
+ borderRadius: 999,
2290
+ borderWidth: 1,
2291
+ alignItems: 'center',
2292
+ justifyContent: 'center'
2293
+ },
2294
+ viewBtnActive: {
2295
+ backgroundColor: '#E8F5E9',
2296
+ borderColor: '#4CAF50'
2297
+ },
2298
+ viewBtnDisabled: {
2299
+ backgroundColor: '#F5F5F5',
2300
+ borderColor: '#E0E0E0'
2301
+ },
2302
+ viewBtnText: {
2303
+ fontSize: 12,
2304
+ fontWeight: '700'
2305
+ },
2306
+ viewBtnTextActive: {
2307
+ color: '#2E7D32'
2308
+ },
2309
+ viewBtnTextDisabled: {
2310
+ color: '#757575'
2311
+ },
2312
+ primaryBtn: {
2313
+ paddingHorizontal: 14,
2314
+ paddingVertical: 10,
2315
+ backgroundColor: '#111827',
2316
+ borderRadius: 10,
2317
+ alignItems: 'center',
2318
+ justifyContent: 'center'
2319
+ },
2320
+ primaryBtnText: {
2321
+ color: '#FFFFFF',
2322
+ fontWeight: '600'
2323
+ },
2324
+ secondaryBtn: {
2325
+ paddingHorizontal: 14,
2326
+ paddingVertical: 10,
2327
+ backgroundColor: '#F3F4F6',
2328
+ borderRadius: 10,
2329
+ alignItems: 'center',
2330
+ justifyContent: 'center'
2331
+ },
2332
+ secondaryBtnTextSmall: {
2333
+ color: '#111827',
2334
+ fontWeight: '600',
2335
+ fontSize: 12
2336
+ },
2337
+ secondaryBtnText: {
2338
+ color: '#111827',
2339
+ fontWeight: '600'
2340
+ },
2341
+ form: {
2342
+ padding: 16,
2343
+ gap: 12
2344
+ },
2345
+ field: {
2346
+ gap: 6
2347
+ },
2348
+ label: {
2349
+ fontSize: 12,
2350
+ color: '#6B7280'
2351
+ },
2352
+ input: {
2353
+ borderWidth: 1,
2354
+ borderColor: '#E5E7EB',
2355
+ borderRadius: 10,
2356
+ paddingHorizontal: 12,
2357
+ paddingVertical: 10,
2358
+ color: '#111827'
2359
+ },
2360
+ textarea: {
2361
+ minHeight: 90,
2362
+ textAlignVertical: 'top'
2363
+ },
2364
+ select: {
2365
+ borderWidth: 1,
2366
+ borderColor: '#E5E7EB',
2367
+ borderRadius: 10,
2368
+ paddingHorizontal: 12,
2369
+ paddingVertical: 12,
2370
+ backgroundColor: '#FFFFFF'
2371
+ },
2372
+ selectText: {
2373
+ color: '#111827'
2374
+ },
2375
+ modalBackdrop: {
2376
+ flex: 1,
2377
+ backgroundColor: 'rgba(0,0,0,0.35)',
2378
+ justifyContent: 'center',
2379
+ padding: 16
2380
+ },
2381
+ modalCard: {
2382
+ backgroundColor: '#FFFFFF',
2383
+ borderRadius: 12,
2384
+ maxHeight: '70%',
2385
+ paddingVertical: 12
2386
+ },
2387
+ sheetCard: {
2388
+ width: '92%',
2389
+ height: '85%',
2390
+ borderRadius: 18,
2391
+ backgroundColor: '#FFFFFF',
2392
+ overflow: 'hidden'
2393
+ },
2394
+ pdfRoot: {
2395
+ flex: 1,
2396
+ backgroundColor: '#FFFFFF'
2397
+ },
2398
+ pdfHeader: {
2399
+ paddingHorizontal: 16,
2400
+ paddingVertical: 12,
2401
+ borderBottomWidth: 1,
2402
+ borderBottomColor: '#E5E7EB',
2403
+ flexDirection: 'row',
2404
+ justifyContent: 'space-between',
2405
+ alignItems: 'center'
2406
+ },
2407
+ pdfTitle: {
2408
+ fontSize: 16,
2409
+ fontWeight: '700',
2410
+ color: '#111827'
2411
+ },
2412
+ pdfViewer: {
2413
+ flex: 1,
2414
+ width: '100%',
2415
+ backgroundColor: '#F3F4F6'
2416
+ },
2417
+ modalTitle: {
2418
+ fontSize: 16,
2419
+ fontWeight: '600',
2420
+ color: '#111827',
2421
+ paddingHorizontal: 16,
2422
+ paddingBottom: 12
2423
+ },
2424
+ optionRow: {
2425
+ paddingHorizontal: 16,
2426
+ paddingVertical: 12
2427
+ },
2428
+ optionText: {
2429
+ fontSize: 14,
2430
+ color: '#111827'
2431
+ },
2432
+ optionEmpty: {
2433
+ paddingHorizontal: 16,
2434
+ paddingVertical: 12,
2435
+ color: '#6B7280'
2436
+ },
2437
+ modalOverlay: {
2438
+ flex: 1,
2439
+ backgroundColor: 'rgba(0,0,0,0.45)',
2440
+ alignItems: 'center',
2441
+ justifyContent: 'center',
2442
+ padding: 16
2443
+ },
2444
+ modalHeader: {
2445
+ paddingHorizontal: 16,
2446
+ paddingVertical: 12,
2447
+ borderBottomWidth: 1,
2448
+ borderBottomColor: '#E5E7EB',
2449
+ flexDirection: 'row',
2450
+ justifyContent: 'space-between',
2451
+ alignItems: 'center'
2452
+ },
2453
+ modalHeaderTitle: {
2454
+ fontSize: 16,
2455
+ fontWeight: '700',
2456
+ color: '#111827'
2457
+ },
2458
+ modalClose: {
2459
+ paddingHorizontal: 10,
2460
+ paddingVertical: 6,
2461
+ borderRadius: 10,
2462
+ backgroundColor: '#F3F4F6'
2463
+ },
2464
+ modalCloseText: {
2465
+ fontSize: 12,
2466
+ fontWeight: '700',
2467
+ color: '#111827'
2468
+ },
2469
+ submissionList: {
2470
+ padding: 16,
2471
+ gap: 12
2472
+ },
2473
+ submissionCard: {
2474
+ borderWidth: 1,
2475
+ borderColor: '#E5E7EB',
2476
+ borderRadius: 12,
2477
+ padding: 12,
2478
+ backgroundColor: '#FFFFFF'
2479
+ },
2480
+ submissionHeaderRow: {
2481
+ flexDirection: 'row',
2482
+ alignItems: 'center',
2483
+ justifyContent: 'space-between',
2484
+ gap: 10
2485
+ },
2486
+ submissionInputRow: {
2487
+ marginTop: 10,
2488
+ flexDirection: 'row',
2489
+ alignItems: 'flex-start',
2490
+ gap: 10
2491
+ },
2492
+ smallInput: {
2493
+ borderWidth: 1,
2494
+ borderColor: '#E5E7EB',
2495
+ borderRadius: 10,
2496
+ paddingHorizontal: 10,
2497
+ paddingVertical: 8,
2498
+ color: '#111827'
2499
+ },
2500
+ uploadBtn: {
2501
+ marginTop: 10,
2502
+ alignSelf: 'stretch',
2503
+ paddingHorizontal: 12,
2504
+ paddingVertical: 10,
2505
+ borderRadius: 10,
2506
+ backgroundColor: '#111827',
2507
+ alignItems: 'center'
2508
+ },
2509
+ uploadBtnText: {
2510
+ color: '#FFFFFF',
2511
+ fontWeight: '700',
2512
+ fontSize: 12
2513
+ },
2514
+ submissionFooter: {
2515
+ padding: 12,
2516
+ borderTopWidth: 1,
2517
+ borderTopColor: '#E5E7EB',
2518
+ backgroundColor: '#FFFFFF'
2519
+ },
2520
+ giveMarksBtn: {
2521
+ paddingHorizontal: 14,
2522
+ paddingVertical: 12,
2523
+ borderRadius: 12,
2524
+ backgroundColor: '#DC2626',
2525
+ alignItems: 'center',
2526
+ justifyContent: 'center'
2527
+ },
2528
+ giveMarksBtnText: {
2529
+ color: '#FFFFFF',
2530
+ fontWeight: '800'
2531
+ },
2532
+ submissionName: {
2533
+ fontSize: 14,
2534
+ fontWeight: '800',
2535
+ color: '#111827'
2536
+ },
2537
+ submissionMeta: {
2538
+ marginTop: 6,
2539
+ fontSize: 12,
2540
+ color: '#6B7280'
2541
+ },
2542
+ row: {
2543
+ flexDirection: 'row',
2544
+ alignItems: 'center',
2545
+ gap: 10
2546
+ },
2547
+ chip: {
2548
+ borderWidth: 1,
2549
+ borderColor: '#E5E7EB',
2550
+ borderRadius: 999,
2551
+ paddingHorizontal: 12,
2552
+ paddingVertical: 8
2553
+ },
2554
+ chipActive: {
2555
+ backgroundColor: '#111827',
2556
+ borderColor: '#111827'
2557
+ },
2558
+ chipText: {
2559
+ fontSize: 12,
2560
+ color: '#111827',
2561
+ fontWeight: '600'
2562
+ },
2563
+ chipTextActive: {
2564
+ color: '#FFFFFF'
2565
+ },
2566
+ fileText: {
2567
+ flex: 1,
2568
+ fontSize: 12,
2569
+ color: '#6B7280'
2570
+ },
2571
+ fileSelectedText: {
2572
+ marginTop: 10,
2573
+ fontSize: 12,
2574
+ color: '#6B7280'
2575
+ },
2576
+ attachmentPreview: {
2577
+ marginTop: 10,
2578
+ borderWidth: 1,
2579
+ borderColor: '#E5E7EB',
2580
+ borderRadius: 12,
2581
+ backgroundColor: '#F9FAFB',
2582
+ overflow: 'hidden',
2583
+ height: 160
2584
+ },
2585
+ attachmentPreviewImage: {
2586
+ width: '100%',
2587
+ height: '100%'
2588
+ },
2589
+ viewDocumentBtn: {
2590
+ marginTop: 10,
2591
+ paddingVertical: 12,
2592
+ borderRadius: 12,
2593
+ borderWidth: 1,
2594
+ borderStyle: 'dashed',
2595
+ borderColor: '#93C5FD',
2596
+ backgroundColor: '#EFF6FF',
2597
+ alignItems: 'center',
2598
+ justifyContent: 'center'
2599
+ },
2600
+ viewDocumentBtnText: {
2601
+ color: '#1D4ED8',
2602
+ fontWeight: '800'
2603
+ },
2604
+ errorText: {
2605
+ marginTop: 6,
2606
+ fontSize: 12,
2607
+ color: '#DC2626',
2608
+ fontWeight: '600'
2609
+ },
2610
+ helperText: {
2611
+ marginTop: 6,
2612
+ fontSize: 12,
2613
+ color: '#6B7280'
2614
+ }
2615
+ });