Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CDPS-161 - Conditionally show content on the banner based on users caseload #125

Merged
merged 2 commits into from
May 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions integration_tests/e2e/alertsPage.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ const visitEmptyAlertsPage = (): AlertsPage => {
context('Alerts Page', () => {
beforeEach(() => {
cy.task('reset')
cy.task('stubSignIn')
cy.task('stubAuthUser')
cy.setupUserAuth()
})

context('Active Alerts', () => {
Expand Down
3 changes: 1 addition & 2 deletions integration_tests/e2e/offencesPage.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ context('Offences Page', () => {

beforeEach(() => {
cy.task('reset')
cy.task('stubSignIn')
cy.task('stubAuthUser')
cy.setupUserAuth()
cy.setupBannerStubs({ prisonerNumber })
cy.setupOffencesPageStubs({ prisonerNumber: 'G6123VU', bookingId: 1102484 })
})
Expand Down
3 changes: 1 addition & 2 deletions integration_tests/e2e/photoPage.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ context('Photo Page', () => {

beforeEach(() => {
cy.task('reset')
cy.task('stubSignIn')
cy.task('stubAuthUser')
cy.setupUserAuth()
cy.setupBannerStubs({ prisonerNumber })
cy.setupOverviewPageStubs({ prisonerNumber, bookingId })
})
Expand Down
43 changes: 43 additions & 0 deletions integration_tests/e2e/profileBanner.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import Page from '../pages/page'
import OverviewPage from '../pages/overviewPage'

const visitOverviewPage = (): OverviewPage => {
cy.signIn({ redirectPath: '/prisoner/G6123VU' })
return Page.verifyOnPage(OverviewPage)
}

context('Profile banner', () => {
context('Given the prisoner is not within the users caseload', () => {
context('Given the user has the GLOBAL_SEARCH role', () => {
beforeEach(() => {
cy.task('reset')
cy.setupUserAuth({
roles: ['ROLE_GLOBAL_SEARCH'],
caseLoads: [{ caseloadFunction: '', caseLoadId: '123', currentlyActive: true, description: '', type: '' }],
})
cy.setupOverviewPageStubs({ prisonerNumber: 'G6123VU', bookingId: '1102484' })
})

it('Displays the banner', () => {
visitOverviewPage()
cy.getDataQa('visible-outside-establishment-banner').should('exist')
})
})
})

context('Given the prisoner is within the users caseload', () => {
beforeEach(() => {
cy.task('reset')
cy.setupUserAuth({
roles: ['ROLE_PRISON'],
caseLoads: [{ caseloadFunction: '', caseLoadId: 'MDI', currentlyActive: true, description: '', type: '' }],
})
cy.setupOverviewPageStubs({ prisonerNumber: 'G6123VU', bookingId: '1102484' })
})

it('Hides the banner', () => {
visitOverviewPage()
cy.getDataQa('hidden-outside-establishment-banner').should('exist')
})
})
})
3 changes: 1 addition & 2 deletions integration_tests/e2e/workAndSkillsPage.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ const visitWorkAndSkillsPage = (): WorkAndSkillsPage => {
context('Work and Skills Page', () => {
beforeEach(() => {
cy.task('reset')
cy.task('stubSignIn')
cy.task('stubAuthUser')
cy.setupUserAuth()
})

context('Default Prisoner State', () => {
Expand Down
6 changes: 4 additions & 2 deletions server/controllers/alertsController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { pagedActiveAlertsMock, pagedInactiveAlertsMock } from '../data/localMoc
import AlertsController from './alertsController'
import * as headerMappers from '../mappers/headerMappers'
import { PrisonerMockDataA } from '../data/localMockData/prisoner'
import { CaseLoadsDummyDataA } from '../data/localMockData/caseLoad'

let req: any
let res: any
Expand All @@ -22,6 +23,7 @@ describe('Alerts Controller', () => {
user: {
activeCaseLoadId: 'MDI',
userRoles: ['ROLE_UPDATE_ALERT'],
caseLoads: CaseLoadsDummyDataA,
},
clientToken: 'CLIENT_TOKEN',
},
Expand Down Expand Up @@ -54,7 +56,7 @@ describe('Alerts Controller', () => {
},
true,
)
expect(mapSpy).toHaveBeenCalledWith(PrisonerMockDataA, true, 'alerts')
expect(mapSpy).toHaveBeenCalledWith(PrisonerMockDataA, CaseLoadsDummyDataA, true, 'alerts')
})

it('should get inactive alerts', async () => {
Expand Down Expand Up @@ -84,7 +86,7 @@ describe('Alerts Controller', () => {
},
true,
)
expect(mapSpy).toHaveBeenCalledWith(PrisonerMockDataA, true, 'alerts')
expect(mapSpy).toHaveBeenCalledWith(PrisonerMockDataA, CaseLoadsDummyDataA, true, 'alerts')
})

it('should set canUpdateAlert to true if user has role and active caseload', async () => {
Expand Down
1 change: 1 addition & 0 deletions server/controllers/alertsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export default class AlertsController {
pageTitle: 'Alerts',
...mapHeaderData(
prisonerData,
res.locals.user.caseLoads,
canViewOrAddCaseNotes(res.locals.user.userRoles, res.locals.user.activeCaseLoadId, prisonerData.prisonId),
'alerts',
),
Expand Down
4 changes: 3 additions & 1 deletion server/controllers/caseNotesController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as headerMappers from '../mappers/headerMappers'
import CaseNotesController from './caseNotesController'
import { pagedCaseNotesMock } from '../data/localMockData/pagedCaseNotesMock'
import { caseNoteUsageMock } from '../data/localMockData/caseNoteUsageMock'
import { CaseLoadsDummyDataA } from '../data/localMockData/caseLoad'

let req: any
let res: any
Expand Down Expand Up @@ -31,6 +32,7 @@ describe('Case Notes Controller', () => {
clientToken: 'CLIENT_TOKEN',
user: {
staffId: 487023,
caseLoads: CaseLoadsDummyDataA,
},
},
render: jest.fn(),
Expand Down Expand Up @@ -65,6 +67,6 @@ describe('Case Notes Controller', () => {
},
false,
)
expect(mapSpy).toHaveBeenCalledWith(prisonerDetailMock, true, 'case-notes')
expect(mapSpy).toHaveBeenCalledWith(prisonerDetailMock, CaseLoadsDummyDataA, true, 'case-notes')
})
})
2 changes: 1 addition & 1 deletion server/controllers/caseNotesController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export default class CaseNotesController {
// Render page
return res.render('pages/caseNotesPage', {
pageTitle: 'Case notes',
...mapHeaderData(prisonerData, canViewCaseNotes, 'case-notes'),
...mapHeaderData(prisonerData, res.locals.user.caseLoads, canViewCaseNotes, 'case-notes'),
...caseNotesPageData,
hasCaseNotes,
staffId,
Expand Down
2 changes: 1 addition & 1 deletion server/controllers/overviewController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default class OverviewController {

res.render('pages/overviewPage', {
pageTitle: 'Overview',
...mapHeaderData(prisonerData, canViewCaseNotes, 'overview'),
...mapHeaderData(prisonerData, res.locals.user.caseLoads, canViewCaseNotes, 'overview'),
...overviewPageData,
canViewCaseNotes,
canAddCaseNotes,
Expand Down
16 changes: 13 additions & 3 deletions server/mappers/headerMapper.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
import { CaseLoadsDummyDataA } from '../data/localMockData/caseLoad'
import { PrisonerMockDataA, PrisonerMockDataB } from '../data/localMockData/prisoner'
import { mapHeaderData } from './headerMappers'
import { mapHeaderData, mapProfileBannerTopLinks } from './headerMappers'

describe('HeaderMapping', () => {
describe('Header data', () => {
describe('If the prisoner is part of the users case loads', () => {
it('Contains the location', () => {
const topLinks = mapProfileBannerTopLinks(PrisonerMockDataA, CaseLoadsDummyDataA)
expect(topLinks.length).toEqual(4)
expect(topLinks[0].heading).toEqual('Location')
})
})
})
describe('Category A prisoner', () => {
it('Photo type should be photoWithheld for security purposes', async () => {
const headerData = mapHeaderData(PrisonerMockDataA)
const headerData = mapHeaderData(PrisonerMockDataA, CaseLoadsDummyDataA)
expect(headerData.photoType).toBe('photoWithheld')
})
it('Photo type should return as placeholder if the category is not A', async () => {
const headerData = mapHeaderData(PrisonerMockDataB)
const headerData = mapHeaderData(PrisonerMockDataB, CaseLoadsDummyDataA)
expect(headerData.photoType).toBe('placeholder')
})
})
Expand Down
61 changes: 35 additions & 26 deletions server/mappers/headerMappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,42 @@ import { Alert, Prisoner } from '../interfaces/prisoner'
import { tabLinks } from '../data/profileBanner/profileBanner'
import { AlertFlagLabel } from '../interfaces/alertFlagLabels'
import { alertFlagLabels } from '../data/alertFlags/alertFlags'
import { formatName } from '../utils/utils'
import { formatName, prisonerBelongsToUsersCaseLoad } from '../utils/utils'
import { NameFormatStyle } from '../data/enums/nameFormatStyle'
import { CaseLoad } from '../interfaces/caseLoad'

export const placeHolderImagePath = '/assets/images/prisoner-profile-photo.png'

export function mapProfileBannerTopLinks(prisonerData: Prisoner) {
const profileBannerTopLinks = [
{
export function mapProfileBannerTopLinks(prisonerData: Prisoner, userCaseLoads: CaseLoad[]) {
const profileBannerTopLinks = []

if (prisonerBelongsToUsersCaseLoad(prisonerData.prisonId, userCaseLoads)) {
profileBannerTopLinks.push({
heading: 'Location',
hiddenLabel: 'View location details',
info: prisonerData.cellLocation,
classes: '',
},
{
heading: 'Category',
hiddenLabel: 'Manage category',
info: prisonerData.category === 'U' ? 'Unsentenced' : prisonerData.category,
classes: '',
},
{
heading: 'CSRA',
hiddenLabel: 'View CSRA history',
info: prisonerData.csra ? prisonerData.csra : 'Not entered',
classes: '',
},
{
heading: 'Incentive level',
hiddenLabel: 'View incentive level details',
info: prisonerData.currentIncentive ? prisonerData.currentIncentive.level.description : 'Not entered',
classes: 'remove-column-gutter-right',
},
]
})
}

profileBannerTopLinks.push({
heading: 'Category',
hiddenLabel: 'Manage category',
info: prisonerData.category === 'U' ? 'Unsentenced' : prisonerData.category,
classes: '',
})
profileBannerTopLinks.push({
heading: 'CSRA',
hiddenLabel: 'View CSRA history',
info: prisonerData.csra ? prisonerData.csra : 'Not entered',
classes: '',
})
profileBannerTopLinks.push({
heading: 'Incentive level',
hiddenLabel: 'View incentive level details',
info: prisonerData.currentIncentive ? prisonerData.currentIncentive.level.description : 'Not entered',
classes: 'remove-column-gutter-right',
})
return profileBannerTopLinks
}

Expand All @@ -51,7 +55,12 @@ export function mapAlerts(prisonerData: Prisoner, alertFlags: AlertFlagLabel[])
return alerts
}

export function mapHeaderData(prisonerData: Prisoner, canViewCaseNotes?: boolean, pageId?: string) {
export function mapHeaderData(
prisonerData: Prisoner,
userCaseLoads: CaseLoad[],
canViewCaseNotes?: boolean,
pageId?: string,
) {
const photoType = prisonerData.category === 'A' ? 'photoWithheld' : 'placeholder'
const tabs = tabLinks(prisonerData.prisonerNumber, canViewCaseNotes)

Expand All @@ -65,7 +74,7 @@ export function mapHeaderData(prisonerData: Prisoner, canViewCaseNotes?: boolean
style: NameFormatStyle.lastCommaFirst,
}),
prisonerNumber: prisonerData.prisonerNumber,
profileBannerTopLinks: mapProfileBannerTopLinks(prisonerData),
profileBannerTopLinks: mapProfileBannerTopLinks(prisonerData, userCaseLoads),
alerts: mapAlerts(prisonerData, alertFlagLabels),
tabLinks: tabs,
photoType,
Expand Down
4 changes: 4 additions & 0 deletions server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export default function routes(service: Services): Router {
res.render('pages/photoPage', {
...mapHeaderData(
prisonerData,
res.locals.user.caseLoads,
canViewOrAddCaseNotes(res.locals.user.userRoles, res.locals.user.activeCaseLoadId, prisonerData.prisonId),
),
})
Expand All @@ -68,6 +69,7 @@ export default function routes(service: Services): Router {
pageTitle: 'Personal',
...mapHeaderData(
prisonerData,
res.locals.user.caseLoads,
canViewOrAddCaseNotes(res.locals.user.userRoles, res.locals.user.activeCaseLoadId, prisonerData.prisonId),
'personal',
),
Expand All @@ -88,6 +90,7 @@ export default function routes(service: Services): Router {
res.render('pages/workAndSkills', {
...mapHeaderData(
prisonerData,
res.locals.user.caseLoads,
canViewOrAddCaseNotes(res.locals.user.userRoles, res.locals.user.activeCaseLoadId, prisonerData.prisonId),
'work-and-skills',
),
Expand Down Expand Up @@ -121,6 +124,7 @@ export default function routes(service: Services): Router {
pageTitle: 'Offences',
...mapHeaderData(
prisonerData,
res.locals.user.caseLoads,
canViewOrAddCaseNotes(res.locals.user.userRoles, res.locals.user.activeCaseLoadId, prisonerData.prisonId),
'offences',
),
Expand Down
49 changes: 34 additions & 15 deletions server/views/partials/profileBanner/profileBannerTop.njk
Original file line number Diff line number Diff line change
@@ -1,20 +1,39 @@
{% from "../../macros/conditionallyShow.njk" import conditionallyShow %}
<div class="govuk-grid-row">
<div class="govuk-grid-column-full hmpps-profile-banner-top">
<a href="/prisoner/{{ prisonerNumber }}/image" class="hmpps-clickable-image" data-qa=prisoner-photo-link>
<img class="hmpps-profile-photo" src="/api/prisoner/{{ prisonerNumber or photoType }}/image" alt="{{ prisonerName }}" />
</a>
<h2 class="govuk-heading-l hmpps-profile-prisoner-name">{{ prisonerName }}</h2>
<div class="info">
<p class="govuk-body govuk-!-font-size-24 hmpps-profile-prisoner-number" aria-label="Prisoner Number">
{{ prisonerNumber }}
</p>
<div class="info__links">
{% for link in profileBannerTopLinks %}
<div class="{{ link.classes }}">
<h3 class="govuk-heading-s">{{ link.heading }}</h3>
<a href="#" aria-label="{{ link.hiddenLabel }}" class="govuk-link govuk-link--inverse">{{ link.info }}</a>
<div class="govuk-grid-column-full">
{%- call conditionallyShow({condition: prisonerBelongsToUsersCaseLoad(prisonId, user.caseLoads) != true, id: 'outside-establishment-banner'}) -%}
<div class="govuk-grid-row" class="govuk-!-padding-top-3 govuk-!-padding-bottom-3" style="background-color: #fbc9af;">
<div class="govuk-grid-column-full">
<p class="govuk-heading-m govuk-!-margin-top-2 govuk-!-margin-bottom-2">This prisoner is not in your establishment</p>
<p class="govuk-body govuk-!-margin-bottom-2">Some information may be hidden</p>
</div>
</div>
{%- endcall -%}
<div class="govuk-grid-row">
<div class="govuk-grid-column-full hmpps-profile-banner-top">
<a href="/prisoner/{{ prisonerNumber }}/image" class="hmpps-clickable-image" data-qa="prisoner-photo-link">
<img
class="hmpps-profile-photo"
src="/api/prisoner/{{ prisonerNumber or photoType }}/image"
alt="{{ prisonerName }}"
/>
</a>
<h2 class="govuk-heading-l hmpps-profile-prisoner-name">{{ prisonerName }}</h2>
<div class="info">
<p class="govuk-body govuk-!-font-size-24 hmpps-profile-prisoner-number" aria-label="Prisoner Number">
{{ prisonerNumber }}
</p>
<div class="info__links">
{% for link in profileBannerTopLinks %}
<div class="{{ link.classes }}">
<h3 class="govuk-heading-s">{{ link.heading }}</h3>
<a href="#" aria-label="{{ link.hiddenLabel }}" class="govuk-link govuk-link--inverse"
>{{ link.info }}</a
>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
</div>
</div>
</div>
Expand Down