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-94: Prisoner profile change content in the incentives mini card on the overview page #109

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
51 changes: 49 additions & 2 deletions assets/scss/components/_miniSummary.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,59 @@
margin-bottom: 12px;
color: govuk-colour('dark-grey');
}

.hmpps-mini-card__error {
font-weight: 700;
color: $govuk-error-colour;
margin-bottom: 12px;
margin-top: -7px;
}

.hmpps-mini-card__link {
margin-top: auto;
}
}

.boxStyle2 {
@include govuk-responsive-padding(4, 'top');
.hmpps-miniSummaryWrapper {
display: flex;
align-items: stretch;
flex-wrap: wrap;
margin-bottom: 20px;
gap: 23px 0;

.hmpps-summary-card {
height: 100%;
margin-bottom: 0;

&__body {
height: 100%;

.card-body:not(.hmpps-summary-card__no-header) {
height: calc(100% - 65px);
}

.card-body.hmpps-summary-card__no-header {
height: calc(100% - 15px);
}

.card-body .govuk-grid-column-full {
display: flex;
flex-direction: column;
height: 100%;
}
}
}

}

@media (min-width: 1181px) {
.hmpps-miniSummaryWrapper .govuk-grid-column-one-third:nth-of-type(2) .hmpps-summary-card {
margin-left: -2px;
margin-right: -2px;
}
}


@media (max-width: 1180px) {
.hmpps-miniSummaryWrapper .govuk-grid-column-one-third {
width: 50%;
Expand Down
2 changes: 2 additions & 0 deletions cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import pomApi from './integration_tests/mockApis/pom'
import keyWorkerApi from './integration_tests/mockApis/keyWorker'
import curiousApi from './integration_tests/mockApis/curiousApi'
import caseNotesApi from './integration_tests/mockApis/caseNotesApi'
import incentivesMockApi from './integration_tests/mockApis/incentivesMockApi'

export default defineConfig({
viewportWidth: 1152,
Expand Down Expand Up @@ -39,6 +40,7 @@ export default defineConfig({
...keyWorkerApi,
...curiousApi,
...caseNotesApi,
...incentivesMockApi,
})
},
baseUrl: 'http://localhost:3007',
Expand Down
3 changes: 2 additions & 1 deletion feature.env
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ ALLOCATION_MANAGER_ENDPOINT_URL=http://localhost:9091/allocation
KEYWORKER_API_URL=http://localhost:9091/keyworker
CURIOUS_API_URL=http://localhost:9091/curiousApi
WHEREABOUTS_API_URL=http://localhost:9091/whereabouts
CASE_NOTES_API_URL=http://localhost:9091/casenotes
CASE_NOTES_API_URL=http://localhost:9091/casenotes
INCENTIVES_API_URL=http://localhost:9091/incentives
1 change: 1 addition & 0 deletions helm_deploy/values-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ generic-service:
CURIOUS_API_URL: "https://testservices.sequation.net/sequation-virtual-campus2-api"
WHEREABOUTS_API_URL: "https://whereabouts-api-dev.service.justice.gov.uk"
CASE_NOTES_API_URL: "https://dev.offender-case-notes.service.justice.gov.uk"
INCENTIVES_API_URL: "https://incentives-api-dev.hmpps.service.justice.gov.uk"

generic-prometheus-alerts:
alertSeverity: digital-prison-service-dev
1 change: 1 addition & 0 deletions helm_deploy/values-preprod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ generic-service:
CURIOUS_API_URL: "https://preprodservices.sequation.net/sequation-virtual-campus2-api"
WHEREABOUTS_API_URL: "https://whereabouts-api-preprod.service.justice.gov.uk"
CASE_NOTES_API_URL: "https://preprod.offender-case-notes.service.justice.gov.uk"
INCENTIVES_API_URL: "https://incentives-api-preprod.hmpps.service.justice.gov.uk"

generic-prometheus-alerts:
alertSeverity: digital-prison-service-dev
1 change: 1 addition & 0 deletions helm_deploy/values-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ generic-service:
CURIOUS_API_URL: "https://liveservices.sequation.com/sequation-virtual-campus2-api"
WHEREABOUTS_API_URL: "https://whereabouts-api.service.justice.gov.uk"
CASE_NOTES_API_URL: "https://offender-case-notes.service.justice.gov.uk"
INCENTIVES_API_URL: "https://incentives-api.hmpps.service.justice.gov.uk"

generic-prometheus-alerts:
alertSeverity: digital-prison-service
7 changes: 4 additions & 3 deletions integration_tests/e2e/overviewPage.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,10 @@ context('Overview Page', () => {

it('Mini summary Group B should contain Incentives card with correct data', () => {
const overviewPage = visitOverviewPage()
overviewPage.incentivesCard().contains('p', 'Incentive level')
overviewPage.incentivesCard().contains('p', 'Standard')
overviewPage.incentivesCard().contains('p', 'Next review: 30/01/2024')
overviewPage.incentivesCard().contains('p', 'Incentives: since last review')
overviewPage.incentivesCard().contains('p', 'Positive behaviours: 1')
overviewPage.incentivesCard().contains('p', 'Negative behaviours: 1')
overviewPage.incentivesCard().contains('p', 'Next review by: 01/01/2024')
overviewPage.incentivesCard().contains('a', 'Incentive level details')
})

Expand Down
20 changes: 20 additions & 0 deletions integration_tests/mockApis/incentivesMockApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { stubFor } from './wiremock'
import { incentiveReviewsMock } from '../../server/data/localMockData/incentiveReviewsMock'

export default {
stubGetReviews: (bookingId: number) => {
return stubFor({
request: {
method: 'GET',
urlPattern: `/incentives/iep/reviews/booking/${bookingId}`,
},
response: {
status: 200,
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
jsonBody: incentiveReviewsMock,
},
})
},
}
17 changes: 17 additions & 0 deletions integration_tests/mockApis/prison.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import OffenceHistoryMock from '../../server/data/localMockData/offenceHistoryMo
import sentenceTermsMock from '../../server/data/localMockData/sentenceTermsMock'
import { prisonerSentenceDetailsMock } from '../../server/data/localMockData/prisonerSentenceDetails'
import { caseNoteUsageMock } from '../../server/data/localMockData/caseNoteUsageMock'
import { caseNoteCountMock } from '../../server/data/localMockData/caseNoteCountMock'

const placeHolderImagePath = './../../assets/images/average-face.jpg'

Expand Down Expand Up @@ -581,4 +582,20 @@ export default {
},
})
},

stubGetCaseNoteCount: (bookingId: number) => {
return stubFor({
request: {
method: 'GET',
urlPattern: `/prison/api/bookings/${bookingId}/caseNotes/(.*)`,
},
response: {
status: 200,
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
jsonBody: caseNoteCountMock,
},
})
},
}
2 changes: 2 additions & 0 deletions integration_tests/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Cypress.Commands.add('setupOverviewPageStubs', ({ bookingId, prisonerNumber }) =
cy.task('stubPrisonerData', prisonerNumber)
cy.task('stubAccountBalances', bookingId)
cy.task('stubAdjudications', bookingId)
cy.task('stubGetCaseNoteCount', bookingId)
cy.task('stubGetReviews', bookingId)
cy.task('stubVisitSummary', bookingId)
cy.task('stubVisitBalances', prisonerNumber)
cy.task('stubAssessments', bookingId)
Expand Down
8 changes: 8 additions & 0 deletions server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,14 @@ export default {
},
agent: new AgentConfig(Number(get('CASE_NOTES_API_TIMEOUT_DEADLINE', 20000))),
},
incentivesApi: {
url: get('INCENTIVES_API_URL', 'http://localhost:8082', requiredInProduction),
timeout: {
response: Number(get('INCENTIVES_API_TIMEOUT_RESPONSE', 20000)),
deadline: Number(get('INCENTIVES_API_TIMEOUT_DEADLINE', 20000)),
},
agent: new AgentConfig(Number(get('INCENTIVES_API_TIMEOUT_DEADLINE', 20000))),
},
},
domain: get('INGRESS_URL', 'http://localhost:3000', requiredInProduction),
localMockData: get('LOCAL_MOCK_DATA', false),
Expand Down
11 changes: 11 additions & 0 deletions server/data/enums/caseNoteType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// eslint-disable-next-line no-shadow
export enum CaseNoteType {
PositiveBehaviour = 'POS',
NegativeBehaviour = 'NEG',
}

// eslint-disable-next-line no-shadow
export enum CaseNoteSubType {
IncentiveEncouragement = 'IEP_ENC',
IncentiveWarning = 'IEP_WARN',
}
37 changes: 37 additions & 0 deletions server/data/incentivesApiClient.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import nock from 'nock'
import config from '../config'
import IncentivesApiRestClient from './incentivesApiClient'
import { incentiveReviewsMock } from './localMockData/incentiveReviewsMock'

jest.mock('./tokenStore')

const token = { access_token: 'token-1', expires_in: 300 }

describe('caseNotesApiClient', () => {
let incentivesApi: nock.Scope
let incentivesApiClient: IncentivesApiRestClient

beforeEach(() => {
incentivesApi = nock(config.apis.incentivesApi.url)
incentivesApiClient = new IncentivesApiRestClient(token.access_token)
})

afterEach(() => {
jest.resetAllMocks()
nock.cleanAll()
})

const mockSuccessfulIncentivesApiCall = <TReturnData>(url: string, returnData: TReturnData) => {
incentivesApi.get(url).matchHeader('authorization', `Bearer ${token.access_token}`).reply(200, returnData)
}

describe('getReviews', () => {
it('Should return data from the API', async () => {
const bookingId = 123456
mockSuccessfulIncentivesApiCall(`/iep/reviews/booking/${bookingId}`, incentiveReviewsMock)

const output = await incentivesApiClient.getReviews(bookingId)
expect(output).toEqual(incentiveReviewsMock)
})
})
})
29 changes: 29 additions & 0 deletions server/data/incentivesApiClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import config from '../config'
import RestClient from './restClient'
import { IncentivesApiClient } from './interfaces/incentivesApiClient'
import { IncentiveReviews } from '../interfaces/IncentivesApi/incentiveReviews'

export default class IncentivesApiRestClient implements IncentivesApiClient {
restClient: RestClient

constructor(token: string) {
this.restClient = new RestClient('Case Notes API', config.apis.incentivesApi, token)
}

private async get<T>(args: object, localMockData?: T): Promise<T> {
try {
return await this.restClient.get<T>(args)
} catch (error) {
if (config.localMockData === 'true' && localMockData) {
return localMockData
}
return error
}
}

async getReviews(bookingId: number): Promise<IncentiveReviews> {
return this.get<IncentiveReviews>({
path: `/iep/reviews/booking/${bookingId}`,
})
}
}
5 changes: 5 additions & 0 deletions server/data/interfaces/incentivesApiClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { IncentiveReviews } from '../../interfaces/IncentivesApi/incentiveReviews'

export interface IncentivesApiClient {
getReviews(bookingId: number): Promise<IncentiveReviews>
}
8 changes: 8 additions & 0 deletions server/data/interfaces/prisonApiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { OffenderContacts } from '../../interfaces/prisonApi/offenderContacts'
import { ReferenceCode, ReferenceCodeDomain } from '../../interfaces/prisonApi/referenceCode'
import { ReasonableAdjustments } from '../../interfaces/prisonApi/reasonableAdjustment'
import { CaseNoteUsage } from '../../interfaces/prisonApi/caseNoteUsage'
import { CaseNoteCount } from '../../interfaces/prisonApi/caseNoteCount'

export interface PrisonApiClient {
getUserLocations(): Promise<Location[]>
Expand Down Expand Up @@ -63,4 +64,11 @@ export interface PrisonApiClient {
getReferenceCodesByDomain(domain: ReferenceCodeDomain): Promise<ReferenceCode[]>
getReasonableAdjustments(bookingId: number, treatmentCodes: string[]): Promise<ReasonableAdjustments>
getCaseNotesUsage(offenderNumber: string): Promise<CaseNoteUsage[]>
getCaseNoteCount(
bookingId: number,
type: string,
subType: string,
fromDate: string,
toDate: string,
): Promise<CaseNoteCount>
}
6 changes: 6 additions & 0 deletions server/data/localMockData/caseNoteCountMock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { CaseNoteCount } from '../../interfaces/prisonApi/caseNoteCount'

// eslint-disable-next-line import/prefer-default-export
export const caseNoteCountMock: CaseNoteCount = {
count: 1,
}
7 changes: 7 additions & 0 deletions server/data/localMockData/incentiveReviewsMock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { IncentiveReviews } from '../../interfaces/IncentivesApi/incentiveReviews'

// eslint-disable-next-line import/prefer-default-export
export const incentiveReviewsMock: IncentiveReviews = {
iepDate: '2023-01-01',
nextReviewDate: '2024-01-01',
}
13 changes: 7 additions & 6 deletions server/data/localMockData/miniSummaryMock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ const visitsSummaryDataMock: MiniSummaryData = {
topClass: 'big',
bottomLabel: 'Remaining visits',
bottomContentLine1: 6,
bottomContentLine2: formatPrivilegedVisitsSummary(2),
bottomContentLine3: formatPrivilegedVisitsSummary(2),
bottomClass: 'small',
linkLabel: 'Visits details',
linkHref: '#',
Expand All @@ -88,16 +88,17 @@ const visitsSummaryDataMock: MiniSummaryData = {
const categorySummaryDataMock: MiniSummaryData = {
bottomLabel: 'Category',
bottomContentLine1: 'B',
bottomContentLine2: `Next review: ${formatDate('2023-02-19', 'short')}`,
bottomContentLine3: `Next review: ${formatDate('2023-02-19', 'short')}`,
bottomClass: 'small',
linkLabel: 'Manage category',
linkHref: '#',
}

const incentiveSummaryDataMock: MiniSummaryData = {
bottomLabel: 'Incentive level',
bottomContentLine1: 'Standard',
bottomContentLine2: `Next review: ${formatDate('2024-01-30', 'short')}`,
bottomLabel: 'Incentives: since last review',
bottomContentLine1: 'Positive behaviours: 1',
bottomContentLine2: 'Negative behaviours: 1',
bottomContentLine3: `Next review by: ${formatDate('2024-01-01', 'short')}`,
bottomClass: 'small',
linkLabel: 'Incentive level details',
linkHref: '#',
Expand All @@ -106,7 +107,7 @@ const incentiveSummaryDataMock: MiniSummaryData = {
const csraSummaryDataMock: MiniSummaryData = {
bottomLabel: 'CSRA',
bottomContentLine1: 'Standard',
bottomContentLine2: `Last review: ${formatDate('2021-02-19', 'short')}`,
bottomContentLine3: `Last review: ${formatDate('2021-02-19', 'short')}`,
bottomClass: 'small',
linkLabel: 'CSRA history',
linkHref: '#',
Expand Down
14 changes: 14 additions & 0 deletions server/data/prisonApiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { ReferenceCode, ReferenceCodeDomain } from '../interfaces/prisonApi/refe
import { ReasonableAdjustments } from '../interfaces/prisonApi/reasonableAdjustment'
import { CaseNoteUsage } from '../interfaces/prisonApi/caseNoteUsage'
import { formatDateISO } from '../utils/dateHelpers'
import { CaseNoteCount } from '../interfaces/prisonApi/caseNoteCount'

export default class PrisonApiRestClient implements PrisonApiClient {
restClient: RestClient
Expand Down Expand Up @@ -250,4 +251,17 @@ export default class PrisonApiRestClient implements PrisonApiClient {
query: `offenderNo=${prisonerNumber}&toDate=${today}&numMonths=1200`,
})
}

async getCaseNoteCount(
bookingId: number,
type: string,
subType: string,
fromDate: string,
toDate: string,
): Promise<CaseNoteCount> {
return this.get({
path: `/api/bookings/${bookingId}/caseNotes/${type}/${subType}/count`,
query: `fromDate=${fromDate}&toDate=${toDate}`,
})
}
}
4 changes: 4 additions & 0 deletions server/interfaces/IncentivesApi/incentiveReviews.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface IncentiveReviews {
iepDate: string
nextReviewDate: string
}
5 changes: 5 additions & 0 deletions server/interfaces/incentiveSummary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface IncentiveSummary {
positiveCount: number
negativeCount: number
nextReviewDate: string
}
Loading