@ubkinfotech/tecaher-erp 0.1.0 → 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/README.md +143 -67
- package/lib/commonjs/core/api/endpoints.js +1 -1
- package/lib/commonjs/core/auth/authContext.js +1 -2
- package/lib/commonjs/core/provider/ERPProvider.js +1 -2
- package/lib/commonjs/modules/assignment/screens/AssignmentScreen.js +295 -80
- package/lib/commonjs/modules/assignment/services/assignmentService.js +2 -2
- package/lib/commonjs/modules/attendance/screens/AttendanceScreen.js +1 -2
- package/lib/commonjs/modules/leaveRequest/screens/LeaveRequestScreen.js +7 -7
- package/lib/commonjs/modules/leaveRequest/services/leaveRequestService.js +10 -2
- package/lib/commonjs/modules/marks/screens/MarksScreen.js +1 -2
- package/lib/commonjs/modules/myAttendance/screens/MyAttendanceScreen.js +1 -2
- package/lib/commonjs/modules/notes/screens/NotesScreen.js +410 -97
- package/lib/commonjs/modules/notes/services/notesService.js +10 -2
- package/lib/commonjs/modules/noticeboard/screens/NoticeBoardScreen.js +1 -2
- package/lib/commonjs/modules/notification/screens/NotificationScreen.js +1 -2
- package/lib/commonjs/modules/promoteStudent/screens/PromoteStudentScreen.js +1 -2
- package/lib/commonjs/modules/timetable/screens/TimeTableScreen.js +1 -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 +3 -3
|
@@ -15,8 +15,7 @@ var _useHomeworkDetails = require("../hooks/useHomeworkDetails");
|
|
|
15
15
|
var _useHomeworkSubmissions = require("../hooks/useHomeworkSubmissions");
|
|
16
16
|
var _assignmentService = require("../services/assignmentService");
|
|
17
17
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
18
|
-
function
|
|
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; }
|
|
18
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
20
19
|
function normalizeOptions(result) {
|
|
21
20
|
if (!result) {
|
|
22
21
|
return [];
|
|
@@ -132,6 +131,63 @@ function SelectModal({
|
|
|
132
131
|
})]
|
|
133
132
|
});
|
|
134
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
|
+
}
|
|
135
191
|
function resolveFileBaseUrl(args) {
|
|
136
192
|
if (args.fileBaseUrl) {
|
|
137
193
|
return args.fileBaseUrl.endsWith('/') ? args.fileBaseUrl : `${args.fileBaseUrl}/`;
|
|
@@ -480,9 +536,14 @@ function resolveUploadUrl(downloadsBaseUrl, rawPath) {
|
|
|
480
536
|
}
|
|
481
537
|
const cleaned = raw.replace(/\\/g, '/').replace(/^\/+/, '');
|
|
482
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;
|
|
483
541
|
if (cleaned.startsWith('uploads/')) {
|
|
484
542
|
return encodeURI(`${uploadsRoot}${cleaned}`);
|
|
485
543
|
}
|
|
544
|
+
if (cleaned.startsWith('school_')) {
|
|
545
|
+
return encodeURI(`${uploadsBase}${cleaned}`);
|
|
546
|
+
}
|
|
486
547
|
if (cleaned.startsWith('student/homework/')) {
|
|
487
548
|
return encodeURI(`${downloadsBaseUrl}${cleaned}`);
|
|
488
549
|
}
|
|
@@ -914,6 +975,7 @@ function CreateAssignmentView({
|
|
|
914
975
|
} = (0, _useERP.useERP)();
|
|
915
976
|
const ImageView = (0, _react.useMemo)(() => tryGetImageViewer(), []);
|
|
916
977
|
const Pdf = (0, _react.useMemo)(() => tryGetPdf(), []);
|
|
978
|
+
const Calendar = (0, _react.useMemo)(() => tryGetCalendar(), []);
|
|
917
979
|
const RNFS = (0, _react.useMemo)(() => tryGetRNFS(), []);
|
|
918
980
|
const FileViewer = (0, _react.useMemo)(() => tryGetFileViewer(), []);
|
|
919
981
|
const [viewerOpen, setViewerOpen] = (0, _react.useState)(false);
|
|
@@ -924,7 +986,11 @@ function CreateAssignmentView({
|
|
|
924
986
|
const [pdfHeaders, setPdfHeaders] = (0, _react.useState)({});
|
|
925
987
|
const [pdfLoading, setPdfLoading] = (0, _react.useState)(false);
|
|
926
988
|
const [isDownloading, setIsDownloading] = (0, _react.useState)(false);
|
|
927
|
-
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)('');
|
|
928
994
|
const [title, setTitle] = (0, _react.useState)('');
|
|
929
995
|
const [description, setDescription] = (0, _react.useState)('');
|
|
930
996
|
const [classOpt, setClassOpt] = (0, _react.useState)(null);
|
|
@@ -935,6 +1001,7 @@ function CreateAssignmentView({
|
|
|
935
1001
|
const [homewDate, setHomewDate] = (0, _react.useState)('');
|
|
936
1002
|
const [submissionDate, setSubmissionDate] = (0, _react.useState)('');
|
|
937
1003
|
const [status, setStatus] = (0, _react.useState)('publish');
|
|
1004
|
+
const [markedDates, setMarkedDates] = (0, _react.useState)({});
|
|
938
1005
|
const [fileList, setFileList] = (0, _react.useState)([]);
|
|
939
1006
|
const [activeFile, setActiveFile] = (0, _react.useState)('');
|
|
940
1007
|
const [fileError, setFileError] = (0, _react.useState)(null);
|
|
@@ -1077,20 +1144,28 @@ function CreateAssignmentView({
|
|
|
1077
1144
|
type: asset.type ?? 'image/jpeg'
|
|
1078
1145
|
};
|
|
1079
1146
|
setFileError(null);
|
|
1080
|
-
|
|
1147
|
+
setUploadStatus('Uploading image...');
|
|
1148
|
+
setIsUploading(true);
|
|
1081
1149
|
try {
|
|
1082
|
-
const uploaded = await (0, _assignmentService.uploadAssignmentFile)(api, file);
|
|
1150
|
+
const uploaded = await (0, _assignmentService.uploadAssignmentFile)(api, file, schoolCode);
|
|
1083
1151
|
if (uploaded?.Status === 'Success' && uploaded.data) {
|
|
1084
1152
|
const nextPath = String(uploaded.data);
|
|
1085
1153
|
setFileList(prev => [...prev, nextPath]);
|
|
1086
1154
|
setActiveFile(prev => prev || nextPath);
|
|
1155
|
+
setUploadStatus('Image uploaded successfully');
|
|
1087
1156
|
} else {
|
|
1088
1157
|
setFileError('File upload failed');
|
|
1158
|
+
setUploadStatus('Image upload failed');
|
|
1159
|
+
_reactNative.Alert.alert('Error', String(uploaded?.msg ?? 'File not uploaded'));
|
|
1089
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'));
|
|
1090
1165
|
} finally {
|
|
1091
|
-
|
|
1166
|
+
setIsUploading(false);
|
|
1092
1167
|
}
|
|
1093
|
-
}, [api]);
|
|
1168
|
+
}, [api, schoolCode]);
|
|
1094
1169
|
const pickFiles = (0, _react.useCallback)(async () => {
|
|
1095
1170
|
let dp = null;
|
|
1096
1171
|
try {
|
|
@@ -1120,54 +1195,86 @@ function CreateAssignmentView({
|
|
|
1120
1195
|
return;
|
|
1121
1196
|
}
|
|
1122
1197
|
setFileError(null);
|
|
1123
|
-
|
|
1198
|
+
setUploadStatus(`Uploading ${files.length} file(s)...`);
|
|
1199
|
+
setIsUploading(true);
|
|
1124
1200
|
try {
|
|
1125
|
-
const
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1201
|
+
const {
|
|
1202
|
+
uploaded,
|
|
1203
|
+
failedCount
|
|
1204
|
+
} = await uploadAssignmentFilesBatch({
|
|
1205
|
+
api,
|
|
1206
|
+
files,
|
|
1207
|
+
schoolCode
|
|
1208
|
+
});
|
|
1132
1209
|
if (!uploaded.length) {
|
|
1133
1210
|
setFileError('File upload failed');
|
|
1211
|
+
setUploadStatus('File upload failed');
|
|
1212
|
+
_reactNative.Alert.alert('Error', 'File not uploaded');
|
|
1134
1213
|
return;
|
|
1135
1214
|
}
|
|
1136
1215
|
setFileList(prev => [...prev, ...uploaded]);
|
|
1137
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'));
|
|
1138
1229
|
} finally {
|
|
1139
|
-
|
|
1230
|
+
setIsUploading(false);
|
|
1140
1231
|
}
|
|
1141
|
-
}, [api]);
|
|
1232
|
+
}, [api, schoolCode]);
|
|
1142
1233
|
const submit = (0, _react.useCallback)(async () => {
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
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(', ')}`);
|
|
1147
1247
|
return;
|
|
1148
1248
|
}
|
|
1149
1249
|
const fileCsv = fileList.join(',');
|
|
1150
1250
|
const payload = {
|
|
1151
1251
|
title,
|
|
1152
1252
|
description,
|
|
1153
|
-
class_id:
|
|
1154
|
-
section_id:
|
|
1155
|
-
sub_id:
|
|
1253
|
+
class_id: selectedClassId,
|
|
1254
|
+
section_id: selectedSectionId,
|
|
1255
|
+
sub_id: selectedSubjectId,
|
|
1156
1256
|
homew_date: homewDate,
|
|
1157
1257
|
submission_date: submissionDate,
|
|
1158
1258
|
status,
|
|
1159
1259
|
file: fileCsv
|
|
1160
1260
|
};
|
|
1161
|
-
|
|
1261
|
+
setIsSubmitting(true);
|
|
1162
1262
|
try {
|
|
1163
|
-
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'));
|
|
1164
1269
|
onDone();
|
|
1270
|
+
} catch (error) {
|
|
1271
|
+
_reactNative.Alert.alert('Failed', String(error?.message ?? 'Could not create assignment'));
|
|
1165
1272
|
} finally {
|
|
1166
|
-
|
|
1273
|
+
setIsSubmitting(false);
|
|
1167
1274
|
}
|
|
1168
1275
|
}, [api, classOpt?.value, description, homewDate, onDone, sectionOpt?.value, status, subjectOpt?.value, submissionDate, title, fileList]);
|
|
1169
1276
|
const canPickSubject = sessionId !== undefined;
|
|
1170
|
-
const canSubmit =
|
|
1277
|
+
const canSubmit = !isSubmitting && !isUploading;
|
|
1171
1278
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.SafeAreaView, {
|
|
1172
1279
|
style: styles.root,
|
|
1173
1280
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
@@ -1225,25 +1332,33 @@ function CreateAssignmentView({
|
|
|
1225
1332
|
style: styles.field,
|
|
1226
1333
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
1227
1334
|
style: styles.label,
|
|
1228
|
-
children: "Homework Date
|
|
1229
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
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
|
+
})
|
|
1235
1346
|
})]
|
|
1236
1347
|
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
1237
1348
|
style: styles.field,
|
|
1238
1349
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
1239
1350
|
style: styles.label,
|
|
1240
|
-
children: "Submission Date
|
|
1241
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
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
|
+
})
|
|
1247
1362
|
})]
|
|
1248
1363
|
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
1249
1364
|
style: styles.field,
|
|
@@ -1280,7 +1395,7 @@ function CreateAssignmentView({
|
|
|
1280
1395
|
onPress: () => {
|
|
1281
1396
|
pickFiles().catch(() => {});
|
|
1282
1397
|
},
|
|
1283
|
-
disabled:
|
|
1398
|
+
disabled: isUploading || isSubmitting,
|
|
1284
1399
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
1285
1400
|
style: styles.secondaryBtnText,
|
|
1286
1401
|
children: "Gallery / File"
|
|
@@ -1290,13 +1405,19 @@ function CreateAssignmentView({
|
|
|
1290
1405
|
onPress: () => {
|
|
1291
1406
|
pickFromCamera().catch(() => {});
|
|
1292
1407
|
},
|
|
1293
|
-
disabled:
|
|
1408
|
+
disabled: isUploading || isSubmitting,
|
|
1294
1409
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
1295
1410
|
style: styles.secondaryBtnText,
|
|
1296
1411
|
children: "Camera"
|
|
1297
1412
|
})
|
|
1298
1413
|
})]
|
|
1299
|
-
}),
|
|
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, {
|
|
1300
1421
|
style: styles.fileSelectedText,
|
|
1301
1422
|
children: ["Selected: ", fileList.length, " files"]
|
|
1302
1423
|
}) : null, fileList.length ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
@@ -1443,7 +1564,7 @@ function CreateAssignmentView({
|
|
|
1443
1564
|
disabled: !canSubmit,
|
|
1444
1565
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
1445
1566
|
style: styles.primaryBtnText,
|
|
1446
|
-
children:
|
|
1567
|
+
children: isSubmitting ? 'Submitting...' : isUploading ? 'Uploading...' : 'Submit'
|
|
1447
1568
|
})
|
|
1448
1569
|
}), !canPickSubject ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
1449
1570
|
style: styles.helperText,
|
|
@@ -1490,6 +1611,55 @@ function CreateAssignmentView({
|
|
|
1490
1611
|
title: "PDF Viewer Missing"
|
|
1491
1612
|
})]
|
|
1492
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
|
+
})
|
|
1493
1663
|
})]
|
|
1494
1664
|
});
|
|
1495
1665
|
}
|
|
@@ -1522,6 +1692,9 @@ function EditAssignmentView({
|
|
|
1522
1692
|
const [calendarOpen, setCalendarOpen] = (0, _react.useState)(false);
|
|
1523
1693
|
const [dateMode, setDateMode] = (0, _react.useState)('start');
|
|
1524
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)('');
|
|
1525
1698
|
const homeworkId = detailValue(initial, ['id', 'homework_id', 'homeworkId']);
|
|
1526
1699
|
const initialTitle = String(detailValue(initial, ['title', 'homework_title'], '') ?? '');
|
|
1527
1700
|
const initialDescription = String(detailValue(initial, ['description', 'desc', 'homework_description'], '') ?? '');
|
|
@@ -1533,7 +1706,6 @@ function EditAssignmentView({
|
|
|
1533
1706
|
const initialStatus = initial?.status === 'unpublish' ? 'unpublish' : 'publish';
|
|
1534
1707
|
const existingFile = String(detailValue(initial, ['document', 'file', 'attachment', 'document_path'], '') ?? '');
|
|
1535
1708
|
const initialFiles = splitFileCsv(existingFile);
|
|
1536
|
-
const [busy, setBusy] = (0, _react.useState)(false);
|
|
1537
1709
|
const [fileError, setFileError] = (0, _react.useState)(null);
|
|
1538
1710
|
const [fileList, setFileList] = (0, _react.useState)(() => initialFiles);
|
|
1539
1711
|
const [activeFile, setActiveFile] = (0, _react.useState)(() => initialFiles[0] ?? '');
|
|
@@ -1590,7 +1762,6 @@ function EditAssignmentView({
|
|
|
1590
1762
|
}).catch(() => {});
|
|
1591
1763
|
} else {
|
|
1592
1764
|
setSubjects([]);
|
|
1593
|
-
setSubjectOpt(null);
|
|
1594
1765
|
}
|
|
1595
1766
|
}, [api, classOpt?.value, initialSectionId, initialSubjectId, sessionId]);
|
|
1596
1767
|
const viewAttachment = (0, _react.useCallback)(async rawFile => {
|
|
@@ -1707,20 +1878,28 @@ function EditAssignmentView({
|
|
|
1707
1878
|
type: asset.type ?? 'image/jpeg'
|
|
1708
1879
|
};
|
|
1709
1880
|
setFileError(null);
|
|
1710
|
-
|
|
1881
|
+
setUploadStatus('Uploading image...');
|
|
1882
|
+
setIsUploading(true);
|
|
1711
1883
|
try {
|
|
1712
|
-
const uploaded = await (0, _assignmentService.uploadAssignmentFile)(api, file);
|
|
1884
|
+
const uploaded = await (0, _assignmentService.uploadAssignmentFile)(api, file, schoolCode);
|
|
1713
1885
|
if (uploaded?.Status === 'Success' && uploaded.data) {
|
|
1714
1886
|
const nextPath = String(uploaded.data);
|
|
1715
1887
|
setFileList(prev => [...prev, nextPath]);
|
|
1716
1888
|
setActiveFile(prev => prev || nextPath);
|
|
1889
|
+
setUploadStatus('Image uploaded successfully');
|
|
1717
1890
|
} else {
|
|
1718
1891
|
setFileError('File upload failed');
|
|
1892
|
+
setUploadStatus('Image upload failed');
|
|
1893
|
+
_reactNative.Alert.alert('Error', String(uploaded?.msg ?? 'File not uploaded'));
|
|
1719
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'));
|
|
1720
1899
|
} finally {
|
|
1721
|
-
|
|
1900
|
+
setIsUploading(false);
|
|
1722
1901
|
}
|
|
1723
|
-
}, [api]);
|
|
1902
|
+
}, [api, schoolCode]);
|
|
1724
1903
|
const pickFiles = (0, _react.useCallback)(async () => {
|
|
1725
1904
|
let dp = null;
|
|
1726
1905
|
try {
|
|
@@ -1750,53 +1929,68 @@ function EditAssignmentView({
|
|
|
1750
1929
|
return;
|
|
1751
1930
|
}
|
|
1752
1931
|
setFileError(null);
|
|
1753
|
-
|
|
1932
|
+
setUploadStatus(`Uploading ${files.length} file(s)...`);
|
|
1933
|
+
setIsUploading(true);
|
|
1754
1934
|
try {
|
|
1755
|
-
const
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1935
|
+
const {
|
|
1936
|
+
uploaded,
|
|
1937
|
+
failedCount
|
|
1938
|
+
} = await uploadAssignmentFilesBatch({
|
|
1939
|
+
api,
|
|
1940
|
+
files,
|
|
1941
|
+
schoolCode
|
|
1942
|
+
});
|
|
1762
1943
|
if (!uploaded.length) {
|
|
1763
1944
|
setFileError('File upload failed');
|
|
1945
|
+
setUploadStatus('File upload failed');
|
|
1946
|
+
_reactNative.Alert.alert('Error', 'File not uploaded');
|
|
1764
1947
|
return;
|
|
1765
1948
|
}
|
|
1766
1949
|
setFileList(prev => [...prev, ...uploaded]);
|
|
1767
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'));
|
|
1768
1963
|
} finally {
|
|
1769
|
-
|
|
1964
|
+
setIsUploading(false);
|
|
1770
1965
|
}
|
|
1771
|
-
}, [api]);
|
|
1966
|
+
}, [api, schoolCode]);
|
|
1772
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;
|
|
1773
1971
|
const fileToSend = fileList.length ? fileList.join(',') : undefined;
|
|
1774
1972
|
const missing = [];
|
|
1775
1973
|
if (!homeworkId) missing.push('homework id');
|
|
1776
1974
|
if (!title) missing.push('title');
|
|
1777
1975
|
if (!description) missing.push('description');
|
|
1778
|
-
if (!
|
|
1779
|
-
if (!
|
|
1780
|
-
if (!
|
|
1976
|
+
if (!selectedClassId) missing.push('class');
|
|
1977
|
+
if (!selectedSectionId) missing.push('section');
|
|
1978
|
+
if (!selectedSubjectId) missing.push('subject');
|
|
1781
1979
|
if (!homewDate) missing.push('homework date');
|
|
1782
1980
|
if (!submissionDate) missing.push('submission date');
|
|
1783
|
-
if (!fileToSend) missing.push('file');
|
|
1784
1981
|
if (missing.length) {
|
|
1785
|
-
if (!fileToSend) {
|
|
1786
|
-
setFileError('File is required');
|
|
1787
|
-
}
|
|
1788
1982
|
_reactNative.Alert.alert('Missing Fields', `Please fill: ${missing.join(', ')}`);
|
|
1789
1983
|
return;
|
|
1790
1984
|
}
|
|
1791
|
-
|
|
1985
|
+
setIsSubmitting(true);
|
|
1792
1986
|
try {
|
|
1793
1987
|
const res = await (0, _assignmentService.updateAssignment)(api, {
|
|
1794
1988
|
id: homeworkId,
|
|
1795
1989
|
title,
|
|
1796
1990
|
description,
|
|
1797
|
-
class_id:
|
|
1798
|
-
section_id:
|
|
1799
|
-
sub_id:
|
|
1991
|
+
class_id: selectedClassId,
|
|
1992
|
+
section_id: selectedSectionId,
|
|
1993
|
+
sub_id: selectedSubjectId,
|
|
1800
1994
|
homew_date: homewDate,
|
|
1801
1995
|
submission_date: submissionDate,
|
|
1802
1996
|
status,
|
|
@@ -1812,11 +2006,11 @@ function EditAssignmentView({
|
|
|
1812
2006
|
} catch (error) {
|
|
1813
2007
|
_reactNative.Alert.alert('Update Failed', String(error?.message ?? 'Could not update assignment'));
|
|
1814
2008
|
} finally {
|
|
1815
|
-
|
|
2009
|
+
setIsSubmitting(false);
|
|
1816
2010
|
}
|
|
1817
|
-
}, [api,
|
|
2011
|
+
}, [api, description, fileList, homewDate, homeworkId, initialClassId, initialSectionId, initialSubjectId, onDone, status, submissionDate, title, classOpt?.value, sectionOpt?.value, subjectOpt?.value]);
|
|
1818
2012
|
const canPickSubject = sessionId !== undefined;
|
|
1819
|
-
const canSubmit =
|
|
2013
|
+
const canSubmit = !isSubmitting && !isUploading;
|
|
1820
2014
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.SafeAreaView, {
|
|
1821
2015
|
style: styles.root,
|
|
1822
2016
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
@@ -1944,7 +2138,7 @@ function EditAssignmentView({
|
|
|
1944
2138
|
onPress: () => {
|
|
1945
2139
|
pickFiles().catch(() => {});
|
|
1946
2140
|
},
|
|
1947
|
-
disabled:
|
|
2141
|
+
disabled: isUploading || isSubmitting,
|
|
1948
2142
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
1949
2143
|
style: styles.secondaryBtnText,
|
|
1950
2144
|
children: "Add More Files"
|
|
@@ -1954,13 +2148,19 @@ function EditAssignmentView({
|
|
|
1954
2148
|
onPress: () => {
|
|
1955
2149
|
pickFromCamera().catch(() => {});
|
|
1956
2150
|
},
|
|
1957
|
-
disabled:
|
|
2151
|
+
disabled: isUploading || isSubmitting,
|
|
1958
2152
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
1959
2153
|
style: styles.secondaryBtnText,
|
|
1960
2154
|
children: "Camera"
|
|
1961
2155
|
})
|
|
1962
2156
|
})]
|
|
1963
|
-
}),
|
|
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, {
|
|
1964
2164
|
style: styles.fileSelectedText,
|
|
1965
2165
|
children: ["Selected: ", fileList.length, " files"]
|
|
1966
2166
|
}) : null, fileList.length ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
@@ -2107,7 +2307,7 @@ function EditAssignmentView({
|
|
|
2107
2307
|
disabled: !canSubmit,
|
|
2108
2308
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
2109
2309
|
style: styles.primaryBtnText,
|
|
2110
|
-
children:
|
|
2310
|
+
children: isSubmitting ? 'Updating...' : isUploading ? 'Uploading...' : 'Update'
|
|
2111
2311
|
})
|
|
2112
2312
|
}), !canPickSubject ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
2113
2313
|
style: styles.helperText,
|
|
@@ -2611,5 +2811,20 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
2611
2811
|
marginTop: 6,
|
|
2612
2812
|
fontSize: 12,
|
|
2613
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'
|
|
2614
2829
|
}
|
|
2615
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'
|
|
@@ -11,8 +11,7 @@ var _EmptyState = require("../../../shared/empty-states/EmptyState");
|
|
|
11
11
|
var _LoadingState = require("../../../shared/loaders/LoadingState");
|
|
12
12
|
var _attendanceService = require("../services/attendanceService");
|
|
13
13
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
|
-
function
|
|
15
|
-
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; }
|
|
14
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
16
15
|
function normalizeOptions(result) {
|
|
17
16
|
if (!result) {
|
|
18
17
|
return [];
|