From 176b3a786bb2bb83ac2de1a9ce8114dcf28cd45f Mon Sep 17 00:00:00 2001 From: "QSL\\SumathiT" Date: Mon, 25 Nov 2024 11:27:51 -0800 Subject: [PATCH 1/5] Edit Registrations. --- backend/src/components/cache-service.js | 7 +- backend/src/components/eas/eas.js | 98 +++- backend/src/routes/eas.js | 8 +- backend/src/validations/eas.js | 34 ++ frontend/src/common/apiService.js | 4 + .../assessments/AssessmentSessionDetail.vue | 7 +- .../StudentRegistrationDetail.vue | 106 ++++ .../registrations/StudentRegistrations.vue | 54 +- .../StudentRegistrationsCustomTable.vue | 23 +- .../StudentRegistrationsFilter.vue | 87 +++- .../forms/EditStudentRegistration.vue | 493 ++++++++++++++++++ frontend/src/store/modules/eas.js | 23 + frontend/src/utils/constants.js | 2 + .../StudentRegistrationTableConfiguration.js | 79 +-- 14 files changed, 878 insertions(+), 147 deletions(-) create mode 100644 backend/src/validations/eas.js create mode 100644 frontend/src/components/assessments/registrations/StudentRegistrationDetail.vue create mode 100644 frontend/src/components/assessments/registrations/forms/EditStudentRegistration.vue create mode 100644 frontend/src/store/modules/eas.js diff --git a/backend/src/components/cache-service.js b/backend/src/components/cache-service.js index 1d38650b..0e652c0b 100644 --- a/backend/src/components/cache-service.js +++ b/backend/src/components/cache-service.js @@ -112,7 +112,7 @@ const cacheService = { if (assessmentTypeCodesResponse && assessmentTypeCodesResponse.length > 0) { assessmentTypeCodesMap.clear(); assessmentTypeCodesResponse.forEach(entry => { - assessmentTypeCodesMap.set(entry.assessmentTypeCode, entry.label); + assessmentTypeCodesMap.set(entry.assessmentTypeCode, {'label': entry.label, 'displayOrder': entry.displayOrder }); }); } log.info(`Loaded ${assessmentTypeCodesMap.size} assessmentTypeCodes.`); @@ -384,12 +384,15 @@ const cacheService = { getGradDataCollectionValidationIssueCodes() { return cachedData[constants.CACHE_KEYS.GDC_VALIDATION_ISSUE_TYPE_CODES].records; }, - getAssessmentTypeLabelByCode(assessmentTypeCode) { + getAssessmentTypeByCode(assessmentTypeCode) { return assessmentTypeCodesMap.get(assessmentTypeCode); }, getSpecialCaseTypeLabelByCode(specialCaseTypeCode) { return assessmentSpecialCaseTypeCodesMap.get(specialCaseTypeCode); }, + getAllAssessmentSpecialCases(){ + return assessmentSpecialCaseTypeCodesMap; + }, }; module.exports = cacheService; diff --git a/backend/src/components/eas/eas.js b/backend/src/components/eas/eas.js index 33273e9b..82335283 100644 --- a/backend/src/components/eas/eas.js +++ b/backend/src/components/eas/eas.js @@ -1,5 +1,5 @@ 'use strict'; -const { logApiError, getAccessToken, getData, getDataWithParams, errorResponse, handleExceptionResponse } = require('../utils'); +const { logApiError, getAccessToken, getData, putData, getCreateOrUpdateUserValue, getDataWithParams, errorResponse, handleExceptionResponse } = require('../utils'); const HttpStatus = require('http-status-codes'); const config = require('../../config'); const cacheService = require('../cache-service'); @@ -39,8 +39,12 @@ async function getAssessmentSessionsBySchoolYear(req, res) { const token = getAccessToken(req); let data = await getData(token, url); data.forEach(session => { + session.isOpen = new Date(session.activeFromDate) <= new Date() && new Date(session.activeUntilDate) >= new Date(); session.assessments.forEach(assessment => { - assessment.assessmentTypeName = cacheService.getAssessmentTypeLabelByCode(assessment.assessmentTypeCode)+' ('+assessment.assessmentTypeCode+')'; + let assessmentType = cacheService.getAssessmentTypeByCode(assessment.assessmentTypeCode); + assessment.assessmentTypeName = assessmentType.label+' ('+assessment.assessmentTypeCode+')'; + assessment.displayOrder = assessmentType.displayOrder; + }); }); return res.status(200).json(data); @@ -78,22 +82,7 @@ async function getAssessmentStudentsPaginated(req, res) { return res.status(HttpStatus.OK).json(result); } data?.content.forEach(value => { - let school = cacheService.getSchoolBySchoolID(value.schoolID); - let assessmentCenter = cacheService.getSchoolBySchoolID(value.assessmentCenterID); - let district = cacheService.getDistrictJSONByDistrictID(school.districtID); - - value.schoolNumber = school.mincode; - value.schoolName = getSchoolName(school); - value.districtID = school.districtID; - value.districtNumber = district.districtNumber; - value.districtName = getDistrictName(district); - value.assessmentCenterNumber = assessmentCenter.mincode; - value.assessmentCenterName = getSchoolName(assessmentCenter); - - value.assessmentTypeName = cacheService.getAssessmentTypeLabelByCode(value.assessmentTypeCode)+' ('+value.assessmentTypeCode+')'; - value.provincialSpecialCaseName = value.provincialSpecialCaseName ? cacheService.getSpecialCaseTypeLabelByCode(value.provincialSpecialCaseCode) : '-'; - value.sessionName = moment(value.courseMonth, 'MM').format('MMMM') +' '+value.courseYear; - + includeAssessmentStudentProps(value); }); return res.status(200).json(data); } catch (e) { @@ -106,6 +95,64 @@ async function getAssessmentStudentsPaginated(req, res) { } } +async function getAssessmentStudentByID(req, res) { + try { + const token = getAccessToken(req); + let data = await getData(token, `${config.get('eas:assessmentStudentsURL')}/${req.params.assessmentStudentID}`); + return res.status(200).json(includeAssessmentStudentProps(data)); + } catch (e) { + if (e?.status === 404) { + res.status(HttpStatus.OK).json(null); + } else { + await logApiError(e, 'Error getting eas assessment student'); + return errorResponse(res); + } + } +} + +function includeAssessmentStudentProps(assessmentStudent) { + if(assessmentStudent) { + let school = cacheService.getSchoolBySchoolID(assessmentStudent.schoolID); + let assessmentCenter = cacheService.getSchoolBySchoolID(assessmentStudent.assessmentCenterID); + let district = cacheService.getDistrictJSONByDistrictID(school.districtID); + + if(school && district) { + assessmentStudent.schoolName_desc = getSchoolName(school); + assessmentStudent.districtID = school.districtID; + assessmentStudent.districtName_desc = getDistrictName(district); + } + + if(school && district) { + assessmentStudent.assessmentCenterName_desc = getSchoolName(assessmentCenter); + } + + assessmentStudent.assessmentTypeName_desc = cacheService.getAssessmentTypeByCode(assessmentStudent.assessmentTypeCode).label+' ('+assessmentStudent.assessmentTypeCode+')'; + assessmentStudent.provincialSpecialCaseName_desc = cacheService.getSpecialCaseTypeLabelByCode(assessmentStudent.provincialSpecialCaseCode); + assessmentStudent.sessionName_desc = moment(assessmentStudent.courseMonth, 'MM').format('MMMM') +' '+assessmentStudent.courseYear; + } + return assessmentStudent; +} + +async function updateAssessmentStudentByID(req, res) { + if (req.params.assessmentStudentID !== req.body.assessmentStudentID) { + return res.status(HttpStatus.BAD_REQUEST).json({ + message: 'The assessmentStudentID in the URL didn\'t match the assessmentStudentID in the request body.' + }); + } + try { + const token = getAccessToken(req); + const payload = req.body; + payload.updateUser = getCreateOrUpdateUserValue(req); + payload.updateDate = null; + payload.createDate = null; + const result = await putData(token, payload, `${config.get('eas:assessmentStudentsURL')}/${req.params.assessmentStudentID}`, getCreateOrUpdateUserValue(req)); + return res.status(HttpStatus.OK).json(result); + } catch (e) { + logApiError(e, 'updateAssessmentStudent', 'Error occurred while attempting to save the changes to the assessment student registration.'); + return errorResponse(res); + } +} + function getSchoolName(school) { return school.mincode + ' - ' + school.schoolName; } @@ -114,9 +161,22 @@ function getDistrictName(district) { return district.districtNumber + ' - ' + district.name; } +function getAssessmentSpecialCases(req, res) { + try { + const codes = cacheService.getAllAssessmentSpecialCases(); + return res.status(HttpStatus.OK).json(Object.fromEntries(codes)); + } catch (e) { + logApiError(e, 'getAssessmentSpecialCases', 'Error occurred while attempting to get specialcase types.'); + return errorResponse(res); + } +} + module.exports = { getAssessmentSessions, getActiveAssessmentSessions, getAssessmentSessionsBySchoolYear, - getAssessmentStudentsPaginated + getAssessmentStudentsPaginated, + getAssessmentStudentByID, + updateAssessmentStudentByID, + getAssessmentSpecialCases }; diff --git a/backend/src/routes/eas.js b/backend/src/routes/eas.js index bbfc05ff..6c468a1e 100644 --- a/backend/src/routes/eas.js +++ b/backend/src/routes/eas.js @@ -1,18 +1,22 @@ const passport = require('passport'); const express = require('express'); const router = express.Router(); -const { getAssessmentSessions, getActiveAssessmentSessions, getAssessmentSessionsBySchoolYear, getAssessmentStudentsPaginated } = require('../components/eas/eas'); +const { getAssessmentSessions, getActiveAssessmentSessions, getAssessmentSessionsBySchoolYear, getAssessmentStudentsPaginated, getAssessmentStudentByID, updateAssessmentStudentByID, getAssessmentSpecialCases } = require('../components/eas/eas'); const auth = require('../components/auth'); const isValidBackendToken = auth.isValidBackendToken(); const { validateAccessToken, findInstituteType_params, checkPermissionForRequestedInstitute } = require('../components/permissionUtils'); const { PERMISSION } = require('../util/Permission'); - +const validate = require('../components/validator'); +const {putStudentAssessmentSchema} = require('../validations/eas'); router.get('/assessment-sessions/:instituteType', passport.authenticate('jwt', {session: false}, undefined), isValidBackendToken, validateAccessToken, findInstituteType_params, checkPermissionForRequestedInstitute(PERMISSION.EAS_DIS_EDIT, PERMISSION.EAS_SCH_EDIT), getAssessmentSessions); router.get('/assessment-sessions/active/:instituteType', passport.authenticate('jwt', {session: false}, undefined), isValidBackendToken, validateAccessToken, findInstituteType_params, checkPermissionForRequestedInstitute(PERMISSION.EAS_DIS_EDIT, PERMISSION.EAS_SCH_EDIT), getActiveAssessmentSessions); router.get('/assessment-sessions/school-year/:schoolYear/:instituteType', passport.authenticate('jwt', {session: false}, undefined), isValidBackendToken, validateAccessToken, findInstituteType_params, checkPermissionForRequestedInstitute(PERMISSION.EAS_DIS_EDIT, PERMISSION.EAS_SCH_EDIT), getAssessmentSessionsBySchoolYear); +router.get('/assessment-registrations/student/:instituteType/:assessmentStudentID', passport.authenticate('jwt', {session: false}, undefined), isValidBackendToken, validateAccessToken, findInstituteType_params, checkPermissionForRequestedInstitute(PERMISSION.EAS_DIS_EDIT, PERMISSION.EAS_SCH_EDIT), getAssessmentStudentByID); +router.put('/assessment-registrations/student/:instituteType/:assessmentStudentID', passport.authenticate('jwt', {session: false}, undefined), isValidBackendToken, validateAccessToken, findInstituteType_params, checkPermissionForRequestedInstitute(PERMISSION.EAS_DIS_EDIT, PERMISSION.EAS_SCH_EDIT), validate(putStudentAssessmentSchema), updateAssessmentStudentByID); router.get('/assessment-registrations/paginated/:instituteType', passport.authenticate('jwt', {session: false}, undefined), isValidBackendToken, validateAccessToken, findInstituteType_params, checkPermissionForRequestedInstitute(PERMISSION.EAS_DIS_EDIT, PERMISSION.EAS_SCH_EDIT), getAssessmentStudentsPaginated); +router.get('/assessment-specialcase-types', passport.authenticate('jwt', {session: false}, undefined), isValidBackendToken, validateAccessToken, getAssessmentSpecialCases); module.exports = router; diff --git a/backend/src/validations/eas.js b/backend/src/validations/eas.js new file mode 100644 index 00000000..7ce498de --- /dev/null +++ b/backend/src/validations/eas.js @@ -0,0 +1,34 @@ +const { object, string, boolean, number } = require('yup'); +const { baseRequestSchema } = require('./base'); + +const putStudentAssessmentSchema = object({ + body: object({ + assessmentStudentID: string().nonNullable(), + sessionID:string().nonNullable(), + districtID: string().nonNullable(), + schoolID: string().nonNullable(), + assessmentCenterID: string().nonNullable(), + assessmentID:string().nonNullable(), + assessmentTypeCode: string().nonNullable(), + studentID: string().nonNullable(), + pen: string().max(9).nonNullable(), + localID: string().max(12).nonNullable(), + givenName: string().max(25).nonNullable(), + surName: string().max(25).nonNullable(), + isElectronicExam: boolean().nonNullable(), + proficiencyScore: number().nonNullable(), + provincialSpecialCaseCode: string().max(1).nonNullable().optional(), + courseStatusCode: string().max(1).nonNullable().optional(), + numberOfAttempts: number().nonNullable(), + courseMonth: number().optional(), + courseYear: number().optional(), + }).concat(baseRequestSchema).noUnknown(), + params: object({ + studentAssessmentID: string().nonNullable(), + }), + query: object().noUnknown(), +}).noUnknown(); + +module.exports = { + putStudentAssessmentSchema, +}; diff --git a/frontend/src/common/apiService.js b/frontend/src/common/apiService.js index 5dfd0af1..e03e045f 100644 --- a/frontend/src/common/apiService.js +++ b/frontend/src/common/apiService.js @@ -181,6 +181,10 @@ export default { getAllActiveSchoolFundingCodes:getCodes(`${ApiRoutes.sdc.SDC_SCHOOL_FUNDING_CODES}?active=true`), getAllActiveSpecialEdCodes:getCodes(`${ApiRoutes.sdc.SDC_SPECIAL_ED_CODES}?active=true`), getAllCollectionTypeCodes: getCodes(`${ApiRoutes.sdc.COLLECTION_TYPE_CODES_URL}`), + + getAllEASSpecialCaseCodes:getCodes(`${ApiRoutes.eas.GET_ASSESSMENT_SPECIALCASE_TYPES}`), + + }; function getCodes(url) { return async function getCodesHandler(query) { diff --git a/frontend/src/components/assessments/AssessmentSessionDetail.vue b/frontend/src/components/assessments/AssessmentSessionDetail.vue index cb88811e..e9e2a6e4 100644 --- a/frontend/src/components/assessments/AssessmentSessionDetail.vue +++ b/frontend/src/components/assessments/AssessmentSessionDetail.vue @@ -44,8 +44,8 @@ import StudentRegistrations from './registrations/StudentRegistrations.vue'; import ApiService from '../../common/apiService'; import { ApiRoutes } from '../../utils/constants'; import { mapState } from 'pinia'; -import Spinner from "../common/Spinner.vue"; -import {authStore} from "../../store/modules/auth"; +import Spinner from '../common/Spinner.vue'; +import {authStore} from '../../store/modules/auth'; export default { name: 'AssessmentSessionDetail', @@ -87,8 +87,7 @@ export default { .get( `${ApiRoutes.eas.GET_ASSESSMENT_SESSIONS}/school-year/` + this.schoolYear + '/' + this.userInfo.activeInstituteType, {} - ) - .then((response) => { + ).then((response) => { this.schoolYearSessions = response.data; }) .catch((error) => { diff --git a/frontend/src/components/assessments/registrations/StudentRegistrationDetail.vue b/frontend/src/components/assessments/registrations/StudentRegistrationDetail.vue new file mode 100644 index 00000000..b4d8c312 --- /dev/null +++ b/frontend/src/components/assessments/registrations/StudentRegistrationDetail.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/frontend/src/components/assessments/registrations/StudentRegistrations.vue b/frontend/src/components/assessments/registrations/StudentRegistrations.vue index 18db9f73..ca2354a3 100644 --- a/frontend/src/components/assessments/registrations/StudentRegistrations.vue +++ b/frontend/src/components/assessments/registrations/StudentRegistrations.vue @@ -40,6 +40,8 @@ :reset="resetFlag" :can-load-next="canLoadNext" :can-load-previous="canLoadPrevious" + @reload-registrations="reload" + @editSelectedRow="editRegistration" @loadNext="loadNext" @loadPrevious="loadPrevious" /> @@ -67,6 +69,21 @@ /> + + + @@ -75,17 +92,19 @@ import StudentRegistrationsCustomTable from './StudentRegistrationsCustomTable.v import { cloneDeep, isEmpty, omitBy } from 'lodash'; import StudentRegistrationsFilter from './StudentRegistrationsFilter.vue'; import moment from 'moment'; -import {SCHOOL_YEAR_REGISTRATIONS_VIEW_DISTRICT, SCHOOL_YEAR_REGISTRATIONS_VIEW_SCHOOL, SESSION_REGISTRATIONS_VIEW_DISTRICT, SESSION_REGISTRATIONS_VIEW_SCHOOL} from "../../../utils/eas/StudentRegistrationTableConfiguration"; -import ApiService from "../../../common/apiService"; -import {ApiRoutes} from "../../../utils/constants"; -import {authStore} from "../../../store/modules/auth"; -import {mapState} from "pinia"; +import {SCHOOL_YEAR_REGISTRATIONS_VIEW_DISTRICT, SCHOOL_YEAR_REGISTRATIONS_VIEW_SCHOOL} from '../../../utils/eas/StudentRegistrationTableConfiguration'; +import ApiService from '../../../common/apiService'; +import {ApiRoutes} from '../../../utils/constants'; +import {authStore} from '../../../store/modules/auth'; +import {mapState} from 'pinia'; +import StudentRegistrationDetail from './StudentRegistrationDetail.vue'; export default { name: 'StudentRegistrations', components: { StudentRegistrationsCustomTable, StudentRegistrationsFilter, + StudentRegistrationDetail }, props: { schoolYear: { @@ -118,6 +137,9 @@ export default { canLoadNext: false, canLoadPrevious: false, resetFlag: false, + studentRegistrationForEdit: null, + reloadStudentRegistrationsFlag: false, + editStudentRegistrationSheet: false, }; }, computed: { @@ -132,9 +154,19 @@ export default { this.selectTableConfig(); }, methods: { + editRegistration($event) { + this.studentRegistrationForEdit = cloneDeep($event); + this.editStudentRegistrationSheet = true; + }, + closeEditAndLoadStudentRegistrations() { + this.editStudentRegistrationSheet = !this.editStudentRegistrationSheet; + if (this.reloadStudentRegistrationsFlag === true) { + this.getAssessmentStudents(); + } + this.reloadStudentRegistrationsFlag = false; + }, selectTableConfig() { - this.config = this.sessionID ? (this.userInfo.activeInstituteType === 'DISTRICT' ? SESSION_REGISTRATIONS_VIEW_DISTRICT : SESSION_REGISTRATIONS_VIEW_SCHOOL) : - (this.userInfo.activeInstituteType === 'DISTRICT' ? SCHOOL_YEAR_REGISTRATIONS_VIEW_DISTRICT : SCHOOL_YEAR_REGISTRATIONS_VIEW_SCHOOL); + this.config = this.userInfo.activeInstituteType === 'DISTRICT' ? SCHOOL_YEAR_REGISTRATIONS_VIEW_DISTRICT : SCHOOL_YEAR_REGISTRATIONS_VIEW_SCHOOL; }, applyDefaultFilters() { if (this.sessionID) { @@ -210,6 +242,14 @@ export default { this.pageNumber -= 1; this.getAssessmentStudents(); } + }, + reload(value) { + if(value?.pageSize) { + this.pageSize = value?.pageSize; + } else if(value?.pageNumber) { + this.pageNumber = value?.pageNumber; + } + this.getAssessmentStudents(); } }, }; diff --git a/frontend/src/components/assessments/registrations/StudentRegistrationsCustomTable.vue b/frontend/src/components/assessments/registrations/StudentRegistrationsCustomTable.vue index 2dbf6da2..96846734 100644 --- a/frontend/src/components/assessments/registrations/StudentRegistrationsCustomTable.vue +++ b/frontend/src/components/assessments/registrations/StudentRegistrationsCustomTable.vue @@ -118,7 +118,7 @@ export default { default: false, }, }, - emits: [ 'reload', 'openStudentDetails', 'selections', 'editSelectedRow', 'loadPrevious', 'loadNext'], + emits: [ 'reload-registrations', 'openStudentDetails', 'editSelectedRow', 'loadPrevious', 'loadNext'], data() { return { masterCheckbox: false, @@ -139,26 +139,9 @@ export default { if (val) { this.masterCheckbox = false; this.selected = []; - this.$emit('reload', { pageNumber: val }); + this.$emit('reload-registrations', { pageNumber: val }); } - }, - selected: { - handler(val) { - if (val) { - this.$emit('selections', this.selected); - } - }, - deep: true, - }, - reset: { - handler(val) { - if (val) { - this.masterCheckbox = false; - this.selected.splice(0); - } - }, - immediate: true, - }, + } }, created() { appStore() diff --git a/frontend/src/components/assessments/registrations/StudentRegistrationsFilter.vue b/frontend/src/components/assessments/registrations/StudentRegistrationsFilter.vue index 597926e7..162154b4 100644 --- a/frontend/src/components/assessments/registrations/StudentRegistrationsFilter.vue +++ b/frontend/src/components/assessments/registrations/StudentRegistrationsFilter.vue @@ -35,7 +35,7 @@ large-icon icon="mdi-magnify" text="Search Name and ID" - :click-action="setPenLocalIdNameFilter" + :click-action="($event) => setPenLocalIdNameFilter($event, 'click')" /> @@ -65,18 +65,18 @@ - + +
+ + + {{ option?.title }} + + +
{ + this.setupSpecialCaseCodes(); + this.loading = false; + }); }); Object.keys(this.filters).forEach((key) => { this.selected[key] = []; }); this.setupAssessmentSessions(); }, - methods: { + methods: { close() { this.$emit('close-assessment-filter'); }, @@ -325,19 +348,34 @@ export default { this.sessionSearchNames = []; this.assessmentTypeSearchNames = []; this.schoolYearSessions.forEach(session => { - this.sessionSearchNames.push({title: this.formatMonth(session.courseMonth), id: session.sessionID, value: session.sessionID}); + this.sessionSearchNames.push({ + id: session.sessionID, + courseMonth: parseInt(session.courseMonth), + courseYear: parseInt(session.courseYear), + title: this.formatMonth(session.courseMonth), + value: session.sessionID + }); session.assessments.forEach(assessment => { let existingItem = this.assessmentTypeSearchNames.find(item => item.id === assessment.assessmentTypeCode); if (!existingItem) { - this.assessmentTypeSearchNames.push({title: assessment.assessmentTypeName, id: assessment.assessmentTypeCode, value: assessment.assessmentTypeCode}); + this.assessmentTypeSearchNames.push({title: assessment.assessmentTypeName, id: assessment.assessmentTypeCode, value: assessment.assessmentTypeCode, displayOrder: assessment.displayOrder}); } }); }); + this.sessionSearchNames = sortBy(this.sessionSearchNames, ['courseYear','courseMonth']); + this.assessmentTypeSearchNames = sortBy(this.assessmentTypeSearchNames, ['displayOrder']); + }, + setupSpecialCaseCodes() { + this.specialCaseSearchNames = []; + Object.keys(this.specialCaseCodes).forEach(key => { + this.specialCaseSearchNames.push({title: this.specialCaseCodes[key], id: key, value: key}); + }); + this.specialCaseSearchNames = sortBy(this.specialCaseSearchNames, ['title']); }, setupSchoolLists() { this.schoolSearchNames = []; this.assessmentCenterSearchNames = []; - this.activeSchoolsMap?.forEach((school) => { + this.schoolsMap?.forEach((school) => { let schoolCodeName = school.schoolName + ' - ' + school.mincode; this.assessmentCenterSearchNames.push({schoolCodeName: schoolCodeName, schoolCodeValue: school.schoolID}); @@ -349,7 +387,7 @@ export default { this.schoolSearchNames = sortBy(this.schoolSearchNames, ['schoolCodeName']); this.assessmentCenterSearchNames = sortBy(this.assessmentCenterSearchNames, ['schoolCodeName']); }, - setPenLocalIdNameFilter() { + setPenLocalIdNameFilter($event, val) { const keys = ['givenName', 'surName', 'pen', 'localID']; keys.forEach((key) => { if (this[key] != null) { @@ -360,9 +398,10 @@ export default { } } }); - this.apply(); - - }, + if ($event && val === 'click') { + this.apply(); + } + }, setSchoolNameNumberFilter(key, $event) { this.setPenLocalIdNameFilter($event, null); if ($event) { diff --git a/frontend/src/components/assessments/registrations/forms/EditStudentRegistration.vue b/frontend/src/components/assessments/registrations/forms/EditStudentRegistration.vue new file mode 100644 index 00000000..9d2528cc --- /dev/null +++ b/frontend/src/components/assessments/registrations/forms/EditStudentRegistration.vue @@ -0,0 +1,493 @@ + + + + diff --git a/frontend/src/store/modules/eas.js b/frontend/src/store/modules/eas.js new file mode 100644 index 00000000..eb5ab542 --- /dev/null +++ b/frontend/src/store/modules/eas.js @@ -0,0 +1,23 @@ +import {defineStore} from 'pinia'; +import ApiService from '../../common/apiService'; + +export const easStore = defineStore('EasStore', { + namespaced: true, + state: () => ({ + specialCaseCodes: {}, + }), + actions: { + async setSpecialCaseCodes(payload) { + this.specialCaseCodes = payload; + }, + async getSpecialCaseCodes() { + if(localStorage.getItem('jwtToken')) { // DONT Call api if there is not token. + if (Object.keys(this.specialCaseCodes).length === 0) { + const response = await ApiService.getAllEASSpecialCaseCodes(); + await this.setSpecialCaseCodes(response.data); + } + } + return this.specialCaseCodes; + }, + }, +}); diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.js index 01a50aa6..437da3d3 100644 --- a/frontend/src/utils/constants.js +++ b/frontend/src/utils/constants.js @@ -132,6 +132,8 @@ export const ApiRoutes = Object.freeze({ BASE_URL: easRoot, GET_ASSESSMENT_SESSIONS: easRoot + '/assessment-sessions', GET_ASSESSMENT_TYPES: easRoot + '/assessment-types', + GET_ASSESSMENT_SPECIALCASE_TYPES: easRoot + '/assessment-specialcase-types', + ASSESSMENT_STUDENTS: easRoot + '/assessment-registrations/student', GET_ASSESSMENT_STUDENTS_PAGINATED: easRoot + '/assessment-registrations/paginated', } }); diff --git a/frontend/src/utils/eas/StudentRegistrationTableConfiguration.js b/frontend/src/utils/eas/StudentRegistrationTableConfiguration.js index 9bf40493..4f120f40 100644 --- a/frontend/src/utils/eas/StudentRegistrationTableConfiguration.js +++ b/frontend/src/utils/eas/StudentRegistrationTableConfiguration.js @@ -27,21 +27,6 @@ export const SPECIAL_CASE_FILTER = Object.freeze( multiple: true, key: 'specialCaseCode', filterOptions: [ - { - title: 'Aegrotat', - id: 'aegrotat', - value: 'A' - }, - { - title: 'Exemption', - id: 'exemption', - value: 'E' - }, - { - title: 'Disqualified', - id: 'disqualified', - value: 'D' - } ] } ); @@ -101,15 +86,15 @@ export const PROFICIENCY_SCORE_RANGE_FILTER = Object.freeze( export const SCHOOL_YEAR_REGISTRATIONS_VIEW_DISTRICT = Object.freeze( { tableHeaders: [ - { title: 'Session', key: 'sessionName'}, - { title: 'School', key: 'schoolName' }, - { title: 'Assessment Center', key: 'assessmentCenterName' }, + { title: 'Session', key: 'sessionName_desc'}, + { title: 'School', key: 'schoolName_desc' }, + { title: 'Assessment Center', key: 'assessmentCenterName_desc' }, { title: 'PEN', key: 'pen' }, { title: 'Local ID', key: 'localID' }, { title: 'Surname', key: 'surName' }, { title: 'Given Name', key: 'givenName' }, - { title: 'Course Name (Code)', key: 'assessmentTypeName' }, - { title: 'Special Case', key: 'provincialSpecialCaseName' }, + { title: 'Course Name (Code)', key: 'assessmentTypeName_desc' }, + { title: 'Special Case', key: 'provincialSpecialCaseName_desc' }, { title: 'Proficiency Score', key: 'proficiencyScore' }, ], allowedFilters: { @@ -122,39 +107,16 @@ export const SCHOOL_YEAR_REGISTRATIONS_VIEW_DISTRICT = Object.freeze( }); export const SCHOOL_YEAR_REGISTRATIONS_VIEW_SCHOOL = Object.freeze( - { - tableHeaders: [ - { title: 'Session', key: 'sessionName'}, - { title: 'Assessment Center', key: 'assessmentCenterName' }, - { title: 'PEN', key: 'pen' }, - { title: 'Local ID', key: 'localID' }, - { title: 'Surname', key: 'surName' }, - { title: 'Given Name', key: 'givenName' }, - { title: 'Course Name (Code)', key: 'assessmentTypeName' }, - { title: 'Special Case', key: 'provincialSpecialCaseName' }, - { title: 'Proficiency Score', key: 'proficiencyScore' }, - ], - allowedFilters: { - session: SESSION_CODE_FILTER, - assessmentTypeCode: ASSESSMENT_TYPE_CODE_FILTER, - specialCaseCode: SPECIAL_CASE_FILTER, - proficiencyScore: PROFICIENCY_SCORE_FILTER, - proficiencyScoreValue: PROFICIENCY_SCORE_RANGE_FILTER - } - }); - -export const SESSION_REGISTRATIONS_VIEW_DISTRICT = Object.freeze( { tableHeaders: [ - { title: 'Session', key: 'sessionName'}, - { title: 'School', key: 'schoolName' }, - { title: 'Assessment Center', key: 'assessmentCenterName' }, + { title: 'Session', key: 'sessionName_desc'}, + { title: 'Assessment Center', key: 'assessmentCenterName_desc' }, { title: 'PEN', key: 'pen' }, { title: 'Local ID', key: 'localID' }, { title: 'Surname', key: 'surName' }, { title: 'Given Name', key: 'givenName' }, - { title: 'Course Name (Code)', key: 'assessmentTypeName' }, - { title: 'Special Case', key: 'provincialSpecialCaseName' }, + { title: 'Course Name (Code)', key: 'assessmentTypeName_desc' }, + { title: 'Special Case', key: 'provincialSpecialCaseName_desc' }, { title: 'Proficiency Score', key: 'proficiencyScore' }, ], allowedFilters: { @@ -162,28 +124,7 @@ export const SESSION_REGISTRATIONS_VIEW_DISTRICT = Object.freeze( assessmentTypeCode: ASSESSMENT_TYPE_CODE_FILTER, specialCaseCode: SPECIAL_CASE_FILTER, proficiencyScore: PROFICIENCY_SCORE_FILTER, - proficiencyScoreValue: PROFICIENCY_SCORE_RANGE_FILTER + proficiencyScoreValue: PROFICIENCY_SCORE_RANGE_FILTER } }); -export const SESSION_REGISTRATIONS_VIEW_SCHOOL = Object.freeze( - { - tableHeaders: [ - { title: 'Session', key: 'sessionName'}, - { title: 'Assessment Center', key: 'assessmentCenterName' }, - { title: 'PEN', key: 'pen' }, - { title: 'Local ID', key: 'localID' }, - { title: 'Surname', key: 'surName' }, - { title: 'Given Name', key: 'givenName' }, - { title: 'Course Name (Code)', key: 'assessmentTypeName' }, - { title: 'Special Case', key: 'provincialSpecialCaseName' }, - { title: 'Proficiency Score', key: 'proficiencyScore' }, - ], - allowedFilters: { - session: SESSION_CODE_FILTER, - assessmentTypeCode: ASSESSMENT_TYPE_CODE_FILTER, - specialCaseCode: SPECIAL_CASE_FILTER, - proficiencyScore: PROFICIENCY_SCORE_FILTER, - proficiencyScoreValue: PROFICIENCY_SCORE_RANGE_FILTER - } - }); From ed9b2e2dd16f6f8ae6437c4bd1c99d32ce4ad479 Mon Sep 17 00:00:00 2001 From: "QSL\\SumathiT" Date: Mon, 25 Nov 2024 13:20:00 -0800 Subject: [PATCH 2/5] Changes to disable based on score + disable special case field. --- backend/src/validations/eas.js | 6 +- .../forms/EditStudentRegistration.vue | 66 +++++++++++-------- 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/backend/src/validations/eas.js b/backend/src/validations/eas.js index 7ce498de..eae01afe 100644 --- a/backend/src/validations/eas.js +++ b/backend/src/validations/eas.js @@ -17,9 +17,9 @@ const putStudentAssessmentSchema = object({ surName: string().max(25).nonNullable(), isElectronicExam: boolean().nonNullable(), proficiencyScore: number().nonNullable(), - provincialSpecialCaseCode: string().max(1).nonNullable().optional(), - courseStatusCode: string().max(1).nonNullable().optional(), - numberOfAttempts: number().nonNullable(), + provincialSpecialCaseCode: string().max(1).nullable().optional(), + courseStatusCode: string().max(1).nullable().optional(), + numberOfAttempts: number().nullable(), courseMonth: number().optional(), courseYear: number().optional(), }).concat(baseRequestSchema).noUnknown(), diff --git a/frontend/src/components/assessments/registrations/forms/EditStudentRegistration.vue b/frontend/src/components/assessments/registrations/forms/EditStudentRegistration.vue index 9d2528cc..33855fbc 100644 --- a/frontend/src/components/assessments/registrations/forms/EditStudentRegistration.vue +++ b/frontend/src/components/assessments/registrations/forms/EditStudentRegistration.vue @@ -24,38 +24,40 @@ - + - + + :color="getFieldColor()" + :readonly="!isActive" + :class="!isActive ? 'readonly-text' : 'fieldtext'" + :rules="[rules.required()]" + @update:model-value="syncAssessmentValue($event)" + /> @@ -85,37 +87,35 @@ - + - - + + :readonly="true" + :class="['readonly-text']" + /> @@ -200,7 +200,11 @@
- + { this.assessmentStudentDetail = response.data; this.refreshAssessmentTypes(this.assessmentStudentDetail.sessionID); + this.setupActiveFlag(); }).catch(error => { console.error(error); setFailureAlert(error?.response?.data?.message ? error?.response?.data?.message : 'An error occurred while trying to get student registration details. Please try again later.'); @@ -440,6 +446,9 @@ export default { } }); }, + setupActiveFlag() { + this.isActive = this.schoolYearSessions.find(session => session.sessionID === this.assessmentStudentDetail.sessionID)?.isOpen && !parseInt(this.assessmentStudentDetail.proficiencyScore) > 0; + }, saveStudentRegistration() { this.loadingCount += 1; const putAssessmentStudentDetail = Object.fromEntries( @@ -473,15 +482,15 @@ export default { return; } }, - isActiveSession() { - return this.schoolYearSessions.find(session => session.sessionID === this.assessmentStudentDetail.sessionID)?.isOpen; - }, validateForm() { this.$refs?.registrationDetailsForm?.validate(); }, formatMonth(month) { return moment(month, 'MM').format('MMMM'); }, + getFieldColor() { + return !this.isActive ? '#7f7f7f' : '#003366'; + } }, }; @@ -489,5 +498,4 @@ export default { .readonly-text { color: #7f7f7f; } - From f7374824498d64a8cf1066ac970cf0a395ca67f6 Mon Sep 17 00:00:00 2001 From: "QSL\\SumathiT" Date: Mon, 25 Nov 2024 13:45:44 -0800 Subject: [PATCH 3/5] Exclude zero value for score. --- backend/src/validations/eas.js | 2 +- .../assessments/registrations/forms/EditStudentRegistration.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/validations/eas.js b/backend/src/validations/eas.js index eae01afe..3ed29897 100644 --- a/backend/src/validations/eas.js +++ b/backend/src/validations/eas.js @@ -16,7 +16,7 @@ const putStudentAssessmentSchema = object({ givenName: string().max(25).nonNullable(), surName: string().max(25).nonNullable(), isElectronicExam: boolean().nonNullable(), - proficiencyScore: number().nonNullable(), + proficiencyScore: number().nullable().optional(), provincialSpecialCaseCode: string().max(1).nullable().optional(), courseStatusCode: string().max(1).nullable().optional(), numberOfAttempts: number().nullable(), diff --git a/frontend/src/components/assessments/registrations/forms/EditStudentRegistration.vue b/frontend/src/components/assessments/registrations/forms/EditStudentRegistration.vue index 33855fbc..1eb4e5a9 100644 --- a/frontend/src/components/assessments/registrations/forms/EditStudentRegistration.vue +++ b/frontend/src/components/assessments/registrations/forms/EditStudentRegistration.vue @@ -447,7 +447,7 @@ export default { }); }, setupActiveFlag() { - this.isActive = this.schoolYearSessions.find(session => session.sessionID === this.assessmentStudentDetail.sessionID)?.isOpen && !parseInt(this.assessmentStudentDetail.proficiencyScore) > 0; + this.isActive = this.schoolYearSessions.find(session => session.sessionID === this.assessmentStudentDetail.sessionID)?.isOpen && !(this.assessmentStudentDetail.provincialSpecialCaseCode && parseInt(this.assessmentStudentDetail.proficiencyScore) >= 0); }, saveStudentRegistration() { this.loadingCount += 1; From 06240303c500c1112de9103e6fff122327c16528 Mon Sep 17 00:00:00 2001 From: "QSL\\SumathiT" Date: Mon, 25 Nov 2024 13:52:55 -0800 Subject: [PATCH 4/5] Exclude zero value for score. --- .../assessments/registrations/forms/EditStudentRegistration.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/assessments/registrations/forms/EditStudentRegistration.vue b/frontend/src/components/assessments/registrations/forms/EditStudentRegistration.vue index 1eb4e5a9..66e65236 100644 --- a/frontend/src/components/assessments/registrations/forms/EditStudentRegistration.vue +++ b/frontend/src/components/assessments/registrations/forms/EditStudentRegistration.vue @@ -447,7 +447,7 @@ export default { }); }, setupActiveFlag() { - this.isActive = this.schoolYearSessions.find(session => session.sessionID === this.assessmentStudentDetail.sessionID)?.isOpen && !(this.assessmentStudentDetail.provincialSpecialCaseCode && parseInt(this.assessmentStudentDetail.proficiencyScore) >= 0); + this.isActive = this.schoolYearSessions.find(session => session.sessionID === this.assessmentStudentDetail.sessionID)?.isOpen && !(this.assessmentStudentDetail.proficiencyScore && parseInt(this.assessmentStudentDetail.proficiencyScore) >= 0); }, saveStudentRegistration() { this.loadingCount += 1; From 279485d6a7acca57aeb647e17f7f7caba62fceda Mon Sep 17 00:00:00 2001 From: "QSL\\SumathiT" Date: Mon, 25 Nov 2024 14:01:45 -0800 Subject: [PATCH 5/5] Check to exclude special case scenario. --- .../assessments/registrations/forms/EditStudentRegistration.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/assessments/registrations/forms/EditStudentRegistration.vue b/frontend/src/components/assessments/registrations/forms/EditStudentRegistration.vue index 66e65236..d8885910 100644 --- a/frontend/src/components/assessments/registrations/forms/EditStudentRegistration.vue +++ b/frontend/src/components/assessments/registrations/forms/EditStudentRegistration.vue @@ -447,7 +447,7 @@ export default { }); }, setupActiveFlag() { - this.isActive = this.schoolYearSessions.find(session => session.sessionID === this.assessmentStudentDetail.sessionID)?.isOpen && !(this.assessmentStudentDetail.proficiencyScore && parseInt(this.assessmentStudentDetail.proficiencyScore) >= 0); + this.isActive = this.schoolYearSessions.find(session => session.sessionID === this.assessmentStudentDetail.sessionID)?.isOpen && !this.assessmentStudentDetail.provincialSpecialCaseCode && !(this.assessmentStudentDetail.proficiencyScore && parseInt(this.assessmentStudentDetail.proficiencyScore) >= 0); }, saveStudentRegistration() { this.loadingCount += 1;