Skip to content

Commit

Permalink
Ofmcc 728 view access permissions (#49)
Browse files Browse the repository at this point in the history
* OFMCC-728. 1st draft commit. Backend spoofed until endpoint ready. Merging to recieve updates from main.

* Committing changes in prep for merge of role security changes required for OFMCC-728.

* update: OFMCC-728 (View access permissions), added a proposed solution to facility header exemption.

* Updated implementation of the facility header show/hide change facility.

* Added test for Menu to test conditional display.

* OFMCC-728: view user/roles/facilities QA code ready checkin.

* OFMCC-728 Code QA changes.

* Refactored styles.
Updated env banner due to h3 change.

* QA code changes.

* Fixed issue with button styling.

* Addressed QA feedback in SettingsView.vue

* Minor style updates.

---------

Co-authored-by: weskubo-cgi <[email protected]>
  • Loading branch information
jgstorey and weskubo-cgi authored Dec 1, 2023
1 parent 7f997b8 commit 39d7c2a
Show file tree
Hide file tree
Showing 16 changed files with 589 additions and 39 deletions.
35 changes: 34 additions & 1 deletion backend/src/components/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ 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 {
UserProfileMappings,
UserProfileOrganizationMappings,
UserProfileFacilityPermissionMappings,
UserProfileFacilityMappings,
UserPermissionMappings,
FacilityMappings,
} = require('../util/mapping/Mappings')

const { MappableObjectForFront } = require('../util/mapping/MappableObject')
const _ = require('lodash')
Expand Down Expand Up @@ -157,6 +164,32 @@ function parseFacilityPermissions(userResponse) {
.sort((a, b) => a.facilityName.localeCompare(b.facilityName))
}

function mapUsersPermissionsFacilitiesObjectForFront(data) {
const usersPermissionsFacilities = new MappableObjectForFront(data, UserPermissionMappings).toJSON()
if (usersPermissionsFacilities?.facilities) {
usersPermissionsFacilities.facilities = usersPermissionsFacilities.facilities.map((facility) => {
let facilityData = new MappableObjectForFront(facility, FacilityMappings).toJSON()
facilityData.city = facilityData.address.address1_city
facilityData.address = facilityData.address.address1_line1
return facilityData
})
}
return usersPermissionsFacilities
}

async function getUsersPermissionsFacilities(req, res) {
try {
let usersPermissionsFacilities = []
const operation = `contacts?$select=ccof_userid,ccof_username,contactid,emailaddress1,ofm_first_name,ofm_is_primary_contact,ofm_last_name,ofm_portal_role,telephone1,ofm_is_expense_authority,statecode&$expand=ofm_facility_business_bceid($select=_ofm_bceid_value,ofm_bceid_facilityid,_ofm_facility_value,ofm_name,ofm_portal_access,statecode,statuscode;$expand=ofm_facility($select=address1_line1,address1_line2,address1_line3,address1_city))&$filter=(_parentcustomerid_value eq ${req.params.organizationId})`
const response = await getOperation(operation)
response?.value?.forEach((item) => usersPermissionsFacilities.push(mapUsersPermissionsFacilitiesObjectForFront(item)))
return res.status(HttpStatus.OK).json(usersPermissionsFacilities)
} catch (e) {
return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json(e.data ? e.data : e?.status)
}
}

module.exports = {
getUserInfo,
getUsersPermissionsFacilities,
}
4 changes: 3 additions & 1 deletion backend/src/routes/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ const router = express.Router()
const auth = require('../components/auth')
const isValidBackendToken = auth.isValidBackendToken()

const { getUserInfo } = require('../components/user')
const { getUserInfo, getUsersPermissionsFacilities } = require('../components/user')

router.get('/', passport.authenticate('jwt', { session: false }), isValidBackendToken, getUserInfo)

router.get('/:queryUserName', passport.authenticate('jwt', { session: false }), isValidBackendToken, getUserInfo)

router.get('/facilities/:organizationId', passport.authenticate('jwt', { session: false }), isValidBackendToken, getUsersPermissionsFacilities)

module.exports = router
22 changes: 22 additions & 0 deletions backend/src/util/mapping/Mappings.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,26 @@ const NotificationMappings = [
{ back: 'ofm_is_read', front: 'isRead' },
]

const UserPermissionMappings = [
{ back: 'contactid', front: 'contactId' },
{ back: 'ofm_first_name', front: 'firstName' },
{ back: 'ofm_last_name', front: 'lastName' },
{ back: 'emailaddress1', front: 'email' },
{ back: 'ccof_username', front: 'userName' },
{ back: 'ofm_portal_role', front: 'roles' },
{ back: 'ofm_is_expense_authority', front: 'isExpenseAuthority' },
{ back: 'statecode', front: 'stateCode' },
{ back: 'ofm_facility_business_bceid', front: 'facilities' },
]

const FacilityMappings = [
{ back: 'ofm_bceid_facilityid', front: 'facilityId' },
{ back: '[email protected]', front: 'name' },
{ back: 'statecode', front: 'stateCode' },
{ back: 'statuscode', front: 'statusCode' },
{ back: 'ofm_facility', front: 'address' },
]

module.exports = {
NotificationMappings,
UserProfileMappings,
Expand All @@ -87,4 +107,6 @@ module.exports = {
AssistanceRequestMappings,
AssistanceRequestFacilityMappings,
AssistanceRequestConversationMappings,
UserPermissionMappings,
FacilityMappings,
}
21 changes: 8 additions & 13 deletions frontend/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!-- eslint-disable vue/no-reserved-component-names -->
<template>
<v-app id="app">
<div class="header">
<div class="app-header">
<TheHeader @menu-toggled="handleMenuToggled" />
<TheSnackBar />
<TheNavBar v-if="pageTitle && isAuthenticated && showNavBar" :title="pageTitle" />
Expand All @@ -10,10 +10,10 @@

<v-main class="align-start">
<TheModalIdle v-if="isAuthenticated" class="align-start px-8 mb-0" />
<v-navigation-drawer class="site-menu" :width="200" :model-value="showMenu" :scrim="false" v-if="isAuthenticated && userInfo">
<v-navigation-drawer class="app-menu" :width="200" :model-value="showMenu" :scrim="false" v-if="isAuthenticated && userInfo">
<TheMenu />
</v-navigation-drawer>
<TheFacilityHeader v-if="isActingProvider" />
<TheFacilityHeader v-if="isActingProvider" :showFacility="$route.meta.showFacility" />
<router-view class="align-start pt-8 px-8 mb-0" />
</v-main>
<TheFooter />
Expand Down Expand Up @@ -110,16 +110,8 @@ export default {
}
</script>

<style>
.site-menu {
margin-top: 2px;
}

.envBanner {
font-size: 0.8rem;
}

.header {
<style scoped>
.app-header {
/* background-color: #036;
border-bottom: 2px solid #fcba19;
padding: 0 65px 0 65px;
Expand All @@ -133,4 +125,7 @@ export default {
width: 100%;
z-index: 1002;
}
.app-menu {
margin-top: 2px;
}
</style>
9 changes: 9 additions & 0 deletions frontend/src/assets/css/reset.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ html {
h1 {
font-size: 1.25rem;
}

h3 {
color: #313132;
}

h4 {
color: #313132;
}

a {
color: #1976d2;
}
Expand Down
7 changes: 5 additions & 2 deletions frontend/src/components/TheEnvBar.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<v-app-bar class="env-bar" v-if="bannerColor !== ''" :color="bannerColor" height="20">
<div>
<h3 class="envBanner">{{ bannerEnvironment }} Environment</h3>
<h3 class="env-text">{{ bannerEnvironment }} Environment</h3>
</div>
</v-app-bar>

Expand Down Expand Up @@ -30,8 +30,11 @@ export default {
</script>
<style scoped>
.env-bar {
color: #ffffff;
margin-top: 2px;
padding: 0 16px;
}
.env-text {
color: #ffffff;
font-size: 0.8rem;
}
</style>
13 changes: 11 additions & 2 deletions frontend/src/components/TheFacilityHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
<v-col cols="6" class="header-org">
{{ userInfo.organizationName }}
</v-col>
<v-col class="header-facility" cols="6">
<v-col v-if="showFacility" class="header-facility" cols="6">
Facility: {{ currentFacility?.facilityName }}
<v-menu id="facilityMenu">
<template v-slot:activator="{ props }">
<v-btn color="primary" id="changeFacility" variant="text" v-bind="props">(change)</v-btn>
</template>
<v-list>
<v-list-item v-for="facility in userInfo.facilities" :key="facility.facilityId" @click="changeFacility(facility)">
<v-list-item v-for="facility in userInfo.facilities" :key="facility.facilityId"
@click="changeFacility(facility)">
<v-list-item-title>{{ facility.facilityName }}</v-list-item-title>
</v-list-item>
</v-list>
Expand All @@ -23,7 +24,14 @@
<script>
import { mapState, mapWritableState } from 'pinia'
import { useAuthStore } from '@/stores/auth'
export default {
props: {
showFacility: {
type: Boolean,
default: true,
},
},
computed: {
...mapState(useAuthStore, ['isAuthenticated', 'userInfo']),
...mapWritableState(useAuthStore, ['currentFacility']),
Expand All @@ -39,6 +47,7 @@ export default {
.facility-container {
max-width: 100%;
}
.header-org {
display: flex;
align-items: flex-end;
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/components/TheMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
<AppMenuItem icon="mdi-home-outline" :to="{ name: 'home' }">Home</AppMenuItem>
<AppMenuItem :to="{ name: 'messaging' }">
<div class="badge-wrapper">
<v-badge v-if="messageNotificationCount > 0" :content="messageNotificationCount" color="red" offset-x="18" offset-y="17">
<v-badge v-if="messageNotificationCount > 0" :content="messageNotificationCount" color="red" offset-x="18"
offset-y="17">
<v-icon class="badge-icon" aria-hidden="false" icon="mdi-email-outline" size="30" />
</v-badge>
<v-icon v-else class="badge-icon" aria-hidden="false" icon="mdi-email-outline" size="30" />
Expand All @@ -15,7 +16,8 @@
<AppMenuItem icon="mdi-folder-outline" :to="{ name: 'documents' }">Documents</AppMenuItem>
<AppMenuItem icon="mdi-file-document-edit-outline" :to="{ name: 'applications' }">Applications</AppMenuItem>
<AppMenuItem icon="mdi-help" :to="{ name: 'resources' }">Resources</AppMenuItem>
<AppMenuItem icon="mdi-cog-outline" :to="{ name: 'settings' }" v-if="hasRole(ROLES.ACCOUNT_MANAGEMENT)">Settings</AppMenuItem>
<AppMenuItem icon="mdi-cog-outline" :to="{ name: 'settings' }" v-if="hasRole(ROLES.ACCOUNT_MANAGEMENT)">Settings
</AppMenuItem>
</div>
</template>

Expand Down
16 changes: 9 additions & 7 deletions frontend/src/components/messages/CloseRequestBanner.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@
</v-col>
</v-row>
</v-alert>
<AppDialog v-model="showConfirmDialog" title="Confirm" :isLoading="isLoading" persistent max-width="40%" @close="toggleConfirmDialog">
<AppDialog v-model="showConfirmDialog" title="Confirm" :isLoading="isLoading" persistent max-width="40%"
@close="toggleConfirmDialog">
<template #content>
<div align="center" class="confirm-dialog-text">Are you sure you want to close this request?</div>
</template>
<template #button>
<v-row class="mt-2" justify="space-around">
<AppButton id="dialog-go-back" :primary="false" size="large" width="170px" @click="toggleConfirmDialog" :loading="isLoading">Go back</AppButton>
<AppButton id="dialog-close-request" size="large" width="170px" @click="closeAssistanceRequest" :loading="isLoading">Close request</AppButton>
<AppButton id="dialog-go-back" :primary="false" size="large" width="170px" @click="toggleConfirmDialog"
:loading="isLoading">Go back</AppButton>
<AppButton id="dialog-close-request" size="large" width="170px" @click="closeAssistanceRequest"
:loading="isLoading">Close request</AppButton>
</v-row>
</template>
</AppDialog>
Expand All @@ -28,7 +31,7 @@
import { mapActions } from 'pinia'
import AppButton from '@/components/ui/AppButton.vue'
import AppDialog from '@/components/ui/AppDialog.vue'
import { ASSISTANCE_REQUEST_STATUS_CODES, ASSISTANCE_REQUEST_STATE_CODES } from '@/utils/constants'
import { ASSISTANCE_REQUEST_STATUS_CODES, CRM_STATE_CODES } from '@/utils/constants'
import { useMessagesStore } from '@/stores/messages'
import alertMixin from '@/mixins/alertMixin'
Expand Down Expand Up @@ -59,7 +62,7 @@ export default {
this.isLoading = true
const payload = {
statusCode: ASSISTANCE_REQUEST_STATUS_CODES.CLOSED_COMPLETE,
stateCode: ASSISTANCE_REQUEST_STATE_CODES.INACTIVE,
stateCode: CRM_STATE_CODES.INACTIVE,
}
await this.updateAssistanceRequest(this.assistanceRequestId, payload)
await this.updateAssistanceRequestInStore(this.assistanceRequestId)
Expand Down Expand Up @@ -88,5 +91,4 @@ export default {
.confirm-dialog-text {
margin: 12px 0px;
font-size: 1.1em;
}
</style>
}</style>
31 changes: 27 additions & 4 deletions frontend/src/components/ui/AppButton.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,38 @@
<template>
<v-btn :class="[primary ? 'BC-Gov-PrimaryButton' : 'BC-Gov-SecondaryButton']" v-bind="$attrs">
<v-btn class="text-none" :class="buttonClass" v-bind="$attrs">
<slot />
<v-icon v-if="$attrs.icon">{{ $attrs.icon }}</v-icon>
<v-icon class="button-icon" v-if="$attrs.icon">{{ $attrs.icon }}</v-icon>
</v-btn>
</template>
<script>
export default {
inheritAttrs: true,
computed: {
buttonClass() {
return {
'BC-Gov-PrimaryButton': this.$attrs.variant === undefined && this.primary,
'BC-Gov-SecondaryButton': this.$attrs.variant === undefined && !this.primary,
'text-app-button': this.$attrs.variant !== '',
}
},
},
props: {
primary: {
type: Boolean,
default: true,
default: 'primary',
},
},
}
</script>
<style scoped>
.button-icon {
font-size: 24px;
}
.text-app-button {
color: #003366;
}
.BC-Gov-PrimaryButton {
background-color: #003366;
border: none;
Expand All @@ -24,6 +41,7 @@ export default {
margin: 8px;
/* ma-2 */
padding: 0 12px;
font-size: 16px;
/* px-3 */
/* padding: 12px 32px;
text-align: center;
Expand All @@ -46,7 +64,7 @@ export default {
opacity: 0.8;
}
:focus {
.BC-Gov-PrimaryButton:focus {
outline: 4px solid #3b99fc;
outline-offset: 1px;
}
Expand Down Expand Up @@ -77,6 +95,11 @@ export default {
background-color: #003366;
color: #ffffff;
}
.BC-Gov-SecondaryButton:focus {
outline: 4px solid #3b99fc;
outline-offset: 1px;
}
.BC-Gov-SecondaryButton:active {
opacity: 1;
}
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import '@/assets/css/main.css'
import '@/assets/css/reset.css'

import App from './App.vue'
import { VDataTable } from 'vuetify/lib/labs/components.mjs'
import { VDataTableVirtual } from 'vuetify/lib/labs/components.mjs'
import { VSkeletonLoader } from 'vuetify/labs/VSkeletonLoader'
import { createApp } from 'vue'
Expand All @@ -17,6 +18,7 @@ const app = createApp(App)

const pinia = createPinia()

app.component('VDataTable', VDataTable)
app.component('VDataTableVirtual', VDataTableVirtual)
app.component('VSkeletonLoader', VSkeletonLoader)
app.provide('$moment', moment)
Expand Down
Loading

0 comments on commit 39d7c2a

Please sign in to comment.