From 0a22f802a58b3a417faea89b59ebed28df78ac63 Mon Sep 17 00:00:00 2001 From: mightycox Date: Fri, 29 Sep 2023 11:14:18 -0700 Subject: [PATCH 01/32] EDX-1844 - removes sdc user role when sdc is disabled EDX-1864 - removes funding tab when sdc is disabled --- backend/src/config/index.js | 3 ++- backend/src/routes/config.js | 3 ++- frontend/src/components/institute/SchoolDetails.vue | 4 +++- .../secure-message/AccessSchoolUsersPage.vue | 12 ++++++++---- frontend/src/utils/constants/Permission.js | 12 ++++++++++++ tools/config/update-configmap.sh | 5 ++++- 6 files changed, 31 insertions(+), 8 deletions(-) create mode 100644 frontend/src/utils/constants/Permission.js diff --git a/backend/src/config/index.js b/backend/src/config/index.js index 8081c1c2f..916a15470 100644 --- a/backend/src/config/index.js +++ b/backend/src/config/index.js @@ -176,7 +176,8 @@ nconf.defaults({ frontendConfig: { bannerEnvironment: process.env.BANNER_ENVIRONMENT, bannerColor: process.env.BANNER_COLOR, - webSocketURL: process.env.WEB_SOCKET_URL + webSocketURL: process.env.WEB_SOCKET_URL, + disableSdcFunctionality: process.env.DISABLE_SDC_FUNCTIONALITY }, sdc: { rootURL: process.env.SDC_API_URL, diff --git a/backend/src/routes/config.js b/backend/src/routes/config.js index 988c599eb..f8f121fc6 100644 --- a/backend/src/routes/config.js +++ b/backend/src/routes/config.js @@ -12,7 +12,8 @@ async function getConfig(req, res) { const frontConfig = { BANNER_ENVIRONMENT: frontendConfig.bannerEnvironment, BANNER_COLOR: frontendConfig.bannerColor, - WEB_SOCKET_URL: frontendConfig.webSocketURL + WEB_SOCKET_URL: frontendConfig.webSocketURL, + DISABLE_SDC_FUNCTIONALITY: frontendConfig.disableSdcFunctionality }; return res.status(HttpStatus.OK).json(frontConfig); } diff --git a/frontend/src/components/institute/SchoolDetails.vue b/frontend/src/components/institute/SchoolDetails.vue index b619ba1de..8f5e5fe12 100644 --- a/frontend/src/components/institute/SchoolDetails.vue +++ b/frontend/src/components/institute/SchoolDetails.vue @@ -210,6 +210,7 @@ import SchoolFunding from './common/SchoolFunding.vue'; import {authStore} from '@/store/modules/auth'; import {notificationsStore} from '@/store/modules/notifications'; import InstituteNotes from '@/components/institute/common/InstituteNotes.vue'; +import {appStore} from '@/store/modules/app'; export default { name: 'SchoolDetailsPage', @@ -247,6 +248,7 @@ export default { computed: { ...mapState(authStore, ['isAuthenticated', 'userInfo', 'SCHOOL_ADMIN_ROLE', 'INDEPENDENT_SCHOOLS_ADMIN_ROLE', 'OFFSHORE_SCHOOLS_ADMIN_ROLE']), ...mapState(notificationsStore, ['notification']), + ...mapState(appStore, ['config']), notesLoading() { return this.noteRequestCount > 0; }, @@ -344,7 +346,7 @@ export default { return this.SCHOOL_ADMIN_ROLE; }, canViewFundingTab() { - return this.independentArray.includes(this.school.schoolCategoryCode) && (this.INDEPENDENT_SCHOOLS_ADMIN_ROLE || this.SCHOOL_ADMIN_ROLE); + return this.independentArray.includes(this.school.schoolCategoryCode) && (this.INDEPENDENT_SCHOOLS_ADMIN_ROLE || this.SCHOOL_ADMIN_ROLE) && !this.config.DISABLE_SDC_FUNCTIONALITY; }, saveNewSchoolNote(schoolNote) { this.noteRequestCount += 1; diff --git a/frontend/src/components/secure-message/AccessSchoolUsersPage.vue b/frontend/src/components/secure-message/AccessSchoolUsersPage.vue index c8cdc60e8..a8535f0c2 100644 --- a/frontend/src/components/secure-message/AccessSchoolUsersPage.vue +++ b/frontend/src/components/secure-message/AccessSchoolUsersPage.vue @@ -117,7 +117,7 @@ v-model="searchFilter.roleName" clearable variant="underlined" - :items="schoolRoles" + :items="filteredSchoolRoles" item-title="label" item-value="edxRoleCode" label="Role" @@ -174,7 +174,7 @@ :user-roles="user.edxUserSchools[0].edxUserSchoolRoles" :user="user" :institute-code="schoolID" - :institute-roles="schoolRoles" + :institute-roles="filteredSchoolRoles" institute-type-code="SCHOOL" institute-type-label="School" @refresh="getUsersData" @@ -246,7 +246,7 @@ { return user.edxUserSchools.some(school => school.edxUserSchoolRoles.some(role => role.edxRoleCode === ROLE.EDX_SCHOOL_ADMIN)); })?.length > 0; + }, + filteredSchoolRoles() { + return this.config.DISABLE_SDC_FUNCTIONALITY ? this.schoolRoles.filter(role => role.edxRoleCode !== PERMISSION.STUDENT_DATA_COLLECTION) : this.schoolRoles; } }, async beforeMount() { diff --git a/frontend/src/utils/constants/Permission.js b/frontend/src/utils/constants/Permission.js new file mode 100644 index 000000000..4175cacb0 --- /dev/null +++ b/frontend/src/utils/constants/Permission.js @@ -0,0 +1,12 @@ +export const PERMISSION = Object.freeze( + { + EDX_USER_SCHOOL_ADMIN: 'EDX_USER_SCHOOL_ADMIN', + + SECURE_EXCHANGE: 'SECURE_EXCHANGE', + + EDX_USER_DISTRICT_ADMIN: 'EDX_USER_DISTRICT_ADMIN', + + STUDENT_DATA_COLLECTION: 'STUDENT_DATA_COLLECTION' + } +); + diff --git a/tools/config/update-configmap.sh b/tools/config/update-configmap.sh index 7892da086..fa43033d8 100644 --- a/tools/config/update-configmap.sh +++ b/tools/config/update-configmap.sh @@ -273,12 +273,15 @@ if [ "$envValue" = "dev" ]; then bannerEnvironment="DEV" bannerColor="#8d28d7" SCHEDULER_CRON_DOC_TYPE_MIGRATION="0 0 0 * * *" + disableSdcFunctionality=false elif [ "$envValue" = "test" ]; then bannerEnvironment="TEST" bannerColor="#58fe01" SCHEDULER_CRON_DOC_TYPE_MIGRATION="0 0 0 * * *" + disableSdcFunctionality=false else SCHEDULER_CRON_DOC_TYPE_MIGRATION="0 0 0 17 9 *" + disableSdcFunctionality=true fi BANNER_ENVIRONMENT="$bannerEnvironment" @@ -286,7 +289,7 @@ BANNER_COLOR="$bannerColor" WEB_SOCKET_URL="wss://$SERVER_FRONTEND/api/socket" echo Creating config map $APP_NAME-backend-config-map -oc create -n $PEN_NAMESPACE-$envValue configmap $APP_NAME-backend-config-map --from-literal=WEB_SOCKET_URL="$WEB_SOCKET_URL" --from-literal=BANNER_COLOR="$BANNER_COLOR" --from-literal=BANNER_ENVIRONMENT="$BANNER_ENVIRONMENT" --from-literal=TZ=$TZVALUE --from-literal=UI_PRIVATE_KEY="$UI_PRIVATE_KEY_VAL" --from-literal=SITEMINDER_LOGOUT_ENDPOINT="$siteMinderLogoutUrl" --from-literal=UI_PUBLIC_KEY="$UI_PUBLIC_KEY_VAL" --from-literal=ID=$APP_NAME-soam --from-literal=SECRET=$studentAdminClientSecret --from-literal=SERVER_FRONTEND=https://$SERVER_FRONTEND --from-literal=ISSUER=STUDENT_ADMIN_APPLICATION --from-literal=SOAM_PUBLIC_KEY="$formattedPublicKey" --from-literal=PEN_REQUEST_EMAIL_API_URL="http://student-profile-email-api-master.$PEN_NAMESPACE-$envValue.svc.cluster.local:8080/gmp" --from-literal=PEN_REQUEST_API_URL="http://pen-request-api-master.$COMMON_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/pen-request" --from-literal=DISCOVERY=https://$SOAM_KC/auth/realms/$SOAM_KC_REALM_ID/.well-known/openid-configuration --from-literal=KC_DOMAIN=https://$SOAM_KC/auth/realms/$SOAM_KC_REALM_ID --from-literal=PEN_DEMOGRAPHICS_URL="http://pen-demographics-api-master.$COMMON_NAMESPACE-$envValue.svc.cluster.local:8080" --from-literal=DIGITAL_ID_URL="http://digitalid-api-master.$COMMON_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/digital-id" --from-literal=STUDENT_API_URL="http://student-api-master.$COMMON_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/student" --from-literal=LOG_LEVEL=info --from-literal=IDIR_IDP_HINT=keycloak_bcdevexchange_idir --from-literal=REDIS_HOST=redis --from-literal=REDIS_PORT=6379 --from-literal=STUDENT_PROFILE_API_URL="http://student-profile-api-master.$COMMON_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/student-profile" --from-literal=SCHOOL_API_URL="http://school-api-master.$COMMON_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1" --from-literal=STUDENT_PROFILE_EMAIL_API_URL="http://student-profile-email-api-master.$PEN_NAMESPACE-$envValue.svc.cluster.local:8080/ump" --from-literal=PROFILE_REQUEST_SAGA_API_URL="http://student-profile-saga-api-master.$PEN_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/student-profile-saga" --from-literal=NATS_URL="$NATS_URL" --from-literal=NATS_CLUSTER="$NATS_CLUSTER" --from-literal=UMP_ROLES="STUDENT_PROFILE_ADMIN,STUDENT_PROFILE_READ_ONLY" --from-literal=GMP_ROLES="STUDENT_ADMIN,STUDENT_ADMIN_READ_ONLY" --from-literal=STUDENT_SEARCH_ADMIN="STUDENT_SEARCH_ADMIN" --from-literal=STUDENT_SEARCH_ROLES="STUDENT_SEARCH_ADMIN,STUDENT_SEARCH_READ_ONLY" --from-literal=STUDENT_ADMIN_ADMINISTRATOR="STUDENT_ADMIN_ADMINISTRATOR" --from-literal=UMP_ROLE_ADMIN="STUDENT_PROFILE_ADMIN" --from-literal=GMP_ROLE_ADMIN="STUDENT_ADMIN" --from-literal=PEN_REQUEST_BATCH_ADMIN="PEN_REQUEST_BATCH_ADMIN" --from-literal=EDX_ADMIN="EDX_ADMIN" --from-literal=PEN_REQUEST_BATCH_API_URL="http://pen-reg-batch-api-master.$PEN_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1" --from-literal=PEN_MATCH_API_URL="http://pen-match-api-master.$PEN_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/pen-match" --from-literal=SESSION_MAX_AGE=$sessionMaxAge --from-literal=TOKEN_EXPIRES_IN=$tokenExpiresIn --from-literal=SCHEDULER_CRON_STALE_SAGA_RECORD_REDIS="0/30 * * * * *" --from-literal=MIN_TIME_BEFORE_SAGA_IS_STALE_IN_SECONDS=10 --from-literal=PEN_SERVICES_API_URL="http://pen-services-api-master.$PEN_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/pen-services" --from-literal=PEN_TRAX_API_URL="http://pen-trax-api-master.$COMMON_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1" --from-literal=SLD_API_URL="http://sld-api-master.$COMMON_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1" --from-literal=QUEUE_GROUP_NAME="student-admin-node-queue-group" --from-literal=STAN_ENABLED="true" --from-literal=NODE_ENV="openshift" --from-literal=SCHEDULER_CRON_DOC_TYPE_MIGRATION="$SCHEDULER_CRON_DOC_TYPE_MIGRATION" --from-literal=ENABLE_PRR_STUDENT_DEMOGRAPHICS="$ENABLE_PRR_STUDENT_DEMOGRAPHICS" --from-literal=NOMINAL_ROLL="NOMINAL_ROLL_EDIT" --from-literal=MACRO_API_URL="http://macro-api-master.$COMMON_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/macro" --from-literal=NOMINAL_ROLL_API_URL="http://pen-nominal-roll-api-main.$PEN_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/nominal-roll" --from-literal=STUDENT_ANALYTICS_STUDENT_PROFILE="STUDENT_ANALYTICS_STUDENT_PROFILE" --from-literal=STUDENT_ANALYTICS_BATCH="STUDENT_ANALYTICS_BATCH" --from-literal=NOMINAL_ROLL_ROLES="NOMINAL_ROLL,NOMINAL_ROLL_EDIT" --from-literal=EDX_API_URL="http://edx-api-master.$EDX_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/edx" --from-literal=SDC_API_URL="http://student-data-collection-api-master.$EDX_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/student-data-collection" --from-literal=INSTITUTE_API_URL="http://institute-api-master.$COMMON_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/institute" --from-literal=EDX_PEN_TEAM_ROLES="PEN_TEAM_ROLE" --dry-run -o yaml | oc apply -f - +oc create -n $PEN_NAMESPACE-$envValue configmap $APP_NAME-backend-config-map --from-literal=WEB_SOCKET_URL="$WEB_SOCKET_URL" --from-literal=BANNER_COLOR="$BANNER_COLOR" --from-literal=BANNER_ENVIRONMENT="$BANNER_ENVIRONMENT" --from-literal=TZ=$TZVALUE --from-literal=UI_PRIVATE_KEY="$UI_PRIVATE_KEY_VAL" --from-literal=SITEMINDER_LOGOUT_ENDPOINT="$siteMinderLogoutUrl" --from-literal=UI_PUBLIC_KEY="$UI_PUBLIC_KEY_VAL" --from-literal=ID=$APP_NAME-soam --from-literal=SECRET=$studentAdminClientSecret --from-literal=SERVER_FRONTEND=https://$SERVER_FRONTEND --from-literal=ISSUER=STUDENT_ADMIN_APPLICATION --from-literal=SOAM_PUBLIC_KEY="$formattedPublicKey" --from-literal=PEN_REQUEST_EMAIL_API_URL="http://student-profile-email-api-master.$PEN_NAMESPACE-$envValue.svc.cluster.local:8080/gmp" --from-literal=PEN_REQUEST_API_URL="http://pen-request-api-master.$COMMON_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/pen-request" --from-literal=DISCOVERY=https://$SOAM_KC/auth/realms/$SOAM_KC_REALM_ID/.well-known/openid-configuration --from-literal=KC_DOMAIN=https://$SOAM_KC/auth/realms/$SOAM_KC_REALM_ID --from-literal=PEN_DEMOGRAPHICS_URL="http://pen-demographics-api-master.$COMMON_NAMESPACE-$envValue.svc.cluster.local:8080" --from-literal=DIGITAL_ID_URL="http://digitalid-api-master.$COMMON_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/digital-id" --from-literal=STUDENT_API_URL="http://student-api-master.$COMMON_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/student" --from-literal=LOG_LEVEL=info --from-literal=IDIR_IDP_HINT=keycloak_bcdevexchange_idir --from-literal=REDIS_HOST=redis --from-literal=REDIS_PORT=6379 --from-literal=STUDENT_PROFILE_API_URL="http://student-profile-api-master.$COMMON_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/student-profile" --from-literal=SCHOOL_API_URL="http://school-api-master.$COMMON_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1" --from-literal=STUDENT_PROFILE_EMAIL_API_URL="http://student-profile-email-api-master.$PEN_NAMESPACE-$envValue.svc.cluster.local:8080/ump" --from-literal=PROFILE_REQUEST_SAGA_API_URL="http://student-profile-saga-api-master.$PEN_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/student-profile-saga" --from-literal=NATS_URL="$NATS_URL" --from-literal=NATS_CLUSTER="$NATS_CLUSTER" --from-literal=UMP_ROLES="STUDENT_PROFILE_ADMIN,STUDENT_PROFILE_READ_ONLY" --from-literal=GMP_ROLES="STUDENT_ADMIN,STUDENT_ADMIN_READ_ONLY" --from-literal=STUDENT_SEARCH_ADMIN="STUDENT_SEARCH_ADMIN" --from-literal=STUDENT_SEARCH_ROLES="STUDENT_SEARCH_ADMIN,STUDENT_SEARCH_READ_ONLY" --from-literal=STUDENT_ADMIN_ADMINISTRATOR="STUDENT_ADMIN_ADMINISTRATOR" --from-literal=UMP_ROLE_ADMIN="STUDENT_PROFILE_ADMIN" --from-literal=GMP_ROLE_ADMIN="STUDENT_ADMIN" --from-literal=PEN_REQUEST_BATCH_ADMIN="PEN_REQUEST_BATCH_ADMIN" --from-literal=EDX_ADMIN="EDX_ADMIN" --from-literal=PEN_REQUEST_BATCH_API_URL="http://pen-reg-batch-api-master.$PEN_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1" --from-literal=PEN_MATCH_API_URL="http://pen-match-api-master.$PEN_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/pen-match" --from-literal=SESSION_MAX_AGE=$sessionMaxAge --from-literal=TOKEN_EXPIRES_IN=$tokenExpiresIn --from-literal=SCHEDULER_CRON_STALE_SAGA_RECORD_REDIS="0/30 * * * * *" --from-literal=MIN_TIME_BEFORE_SAGA_IS_STALE_IN_SECONDS=10 --from-literal=PEN_SERVICES_API_URL="http://pen-services-api-master.$PEN_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/pen-services" --from-literal=PEN_TRAX_API_URL="http://pen-trax-api-master.$COMMON_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1" --from-literal=SLD_API_URL="http://sld-api-master.$COMMON_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1" --from-literal=QUEUE_GROUP_NAME="student-admin-node-queue-group" --from-literal=STAN_ENABLED="true" --from-literal=NODE_ENV="openshift" --from-literal=SCHEDULER_CRON_DOC_TYPE_MIGRATION="$SCHEDULER_CRON_DOC_TYPE_MIGRATION" --from-literal=ENABLE_PRR_STUDENT_DEMOGRAPHICS="$ENABLE_PRR_STUDENT_DEMOGRAPHICS" --from-literal=NOMINAL_ROLL="NOMINAL_ROLL_EDIT" --from-literal=MACRO_API_URL="http://macro-api-master.$COMMON_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/macro" --from-literal=NOMINAL_ROLL_API_URL="http://pen-nominal-roll-api-main.$PEN_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/nominal-roll" --from-literal=STUDENT_ANALYTICS_STUDENT_PROFILE="STUDENT_ANALYTICS_STUDENT_PROFILE" --from-literal=STUDENT_ANALYTICS_BATCH="STUDENT_ANALYTICS_BATCH" --from-literal=NOMINAL_ROLL_ROLES="NOMINAL_ROLL,NOMINAL_ROLL_EDIT" --from-literal=EDX_API_URL="http://edx-api-master.$EDX_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/edx" --from-literal=SDC_API_URL="http://student-data-collection-api-master.$EDX_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/student-data-collection" --from-literal=INSTITUTE_API_URL="http://institute-api-master.$COMMON_NAMESPACE-$envValue.svc.cluster.local:8080/api/v1/institute" --from-literal=EDX_PEN_TEAM_ROLES="PEN_TEAM_ROLE" --from-literal=DISABLE_SDC_FUNCTIONALITY=$disableSdcFunctionality --dry-run -o yaml | oc apply -f - echo echo Setting environment variables for $APP_NAME-backend-$SOAM_KC_REALM_ID application oc -n $PEN_NAMESPACE-$envValue set env --from=configmap/$APP_NAME-backend-config-map dc/$APP_NAME-backend-$SOAM_KC_REALM_ID From 163aec3315c62350d04d3365f72a2769339ef72a Mon Sep 17 00:00:00 2001 From: mightycox Date: Thu, 5 Oct 2023 16:51:58 -0700 Subject: [PATCH 02/32] Fixes configmap not having booleans issue --- backend/src/config/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/config/index.js b/backend/src/config/index.js index 916a15470..18f7346cf 100644 --- a/backend/src/config/index.js +++ b/backend/src/config/index.js @@ -177,7 +177,7 @@ nconf.defaults({ bannerEnvironment: process.env.BANNER_ENVIRONMENT, bannerColor: process.env.BANNER_COLOR, webSocketURL: process.env.WEB_SOCKET_URL, - disableSdcFunctionality: process.env.DISABLE_SDC_FUNCTIONALITY + disableSdcFunctionality: process.env.DISABLE_SDC_FUNCTIONALITY === 'true' }, sdc: { rootURL: process.env.SDC_API_URL, From a5dfbcd9a1d9efd9cd94c7733ca00b8724cfeabc Mon Sep 17 00:00:00 2001 From: mightycox Date: Tue, 10 Oct 2023 09:49:37 -0700 Subject: [PATCH 03/32] EDX-1859 - pushing datepicker component for Avisha --- frontend/src/components/util/DatePicker.vue | 97 +++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 frontend/src/components/util/DatePicker.vue diff --git a/frontend/src/components/util/DatePicker.vue b/frontend/src/components/util/DatePicker.vue new file mode 100644 index 000000000..aca2cff79 --- /dev/null +++ b/frontend/src/components/util/DatePicker.vue @@ -0,0 +1,97 @@ + + + + + From 18dce40475e4dc8542424ce2dab70278f7a45c6a Mon Sep 17 00:00:00 2001 From: Marco Villeneuve Date: Tue, 10 Oct 2023 11:55:12 -0700 Subject: [PATCH 04/32] Small update for not using SDC --- tools/config/update-configmap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/config/update-configmap.sh b/tools/config/update-configmap.sh index fa43033d8..db285337f 100644 --- a/tools/config/update-configmap.sh +++ b/tools/config/update-configmap.sh @@ -278,7 +278,7 @@ elif [ "$envValue" = "test" ]; then bannerEnvironment="TEST" bannerColor="#58fe01" SCHEDULER_CRON_DOC_TYPE_MIGRATION="0 0 0 * * *" - disableSdcFunctionality=false + disableSdcFunctionality=true else SCHEDULER_CRON_DOC_TYPE_MIGRATION="0 0 0 17 9 *" disableSdcFunctionality=true From 1797a28c8159ac1ee7fc5f33a4adea2300ac15a8 Mon Sep 17 00:00:00 2001 From: mightycox Date: Tue, 10 Oct 2023 13:26:55 -0700 Subject: [PATCH 05/32] Blocks some missed sdc calls when sdc is disabled --- frontend/src/store/modules/app.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/frontend/src/store/modules/app.js b/frontend/src/store/modules/app.js index c4f3eaa63..7b74dcde8 100644 --- a/frontend/src/store/modules/app.js +++ b/frontend/src/store/modules/app.js @@ -145,7 +145,7 @@ export const appStore = defineStore('app', { const response = await ApiService.getSchoolApiMincodeSchoolNames(); await this.setSchoolApiMincodeSchoolNameAndDistrictCodes(response.data); } - if(this.fundingGroupsMap.size === 0) { + if(this.fundingGroupsMap.size === 0 && !this.config.DISABLE_SDC_FUNCTIONALITY) { const response = await ApiService.getAllFundingGroups(); await this.setFundingGroups(response.data); } @@ -175,8 +175,10 @@ export const appStore = defineStore('app', { const responseSchoolApiMin = await ApiService.getSchoolApiMincodeSchoolNames(); await this.setSchoolApiMincodeSchoolNameAndDistrictCodes(responseSchoolApiMin.data); - const responseFunding = await ApiService.getAllFundingGroups(); - await this.setFundingGroups(responseFunding.data); + if(!this.config.DISABLE_SDC_FUNCTIONALITY) { + const responseFunding = await ApiService.getAllFundingGroups(); + await this.setFundingGroups(responseFunding.data); + } } }, }, From f70d2ad2278055cd8f0f568528e6c6263e12b250 Mon Sep 17 00:00:00 2001 From: mightycox Date: Tue, 10 Oct 2023 13:47:18 -0700 Subject: [PATCH 06/32] Blocks some missed sdc calls when sdc is disabled --- backend/src/server.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/backend/src/server.js b/backend/src/server.js index 7fe11b6b8..e20a1e378 100644 --- a/backend/src/server.js +++ b/backend/src/server.js @@ -107,11 +107,13 @@ cacheService.loadDataToCache( constants.CACHE_KEYS.AUTHORITY_CONTACT_TYPES, 'ser }).catch((e) => { log.error('Error loading AUTHORITY_CONTACT_TYPES data during boot .', e); }); -cacheService.loadDataToCache( constants.CACHE_KEYS.SDC_FUNDING_GROUPS, 'sdc:fundingGroupsURL').then(() => { - log.info('Loaded FUNDING_GROUPS data to memory'); -}).catch((e) => { - log.error('Error loading FUNDING_GROUPS data during boot .', e); -}); +if(!config.get('frontendConfig').disableSdcFunctionality) { + cacheService.loadDataToCache( constants.CACHE_KEYS.SDC_FUNDING_GROUPS, 'sdc:fundingGroupsURL').then(() => { + log.info('Loaded FUNDING_GROUPS data to memory'); + }).catch((e) => { + log.error('Error loading FUNDING_GROUPS data during boot .', e); + }); +} cacheService.loadAllAuthoritiesToMap().then(() => { log.info('Loaded authorities data to memory'); From cbc1c56f7394eb30f7865d13e5cadef860866fc0 Mon Sep 17 00:00:00 2001 From: SodhiA1 <38086281+SodhiA1@users.noreply.github.com> Date: Tue, 10 Oct 2023 16:35:56 -0700 Subject: [PATCH 07/32] MIN: Set Expiry Date for EDX User --- backend/src/components/edx/exchange.js | 13 +- frontend/package-lock.json | 158 +++++++++++------- frontend/package.json | 1 + .../secure-message/AccessUserCard.vue | 65 ++++++- .../secure-message/InviteUserPage.vue | 20 ++- 5 files changed, 180 insertions(+), 77 deletions(-) diff --git a/backend/src/components/edx/exchange.js b/backend/src/components/edx/exchange.js index 96578dcd2..f8f0dc889 100644 --- a/backend/src/components/edx/exchange.js +++ b/backend/src/components/edx/exchange.js @@ -6,7 +6,7 @@ const config = require('../../config'); const {getData, getCodeTable, putData} = require('../utils'); const utils = require('../utils'); const {FILTER_OPERATION, VALUE_TYPE, CACHE_KEYS} = require('../../util/constants'); -const {LocalDateTime, DateTimeFormatter} = require('@js-joda/core'); +const {LocalDateTime, LocalDate, DateTimeFormatter} = require('@js-joda/core'); const cacheService = require('../cache-service'); const log = require('../logger'); @@ -497,6 +497,7 @@ async function checkIfPrimaryCodeExists(req,res, token, instituteType, institute async function districtUserActivationInvite(req, res) { const token = utils.getBackendToken(req); + const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss'); if (!token) { return res.status(HttpStatus.UNAUTHORIZED).json({ message: 'No access token' @@ -510,7 +511,8 @@ async function districtUserActivationInvite(req, res) { } const payload = { - ...req.body + ...req.body, + edxUserExpiryDate: req.body.edxUserExpiryDate ? LocalDate.parse(req.body.edxUserExpiryDate).atStartOfDay().format(formatter) : null }; try { const response = await utils.postData(token, config.get('server:edx:districtUserActivationInviteURL'), payload, null, utils.getUser(req).idir_username); @@ -536,7 +538,8 @@ async function schoolUserActivationInvite(req, res) { } const payload = { - ...req.body + ...req.body, + edxUserExpiryDate: req.body.edxUserExpiryDate }; try { const response = await utils.postData(token, config.get('server:edx:schoolUserActivationInviteURL'), payload, null, utils.getUser(req).idir_username); @@ -552,6 +555,7 @@ async function updateEdxUserSchoolRoles(req, res) { try { const token = utils.getBackendToken(req); const userInfo = utils.getUser(req); + const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss'); let edxUser = await getData(token, `${config.get('server:edx:edxUsersURL')}/${req.body.params.edxUserID}`); let selectedUserSchools = edxUser.edxUserSchools.filter(school => school.schoolID === req.body.params.schoolID); @@ -580,6 +584,7 @@ async function updateEdxUserSchoolRoles(req, res) { selectedUserSchool.updateDate = null; selectedUserSchool.createDate = null; + selectedUserSchool.expiryDate = req.body.params.expiryDate ? LocalDate.parse(req.body.params.expiryDate).atStartOfDay().format(formatter) : null; const result = await utils.putData(token, `${config.get('server:edx:edxUsersURL')}/${selectedUserSchool.edxUserID}/school`, selectedUserSchool, userInfo.idir_username); return res.status(HttpStatus.OK).json(result); @@ -593,6 +598,7 @@ async function updateEdxUserDistrictRoles(req, res) { try { const token = utils.getBackendToken(req); const userInfo = utils.getUser(req); + const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss'); let edxUser = await getData(token, `${config.get('server:edx:edxUsersURL')}/${req.body.params.edxUserID}`); let selectedUserDistricts = edxUser.edxUserDistricts.filter(district => district.districtID === req.body.params.districtId); @@ -621,6 +627,7 @@ async function updateEdxUserDistrictRoles(req, res) { selectedUserDistrict.updateDate = null; selectedUserDistrict.createDate = null; + selectedUserSchool.expiryDate = req.body.params.expiryDate ? LocalDate.parse(req.body.params.expiryDate).atStartOfDay().format(formatter) : null; const result = await utils.putData(token, `${config.get('server:edx:edxUsersURL')}/${selectedUserDistrict.edxUserID}/district`, selectedUserDistrict, userInfo.idir_username); return res.status(HttpStatus.OK).json(result); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7d92530ac..8e61fe281 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -12,6 +12,7 @@ "@fortawesome/fontawesome-free": "6.4.0", "@js-joda/core": "^5.5.3", "@mdi/font": "^7.2.96", + "@vuepic/vue-datepicker": "^4.2.0", "axios": "^1.4.0", "chart.js": "^4.3.0", "chartjs-plugin-datalabels": "^2.2.0", @@ -43,7 +44,6 @@ "vue-meta": "^3.0.0-alpha.10", "vue-quick-chat": "^1.2.8", "vue-router": "^4.2.2", - "vue3-treeview": "^0.4.1", "vuetify": "npm:@vuetify/nightly@3.3.5-pr-17265.042d482" }, "devDependencies": { @@ -569,6 +569,22 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.23.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.1.tgz", + "integrity": "sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime/node_modules/regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + }, "node_modules/@babel/template": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", @@ -2239,6 +2255,21 @@ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz", "integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==" }, + "node_modules/@vuepic/vue-datepicker": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@vuepic/vue-datepicker/-/vue-datepicker-4.5.1.tgz", + "integrity": "sha512-zdG37Q8iuwTPWnQEI9/WvabqmrDDfDLgdXUTW08FXHSbiHuNN2S+bVeN1Wm9yeD+onHgzEn6DiW6J3fYXEfa0Q==", + "dependencies": { + "date-fns": "^2.29.3", + "date-fns-tz": "^1.3.7" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "vue": ">=3.2.0" + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", @@ -3097,6 +3128,29 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/date-fns-tz": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-1.3.8.tgz", + "integrity": "sha512-qwNXUFtMHTTU6CFSFjoJ80W8Fzzp24LntbjFFBgL/faqds4e5mo9mftoRLgr3Vi1trISsg4awSpYVsOQCRnapQ==", + "peerDependencies": { + "date-fns": ">=2.0.0" + } + }, "node_modules/de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", @@ -6652,32 +6706,12 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "node_modules/lodash.eq": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/lodash.eq/-/lodash.eq-4.0.0.tgz", - "integrity": "sha512-vbrJpXL6kQNG6TkInxX12DZRfuYVllSxhwYqjYB78g2zF3UI15nFO/0AgmZnZRnaQ38sZtjCiVjGr2rnKt4v0g==" - }, - "node_modules/lodash.isnil": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", - "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "node_modules/lodash.tointeger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.tointeger/-/lodash.tointeger-4.0.4.tgz", - "integrity": "sha512-UQbTdxNHMm4jGlf+LtxB5ZPJFufuND2gZCYjMxSOUmcxfq350x9KnwL3EzxDDNth8fOZ9wpwDTGWZQnFgdV3gg==" - }, - "node_modules/lodash.uniqueid": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.uniqueid/-/lodash.uniqueid-4.0.1.tgz", - "integrity": "sha512-GQQWaIeGlL6DIIr06kj1j6sSmBxyNMwI8kaX9aKpHR/XsMTiaXDVPNPAkiboOTK9OJpTJF/dXT3xYoFQnj386Q==" - }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -8420,18 +8454,6 @@ "he": "^1.2.0" } }, - "node_modules/vue3-treeview": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/vue3-treeview/-/vue3-treeview-0.4.1.tgz", - "integrity": "sha512-IUQN9Gzqxr66Qc6l+Js+ZBo57q5dHmiyD6FBRdt2WRnmMZ+wRtZ2UuwdLMiuGBT6gOVEzL7ucLIZGPZyKyyI4g==", - "dependencies": { - "lodash.eq": "^4.0.0", - "lodash.isnil": "^4.0.0", - "lodash.tointeger": "^4.0.4", - "lodash.uniqueid": "^4.0.1", - "vue": "^3.1.2" - } - }, "node_modules/vuetify": { "name": "@vuetify/nightly", "version": "3.3.5-pr-17265.042d482", @@ -9056,6 +9078,21 @@ "@babel/helper-plugin-utils": "^7.19.0" } }, + "@babel/runtime": { + "version": "7.23.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.1.tgz", + "integrity": "sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==", + "requires": { + "regenerator-runtime": "^0.14.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + } + } + }, "@babel/template": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", @@ -10270,6 +10307,15 @@ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz", "integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==" }, + "@vuepic/vue-datepicker": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@vuepic/vue-datepicker/-/vue-datepicker-4.5.1.tgz", + "integrity": "sha512-zdG37Q8iuwTPWnQEI9/WvabqmrDDfDLgdXUTW08FXHSbiHuNN2S+bVeN1Wm9yeD+onHgzEn6DiW6J3fYXEfa0Q==", + "requires": { + "date-fns": "^2.29.3", + "date-fns-tz": "^1.3.7" + } + }, "@webassemblyjs/ast": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", @@ -10929,6 +10975,20 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, + "date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "requires": { + "@babel/runtime": "^7.21.0" + } + }, + "date-fns-tz": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-1.3.8.tgz", + "integrity": "sha512-qwNXUFtMHTTU6CFSFjoJ80W8Fzzp24LntbjFFBgL/faqds4e5mo9mftoRLgr3Vi1trISsg4awSpYVsOQCRnapQ==", + "requires": {} + }, "de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", @@ -13557,32 +13617,12 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "lodash.eq": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/lodash.eq/-/lodash.eq-4.0.0.tgz", - "integrity": "sha512-vbrJpXL6kQNG6TkInxX12DZRfuYVllSxhwYqjYB78g2zF3UI15nFO/0AgmZnZRnaQ38sZtjCiVjGr2rnKt4v0g==" - }, - "lodash.isnil": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", - "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==" - }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "lodash.tointeger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.tointeger/-/lodash.tointeger-4.0.4.tgz", - "integrity": "sha512-UQbTdxNHMm4jGlf+LtxB5ZPJFufuND2gZCYjMxSOUmcxfq350x9KnwL3EzxDDNth8fOZ9wpwDTGWZQnFgdV3gg==" - }, - "lodash.uniqueid": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.uniqueid/-/lodash.uniqueid-4.0.1.tgz", - "integrity": "sha512-GQQWaIeGlL6DIIr06kj1j6sSmBxyNMwI8kaX9aKpHR/XsMTiaXDVPNPAkiboOTK9OJpTJF/dXT3xYoFQnj386Q==" - }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -14756,18 +14796,6 @@ "he": "^1.2.0" } }, - "vue3-treeview": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/vue3-treeview/-/vue3-treeview-0.4.1.tgz", - "integrity": "sha512-IUQN9Gzqxr66Qc6l+Js+ZBo57q5dHmiyD6FBRdt2WRnmMZ+wRtZ2UuwdLMiuGBT6gOVEzL7ucLIZGPZyKyyI4g==", - "requires": { - "lodash.eq": "^4.0.0", - "lodash.isnil": "^4.0.0", - "lodash.tointeger": "^4.0.4", - "lodash.uniqueid": "^4.0.1", - "vue": "^3.1.2" - } - }, "vuetify": { "version": "npm:@vuetify/nightly@3.3.5-pr-17265.042d482", "resolved": "https://registry.npmjs.org/@vuetify/nightly/-/nightly-3.3.5-pr-17265.042d482.tgz", diff --git a/frontend/package.json b/frontend/package.json index 73ee26b31..a01b72896 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,6 +13,7 @@ "@fortawesome/fontawesome-free": "6.4.0", "@js-joda/core": "^5.5.3", "@mdi/font": "^7.2.96", + "@vuepic/vue-datepicker": "^4.2.0", "axios": "^1.4.0", "chart.js": "^4.3.0", "chartjs-plugin-datalabels": "^2.2.0", diff --git a/frontend/src/components/secure-message/AccessUserCard.vue b/frontend/src/components/secure-message/AccessUserCard.vue index 5e06938cf..a39a25173 100644 --- a/frontend/src/components/secure-message/AccessUserCard.vue +++ b/frontend/src/components/secure-message/AccessUserCard.vue @@ -92,7 +92,8 @@ class="pt-2" :style="[editState ? {'background-color': '#e7ebf0'} : {'background-color': 'white'}]" > - +
+ - + + mdi-delete-clock-outline + + {{ formatExpiryDate(getExpiryDate(user)) }} +

+
+ +
+
+ + + +
0) { return this.instituteRoles.find((role) => role.edxRoleCode === curRole.edxRoleCode).label; } return ''; }, + formatExpiryDate(date) { + return formatDate(date, this.from, this.pickerFormat); + }, clickEditButton() { this.relinkState = false; this.deleteState = false; @@ -344,6 +382,9 @@ export default { this.deleteState = false; this.relinkState = !this.relinkState; }, + formatExpiryDate(date) { + return formatDate(date, this.from, this.pickerFormat); + }, clickActionRelinkButton() { this.isRelinking = true; const payload = { @@ -405,7 +446,8 @@ export default { const payload = { params: { edxUserID: this.user.edxUserID, - selectedRoles: this.selectedRoles + selectedRoles: this.selectedRoles, + expiryDate: this.accessExpiryDate } }; let url = Routes.edx.EXCHANGE_ACCESS_ROLES_URL; @@ -440,7 +482,11 @@ export default { }); this.selectedRoles = [...mySelection]; - } + this.accessExpiryDate = this.formatExpiryDate(this.user.edxUserSchools[0].expiryDate); + }, + clearExpiryDate(){ + this.accessExpiryDate = null; + }, } }; @@ -470,5 +516,10 @@ export default { transform: scale(1); } } + +.expiry-date { + color: grey; + text-align: right; +} diff --git a/frontend/src/components/secure-message/InviteUserPage.vue b/frontend/src/components/secure-message/InviteUserPage.vue index d2397e9fc..34d48bba9 100644 --- a/frontend/src/components/secure-message/InviteUserPage.vue +++ b/frontend/src/components/secure-message/InviteUserPage.vue @@ -114,6 +114,15 @@ + + @@ -155,12 +164,14 @@ import {Routes} from '@/utils/constants'; import {mapState} from 'pinia'; import {authStore} from '@/store/modules/auth'; import {appStore} from '@/store/modules/app'; +import DatePicker from '../util/DatePicker.vue'; export default { name: 'InviteUserPage', components: { PrimaryButton, ConfirmationDialog, + DatePicker }, mixins: [alertMixin], props: { @@ -207,7 +218,8 @@ export default { processing: false, edxAdminUserCode: '', rolesHint: 'Pick the roles to be assigned to the new user', - emailHint: 'Valid Email Required' + emailHint: 'Valid Email Required', + accessExpiryDate: null }; }, mounted() { @@ -286,7 +298,8 @@ export default { firstName: this.firstName, lastName: this.lastName, email: this.email, - edxActivationRoleCodes: this.edxActivationRoleCodes + edxActivationRoleCodes: this.edxActivationRoleCodes, + edxUserExpiryDate: this.accessExpiryDate }; let url = null; if (this.instituteTypeCode === 'SCHOOL') { @@ -316,6 +329,9 @@ export default { const isValid = this.$refs.newUserForm.validate(); this.isValidForm = isValid.valid; }, + clearExpiryDate(){ + this.accessExpiryDate = null; + }, } }; From 0851dcae8f1431f18e36276314b821bd65a93ed7 Mon Sep 17 00:00:00 2001 From: SodhiA1 <38086281+SodhiA1@users.noreply.github.com> Date: Tue, 10 Oct 2023 16:38:10 -0700 Subject: [PATCH 08/32] Update exchange.js --- backend/src/components/edx/exchange.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/components/edx/exchange.js b/backend/src/components/edx/exchange.js index f8f0dc889..8e5372148 100644 --- a/backend/src/components/edx/exchange.js +++ b/backend/src/components/edx/exchange.js @@ -627,7 +627,7 @@ async function updateEdxUserDistrictRoles(req, res) { selectedUserDistrict.updateDate = null; selectedUserDistrict.createDate = null; - selectedUserSchool.expiryDate = req.body.params.expiryDate ? LocalDate.parse(req.body.params.expiryDate).atStartOfDay().format(formatter) : null; + selectedUserDistrict.expiryDate = req.body.params.expiryDate ? LocalDate.parse(req.body.params.expiryDate).atStartOfDay().format(formatter) : null; const result = await utils.putData(token, `${config.get('server:edx:edxUsersURL')}/${selectedUserDistrict.edxUserID}/district`, selectedUserDistrict, userInfo.idir_username); return res.status(HttpStatus.OK).json(result); From 54af28298a87a1142715f5112ecb819ab29e524d Mon Sep 17 00:00:00 2001 From: SodhiA1 <38086281+SodhiA1@users.noreply.github.com> Date: Tue, 10 Oct 2023 16:39:58 -0700 Subject: [PATCH 09/32] Update AccessUserCard.vue --- frontend/src/components/secure-message/AccessUserCard.vue | 3 --- 1 file changed, 3 deletions(-) diff --git a/frontend/src/components/secure-message/AccessUserCard.vue b/frontend/src/components/secure-message/AccessUserCard.vue index a39a25173..e3c5f4dee 100644 --- a/frontend/src/components/secure-message/AccessUserCard.vue +++ b/frontend/src/components/secure-message/AccessUserCard.vue @@ -363,9 +363,6 @@ export default { } return ''; }, - formatExpiryDate(date) { - return formatDate(date, this.from, this.pickerFormat); - }, clickEditButton() { this.relinkState = false; this.deleteState = false; From c452ca2fd078650131fc08eb0b9096abd50fee4a Mon Sep 17 00:00:00 2001 From: SodhiA1 <38086281+SodhiA1@users.noreply.github.com> Date: Wed, 11 Oct 2023 10:46:54 -0700 Subject: [PATCH 10/32] removed date formatter --- backend/src/components/edx/exchange.js | 9 +++------ .../src/components/secure-message/AccessUserCard.vue | 3 ++- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/backend/src/components/edx/exchange.js b/backend/src/components/edx/exchange.js index 8e5372148..36b81103a 100644 --- a/backend/src/components/edx/exchange.js +++ b/backend/src/components/edx/exchange.js @@ -497,7 +497,6 @@ async function checkIfPrimaryCodeExists(req,res, token, instituteType, institute async function districtUserActivationInvite(req, res) { const token = utils.getBackendToken(req); - const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss'); if (!token) { return res.status(HttpStatus.UNAUTHORIZED).json({ message: 'No access token' @@ -512,7 +511,7 @@ async function districtUserActivationInvite(req, res) { const payload = { ...req.body, - edxUserExpiryDate: req.body.edxUserExpiryDate ? LocalDate.parse(req.body.edxUserExpiryDate).atStartOfDay().format(formatter) : null + edxUserExpiryDate: req.body.edxUserExpiryDate }; try { const response = await utils.postData(token, config.get('server:edx:districtUserActivationInviteURL'), payload, null, utils.getUser(req).idir_username); @@ -555,7 +554,6 @@ async function updateEdxUserSchoolRoles(req, res) { try { const token = utils.getBackendToken(req); const userInfo = utils.getUser(req); - const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss'); let edxUser = await getData(token, `${config.get('server:edx:edxUsersURL')}/${req.body.params.edxUserID}`); let selectedUserSchools = edxUser.edxUserSchools.filter(school => school.schoolID === req.body.params.schoolID); @@ -584,7 +582,7 @@ async function updateEdxUserSchoolRoles(req, res) { selectedUserSchool.updateDate = null; selectedUserSchool.createDate = null; - selectedUserSchool.expiryDate = req.body.params.expiryDate ? LocalDate.parse(req.body.params.expiryDate).atStartOfDay().format(formatter) : null; + selectedUserSchool.expiryDate = req.body.params.expiryDate; const result = await utils.putData(token, `${config.get('server:edx:edxUsersURL')}/${selectedUserSchool.edxUserID}/school`, selectedUserSchool, userInfo.idir_username); return res.status(HttpStatus.OK).json(result); @@ -598,7 +596,6 @@ async function updateEdxUserDistrictRoles(req, res) { try { const token = utils.getBackendToken(req); const userInfo = utils.getUser(req); - const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss'); let edxUser = await getData(token, `${config.get('server:edx:edxUsersURL')}/${req.body.params.edxUserID}`); let selectedUserDistricts = edxUser.edxUserDistricts.filter(district => district.districtID === req.body.params.districtId); @@ -627,7 +624,7 @@ async function updateEdxUserDistrictRoles(req, res) { selectedUserDistrict.updateDate = null; selectedUserDistrict.createDate = null; - selectedUserDistrict.expiryDate = req.body.params.expiryDate ? LocalDate.parse(req.body.params.expiryDate).atStartOfDay().format(formatter) : null; + selectedUserDistrict.expiryDate = req.body.params.expiryDate; const result = await utils.putData(token, `${config.get('server:edx:edxUsersURL')}/${selectedUserDistrict.edxUserID}/district`, selectedUserDistrict, userInfo.idir_username); return res.status(HttpStatus.OK).json(result); diff --git a/frontend/src/components/secure-message/AccessUserCard.vue b/frontend/src/components/secure-message/AccessUserCard.vue index e3c5f4dee..ef0516c5a 100644 --- a/frontend/src/components/secure-message/AccessUserCard.vue +++ b/frontend/src/components/secure-message/AccessUserCard.vue @@ -154,6 +154,7 @@ id="accessExpiryDate" v-model="accessExpiryDate" label="Access Expiry Date" + model-type="yyyy-MM-dd'T'00:00:00" @clear-date="clearExpiryDate" /> @@ -479,7 +480,7 @@ export default { }); this.selectedRoles = [...mySelection]; - this.accessExpiryDate = this.formatExpiryDate(this.user.edxUserSchools[0].expiryDate); + this.accessExpiryDate = this.user.edxUserSchools[0].expiryDate; }, clearExpiryDate(){ this.accessExpiryDate = null; From eed22aa9d0106f463ccfac189bb047fd342f6d2c Mon Sep 17 00:00:00 2001 From: SodhiA1 <38086281+SodhiA1@users.noreply.github.com> Date: Wed, 11 Oct 2023 10:49:38 -0700 Subject: [PATCH 11/32] Update exchange.js --- backend/src/components/edx/exchange.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/components/edx/exchange.js b/backend/src/components/edx/exchange.js index 36b81103a..f7515c89a 100644 --- a/backend/src/components/edx/exchange.js +++ b/backend/src/components/edx/exchange.js @@ -6,7 +6,7 @@ const config = require('../../config'); const {getData, getCodeTable, putData} = require('../utils'); const utils = require('../utils'); const {FILTER_OPERATION, VALUE_TYPE, CACHE_KEYS} = require('../../util/constants'); -const {LocalDateTime, LocalDate, DateTimeFormatter} = require('@js-joda/core'); +const {LocalDateTime, DateTimeFormatter} = require('@js-joda/core'); const cacheService = require('../cache-service'); const log = require('../logger'); From ea55ebf99bc68a2335e239e0e778d0876a6ba404 Mon Sep 17 00:00:00 2001 From: mightycox Date: Wed, 11 Oct 2023 15:32:40 -0700 Subject: [PATCH 12/32] EDX-1859 - converts all edx date pickers to standardized one --- backend/src/components/edx/exchange.js | 2 +- backend/src/components/institute/institute.js | 48 +++---- frontend/package.json | 1 + .../components/institute/AuthorityDetails.vue | 10 +- .../components/institute/AuthorityStatus.vue | 124 +++++------------ .../institute/EditAuthorityContactPage.vue | 73 ++++------ .../institute/EditDistrictContactPage.vue | 73 +++------- .../components/institute/MoveSchoolPage.vue | 19 ++- .../institute/NewAuthorityContactPage.vue | 52 ++----- .../components/institute/NewAuthorityPage.vue | 41 ++---- .../institute/NewDistrictContactPage.vue | 42 ++---- .../components/institute/NewSchoolPage.vue | 15 +- .../src/components/institute/SchoolStatus.vue | 129 ++++-------------- .../components/institute/common/Details.vue | 4 +- .../common/EditSchoolContactPage.vue | 77 ++++------- .../institute/common/NewSchoolContactPage.vue | 48 ++----- .../secure-message/ExchangePage.vue | 23 +--- frontend/src/components/util/DatePicker.vue | 20 +-- frontend/src/utils/dateHelpers.js | 4 +- 19 files changed, 236 insertions(+), 569 deletions(-) diff --git a/backend/src/components/edx/exchange.js b/backend/src/components/edx/exchange.js index 96578dcd2..1ef672401 100644 --- a/backend/src/components/edx/exchange.js +++ b/backend/src/components/edx/exchange.js @@ -709,7 +709,7 @@ const createSearchParamObject = (key, value) => { valueType = VALUE_TYPE.UUID; } else if (key === 'createDate') { value.forEach((date, index) => { - value[index] = date + 'T00:00:00'; + value[index] = date; }); if (value.length === 1) { value.push(LocalDateTime.parse(value[0]).plusHours(23).plusMinutes(59).plusSeconds(59)); diff --git a/backend/src/components/institute/institute.js b/backend/src/components/institute/institute.js index d94eba82d..76c65b3e2 100644 --- a/backend/src/components/institute/institute.js +++ b/backend/src/components/institute/institute.js @@ -58,7 +58,6 @@ async function getDistricts(req, res) { async function addDistrictContact(req, res) { try { const token = getBackendToken(req); - const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss'); let district = cacheService.getDistrictJSONByDistrictId(req.body.districtID); if(!district || !hasDistrictAdminRole(req)){ @@ -85,8 +84,8 @@ async function addDistrictContact(req, res) { phoneExtension: req.body.phoneExtension, alternatePhoneNumber: req.body.alternatePhoneNumber, alternatePhoneExtension: req.body.alternatePhoneExtension, - effectiveDate: req.body.effectiveDate ? LocalDate.parse(req.body.effectiveDate).atStartOfDay().format(formatter) : null, - expiryDate: req.body.expiryDate ? LocalDate.parse(req.body.expiryDate).atStartOfDay().format(formatter) : null + effectiveDate: req.body.effectiveDate ? req.body.effectiveDate : null, + expiryDate: req.body.expiryDate ? req.body.expiryDate : null }; const data = await utils.postData(token, url, payload, null, utils.getUser(req).idir_username); @@ -166,7 +165,6 @@ async function updateDistrict(req, res) { async function updateDistrictContact(req, res) { try { const token = getBackendToken(req); - const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss'); let district = cacheService.getDistrictJSONByDistrictId(req.body.districtId); if(!district || !hasDistrictAdminRole(req)){ @@ -185,8 +183,6 @@ async function updateDistrictContact(req, res) { params.updateDate = null; params.createDate = null; params.updateUser = utils.getUser(req).idir_username; - params.effectiveDate = params.effectiveDate ? LocalDate.parse(req.body.effectiveDate).atStartOfDay().format(formatter) : null; - params.expiryDate = req.body.expiryDate ? LocalDate.parse(req.body.expiryDate).atStartOfDay().format(formatter) : null; const result = await utils.putData(token, `${config.get('server:institute:instituteDistrictURL')}/${req.body.districtId}/contact/${req.params.contactId}` , params, utils.getUser(req).idir_username); return res.status(HttpStatus.OK).json(result); @@ -199,7 +195,6 @@ async function updateDistrictContact(req, res) { async function deleteDistrictContact(req, res) { try { const token = getBackendToken(req); - const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss'); let district = cacheService.getDistrictJSONByDistrictId(req.params.districtId); if(!district || !hasDistrictAdminRole(req)){ @@ -225,7 +220,7 @@ async function deleteDistrictContact(req, res) { contact.createDate = null; contact.updateDate = null; contact.updateUser = utils.getUser(req).idir_username; - contact.expiryDate = LocalDateTime.now().format(formatter); + contact.expiryDate = LocalDate.now().atStartOfDay().format(DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss')); await utils.putData(token, config.get('server:institute:instituteDistrictURL') + '/' + req.params.districtId + '/contact/'+ req.params.contactId , contact, utils.getUser(req).idir_username); @@ -347,8 +342,6 @@ async function addSchool(req, res) { }); } - const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss'); - const payload = { createUser: utils.getUser(req).idir_username, createDate: null, @@ -366,7 +359,7 @@ async function addSchool(req, res) { schoolOrganizationCode: req.body.schoolOrganizationCode, schoolCategoryCode: req.body.schoolCategoryCode, facilityTypeCode: req.body.facilityTypeCode, - openedDate: req.body.openedDate ? LocalDate.parse(req.body.openedDate).atStartOfDay().format(formatter) : null, + openedDate: req.body.openedDate ? req.body.openedDate : null, closedDate: null, addresses: [], grades: [], @@ -496,7 +489,6 @@ async function deleteSchoolNote(req, res) { async function addSchoolContact(req, res) { try { const token = getBackendToken(req); - const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss'); let school = cacheService.getSchoolBySchoolID(req.body.schoolID); if(!school || !hasSchoolAdminRole(req, school)){ @@ -523,8 +515,8 @@ async function addSchoolContact(req, res) { phoneExtension: req.body.phoneExtension, alternatePhoneNumber: req.body.alternatePhoneNumber, alternatePhoneExtension: req.body.alternatePhoneExtension, - effectiveDate: req.body.effectiveDate ? LocalDate.parse(req.body.effectiveDate).atStartOfDay().format(formatter) : null, - expiryDate: req.body.expiryDate ? LocalDate.parse(req.body.expiryDate).atStartOfDay().format(formatter) : null + effectiveDate: req.body.effectiveDate ? req.body.effectiveDate : null, + expiryDate: req.body.expiryDate ? req.body.expiryDate : null }; const data = await utils.postData(token, url, payload, null, utils.getUser(req).idir_username); @@ -539,7 +531,6 @@ async function addSchoolContact(req, res) { async function updateSchoolContact(req, res) { try { const token = getBackendToken(req); - const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss'); let school = cacheService.getSchoolBySchoolID(req.body.schoolID); if(!school || !hasSchoolAdminRole(req, school)){ @@ -558,8 +549,9 @@ async function updateSchoolContact(req, res) { params.updateDate = null; params.createDate = null; params.updateUser = utils.getUser(req).idir_username; - params.effectiveDate = params.effectiveDate ? LocalDate.parse(req.body.effectiveDate).atStartOfDay().format(formatter) : null; - params.expiryDate = req.body.expiryDate ? LocalDate.parse(req.body.expiryDate).atStartOfDay().format(formatter) : null; + params.effectiveDate = params.effectiveDate ? req.body.effectiveDate : null; + params.expiryDate = req.body.expiryDate ? req.body.expiryDate : null; + params.expiryDate = req.body.expiryDate ? req.body.expiryDate : null; const result = await utils.putData(token, config.get('server:institute:instituteSchoolURL') + '/' + req.body.schoolID + '/contact/'+ req.params.contactId , params, utils.getUser(req).idir_username); return res.status(HttpStatus.OK).json(result); @@ -572,7 +564,6 @@ async function updateSchoolContact(req, res) { async function deleteSchoolContact(req, res) { try { const token = getBackendToken(req); - const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss'); let school = cacheService.getSchoolBySchoolID(req.params.schoolId); if(!school || !hasSchoolAdminRole(req, school)){ @@ -598,7 +589,7 @@ async function deleteSchoolContact(req, res) { contact.createDate = null; contact.updateDate = null; contact.updateUser = utils.getUser(req).idir_username; - contact.expiryDate = LocalDateTime.now().format(formatter); + contact.expiryDate = LocalDate.now().atStartOfDay().format(DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss')); await utils.putData(token, config.get('server:institute:instituteSchoolURL') + '/' + req.params.schoolId + '/contact/'+ req.params.contactId , contact, utils.getUser(req).idir_username); @@ -612,7 +603,6 @@ async function deleteSchoolContact(req, res) { async function addAuthorityContact(req, res) { try { const token = getBackendToken(req); - const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss'); let authority = cacheService.getAuthorityJSONByAuthorityId(req.body.authorityID); if(!authority || !hasAuthorityAdminRole(req, authority)){ @@ -639,8 +629,8 @@ async function addAuthorityContact(req, res) { phoneExtension: req.body.phoneExtension, alternatePhoneNumber: req.body.alternatePhoneNumber, alternatePhoneExtension: req.body.alternatePhoneExtension, - effectiveDate: req.body.effectiveDate ? LocalDate.parse(req.body.effectiveDate).atStartOfDay().format(formatter) : null, - expiryDate: req.body.expiryDate ? LocalDate.parse(req.body.expiryDate).atStartOfDay().format(formatter) : null + effectiveDate: req.body.effectiveDate ? req.body.effectiveDate : null, + expiryDate: req.body.expiryDate ? req.body.expiryDate : null }; const data = await utils.postData(token, url, payload, null, utils.getUser(req).idir_username); @@ -655,7 +645,6 @@ async function addAuthorityContact(req, res) { async function updateAuthorityContact(req, res) { try { const token = getBackendToken(req); - const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss'); let authority = cacheService.getAuthorityJSONByAuthorityId(req.body.independentAuthorityId); if(!authority || !hasAuthorityAdminRole(req, authority)){ @@ -674,8 +663,6 @@ async function updateAuthorityContact(req, res) { params.updateDate = null; params.createDate = null; params.updateUser = utils.getUser(req).idir_username; - params.effectiveDate = params.effectiveDate ? LocalDate.parse(req.body.effectiveDate).atStartOfDay().format(formatter) : null; - params.expiryDate = req.body.expiryDate ? LocalDate.parse(req.body.expiryDate).atStartOfDay().format(formatter) : null; const result = await utils.putData(token, config.get('server:institute:instituteAuthorityURL') + '/' + req.body.independentAuthorityId + '/contact/'+ req.params.contactId , params, utils.getUser(req).idir_username); return res.status(HttpStatus.OK).json(result); @@ -688,7 +675,6 @@ async function updateAuthorityContact(req, res) { async function deleteAuthorityContact(req, res) { try { const token = getBackendToken(req); - const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss'); let authority = cacheService.getAuthorityJSONByAuthorityId(req.params.independentAuthorityId); if(!authority || !hasAuthorityAdminRole(req, authority)){ @@ -714,7 +700,7 @@ async function deleteAuthorityContact(req, res) { contact.createDate = null; contact.updateDate = null; contact.updateUser = utils.getUser(req).idir_username; - contact.expiryDate = LocalDateTime.now().format(formatter); + contact.expiryDate = LocalDate.now().atStartOfDay().format(DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss')); await utils.putData(token, config.get('server:institute:instituteAuthorityURL') + '/' + req.params.independentAuthorityId + '/contact/'+ req.params.contactId , contact, utils.getUser(req).idir_username); @@ -728,7 +714,6 @@ async function deleteAuthorityContact(req, res) { async function addAuthority(req, res) { try { const token = getBackendToken(req); - const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss'); if(!hasAuthorityAdminRole(req)){ return res.status(HttpStatus.UNAUTHORIZED).json({ @@ -742,7 +727,7 @@ async function addAuthority(req, res) { displayName: req.body.authorityName, authorityTypeCode: req.body.authorityTypeCode, - openedDate: req.body.openDate ? LocalDate.parse(req.body.openDate).atStartOfDay().format(formatter) : null, + openedDate: req.body.openDate ? req.body.openDate : null, email: req.body.email, phoneNumber: req.body.phoneNumber, faxNumber: req.body.faxNumber, @@ -991,7 +976,6 @@ async function getSchoolsPaginated(req, res){ async function moveSchool(req, res) { try { const token = getBackendToken(req); - const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss'); if(!hasSchoolAdminRole(req, req.body.toSchool)){ return res.status(HttpStatus.UNAUTHORIZED).json({ @@ -1000,7 +984,7 @@ async function moveSchool(req, res) { } const incomingPayload = req.body; - incomingPayload.toSchool.openedDate = LocalDate.parse(incomingPayload.toSchool.moveDate).atStartOfDay().format(formatter); + incomingPayload.toSchool.openedDate = incomingPayload.toSchool.moveDate; incomingPayload.toSchool.createDate = null; incomingPayload.toSchool.updateDate = null; incomingPayload.toSchool.createUser = utils.getUser(req).idir_username; @@ -1055,7 +1039,7 @@ async function moveSchool(req, res) { const payload = { toSchool: incomingPayload.toSchool, - moveDate: LocalDate.parse(incomingPayload.toSchool.moveDate).atStartOfDay().format(formatter), + moveDate: incomingPayload.toSchool.moveDate, fromSchoolId: req.body.fromSchoolId }; diff --git a/frontend/package.json b/frontend/package.json index 73ee26b31..3acf44819 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,6 +13,7 @@ "@fortawesome/fontawesome-free": "6.4.0", "@js-joda/core": "^5.5.3", "@mdi/font": "^7.2.96", + "@vuepic/vue-datepicker": "^7.0.0", "axios": "^1.4.0", "chart.js": "^4.3.0", "chartjs-plugin-datalabels": "^2.2.0", diff --git a/frontend/src/components/institute/AuthorityDetails.vue b/frontend/src/components/institute/AuthorityDetails.vue index 87a2a5ede..000849212 100644 --- a/frontend/src/components/institute/AuthorityDetails.vue +++ b/frontend/src/components/institute/AuthorityDetails.vue @@ -797,8 +797,8 @@ :date-of-last-school-closure="closedDateOfLastClosingSchool" :list-of-open-schools="listOfOpenSchools" :list-of-closing-schools="listOfClosingSchools" - @updateAuthorityDates="handleUpdatesToAuthorityStatus" - @authorityStatus:closeEditAuthorityStatusPage="openAuthorityStatusEditCard = !openAuthorityStatusEditCard" + @update-authority-dates="handleUpdatesToAuthorityStatus" + @authority-status:close-edit-authority-status-page="openAuthorityStatusEditCard = !openAuthorityStatusEditCard" /> @@ -961,7 +961,7 @@ export default { } }).then(response => { this.listOfClosingSchools = response.data.content; - this.closedDateOfLastClosingSchool = response.data.content[0] ? response.data.content[0].closedDate.substring(0, 10) : null; + this.closedDateOfLastClosingSchool = response.data.content[0] ? response.data.content[0].closedDate : null; }).catch(error => { console.error(error); this.setFailureAlert(error?.response?.data?.message ? error?.response?.data?.message : 'An error occurred while trying to find the closed date of the last closing school. Please try again later.'); @@ -1107,12 +1107,12 @@ export default { async handleUpdatesToAuthorityStatus(updatedDatesForAuthority) { await this.$nextTick(); if (updatedDatesForAuthority.openedDate) { - this.authorityCopy.openedDate = updatedDatesForAuthority.openedDate?.replaceAll('/', '-').concat('T00:00:00'); + this.authorityCopy.openedDate = updatedDatesForAuthority.openedDate; } else { this.authorityCopy.openedDate = null; } if (updatedDatesForAuthority.closedDate) { - this.authorityCopy.closedDate = updatedDatesForAuthority.closedDate?.replaceAll('/', '-').concat('T00:00:00'); + this.authorityCopy.closedDate = updatedDatesForAuthority.closedDate; } else { this.authorityCopy.closedDate = null; } diff --git a/frontend/src/components/institute/AuthorityStatus.vue b/frontend/src/components/institute/AuthorityStatus.vue index ce491245a..023c8614b 100644 --- a/frontend/src/components/institute/AuthorityStatus.vue +++ b/frontend/src/components/institute/AuthorityStatus.vue @@ -55,15 +55,13 @@ - @@ -104,8 +102,9 @@ v-if="showAlertForFutureClosureDate" color="#003366" density="compact" - text type="info" + class="px-2" + variant="tonal" >

Some schools under this authority have closing dates in the future.

@@ -132,23 +131,20 @@

Refresh the page to see an updated list of schools.

- +

Select the closure date

- @@ -164,8 +160,9 @@ v-if="showAlertForFutureClosureDate" color="#003366" density="compact" - text type="info" + class="px-2" + variant="tonal" >

Some schools under this authority have closing dates in the future.

@@ -192,23 +189,20 @@

Refresh the page to see an updated list of schools.

- +

Select the new closing date

- @@ -243,12 +237,13 @@ import PrimaryButton from '../util/PrimaryButton.vue'; import alertMixin from '@/mixins/alertMixin'; import * as Rules from '@/utils/institute/formRules'; import {formatDate, formatDisplayDate} from '@/utils/format'; -import {parseDate} from '@/utils/dateHelpers'; -import {LocalDate} from '@js-joda/core'; +import {DateTimeFormatter, LocalDate, LocalDateTime} from '@js-joda/core'; +import DatePicker from '@/components/util/DatePicker.vue'; export default { name: 'AuthorityStatus', components: { + DatePicker, PrimaryButton, }, mixins: [alertMixin], @@ -283,16 +278,17 @@ export default { } }, data() { - let currentLocalDate = LocalDate.now().toString(); + let currentLocalDate = LocalDate.now(); return { - currentDate: currentLocalDate, + currentDate: currentLocalDate.toString(), + cutOffDate: currentLocalDate.plusDays(1).toString(), isFormValid: false, processing: false, rules: Rules, action: this.defaultUpdateActionForAuthority(), - newOpenDate: currentLocalDate, + newOpenDate: currentLocalDate.atStartOfDay().format(DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss')), newCloseDate: null, - updatedCloseDate: this.parseDate(formatDate(this.authorityCloseDate)) + updatedCloseDate: this.authorityCloseDate }; }, computed: { @@ -318,37 +314,13 @@ export default { if (!this.dateOfLastSchoolClosure) { return ''; } - return this.formatDisplayDate(this.dateOfLastSchoolClosure); + return this.formatDate(this.dateOfLastSchoolClosure); }, showAlertForFutureClosureDate() { if (!this.dateOfLastSchoolClosure) { return false; } - return LocalDate.now().isBefore(LocalDate.parse(this.dateOfLastSchoolClosure)); - }, - newOpenDateFormatted: { - get() { - return this.formatDisplayDate(this.newOpenDate); - }, - set(newValue) { - this.newOpenDate = this.parseDate(newValue); - } - }, - newCloseDateFormatted: { - get() { - return this.formatDisplayDate(this.newCloseDate); - }, - set(newValue) { - this.newCloseDate = this.parseDate(newValue); - } - }, - updatedCloseDateFormatted: { - get() { - return this.formatDisplayDate(this.updatedCloseDate); - }, - set(newValue) { - this.updatedCloseDate = this.parseDate(newValue); - } + return LocalDateTime.now().isBefore(LocalDateTime.parse(this.dateOfLastSchoolClosure)); }, listOfOpenSchoolsByMincode() { return [...this.listOfOpenSchools].sort((schoolA, schoolB) => { @@ -361,24 +333,6 @@ export default { }); }, }, - watch: { - //watching effective date to valid form because we need to cross validate expiry and effective date fields - 'newOpenDate': { - handler() { - this.validateForm(); - } - }, - 'newCloseDate': { - handler() { - this.validateForm(); - } - }, - 'updatedCloseDate': { - handler() { - this.validateForm(); - } - } - }, mounted() { this.validateForm(); }, @@ -394,15 +348,6 @@ export default { return ''; } }, - saveNewOpenDate(date) { - this.$refs.newOpenDateFilter.save(date); - }, - saveNewCloseDate(date) { - this.$refs.newCloseDateFilter.save(date); - }, - saveUpdatedCloseDate(date) { - this.$refs.updatedCloseDateFilter.save(date); - }, closeEditAuthorityStatus() { this.resetForm(); this.$emit('authorityStatus:closeEditAuthorityStatusPage'); @@ -416,20 +361,20 @@ export default { switch (this.action) { case 'setOpenDate': updatedAuthorityDates = { - openedDate: this.newOpenDateFormatted, + openedDate: this.newOpenDate, closedDate: null, }; break; case 'setCloseDate': updatedAuthorityDates = { - openedDate: this.parseDate(formatDate(this.authorityOpenDate)), - closedDate: this.newCloseDateFormatted + openedDate: this.authorityOpenDate, + closedDate: this.newCloseDate }; break; case 'updateCloseDate': updatedAuthorityDates = { - openedDate: this.parseDate(formatDate(this.authorityOpenDate)), - closedDate: this.updatedCloseDateFormatted, + openedDate: this.authorityOpenDate, + closedDate: this.updatedCloseDate, }; break; default: @@ -451,8 +396,7 @@ export default { this.isFormValid = isValid.valid; }, formatDate, - formatDisplayDate, - parseDate + formatDisplayDate } }; diff --git a/frontend/src/components/institute/EditAuthorityContactPage.vue b/frontend/src/components/institute/EditAuthorityContactPage.vue index 5cafcd142..00277c399 100644 --- a/frontend/src/components/institute/EditAuthorityContactPage.vue +++ b/frontend/src/components/institute/EditAuthorityContactPage.vue @@ -105,29 +105,25 @@ - - @@ -162,14 +158,14 @@ import ApiService from '@/common/apiService'; import {Routes} from '@/utils/constants'; import * as Rules from '@/utils/institute/formRules'; import {isNumber} from '@/utils/institute/formInput'; -import {formatDate, formatDisplayDate} from '@/utils/format'; -import {parseDate} from '@/utils/dateHelpers'; import {authStore} from '@/store/modules/auth'; import _ from 'lodash'; +import DatePicker from '@/components/util/DatePicker.vue'; export default { name: 'EditAuthorityContactPage', components: { + DatePicker, PrimaryButton, }, mixins: [alertMixin], @@ -189,40 +185,28 @@ export default { }, data() { let clonedContact = _.cloneDeep(this.contact); - clonedContact.effectiveDate = this.parseDate(formatDate(clonedContact.effectiveDate)); - clonedContact.expiryDate = this.parseDate(formatDate(clonedContact.expiryDate)); return { isFormValid: false, processing: false, editContact: clonedContact, - rules: Rules, - editAuthorityContactEffectiveDatePicker: null, - editAuthorityContactExpiryDatePicker: null + rules: Rules }; }, - mounted() { - this.validateForm(); - }, computed: { ...mapState(authStore, ['isAuthenticated', 'userInfo']), - authorityContactEffectiveDateFormatted: { - get() { - return this.formatDisplayDate(this.editContact.effectiveDate); - }, - set(newValue) { - this.editContact.effectiveDate = this.parseDate(newValue); - } - }, - authorityContactExpiryDateFormatted: { - get() { - return this.formatDisplayDate(this.editContact.expiryDate); - }, - set(newValue) { - this.editContact.expiryDate = this.parseDate(newValue); - } - } + }, + mounted() { + this.validateForm(); }, methods: { + clearEffectiveDate() { + this.editContact.effectiveDate = null; + this.validateForm(); + }, + clearExpiryDate() { + this.editContact.expiryDate = null; + this.validateForm(); + }, cancelEditAuthorityContactPage() { this.resetForm(); this.$emit('editAuthorityContact:cancelEditAuthorityContactPage'); @@ -254,18 +238,7 @@ export default { const isValid = await this.$refs.editAuthorityContactForm.validate(); this.isFormValid = isValid.valid; }, - isNumber, - formatDate, - formatDisplayDate, - parseDate - }, - watch: { - //watching effective date to valid form because we need to cross validate expiry and effective date fields - 'editContact.effectiveDate': { - handler() { - this.validateForm(); - } - } + isNumber } }; diff --git a/frontend/src/components/institute/EditDistrictContactPage.vue b/frontend/src/components/institute/EditDistrictContactPage.vue index 37ae86874..9795d2c4d 100644 --- a/frontend/src/components/institute/EditDistrictContactPage.vue +++ b/frontend/src/components/institute/EditDistrictContactPage.vue @@ -114,29 +114,25 @@
- - @@ -171,14 +167,14 @@ import ApiService from '@/common/apiService'; import {Routes} from '@/utils/constants'; import * as Rules from '@/utils/institute/formRules'; import {isNumber} from '@/utils/institute/formInput'; -import {formatDate, formatDisplayDate} from '@/utils/format'; -import {parseDate} from '@/utils/dateHelpers'; import {authStore} from '@/store/modules/auth'; import _ from 'lodash'; +import DatePicker from '@/components/util/DatePicker.vue'; export default { name: 'EditDistrictContactPage', components: { + DatePicker, PrimaryButton, }, mixins: [alertMixin], @@ -199,48 +195,28 @@ export default { emits: ['edit-district-contact:cancel-edit-district-contact-page', 'edit-district-contact:edit-district-contact-success'], data() { let clonedContact = _.cloneDeep(this.contact); - clonedContact.effectiveDate = this.parseDate(formatDate(clonedContact.effectiveDate)); - clonedContact.expiryDate = this.parseDate(formatDate(clonedContact.expiryDate)); return { isFormValid: false, processing: false, editContact: clonedContact, - rules: Rules, - editDistrictContactEffectiveDatePicker: null, - editDistrictContactExpiryDatePicker: null + rules: Rules }; }, computed: { ...mapState(authStore, ['isAuthenticated', 'userInfo']), - districtContactEffectiveDateFormatted: { - get() { - return this.formatDisplayDate(this.editContact.effectiveDate); - }, - set(newValue) { - this.editContact.effectiveDate = this.parseDate(newValue); - } - }, - districtContactExpiryDateFormatted: { - get() { - return this.formatDisplayDate(this.editContact.expiryDate); - }, - set(newValue) { - this.editContact.expiryDate = this.parseDate(newValue); - } - } - }, - watch: { - //watching effective date to valid form because we need to cross validate expiry and effective date fields - 'editContact.effectiveDate': { - handler() { - this.validateForm(); - } - } }, mounted() { this.validateForm(); }, methods: { + clearEffectiveDate() { + this.editContact.effectiveDate = null; + this.validateForm(); + }, + clearExpiryDate() { + this.editContact.expiryDate = null; + this.validateForm(); + }, cancelEditDistrictContactPage() { this.resetForm(); this.$emit('edit-district-contact:cancel-edit-district-contact-page'); @@ -265,12 +241,6 @@ export default { this.processing = false; }); }, - saveEditDistrictContactEffectiveDate(date) { - this.$refs.editDistrictContactEffectiveDateFilter.save(date); - }, - saveEditDistrictContactExpiryDate(date) { - this.$refs.editDistrictContactExpiryDateFilter.save(date); - }, resetForm() { this.$refs.editDistrictContactForm.reset(); }, @@ -278,10 +248,7 @@ export default { const isValid = await this.$refs.editDistrictContactForm.validate(); this.isFormValid = isValid.valid; }, - isNumber, - formatDate, - formatDisplayDate, - parseDate + isNumber } }; diff --git a/frontend/src/components/institute/MoveSchoolPage.vue b/frontend/src/components/institute/MoveSchoolPage.vue index ba2014188..0d7cd0619 100644 --- a/frontend/src/components/institute/MoveSchoolPage.vue +++ b/frontend/src/components/institute/MoveSchoolPage.vue @@ -47,14 +47,13 @@
- @@ -492,14 +491,16 @@ import * as Rules from '@/utils/institute/formRules'; import {sortByNameValue, formatDate} from '@/utils/format'; import {isNumber} from '@/utils/institute/formInput'; import {sortBy} from 'lodash'; -import {LocalDate} from '@js-joda/core'; +import {DateTimeFormatter, LocalDate} from '@js-joda/core'; import {isOpenNotClosingAuthority} from '@/utils/common'; import {authStore} from '@/store/modules/auth'; import {instituteStore} from '@/store/modules/institute'; +import DatePicker from '@/components/util/DatePicker.vue'; export default { name: 'MoveSchoolPage', components: { + DatePicker, PrimaryButton, }, mixins: [alertMixin], @@ -564,7 +565,6 @@ export default { moveDate: this.calculateDefaultMoveDate(), }, rules: Rules, - moveDatePicker: null, sameAsMailingCheckbox: true, showAddress: false, }; @@ -720,9 +720,6 @@ export default { } return true; }, - saveMoveSchoolDate(date) { - this.$refs.moveDateFilter.save(date); - }, closeMoveSchoolPage() { this.$emit('moveSchool:closeMoveSchoolPage'); }, @@ -746,7 +743,7 @@ export default { }); }, calculateDefaultMoveDate() { - return (LocalDate.now()).toString(); + return (LocalDate.now().atStartOfDay().format(DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss'))).toString(); }, schoolDistrictChanged() { this.schoolCategoryDisabled = false; diff --git a/frontend/src/components/institute/NewAuthorityContactPage.vue b/frontend/src/components/institute/NewAuthorityContactPage.vue index 0445c7032..208bc83a3 100644 --- a/frontend/src/components/institute/NewAuthorityContactPage.vue +++ b/frontend/src/components/institute/NewAuthorityContactPage.vue @@ -105,28 +105,22 @@ - - @@ -146,9 +140,9 @@ id="newContactPostBtn" text="Save" width="7rem" - @click-action="addNewAuthorityContact" :disabled="!isFormValid" :loading="processing" + @click-action="addNewAuthorityContact" /> @@ -162,12 +156,14 @@ import ApiService from '@/common/apiService'; import {Routes} from '@/utils/constants'; import * as Rules from '@/utils/institute/formRules'; import {isNumber} from '@/utils/institute/formInput'; -import {LocalDate} from '@js-joda/core'; +import {DateTimeFormatter, LocalDate} from '@js-joda/core'; import {authStore} from '@/store/modules/auth'; +import DatePicker from '@/components/util/DatePicker.vue'; export default { name: 'NewAuthorityContactPage', components: { + DatePicker, PrimaryButton, }, mixins: [alertMixin], @@ -194,29 +190,19 @@ export default { phoneExtension: null, alternatePhoneNumber: null, alternatePhoneExtension: null, - effectiveDate: LocalDate.now().toString(), + effectiveDate: LocalDate.now().atStartOfDay().format(DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss')).toString(), expiryDate: null }, - rules: Rules, - effectiveDateFilter: false, - newContactEffectiveDatePicker: null, - expiryDateFilter: false, - newContactExpiryDatePicker: null + rules: Rules }; }, - mounted() { - this.validateForm(); - }, computed: { ...mapState(authStore, ['isAuthenticated', 'userInfo']), }, + mounted() { + this.validateForm(); + }, methods: { - saveNewContactEffectiveDate(date) { - this.$refs.newContactEffectiveDateFilter.save(date); - }, - saveNewContactExpiryDate(date) { - this.$refs.newContactExpiryDateFilter.save(date); - }, closeNewContactPage() { this.resetForm(); this.$emit('newAuthorityContact:closeNewAuthorityContactPage'); @@ -247,14 +233,6 @@ export default { this.isFormValid = isValid.valid; }, isNumber, - }, - watch: { - //watching effective date to valid form because we need to cross validate expiry and effective date fields - 'newContact.effectiveDate': { - handler() { - this.validateForm(); - } - } } }; diff --git a/frontend/src/components/institute/NewAuthorityPage.vue b/frontend/src/components/institute/NewAuthorityPage.vue index bbfa57d6c..1003bcf71 100644 --- a/frontend/src/components/institute/NewAuthorityPage.vue +++ b/frontend/src/components/institute/NewAuthorityPage.vue @@ -31,20 +31,16 @@ variant="underlined" :items="authorityTypes" :rules="[rules.required()]" - clearable + :clearable="true" /> - @@ -256,9 +252,9 @@ id="newAuthorityPostBtn" text="Save" width="7rem" - @click-action="addNewAuthority" :disabled="!isFormValid" :loading="processing" + @click-action="addNewAuthority" /> @@ -272,21 +268,23 @@ import ApiService from '@/common/apiService'; import {Routes} from '@/utils/constants'; import * as Rules from '@/utils/institute/formRules'; import {isNumber} from '@/utils/institute/formInput'; -import {LocalDate} from '@js-joda/core'; +import {DateTimeFormatter, LocalDate} from '@js-joda/core'; import {authStore} from '@/store/modules/auth'; import {instituteStore} from '@/store/modules/institute'; +import DatePicker from '@/components/util/DatePicker.vue'; export default { name: 'NewAuthorityPage', components: { + DatePicker, PrimaryButton }, mixins: [alertMixin], + emits: ['newAuthority:closeNewAuthorityPage'], data() { return { isFormValid: false, processing: false, - localDate: LocalDate, newAuthority: { authorityName: null, authorityTypeCode: null, @@ -308,7 +306,6 @@ export default { physicalAddrPostal: null, }, rules: Rules, - newAuthorityOpenDatePicker: null, schoolFacilityTypes: [], schoolCategoryTypes: [], schoolOrganizationTypes: [], @@ -323,9 +320,6 @@ export default { ], }; }, - mounted() { - this.validateForm(); - }, computed: { ...mapState(authStore, ['isAuthenticated', 'userInfo']), ...mapState(instituteStore, ['authorityTypeCodes', 'provinceCodes', 'countryCodes']), @@ -333,6 +327,9 @@ export default { return !this.excludeShowingPhysicalAddressesForAuthoritiesOfType.includes(this.newAuthority.authorityTypeCode); } }, + mounted() { + this.validateForm(); + }, created() { const instStore = instituteStore(); instStore.getAllAuthorityTypeCodes().then(() => { @@ -347,10 +344,7 @@ export default { }, methods: { calculateDefaultOpenDate() { - return LocalDate.now().toString(); - }, - openDatePicker() { - this.$refs.newAuthorityDatePicker.openMenu(); + return LocalDate.now().atStartOfDay().format(DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss')).toString(); }, closeNewAuthorityPage() { this.resetForm(); @@ -401,13 +395,4 @@ export default { font-size: medium !important; font-weight: bolder !important; } - -:deep(.dp__input) { - display: none; -} - -:deep(.dp__icon) { - display: none; -} - diff --git a/frontend/src/components/institute/NewDistrictContactPage.vue b/frontend/src/components/institute/NewDistrictContactPage.vue index df69a1f65..7c72441d2 100644 --- a/frontend/src/components/institute/NewDistrictContactPage.vue +++ b/frontend/src/components/institute/NewDistrictContactPage.vue @@ -114,28 +114,22 @@ - - @@ -171,12 +165,14 @@ import ApiService from '@/common/apiService'; import {Routes} from '@/utils/constants'; import * as Rules from '@/utils/institute/formRules'; import {isNumber} from '@/utils/institute/formInput'; -import {LocalDate} from '@js-joda/core'; +import {DateTimeFormatter, LocalDate} from '@js-joda/core'; import {authStore} from '@/store/modules/auth'; +import DatePicker from '@/components/util/DatePicker.vue'; export default { name: 'NewDistrictContactPage', components: { + DatePicker, PrimaryButton, }, mixins: [alertMixin], @@ -205,35 +201,19 @@ export default { phoneExtension: null, alternatePhoneNumber: null, alternatePhoneExtension: null, - effectiveDate: LocalDate.now().toString(), + effectiveDate: LocalDate.now().atStartOfDay().format(DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss')).toString(), expiryDate: null }, - rules: Rules, - newContactEffectiveDatePicker: null, - newContactExpiryDatePicker: null + rules: Rules }; }, computed: { ...mapState(authStore, ['isAuthenticated', 'userInfo']), }, - watch: { - //watching effective date to valid form because we need to cross validate expiry and effective date fields - 'newContact.effectiveDate': { - handler() { - this.validateForm(); - } - } - }, mounted() { this.validateForm(); }, methods: { - saveNewContactEffectiveDate(date) { - this.$refs.newContactEffectiveDateFilter.save(date); - }, - saveNewContactExpiryDate(date) { - this.$refs.newContactExpiryDateFilter.save(date); - }, closeNewContactPage() { this.resetForm(); this.$emit('new-district-contact:close-new-district-contact-page', 'new-district-contact:add-new-district-contact'); diff --git a/frontend/src/components/institute/NewSchoolPage.vue b/frontend/src/components/institute/NewSchoolPage.vue index cf71cf71a..da2aa7f59 100644 --- a/frontend/src/components/institute/NewSchoolPage.vue +++ b/frontend/src/components/institute/NewSchoolPage.vue @@ -66,15 +66,12 @@ /> - @@ -499,10 +496,12 @@ import {findUpcomingDate} from '@/utils/dateHelpers'; import {sortBy} from 'lodash'; import {authStore} from '@/store/modules/auth'; import {instituteStore} from '@/store/modules/institute'; +import DatePicker from '@/components/util/DatePicker.vue'; export default { name: 'NewSchoolPage', components: { + DatePicker, PrimaryButton }, mixins: [alertMixin], @@ -565,7 +564,6 @@ export default { physicalAddrPostal: null, }, rules: Rules, - newSchoolOpenDatePicker: null, sameAsMailingCheckbox: true, showAddress: false, addressButton: { @@ -646,9 +644,6 @@ export default { this.preselectSchoolDistrict(); }, methods: { - openEffectiveDatePicker() { - this.$refs.newSchoolDatePicker.openMenu(); - }, preselectSchoolDistrict() { if (this.filteredDistrictNames.length !== 1) { return; diff --git a/frontend/src/components/institute/SchoolStatus.vue b/frontend/src/components/institute/SchoolStatus.vue index c4891c296..725fd97dc 100644 --- a/frontend/src/components/institute/SchoolStatus.vue +++ b/frontend/src/components/institute/SchoolStatus.vue @@ -97,15 +97,12 @@ - @@ -122,15 +119,12 @@ - @@ -147,15 +141,12 @@ - @@ -172,15 +163,12 @@ - @@ -215,12 +203,14 @@ import PrimaryButton from '../util/PrimaryButton.vue'; import alertMixin from '@/mixins/alertMixin'; import * as Rules from '@/utils/institute/formRules'; -import {formatDate, formatDisplayDate} from '@/utils/format'; -import {findUpcomingDate, parseDate} from '@/utils/dateHelpers'; +import {formatDate} from '@/utils/format'; +import {findUpcomingDate} from '@/utils/dateHelpers'; +import DatePicker from '@/components/util/DatePicker.vue'; export default { name: 'SchoolStatus', components: { + DatePicker, PrimaryButton, }, mixins: [alertMixin], @@ -249,9 +239,9 @@ export default { rules: Rules, action: this.defaultUpdateActionForSchool(), newOpenDate: findUpcomingDate(7, 1).toString(), - updatedOpenDate: this.parseDate(formatDate(this.schoolOpenDate)), + updatedOpenDate: this.schoolOpenDate, newCloseDate: findUpcomingDate(6, 30).toString(), - updatedCloseDate: this.parseDate(formatDate(this.schoolCloseDate)) + updatedCloseDate: this.schoolCloseDate }; }, computed: { @@ -270,38 +260,6 @@ export default { displayOptionsForNeverOpenedSchoolStatus() { return this.schoolStatus === 'Never Opened'; }, - newOpenDateFormatted: { - get() { - return this.formatDisplayDate(this.newOpenDate); - }, - set(newValue) { - this.newOpenDate = this.parseDate(newValue); - } - }, - updatedOpenDateFormatted: { - get() { - return this.formatDisplayDate(this.updatedOpenDate); - }, - set(newValue) { - this.updatedOpenDate = this.parseDate(newValue); - } - }, - newCloseDateFormatted: { - get() { - return this.formatDisplayDate(this.newCloseDate); - }, - set(newValue) { - this.newCloseDate = this.parseDate(newValue); - } - }, - updatedCloseDateFormatted: { - get() { - return this.formatDisplayDate(this.updatedCloseDate); - }, - set(newValue) { - this.updatedCloseDate = this.parseDate(newValue); - } - }, openDateFormatted() { if (!this.schoolOpenDate) { return ''; @@ -309,29 +267,6 @@ export default { return this.formatDate(this.schoolOpenDate); } }, - watch: { - //watching effective date to valid form because we need to cross validate expiry and effective date fields - 'newOpenDate': { - handler() { - this.validateForm(); - } - }, - 'updatedOpenDate': { - handler() { - this.validateForm(); - } - }, - 'newCloseDate': { - handler() { - this.validateForm(); - } - }, - 'updatedCloseDate': { - handler() { - this.validateForm(); - } - } - }, mounted() { if (this.isSchoolStatusUpdateAllowed) { this.validateForm(); @@ -352,18 +287,6 @@ export default { return ''; } }, - saveNewOpenDate(date) { - this.$refs.newOpenDateFilter.save(date); - }, - saveUpdatedOpenDate(date) { - this.$refs.updatedOpenDateFilter.save(date); - }, - saveNewCloseDate(date) { - this.$refs.newCloseDateFilter.save(date); - }, - saveUpdatedCloseDate(date) { - this.$refs.updatedCloseDateFilter.save(date); - }, closeEditSchoolStatus() { this.resetForm(); this.$emit('schoolStatus:closeEditSchoolStatusPage'); @@ -382,26 +305,26 @@ export default { break; case 'setOpenDate': updatedSchoolDates = { - openedDate: this.newOpenDateFormatted, + openedDate: this.newOpenDate, closedDate: null, }; break; case 'updateOpenDate': updatedSchoolDates = { - openedDate: this.updatedOpenDateFormatted, + openedDate: this.updatedOpenDate, closedDate: null, }; break; case 'setCloseDate': updatedSchoolDates = { - openedDate: this.parseDate(formatDate(this.schoolOpenDate)), - closedDate: this.newCloseDateFormatted + openedDate: this.schoolOpenDate, + closedDate: this.newCloseDate }; break; case 'updateCloseDate': updatedSchoolDates = { - openedDate: this.parseDate(formatDate(this.schoolOpenDate)), - closedDate: this.updatedCloseDateFormatted, + openedDate: this.schoolOpenDate, + closedDate: this.updatedCloseDate, }; break; default: @@ -418,9 +341,7 @@ export default { const isValid = await this.$refs.schoolStatusForm.validate(); this.isFormValid = isValid.valid; }, - formatDate, - formatDisplayDate, - parseDate + formatDate } }; diff --git a/frontend/src/components/institute/common/Details.vue b/frontend/src/components/institute/common/Details.vue index aabe5e863..05d87b974 100644 --- a/frontend/src/components/institute/common/Details.vue +++ b/frontend/src/components/institute/common/Details.vue @@ -1589,12 +1589,12 @@ export default { async handleUpdatesToSchoolStatus(updatedDatesForSchool) { await this.$nextTick(); if (updatedDatesForSchool.openedDate) { - this.schoolDetailsCopy.openedDate = updatedDatesForSchool.openedDate?.replaceAll('/', '-').concat('T00:00:00'); + this.schoolDetailsCopy.openedDate = updatedDatesForSchool.openedDate; } else { this.schoolDetailsCopy.openedDate = null; } if (updatedDatesForSchool.closedDate) { - this.schoolDetailsCopy.closedDate = updatedDatesForSchool.closedDate?.replaceAll('/', '-').concat('T00:00:00'); + this.schoolDetailsCopy.closedDate = updatedDatesForSchool.closedDate; } else { this.schoolDetailsCopy.closedDate = null; } diff --git a/frontend/src/components/institute/common/EditSchoolContactPage.vue b/frontend/src/components/institute/common/EditSchoolContactPage.vue index 2540e0435..da78e3495 100644 --- a/frontend/src/components/institute/common/EditSchoolContactPage.vue +++ b/frontend/src/components/institute/common/EditSchoolContactPage.vue @@ -114,28 +114,23 @@ - - @@ -154,9 +149,9 @@ id="saveChangesToSchoolContactButton" text="Save" width="7rem" - @click-action="saveChangesToSchoolContact" :disabled="!isFormValid" :loading="processing" + @click-action="saveChangesToSchoolContact" /> @@ -170,14 +165,14 @@ import ApiService from '@/common/apiService'; import {Routes} from '@/utils/constants'; import * as Rules from '@/utils/institute/formRules'; import {isNumber} from '@/utils/institute/formInput'; -import {formatDate, formatDisplayDate} from '@/utils/format'; -import {parseDate} from '@/utils/dateHelpers'; import {authStore} from '@/store/modules/auth'; import _ from 'lodash'; +import DatePicker from '@/components/util/DatePicker.vue'; export default { name: 'EditSchoolContactPage', components: { + DatePicker, PrimaryButton, }, mixins: [alertMixin], @@ -195,42 +190,32 @@ export default { required: true } }, + emits: ['editSchoolContact:editSchoolContactSuccess', 'editSchoolContact:cancelEditSchoolContactPage'], data() { let clonedContact = _.cloneDeep(this.contact); - clonedContact.effectiveDate = this.parseDate(formatDate(clonedContact.effectiveDate)); - clonedContact.expiryDate = this.parseDate(formatDate(clonedContact.expiryDate)); return { isFormValid: false, processing: false, editContact: clonedContact, - rules: Rules, - editSchoolContactEffectiveDatePicker: null, - editSchoolContactExpiryDatePicker: null + rules: Rules }; }, - mounted() { - this.validateForm(); - }, computed: { ...mapState(authStore, ['isAuthenticated', 'userInfo']), - schoolContactEffectiveDateFormatted: { - get() { - return this.formatDisplayDate(this.editContact.effectiveDate); - }, - set(newValue) { - this.editContact.effectiveDate = this.parseDate(newValue); - } - }, - schoolContactExpiryDateFormatted: { - get() { - return this.formatDisplayDate(this.editContact.expiryDate); - }, - set(newValue) { - this.editContact.expiryDate = this.parseDate(newValue); - } - } + }, + + mounted() { + this.validateForm(); }, methods: { + clearEffectiveDate() { + this.editContact.effectiveDate = null; + this.validateForm(); + }, + clearExpiryDate() { + this.editContact.expiryDate = null; + this.validateForm(); + }, cancelEditSchoolContactPage() { this.resetForm(); this.$emit('editSchoolContact:cancelEditSchoolContactPage'); @@ -255,9 +240,6 @@ export default { this.processing = false; }); }, - saveEditSchoolContactExpiryDate(date) { - this.$refs.editSchoolContactExpiryDateFilter.save(date); - }, resetForm() { this.$refs.editSchoolContactForm.reset(); }, @@ -267,18 +249,7 @@ export default { this.isFormValid = isValid.valid; } }, - isNumber, - formatDate, - formatDisplayDate, - parseDate - }, - watch: { - //watching effective date to valid form because we need to cross validate expiry and effective date fields - 'editContact.effectiveDate': { - handler() { - this.validateForm(); - } - } + isNumber } }; diff --git a/frontend/src/components/institute/common/NewSchoolContactPage.vue b/frontend/src/components/institute/common/NewSchoolContactPage.vue index 11a5a47a8..75d9384a8 100644 --- a/frontend/src/components/institute/common/NewSchoolContactPage.vue +++ b/frontend/src/components/institute/common/NewSchoolContactPage.vue @@ -114,28 +114,22 @@
- - @@ -171,12 +165,14 @@ import ApiService from '@/common/apiService'; import {Routes} from '@/utils/constants'; import * as Rules from '@/utils/institute/formRules'; import {isNumber} from '@/utils/institute/formInput'; -import {LocalDate} from '@js-joda/core'; +import {DateTimeFormatter, LocalDate} from '@js-joda/core'; import {authStore} from '@/store/modules/auth'; +import DatePicker from '@/components/util/DatePicker.vue'; export default { name: 'NewSchoolContactPage', components: { + DatePicker, PrimaryButton, }, mixins: [alertMixin], @@ -204,27 +200,19 @@ export default { phoneExtension: null, alternatePhoneNumber: null, alternatePhoneExtension: null, - effectiveDate: LocalDate.now().toString(), + effectiveDate: LocalDate.now().atStartOfDay().format(DateTimeFormatter.ofPattern('yyyy-MM-dd\'T\'HH:mm:ss')).toString(), expiryDate: null }, - rules: Rules, - newContactEffectiveDatePicker: null, - newContactExpiryDatePicker: null + rules: Rules }; }, - mounted() { - this.validateForm(); - }, computed: { ...mapState(authStore, ['isAuthenticated', 'userInfo']), }, + mounted() { + this.validateForm(); + }, methods: { - saveNewContactEffectiveDate(date) { - this.$refs.newContactEffectiveDateFilter.save(date); - }, - saveNewContactExpiryDate(date) { - this.$refs.newContactExpiryDateFilter.save(date); - }, closeNewContactPage() { this.resetForm(); this.$emit('newSchoolContact:closeNewSchoolContactPage'); @@ -255,14 +243,6 @@ export default { this.isFormValid = isValid.valid; }, isNumber, - }, - watch: { - //watching effective date to valid form because we need to cross validate expiry and effective date fields - 'newContact.effectiveDate': { - handler() { - this.validateForm(); - } - } } }; diff --git a/frontend/src/components/secure-message/ExchangePage.vue b/frontend/src/components/secure-message/ExchangePage.vue index 4edcce0bf..85313a50d 100644 --- a/frontend/src/components/secure-message/ExchangePage.vue +++ b/frontend/src/components/secure-message/ExchangePage.vue @@ -187,16 +187,14 @@ - @@ -528,10 +526,12 @@ import {edxStore} from '@/store/modules/edx'; import {appStore} from '@/store/modules/app'; import {authStore} from '@/store/modules/auth'; import {notificationsStore} from '@/store/modules/notifications'; +import DatePicker from '@/components/util/DatePicker.vue'; export default { name: 'ExchangeInbox', components: { + DatePicker, PrimaryButton, NewMessagePage }, @@ -548,8 +548,6 @@ export default { statusSelectFilter: null, statusRadioGroup: 'statusFilterAllActive', statusRadioGroupEnabled: true, - messageDateFilter: false, - activeMessageDatePicker: null, messageDate: null, subjectFilter: '', messageIDFilter: '', @@ -580,7 +578,6 @@ export default { {value: 50, title: '50'} ], loadingTableCount: 0, - dateMenu: false, headerSearchParams: { sequenceNumber: '', contact: '', @@ -588,10 +585,6 @@ export default { createDate: [], secureExchangeStatusCode: '' }, - headerSortParams: { - currentSort: 'createDate', - currentSortDir: true - }, exchanges: [], selectedExchanges: [], isActiveMessagesTabEnabled: true, @@ -666,9 +659,6 @@ export default { this.filterExchanges(); } }, - openMessageDatePicker() { - this.$refs.messageDatePicker.openMenu(); - }, messageSent() { this.newMessageSheet = !this.newMessageSheet; }, @@ -746,7 +736,6 @@ export default { this.claimedByFilter = null; this.contactNameFilter = null; this.messageDate = null; - this.messageDateFilter = null; this.statusSelectFilter = null; if (runSearch) { this.resetPageNumber(); diff --git a/frontend/src/components/util/DatePicker.vue b/frontend/src/components/util/DatePicker.vue index aca2cff79..4eed17e19 100644 --- a/frontend/src/components/util/DatePicker.vue +++ b/frontend/src/components/util/DatePicker.vue @@ -63,12 +63,13 @@ export default defineComponent({ >