@ubkinfotech/tecaher-erp 0.1.1 → 0.1.2

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.
@@ -95,7 +95,7 @@ const endpoints = exports.endpoints = {
95
95
  return `staff/teacherAPP/attendenceSection/getsectionDropdownforTeacher?class_id=${args.classId}`;
96
96
  },
97
97
  teacherSubjectsByClass: args => {
98
- return `staff/teacherAPP/StaffHomework/getsubjectDropdownByClass?class_id=${args.classId}&session_id=${args.sessionId}`;
98
+ return `staff/teacherAPP/teacherbasicDetails/getteacherSubjectsaccordingtoClass?session_id=${args.sessionId}&class_id=${args.classId}`;
99
99
  },
100
100
  details: args => {
101
101
  return `staff/teacherAPP/Notes/getNotedatabyID?id=${args.noteId}`;
@@ -131,6 +131,63 @@ function SelectModal({
131
131
  })]
132
132
  });
133
133
  }
134
+ function StatusBanner({
135
+ text,
136
+ busy = false,
137
+ tone = 'info'
138
+ }) {
139
+ const palette = tone === 'error' ? {
140
+ backgroundColor: '#FEF2F2',
141
+ borderColor: '#FECACA',
142
+ textColor: '#B91C1C',
143
+ spinnerColor: '#DC2626'
144
+ } : tone === 'success' ? {
145
+ backgroundColor: '#ECFDF5',
146
+ borderColor: '#A7F3D0',
147
+ textColor: '#047857',
148
+ spinnerColor: '#059669'
149
+ } : {
150
+ backgroundColor: '#EFF6FF',
151
+ borderColor: '#BFDBFE',
152
+ textColor: '#1D4ED8',
153
+ spinnerColor: '#2563EB'
154
+ };
155
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
156
+ style: [styles.statusBanner, {
157
+ backgroundColor: palette.backgroundColor,
158
+ borderColor: palette.borderColor
159
+ }],
160
+ children: [busy ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
161
+ size: "small",
162
+ color: palette.spinnerColor
163
+ }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
164
+ style: [styles.statusBannerText, {
165
+ color: palette.textColor
166
+ }],
167
+ children: text
168
+ })]
169
+ });
170
+ }
171
+ async function uploadAssignmentFilesBatch(args) {
172
+ const uploaded = [];
173
+ let failedCount = 0;
174
+ await Promise.all(args.files.map(async file => {
175
+ try {
176
+ const res = await (0, _assignmentService.uploadAssignmentFile)(args.api, file, args.schoolCode);
177
+ if (res?.Status === 'Success' && res?.data) {
178
+ uploaded.push(String(res.data));
179
+ return;
180
+ }
181
+ } catch {
182
+ // Keep partial success behavior for multi-file uploads.
183
+ }
184
+ failedCount += 1;
185
+ }));
186
+ return {
187
+ uploaded,
188
+ failedCount
189
+ };
190
+ }
134
191
  function resolveFileBaseUrl(args) {
135
192
  if (args.fileBaseUrl) {
136
193
  return args.fileBaseUrl.endsWith('/') ? args.fileBaseUrl : `${args.fileBaseUrl}/`;
@@ -479,9 +536,14 @@ function resolveUploadUrl(downloadsBaseUrl, rawPath) {
479
536
  }
480
537
  const cleaned = raw.replace(/\\/g, '/').replace(/^\/+/, '');
481
538
  const uploadsRoot = downloadsBaseUrl.replace(/uploads\/?$/i, '');
539
+ const uploadsBaseIndex = downloadsBaseUrl.toLowerCase().indexOf('uploads/');
540
+ const uploadsBase = uploadsBaseIndex >= 0 ? downloadsBaseUrl.slice(0, uploadsBaseIndex + 'uploads/'.length) : downloadsBaseUrl;
482
541
  if (cleaned.startsWith('uploads/')) {
483
542
  return encodeURI(`${uploadsRoot}${cleaned}`);
484
543
  }
544
+ if (cleaned.startsWith('school_')) {
545
+ return encodeURI(`${uploadsBase}${cleaned}`);
546
+ }
485
547
  if (cleaned.startsWith('student/homework/')) {
486
548
  return encodeURI(`${downloadsBaseUrl}${cleaned}`);
487
549
  }
@@ -913,6 +975,7 @@ function CreateAssignmentView({
913
975
  } = (0, _useERP.useERP)();
914
976
  const ImageView = (0, _react.useMemo)(() => tryGetImageViewer(), []);
915
977
  const Pdf = (0, _react.useMemo)(() => tryGetPdf(), []);
978
+ const Calendar = (0, _react.useMemo)(() => tryGetCalendar(), []);
916
979
  const RNFS = (0, _react.useMemo)(() => tryGetRNFS(), []);
917
980
  const FileViewer = (0, _react.useMemo)(() => tryGetFileViewer(), []);
918
981
  const [viewerOpen, setViewerOpen] = (0, _react.useState)(false);
@@ -923,7 +986,11 @@ function CreateAssignmentView({
923
986
  const [pdfHeaders, setPdfHeaders] = (0, _react.useState)({});
924
987
  const [pdfLoading, setPdfLoading] = (0, _react.useState)(false);
925
988
  const [isDownloading, setIsDownloading] = (0, _react.useState)(false);
926
- const [busy, setBusy] = (0, _react.useState)(false);
989
+ const [calendarOpen, setCalendarOpen] = (0, _react.useState)(false);
990
+ const [dateMode, setDateMode] = (0, _react.useState)('start');
991
+ const [isUploading, setIsUploading] = (0, _react.useState)(false);
992
+ const [isSubmitting, setIsSubmitting] = (0, _react.useState)(false);
993
+ const [uploadStatus, setUploadStatus] = (0, _react.useState)('');
927
994
  const [title, setTitle] = (0, _react.useState)('');
928
995
  const [description, setDescription] = (0, _react.useState)('');
929
996
  const [classOpt, setClassOpt] = (0, _react.useState)(null);
@@ -934,6 +1001,7 @@ function CreateAssignmentView({
934
1001
  const [homewDate, setHomewDate] = (0, _react.useState)('');
935
1002
  const [submissionDate, setSubmissionDate] = (0, _react.useState)('');
936
1003
  const [status, setStatus] = (0, _react.useState)('publish');
1004
+ const [markedDates, setMarkedDates] = (0, _react.useState)({});
937
1005
  const [fileList, setFileList] = (0, _react.useState)([]);
938
1006
  const [activeFile, setActiveFile] = (0, _react.useState)('');
939
1007
  const [fileError, setFileError] = (0, _react.useState)(null);
@@ -1076,20 +1144,28 @@ function CreateAssignmentView({
1076
1144
  type: asset.type ?? 'image/jpeg'
1077
1145
  };
1078
1146
  setFileError(null);
1079
- setBusy(true);
1147
+ setUploadStatus('Uploading image...');
1148
+ setIsUploading(true);
1080
1149
  try {
1081
- const uploaded = await (0, _assignmentService.uploadAssignmentFile)(api, file);
1150
+ const uploaded = await (0, _assignmentService.uploadAssignmentFile)(api, file, schoolCode);
1082
1151
  if (uploaded?.Status === 'Success' && uploaded.data) {
1083
1152
  const nextPath = String(uploaded.data);
1084
1153
  setFileList(prev => [...prev, nextPath]);
1085
1154
  setActiveFile(prev => prev || nextPath);
1155
+ setUploadStatus('Image uploaded successfully');
1086
1156
  } else {
1087
1157
  setFileError('File upload failed');
1158
+ setUploadStatus('Image upload failed');
1159
+ _reactNative.Alert.alert('Error', String(uploaded?.msg ?? 'File not uploaded'));
1088
1160
  }
1161
+ } catch (error) {
1162
+ setFileError('File upload failed');
1163
+ setUploadStatus('Image upload failed');
1164
+ _reactNative.Alert.alert('Error', String(error?.message ?? 'File not uploaded'));
1089
1165
  } finally {
1090
- setBusy(false);
1166
+ setIsUploading(false);
1091
1167
  }
1092
- }, [api]);
1168
+ }, [api, schoolCode]);
1093
1169
  const pickFiles = (0, _react.useCallback)(async () => {
1094
1170
  let dp = null;
1095
1171
  try {
@@ -1119,54 +1195,86 @@ function CreateAssignmentView({
1119
1195
  return;
1120
1196
  }
1121
1197
  setFileError(null);
1122
- setBusy(true);
1198
+ setUploadStatus(`Uploading ${files.length} file(s)...`);
1199
+ setIsUploading(true);
1123
1200
  try {
1124
- const uploaded = [];
1125
- for (const f of files) {
1126
- const res = await (0, _assignmentService.uploadAssignmentFile)(api, f);
1127
- if (res?.Status === 'Success' && res.data) {
1128
- uploaded.push(String(res.data));
1129
- }
1130
- }
1201
+ const {
1202
+ uploaded,
1203
+ failedCount
1204
+ } = await uploadAssignmentFilesBatch({
1205
+ api,
1206
+ files,
1207
+ schoolCode
1208
+ });
1131
1209
  if (!uploaded.length) {
1132
1210
  setFileError('File upload failed');
1211
+ setUploadStatus('File upload failed');
1212
+ _reactNative.Alert.alert('Error', 'File not uploaded');
1133
1213
  return;
1134
1214
  }
1135
1215
  setFileList(prev => [...prev, ...uploaded]);
1136
1216
  setActiveFile(prev => prev ? prev : uploaded[0] ?? '');
1217
+ if (failedCount > 0) {
1218
+ setFileError(`${failedCount} file(s) failed to upload`);
1219
+ setUploadStatus(`${uploaded.length} file(s) uploaded, ${failedCount} failed`);
1220
+ _reactNative.Alert.alert('Upload Incomplete', `${uploaded.length} file(s) uploaded and ${failedCount} failed.`);
1221
+ return;
1222
+ }
1223
+ setFileError(null);
1224
+ setUploadStatus(`${uploaded.length} file(s) uploaded successfully`);
1225
+ } catch (error) {
1226
+ setFileError('File upload failed');
1227
+ setUploadStatus('File upload failed');
1228
+ _reactNative.Alert.alert('Error', String(error?.message ?? 'File not uploaded'));
1137
1229
  } finally {
1138
- setBusy(false);
1230
+ setIsUploading(false);
1139
1231
  }
1140
- }, [api]);
1232
+ }, [api, schoolCode]);
1141
1233
  const submit = (0, _react.useCallback)(async () => {
1142
- if (!title || !description || !classOpt?.value || !sectionOpt?.value || !subjectOpt?.value || !homewDate || !submissionDate || !fileList.length) {
1143
- if (!fileList.length) {
1144
- setFileError('File is required');
1145
- }
1234
+ const missing = [];
1235
+ const selectedClassId = classOpt?.value;
1236
+ const selectedSectionId = sectionOpt?.value;
1237
+ const selectedSubjectId = subjectOpt?.value;
1238
+ if (!title) missing.push('title');
1239
+ if (!description) missing.push('description');
1240
+ if (!selectedClassId) missing.push('class');
1241
+ if (!selectedSectionId) missing.push('section');
1242
+ if (!selectedSubjectId) missing.push('subject');
1243
+ if (!homewDate) missing.push('homework date');
1244
+ if (!submissionDate) missing.push('submission date');
1245
+ if (missing.length) {
1246
+ _reactNative.Alert.alert('Missing Fields', `Please fill: ${missing.join(', ')}`);
1146
1247
  return;
1147
1248
  }
1148
1249
  const fileCsv = fileList.join(',');
1149
1250
  const payload = {
1150
1251
  title,
1151
1252
  description,
1152
- class_id: classOpt.value,
1153
- section_id: sectionOpt.value,
1154
- sub_id: subjectOpt.value,
1253
+ class_id: selectedClassId,
1254
+ section_id: selectedSectionId,
1255
+ sub_id: selectedSubjectId,
1155
1256
  homew_date: homewDate,
1156
1257
  submission_date: submissionDate,
1157
1258
  status,
1158
1259
  file: fileCsv
1159
1260
  };
1160
- setBusy(true);
1261
+ setIsSubmitting(true);
1161
1262
  try {
1162
- await (0, _assignmentService.createAssignment)(api, payload);
1263
+ const response = await (0, _assignmentService.createAssignment)(api, payload);
1264
+ if (response?.Status && response.Status !== 'Success') {
1265
+ _reactNative.Alert.alert('Failed', String(response?.msg ?? response?.Status ?? 'Could not create assignment'));
1266
+ return;
1267
+ }
1268
+ _reactNative.Alert.alert('Success', String(response?.msg ?? 'Assignment created Successfully'));
1163
1269
  onDone();
1270
+ } catch (error) {
1271
+ _reactNative.Alert.alert('Failed', String(error?.message ?? 'Could not create assignment'));
1164
1272
  } finally {
1165
- setBusy(false);
1273
+ setIsSubmitting(false);
1166
1274
  }
1167
1275
  }, [api, classOpt?.value, description, homewDate, onDone, sectionOpt?.value, status, subjectOpt?.value, submissionDate, title, fileList]);
1168
1276
  const canPickSubject = sessionId !== undefined;
1169
- const canSubmit = canPickSubject && fileList.length > 0 && !busy;
1277
+ const canSubmit = !isSubmitting && !isUploading;
1170
1278
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.SafeAreaView, {
1171
1279
  style: styles.root,
1172
1280
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
@@ -1224,25 +1332,33 @@ function CreateAssignmentView({
1224
1332
  style: styles.field,
1225
1333
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1226
1334
  style: styles.label,
1227
- children: "Homework Date (YYYY-MM-DD)"
1228
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
1229
- value: homewDate,
1230
- onChangeText: setHomewDate,
1231
- placeholder: "2026-01-31",
1232
- placeholderTextColor: "#9CA3AF",
1233
- style: styles.input
1335
+ children: "Homework Date"
1336
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1337
+ style: styles.secondaryBtn,
1338
+ onPress: () => {
1339
+ setDateMode('start');
1340
+ setCalendarOpen(true);
1341
+ },
1342
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1343
+ style: styles.secondaryBtnText,
1344
+ children: homewDate || 'Select start date'
1345
+ })
1234
1346
  })]
1235
1347
  }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1236
1348
  style: styles.field,
1237
1349
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1238
1350
  style: styles.label,
1239
- children: "Submission Date (YYYY-MM-DD)"
1240
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
1241
- value: submissionDate,
1242
- onChangeText: setSubmissionDate,
1243
- placeholder: "2026-02-05",
1244
- placeholderTextColor: "#9CA3AF",
1245
- style: styles.input
1351
+ children: "Submission Date"
1352
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1353
+ style: styles.secondaryBtn,
1354
+ onPress: () => {
1355
+ setDateMode('end');
1356
+ setCalendarOpen(true);
1357
+ },
1358
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1359
+ style: styles.secondaryBtnText,
1360
+ children: submissionDate || 'Select end date'
1361
+ })
1246
1362
  })]
1247
1363
  }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1248
1364
  style: styles.field,
@@ -1279,7 +1395,7 @@ function CreateAssignmentView({
1279
1395
  onPress: () => {
1280
1396
  pickFiles().catch(() => {});
1281
1397
  },
1282
- disabled: busy,
1398
+ disabled: isUploading || isSubmitting,
1283
1399
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1284
1400
  style: styles.secondaryBtnText,
1285
1401
  children: "Gallery / File"
@@ -1289,13 +1405,19 @@ function CreateAssignmentView({
1289
1405
  onPress: () => {
1290
1406
  pickFromCamera().catch(() => {});
1291
1407
  },
1292
- disabled: busy,
1408
+ disabled: isUploading || isSubmitting,
1293
1409
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1294
1410
  style: styles.secondaryBtnText,
1295
1411
  children: "Camera"
1296
1412
  })
1297
1413
  })]
1298
- }), fileList.length ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
1414
+ }), isUploading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(StatusBanner, {
1415
+ text: uploadStatus || 'Uploading file...',
1416
+ busy: true
1417
+ }) : uploadStatus ? /*#__PURE__*/(0, _jsxRuntime.jsx)(StatusBanner, {
1418
+ text: uploadStatus,
1419
+ tone: fileError ? 'error' : 'success'
1420
+ }) : null, fileList.length ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
1299
1421
  style: styles.fileSelectedText,
1300
1422
  children: ["Selected: ", fileList.length, " files"]
1301
1423
  }) : null, fileList.length ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
@@ -1442,7 +1564,7 @@ function CreateAssignmentView({
1442
1564
  disabled: !canSubmit,
1443
1565
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1444
1566
  style: styles.primaryBtnText,
1445
- children: busy ? 'Please wait...' : 'Submit'
1567
+ children: isSubmitting ? 'Submitting...' : isUploading ? 'Uploading...' : 'Submit'
1446
1568
  })
1447
1569
  }), !canPickSubject ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1448
1570
  style: styles.helperText,
@@ -1489,6 +1611,55 @@ function CreateAssignmentView({
1489
1611
  title: "PDF Viewer Missing"
1490
1612
  })]
1491
1613
  })
1614
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
1615
+ visible: calendarOpen,
1616
+ transparent: true,
1617
+ animationType: "fade",
1618
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
1619
+ style: styles.modalOverlay,
1620
+ onPress: () => setCalendarOpen(false),
1621
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
1622
+ style: styles.sheetCard,
1623
+ onPress: () => {},
1624
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
1625
+ style: styles.modalHeader,
1626
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1627
+ style: styles.modalHeaderTitle,
1628
+ children: dateMode === 'start' ? 'Select start date' : 'Select end date'
1629
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
1630
+ onPress: () => setCalendarOpen(false),
1631
+ style: styles.modalClose,
1632
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1633
+ style: styles.modalCloseText,
1634
+ children: "Close"
1635
+ })
1636
+ })]
1637
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1638
+ style: {
1639
+ flex: 1
1640
+ },
1641
+ children: Calendar ? /*#__PURE__*/(0, _jsxRuntime.jsx)(Calendar, {
1642
+ markingType: "period",
1643
+ markedDates: markedDates,
1644
+ onDayPress: day => {
1645
+ const d = String(day?.dateString ?? '');
1646
+ if (!d) return;
1647
+ if (dateMode === 'start') {
1648
+ setHomewDate(d);
1649
+ setMarkedDates(rangeMarkedDates(d, submissionDate || d));
1650
+ } else {
1651
+ setSubmissionDate(d);
1652
+ setMarkedDates(rangeMarkedDates(homewDate || d, d));
1653
+ }
1654
+ setCalendarOpen(false);
1655
+ }
1656
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_EmptyState.EmptyState, {
1657
+ title: "Calendar missing",
1658
+ message: "Install react-native-calendars to enable date picker"
1659
+ })
1660
+ })]
1661
+ })
1662
+ })
1492
1663
  })]
1493
1664
  });
1494
1665
  }
@@ -1521,6 +1692,9 @@ function EditAssignmentView({
1521
1692
  const [calendarOpen, setCalendarOpen] = (0, _react.useState)(false);
1522
1693
  const [dateMode, setDateMode] = (0, _react.useState)('start');
1523
1694
  const [isDownloading, setIsDownloading] = (0, _react.useState)(false);
1695
+ const [isUploading, setIsUploading] = (0, _react.useState)(false);
1696
+ const [isSubmitting, setIsSubmitting] = (0, _react.useState)(false);
1697
+ const [uploadStatus, setUploadStatus] = (0, _react.useState)('');
1524
1698
  const homeworkId = detailValue(initial, ['id', 'homework_id', 'homeworkId']);
1525
1699
  const initialTitle = String(detailValue(initial, ['title', 'homework_title'], '') ?? '');
1526
1700
  const initialDescription = String(detailValue(initial, ['description', 'desc', 'homework_description'], '') ?? '');
@@ -1532,7 +1706,6 @@ function EditAssignmentView({
1532
1706
  const initialStatus = initial?.status === 'unpublish' ? 'unpublish' : 'publish';
1533
1707
  const existingFile = String(detailValue(initial, ['document', 'file', 'attachment', 'document_path'], '') ?? '');
1534
1708
  const initialFiles = splitFileCsv(existingFile);
1535
- const [busy, setBusy] = (0, _react.useState)(false);
1536
1709
  const [fileError, setFileError] = (0, _react.useState)(null);
1537
1710
  const [fileList, setFileList] = (0, _react.useState)(() => initialFiles);
1538
1711
  const [activeFile, setActiveFile] = (0, _react.useState)(() => initialFiles[0] ?? '');
@@ -1589,7 +1762,6 @@ function EditAssignmentView({
1589
1762
  }).catch(() => {});
1590
1763
  } else {
1591
1764
  setSubjects([]);
1592
- setSubjectOpt(null);
1593
1765
  }
1594
1766
  }, [api, classOpt?.value, initialSectionId, initialSubjectId, sessionId]);
1595
1767
  const viewAttachment = (0, _react.useCallback)(async rawFile => {
@@ -1706,20 +1878,28 @@ function EditAssignmentView({
1706
1878
  type: asset.type ?? 'image/jpeg'
1707
1879
  };
1708
1880
  setFileError(null);
1709
- setBusy(true);
1881
+ setUploadStatus('Uploading image...');
1882
+ setIsUploading(true);
1710
1883
  try {
1711
- const uploaded = await (0, _assignmentService.uploadAssignmentFile)(api, file);
1884
+ const uploaded = await (0, _assignmentService.uploadAssignmentFile)(api, file, schoolCode);
1712
1885
  if (uploaded?.Status === 'Success' && uploaded.data) {
1713
1886
  const nextPath = String(uploaded.data);
1714
1887
  setFileList(prev => [...prev, nextPath]);
1715
1888
  setActiveFile(prev => prev || nextPath);
1889
+ setUploadStatus('Image uploaded successfully');
1716
1890
  } else {
1717
1891
  setFileError('File upload failed');
1892
+ setUploadStatus('Image upload failed');
1893
+ _reactNative.Alert.alert('Error', String(uploaded?.msg ?? 'File not uploaded'));
1718
1894
  }
1895
+ } catch (error) {
1896
+ setFileError('File upload failed');
1897
+ setUploadStatus('Image upload failed');
1898
+ _reactNative.Alert.alert('Error', String(error?.message ?? 'File not uploaded'));
1719
1899
  } finally {
1720
- setBusy(false);
1900
+ setIsUploading(false);
1721
1901
  }
1722
- }, [api]);
1902
+ }, [api, schoolCode]);
1723
1903
  const pickFiles = (0, _react.useCallback)(async () => {
1724
1904
  let dp = null;
1725
1905
  try {
@@ -1749,53 +1929,68 @@ function EditAssignmentView({
1749
1929
  return;
1750
1930
  }
1751
1931
  setFileError(null);
1752
- setBusy(true);
1932
+ setUploadStatus(`Uploading ${files.length} file(s)...`);
1933
+ setIsUploading(true);
1753
1934
  try {
1754
- const uploaded = [];
1755
- for (const f of files) {
1756
- const res = await (0, _assignmentService.uploadAssignmentFile)(api, f);
1757
- if (res?.Status === 'Success' && res.data) {
1758
- uploaded.push(String(res.data));
1759
- }
1760
- }
1935
+ const {
1936
+ uploaded,
1937
+ failedCount
1938
+ } = await uploadAssignmentFilesBatch({
1939
+ api,
1940
+ files,
1941
+ schoolCode
1942
+ });
1761
1943
  if (!uploaded.length) {
1762
1944
  setFileError('File upload failed');
1945
+ setUploadStatus('File upload failed');
1946
+ _reactNative.Alert.alert('Error', 'File not uploaded');
1763
1947
  return;
1764
1948
  }
1765
1949
  setFileList(prev => [...prev, ...uploaded]);
1766
1950
  setActiveFile(prev => prev ? prev : uploaded[0] ?? '');
1951
+ if (failedCount > 0) {
1952
+ setFileError(`${failedCount} file(s) failed to upload`);
1953
+ setUploadStatus(`${uploaded.length} file(s) uploaded, ${failedCount} failed`);
1954
+ _reactNative.Alert.alert('Upload Incomplete', `${uploaded.length} file(s) uploaded and ${failedCount} failed.`);
1955
+ return;
1956
+ }
1957
+ setFileError(null);
1958
+ setUploadStatus(`${uploaded.length} file(s) uploaded successfully`);
1959
+ } catch (error) {
1960
+ setFileError('File upload failed');
1961
+ setUploadStatus('File upload failed');
1962
+ _reactNative.Alert.alert('Error', String(error?.message ?? 'File not uploaded'));
1767
1963
  } finally {
1768
- setBusy(false);
1964
+ setIsUploading(false);
1769
1965
  }
1770
- }, [api]);
1966
+ }, [api, schoolCode]);
1771
1967
  const submit = (0, _react.useCallback)(async () => {
1968
+ const selectedClassId = classOpt?.value ?? initialClassId;
1969
+ const selectedSectionId = sectionOpt?.value ?? initialSectionId;
1970
+ const selectedSubjectId = subjectOpt?.value ?? initialSubjectId;
1772
1971
  const fileToSend = fileList.length ? fileList.join(',') : undefined;
1773
1972
  const missing = [];
1774
1973
  if (!homeworkId) missing.push('homework id');
1775
1974
  if (!title) missing.push('title');
1776
1975
  if (!description) missing.push('description');
1777
- if (!classOpt?.value) missing.push('class');
1778
- if (!sectionOpt?.value) missing.push('section');
1779
- if (!subjectOpt?.value) missing.push('subject');
1976
+ if (!selectedClassId) missing.push('class');
1977
+ if (!selectedSectionId) missing.push('section');
1978
+ if (!selectedSubjectId) missing.push('subject');
1780
1979
  if (!homewDate) missing.push('homework date');
1781
1980
  if (!submissionDate) missing.push('submission date');
1782
- if (!fileToSend) missing.push('file');
1783
1981
  if (missing.length) {
1784
- if (!fileToSend) {
1785
- setFileError('File is required');
1786
- }
1787
1982
  _reactNative.Alert.alert('Missing Fields', `Please fill: ${missing.join(', ')}`);
1788
1983
  return;
1789
1984
  }
1790
- setBusy(true);
1985
+ setIsSubmitting(true);
1791
1986
  try {
1792
1987
  const res = await (0, _assignmentService.updateAssignment)(api, {
1793
1988
  id: homeworkId,
1794
1989
  title,
1795
1990
  description,
1796
- class_id: classOpt.value,
1797
- section_id: sectionOpt.value,
1798
- sub_id: subjectOpt.value,
1991
+ class_id: selectedClassId,
1992
+ section_id: selectedSectionId,
1993
+ sub_id: selectedSubjectId,
1799
1994
  homew_date: homewDate,
1800
1995
  submission_date: submissionDate,
1801
1996
  status,
@@ -1811,11 +2006,11 @@ function EditAssignmentView({
1811
2006
  } catch (error) {
1812
2007
  _reactNative.Alert.alert('Update Failed', String(error?.message ?? 'Could not update assignment'));
1813
2008
  } finally {
1814
- setBusy(false);
2009
+ setIsSubmitting(false);
1815
2010
  }
1816
- }, [api, classOpt?.value, description, fileList, homewDate, homeworkId, onDone, sectionOpt?.value, status, subjectOpt?.value, submissionDate, title]);
2011
+ }, [api, description, fileList, homewDate, homeworkId, initialClassId, initialSectionId, initialSubjectId, onDone, status, submissionDate, title, classOpt?.value, sectionOpt?.value, subjectOpt?.value]);
1817
2012
  const canPickSubject = sessionId !== undefined;
1818
- const canSubmit = canPickSubject && fileList.length > 0 && !busy;
2013
+ const canSubmit = !isSubmitting && !isUploading;
1819
2014
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.SafeAreaView, {
1820
2015
  style: styles.root,
1821
2016
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
@@ -1943,7 +2138,7 @@ function EditAssignmentView({
1943
2138
  onPress: () => {
1944
2139
  pickFiles().catch(() => {});
1945
2140
  },
1946
- disabled: busy,
2141
+ disabled: isUploading || isSubmitting,
1947
2142
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1948
2143
  style: styles.secondaryBtnText,
1949
2144
  children: "Add More Files"
@@ -1953,13 +2148,19 @@ function EditAssignmentView({
1953
2148
  onPress: () => {
1954
2149
  pickFromCamera().catch(() => {});
1955
2150
  },
1956
- disabled: busy,
2151
+ disabled: isUploading || isSubmitting,
1957
2152
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
1958
2153
  style: styles.secondaryBtnText,
1959
2154
  children: "Camera"
1960
2155
  })
1961
2156
  })]
1962
- }), fileList.length ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
2157
+ }), isUploading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(StatusBanner, {
2158
+ text: uploadStatus || 'Uploading file...',
2159
+ busy: true
2160
+ }) : uploadStatus ? /*#__PURE__*/(0, _jsxRuntime.jsx)(StatusBanner, {
2161
+ text: uploadStatus,
2162
+ tone: fileError ? 'error' : 'success'
2163
+ }) : null, fileList.length ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
1963
2164
  style: styles.fileSelectedText,
1964
2165
  children: ["Selected: ", fileList.length, " files"]
1965
2166
  }) : null, fileList.length ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
@@ -2106,7 +2307,7 @@ function EditAssignmentView({
2106
2307
  disabled: !canSubmit,
2107
2308
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
2108
2309
  style: styles.primaryBtnText,
2109
- children: busy ? 'Please wait...' : 'Update'
2310
+ children: isSubmitting ? 'Updating...' : isUploading ? 'Uploading...' : 'Update'
2110
2311
  })
2111
2312
  }), !canPickSubject ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
2112
2313
  style: styles.helperText,
@@ -2610,5 +2811,20 @@ const styles = _reactNative.StyleSheet.create({
2610
2811
  marginTop: 6,
2611
2812
  fontSize: 12,
2612
2813
  color: '#6B7280'
2814
+ },
2815
+ statusBanner: {
2816
+ marginTop: 10,
2817
+ paddingHorizontal: 12,
2818
+ paddingVertical: 10,
2819
+ borderRadius: 12,
2820
+ borderWidth: 1,
2821
+ flexDirection: 'row',
2822
+ alignItems: 'center',
2823
+ gap: 10
2824
+ },
2825
+ statusBannerText: {
2826
+ flex: 1,
2827
+ fontSize: 12,
2828
+ fontWeight: '700'
2613
2829
  }
2614
2830
  });
@@ -42,14 +42,14 @@ async function createAssignment(api, payload) {
42
42
  const res = await api.post(_endpoints.endpoints.assignment.add, payload);
43
43
  return res.data;
44
44
  }
45
- async function uploadAssignmentFile(api, file) {
45
+ async function uploadAssignmentFile(api, file, _schoolCode) {
46
46
  const formData = new FormData();
47
47
  formData.append('file', {
48
48
  uri: file.uri,
49
49
  name: file.name,
50
50
  type: file.type
51
51
  });
52
- formData.append('filepath', 'uploads/student/homework/');
52
+ formData.append('filepath', 'student/homework/');
53
53
  const res = await api.post(_endpoints.endpoints.assignment.uploadFile, formData, {
54
54
  headers: {
55
55
  'Content-Type': 'multipart/form-data'