From eb2f78248cae953ebc68a8c490da6ffec9bb75cf Mon Sep 17 00:00:00 2001 From: Menaka Date: Fri, 23 Aug 2024 11:27:41 +0530 Subject: [PATCH] Assessment intial commit --- android/build.gradle | 10 + android/settings.gradle | 1 + package-lock.json | 60 ++- package.json | 7 +- src/actions/class.action.tsx | 35 +- src/actions/index.tsx | 4 +- src/actions/questionbank.action.tsx | 154 +++++++ src/actions/type.tsx | 44 ++ src/api/class.api.tsx | 18 + src/api/questionbank.api.tsx | 94 +++++ src/components/Sidebar.tsx | 6 + src/config/Config.tsx | 12 +- src/navigation/AppNavigator.tsx | 10 + src/reducers/class.reducer.tsx | 19 +- src/reducers/index.tsx | 4 +- src/reducers/questionbank.reducer.tsx | 105 +++++ src/sagas/class.sagas.tsx | 17 +- src/sagas/index.tsx | 17 +- src/sagas/questionbank.saga.tsx | 130 ++++++ src/screens/QuestionBank/Assessment.tsx | 180 ++++++++ src/screens/QuestionBank/AssessmentCreate.tsx | 379 +++++++++++++++++ src/screens/QuestionBank/AssessmentEdit.tsx | 397 ++++++++++++++++++ .../QuestionBank/AssessmentScanner.tsx | 277 ++++++++++++ src/screens/QuestionBank/QuestionBank.tsx | 165 ++++++++ yarn.lock | 46 +- 25 files changed, 2161 insertions(+), 30 deletions(-) create mode 100644 src/actions/questionbank.action.tsx create mode 100644 src/api/questionbank.api.tsx create mode 100644 src/reducers/questionbank.reducer.tsx create mode 100644 src/sagas/questionbank.saga.tsx create mode 100644 src/screens/QuestionBank/Assessment.tsx create mode 100644 src/screens/QuestionBank/AssessmentCreate.tsx create mode 100644 src/screens/QuestionBank/AssessmentEdit.tsx create mode 100644 src/screens/QuestionBank/AssessmentScanner.tsx create mode 100644 src/screens/QuestionBank/QuestionBank.tsx diff --git a/android/build.gradle b/android/build.gradle index 3d6dfe0..85c9557 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -28,6 +28,16 @@ allprojects { force "com.facebook.react:react-native:" + REACT_NATIVE_VERSION } } + repositories { + + // * Your other repositories here * + + // * Add a new maven block after other repositories / blocks * + maven { + // expo-camera bundles a custom com.google.android:cameraview + url "$rootDir/../node_modules/expo-camera/android/maven" + } + } // ... } diff --git a/android/settings.gradle b/android/settings.gradle index 0fbd356..eac979b 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -2,6 +2,7 @@ rootProject.name = 'TedQu' apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) include ':app' includeBuild('../node_modules/@react-native/gradle-plugin') +include ':expo-modules-core' include ':react-native-vector-icons' project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android') diff --git a/package-lock.json b/package-lock.json index 6577465..81c1fe4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "crypto-ts": "^1.0.2", "deprecated-react-native-prop-types": "^5.0.0", "expo": "^50.0.7", - "expo-camera": "^15.0.14", + "expo-camera": "^14.1.3", "expo-constants": "~15.4.6", "expo-file-system": "~16.0.9", "expo-firebase-analytics": "^8.0.0", @@ -58,8 +58,10 @@ "react-native-calendars": "^1.1303.0", "react-native-chart-kit": "^6.12.0", "react-native-charts-wrapper": "^0.6.0", - "react-native-document-scanner-plugin": "^0.9.1", + "react-native-document-scanner-plugin": "^1.0.0", + "react-native-fetch-polyfill": "^1.1.3", "react-native-flash-message": "^0.4.2", + "react-native-fs": "^2.20.0", "react-native-gesture-handler": "^2.15.0", "react-native-get-random-values": "~1.9.0", "react-native-gifted-charts": "^1.4.16", @@ -91,6 +93,7 @@ "redux-persist": "^6.0.0", "redux-saga": "^1.2.3", "victory-native": "^35.3.2", + "whatwg-fetch-timeout": "^2.0.2-timeout", "yup": "^1.3.3" }, "devDependencies": { @@ -8951,6 +8954,11 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base-64": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", + "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==" + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -11485,9 +11493,9 @@ } }, "node_modules/expo-camera": { - "version": "15.0.14", - "resolved": "https://registry.npmjs.org/expo-camera/-/expo-camera-15.0.14.tgz", - "integrity": "sha512-O4uvVywGsQ3a89d0BX4lq6mDuGYGukx1PYY4QrC9zw1yzD2W9BVTl8lanFRPC5h4PRniekfeWUVH1a0jJmkLIw==", + "version": "14.1.3", + "resolved": "https://registry.npmjs.org/expo-camera/-/expo-camera-14.1.3.tgz", + "integrity": "sha512-JodpVjOY8JDuSp/RkphS8Bxqaj/gwg0h0UbQB9MLr1LoxbL9brvJt7IZnmTf7+ON8jRKUx9E5o/F02pRNbmSbQ==", "dependencies": { "invariant": "^2.2.4" }, @@ -18832,9 +18840,9 @@ } }, "node_modules/react-native-document-scanner-plugin": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/react-native-document-scanner-plugin/-/react-native-document-scanner-plugin-0.9.1.tgz", - "integrity": "sha512-iHqd/ZEHmKnPsA0X2DoSZ7B324WSDQO45I22busHHfc2vSRMHozP9fcIP34YTuNvfbvjJFhziCIw66YcGWLotA==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/react-native-document-scanner-plugin/-/react-native-document-scanner-plugin-1.0.0.tgz", + "integrity": "sha512-dDsSclFpxl4U0Bnz/oEE0OEEqEjA6zNCu/2lmls4lslq3JqgHacI8xEl5DygMHd4AlDj3X4g1yKUacllfaC+UA==", "peerDependencies": { "expo": ">=47.0.0", "react": "*", @@ -18846,6 +18854,14 @@ } } }, + "node_modules/react-native-fetch-polyfill": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/react-native-fetch-polyfill/-/react-native-fetch-polyfill-1.1.3.tgz", + "integrity": "sha512-zr5yXQftuGq+ABGa3n4ZE+vkL1lBsMSePkRINm3/6vlpbwnLXYoijwazTO/W8GjsV4LAgGmzuieZxKO/NxW19A==", + "peerDependencies": { + "react-native": ">=0.27" + } + }, "node_modules/react-native-flash-message": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/react-native-flash-message/-/react-native-flash-message-0.4.2.tgz", @@ -18860,6 +18876,24 @@ "react-native": "*" } }, + "node_modules/react-native-fs": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/react-native-fs/-/react-native-fs-2.20.0.tgz", + "integrity": "sha512-VkTBzs7fIDUiy/XajOSNk0XazFE9l+QlMAce7lGuebZcag5CnjszB+u4BdqzwaQOdcYb5wsJIsqq4kxInIRpJQ==", + "dependencies": { + "base-64": "^0.1.0", + "utf8": "^3.0.0" + }, + "peerDependencies": { + "react-native": "*", + "react-native-windows": "*" + }, + "peerDependenciesMeta": { + "react-native-windows": { + "optional": true + } + } + }, "node_modules/react-native-gesture-handler": { "version": "2.15.0", "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.15.0.tgz", @@ -21693,6 +21727,11 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -22265,6 +22304,11 @@ "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" }, + "node_modules/whatwg-fetch-timeout": { + "version": "2.0.2-timeout", + "resolved": "https://registry.npmjs.org/whatwg-fetch-timeout/-/whatwg-fetch-timeout-2.0.2-timeout.tgz", + "integrity": "sha512-1y8WJdP1DaBXbAp9Ez8Um2Ft7iTVopOwUgnyiJPYIsWir1HuJBBxSYdvEtskQx+r/L1WMWQQxbcS8lHubCHtBw==" + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", diff --git a/package.json b/package.json index a067166..f854ab7 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "crypto-ts": "^1.0.2", "deprecated-react-native-prop-types": "^5.0.0", "expo": "^50.0.7", - "expo-camera": "^15.0.14", + "expo-camera": "^14.1.3", "expo-constants": "~15.4.6", "expo-file-system": "~16.0.9", "expo-firebase-analytics": "^8.0.0", @@ -60,8 +60,10 @@ "react-native-calendars": "^1.1303.0", "react-native-chart-kit": "^6.12.0", "react-native-charts-wrapper": "^0.6.0", - "react-native-document-scanner-plugin": "^0.9.1", + "react-native-document-scanner-plugin": "^1.0.0", + "react-native-fetch-polyfill": "^1.1.3", "react-native-flash-message": "^0.4.2", + "react-native-fs": "^2.20.0", "react-native-gesture-handler": "^2.15.0", "react-native-get-random-values": "~1.9.0", "react-native-gifted-charts": "^1.4.16", @@ -93,6 +95,7 @@ "redux-persist": "^6.0.0", "redux-saga": "^1.2.3", "victory-native": "^35.3.2", + "whatwg-fetch-timeout": "^2.0.2-timeout", "yup": "^1.3.3" }, "devDependencies": { diff --git a/src/actions/class.action.tsx b/src/actions/class.action.tsx index fd9e33b..41c1cd8 100644 --- a/src/actions/class.action.tsx +++ b/src/actions/class.action.tsx @@ -2,7 +2,10 @@ import { CREATE_CLASS_REQUEST, CREATE_CLASS_REQUEST_SUCCESS, CREATE_CLASS_REQUEST_FAILURE, GET_CLASS_DETAILS, GET_CLASS_DETAILS_SUCCESS, GET_CLASS_DETAILS_FAILURE, GET_CLASS_PAGINATION_REQUEST, GET_CLASS_PAGINATION_REQUEST_SUCCESS, GET_CLASS_PAGINATION_REQUEST_FAILURE, UPDATE_CLASS_REQUEST, UPDATE_CLASS_REQUEST_SUCCESS, UPDATE_CLASS_REQUEST_FAILURE, DELETE_CLASS_REQUEST, DELETE_CLASS_REQUEST_FAILURE, DELETE_CLASS_REQUEST_SUCCESS, GET_GRADE_REQUEST, GET_GRADE_REQUEST_SUCCESS, - GET_GRADE_REQUEST_FAILURE, GET_STANDARD_REQUEST, GET_STANDARD_REQUEST_FAILURE, GET_STANDARD_REQUEST_SUCCESS, INIT_CLASS_MODULE } from './type' + GET_GRADE_REQUEST_FAILURE, GET_STANDARD_REQUEST, GET_STANDARD_REQUEST_FAILURE, GET_STANDARD_REQUEST_SUCCESS, INIT_CLASS_MODULE, + GET_CLASS_GRADE_REQUEST, GET_CLASS_GRADE_REQUEST_SUCCESS, GET_CLASS_GRADE_REQUEST_FAILURE, + GET_CLASS_STANDARD_REQUEST, GET_CLASS_STANDARD_REQUEST_SUCCESS, GET_CLASS_STANDARD_REQUEST_FAILURE + } from './type' /** Class Type Action */ export const initClassAction = () => ({ @@ -114,3 +117,33 @@ export const deleteClassRequestFailure = (error: any) => ({ error }) +export const getMyClassGradesAction = (data: any) => ({ + type: GET_CLASS_GRADE_REQUEST, + data +}) + +export const getMyClassGradesSuccess = (data: any) => ({ + type: GET_CLASS_GRADE_REQUEST_SUCCESS, + data +}) + +export const getMyClassGradesFailure = (error: any) => ({ + type: GET_CLASS_GRADE_REQUEST_FAILURE, + error +}) + +export const getMyClassStandardAction = (data: any) => ({ + type: GET_CLASS_STANDARD_REQUEST, + data +}) + +export const getMyClassStandardSuccess = (data: any) => ({ + type: GET_CLASS_STANDARD_REQUEST_SUCCESS, + data +}) + +export const getMyClassStandardFailure = (error: any) => ({ + type: GET_CLASS_STANDARD_REQUEST_FAILURE, + error +}) + diff --git a/src/actions/index.tsx b/src/actions/index.tsx index 60ab698..450ae3b 100644 --- a/src/actions/index.tsx +++ b/src/actions/index.tsx @@ -13,6 +13,7 @@ import * as Attendance from './attendance.action' import * as Quizzes from './quizzes.action' import * as Psychometric from './psychometric.action' import * as License from './license.action' +import * as QuestionBank from './questionbank.action' export const ActionCreators = { ...Users, @@ -28,5 +29,6 @@ export const ActionCreators = { ...Attendance, ...Quizzes, ...Psychometric, - ...License + ...License, + ...QuestionBank } diff --git a/src/actions/questionbank.action.tsx b/src/actions/questionbank.action.tsx new file mode 100644 index 0000000..e65fdfa --- /dev/null +++ b/src/actions/questionbank.action.tsx @@ -0,0 +1,154 @@ +import { + INIT_ASSESSMENT_MODULE, + GET_QUESTION_PAPER_PAGINATION_REQUEST, GET_QUESTION_PAPER_PAGINATION_SUCCESS, GET_QUESTION_PAPER_PAGINATION_FAILURE, + GET_ASSESSMENT_PAGINATION_REQUEST, GET_ASSESSMENT_PAGINATION_SUCCESS, GET_ASSESSMENT_PAGINATION_FALURE, + GET_QUESTION_PAPER_REQUEST, GET_QUESTION_PAPER_SUCCESS, GET_QUESTION_PAPER_FAILURE, + CREATE_ASSESSMENT_REQUEST, CREATE_ASSESSMENT_SUCCESS, CREATE_ASSESSMENT_FALURE, + GET_ASSESSMENT_REQUEST, GET_ASSESSMENT_SUCCESS, GET_ASSESSMENT_FALURE, + UPDATE_ASSESSMENT_REQUEST, UPDATE_ASSESSMENT_SUCCESS, UPDATE_ASSESSMENT_FALURE, + DELETE_ASSESSMENT_REQUEST, DELETE_ASSESSMENT_SUCCESS, DELETE_ASSESSMENT_FALURE, + GET_ASSESSMENT_STUDENT_LIST_REQUEST, GET_ASSESSMENT_STUDENT_LIST_SUCCESS, GET_ASSESSMENT_STUDENT_LIST_FAILURE, + CREATE_ASSESSMENT_SCAN_ANSWERS_REQUEST,CREATE_ASSESSMENT_SCAN_ANSWERS_SUCCESS, CREATE_ASSESSMENT_SCAN_ANSWERS_FALURE + + } from './type' + + export const initAssessmentAction = () => ({ + type: INIT_ASSESSMENT_MODULE + }) + + export const getQuestionPaperPaginationAction = (data: any) => ({ + type: GET_QUESTION_PAPER_PAGINATION_REQUEST, + data + }) + + export const getQuestionPaperPaginationSuccess = (data: any) => ({ + type: GET_QUESTION_PAPER_PAGINATION_SUCCESS, + data + }) + + export const getQuestionPaperPaginationFailure = (error: any) => ({ + type: GET_QUESTION_PAPER_PAGINATION_FAILURE, + error + }) + + export const getAssessmentPaginationAction = (data: any) => ({ + type: GET_ASSESSMENT_PAGINATION_REQUEST, + data + }) + + export const getAssessmentPaginationSuccess = (data: any) => ({ + type: GET_ASSESSMENT_PAGINATION_SUCCESS, + data + }) + + export const getAssessmentPaginationFailure = (error: any) => ({ + type: GET_ASSESSMENT_PAGINATION_FALURE, + error + }) + + export const getQuestionPaperAction = (data: any) => ({ + type: GET_QUESTION_PAPER_REQUEST, + data + }) + + export const getQuestionPaperSuccess = (data: any) => ({ + type: GET_QUESTION_PAPER_SUCCESS, + data + }) + + export const getQuestionPaperFailure = (error: any) => ({ + type: GET_QUESTION_PAPER_FAILURE, + error + }) + + export const createAssessmentAction = (data: any) => ({ + type: CREATE_ASSESSMENT_REQUEST, + data + }) + + export const createAssessmentSuccess = (data: any) => ({ + type: CREATE_ASSESSMENT_SUCCESS, + data + }) + + export const createAssessmentFailure = (error: any) => ({ + type: CREATE_ASSESSMENT_FALURE, + error + }) + + export const getAssessmentAction = (data: any) => ({ + type: GET_ASSESSMENT_REQUEST, + data + }) + + export const getAssessmentSuccess = (data: any) => ({ + type: GET_ASSESSMENT_SUCCESS, + data + }) + + export const getAssessmentFailure = (error: any) => ({ + type: GET_ASSESSMENT_FALURE, + error + }) + + export const getAssessmentStudentListAction = (data: any) => ({ + type: GET_ASSESSMENT_STUDENT_LIST_REQUEST, + data + }) + + export const getAssessmentStudentListSuccess = (data: any) => ({ + type: GET_ASSESSMENT_STUDENT_LIST_SUCCESS, + data + }) + + export const getAssessmentStudentListFailure = (error: any) => ({ + type: GET_ASSESSMENT_STUDENT_LIST_FAILURE, + error + }) + + export const updateAssessmentAction = (data: any) => ({ + type: UPDATE_ASSESSMENT_REQUEST, + data + }) + + export const updateAssessmentSuccess = (data: any) => ({ + type: UPDATE_ASSESSMENT_SUCCESS, + data + }) + + export const updateAssessmentFailure = (error: any) => ({ + type: UPDATE_ASSESSMENT_FALURE, + error + }) + + export const deleteAssessmentAction = (data: any) => ({ + type: DELETE_ASSESSMENT_REQUEST, + data + }) + + export const deleteAssessmentSuccess = (data: any) => ({ + type: DELETE_ASSESSMENT_SUCCESS, + data + }) + + export const deleteAssessmentFailure = (error: any) => ({ + type: DELETE_ASSESSMENT_FALURE, + error + }) + + export const createAssessmentScanAnswerAction = (data: any) => ({ + type: CREATE_ASSESSMENT_SCAN_ANSWERS_REQUEST, + data + }) + + export const createAssessmentScanAnswerSuccess = (data: any) => ({ + type: CREATE_ASSESSMENT_SCAN_ANSWERS_SUCCESS, + data + }) + + export const createAssessmentScanAnswerFailure = (error: any) => ({ + type:CREATE_ASSESSMENT_SCAN_ANSWERS_FALURE, + error + }) + + \ No newline at end of file diff --git a/src/actions/type.tsx b/src/actions/type.tsx index 152ca8c..1a14602 100644 --- a/src/actions/type.tsx +++ b/src/actions/type.tsx @@ -87,6 +87,12 @@ export const DELETE_CLASS_REQUEST_FAILURE = 'DELETE_CLASS_REQUEST_FAILURE' export const GET_CLASS_PAGINATION_REQUEST = 'GET_CLASS_PAGINATION_REQUEST' export const GET_CLASS_PAGINATION_REQUEST_SUCCESS = 'GET_CLASS_PAGINATION_REQUEST_SUCCESS' export const GET_CLASS_PAGINATION_REQUEST_FAILURE = 'GET_CLASS_PAGINATION_REQUEST_FAILURE' +export const GET_CLASS_GRADE_REQUEST = 'GET_CLASS_GRADE_REQUEST' +export const GET_CLASS_GRADE_REQUEST_SUCCESS = 'GET_CLASS_GRADE_REQUEST_SUCCESS' +export const GET_CLASS_GRADE_REQUEST_FAILURE = 'GET_CLASS_GRADE_REQUEST_FAILURE' +export const GET_CLASS_STANDARD_REQUEST = 'GET_CLASS_STANDARD_REQUEST' +export const GET_CLASS_STANDARD_REQUEST_SUCCESS = 'GET_CLASS_STANDARD_REQUEST_SUCCESS' +export const GET_CLASS_STANDARD_REQUEST_FAILURE = 'GET_CLASS_STANDARD_REQUEST_FAILURE' /** Teacher Type Action */ export const INIT_TEACHER_MODULE = 'INIT_TEACHER_MODULE' @@ -381,3 +387,41 @@ export const GET_LICENSE_SCHOOLS_FAILURE = 'GET_LICENSE_SCHOOLS_FAILURE' export const GET_LICENSE_SCHOOL_DOMAIN = 'GET_LICENSE_SCHOOL_DOMAIN' export const GET_LICENSE_SCHOOL_DOMAIN_SUCCESS = 'GET_LICENSE_SCHOOL_DOMAIN_SUCCESS' export const GET_LICENSE_SCHOOL_DOMAIN_FAILURE = 'GET_LICENSE_SCHOOL_DOMAIN_FAILURE' + +export const GET_QUESTION_PAPER_PAGINATION_REQUEST = 'GET_QUESTION_PAPER_PAGINATION_REQUEST' +export const GET_QUESTION_PAPER_PAGINATION_SUCCESS = 'GET_QUESTION_PAPER_PAGINATION_SUCCESS' +export const GET_QUESTION_PAPER_PAGINATION_FAILURE = 'GET_QUESTION_PAPER_FAILURE' + +export const INIT_ASSESSMENT_MODULE = 'INIT_ASSESSMENT_MODULE' + +export const GET_ASSESSMENT_PAGINATION_REQUEST = 'GET_ASSESSMENT_PAGINATION_REQUEST' +export const GET_ASSESSMENT_PAGINATION_SUCCESS = 'GET_ASSESSMENT_PAGINATION_SUCCESS' +export const GET_ASSESSMENT_PAGINATION_FALURE = 'GET_ASSESSMENT_PAGINATION_FALURE' + +export const GET_QUESTION_PAPER_REQUEST = 'GET_QUESTION_PAPER_REQUEST' +export const GET_QUESTION_PAPER_SUCCESS = 'GET_QUESTION_PAPER_SUCCESS' +export const GET_QUESTION_PAPER_FAILURE = 'GET_QUESTION_PAPER_FAILURE' + +export const CREATE_ASSESSMENT_REQUEST = 'CREATE_ASSESSMENT_REQUEST' +export const CREATE_ASSESSMENT_SUCCESS = 'CREATE_ASSESSMENT_SUCCESS' +export const CREATE_ASSESSMENT_FALURE = 'CREATE_ASSESSMENT_FALURE' + +export const GET_ASSESSMENT_REQUEST = 'GET_ASSESSMENT_REQUEST' +export const GET_ASSESSMENT_SUCCESS = 'GET_ASSESSMENT_SUCCESS' +export const GET_ASSESSMENT_FALURE = 'GET_ASSESSMENT_FALURE' + +export const UPDATE_ASSESSMENT_REQUEST = 'UPDATE_ASSESSMENT_REQUEST' +export const UPDATE_ASSESSMENT_SUCCESS = 'UPDATE_ASSESSMENT_SUCCESS' +export const UPDATE_ASSESSMENT_FALURE = 'UPDATE_ASSESSMENT_FALURE' + +export const GET_ASSESSMENT_STUDENT_LIST_REQUEST = 'GET_ASSESSMENT_STUDENT_LIST_REQUEST' +export const GET_ASSESSMENT_STUDENT_LIST_SUCCESS = 'GET_ASSESSMENT_STUDENT_LIST_SUCCESS' +export const GET_ASSESSMENT_STUDENT_LIST_FAILURE = 'GET_ASSESSMENT_STUDENT_LIST_FAILURE' + +export const DELETE_ASSESSMENT_REQUEST = 'DELETE_ASSESSMENT_REQUEST' +export const DELETE_ASSESSMENT_SUCCESS = 'DELETE_ASSESSMENT_SUCCESS' +export const DELETE_ASSESSMENT_FALURE = 'DELETE_ASSESSMENT_FALURE' + +export const CREATE_ASSESSMENT_SCAN_ANSWERS_REQUEST = 'CREATE_ASSESSMENT_SCAN_ANSWERS_REQUEST' +export const CREATE_ASSESSMENT_SCAN_ANSWERS_SUCCESS = 'CREATE_ASSESSMENT_SCAN_ANSWERS_SUCCESS' +export const CREATE_ASSESSMENT_SCAN_ANSWERS_FALURE = 'CREATE_ASSESSMENT_SCAN_ANSWERS_FALURE' \ No newline at end of file diff --git a/src/api/class.api.tsx b/src/api/class.api.tsx index 4c4c263..1d1e4bf 100644 --- a/src/api/class.api.tsx +++ b/src/api/class.api.tsx @@ -72,3 +72,21 @@ export const getClassStandards = ({token}: any) => { const url = CLASSAPI.GET_CLASS_STANDARDS return fetchAppBase(url, requestOptions) } + +export const getMyClassGrades = ({token}: any) => { + const requestOptions = { + method: 'GET', + headers: { 'Content-Type': apiConfig.contentTypeJSON, 'Authorization': token } + } + const url = CLASSAPI.GET_MY_CLASS_GRADES + return fetchAppBase(url, requestOptions) +} + +export const getMyClassStandards = ({token, grade_id}: any) => { + const requestOptions = { + method: 'GET', + headers: { 'Content-Type': apiConfig.contentTypeJSON, 'Authorization': token } + } + const url = `${CLASSAPI.GET_MY_CLASS_SECTIONS}/?grade=${grade_id}` + return fetchAppBase(url, requestOptions) +} \ No newline at end of file diff --git a/src/api/questionbank.api.tsx b/src/api/questionbank.api.tsx new file mode 100644 index 0000000..1e09421 --- /dev/null +++ b/src/api/questionbank.api.tsx @@ -0,0 +1,94 @@ +import { QUESTION_BANK_API } from '@config/Config' //NOSONAR +import { apiConfig, fetchAppBase } from '@constants/Constants' //NOSONAR + +export const getAllQuestionPaper = async ({token, academic_year, page_no}: any) => { + const requestOptions = { + method: 'PATCH', + headers: { 'Content-Type': apiConfig.contentTypeJSON, 'Authorization': token }, + //body: JSON.stringify(data) + } + const url = `${QUESTION_BANK_API.GET_QUESTION_BANK}/?page_no=${page_no}&search=&order_by=&sort_by=` + return fetchAppBase(url, requestOptions) +} + +export const getAllAssessmentList = async ({token, academic_year, question_paper_id,page_no}: any) => { + const requestOptions = { + method: 'PATCH', + headers: { 'Content-Type': apiConfig.contentTypeJSON, 'Authorization': token }, + //body: JSON.stringify(data) + } + const url = `${QUESTION_BANK_API.GET_ASSESSMENT}/?page_no=${page_no}&question_paper_id=${question_paper_id}&search=&order_by=&sort_by=` + return fetchAppBase(url, requestOptions) +} + +export const getQuestionPaper = async ({token, academic_year, question_paper_id,page_no}: any) => { + console.log('getQuestionPaper') + const requestOptions = { + method: 'GET', + headers: { 'Content-Type': apiConfig.contentTypeJSON, 'Authorization': token }, + } + const url = `${QUESTION_BANK_API.GET_QUESTION_PAPER}/?question_paper_id=${question_paper_id}` + console.log(url) + console.log(token) + return fetchAppBase(url, requestOptions) +} + +export const createAssessment = async ({token, data}: any) => { + const requestOptions = { + method: 'POST', + headers: { 'Content-Type': apiConfig.contentTypeJSON, 'Authorization': token }, + body: JSON.stringify(data) + } + const url = `${QUESTION_BANK_API.GET_ASSESSMENT}/` + return fetchAppBase(url, requestOptions) +} + +export const getAssessment = async ({token, academic_year, uid}: any) => { + const requestOptions = { + method: 'GET', + headers: { 'Content-Type': apiConfig.contentTypeJSON, 'Authorization': token }, + } + const url = `${QUESTION_BANK_API.GET_ASSESSMENT}/?uid=${uid}` + return fetchAppBase(url, requestOptions) +} + +export const getAssessmentStudentList = async ({token, academic_year, question_paper_id,class_id}: any) => { + const requestOptions = { + method: 'GET', + headers: { 'Content-Type': apiConfig.contentTypeJSON, 'Authorization': token }, + } + console.log(token) + const url = `${QUESTION_BANK_API.VIEW_ASSESSMENT_STUDENT_LIST}/?question_paper_id=${question_paper_id}&class_id=${class_id}` + return fetchAppBase(url, requestOptions) +} + +export const updateAssessment = async ({token, academic_year, uid,data}: any) => { + const requestOptions = { + method: 'PUT', + headers: { 'Content-Type': apiConfig.contentTypeJSON, 'Authorization': token }, + body: JSON.stringify(data) + } + const url = `${QUESTION_BANK_API.GET_ASSESSMENT}/?uid=${uid}` + return fetchAppBase(url, requestOptions) +} + + +export const deleteAssessment = async ({token, academic_year, uid}: any) => { + const requestOptions = { + method: 'DELETE', + headers: { 'Content-Type': apiConfig.contentTypeJSON, 'Authorization': token }, + } + const url = `${QUESTION_BANK_API.GET_ASSESSMENT}/?uid=${uid}` + return fetchAppBase(url, requestOptions) +} + +export const createAssessmentScanAnswer = ({ data, token }: any) => { + const requestOptions = { + method: 'POST', + headers: { 'Content-Type': 'multipart/form-data; ', 'Authorization': token }, + body: data + } + console.log(data) + const url = `${QUESTION_BANK_API.ASSESSMENT_SCAN_ANSWERS}` + return fetchAppBase(url, requestOptions) +} \ No newline at end of file diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 427d096..f86cf50 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -64,6 +64,12 @@ class Sidebar extends React.Component { icon: "ios-person", roles: [UserRoles.schoolAdmin] }, + { + name: "Question Bank", + action: "QuestionBank", + icon: "ios-person", + roles: [UserRoles.schoolAdmin] + }, { name: "Settings", action: "Settings", diff --git a/src/config/Config.tsx b/src/config/Config.tsx index b5c69f0..7b913dc 100644 --- a/src/config/Config.tsx +++ b/src/config/Config.tsx @@ -23,8 +23,10 @@ export const CLASSAPI: any = { GET_COMMON_URL: "/classes", GET_CLASS_GRADES: "/lovs/grade/", // doubt - GET_CLASS_STANDARDS: "/lovs/standard/" + GET_CLASS_STANDARDS: "/lovs/standard/", // doubt + GET_MY_CLASS_GRADES: "/classes/get-my-grades/", + GET_MY_CLASS_SECTIONS: "/classes/get-my-sections/" } export const SCHOOLAPI: any = { @@ -131,4 +133,12 @@ export const LICENSE_API: any = { export const SCHOOL_LIST_API: any = { GET_LICENSE_SCHOOLS: "/license_module/get-all-schools", GET_LICENSE_SCHOOL_DOMAIN: "/license_module/get-domain-list", +} + +export const QUESTION_BANK_API: any = { + GET_QUESTION_BANK: "/questionbank-module/create-question-paper", + GET_ASSESSMENT: "/questionbank-module/create-assessment", + GET_QUESTION_PAPER : "/questionbank-module/view-questions", + VIEW_ASSESSMENT_STUDENT_LIST: "/questionbank-module/view-assessment-studentlist", + ASSESSMENT_SCAN_ANSWERS:"/questionbank-module/assessment-answers/", } \ No newline at end of file diff --git a/src/navigation/AppNavigator.tsx b/src/navigation/AppNavigator.tsx index d074712..96a356e 100644 --- a/src/navigation/AppNavigator.tsx +++ b/src/navigation/AppNavigator.tsx @@ -41,6 +41,11 @@ import ReportPsychometric from "@screens/Pscyometric/ReportPsychometric" import { Ionicons } from '@expo/vector-icons' import { StackActions, NavigationActions } from 'react-navigation' import { CommonActions } from "@react-navigation/native" +import QuestionBank from "@screens/QuestionBank/QuestionBank" +import Assessment from "@screens/QuestionBank/Assessment" +import AssessmentCreate from "@screens/QuestionBank/AssessmentCreate" +import AssessmentEdit from "@screens/QuestionBank/AssessmentEdit" +import AssessmentScanner from "@screens/QuestionBank/AssessmentScanner" const navigationOptions = ({ navigation, screenProps }: any) => ({ headerStyle: { @@ -206,6 +211,11 @@ const AppNavigator = () => { + + + + + ); }; diff --git a/src/reducers/class.reducer.tsx b/src/reducers/class.reducer.tsx index ab063fa..0bf9bc9 100644 --- a/src/reducers/class.reducer.tsx +++ b/src/reducers/class.reducer.tsx @@ -2,7 +2,8 @@ import { CREATE_CLASS_REQUEST, CREATE_CLASS_REQUEST_FAILURE, CREATE_CLASS_REQUES GET_CLASS_DETAILS_SUCCESS, GET_CLASS_PAGINATION_REQUEST, GET_CLASS_PAGINATION_REQUEST_FAILURE, GET_CLASS_PAGINATION_REQUEST_SUCCESS, //NOSONAR UPDATE_CLASS_REQUEST, UPDATE_CLASS_REQUEST_FAILURE, UPDATE_CLASS_REQUEST_SUCCESS, DELETE_CLASS_REQUEST, DELETE_CLASS_REQUEST_SUCCESS, //NOSONAR DELETE_CLASS_REQUEST_FAILURE, GET_GRADE_REQUEST, GET_GRADE_REQUEST_SUCCESS, GET_GRADE_REQUEST_FAILURE, GET_STANDARD_REQUEST, GET_STANDARD_REQUEST_SUCCESS, //NOSONAR - GET_STANDARD_REQUEST_FAILURE, INIT_CLASS_MODULE } from '@actions/type' //NOSONAR + GET_STANDARD_REQUEST_FAILURE, INIT_CLASS_MODULE, GET_CLASS_GRADE_REQUEST, GET_CLASS_GRADE_REQUEST_SUCCESS, GET_CLASS_GRADE_REQUEST_FAILURE, + GET_CLASS_STANDARD_REQUEST, GET_CLASS_STANDARD_REQUEST_FAILURE, GET_CLASS_STANDARD_REQUEST_SUCCESS } from '@actions/type' //NOSONAR import Toast from 'react-native-simple-toast' @@ -22,7 +23,9 @@ const defaultState = { data: {}, deleted_info: {}, grade: {}, - standard: {} + standard: {}, + class_grade: {}, + class_standard: {} } export const classes = (state = defaultState, action: any) => { @@ -113,6 +116,18 @@ export const classes = (state = defaultState, action: any) => { return { ...state, loading: false, deleted_info: response } case DELETE_CLASS_REQUEST_FAILURE: return { ...state, loading: false, error: action.error } + case GET_CLASS_GRADE_REQUEST: + return { ...state, loading: true, class_grade: defaultObj } + case GET_CLASS_GRADE_REQUEST_SUCCESS: + return { ...state, loading: false, class_grade: response } + case GET_CLASS_GRADE_REQUEST_FAILURE: + return { ...state, loading: false, error: action.error } + case GET_CLASS_STANDARD_REQUEST: + return { ...state, loading: true, class_standard: defaultObj } + case GET_CLASS_STANDARD_REQUEST_SUCCESS: + return { ...state, loading: false, class_standard: response } + case GET_CLASS_STANDARD_REQUEST_FAILURE: + return { ...state, loading: false, error: action.error } default: return state } diff --git a/src/reducers/index.tsx b/src/reducers/index.tsx index 96ae5c1..a956693 100644 --- a/src/reducers/index.tsx +++ b/src/reducers/index.tsx @@ -14,7 +14,7 @@ import { attendance } from './attendance.reducer' import { quizzes } from './quizzes.reducer' import { psychometric } from './psychometric.reducer' import { license } from './license.reducer' - +import { questionbank } from './questionbank.reducer' const rootReducer = combineReducers({ users, @@ -22,7 +22,7 @@ const rootReducer = combineReducers({ school, classes, teachers, teacherSubjectMap, teacherClassMap, dashboard, noticeboard, diary, - students, instantfeedback, attendance, quizzes, psychometric, license + students, instantfeedback, attendance, quizzes, psychometric, license, questionbank }) export default rootReducer diff --git a/src/reducers/questionbank.reducer.tsx b/src/reducers/questionbank.reducer.tsx new file mode 100644 index 0000000..ba2873f --- /dev/null +++ b/src/reducers/questionbank.reducer.tsx @@ -0,0 +1,105 @@ +import { + INIT_ASSESSMENT_MODULE, + GET_QUESTION_PAPER_PAGINATION_REQUEST, GET_QUESTION_PAPER_PAGINATION_SUCCESS, GET_QUESTION_PAPER_PAGINATION_FAILURE, + GET_ASSESSMENT_PAGINATION_REQUEST, GET_ASSESSMENT_PAGINATION_SUCCESS, GET_ASSESSMENT_PAGINATION_FALURE, + GET_QUESTION_PAPER_REQUEST, GET_QUESTION_PAPER_SUCCESS, GET_QUESTION_PAPER_FAILURE, + CREATE_ASSESSMENT_REQUEST, CREATE_ASSESSMENT_SUCCESS, CREATE_ASSESSMENT_FALURE, + GET_ASSESSMENT_REQUEST, GET_ASSESSMENT_SUCCESS, GET_ASSESSMENT_FALURE, + UPDATE_ASSESSMENT_REQUEST, UPDATE_ASSESSMENT_SUCCESS, UPDATE_ASSESSMENT_FALURE, + DELETE_ASSESSMENT_REQUEST, DELETE_ASSESSMENT_SUCCESS, DELETE_ASSESSMENT_FALURE, + GET_ASSESSMENT_STUDENT_LIST_REQUEST, GET_ASSESSMENT_STUDENT_LIST_SUCCESS, GET_ASSESSMENT_STUDENT_LIST_FAILURE, + CREATE_ASSESSMENT_SCAN_ANSWERS_REQUEST, CREATE_ASSESSMENT_SCAN_ANSWERS_SUCCESS, CREATE_ASSESSMENT_SCAN_ANSWERS_FALURE + +} from '@actions/type' //NOSONAR + +import Toast from 'react-native-simple-toast' + +const defaultObj: any = {} +const defaultState = { + loading: false, + menu_list: [], + license: {}, + schoolList: [], + userinfo: {}, + schoolDomain: [], + records: [], + list: {}, + assessmentrecords: [], + assessmentlist: {}, + question_details: [], + assessment_create: {}, + assessment: {}, + assessment_delete: {}, + assessment_update: {}, + assessment_student_list: {}, + assessment_answer: {}, +} + +export const questionbank = (state = defaultState, action: any) => { + const response = action.data + switch (action.type) { + case INIT_ASSESSMENT_MODULE: + return { ...state, loading: true, license: defaultObj, assessment_create: {}, assessment_update: {}, assessment_answer: {} } + case GET_QUESTION_PAPER_PAGINATION_REQUEST: + case GET_ASSESSMENT_PAGINATION_REQUEST: + case GET_QUESTION_PAPER_REQUEST: + case CREATE_ASSESSMENT_REQUEST: + case GET_ASSESSMENT_REQUEST: + case UPDATE_ASSESSMENT_REQUEST: + case DELETE_ASSESSMENT_REQUEST: + case GET_ASSESSMENT_STUDENT_LIST_REQUEST: + case CREATE_ASSESSMENT_SCAN_ANSWERS_REQUEST: + return { ...state, loading: true, license: defaultObj, assessment_create: {} } + case GET_QUESTION_PAPER_PAGINATION_SUCCESS: + if (response.hasOwnProperty('status')) { + if (!response.status) { + Toast.show(response.message, Toast.SHORT) + } + } + const { records, page } = response.data + if (page === 1) { + state.records = [] + } + return { + ...state, loading: false, list: response, records: [...state.records, ...records] + } + case GET_ASSESSMENT_PAGINATION_SUCCESS: + if (response.hasOwnProperty('status')) { + if (!response.status) { + Toast.show(response.message, Toast.SHORT) + } + } + if (response.data.page === 1) { + state.assessmentrecords = [] + } + return { + ...state, loading: false, assessmentlist: response, assessmentrecords: [...state.assessmentrecords, ...response.data.records] + } + case GET_QUESTION_PAPER_SUCCESS: + return { + ...state, loading: false, question_details: response.data.question_details + } + case CREATE_ASSESSMENT_SUCCESS: + return { ...state, loading: false, assessment_create: response } + case UPDATE_ASSESSMENT_SUCCESS: + return { ...state, loading: false, assessment_update: response } + case GET_ASSESSMENT_SUCCESS: + return { ...state, loading: false, assessment: response.data } + case GET_ASSESSMENT_STUDENT_LIST_SUCCESS: + return { ...state, loading: false, assessment_student_list: response.data } + case CREATE_ASSESSMENT_SCAN_ANSWERS_SUCCESS: + return { ...state, loading: false, assessment_answer: response } + case GET_QUESTION_PAPER_PAGINATION_FAILURE: + case GET_ASSESSMENT_PAGINATION_FALURE: + case GET_ASSESSMENT_FALURE: + return { ...state, loading: false } + case CREATE_ASSESSMENT_FALURE: + return { ...state, loading: false, assessment_create: {} } + case UPDATE_ASSESSMENT_FALURE: + return { ...state, loading: false, assessment_update: {} } + case CREATE_ASSESSMENT_SCAN_ANSWERS_FALURE: + return { ...state, loading: false, assessment_answer: {"something_wrong": "something_wrong"} } + default: + return state + } +} diff --git a/src/sagas/class.sagas.tsx b/src/sagas/class.sagas.tsx index f0c83da..d0fadf4 100644 --- a/src/sagas/class.sagas.tsx +++ b/src/sagas/class.sagas.tsx @@ -1,7 +1,7 @@ import { takeEvery, call, put } from 'redux-saga/effects' import { CREATE_CLASS_REQUEST, GET_CLASS_DETAILS, GET_CLASS_PAGINATION_REQUEST, UPDATE_CLASS_REQUEST, DELETE_CLASS_REQUEST, //NOSONAR - GET_GRADE_REQUEST, GET_STANDARD_REQUEST } from '@actions/type' //NOSONAR -import { createClassDetail, getAllClassDetails, getClassDetail, updateClassDetail, deleteClassDetail, getClassGrades, getClassStandards } from '@api/class.api' //NOSONAR + GET_GRADE_REQUEST, GET_STANDARD_REQUEST,GET_CLASS_GRADE_REQUEST, GET_CLASS_STANDARD_REQUEST } from '@actions/type' //NOSONAR +import { createClassDetail, getAllClassDetails, getClassDetail, updateClassDetail, deleteClassDetail, getClassGrades, getClassStandards, getMyClassGrades, getMyClassStandards } from '@api/class.api' //NOSONAR import { ActionCreators } from '@actions' //NOSONAR export const watchCreateClass = function* () { @@ -93,3 +93,16 @@ function* workerCreateClass(action: any) { yield put(ActionCreators.createClassRequestFailure(e)) } } + +export const watchGetMyClassStandards = function* () { + yield takeEvery(GET_CLASS_STANDARD_REQUEST, workerGetMyClassStandards) +} + +function* workerGetMyClassStandards(action: any) { + try { + const response = yield call(getMyClassStandards, action.data) + yield put(ActionCreators.getMyClassStandardSuccess(response)) + } catch (e) { + yield put(ActionCreators.getMyClassStandardFailure(e)) + } +} \ No newline at end of file diff --git a/src/sagas/index.tsx b/src/sagas/index.tsx index 8e3af73..04212af 100644 --- a/src/sagas/index.tsx +++ b/src/sagas/index.tsx @@ -5,7 +5,7 @@ import { } from "./auth.sagas" import { watchGetUserProfile, watchGetArTagRequest } from "./users.sagas" import { watchGetSchool, watchGetSchoolCategory, watchUpdateSchool } from './school.sagas' -import { watchCreateClass, watchGetClass, watchGetAllClass, watchUpdateClass, watchDeleteClass, watchGetClassGrades, watchGetClassStandards } from './class.sagas' +import { watchCreateClass, watchGetClass, watchGetAllClass, watchUpdateClass, watchDeleteClass, watchGetClassGrades, watchGetClassStandards, watchGetMyClassStandards } from './class.sagas' import { watchCreateTeacher, watchGetTeacher, watchGetAllTeacher, watchUpdateTeacher, watchDeleteTeacher, watchGetAllTeacherSubjects, watchGetAllClasses, watchGetAllSubjects, watchGetAllClassInCharge, watchMapClassIncharge, watchCreateSubjectMapping, watchUpdateSubjectMapping, @@ -35,6 +35,9 @@ import { watchGetPsychometricClassList, watchGetPsychometricStudentListByClass,w import { watchGetLicenseValidate, watchGetMenuList, watchGetLicenseSchoolList, watchGetLicenseSchoolDomain } from './license.saga' + import { + watchGetAllQuestionPaper, watchGetAllAssessmentList, watchGetQuestionPaper, watchCreateAssessment, watchUpdateAssessment, watchDeleteAssessment, + watchGetAssessment, watchGetAssessmentStudentList, watchCreateAssessmentScanAnswer} from './questionbank.saga' export default function* root() { yield all([ watchGetLogin(), @@ -56,6 +59,7 @@ export default function* root() { watchDeleteClass(), watchGetClassGrades(), watchGetClassStandards(), + watchGetMyClassStandards(), watchCreateTeacher(), watchGetTeacher(), watchGetAllTeacher(), @@ -129,6 +133,15 @@ export default function* root() { watchGetLicenseValidate(), watchGetMenuList(), watchGetLicenseSchoolList(), - watchGetLicenseSchoolDomain() + watchGetLicenseSchoolDomain(), + watchGetAllQuestionPaper(), + watchGetAllAssessmentList(), + watchGetQuestionPaper(), + watchCreateAssessment(), + watchGetAssessment(), + watchGetAssessmentStudentList(), + watchUpdateAssessment(), + watchDeleteAssessment(), + watchCreateAssessmentScanAnswer() ]) } diff --git a/src/sagas/questionbank.saga.tsx b/src/sagas/questionbank.saga.tsx new file mode 100644 index 0000000..00d8a04 --- /dev/null +++ b/src/sagas/questionbank.saga.tsx @@ -0,0 +1,130 @@ +import { takeEvery, call, put } from 'redux-saga/effects' +import { + GET_QUESTION_PAPER_PAGINATION_REQUEST, GET_ASSESSMENT_PAGINATION_REQUEST, + GET_QUESTION_PAPER_REQUEST, CREATE_ASSESSMENT_REQUEST, GET_ASSESSMENT_REQUEST, + UPDATE_ASSESSMENT_REQUEST, GET_ASSESSMENT_STUDENT_LIST_REQUEST, DELETE_ASSESSMENT_REQUEST, + CREATE_ASSESSMENT_SCAN_ANSWERS_REQUEST +} from '@actions/type' //NOSONAR +import { getAllQuestionPaper, getAllAssessmentList, getQuestionPaper,createAssessment, updateAssessment, getAssessment, getAssessmentStudentList, deleteAssessment, createAssessmentScanAnswer } from '@api/questionbank.api' //NOSONAR +import { ActionCreators } from '@actions' //NOSONAR + +export const watchGetAllQuestionPaper = function* () { + yield takeEvery(GET_QUESTION_PAPER_PAGINATION_REQUEST, workerGetAllQuestionPaper) +} + +function* workerGetAllQuestionPaper(action: any) { + try { + const response = yield call(getAllQuestionPaper, action.data) + yield put(ActionCreators.getQuestionPaperPaginationSuccess(response)) + } catch (e) { + yield put(ActionCreators.getQuestionPaperPaginationSuccess(e)) + } +} + +export const watchGetAllAssessmentList = function* () { + yield takeEvery(GET_ASSESSMENT_PAGINATION_REQUEST, workerGetAllAssessmentList) +} + + +function* workerGetAllAssessmentList(action: any) { + try { + const response = yield call(getAllAssessmentList, action.data) + yield put(ActionCreators.getAssessmentPaginationSuccess(response)) + } catch (e) { + yield put(ActionCreators.getAssessmentPaginationFailure(e)) + } +} + +export const watchGetQuestionPaper = function* () { + yield takeEvery(GET_QUESTION_PAPER_REQUEST, workerGetQuestionPaper) +} + +function* workerGetQuestionPaper(action: any) { + try { + const response = yield call(getQuestionPaper, action.data) + yield put(ActionCreators.getQuestionPaperSuccess(response)) + } catch (e) { + yield put(ActionCreators.getQuestionPaperFailure(e)) + } +} + +export const watchCreateAssessment = function* () { + yield takeEvery(CREATE_ASSESSMENT_REQUEST, workerCreateAssessment) +} + +function* workerCreateAssessment(action: any) { + try { + const response = yield call(createAssessment, action.data) + yield put(ActionCreators.createAssessmentSuccess(response)) + } catch (e) { + yield put(ActionCreators.createAssessmentFailure(e)) + } +} + +export const watchGetAssessment = function* () { + yield takeEvery(GET_ASSESSMENT_REQUEST, workerGetAssessment) +} + +function* workerGetAssessment(action: any) { + try { + const response = yield call(getAssessment, action.data) + yield put(ActionCreators.getAssessmentSuccess(response)) + } catch (e) { + yield put(ActionCreators.getAssessmentFailure(e)) + } +} + +export const watchGetAssessmentStudentList = function* () { + yield takeEvery(GET_ASSESSMENT_STUDENT_LIST_REQUEST, workerGetAssessmentStudentList) +} + +function* workerGetAssessmentStudentList(action: any) { + try { + const response = yield call(getAssessmentStudentList, action.data) + yield put(ActionCreators.getAssessmentStudentListSuccess(response)) + } catch (e) { + yield put(ActionCreators.getAssessmentStudentListFailure(e)) + } +} + +export const watchUpdateAssessment = function* () { + yield takeEvery(UPDATE_ASSESSMENT_REQUEST, workerUpdateAssessment) +} + +function* workerUpdateAssessment(action: any) { + try { + const response = yield call(updateAssessment, action.data) + yield put(ActionCreators.updateAssessmentSuccess(response)) + } catch (e) { + yield put(ActionCreators.updateAssessmentFailure(e)) + } +} + +export const watchDeleteAssessment = function* () { + yield takeEvery(DELETE_ASSESSMENT_REQUEST, workerDeleteAssessment) +} + +function* workerDeleteAssessment(action: any) { + try { + const response = yield call(deleteAssessment, action.data) + console.log(response) + yield put(ActionCreators.deleteAssessmentSuccess(response)) + } catch (e) { + yield put(ActionCreators.deleteAssessmentFailure(e)) + } +} + +export const watchCreateAssessmentScanAnswer = function* () { + yield takeEvery(CREATE_ASSESSMENT_SCAN_ANSWERS_REQUEST, workerCreateAssessmentScanAnswer) +} + +function* workerCreateAssessmentScanAnswer(action: any) { + try { + //console.log(action.data) + const response = yield call(createAssessmentScanAnswer, action.data) + console.log(response) + yield put(ActionCreators.createAssessmentScanAnswerSuccess(response)) + } catch (e) { + yield put(ActionCreators.createAssessmentScanAnswerFailure(e)) + } +} diff --git a/src/screens/QuestionBank/Assessment.tsx b/src/screens/QuestionBank/Assessment.tsx new file mode 100644 index 0000000..b61c6d8 --- /dev/null +++ b/src/screens/QuestionBank/Assessment.tsx @@ -0,0 +1,180 @@ +import React from "react" +import { StyleSheet, Text, View, TouchableOpacity, ActivityIndicator, Dimensions, FlatList } from 'react-native' +import { AppStyles, BaseStyles } from "@theme/BaseStyles" //NOSONAR +import AsyncStorage from "@react-native-async-storage/async-storage" +import { connect } from "react-redux" +import { ActionCreators } from "@actions" //NOSONAR +import PropTypes from 'prop-types' +import { UserRoles, currentYear, common, currentAcademicYear } from "@constants/Constants" //NOSONAR +import BaseColors from "@theme/Colors" //NOSONAR +import { ScrollView } from "react-native-gesture-handler" +import * as ScreenOrientation from 'expo-screen-orientation' +import { FontAwesome, Ionicons } from "@expo/vector-icons" + +class Assessment extends React.Component{ + static propTypes = {}; + isLoggedinAs: any; + scrollView: any; + token: any; + state: any = { + academicYear: '' + }; + + constructor(props: any) { + super(props) + } + + async componentDidMount() { + const { navigation, route } = this.props; + const title = 'Assessment'; + navigation.setOptions({ title }); + this.getCurrentAcademicYearData() //fetch the Current Academic year from Login Response + this.token = await AsyncStorage.getItem('token') + const userInfo = await AsyncStorage.getItem('userInfo') + + if (this.token) { + if (userInfo) { + const userDetails: any = JSON.parse(userInfo) + this.isLoggedinAs = userDetails.usertype + } + + const dataParams: any = this.props.route.params.data + console.log(dataParams) + this.props.getAssessmentPagination({ token: this.token, academic_year: this.state.academicYear.toString(), question_paper_id: dataParams.uid, page_no: 1 }) + + } else { + this.props.navigation.navigate('signin') + } + } + + changeScreenOrientation = async () => { + await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.LANDSCAPE) + } + + componentDidUpdate() { + } + + /** Get the curremt Academic Year */ + getCurrentAcademicYearData = () => { + currentAcademicYear().then((userInfo) => { + if (userInfo) { + const academicYear = userInfo.current_academic_year + this.setState({ academicYear: academicYear }) + } + }).catch(error => { + console.log(error) + }) + } + + async componentWillUnmount() { + await ScreenOrientation.unlockAsync() + } + + + toAssessmentEdit = (data: any, val: any,) => { + const dataParams: any = this.props.route.params.data + this.props.navigation.navigate("AssessmentEdit", { data: data, question_paper_id: dataParams.uid }) + } + + toAssessmentCreate = () => { + this.props.navigation.navigate("AssessmentCreate", { data: this.props.route.params.data }) + } + + toScanner = () => { + this.props.navigation.navigate("AssessmentScanner", { data: this.props.route.params.data }) + } + + renderRow = ({ item }: any) => ( + + + + {item?.student_name} + + + + + + {/* + View + */} + + + + ) + + render() { + return ( + + + Scan + + + {!this.props.loading && this.props.records && index.toString()} + /> + } + {this.props.loading && + + } + {this.isLoggedinAs && ((this.isLoggedinAs === UserRoles.schoolAdmin) || this.isLoggedinAs === UserRoles.teacher) && + + + } + ) + } +} + +const styles = StyleSheet.create({ + item: { + padding: 16, + backgroundColor: '#fff', + marginBottom: 8, + borderRadius: 8, + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + shadowColor: '#000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.3, + shadowRadius: 2, + elevation: 2, + }, + actions: { + flexDirection: 'row', + }, + iconButton: { + marginHorizontal: 5, + }, +}) + + +const mapStateToProps = (state: any) => ({ + loading: state.questionbank.loading, + isLoading: state.students.loading, + questionPaperList: state.questionbank.question_paper, + records: state.questionbank.assessmentrecords, +}) + +const mapDispatchToProps = (dispatch: any) => ({ + getAssessmentPagination: (user: any) => { + dispatch(ActionCreators.getAssessmentPaginationAction(user)) + }, +}) + +Assessment.propTypes = { + loading: PropTypes.bool, + error: PropTypes.object, + info: PropTypes.object, +} + + +export default connect(mapStateToProps, mapDispatchToProps)(Assessment) + + + diff --git a/src/screens/QuestionBank/AssessmentCreate.tsx b/src/screens/QuestionBank/AssessmentCreate.tsx new file mode 100644 index 0000000..0329d40 --- /dev/null +++ b/src/screens/QuestionBank/AssessmentCreate.tsx @@ -0,0 +1,379 @@ +import React from "react" +import { StyleSheet, Text, View, TouchableOpacity, ActivityIndicator, TextInput, FlatList } from 'react-native' +import { AppStyles, BaseStyles, PickerStyle } from "@theme/BaseStyles" //NOSONAR +import AsyncStorage from "@react-native-async-storage/async-storage" +import { connect } from "react-redux" +import { ActionCreators } from "@actions" //NOSONAR +import PropTypes from 'prop-types' +import { UserRoles, currentYear, common, currentAcademicYear } from "@constants/Constants" //NOSONAR +import BaseColors from "@theme/Colors" //NOSONAR +import { ScrollView } from "react-native-gesture-handler" +import * as ScreenOrientation from 'expo-screen-orientation' +import StarRating from '../../components/StarRating' +import { Formik } from 'formik' +import * as yup from 'yup' +import RNPickerSelect from "react-native-picker-select" +import { Ionicons } from "@expo/vector-icons" + +class AssessmentCreate extends React.Component{ + static propTypes = {}; + isLoggedinAs: any; + scrollView: any; + token: any; + formikActions: any; + state: any = { + academicYear: '', + question_details: [], + question_update: false, + grade_id: '', + grade_name: '', + student_list: [] + }; + + constructor(props: any) { + super(props) + } + + async componentDidMount() { + const { navigation, route } = this.props; + const title = 'Assessment Create'; + navigation.setOptions({ title }); + this.getCurrentAcademicYearData() //fetch the Current Academic year from Login Response + this.token = await AsyncStorage.getItem('token') + const userInfo = await AsyncStorage.getItem('userInfo') + + if (this.token) { + const dataParams: any = this.props.route.params.data + this.setState({ grade_id: dataParams.internal_grade_id, grade_name: dataParams.internal_grade_name }) + await this.props.getQuestionPaper({ token: this.token, academic_year: this.state.academicYear.toString(), question_paper_id: dataParams.uid, page_no: 1 }) + await this.props.getMyClassStandard({ token: this.token, grade_id: dataParams.internal_grade_id }) + + } else { + this.props.navigation.navigate('signin') + } + } + + changeScreenOrientation = async () => { + await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.LANDSCAPE) + } + + async componentDidUpdate(prevProps: any) { + if (prevProps.question_details != this.props.question_details) { + const questionRecords = this.props.question_details; + const order: Record = { + "MCQ": 1, + "FIB": 2, + "Regular-2 Marks": 3, + "Regular-5 Marks": 4, + "Regular-10 Marks": 5 + }; + const sortedQuestions = questionRecords.sort((a: any, b: any) => { + return order[a.question_type_name] - order[b.question_type_name]; + }); + + const initializedData = sortedQuestions.map((group: any) => ({ + ...group, + question: group.question.map((q: any) => ({ + ...q, + currentRating: 0, // Default rating set to 0 + })), + })); + + this.setState({ question_details: initializedData, question_update: true }) + } + if (this.props.assessmentCreate.hasOwnProperty('status')) { + if (this.props.assessmentCreate.status) { + const dataParams: any = this.props.route.params.data + this.props.getAssessmentPagination({ token: this.token, academic_year: this.state.academicYear.toString(), question_paper_id: dataParams.uid, page_no: 1 }) + this.props.navigation.navigate("Assessment", { data: this.props.route.params.data }) + } + } + else if (prevProps.assessmentStudentList != this.props.assessmentStudentList) { + const filterStudents = this.props.studentList.data.filter((student: any) => !this.props.assessmentStudentList.includes(student.student_id.toString())); + this.setState({ student_list: filterStudents }) + } + } + + /** Get the curremt Academic Year */ + getCurrentAcademicYearData = () => { + currentAcademicYear().then((userInfo) => { + if (userInfo) { + const academicYear = userInfo.current_academic_year + this.setState({ academicYear: academicYear }) + } + }).catch(error => { + console.log(error) + }) + } + + async componentWillUnmount() { + await ScreenOrientation.unlockAsync() + } + + onStarRatingPress = (rating: number, questionId: any) => { + const updatedQuestions = this.state.question_details.map((question: any) => ({ + ...question, + question: question.question.map((q: any) => { + if (q.question_id === questionId) { + return { ...q, currentRating: rating }; + } + return q; + }), + })); + this.setState({ question_details: updatedQuestions }) + } + + renderRow = (item: any) => { + return ( + + + {item?.uid} + this.onStarRatingPress(rating, item?.question_id)} + fullStarColor={'orange'} + //starSize={35} + halfStarEnabled={true} + containerStyle={styles.starRating} + /> + + + ) + } + + onAssessmentSubmit = (data: any, actions: any) => { + this.formikActions = actions + data['class_id'] = data.class_id.toString() + data['student_id'] = data.student_id.toString() + data['question_paper_id'] = this.props.route.params.data.uid + const finalData: any = {}; + this.state.question_details.forEach((group: any) => { + group.question.forEach((item: any) => { + finalData[item.uid] = item.currentRating; + }); + }); + data['assessment_details'] = finalData + this.props.createAssessment({ data, token: this.token }) + } + + onCancel = () => { + this.props.navigation.navigate("Assessment", { data: this.props.route.params.data }) + } + + getDataOptions = (data: any, action: any) => { + const options = data && data.map((item: any, index: any) => ({ + key: index, + label: action === "class" ? item.standard : item.student_name, + value: action === "class" ? item.id : item.student_id + })) + if (options && options.length >= 0) { + return options + } + return [] + } + + onSelectChange = (action: any, event: any, setFieldValue: any) => { + setFieldValue(action, event) + if (action === "class_id") { + //this.setState({ card_id: event }) + this.studentList(event) + } + } + + studentList = async (class_id: any) => { + const dataParams: any = this.props.route.params.data + await this.props.getStudentsList({ token: this.token, class_id: class_id, academic_year: this.state.academicYear.toString() }) + await this.props.getAssessmentStudentList({ token: this.token, question_paper_id: dataParams.uid, class_id: class_id, academic_year: this.state.academicYear.toString() }) + } + + render() { + const { grade_id, grade_name } = this.state + + return ( + {grade_name && this.props.class_standard?.data && + + + + this.onAssessmentSubmit(values, actions)} + > + {({ handleChange, handleBlur, handleSubmit, values, errors, touched, isValid, dirty, setFieldValue }) => ( + <> + Class * + + + + {(errors.firstname && touched.firstname) && {errors.firstname}} + Section + + + this.onSelectChange('class_id', itemValue, setFieldValue) + } + placeholder={{ + label: '--Select Section--', + value: null, + }} + useNativeAndroidPickerStyle={false} + items={this.getDataOptions(this.props.class_standard.data, 'class')} + Icon={() => } + /> + + {(errors.lastname && touched.lastname) && {errors.lastname}} + Student * + + + this.onSelectChange('student_id', itemValue, setFieldValue) + } + placeholder={{ + label: '--Select Student--', + value: null, + }} + useNativeAndroidPickerStyle={false} + items={this.getDataOptions(this.state.student_list, 'student_id')} + Icon={() => } + /> + + {(errors.phone_number && touched.phone_number) && {errors.phone_number}} + + {!this.props.loading && this.state.question_details && this.state.question_details.map((item: any, index: any) => ( + + {item.question_type_desc} + {item.marks} + {item.question.map((val: any, index: any) => ( + + {this.renderRow(val)} + + ))} + + ))} + + + + Cancel + + + + + Submit + + + + {this.props.loading && } + + )} + + {this.props.loading && + + } + + } + + + + ) + } +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + flexDirection: 'row', + alignItems: 'flex-start' // if you want to fill rows left to right + }, + item: { + padding: 16, + backgroundColor: '#fff', + marginBottom: 8, + borderRadius: 8, + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + shadowColor: '#000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.3, + shadowRadius: 2, + elevation: 2, + }, + actions: { + flexDirection: 'row', + }, + btnItem: { + width: '50%', // is 50% of whole width + }, + starRating: { + // marginRight: 20, + //width: '50%', + }, +}) + +const mapStateToProps = (state: any) => ({ + loading: state.questionbank.loading, + error: state.quizzes.error, + info: state.quizzes.info, + isLoading: state.students.loading, + questionPaperList: state.questionbank.question_paper, + question_details: state.questionbank.question_details, + class_standard: state.classes.class_standard, + studentList: state.students.student_list, + assessmentCreate: state.questionbank.assessment_create, + assessmentStudentList: state.questionbank.assessment_student_list, +}) + +const mapDispatchToProps = (dispatch: any) => ({ + initQuizzes: (user: any) => { + dispatch(ActionCreators.initQuizzesAction(user)) + }, + getQuestionPaper: (user: any) => { + dispatch(ActionCreators.getQuestionPaperAction(user)) + }, + createAssessment: (user: any) => { + dispatch(ActionCreators.createAssessmentAction(user)) + }, + getMyClassStandard: (user: any) => { + dispatch(ActionCreators.getMyClassStandardAction(user)) + }, + getStudentsList: (user: any) => { + dispatch(ActionCreators.getStudentsListAction(user)) + }, + getAssessmentStudentList: (user: any) => { + dispatch(ActionCreators.getAssessmentStudentListAction(user)) + }, + getAssessmentPagination: (user: any) => { + dispatch(ActionCreators.getAssessmentPaginationAction(user)) + }, +}) + +AssessmentCreate.propTypes = { + loading: PropTypes.bool, + error: PropTypes.object, + info: PropTypes.object, +} + + +export default connect(mapStateToProps, mapDispatchToProps)(AssessmentCreate) + + + diff --git a/src/screens/QuestionBank/AssessmentEdit.tsx b/src/screens/QuestionBank/AssessmentEdit.tsx new file mode 100644 index 0000000..4206a8b --- /dev/null +++ b/src/screens/QuestionBank/AssessmentEdit.tsx @@ -0,0 +1,397 @@ +import React from "react" +import { StyleSheet, Text, View, TouchableOpacity, ActivityIndicator, TextInput, FlatList } from 'react-native' +import { AppStyles, BaseStyles, PickerStyle } from "@theme/BaseStyles" //NOSONAR +import AsyncStorage from "@react-native-async-storage/async-storage" +import { connect } from "react-redux" +import { ActionCreators } from "@actions" //NOSONAR +import PropTypes from 'prop-types' +import { UserRoles, currentYear, common, currentAcademicYear } from "@constants/Constants" //NOSONAR +import BaseColors from "@theme/Colors" //NOSONAR +import { ScrollView } from "react-native-gesture-handler" +import * as ScreenOrientation from 'expo-screen-orientation' +import StarRating from '../../components/StarRating' +import { Formik } from 'formik' +import * as yup from 'yup' +import RNPickerSelect from "react-native-picker-select" +import { Ionicons } from "@expo/vector-icons" + +class AssessmentEdit extends React.Component{ + static propTypes = {}; + isLoggedinAs: any; + scrollView: any; + token: any; + formikActions: any; + state: any = { + academicYear: '', + question_details:[], + question_update:false, + grade_id:'', + grade_name:'', + student_list:[], + studentName:'' + }; + + constructor(props: any) { + super(props) + } + + async componentDidMount() { + const { navigation, route } = this.props; + const title = 'Assessment Edit'; + navigation.setOptions({ title }); + this.getCurrentAcademicYearData() //fetch the Current Academic year from Login Response + this.token = await AsyncStorage.getItem('token') + const userInfo = await AsyncStorage.getItem('userInfo') + if (this.token) { + await this.props.initAssessment({}) + const dataParams: any = this.props.route.params.data + this.setState({ grade_id: dataParams.internal_grade_id, grade_name:dataParams.internal_grade_name }) + await this.props.getAssessment({ token: this.token, uid: dataParams.uid}) + const question_paper_id: any = this.props.route.params.question_paper_id + await this.props.getQuestionPaper({ token: this.token, academic_year: this.state.academicYear.toString(), question_paper_id:question_paper_id,page_no:1}) + } else { + this.props.navigation.navigate('signin') + } + } + + changeScreenOrientation = async () => { + await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.LANDSCAPE) + } + + async componentDidUpdate(prevProps: any) { + + + if (prevProps.question_details != this.props.question_details) { + + const questionRecords = this.props.question_details; + const order: Record = { + "MCQ": 1, + "FIB": 2, + "Regular-2 Marks": 3, + "Regular-5 Marks": 4, + "Regular-10 Marks": 5 + }; + const sortedQuestions = questionRecords.sort((a:any, b:any) => { + return order[a.question_type_name] - order[b.question_type_name]; + }); + + const initializedData = sortedQuestions.map((group:any) => ({ + ...group, + question: group.question.map((q:any) => ({ + ...q, + currentRating: this.props.assessment.assessment_details[q.uid], // Default rating set to 0 + })), + })); + + this.setState({ question_details: initializedData, question_update:true }) + } + + if (prevProps.assessment != this.props.assessment) { + const assessment = this.props.assessment; + this.setState({ grade_name: assessment.class_name, studentName:assessment.student_name }) + } + + + + if (this.props.assessmentUpdate.hasOwnProperty('status')) { + if (this.props.assessmentUpdate.status) { + const question_paper_id: any = this.props.route.params.question_paper_id + this.props.getAssessmentPagination({ token: this.token, academic_year: this.state.academicYear.toString(), question_paper_id:question_paper_id,page_no:1}) + this.props.navigation.navigate("Assessment", { data: this.props.route.params.data }) + } + } + else if (prevProps.assessmentStudentList != this.props.assessmentStudentList) { + const filterStudents = this.props.studentList.data.filter((student:any) => !this.props.assessmentStudentList.includes(student.student_id.toString())); + this.setState({ student_list:filterStudents}) + } + + } + + /** Get the curremt Academic Year */ + getCurrentAcademicYearData = () => { + currentAcademicYear().then((userInfo) => { + if (userInfo) { + const academicYear = userInfo.current_academic_year + this.setState({ academicYear: academicYear }) + } + }).catch(error => { + console.log(error) + }) + } + + async componentWillUnmount() { + await ScreenOrientation.unlockAsync() + } + + onStarRatingPress = (rating: number, questionId:any) => { + + const updatedQuestions = this.state.question_details.map((question:any) => ({ + ...question, + question: question.question.map((q:any) => { + if (q.question_id === questionId) { + return { ...q, currentRating: rating }; + } + return q; + }), + })); + + this.setState({ question_details: updatedQuestions }) + } + + handleStarPress = (newRating:any, rowIndex:any, questionId:any) => { + const updatedRating = newRating + rowIndex * 5; + this.setState({ rating: updatedRating }); + + const updatedQuestions = this.state.question_details.map((question:any) => ({ + ...question, + question: question.question.map((q:any) => { + if (q.question_id === questionId) { + return { ...q, currentRating: updatedRating }; + } + return q; + }), + })); + + this.setState({ question_details: updatedQuestions }) + + }; + + renderStarRows = (item:any) => { + const rating = item?.currentRating; + const starRows = []; + const totalStars = item.question_type_marks; + const starsPerRow = item.question_type_marks <= 5 ? item.question_type_marks : 5; + + for (let i = 0; i < totalStars / starsPerRow; i++) { + starRows.push( + this.handleStarPress(newRating, i,item?.question_id)} + fullStarColor={'orange'} + starSize={35} // Adjust the size of each star + halfStarEnabled={true} + /> + ); + } + return starRows; + }; + + renderRow = (item: any) => { + return ( + + + {item?.uid} + {/* this.onStarRatingPress(rating, item?.question_id)} + fullStarColor={'orange'} + //starSize={35} + halfStarEnabled={true} + containerStyle={styles.starRating} + /> */} + {this.renderStarRows(item)} + + + ) +} + + onAssessmentSubmit = (data: any, actions: any) => { + + this.formikActions = actions + data['class_id'] = this.props.assessment.class_id + data['student_id'] = this.props.assessment.student_id + data['question_paper_id'] = this.props.assessment.question_paper_id + const finalData :any = {}; + this.state.question_details.forEach((group:any) => { + group.question.forEach((item:any) => { + finalData[item.uid] = item.currentRating; + }); + }); + data['assessment_details'] = finalData + this.props.updateAssessment({ data, uid:this.props.assessment.uid, token: this.token }) + } + + onCancel = () => { + this.props.navigation.navigate("Assessment", { data: this.props.route.params.data }) + } + + getDataOptions = (data:any, action: any) => { + const options = data && data.map((item: any, index: any) => ({ + key:index, + label: action === "class" ? item.standard : item.student_name, + value: action === "class" ? item.id : item.student_id + })) + if(options && options.length >= 0){ + return options + } + return [] + } + + render() { + const { grade_id , grade_name , studentName} = this.state + + return ( +{ + + + + this.onAssessmentSubmit(values, actions)} + > + {({ handleChange, handleBlur, handleSubmit, values, errors, touched, isValid, dirty, setFieldValue }) => ( + <> + Class * + + + + {(errors.firstname && touched.firstname) && {errors.firstname}} + Student + + + + { !this.props.loading && this.state.question_details && this.state.question_details.map((item: any, index: any) => ( + + {item.question_type_desc} + {item.marks} + {item.question.map((val: any, index: any) => ( + + {this.renderRow(val)} + + ))} + + + )) } + + + + Cancel + + + + + Submit + + + + {this.props.loading && } + + )} + + {this.props.loading && + + } + + } + ) + } +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + flexDirection: 'row', + alignItems: 'flex-start' // if you want to fill rows left to right + }, + item: { + padding: 16, + backgroundColor: '#fff', + marginBottom: 8, + borderRadius: 8, + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + shadowColor: '#000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.3, + shadowRadius: 2, + elevation: 2, + }, + actions: { + flexDirection: 'row', + }, + btnItem: { + width: '50%', // is 50% of whole width + }, + starRating: { + // marginRight: 20, + //width: '50%', + }, +}) + + +const mapStateToProps = (state: any) => ({ + loading: state.questionbank.loading, + error: state.quizzes.error, + info: state.quizzes.info, + isLoading: state.students.loading, + questionPaperList: state.questionbank.question_paper, + question_details: state.questionbank.question_details, + class_standard: state.classes.class_standard, + studentList: state.students.student_list, + assessmentUpdate: state.questionbank.assessment_update, + assessmentStudentList:state.questionbank.assessment_student_list, + assessment:state.questionbank.assessment, +}) + +const mapDispatchToProps = (dispatch: any) => ({ + initAssessment: () => { + dispatch(ActionCreators.initAssessmentAction()) + }, + getQuestionPaper: (user: any) => { + dispatch(ActionCreators.getQuestionPaperAction(user)) + }, + updateAssessment: (user: any) => { + dispatch(ActionCreators.updateAssessmentAction(user)) + }, + getMyClassStandard: (user: any) => { + dispatch(ActionCreators.getMyClassStandardAction(user)) + }, + getStudentsList: (user: any) => { + dispatch(ActionCreators.getStudentsListAction(user)) + }, + getAssessmentStudentList: (user: any) => { + dispatch(ActionCreators.getAssessmentStudentListAction(user)) + }, + getAssessment: (user: any) => { + dispatch(ActionCreators.getAssessmentAction(user)) + }, + getAssessmentPagination: (user: any) => { + dispatch(ActionCreators.getAssessmentPaginationAction(user)) +}, +}) + +AssessmentEdit.propTypes = { + loading: PropTypes.bool, + error: PropTypes.object, + info: PropTypes.object, +} + + +export default connect(mapStateToProps, mapDispatchToProps)(AssessmentEdit) + + + diff --git a/src/screens/QuestionBank/AssessmentScanner.tsx b/src/screens/QuestionBank/AssessmentScanner.tsx new file mode 100644 index 0000000..97b8a5c --- /dev/null +++ b/src/screens/QuestionBank/AssessmentScanner.tsx @@ -0,0 +1,277 @@ +import React, { Component } from 'react'; +import { Alert, Image, ImageStyle, StyleSheet, View, ViewStyle, TouchableOpacity, Text } from 'react-native'; +import DocumentScanner from 'react-native-document-scanner-plugin'; +import AsyncStorage from "@react-native-async-storage/async-storage" +import { connect } from "react-redux" +import { ActionCreators } from "@actions" //NOSONAR +import PropTypes from 'prop-types' +import { AppStyles, BaseStyles, PickerStyle } from "@theme/BaseStyles" //NOSONAR +import RNPickerSelect from "react-native-picker-select" +import { Ionicons } from "@expo/vector-icons" +import RNFS from 'react-native-fs'; + +interface State { + scannedImage: string | null; +} + +class AssessmentScanner extends React.Component { + static propTypes = {}; + isLoggedinAs: any; + scrollView: any; + token: any; + formikActions: any; + + state: any = { + scannedImage: null, + class_id: '', + }; + + + constructor(props: any) { + super(props) + } + + + async componentDidMount() { + + const { navigation, route } = this.props; + const title = 'Assessment Scanner'; + navigation.setOptions({ title }); + this.token = await AsyncStorage.getItem('token') + const userInfo = await AsyncStorage.getItem('userInfo') + + if (this.token) { + const dataParams: any = this.props.route.params.data + this.setState({ grade_id: dataParams.internal_grade_id, grade_name: dataParams.internal_grade_name }) + await this.props.getMyClassStandard({ token: this.token, grade_id: dataParams.internal_grade_id }) + + } else { + this.props.navigation.navigate('signin') + } + } + + async componentDidUpdate(prevProps: any) { + + console.log('assessmentAnswer') + console.log(this.props.assessmentAnswer) + + if (this.props.assessmentAnswer.hasOwnProperty('data')) { + const result_table_data = this.props.assessmentAnswer?.data?.result_table_data + await this.props.initAssessment({}) + Alert.alert('Required', JSON.stringify(result_table_data)) + } + + if (this.props.assessmentAnswer.hasOwnProperty('message')) { + const message = this.props.assessmentAnswer?.message + await this.props.initAssessment({}) + Alert.alert('Required', JSON.stringify(message)) + } + + if (this.props.assessmentAnswer.hasOwnProperty('something_wrong')) { + const message = this.props.assessmentAnswer?.something_wrong + await this.props.initAssessment({}) + Alert.alert('Required', JSON.stringify(message)) + } + + } + + scanDocument = async () => { + const result = await DocumentScanner.scanDocument(); + const scannedImages = await result.scannedImages; + + if (scannedImages && scannedImages.length > 0) { + console.log("scannedImages") + console.log(scannedImages) + + + // const fileInfo = await RNFS.stat(scannedImages[0]); + + // // Convert file size to MB + // const fileSizeInMB = fileInfo.size / (1024 * 1024); + + // // Print the file size in MB + // console.log(`File size: ${fileSizeInMB.toFixed(2)} MB`); + + const formData: any = new FormData() + formData.append("question_paper_id", this.props.route.params.data.uid) + formData.append("class_id", this.state.class_id.toString()) + + + + // const filePath = scannedImages[0]; // Get the URI of the first scanned image + // const fileName = filePath.split('/').pop(); // Extract the file name + + // const destinationPath = `${RNFS.DocumentDirectoryPath}/ScannedDocs/${fileName}`; // Destination folder path + + // // Check if the source file exists + // const sourceExists = await RNFS.exists(filePath); + // if (!sourceExists) { + // console.error('Source file does not exist at:', filePath); + // return; + // } + + // // Ensure the destination folder exists + // await RNFS.mkdir(`${RNFS.DocumentDirectoryPath}/ScannedDocs`); + + // // Move the file to the new folder + // await RNFS.moveFile(filePath, destinationPath); + + // // Check if the file was moved successfully + // const destinationExists = await RNFS.exists(destinationPath); + // if (destinationExists) { + // console.log('File successfully moved to:', destinationPath); + // } else { + // console.error('File move failed.'); + // } + + // formData.append('file', { + // uri: scannedImages[0], + // type: 'image/jpeg', + // name: 'scanned_image.jpg', + // }); + + const destinationExists = await RNFS.exists(scannedImages[0]); + if (destinationExists) { + console.log('File successfully moved to:', scannedImages[0]); + } else { + console.error('File move failed.'); + } + + // try { + // const response = await fetch('https://beta-api.wizdomwaves.in/api/questionbank-module/assessment-answers', { + // method: 'POST', + // body: formData, + // headers: {'Content-Type': 'multipart/form-data', 'Authorization': this.token}, + // }); + + // if (!response.ok) { + // console.log(response) + // throw new Error(`HTTP error! Status: ${response.status}`); + // } + + // const data = await response.json(); + // console.log('Success:', data); + // } catch (error) { + // console.error('Error:', error); + // } + + + + await this.props.createAssessmentScanAnswer({ data: formData, token: this.token }) + await this.setState({ scannedImage: scannedImages[0] }); + } + } + + onSelectChange = async (id: any) => { + if (id) { + this.setState({ class_id: id }) + } else { + this.setState({ class_id: '' }) + } + } + + getDataOptions = (data: any) => { + const options = data && data.map((item: any, index: any) => ({ + key: index, + label: item.standard, + value: item.id + })) + if (options && options.length >= 0) { + return options + } + return [] + } + + toScan = async () => { + await this.scanDocument(); + } + render() { + const { scannedImage } = this.state; + + return ( + + {scannedImage ? ( + + ) : ( + + + + this.onSelectChange(itemValue) + } + placeholder={{ + label: '--Select Section--', + value: null, + }} + useNativeAndroidPickerStyle={false} + items={this.getDataOptions(this.props.class_standard.data)} + Icon={() => } + /> + + + Scan + + + + )} + + ); + } +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + //justifyContent: 'center', + // alignItems: 'center', + } as ViewStyle, + image: { + width: '100%', + height: '100%', + } as ImageStyle, + placeholder: { + width: '100%', + height: '100%', + justifyContent: 'center', + alignItems: 'center', + } as ViewStyle, +}); + +const mapStateToProps = (state: any) => ({ + loading: state.questionbank.loading, + isLoading: state.students.loading, + questionPaperList: state.questionbank.question_paper, + records: state.questionbank.assessmentrecords, + class_standard: state.classes.class_standard, + assessmentAnswer: state.questionbank.assessment_answer, +}) + +const mapDispatchToProps = (dispatch: any) => ({ + initAssessment: () => { + dispatch(ActionCreators.initAssessmentAction()) + }, + getAssessmentPagination: (user: any) => { + dispatch(ActionCreators.getAssessmentPaginationAction(user)) + }, + getMyClassStandard: (user: any) => { + dispatch(ActionCreators.getMyClassStandardAction(user)) + }, + createAssessmentScanAnswer: (user: any) => { + dispatch(ActionCreators.createAssessmentScanAnswerAction(user)) + } +}) + +AssessmentScanner.propTypes = { + loading: PropTypes.bool, + error: PropTypes.object, + info: PropTypes.object, +} + + +export default connect(mapStateToProps, mapDispatchToProps)(AssessmentScanner) \ No newline at end of file diff --git a/src/screens/QuestionBank/QuestionBank.tsx b/src/screens/QuestionBank/QuestionBank.tsx new file mode 100644 index 0000000..787ecf2 --- /dev/null +++ b/src/screens/QuestionBank/QuestionBank.tsx @@ -0,0 +1,165 @@ +import React from "react" +import { StyleSheet, Text, View, TouchableOpacity, ActivityIndicator, Dimensions, FlatList } from 'react-native' +import { AppStyles, BaseStyles } from "@theme/BaseStyles" //NOSONAR +import AsyncStorage from "@react-native-async-storage/async-storage" +import { connect } from "react-redux" +import { ActionCreators } from "@actions" //NOSONAR +import PropTypes from 'prop-types' +import { UserRoles, currentYear, common, currentAcademicYear } from "@constants/Constants" //NOSONAR +import BaseColors from "@theme/Colors" //NOSONAR +import { ScrollView } from "react-native-gesture-handler" +import * as ScreenOrientation from 'expo-screen-orientation' +import { FontAwesome } from "@expo/vector-icons" +import Toast from 'react-native-simple-toast' + +const Sepator = ({ style }: any) => +const { width } = Dimensions.get('window') + + +class QuestionBank extends React.Component{ + // static navigationOptions = ({ navigation }: any) => ({ + // headerTitle: navigation.state.params?.data?.question_name || 'Quizzes', + // }); + static propTypes = {}; + isLoggedinAs: any; + scrollView: any; + token: any; + state: any = { + academicYear: '' + }; + + constructor(props: any) { + super(props) + } + + async componentDidMount() { + const { navigation, route } = this.props; + const title = 'Question Bank'; + navigation.setOptions({ title }); + this.getCurrentAcademicYearData() //fetch the Current Academic year from Login Response + this.token = await AsyncStorage.getItem('token') + const userInfo = await AsyncStorage.getItem('userInfo') + + if (this.token) { + //const dataParams: any = this.props.route.params.data + this.props.getQuestionPaperList({ token: this.token, academic_year: this.state.academicYear.toString(), page_no: 1 }) + + } else { + this.props.navigation.navigate('signin') + } + } + + changeScreenOrientation = async () => { + await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.LANDSCAPE) + } + + componentDidUpdate() { + } + + /** Get the curremt Academic Year */ + getCurrentAcademicYearData = () => { + currentAcademicYear().then((userInfo) => { + if (userInfo) { + const academicYear = userInfo.current_academic_year + this.setState({ academicYear: academicYear }) + } + }).catch(error => { + console.log(error) + }) + } + + async componentWillUnmount() { + await ScreenOrientation.unlockAsync() + } + + + toAssessmentScreen = (data: any, val: any) => { + this.props.navigation.navigate("Assessment", { data: data }) + } + + renderRow = ({ item }: any) => ( + + {/* + + {item?.question_paper_name} + + + + View + + + */} + + {item?.question_paper_name} + + + ) + + render() { + return ( + {!this.props.loading && this.props.records && index.toString()} + /> + } + + + {this.props.loading && + + } + + ) + } +} + +const styles = StyleSheet.create({ + item: { + padding: 16, + backgroundColor: '#fff', + marginBottom: 8, + borderRadius: 8, + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + shadowColor: '#000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.3, + shadowRadius: 2, + elevation: 2, + }, +}) + + +const mapStateToProps = (state: any) => ({ + loading: state.psychometric.loading, + error: state.quizzes.error, + info: state.quizzes.info, + isLoading: state.students.loading, + questionPaperList: state.questionbank.question_paper, + records: state.questionbank.records, +}) + +const mapDispatchToProps = (dispatch: any) => ({ + initQuizzes: (user: any) => { + dispatch(ActionCreators.initQuizzesAction(user)) + }, + getQuestionPaperList: (user: any) => { + dispatch(ActionCreators.getQuestionPaperPaginationAction(user)) + }, + +}) + +QuestionBank.propTypes = { + loading: PropTypes.bool, + error: PropTypes.object, + info: PropTypes.object, +} + + +export default connect(mapStateToProps, mapDispatchToProps)(QuestionBank) + + + diff --git a/yarn.lock b/yarn.lock index a73710e..9f95213 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3921,6 +3921,11 @@ balanced-match@^1.0.0: resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base-64@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz" + integrity sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA== + base64-js@^1.2.3, base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" @@ -5519,10 +5524,10 @@ expo-asset@~9.0.2: invariant "^2.2.4" md5-file "^3.2.3" -expo-camera@^15.0.14: - version "15.0.14" - resolved "https://registry.npmjs.org/expo-camera/-/expo-camera-15.0.14.tgz" - integrity sha512-O4uvVywGsQ3a89d0BX4lq6mDuGYGukx1PYY4QrC9zw1yzD2W9BVTl8lanFRPC5h4PRniekfeWUVH1a0jJmkLIw== +expo-camera@^14.1.3: + version "14.1.3" + resolved "https://registry.npmjs.org/expo-camera/-/expo-camera-14.1.3.tgz" + integrity sha512-JodpVjOY8JDuSp/RkphS8Bxqaj/gwg0h0UbQB9MLr1LoxbL9brvJt7IZnmTf7+ON8jRKUx9E5o/F02pRNbmSbQ== dependencies: invariant "^2.2.4" @@ -9077,10 +9082,15 @@ react-native-charts-wrapper@^0.6.0: prop-types "^15.8.1" react "16.13.1" -react-native-document-scanner-plugin@^0.9.1: - version "0.9.1" - resolved "https://registry.npmjs.org/react-native-document-scanner-plugin/-/react-native-document-scanner-plugin-0.9.1.tgz" - integrity sha512-iHqd/ZEHmKnPsA0X2DoSZ7B324WSDQO45I22busHHfc2vSRMHozP9fcIP34YTuNvfbvjJFhziCIw66YcGWLotA== +react-native-document-scanner-plugin@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/react-native-document-scanner-plugin/-/react-native-document-scanner-plugin-1.0.0.tgz" + integrity sha512-dDsSclFpxl4U0Bnz/oEE0OEEqEjA6zNCu/2lmls4lslq3JqgHacI8xEl5DygMHd4AlDj3X4g1yKUacllfaC+UA== + +react-native-fetch-polyfill@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/react-native-fetch-polyfill/-/react-native-fetch-polyfill-1.1.3.tgz" + integrity sha512-zr5yXQftuGq+ABGa3n4ZE+vkL1lBsMSePkRINm3/6vlpbwnLXYoijwazTO/W8GjsV4LAgGmzuieZxKO/NxW19A== react-native-flash-message@^0.4.2: version "0.4.2" @@ -9090,6 +9100,14 @@ react-native-flash-message@^0.4.2: prop-types "^15.8.1" react-native-iphone-screen-helper "^2.0.2" +react-native-fs@^2.20.0: + version "2.20.0" + resolved "https://registry.npmjs.org/react-native-fs/-/react-native-fs-2.20.0.tgz" + integrity sha512-VkTBzs7fIDUiy/XajOSNk0XazFE9l+QlMAce7lGuebZcag5CnjszB+u4BdqzwaQOdcYb5wsJIsqq4kxInIRpJQ== + dependencies: + base-64 "^0.1.0" + utf8 "^3.0.0" + react-native-gesture-handler@^2.15.0, "react-native-gesture-handler@>= 1.0.0", "react-native-gesture-handler@>= 1.5.0": version "2.15.0" resolved "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.15.0.tgz" @@ -9292,7 +9310,7 @@ react-native-webview@^13.8.1, "react-native-webview@>= 10.9.0": escape-string-regexp "2.0.0" invariant "2.2.4" -react-native@*, react-native@^*, "react-native@^0.0.0-0 || >=0.60 <1.0", "react-native@>= 0.30.0", "react-native@>= 0.50.0", "react-native@>= 0.60.0", "react-native@>= 0.64.3", react-native@>=0.42.0, react-native@>=0.56, react-native@>=0.57, react-native@>=0.59, react-native@>=0.61, react-native@>=0.61.3, react-native@>=0.61.5, react-native@>=0.65.0, react-native@>=0.69, react-native@>=0.71.0, react-native@>0.57.0, react-native@0.73.5: +react-native@*, react-native@^*, "react-native@^0.0.0-0 || >=0.60 <1.0", "react-native@>= 0.30.0", "react-native@>= 0.50.0", "react-native@>= 0.60.0", "react-native@>= 0.64.3", react-native@>=0.27, react-native@>=0.42.0, react-native@>=0.56, react-native@>=0.57, react-native@>=0.59, react-native@>=0.61, react-native@>=0.61.3, react-native@>=0.61.5, react-native@>=0.65.0, react-native@>=0.69, react-native@>=0.71.0, react-native@>0.57.0, react-native@0.73.5: version "0.73.5" resolved "https://registry.npmjs.org/react-native/-/react-native-0.73.5.tgz" integrity sha512-iHgDArmF4CrhL0qTj+Rn+CBN5pZWUL9lUGl8ub+V9Hwu/vnzQQh8rTMVSwVd2sV6N76KjpE5a4TfIAHkpIHhKg== @@ -10874,6 +10892,11 @@ use-sync-external-store@^1.0.0: resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== +utf8@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== + util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" @@ -11317,6 +11340,11 @@ websocket-extensions@>=0.1.1: resolved "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz" integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== +whatwg-fetch-timeout@^2.0.2-timeout: + version "2.0.2-timeout" + resolved "https://registry.npmjs.org/whatwg-fetch-timeout/-/whatwg-fetch-timeout-2.0.2-timeout.tgz" + integrity sha512-1y8WJdP1DaBXbAp9Ez8Um2Ft7iTVopOwUgnyiJPYIsWir1HuJBBxSYdvEtskQx+r/L1WMWQQxbcS8lHubCHtBw== + whatwg-fetch@^3.0.0: version "3.6.20" resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz"