@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.
- package/lib/commonjs/core/api/endpoints.js +1 -1
- package/lib/commonjs/modules/assignment/screens/AssignmentScreen.js +294 -78
- package/lib/commonjs/modules/assignment/services/assignmentService.js +2 -2
- package/lib/commonjs/modules/leaveRequest/screens/LeaveRequestScreen.js +6 -5
- package/lib/commonjs/modules/leaveRequest/services/leaveRequestService.js +10 -2
- package/lib/commonjs/modules/notes/screens/NotesScreen.js +409 -95
- package/lib/commonjs/modules/notes/services/notesService.js +10 -2
- package/lib/module/core/api/endpoints.js +1 -1
- package/lib/module/modules/assignment/screens/AssignmentScreen.js +295 -79
- package/lib/module/modules/assignment/services/assignmentService.js +2 -2
- package/lib/module/modules/leaveRequest/screens/LeaveRequestScreen.js +6 -5
- package/lib/module/modules/leaveRequest/services/leaveRequestService.js +10 -2
- package/lib/module/modules/notes/screens/NotesScreen.js +410 -96
- package/lib/module/modules/notes/services/notesService.js +10 -2
- package/lib/typescript/modules/assignment/services/assignmentService.d.ts +1 -1
- package/lib/typescript/modules/leaveRequest/services/leaveRequestService.d.ts +1 -1
- package/lib/typescript/modules/notes/services/notesService.d.ts +1 -1
- package/package.json +1 -1
|
@@ -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/
|
|
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 [
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1198
|
+
setUploadStatus(`Uploading ${files.length} file(s)...`);
|
|
1199
|
+
setIsUploading(true);
|
|
1123
1200
|
try {
|
|
1124
|
-
const
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
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
|
-
|
|
1230
|
+
setIsUploading(false);
|
|
1139
1231
|
}
|
|
1140
|
-
}, [api]);
|
|
1232
|
+
}, [api, schoolCode]);
|
|
1141
1233
|
const submit = (0, _react.useCallback)(async () => {
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
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:
|
|
1153
|
-
section_id:
|
|
1154
|
-
sub_id:
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
1228
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
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
|
|
1240
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
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:
|
|
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:
|
|
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
|
-
}),
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1932
|
+
setUploadStatus(`Uploading ${files.length} file(s)...`);
|
|
1933
|
+
setIsUploading(true);
|
|
1753
1934
|
try {
|
|
1754
|
-
const
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
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
|
-
|
|
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 (!
|
|
1778
|
-
if (!
|
|
1779
|
-
if (!
|
|
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
|
-
|
|
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:
|
|
1797
|
-
section_id:
|
|
1798
|
-
sub_id:
|
|
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
|
-
|
|
2009
|
+
setIsSubmitting(false);
|
|
1815
2010
|
}
|
|
1816
|
-
}, [api,
|
|
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 =
|
|
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:
|
|
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:
|
|
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
|
-
}),
|
|
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:
|
|
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', '
|
|
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'
|