diff --git a/integration_tests/mockApis/outOfServiceBed.ts b/integration_tests/mockApis/outOfServiceBed.ts index 14657b895c..e196f18575 100644 --- a/integration_tests/mockApis/outOfServiceBed.ts +++ b/integration_tests/mockApis/outOfServiceBed.ts @@ -1,6 +1,11 @@ import { SuperAgentRequest } from 'superagent' -import { Cas1OutOfServiceBedSortField as OutOfServiceBedSortField, SortDirection } from '@approved-premises/api' +import { + Cas1OutOfServiceBed as OutOfServiceBed, + Cas1OutOfServiceBedSortField as OutOfServiceBedSortField, + SortDirection, + Temporality, +} from '@approved-premises/api' import { getMatchingRequests, stubFor } from './setup' import { bedspaceConflictResponseBody, errorStub } from './utils' import paths from '../../server/paths/api' @@ -52,30 +57,51 @@ export default { }, }), - stubOutOfServiceBedsListForAPremises: ({ premisesId, outOfServiceBeds }): SuperAgentRequest => - stubFor({ - request: { - method: 'GET', - url: paths.manage.premises.outOfServiceBeds.premisesIndex({ premisesId }), - }, - response: { - status: 200, - headers, - jsonBody: outOfServiceBeds, - }, - }), - stubOutOfServiceBedsList: ({ outOfServiceBeds, + premisesId, page = 1, - sortBy = 'startDate', - sortDirection = 'asc', + sortBy, + sortDirection, temporality = 'current', - }): SuperAgentRequest => - stubFor({ + perPage = 10, + }: { + outOfServiceBeds: Array + premisesId?: string + page: number + temporality?: string + sortBy?: OutOfServiceBedSortField + sortDirection?: SortDirection + perPage?: number + }): SuperAgentRequest => { + const queryParameters = { + page: { + equalTo: page.toString(), + }, + + temporality: { + equalTo: temporality, + }, + } as Record + + if (premisesId) { + queryParameters.premisesId = { equalTo: premisesId } + } + if (sortBy) { + queryParameters.sortBy = { equalTo: sortBy } + } + if (sortDirection) { + queryParameters.sortDirection = { equalTo: sortDirection } + } + if (perPage) { + queryParameters.perPage = { equalTo: perPage.toString() } + } + + return stubFor({ request: { method: 'GET', - url: `${paths.manage.outOfServiceBeds.index.pattern}?page=${page}&sortBy=${sortBy}&sortDirection=${sortDirection}&temporality=${temporality}`, + urlPathPattern: paths.manage.outOfServiceBeds.index.pattern, + queryParameters, }, response: { status: 200, @@ -87,7 +113,8 @@ export default { }, jsonBody: outOfServiceBeds, }, - }), + }) + }, stubOutOfServiceBed: ({ premisesId, outOfServiceBed }): SuperAgentRequest => stubFor({ @@ -154,29 +181,48 @@ export default { }) ).body.requests, verifyOutOfServiceBedsDashboard: async ({ - page = '1', - sortBy = 'startDate', - sortDirection = 'asc', + page = 1, + sortBy, + sortDirection, + temporality = 'current', + premisesId, + perPage = 10, }: { - page: string + page: number sortBy: OutOfServiceBedSortField sortDirection: SortDirection - }) => - ( - await getMatchingRequests({ - method: 'GET', - urlPathPattern: paths.manage.outOfServiceBeds.index({}), - queryParameters: { - page: { - equalTo: page, - }, - sortBy: { - equalTo: sortBy, - }, - sortDirection: { - equalTo: sortDirection, - }, - }, - }) - ).body.requests, + temporality: Temporality + premisesId: string + perPage?: number + }) => { + const queryParameters = { + page: { + equalTo: page.toString(), + }, + temporality: { + equalTo: temporality, + }, + } as Record + + if (premisesId) { + queryParameters.premisesId = { equalTo: premisesId } + } + if (sortBy) { + queryParameters.sortBy = { equalTo: sortBy } + } + if (sortDirection) { + queryParameters.sortDirection = { equalTo: sortDirection } + } + if (perPage) { + queryParameters.perPage = { equalTo: perPage.toString() } + } + + const requests = await getMatchingRequests({ + method: 'GET', + urlPathPattern: paths.manage.outOfServiceBeds.index.pattern, + queryParameters, + }) + + return requests.body.requests + }, } diff --git a/integration_tests/pages/v2Manage/outOfServiceBeds/outOfServiceBedList.ts b/integration_tests/pages/v2Manage/outOfServiceBeds/outOfServiceBedList.ts index e5cdaffb24..33d17bf527 100644 --- a/integration_tests/pages/v2Manage/outOfServiceBeds/outOfServiceBedList.ts +++ b/integration_tests/pages/v2Manage/outOfServiceBeds/outOfServiceBedList.ts @@ -1,4 +1,4 @@ -import type { Cas1OutOfServiceBed as OutOfServiceBed, Premises } from '@approved-premises/api' +import type { Cas1OutOfServiceBed as OutOfServiceBed, Premises, Temporality } from '@approved-premises/api' import paths from '../../../../server/paths/manage' import Page from '../../page' @@ -8,8 +8,8 @@ export class OutOfServiceBedListPage extends Page { super('Manage out of service beds') } - static visit(premisesId: Premises['id']): OutOfServiceBedListPage { - cy.visit(paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId })) + static visit(premisesId: Premises['id'], temporality: Temporality): OutOfServiceBedListPage { + cy.visit(paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId, temporality })) return new OutOfServiceBedListPage() } diff --git a/integration_tests/tests/v2Manage/outOfServiceBeds.cy.ts b/integration_tests/tests/v2Manage/outOfServiceBeds.cy.ts index e61d44600f..c31a029691 100644 --- a/integration_tests/tests/v2Manage/outOfServiceBeds.cy.ts +++ b/integration_tests/tests/v2Manage/outOfServiceBeds.cy.ts @@ -144,16 +144,17 @@ context('OutOfServiceBeds', () => { // And there is a out of service bed in the database const outOfServiceBed = outOfServiceBedFactory.build() cy.task('stubOutOfServiceBed', { premisesId, outOfServiceBed }) - cy.task('stubOutOfServiceBedsListForAPremises', { premisesId, outOfServiceBeds: [outOfServiceBed] }) + cy.task('stubOutOfServiceBedsList', { + premisesId, + outOfServiceBeds: [outOfServiceBed], + perPage: 50, + }) cy.task('stubOutOfServiceBedUpdate', { premisesId, outOfServiceBed }) // When I visit the out of service bed index page - const outOfServiceBedListPage = OutOfServiceBedListPage.visit(premisesId) + const outOfServiceBedListPage = OutOfServiceBedListPage.visit(premisesId, 'current') - // Then I see the out of service beds for that premises - outOfServiceBedListPage.shouldShowOutOfServiceBeds([outOfServiceBed]) - - // // When I click manage on a bed + // // And I click manage on a bed outOfServiceBedListPage.clickManageBed(outOfServiceBed) // // Then I should see the out of service bed manage form @@ -193,7 +194,7 @@ context('OutOfServiceBeds', () => { // And there is a out of service bed in the database const outOfServiceBed = outOfServiceBedFactory.build() cy.task('stubOutOfServiceBed', { premisesId, outOfServiceBed }) - cy.task('stubOutOfServiceBedsListForAPremises', { premisesId, outOfServiceBeds: [outOfServiceBed] }) + cy.task('stubOutOfServiceBedsList', { premisesId, outOfServiceBeds: [outOfServiceBed] }) // And I miss required fields cy.task('stubUpdateOutOfServiceBedErrors', { @@ -225,7 +226,7 @@ context('OutOfServiceBeds', () => { const outOfServiceBed = outOfServiceBedFactory.build() const outOfServiceBedCancellation = outOfServiceBedCancellationFactory.build() cy.task('stubOutOfServiceBed', { premisesId, outOfServiceBed }) - cy.task('stubOutOfServiceBedsListForAPremises', { premisesId, outOfServiceBeds: [outOfServiceBed] }) + cy.task('stubOutOfServiceBedsList', { premisesId, outOfServiceBeds: [outOfServiceBed], perPage: 50 }) cy.task('stubCancelOutOfServiceBed', { premisesId, outOfServiceBedId: outOfServiceBed.id, @@ -251,6 +252,53 @@ context('OutOfServiceBeds', () => { }) }) + describe('list all OOS beds for a given AP', () => { + const premisesId = 'abc123' + const outOfServiceBeds = outOfServiceBedFactory.buildList(10) + + beforeEach(() => { + cy.task('reset') + // Given I am signed in as a future manager + signIn(['future_manager']) + }) + + it('allows me to view all out of service beds for a premises', () => { + // And there are out of service beds in the database + cy.task('stubOutOfServiceBedsList', { premisesId, outOfServiceBeds, page: 1, perPage: 50 }) + + // When I visit the out of service bed index page for a premises + const outOfServiceBedListPage = OutOfServiceBedListPage.visit(premisesId, 'current') + + // Then I see the out of service beds for that premises + outOfServiceBedListPage.shouldShowOutOfServiceBeds(outOfServiceBeds) + }) + + it('supports pagination', () => { + cy.task('stubOutOfServiceBedsList', { outOfServiceBeds, page: 1, premisesId, perPage: 50 }) + cy.task('stubOutOfServiceBedsList', { outOfServiceBeds, page: 2, premisesId, perPage: 50 }) + cy.task('stubOutOfServiceBedsList', { outOfServiceBeds, page: 9, premisesId, perPage: 50 }) + + // When I visit the OOS beds index page for a premises + const page = OutOfServiceBedListPage.visit(premisesId, 'current') + + // And I click next + page.clickNext() + + // Then the API should have received a request for the next page + cy.task('verifyOutOfServiceBedsDashboard', { page: 2, premisesId, perPage: 50 }).then(requests => { + expect(requests).to.have.length(1) + }) + + // When I click on a page number + page.clickPageNumber('9') + + // Then the API should have received a request for the that page number + cy.task('verifyOutOfServiceBedsDashboard', { page: 9, premisesId, perPage: 50 }).then(requests => { + expect(requests).to.have.length(1) + }) + }) + }) + describe('CRU Member lists all OOS beds', () => { beforeEach(() => { cy.task('reset') @@ -277,9 +325,9 @@ context('OutOfServiceBeds', () => { }) it('supports pagination', () => { - cy.task('stubOutOfServiceBedsList', { outOfServiceBeds, page: '1' }) - cy.task('stubOutOfServiceBedsList', { outOfServiceBeds, page: '2' }) - cy.task('stubOutOfServiceBedsList', { outOfServiceBeds, page: '9' }) + cy.task('stubOutOfServiceBedsList', { outOfServiceBeds, page: 1 }) + cy.task('stubOutOfServiceBedsList', { outOfServiceBeds, page: 2 }) + cy.task('stubOutOfServiceBedsList', { outOfServiceBeds, page: 9 }) // When I visit the OOS beds index page const page = OutOfServiceBedIndexPage.visit('current') @@ -288,7 +336,7 @@ context('OutOfServiceBeds', () => { page.clickNext() // Then the API should have received a request for the next page - cy.task('verifyOutOfServiceBedsDashboard', { page: '2' }).then(requests => { + cy.task('verifyOutOfServiceBedsDashboard', { page: 2, temporality: 'current' }).then(requests => { expect(requests).to.have.length(1) }) @@ -296,7 +344,7 @@ context('OutOfServiceBeds', () => { page.clickPageNumber('9') // Then the API should have received a request for the that page number - cy.task('verifyOutOfServiceBedsDashboard', { page: '9' }).then(requests => { + cy.task('verifyOutOfServiceBedsDashboard', { page: 9, temporality: 'current' }).then(requests => { expect(requests).to.have.length(1) }) }) @@ -307,17 +355,17 @@ context('OutOfServiceBeds', () => { cy.task('stubOutOfServiceBedsList', { outOfServiceBeds, - page: '1', + page: 1, temporality: 'current', }) cy.task('stubOutOfServiceBedsList', { outOfServiceBeds: futureBeds, - page: '1', + page: 1, temporality: 'future', }) cy.task('stubOutOfServiceBedsList', { outOfServiceBeds: historicBeds, - page: '1', + page: 1, temporality: 'historic', }) @@ -373,14 +421,14 @@ context('OutOfServiceBeds', () => { cy.task('stubOutOfServiceBedsList', { outOfServiceBeds, page: 1 }) cy.task('stubOutOfServiceBedsList', { outOfServiceBeds, - page: '1', + page: 1, sortBy: field, sortDirection: 'asc', temporality: 'current', }) cy.task('stubOutOfServiceBedsList', { outOfServiceBeds, - page: '1', + page: 1, sortBy: field, sortDirection: 'desc', temporality: 'current', @@ -396,8 +444,12 @@ context('OutOfServiceBeds', () => { page.clickSortBy(field) // Then the API should have received a request for the sort - cy.task('verifyOutOfServiceBedsDashboard', { page: '1', sortBy: field, sortDirection: 'asc' }).then(requests => { - expect(requests).to.have.length(field === 'startDate' ? 2 : 1) + cy.task('verifyOutOfServiceBedsDashboard', { + page: 1, + sortBy: field, + temporality: 'current', + }).then(requests => { + expect(requests).to.have.length(1) }) // And the page should show the sorted items @@ -407,7 +459,12 @@ context('OutOfServiceBeds', () => { page.clickSortBy(field) // Then the API should have received a request for the sort - cy.task('verifyOutOfServiceBedsDashboard', { page: '1', sortBy: field, sortDirection: 'desc' }).then(requests => { + cy.task('verifyOutOfServiceBedsDashboard', { + page: 1, + sortBy: field, + sortDirection: 'desc', + temporality: 'current', + }).then(requests => { expect(requests).to.have.length(1) }) diff --git a/server/controllers/manage/index.ts b/server/controllers/manage/index.ts index 35ee4b713f..0e8abdff9c 100644 --- a/server/controllers/manage/index.ts +++ b/server/controllers/manage/index.ts @@ -11,7 +11,6 @@ import LostBedsController from './lostBedsController' import MoveBedsController from './moveBedsController' import BedsController from './premises/bedsController' import DateChangesController from './dateChangesController' -import OutOfServiceBedsController from './outOfServiceBedsController' import type { Services } from '../../services' @@ -24,7 +23,6 @@ export const controllers = (services: Services) => { const departuresController = new DeparturesController(services.departureService, services.bookingService) const cancellationsController = new CancellationsController(services.cancellationService, services.bookingService) const lostBedsController = new LostBedsController(services.lostBedService) - const outOfServiceBedsController = new OutOfServiceBedsController(services.outOfServiceBedService) const bedsController = new BedsController(services.premisesService) const moveBedsController = new MoveBedsController(services.bookingService, services.premisesService) const dateChangesController = new DateChangesController(services.bookingService) @@ -39,7 +37,6 @@ export const controllers = (services: Services) => { departuresController, cancellationsController, lostBedsController, - outOfServiceBedsController, bedsController, moveBedsController, } @@ -49,7 +46,6 @@ export { PremisesController, BookingsController, BookingExtensionsController, - OutOfServiceBedsController, ArrivalsController, NonArrivalsController, DeparturesController, diff --git a/server/controllers/v2Manage/index.ts b/server/controllers/v2Manage/index.ts index 87f7102dea..7fbee5cdb3 100644 --- a/server/controllers/v2Manage/index.ts +++ b/server/controllers/v2Manage/index.ts @@ -2,17 +2,20 @@ import V2PremisesController from './premises/premisesController' import V2BedsController from './premises/bedsController' +import V2OutOfServiceBedsController from './outOfServiceBedsController' import type { Services } from '../../services' export const controllers = (services: Services) => { const v2PremisesController = new V2PremisesController(services.premisesService) const v2BedsController = new V2BedsController(services.premisesService) + const v2OutOfServiceBedsController = new V2OutOfServiceBedsController(services.outOfServiceBedService) return { v2PremisesController, v2BedsController, + v2OutOfServiceBedsController, } } -export { V2PremisesController, V2BedsController } +export { V2PremisesController, V2BedsController, V2OutOfServiceBedsController } diff --git a/server/controllers/manage/outOfServiceBedsController.test.ts b/server/controllers/v2Manage/outOfServiceBedsController.test.ts similarity index 82% rename from server/controllers/manage/outOfServiceBedsController.test.ts rename to server/controllers/v2Manage/outOfServiceBedsController.test.ts index 06181653ca..166442943e 100644 --- a/server/controllers/manage/outOfServiceBedsController.test.ts +++ b/server/controllers/v2Manage/outOfServiceBedsController.test.ts @@ -64,7 +64,7 @@ describe('OutOfServiceBedsController', () => { await requestHandler(request, response, next) expect(response.render).toHaveBeenCalledWith( - 'outOfServiceBeds/new', + 'v2Manage/outOfServiceBeds/new', expect.objectContaining({ premisesId, bedId: request.params.bedId }), ) }) @@ -79,7 +79,7 @@ describe('OutOfServiceBedsController', () => { await requestHandler(request, response, next) expect(response.render).toHaveBeenCalledWith( - 'outOfServiceBeds/new', + 'v2Manage/outOfServiceBeds/new', expect.objectContaining({ errors: errorsAndUserInput.errors, errorSummary: errorsAndUserInput.errorSummary, @@ -178,25 +178,60 @@ describe('OutOfServiceBedsController', () => { await requestHandler(request, response, next) expect(response.render).toHaveBeenCalledWith( - 'outOfServiceBeds/show', + 'v2Manage/outOfServiceBeds/show', expect.objectContaining({ outOfServiceBed }), ) }) }) describe('premisesIndex', () => { - it('shows a list of outOfService beds for a premises', async () => { - outOfServiceBedService.getOutOfServiceBedsForAPremises.mockResolvedValue([outOfServiceBed]) - const requestHandler = outOfServiceBedController.premisesIndex() + it('requests a paginated list of outOfService beds for a premises and renders the template', async () => { + const temporality = 'current' + + const paginatedResponse = paginatedResponseFactory.build({ + data: outOfServiceBedFactory.buildList(1), + }) as PaginatedResponse + const paginationDetails = { + hrefPrefix: `${paths.v2Manage.outOfServiceBeds.premisesIndex.pattern}?${createQueryString({ temporality, premisesId })}`, + pageNumber: 1, + } + + outOfServiceBedService.getAllOutOfServiceBeds.mockResolvedValue(paginatedResponse) + ;(getPaginationDetails as jest.Mock).mockReturnValue(paginationDetails) - await requestHandler({ ...request, params: { premisesId } }, response, next) + const req = { ...request, query: { premisesId }, params: { temporality } } - expect(response.render).toHaveBeenCalledWith('outOfServiceBeds/premisesIndex', { - outOfServiceBeds: [outOfServiceBed], + const requestHandler = outOfServiceBedController.premisesIndex() + await requestHandler({ ...req, params: { premisesId, temporality } }, response, next) + + expect(response.render).toHaveBeenCalledWith('v2Manage/outOfServiceBeds/premisesIndex', { + outOfServiceBeds: paginatedResponse.data, pageHeading: 'Manage out of service beds', premisesId, + hrefPrefix: paginationDetails.hrefPrefix, + temporality, + pageNumber: Number(paginatedResponse.pageNumber), + totalPages: Number(paginatedResponse.totalPages), + }) + expect(outOfServiceBedService.getAllOutOfServiceBeds).toHaveBeenCalledWith({ + token, + page: paginationDetails.pageNumber, + temporality, + premisesId, + perPage: 50, }) - expect(outOfServiceBedService.getOutOfServiceBedsForAPremises).toHaveBeenCalledWith(token, premisesId) + }) + + it('redirects to the current temporality if a stray temporal URL parameter is entered', async () => { + const indexRequest = { ...request, params: { premisesId, temporality: 'abc' } } + + const requestHandler = outOfServiceBedController.premisesIndex() + + await requestHandler(indexRequest, response, next) + + expect(response.redirect).toHaveBeenCalledWith( + paths.v2Manage.outOfServiceBeds.premisesIndex({ temporality: 'current', premisesId }), + ) }) }) @@ -224,7 +259,7 @@ describe('OutOfServiceBedsController', () => { await requestHandler(indexRequest, response, next) - expect(response.render).toHaveBeenCalledWith('outOfServiceBeds/index', { + expect(response.render).toHaveBeenCalledWith('v2Manage/outOfServiceBeds/index', { outOfServiceBeds: paginatedResponse.data, pageHeading: 'View out of service beds', pageNumber: Number(paginatedResponse.pageNumber), @@ -290,7 +325,10 @@ describe('OutOfServiceBedsController', () => { ) expect(request.flash).toHaveBeenCalledWith('success', 'Bed updated') expect(response.redirect).toHaveBeenCalledWith( - paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId: request.params.premisesId }), + paths.v2Manage.outOfServiceBeds.premisesIndex({ + premisesId: request.params.premisesId, + temporality: 'current', + }), ) }) @@ -342,7 +380,10 @@ describe('OutOfServiceBedsController', () => { ) expect(request.flash).toHaveBeenCalledWith('success', 'Bed cancelled') expect(response.redirect).toHaveBeenCalledWith( - paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId: request.params.premisesId }), + paths.v2Manage.outOfServiceBeds.premisesIndex({ + premisesId: request.params.premisesId, + temporality: 'current', + }), ) }) }) diff --git a/server/controllers/manage/outOfServiceBedsController.ts b/server/controllers/v2Manage/outOfServiceBedsController.ts similarity index 82% rename from server/controllers/manage/outOfServiceBedsController.ts rename to server/controllers/v2Manage/outOfServiceBedsController.ts index 19baa9ebf4..0ffdd9f2b8 100644 --- a/server/controllers/manage/outOfServiceBedsController.ts +++ b/server/controllers/v2Manage/outOfServiceBedsController.ts @@ -20,7 +20,7 @@ export default class OutOfServiceBedsController { const { premisesId, bedId } = req.params const { errors, errorSummary, userInput, errorTitle } = fetchErrorsAndUserInput(req) - return res.render('outOfServiceBeds/new', { + return res.render('v2Manage/outOfServiceBeds/new', { premisesId, bedId, errors, @@ -74,17 +74,37 @@ export default class OutOfServiceBedsController { premisesIndex(): RequestHandler { return async (req: Request, res: Response) => { - const { premisesId } = req.params + const { temporality, premisesId } = req.params as { temporality: Temporality; premisesId: string } - const outOfServiceBeds = await this.outOfServiceBedService.getOutOfServiceBedsForAPremises( - req.user.token, - premisesId, + if (!['current', 'future', 'historic'].includes(temporality)) { + return res.redirect(paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId, temporality: 'current' })) + } + + const { pageNumber, hrefPrefix } = getPaginationDetails( + req, + paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId, temporality }), + { + temporality, + premisesId, + }, ) - return res.render('outOfServiceBeds/premisesIndex', { - outOfServiceBeds, + const outOfServiceBeds = await this.outOfServiceBedService.getAllOutOfServiceBeds({ + token: req.user.token, + premisesId, + temporality, + page: pageNumber, + perPage: 50, + }) + + return res.render('v2Manage/outOfServiceBeds/premisesIndex', { + outOfServiceBeds: outOfServiceBeds.data, pageHeading: 'Manage out of service beds', premisesId, + temporality, + pageNumber: Number(outOfServiceBeds.pageNumber), + totalPages: Number(outOfServiceBeds.totalPages), + hrefPrefix, }) } } @@ -122,7 +142,7 @@ export default class OutOfServiceBedsController { apAreaId, }) - return res.render('outOfServiceBeds/index', { + return res.render('v2Manage/outOfServiceBeds/index', { pageHeading: 'View out of service beds', outOfServiceBeds: outOfServiceBeds.data, pageNumber: Number(outOfServiceBeds.pageNumber), @@ -146,7 +166,7 @@ export default class OutOfServiceBedsController { const outOfServiceBed = await this.outOfServiceBedService.getOutOfServiceBed(req.user.token, premisesId, id) - return res.render('outOfServiceBeds/show', { + return res.render('v2Manage/outOfServiceBeds/show', { errors, errorSummary, outOfServiceBed, @@ -172,14 +192,14 @@ export default class OutOfServiceBedsController { req.flash('success', 'Bed cancelled') - return res.redirect(paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId })) + return res.redirect(paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId, temporality: 'current' })) } await this.outOfServiceBedService.updateOutOfServiceBed(req.user.token, id, premisesId, req.body) req.flash('success', 'Bed updated') - return res.redirect(paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId })) + return res.redirect(paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId, temporality: 'current' })) } catch (error) { const redirectPath = req.headers.referer @@ -198,7 +218,7 @@ export default class OutOfServiceBedsController { req.flash('success', 'Bed cancelled') - return res.redirect(paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId })) + return res.redirect(paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId, temporality: 'current' })) } catch (error) { return catchValidationErrorOrPropogate( req, diff --git a/server/data/outOfServiceBedClient.test.ts b/server/data/outOfServiceBedClient.test.ts index e7fd4a6718..8040bb3b83 100644 --- a/server/data/outOfServiceBedClient.test.ts +++ b/server/data/outOfServiceBedClient.test.ts @@ -105,10 +105,11 @@ describeCas1NamespaceClient('OutOfServiceBedClient', provider => { }) describe('get', () => { - it('makes a request to the outOfServiceBeds endpoint with a page number, sort and filter options', async () => { + it('makes a request to the outOfServiceBeds endpoint with a page number, perPage, sort and filter options', async () => { const outOfServiceBeds = outOfServiceBedFactory.buildList(2) const pageNumber = 3 + const perPage = 20 const sortBy = 'roomName' const sortDirection = 'asc' const temporality = 'future' @@ -120,7 +121,14 @@ describeCas1NamespaceClient('OutOfServiceBedClient', provider => { withRequest: { method: 'GET', path: paths.manage.outOfServiceBeds.index({}), - query: { page: pageNumber.toString(), sortBy, sortDirection, temporality, apAreaId }, + query: { + page: pageNumber.toString(), + sortBy, + sortDirection, + temporality, + apAreaId, + perPage: perPage.toString(), + }, headers: { authorization: `Bearer ${token}`, }, @@ -129,9 +137,9 @@ describeCas1NamespaceClient('OutOfServiceBedClient', provider => { status: 200, body: outOfServiceBeds, headers: { - 'X-Pagination-TotalPages': '10', + 'X-Pagination-TotalPages': '5', 'X-Pagination-TotalResults': '100', - 'X-Pagination-PageSize': '10', + 'X-Pagination-PageSize': '20', }, }, }) @@ -142,14 +150,15 @@ describeCas1NamespaceClient('OutOfServiceBedClient', provider => { sortDirection, temporality, apAreaId, + perPage, }) expect(result).toEqual({ data: outOfServiceBeds, pageNumber: pageNumber.toString(), - totalPages: '10', + totalPages: '5', totalResults: '100', - pageSize: '10', + pageSize: '20', }) }) }) diff --git a/server/data/outOfServiceBedClient.ts b/server/data/outOfServiceBedClient.ts index 09a625a77e..1ba6358e6b 100644 --- a/server/data/outOfServiceBedClient.ts +++ b/server/data/outOfServiceBedClient.ts @@ -53,17 +53,19 @@ export default class OutOfServiceBedClient { temporality, premisesId, apAreaId, + perPage, }: { page: number - sortBy: OutOfServiceBedSortField - sortDirection: SortDirection temporality: Temporality + sortBy?: OutOfServiceBedSortField + sortDirection?: SortDirection premisesId?: string apAreaId?: string + perPage?: number }): Promise> { const response = (await this.restClient.get({ path: paths.manage.outOfServiceBeds.index({}), - query: createQueryString({ page, sortBy, sortDirection, temporality, premisesId, apAreaId }), + query: createQueryString({ page, sortBy, sortDirection, temporality, premisesId, apAreaId, perPage }), raw: true, })) as superagent.Response diff --git a/server/paths/manage.ts b/server/paths/manage.ts index 2209481cb9..70553551e0 100644 --- a/server/paths/manage.ts +++ b/server/paths/manage.ts @@ -129,7 +129,7 @@ const v2Manage = { outOfServiceBeds: { new: outOfServiceBedsPath.path('new'), create: outOfServiceBedsPath, - premisesIndex: singlePremisesPath.path('out-of-service-beds'), + premisesIndex: v2SinglePremisesPath.path('out-of-service-beds').path(':temporality'), index: outOfServiceBedsIndexPath.path(':temporality'), show: outOfServiceBedsPath.path(':id'), update: singlePremisesPath.path('out-of-service-beds').path(':id'), diff --git a/server/routes/v2Manage.ts b/server/routes/v2Manage.ts index 25b39505b5..1f763a6b73 100644 --- a/server/routes/v2Manage.ts +++ b/server/routes/v2Manage.ts @@ -16,10 +16,10 @@ export default function routes(controllers: Controllers, router: Router, service premisesController, bookingsController, bookingExtensionsController, - outOfServiceBedsController, bedsController, v2PremisesController, v2BedsController, + v2OutOfServiceBedsController, } = controllers // Premises @@ -93,11 +93,11 @@ export default function routes(controllers: Controllers, router: Router, service get(paths.v2Manage.bookings.extensions.confirm.pattern, bookingExtensionsController.confirm()) // Out of service beds - get(paths.v2Manage.outOfServiceBeds.new.pattern, outOfServiceBedsController.new(), { + get(paths.v2Manage.outOfServiceBeds.new.pattern, v2OutOfServiceBedsController.new(), { auditEvent: 'NEW_OUT_OF_SERVICE_BED', allowedRoles: ['future_manager'], }) - post(paths.v2Manage.outOfServiceBeds.create.pattern, outOfServiceBedsController.create(), { + post(paths.v2Manage.outOfServiceBeds.create.pattern, v2OutOfServiceBedsController.create(), { auditEvent: 'CREATE_OUT_OF_SERVICE_BED_SUCCESS', redirectAuditEventSpecs: [ { @@ -107,15 +107,15 @@ export default function routes(controllers: Controllers, router: Router, service ], allowedRoles: ['future_manager'], }) - get(paths.v2Manage.outOfServiceBeds.premisesIndex.pattern, outOfServiceBedsController.premisesIndex(), { + get(paths.v2Manage.outOfServiceBeds.premisesIndex.pattern, v2OutOfServiceBedsController.premisesIndex(), { auditEvent: 'LIST_OUT_OF_SERVICE_BEDS_FOR_A_PREMISES', allowedRoles: ['future_manager'], }) - get(paths.v2Manage.outOfServiceBeds.show.pattern, outOfServiceBedsController.show(), { + get(paths.v2Manage.outOfServiceBeds.show.pattern, v2OutOfServiceBedsController.show(), { auditEvent: 'SHOW_OUT_OF_SERVICE_BED', allowedRoles: ['future_manager'], }) - post(paths.v2Manage.outOfServiceBeds.update.pattern, outOfServiceBedsController.update(), { + post(paths.v2Manage.outOfServiceBeds.update.pattern, v2OutOfServiceBedsController.update(), { auditEvent: 'UPDATE_OUT_OF_SERVICE_BED_SUCCESS', auditBodyParams: ['cancel'], redirectAuditEventSpecs: [ @@ -126,7 +126,7 @@ export default function routes(controllers: Controllers, router: Router, service ], allowedRoles: ['future_manager'], }) - get(paths.v2Manage.outOfServiceBeds.index.pattern, outOfServiceBedsController.index(), { + get(paths.v2Manage.outOfServiceBeds.index.pattern, v2OutOfServiceBedsController.index(), { auditEvent: 'LIST_ALL_OUT_OF_SERVICE_BEDS', allowedRoles: ['cru_member'], }) diff --git a/server/services/outOfServiceBedService.test.ts b/server/services/outOfServiceBedService.test.ts index 7a1ac85afe..ef311ab7ea 100644 --- a/server/services/outOfServiceBedService.test.ts +++ b/server/services/outOfServiceBedService.test.ts @@ -76,8 +76,9 @@ describe('OutOfServiceBedService', () => { describe('getAllOutOfServiceBeds', () => { const token = 'SOME_TOKEN' - it('calls the get method on the outOfServiceBedClient with a page and sort and filter options', async () => { + it('calls the get method on the outOfServiceBedClient with a page, perPage, sort and filter options', async () => { const pageNumber = 3 + const perPage = 20 const sortBy = 'roomName' const sortDirection = 'asc' const temporality = 'future' @@ -96,6 +97,7 @@ describe('OutOfServiceBedService', () => { temporality, apAreaId, premisesId, + perPage, }) expect(outOfServiceBeds).toEqual(response) @@ -107,10 +109,11 @@ describe('OutOfServiceBedService', () => { temporality, apAreaId, premisesId, + perPage, }) }) - it('defaults to filtering for current items only with no area or premises filter, sorted by "from date" ascending', async () => { + it('uses default values for page, temporality and perPage', async () => { const response = paginatedResponseFactory.build({ data: outOfServiceBedFactory.buildList(1), }) as PaginatedResponse @@ -120,9 +123,8 @@ describe('OutOfServiceBedService', () => { expect(outOfServiceBedClient.get).toHaveBeenCalledWith({ page: 1, - sortBy: 'startDate', - sortDirection: 'asc', temporality: 'current', + perPage: 10, }) }) }) diff --git a/server/services/outOfServiceBedService.ts b/server/services/outOfServiceBedService.ts index b80fdea7cf..6a32db3ef5 100644 --- a/server/services/outOfServiceBedService.ts +++ b/server/services/outOfServiceBedService.ts @@ -62,11 +62,12 @@ export default class OutOfServiceBedService { async getAllOutOfServiceBeds({ token, page = 1, - sortBy = 'startDate', - sortDirection = 'asc', temporality = 'current', + sortBy, + sortDirection, premisesId, apAreaId, + perPage = 10, }: { token: string page?: number @@ -75,6 +76,7 @@ export default class OutOfServiceBedService { temporality?: Temporality premisesId?: string apAreaId?: string + perPage?: number }): Promise> { const outOfServiceBedClient = this.outOfServiceBedClientFactory(token) const outOfServiceBeds = await outOfServiceBedClient.get({ @@ -84,6 +86,7 @@ export default class OutOfServiceBedService { temporality, premisesId, apAreaId, + perPage, }) return outOfServiceBeds diff --git a/server/utils/outOfServiceBedUtils.test.ts b/server/utils/outOfServiceBedUtils.test.ts index 436be93063..b0e9ad58e4 100644 --- a/server/utils/outOfServiceBedUtils.test.ts +++ b/server/utils/outOfServiceBedUtils.test.ts @@ -169,7 +169,7 @@ describe('outOfServiceBedUtils', () => { describe('outOfServiceBedCountForToday', () => { it('returns the correct number of out of service beds for today', () => { - const outOfServiceBedsForToday = [...Array(getRandomInt(1, 10))].map(() => + const outOfServiceBedsForToday = [...Array(getRandomInt(2, 10))].map(() => outOfServiceBedFactory.build({ startDate: DateFormats.dateObjToIsoDate(sub(Date.now(), { days: getRandomInt(1, 10) })), endDate: DateFormats.dateObjToIsoDate(add(Date.now(), { days: getRandomInt(1, 10) })), @@ -190,7 +190,7 @@ describe('outOfServiceBedUtils', () => { expect( outOfServiceBedCountForToday([...outOfServiceBedsForToday, ...futureOutOfServiceBeds, ...pastOutOfServiceBeds]), - ).toEqual(outOfServiceBedsForToday.length) + ).toEqual(`${outOfServiceBedsForToday.length} beds`) }) }) }) diff --git a/server/utils/outOfServiceBedUtils.ts b/server/utils/outOfServiceBedUtils.ts index 8d0146088d..02a51404a8 100644 --- a/server/utils/outOfServiceBedUtils.ts +++ b/server/utils/outOfServiceBedUtils.ts @@ -109,13 +109,15 @@ export const actionCell = (bed: OutOfServiceBed, premisesId: Premises['id']): Ta html: bedLink(bed, premisesId), }) -export const outOfServiceBedCountForToday = (outOfServiceBeds: Array): number => { - return outOfServiceBeds.filter(outOfServiceBed => +export const outOfServiceBedCountForToday = (outOfServiceBeds: Array): string => { + const count = outOfServiceBeds.filter(outOfServiceBed => isWithinInterval(Date.now(), { start: DateFormats.isoToDateObj(outOfServiceBed.startDate), end: DateFormats.isoToDateObj(outOfServiceBed.endDate), }), ).length + + return count === 1 ? '1 bed' : `${count} beds` } const bedLink = (bed: OutOfServiceBed, premisesId: Premises['id']): string => diff --git a/server/utils/premises/premisesActions.test.ts b/server/utils/premises/premisesActions.test.ts index d7657bf532..0cf07ef679 100644 --- a/server/utils/premises/premisesActions.test.ts +++ b/server/utils/premises/premisesActions.test.ts @@ -12,7 +12,7 @@ describe('premisesActions', () => { expect(premisesActions(user, premises)).not.toContainManageAction({ text: 'Manage out of service bed records', classes: 'govuk-button--secondary', - href: paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId: premises.id }), + href: paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId: premises.id, temporality: 'current' }), }) }) @@ -49,7 +49,7 @@ describe('premisesActions', () => { expect(premisesActions(user, premises)).not.toContainManageAction({ text: 'Manage out of service bed records', classes: 'govuk-button--secondary', - href: paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId: premises.id }), + href: paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId: premises.id, temporality: 'current' }), }) }) @@ -86,7 +86,7 @@ describe('premisesActions', () => { expect(premisesActions(user, premises)).not.toContainManageAction({ text: 'Manage out of service bed records', classes: 'govuk-button--secondary', - href: paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId: premises.id }), + href: paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId: premises.id, temporality: 'current' }), }) }) @@ -147,7 +147,7 @@ describe('premisesActions', () => { expect(premisesActions(user, premises)).toContainManageAction({ text: 'Manage out of service bed records', classes: 'govuk-button--secondary', - href: paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId: premises.id }), + href: paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId: premises.id, temporality: 'current' }), }) }) }) @@ -160,7 +160,7 @@ describe('premisesActions', () => { expect(premisesActions(user, premises)).not.toContainManageAction({ text: 'Manage out of service bed records', classes: 'govuk-button--secondary', - href: paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId: premises.id }), + href: paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId: premises.id, temporality: 'current' }), }) }) diff --git a/server/utils/premises/premisesActions.ts b/server/utils/premises/premisesActions.ts index 2480c1b4d0..2e6cf5c9c5 100644 --- a/server/utils/premises/premisesActions.ts +++ b/server/utils/premises/premisesActions.ts @@ -15,7 +15,7 @@ export const premisesActions = (user: UserDetails, premises: Premises) => { actions.push({ text: 'Manage out of service bed records', classes: 'govuk-button--secondary', - href: paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId: premises.id }), + href: paths.v2Manage.outOfServiceBeds.premisesIndex({ premisesId: premises.id, temporality: 'current' }), }) } diff --git a/server/views/outOfServiceBeds/index.njk b/server/views/v2Manage/outOfServiceBeds/index.njk similarity index 97% rename from server/views/outOfServiceBeds/index.njk rename to server/views/v2Manage/outOfServiceBeds/index.njk index 59145a5b1f..6053c3dd99 100644 --- a/server/views/outOfServiceBeds/index.njk +++ b/server/views/v2Manage/outOfServiceBeds/index.njk @@ -6,7 +6,7 @@ {%- from "moj/components/identity-bar/macro.njk" import mojIdentityBar -%} {%- from "moj/components/sub-navigation/macro.njk" import mojSubNavigation -%} -{% extends "../partials/layout.njk" %} +{% extends "../../partials/layout.njk" %} {% set pageTitle = applicationName + " - " + pageHeading %} diff --git a/server/views/outOfServiceBeds/new.njk b/server/views/v2Manage/outOfServiceBeds/new.njk similarity index 95% rename from server/views/outOfServiceBeds/new.njk rename to server/views/v2Manage/outOfServiceBeds/new.njk index 790b2704e6..ee8f352e97 100644 --- a/server/views/outOfServiceBeds/new.njk +++ b/server/views/v2Manage/outOfServiceBeds/new.njk @@ -4,9 +4,9 @@ {% from "govuk/components/radios/macro.njk" import govukRadios %} {% from "govuk/components/back-link/macro.njk" import govukBackLink %} {% from "govuk/components/input/macro.njk" import govukInput %} -{% from "../partials/showErrorSummary.njk" import showErrorSummary %} +{% from "../../partials/showErrorSummary.njk" import showErrorSummary %} -{% extends "../partials/layout.njk" %} +{% extends "../../partials/layout.njk" %} {% set pageTitle = applicationName + " - Mark a bed as out of service" %} {% set mainClasses = "app-container govuk-body" %} diff --git a/server/views/outOfServiceBeds/premisesIndex.njk b/server/views/v2Manage/outOfServiceBeds/premisesIndex.njk similarity index 57% rename from server/views/outOfServiceBeds/premisesIndex.njk rename to server/views/v2Manage/outOfServiceBeds/premisesIndex.njk index 72d236a6ff..caa38968ef 100644 --- a/server/views/outOfServiceBeds/premisesIndex.njk +++ b/server/views/v2Manage/outOfServiceBeds/premisesIndex.njk @@ -1,10 +1,12 @@ {% from "govuk/components/table/macro.njk" import govukTable %} {% from "govuk/components/back-link/macro.njk" import govukBackLink %} {% from "govuk/components/back-link/macro.njk" import govukBackLink %}{% from "govuk/components/summary-list/macro.njk" import govukSummaryList %} +{% from "govuk/components/pagination/macro.njk" import govukPagination %} {%- from "moj/components/identity-bar/macro.njk" import mojIdentityBar -%} +{%- from "moj/components/sub-navigation/macro.njk" import mojSubNavigation -%} -{% extends "../partials/layout.njk" %} +{% extends "../../partials/layout.njk" %} {% set pageTitle = applicationName + " - " + pageHeading %} @@ -18,7 +20,7 @@ {% block content %}
- {% include "../_messages.njk" %} + {% include "../../_messages.njk" %}

{{ pageHeading }}

@@ -27,7 +29,7 @@ classes: 'govuk-summary-list--no-border', rows: [ { key: { - text: "Out of service" + text: "Current out of service beds" }, value: { text: OutOfServiceBedUtils.outOfServiceBedCountForToday(outOfServiceBeds) @@ -37,6 +39,25 @@ }) }} + {{ + mojSubNavigation({ + label: 'Sub navigation', + items: [{ + text: 'Current', + href: paths.v2Manage.outOfServiceBeds.premisesIndex({premisesId: premisesId, temporality: 'current'}), + active: temporality === 'current' + }, { + text: 'Future', + href: paths.v2Manage.outOfServiceBeds.premisesIndex({premisesId: premisesId, temporality: 'future'}), + active: temporality === 'future' + }, { + text: 'Historic', + href: paths.v2Manage.outOfServiceBeds.premisesIndex({premisesId: premisesId, temporality: 'historic'}), + active: temporality === 'historic' + }] + }) + }} + {% if outOfServiceBeds %} {{ @@ -48,6 +69,8 @@ }) }} + {{ govukPagination(pagination(pageNumber, totalPages, hrefPrefix)) }} + {% endif %}
diff --git a/server/views/outOfServiceBeds/show.njk b/server/views/v2Manage/outOfServiceBeds/show.njk similarity index 97% rename from server/views/outOfServiceBeds/show.njk rename to server/views/v2Manage/outOfServiceBeds/show.njk index ac9dbd5d96..475e20ea7a 100644 --- a/server/views/outOfServiceBeds/show.njk +++ b/server/views/v2Manage/outOfServiceBeds/show.njk @@ -7,9 +7,9 @@ {% from "govuk/components/back-link/macro.njk" import govukBackLink %} {% from "govuk/components/input/macro.njk" import govukInput %} -{% from "../partials/showErrorSummary.njk" import showErrorSummary %} +{% from "../../partials/showErrorSummary.njk" import showErrorSummary %} -{% extends "../partials/layout.njk" %} +{% extends "../../partials/layout.njk" %} {% set pageTitle = applicationName + " - " + pageHeading %} {% set mainClasses = "app-container govuk-body" %}