diff --git a/backend/package-lock.json b/backend/package-lock.json index 8021123d..a5c7c871 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1,11 +1,11 @@ { - "name": "ccof-backend", + "name": "ofm-backend", "version": "0.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "ccof-backend", + "name": "ofm-backend", "version": "0.0.1", "license": "Apache-2.0", "dependencies": { diff --git a/backend/src/components/user.js b/backend/src/components/user.js index 1c12925e..a6439acf 100644 --- a/backend/src/components/user.js +++ b/backend/src/components/user.js @@ -1,236 +1,138 @@ -'use strict'; -const {getSessionUser, getHttpHeader, minify, getUserGuid, getUserName, getLabelFromValue, postOperation, isIdirUser, getOperation} = require('./utils'); -const config = require('../config/index'); -const ApiError = require('./error'); -const axios = require('axios'); -const HttpStatus = require('http-status-codes'); -const log = require('../components/logger'); -const { APPLICATION_STATUS_CODES, CCFRI_STATUS_CODES, ECEWE_STATUS_CODES, CCOF_STATUS_CODES, CCOF_APPLICATION_TYPES, ORGANIZATION_PROVIDER_TYPES, CHANGE_REQUEST_TYPES} = require('../util/constants'); -const { UserProfileFacilityMappings, UserProfileOrganizationMappings, UserProfileBaseFundingMappings, UserProfileApplicationMappings, UserProfileCCFRIMappings, UserProfileECEWEMappings /* lint error: , UserProfileChangeRequestNewFacilityMappings*/} = require('../util/mapping/Mappings'); - -const { MappableObjectForFront } = require('../util/mapping/MappableObject'); -const _ = require ('lodash'); - +'use strict' +const { getSessionUser, getUserName, getHttpHeader, minify, getUserGuid, isIdirUser } = require('./utils') +const config = require('../config/index') +const ApiError = require('./error') +const axios = require('axios') +const HttpStatus = require('http-status-codes') +const log = require('../components/logger') +// TODO... const { ORGANIZATION_PROVIDER_TYPES} = require('../util/constants') +const { UserProfileMappings, UserProfileOrganizationMappings, UserProfileFacilityPermissionMappings, UserProfileFacilityMappings } = require('../util/mapping/Mappings') + +const { MappableObjectForFront } = require('../util/mapping/MappableObject') +const _ = require('lodash') async function getUserInfo(req, res) { - - const userInfo = getSessionUser(req); + const userInfo = getSessionUser(req) if (!userInfo || !userInfo.jwt || !userInfo._json) { return res.status(HttpStatus.UNAUTHORIZED).json({ - message: 'No session data' - }); + message: 'No session data', + }) } - const isIdir = isIdirUser(req); - const queryUserName = req.params?.queryUserName; - const userName = getUserName(req); + const isIdir = isIdirUser(req) + const queryUserName = req.params?.queryUserName + const userName = getUserName(req) // if is idir user (ministry user), make sure they are a user in dynamics + // TODO commented out until we focus on IDIR login and weather this code is relevant if (isIdir) { - let response = await getDynamicsUserByEmail(req); + let response = await getDynamicsUserByEmail(req) if (response.value?.length > 0 && response.value[0].systemuserid) { - log.verbose(`Ministry user: [${req.session.passport.user._json.display_name}] logged in.`); + log.verbose(`Ministry user: [${req.session.passport.user._json.display_name}] logged in.`) } else { - log.info(`Ministry user: [${req.session.passport.user._json.display_name}] attempted to log in but is not part of Dynamics.`); + log.info(`Ministry user: [${req.session.passport.user._json.display_name}] attempted to log in but is not part of Dynamics.`) return res.status(HttpStatus.UNAUTHORIZED).json({ - message: 'Not Authorized' - }); + message: 'Not Authorized', + }) } } let resData = { - displayName: (queryUserName)? userName + '-' + queryUserName : userName, + // TODO i thing this has to do with impersonate... displayName: (queryUserName)? userName + '-' + queryUserName : userName, userName: userName, - email: req.session.passport.user._json.email, isMinistryUser: isIdir, serverTime: new Date(), //TODO: unreadMessages is hardcoded. Remove this with API values when built out! unreadMessages: false, - }; - let userResponse = undefined; + } + let userResponse = undefined if (isIdir) { if (queryUserName) { try { - log.info(`Ministry user [${userName}] is impersonating with username: [${queryUserName}].`); + log.info(`Ministry user [${userName}] is impersonating with username: [${queryUserName}].`) // dynamics api requires a userID. if userID not found then it wil use the query name // put a random userID so that we only search by queryname - userResponse = await getUserProfile(null, queryUserName); + userResponse = await getUserProfile(null, queryUserName) if (userResponse === null) { - return res.status(HttpStatus.NOT_FOUND).json({message: 'No user found with that BCeID UserName'}); + return res.status(HttpStatus.NOT_FOUND).json({ message: 'No user found with that BCeID UserName' }) } } catch (e) { - log.error('getUserProfile Error', e.response ? e.response.status : e.message); - throw new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, {message: 'API Get error'}, e); + log.error('getUserProfile Error', e.response ? e.response.status : e.message) + throw new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, { message: 'API Get error' }, e) } } else { //If not looking for a username, return from here since ministry staff should not have an account - return res.status(HttpStatus.OK).json(resData); + return res.status(HttpStatus.OK).json(resData) } } else { //Not an idir user, so just get the guid from the header - const userGuid = getUserGuid(req); - log.verbose('User Guid is: ', userGuid); - userResponse = await getUserProfile(userGuid, userName ); + const userGuid = getUserGuid(req) + log.verbose('User Guid is: ', userGuid) + userResponse = await getUserProfile(userGuid) } if (log.isVerboseEnabled) { - log.verbose('getUserProfile response:',minify(userResponse)); + log.verbose('getUserProfile response:', minify(userResponse)) } if (userResponse === null) { - creatUser(req); - return res.status(HttpStatus.OK).json(resData); - } - if (userResponse === {}){ - // If no data back, then no associated Organization/Facilities, return empty orgination data - return res.status(HttpStatus.OK).json(resData); + // If no data back, then no associated User Roles/Organization/Facilities/ + return res.status(HttpStatus.UNAUTHORIZED).json(resData) } - let organization = new MappableObjectForFront(userResponse, UserProfileOrganizationMappings).data; + let user = new MappableObjectForFront(userResponse, UserProfileMappings).data + let organization = new MappableObjectForFront(userResponse.organization, UserProfileOrganizationMappings).data + resData.facilityPermission = parseFacilityPermissions(userResponse) - let application = new MappableObjectForFront(userResponse.application, UserProfileApplicationMappings).data; - application.organizationProviderType = getLabelFromValue(application.organizationProviderType, ORGANIZATION_PROVIDER_TYPES); - application.applicationStatus = getLabelFromValue(application.applicationStatus, APPLICATION_STATUS_CODES, 'NEW'); - application.applicationType = getLabelFromValue(application.applicationType, CCOF_APPLICATION_TYPES); - application.ccofProgramYearId = userResponse.application?.ccof_ProgramYear?.ccof_program_yearid; - application.ccofProgramYearName = userResponse.application?.ccof_ProgramYear?.ccof_name; - application.ccofApplicationStatus = getLabelFromValue(application.ccofStatus, CCOF_STATUS_CODES, 'NEW'); - - - resData.facilityList = parseFacilityData(userResponse); let results = { ...resData, + ...user, ...organization, - ...application, - }; - return res.status(HttpStatus.OK).json(results); + } + log.verbose('getUserInfo response:', results) + return res.status(HttpStatus.OK).json(results) } -async function getUserProfile(userGuid, userName) { +async function getUserProfile(userGuid) { try { - let url = undefined; + let url = undefined if (userGuid) { - url = config.get('dynamicsApi:apiEndpoint') + `/api/ProviderProfile?userId=${userGuid}&userName=${userName}`; - } else { - url = config.get('dynamicsApi:apiEndpoint') + `/api/ProviderProfile?userName=${userName}`; + url = config.get('dynamicsApi:apiEndpoint') + `/api/ProviderProfile?userId=${userGuid}` } - - log.verbose('UserProfile Url is', url); - const response = await axios.get(url, getHttpHeader()); - return response.data; + log.verbose('UserProfile Url is', url) + let response = undefined + response = await axios.get(url, getHttpHeader()) + log.verbose('getUserProfile response:', response.data) + return response.data } catch (e) { if (e.response?.status == '404') { - log.verbose('response ', e.response.data); + log.verbose('response ', e.response.data) if (e.response?.data?.startsWith('User not found')) { - return null; + return null } - return {}; + return {} } - log.error('getUserProfile Error', e.response ? e.response.status : e.message); - throw e; + log.error('getUserProfile Error', e.response ? e.response.status : e.message) + throw e } } -function updateFacilityWithChangeRequestDetails(changeRequestList, returnValue, facilityId) { - for (const changeRequest of changeRequestList) { - //todo -mk check statuscode - let changeActionNewFacilityList = changeRequest?.ccof_change_action_change_request?.filter(item =>item.ccof_changetype === CHANGE_REQUEST_TYPES.NEW_FACILITY); - for (const changeActionNewFacility of changeActionNewFacilityList) { - let result = changeActionNewFacility?.ccof_change_request_new_facility_change_act.find(item => item['_ccof_facility_value'] === facilityId); - if (result) { - returnValue.changeRequestId = changeRequest?.ccof_change_requestid; - returnValue.unlockCcfri = result?.ccof_unlock_ccfri; - returnValue.unlockNmf = result?.ccof_unlock_nmf_rfi; - returnValue.unlockRfi = result?.ccof_unlock_rfi; - +function parseFacilityPermissions(userResponse) { + const facilityList = Object.entries(userResponse.facility_permission) + .map(([key, value]) => { + // Only add facilities that have portal access + if (value.ofm_portal_access === true) { + const facilityPermission = new MappableObjectForFront(value, UserProfileFacilityPermissionMappings).data + const facility = new MappableObjectForFront(value.facility, UserProfileFacilityMappings).data + const combinedData = { ...facilityPermission, ...facility } + if (!_.isEmpty(combinedData)) { + return combinedData + } } - } - } -} - -function parseFacilityData(userResponse) { - let facilityMap = new Map(userResponse.facilities?.map((m) => [m['accountid'], new MappableObjectForFront(m, UserProfileFacilityMappings).data])); - - if (userResponse.application) { - facilityMap.forEach((value, key, map) => { - let ccfriInfo = userResponse.application.ccof_applicationccfri_Application_ccof_ap?.find(item => item['_ccof_facility_value'] === key); - ccfriInfo = new MappableObjectForFront(ccfriInfo, UserProfileCCFRIMappings).data; - let eceweInfo = userResponse.application.ccof_ccof_application_ccof_applicationecewe_application?.find(item => item['_ccof_facility_value'] === key); - eceweInfo = new MappableObjectForFront(eceweInfo, UserProfileECEWEMappings).data; - let baseFunding = userResponse.application.ccof_application_basefunding_Application?.find(item => item['_ccof_facility_value'] === key); - baseFunding = new MappableObjectForFront(baseFunding, UserProfileBaseFundingMappings).data; - let changeRequestList = userResponse.application.ccof_ccof_change_request_Application_ccof_appl; - let returnValue = { - ...value, - ...ccfriInfo, - ...eceweInfo, - ...baseFunding, - }; - updateFacilityWithChangeRequestDetails(changeRequestList, returnValue, key); - map.set(key, returnValue); - }); - } - let facilityList = []; - facilityMap.forEach((facility) => { - if (!_.isEmpty(facility)) { - facility.ccofBaseFundingStatus = getLabelFromValue(facility.ccofBaseFundingStatus, CCOF_STATUS_CODES); - facility.ccfriStatus = getLabelFromValue(facility.ccfriStatus, CCFRI_STATUS_CODES, 'NOT STARTED'); - facility.eceweStatus = getLabelFromValue(facility.eceweStatus, ECEWE_STATUS_CODES, 'NOT STARTED'); - facilityList.push(facility); - } - }); - return facilityList; -} - -async function getDynamicsUserByEmail(req) { - let email = req.session.passport.user._json.email; - if (!email) { - //If for some reason, an email is not associated with the IDIR, just use IDR@gov.bc.ca - email = `${req.session.passport.user._json.idir_username}@gov.bc.ca`; - } - // eslint-disable-next-line quotes, - email.includes("'") ? email = email.replace("'", "''") : email; - try { - let response = await getOperation(`systemusers?$select=firstname,domainname,lastname&$filter=internalemailaddress eq '${email}'`); - return response; - } catch (e) { - log.error('getDynamicsUserByEmail Error', e.response ? e.response.status : e.message); - throw new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, {message: 'API Get error'}, e); - } -} - -async function creatUser(req) { - log.info('No user found, creating BCeID User: ', getUserName(req)); - let given_name = req.session.passport.user._json.given_name; - let family_name = req.session.passport.user._json.family_name; - let firstname = undefined; - let lastname = undefined; - try { - if (!family_name && given_name && given_name.split(' ').length > 1) { - //If for some reason we don't have a last name from SSO, see if firstname has 2 words - firstname = given_name.split(' ').slice(0, -1).join(' '); - lastname = given_name.split(' ').slice(-1).join(' '); - } else if (!given_name && family_name && family_name.split(' ').length > 1) { - //If for some reason we don't have a firstname name from SSO, see if lastname has 2 words - firstname = family_name.split(' ').slice(0, -1).join(' '); - lastname = family_name.split(' ').slice(-1).join(' '); - } else { - firstname = given_name; - lastname = family_name; - } - - let payload = { - ccof_userid: getUserGuid(req), - firstname: firstname, - lastname: lastname, - emailaddress1: req.session.passport.user._json.email, - ccof_username: getUserName(req) - }; - postOperation('contacts', payload); - } catch (e) { - log.error('Error when creating user: ', e); - throw new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, {message: 'Error while creating a new BCeID User'}, e); - } + return null + }) + .filter((facility) => facility !== null) + return facilityList } module.exports = { getUserInfo, -}; +} diff --git a/backend/src/util/mapping/Mappings.js b/backend/src/util/mapping/Mappings.js index 90664a02..9ad8ebe4 100644 --- a/backend/src/util/mapping/Mappings.js +++ b/backend/src/util/mapping/Mappings.js @@ -1,34 +1,39 @@ -const OrganizationMappings = [ - { back: 'ccof_facilitystartdate', front: 'yearBeganOperation' }, - { back: 'name', front: 'legalName' }, - { back: 'address1_name', front: 'address1' }, //Address - { back: 'address1_city', front: 'city1' }, - { back: 'address1_postalcode', front: 'postalCode1' }, - { back: 'address2_name', front: 'address2' }, //Mailing Address - { back: 'address2_city', front: 'city2' }, - { back: 'address2_postalcode', front: 'postalCode2' }, - { back: 'address1_primarycontactname', front: 'contactName' }, - { back: 'ccof_position', front: 'position' }, - { back: 'telephone1', front: 'phone' }, +const UserProfileMappings = [ + { back: 'contactid', front: 'contactId' }, + { back: 'ccof_userid', front: 'userId' }, + { back: 'ccof_username', front: 'username' }, { back: 'emailaddress1', front: 'email' }, - { back: 'ccof_instructionnumber', front: 'incNumber' },//incorporation number - { back: 'ccof_typeoforganization', front: 'organizationType' }, - { back: 'ccof_formcomplete', front: 'isOrganizationComplete' }, - { back: 'ccof_is_mailing_address_same', front: 'isSameAsMailing'} -]; + { back: 'ofm_first_name', front: 'firstName' }, + { back: 'ofm_last_name', front: 'lastName' }, + { back: 'ofm_portal_role', front: 'roles' }, +] -const ProgramYearMappings = [ - { back: 'ccof_program_yearid', front: 'programYearId' }, - { back: 'ccof_name', front: 'name' }, - { back: 'statuscode', front: 'status' }, - { back: 'ccof_programyearnumber', front: 'order' }, - { back: '_ccof_previousyear_value', front: 'previousYearId' }, - { back: 'ccof_intakeperiodstart', front: 'intakeStart' }, - { back: 'ccof_intakeperiodend', front: 'intakeEnd' }, - { back: 'ccof_declarationbstart', front: 'declarationbStart' }, -]; +const UserProfileOrganizationMappings = [ + { back: 'accountid', front: 'organizationId' }, + { back: 'accountnumber', front: 'organizationAccountNumber' }, + { back: 'ccof_accounttype', front: 'organizationAccountType' }, + { back: 'name', front: 'organizationName' }, + { back: 'statecode', front: 'organizationStateCode' }, + { back: 'statuscode', front: 'organizationStatus' }, +] + +const UserProfileFacilityPermissionMappings = [ + { back: 'statecode', front: 'stateCode' }, + { back: 'statuscode', front: 'statusCode' }, +] + +const UserProfileFacilityMappings = [ + { back: 'accountid', front: 'facilityId' }, + { back: 'accountnumber', front: 'facilityAccountNumber' }, + { back: 'name', front: 'facilityName' }, + { back: 'ccof_accounttype', front: 'facilityType' }, + { back: 'statecode', front: 'facilityStateCode' }, + { back: 'statuscode', front: 'facilityStatusCode' }, +] module.exports = { - OrganizationMappings, - ProgramYearMappings -}; + UserProfileMappings, + UserProfileOrganizationMappings, + UserProfileFacilityPermissionMappings, + UserProfileFacilityMappings, +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 000f6e4a..3d34d0c6 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,7 +11,6 @@ "@bcgov/bc-sans": "^2.0.0", "@js-joda/core": "^5.5.3", "axios": "^1.4.0", - "happy-dom": "^12.8.0", "http-status-codes": "^2.2.0", "lodash": "^4.17.21", "moment": "^2.29.4", @@ -27,12 +26,14 @@ }, "devDependencies": { "@mdi/font": "^7.2.96", + "@pinia/testing": "^0.1.3", "@rushstack/eslint-patch": "^1.3.2", "@vitejs/plugin-vue": "^4.3.1", "@vue/test-utils": "^2.4.1", "eslint": "^8.46.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-vue": "^9.16.1", + "happy-dom": "^12.8.0", "prettier": "^3.0.0", "vite": "^4.4.9", "vitest": "^0.34.6" @@ -625,6 +626,47 @@ "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", "dev": true }, + "node_modules/@pinia/testing": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@pinia/testing/-/testing-0.1.3.tgz", + "integrity": "sha512-D2Ds2s69kKFaRf2KCcP1NhNZEg5+we59aRyQalwRm7ygWfLM25nDH66267U3hNvRUOTx8ofL24GzodZkOmB5xw==", + "dev": true, + "dependencies": { + "vue-demi": ">=0.14.5" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "pinia": ">=2.1.5" + } + }, + "node_modules/@pinia/testing/node_modules/vue-demi": { + "version": "0.14.6", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", + "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", + "dev": true, + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/@rushstack/eslint-patch": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.3.tgz", @@ -1514,7 +1556,8 @@ "node_modules/css.escape": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==" + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true }, "node_modules/cssesc": { "version": "3.0.0", @@ -1671,6 +1714,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, "engines": { "node": ">=0.12" }, @@ -2158,6 +2202,7 @@ "version": "12.8.0", "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-12.8.0.tgz", "integrity": "sha512-ReeZgCPuebuH1sc7NcTthMIyQVEb1NJyvs+p1gOHC1LwccIAKPWyRTNVmKhfyW5ft1Di+sPsOCuVFulF5IeVxw==", + "dev": true, "dependencies": { "css.escape": "^1.5.1", "entities": "^4.5.0", @@ -2184,6 +2229,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -3206,7 +3252,8 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true }, "node_modules/sass": { "version": "1.66.1", @@ -3900,6 +3947,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, "engines": { "node": ">=12" } @@ -3986,6 +4034,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, "dependencies": { "iconv-lite": "0.6.3" }, @@ -3997,6 +4046,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, "engines": { "node": ">=12" } @@ -4398,6 +4448,24 @@ "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", "dev": true }, + "@pinia/testing": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@pinia/testing/-/testing-0.1.3.tgz", + "integrity": "sha512-D2Ds2s69kKFaRf2KCcP1NhNZEg5+we59aRyQalwRm7ygWfLM25nDH66267U3hNvRUOTx8ofL24GzodZkOmB5xw==", + "dev": true, + "requires": { + "vue-demi": ">=0.14.5" + }, + "dependencies": { + "vue-demi": { + "version": "0.14.6", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", + "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", + "dev": true, + "requires": {} + } + } + }, "@rushstack/eslint-patch": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.3.tgz", @@ -5124,7 +5192,8 @@ "css.escape": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==" + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true }, "cssesc": { "version": "3.0.0", @@ -5238,7 +5307,8 @@ "entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true }, "es-module-lexer": { "version": "1.3.0", @@ -5588,6 +5658,7 @@ "version": "12.8.0", "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-12.8.0.tgz", "integrity": "sha512-ReeZgCPuebuH1sc7NcTthMIyQVEb1NJyvs+p1gOHC1LwccIAKPWyRTNVmKhfyW5ft1Di+sPsOCuVFulF5IeVxw==", + "dev": true, "requires": { "css.escape": "^1.5.1", "entities": "^4.5.0", @@ -5611,6 +5682,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3.0.0" } @@ -6316,7 +6388,8 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true }, "sass": { "version": "1.66.1", @@ -6709,7 +6782,8 @@ "webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true }, "webpack": { "version": "5.88.2", @@ -6771,6 +6845,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, "requires": { "iconv-lite": "0.6.3" } @@ -6778,7 +6853,8 @@ "whatwg-mimetype": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==" + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true }, "which": { "version": "2.0.2", diff --git a/frontend/package.json b/frontend/package.json index 1f745fd1..2487afe4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,6 +24,7 @@ "rfdc": "^1.3.0", "sass": "^1.66.1", "sass-loader": "^13.3.2", + "vite-plugin-vuetify": "^1.0.2", "vue": "^3.3.4", "vue-meta": "^3.0.0-alpha.7", "vue-router": "^4.2.4", diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 4d9439a0..f3ff61ef 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,11 +1,13 @@ @@ -42,9 +73,11 @@ import { mapActions, mapState } from 'pinia' import { Routes } from '@/utils/constants' import { useAuthStore } from '@/stores/auth' +import AppButton from '../components/ui/AppButton.vue' export default { - name: 'Login', + name: 'LoginView', + components: { AppButton }, data() { return { routes: Routes, @@ -89,4 +122,8 @@ export default { .full-height { height: 100%; } + +.containerWidth1450 { + max-width: 1450px; +} diff --git a/frontend/src/views/UnAuthorizedView.vue b/frontend/src/views/UnAuthorizedView.vue index 58608ef1..a7a2aae5 100644 --- a/frontend/src/views/UnAuthorizedView.vue +++ b/frontend/src/views/UnAuthorizedView.vue @@ -1,5 +1,5 @@