diff --git a/.github/workflows/planner.yml b/.github/workflows/planner.yml index de0b0189..2b2ea872 100644 --- a/.github/workflows/planner.yml +++ b/.github/workflows/planner.yml @@ -2,7 +2,7 @@ name: Spl-planner-continuous-check on: pull_request: - types: [opened, edited, reopened, ready_for_review, review_requested, auto_merge_enabled] + types: [opened, synchronize, edited, reopened, ready_for_review, review_requested, auto_merge_enabled] branches: - 'master' diff --git a/app/filters.js b/app/filters.js index 1b6896c1..a2d9b13f 100644 --- a/app/filters.js +++ b/app/filters.js @@ -2,7 +2,7 @@ const delve = require('dlv') const { getWeeksArray, parseWeeksFromData } = require('./utils') const Day = require('../common/lib/day') const { parseEligibilityFromData } = require('./lib/eligibility') -const { getBlockLength, getRemainingLeaveAllowance, getRemainingPayAllowance, parseLeaveBlocks, parseSplLeaveBlocks } = require('./lib/blocks') +const { getBlockLength, getPaternalBlockLength, getRemainingLeaveAllowance, getRemainingPayAllowance, parseLeaveBlocks, parseSplLeaveBlocks } = require('./lib/blocks') const _ = require('lodash') // Existing filters can be imported from env using env.getFilter(name) @@ -138,6 +138,10 @@ module.exports = function (env) { return getBlockLength(block) } + function paternalBlockLength (block) { + return getPaternalBlockLength(block) + } + function remainingLeaveAllowance (leaveBlocksDataObject) { const leaveBlocks = parseLeaveBlocks(leaveBlocksDataObject) return getRemainingLeaveAllowance(leaveBlocks) @@ -189,6 +193,7 @@ module.exports = function (env) { blocksToDates, htmlAttributesFromObject, blockLength, + paternalBlockLength, remainingLeaveAllowance, remainingPayAllowance, hasTakenSpl, diff --git a/app/lib/blocks.js b/app/lib/blocks.js index 05fcab9e..ff487c44 100644 --- a/app/lib/blocks.js +++ b/app/lib/blocks.js @@ -14,7 +14,7 @@ function getLeaveBlocks (weeks) { function getParentLeaveBlocks (weeks, parent) { const blocks = { - initial: null, + initial: [], spl: [] } @@ -28,7 +28,7 @@ function getParentLeaveBlocks (weeks, parent) { function store (block) { if (block && ['maternity', 'paternity', 'adoption'].includes(block.leave)) { - blocks.initial = block + blocks.initial.push(block) } else { blocks.spl.push(block) } @@ -273,6 +273,16 @@ function getBlockLength (block) { return parseInt(block.end) - parseInt(block.start) + 1 } +function getPaternalBlockLength (block) { + if (!block || block.length === 0) { + return 0 + } else if (block.length === 2) { + return 2 + } else if (block.length === 1) { + return parseInt(block[0].end) - parseInt(block[0].start) + 1 + } +} + function isBlockDataObject (obj) { return _.isObject(obj) && obj.leave !== undefined && @@ -357,5 +367,6 @@ module.exports = { getRemainingLeaveAllowance, getRemainingPayAllowance, getBlockLength, + getPaternalBlockLength, parseLeaveBlocksIntoLeaveAndPay } diff --git a/app/lib/leaveTracker.js b/app/lib/leaveTracker.js index 086b7996..14923fad 100644 --- a/app/lib/leaveTracker.js +++ b/app/lib/leaveTracker.js @@ -5,6 +5,7 @@ class LeaveTracker { this.initialBlockStarted = false this.initialBlockEnded = false this.initialBlockLength = 0 + this.totalLeaveWeeks = 0 } next (isLeave, weekNumber) { @@ -17,6 +18,9 @@ class LeaveTracker { if (this.initialBlockStarted && !this.initialBlockEnded) { this.initialBlockLength++ } + if (isLeave) { + this.totalLeaveWeeks++ + } } getLeaveBoundaries () { diff --git a/app/lib/weeks.js b/app/lib/weeks.js index f106d601..75baef97 100644 --- a/app/lib/weeks.js +++ b/app/lib/weeks.js @@ -23,16 +23,16 @@ class Weeks { const secondaryLeaveTracker = new LeaveTracker() let hasCurtailedPrimaryPay = false let primarySplHasStarted = false - for (let i = this.minimumWeek; i <= 52; i++) { - const week = this._getBaseWeek(i) - const weekLeaveAndPay = this._getWeekLeaveAndPay(i) + for (let weekNumber = this.minimumWeek; weekNumber <= 52; weekNumber++) { + const week = this._getBaseWeek(weekNumber) + const weekLeaveAndPay = this._getWeekLeaveAndPay(weekNumber) - primaryLeaveTracker.next(weekLeaveAndPay.primary.leave, i) + primaryLeaveTracker.next(weekLeaveAndPay.primary.leave, weekNumber) if (weekLeaveAndPay.primary.leave) { if (!primarySplHasStarted) { const startSplBecauseOfBreak = primaryLeaveTracker.initialBlockEnded const startSplBecauseOfPayAfterCurtailment = hasCurtailedPrimaryPay && weekLeaveAndPay.primary.pay - const startSplBecauseOfUserSelect = this.primary.firstSplWeek <= i + const startSplBecauseOfUserSelect = this.primary.firstSplWeek <= weekNumber primarySplHasStarted = startSplBecauseOfBreak || startSplBecauseOfPayAfterCurtailment || startSplBecauseOfUserSelect } dset(week.primary, 'leave.text', !primarySplHasStarted ? this.primaryLeaveType : 'shared') @@ -51,16 +51,32 @@ class Weeks { } if (!week.secondary.disabled) { - secondaryLeaveTracker.next(weekLeaveAndPay.secondary.leave, i) + secondaryLeaveTracker.next(weekLeaveAndPay.secondary.leave, weekNumber) if (weekLeaveAndPay.secondary.leave) { - const maxCellsDisplayedAsPaternity = this.eligibility.secondary.spl || this.eligibility.secondary.shpp ? 2 : 8 - const usePaternityLeave = i < 8 && !secondaryLeaveTracker.initialBlockEnded && secondaryLeaveTracker.initialBlockLength <= maxCellsDisplayedAsPaternity - dset(week.secondary, 'leave.text', usePaternityLeave ? 'paternity' : 'shared') + const maxCellsDisplayedAsPaternity = 2 + const usePaternityLeave = secondaryLeaveTracker.totalLeaveWeeks <= maxCellsDisplayedAsPaternity + + const latestPrimaryWeekLeave = this._getLatestPrimaryMaternityWeekLeave() + + // determine whether to display paternity or shared pay label + // the label should say "Paternity" if there are enough weeks left e.g. totalLeaveWeeks <= 2 + // and the selected week is within the range of the mother's leave + if (usePaternityLeave && weekNumber <= latestPrimaryWeekLeave && week.primary.leave.text === 'maternity') { + dset(week.secondary, 'leave.text', 'paternity') + } else { + dset(week.secondary, 'leave.text', 'shared') + } + + // determine whether to display pay label if (weekLeaveAndPay.secondary.pay) { dset(week.secondary, 'pay.text', this.payRates.secondary.statutory) } + + // determine whether to display pay checkbox dset(week.secondary, 'pay.eligible', this._weekEligibleForSecondaryPay(week)) } + + // determine whether to display leave checkbox dset(week.secondary, 'leave.eligible', this._weekEligibleForSecondaryLeave(week)) } @@ -89,7 +105,7 @@ class Weeks { if (this.eligibility.secondary.spl) { return true } else if (this.eligibility.secondary.statutoryLeave) { - return week.secondary.leave.text === 'paternity' && week.number < 8 + return week.secondary.leave.text === 'paternity' && week.number < 52 } else { return false } @@ -116,7 +132,7 @@ class Weeks { } else if (!this.eligibility.secondary.statutoryPay) { return false } else if (!this.eligibility.secondary.spl) { - return week.number < 8 + return week.number < 52 } else { return week.secondary.leave.text === 'paternity' } @@ -212,6 +228,11 @@ class Weeks { const payAsFloat = parseFloat(pay) return isNaN(payAsFloat) ? pay : ('£' + (+payAsFloat.toFixed(2)).toLocaleString('en-US')) } + + _getLatestPrimaryMaternityWeekLeave () { + const primaryLeaveWeeks = this.primary.leaveWeeks + return primaryLeaveWeeks.length > 0 ? Math.max(...primaryLeaveWeeks) : 0 + } } module.exports = Weeks diff --git a/app/paths.js b/app/paths.js index f9de905d..645d6645 100644 --- a/app/paths.js +++ b/app/paths.js @@ -200,11 +200,6 @@ class Paths { url: '/planner/paternity-leave/start', workFlowPage: true, workflowParentPath: '/planner/paternity-leave' - }, - end: { - url: '/planner/paternity-leave/end', - workFlowPage: true, - workflowParentPath: '/planner/paternity-leave/start' } }, 'shared-parental-leave': { @@ -213,10 +208,10 @@ class Paths { workflowParentPath: (data, isForValidator) => { if (isForValidator) { // Prevent circular reference when validating page history. - return '/planner/paternity-leave/end' + return '/planner/paternity-leave/start' } const splBlockPlanningOrder = dataUtils.splBlockPlanningOrder(data) - return splBlockPlanningOrder.length > 0 ? '/planner/shared-parental-leave/end' : '/planner/paternity-leave/end' + return splBlockPlanningOrder.length > 0 ? '/planner/shared-parental-leave/end' : '/planner/paternity-leave/start' }, start: { url: '/planner/shared-parental-leave/start', diff --git a/app/router.js b/app/router.js index 2e75f838..fa1df315 100644 --- a/app/router.js +++ b/app/router.js @@ -248,15 +248,22 @@ router.route(paths.getPath('planner.paternity-leave.start')) res.render('accessible-planner/paternity-leave-start') }) .post(function (req, res) { - res.redirect(paths.getPath('planner.paternity-leave.end')) - }) - -router.route(paths.getPath('planner.paternity-leave.end')) - .get(function (req, res) { - clearLaterLeaveBlockAnswers(req, 'secondary.initial.end') - res.render('accessible-planner/paternity-leave-end') - }) - .post(function (req, res) { + const startData = delve(req.session.data, 'leave-blocks.secondary.initial.start') + const updatedData = { + initial: [ + { + start: startData[0], + end: parseInt(startData[0]) + 1, + leave: 'paternity' + }, + { + start: startData[1], + end: parseInt(startData[1]) + 1, + leave: 'paternity' + } + ] + } + dset(req.session.data, 'leave-blocks.secondary.initial', updatedData.initial) res.redirect(paths.getPath('planner.shared-parental-leave')) }) diff --git a/app/views/accessible-planner/answers-so-far/macro.njk b/app/views/accessible-planner/answers-so-far/macro.njk index a0189390..020e358f 100644 --- a/app/views/accessible-planner/answers-so-far/macro.njk +++ b/app/views/accessible-planner/answers-so-far/macro.njk @@ -21,6 +21,11 @@

Your answers so far

+

+ Leave{{ leaveBlocks["secondary"]["initial"][0].start }} + Leave{{ leaveBlocks["secondary"]["initial"][1].start }} +

+ {{ govukSummaryList({ rows: [ { @@ -76,10 +81,10 @@ }, { key: { - text: "Paternity Leave start" + text: "Paternity Leave start (week 1)" }, value: { - text: date(data, leaveBlocks["secondary"]["initial"]["start"]) + text: date(data, leaveBlocks["secondary"]["initial"][0]["start"]) }, actions: { items: [ @@ -93,17 +98,17 @@ }, { key: { - text: "Paternity Leave end" + text: "Paternity Leave start (week 2)" }, value: { - text: dateEnd(data, leaveBlocks["secondary"]["initial"]["end"]) + text: date(data, leaveBlocks["secondary"]["initial"][1]["start"]) }, actions: { items: [ { - href: "/planner/paternity-leave/end", + href: "/planner/paternity-leave/start", text: "Change", - visuallyHiddenText: "Paternity Leave end" + visuallyHiddenText: "Paternity Leave start" } ] } diff --git a/app/views/accessible-planner/paternity-leave-end.njk b/app/views/accessible-planner/paternity-leave-end.njk index ddc6c369..471424dd 100644 --- a/app/views/accessible-planner/paternity-leave-end.njk +++ b/app/views/accessible-planner/paternity-leave-end.njk @@ -25,7 +25,7 @@ {% set isOverseasAdoption = (data | isOverseasAdoption) %} {% set zeroWeek = (data | zeroWeek) %} {% set firstWeekOfLeave = (data["leave-blocks"]["secondary"]["initial"]["start"] | int) %} -{% set options = range(firstWeekOfLeave, firstWeekOfLeave + 2 if firstWeekOfLeave < 7 else 8) %} +{% set options = range(firstWeekOfLeave, firstWeekOfLeave + 2 if firstWeekOfLeave < 51 else 52) %} {% macro optionText(weekNumber) %} {% set totalWeeks = weekNumber - firstWeekOfLeave + 1 %} diff --git a/app/views/accessible-planner/paternity-leave-start.njk b/app/views/accessible-planner/paternity-leave-start.njk index 6adaa911..aa5088c1 100644 --- a/app/views/accessible-planner/paternity-leave-start.njk +++ b/app/views/accessible-planner/paternity-leave-start.njk @@ -26,9 +26,9 @@ {% set zeroWeek = (data | zeroWeek) %} {% set isAdoption = (data | isAdoption) %} {% set birthOrPlacement= (data | birthOrPlacement) %} -{% set primaryLeaveEnd = data["leave-blocks"]["primary"]["initial"]["end"] %} +{% set primaryLeaveEnd = data["leave-blocks"]["primary"]["initial"]["end"] | float %} -{% set options = range(0, 8) %} +{% set options = range(primaryLeaveEnd + 1) %} {% macro optionText(weekNumber) %} {% if (weekNumber === 0) %} @@ -49,7 +49,17 @@ id: "primary-leave-start", name: "leave-blocks[secondary][initial][start]", label: { - text: "When will Paternity Leave start?", + text: "When will the first week of Paternity Leave start?", + classes: "govuk-label--l", + isPageHeading: true + }, + items: options | mapValuesToSelectOptions(optionText) + }) }} + {{ govukSelect({ + id: "primary-leave-start", + name: "leave-blocks[secondary][initial][start]", + label: { + text: "When will the second week of Paternity Leave start?", classes: "govuk-label--l", isPageHeading: true }, diff --git a/app/views/accessible-planner/paternity-leave.njk b/app/views/accessible-planner/paternity-leave.njk index 0d0c19ff..db669ea2 100644 --- a/app/views/accessible-planner/paternity-leave.njk +++ b/app/views/accessible-planner/paternity-leave.njk @@ -42,8 +42,7 @@ {% call appendHiddenFields(data) %} {% set hint %}

- A partner can take up to 2 weeks of Paternity Leave in the first 8 weeks after {{ birthOrPlacement }}. - Weeks must be taken together, without breaks. + A partner can take up to 2 weeks of Paternity Leave in the first year after {{ birthOrPlacement }}.

{% if (remainingLeaveAllowance > 0) %}

diff --git a/app/views/components/paternity-leave-block-summary.njk b/app/views/components/paternity-leave-block-summary.njk new file mode 100644 index 00000000..ac36f691 --- /dev/null +++ b/app/views/components/paternity-leave-block-summary.njk @@ -0,0 +1,42 @@ +{% from "summary-list/macro.njk" import govukSummaryList %} + +{% macro paternityLeaveBlockSummary(options) %} + {{ govukSummaryList({ + classes: "summary-block-print-margin", + rows: [ + { + key: { + text: options.name + " starts (week 1)" + }, + value: { + text: "week starting " + options.data | startDay | startOfWeek | offsetWeeks(options.block[0].start) | formatForDisplay + } + }, + { + key: { + text: options.name + " starts (week 2)" + }, + value: { + text: "week starting " + options.data | startDay | startOfWeek | offsetWeeks(options.block[1].start) | formatForDisplay + } + } if options.block.length > 1 else "", + { + key: { + text: "Length" + }, + value: { + text: options.block | paternalBlockLength | weeks + } + }, + { + key: { + text: "Notify employers" + }, + value: { + html: "by " + (options.notify.date | formatForDisplay) + ("*" if options.notify.asterisk) + "
" + + "(" + options.notify.explanation + ")" + } + } + ] + }) }} +{% endmacro %} diff --git a/app/views/planner.njk b/app/views/planner.njk index 1b0bc0a2..77cd4e35 100644 --- a/app/views/planner.njk +++ b/app/views/planner.njk @@ -105,6 +105,23 @@ html: examples, classes: "print-hide" }) }} + + {% set additionalText %} + {% if isAdoption %} + From the 6th April 2024 (inclusive), adoption partners will be able to take the Paternity Leave and/or Pay that they are entitled to in two-separate, one-week blocks at any time within the 52 weeks after placement of the child. Any Paternity Leave and Pay they wish to claim must still be taken before starting Shared Parental Leave. +

+ If you have been notified that the child is due to be placed with you on or after the 6th April, but the child is then placed with you early and before this date, the adoption partner will still be able to claim their Paternity Leave and Pay under these new rules. + {% else %} + From the 7th April 2024, birth partners of babies that are due to be born after this date will be able to take the Paternity Leave and/or Pay that they are entitled to in two-separate, one-week blocks at any time within the 52 weeks after the birth of the child. Any Paternity Leave and Pay they wish to claim must still be taken before starting Shared Parental Leave. +

+ If your child is due to be born after the 6th April 2024 but is born prematurely or before this date, you will still be entitled to claim your Paternity Leave and Pay under these new rules. + {% endif %} + {% endset %} + + {{ govukInsetText({ + text: additionalText | safe + }) }} +

diff --git a/app/views/tabs/leave-summary.njk b/app/views/tabs/leave-summary.njk index aad7c170..af62d5bf 100644 --- a/app/views/tabs/leave-summary.njk +++ b/app/views/tabs/leave-summary.njk @@ -1,6 +1,7 @@ {% from "summary-list/macro.njk" import govukSummaryList %} {% from "inset-text/macro.njk" import govukInsetText %} {% from "../components/leave-block-summary.njk" import leaveBlockSummary %} +{% from "../components/paternity-leave-block-summary.njk" import paternityLeaveBlockSummary %} {% set primaryLeaveType = (data | primaryLeaveType) %} @@ -72,12 +73,12 @@ {% if data | isAdoption %} -

Paternity Leave can start the day your child is placed with you, or within 8 weeks after.

+

Paternity Leave can start at any point in the first year after the adoption of the child.

{% else %} -

Paternity Leave can start once the child is born, or within 8 weeks of the child’s birth.

+

Paternity Leave can start at any point in the first year after the birth of the child.

{% endif %} - {{ leaveBlockSummary({ + {{ paternityLeaveBlockSummary({ name: "Paternity Leave", block: leaveBlocks.secondary.initial, data: data, diff --git a/common/assets/sass/components/_cookie-message.scss b/common/assets/sass/components/_cookie-message.scss index 5fd0f142..bcaa7830 100644 --- a/common/assets/sass/components/_cookie-message.scss +++ b/common/assets/sass/components/_cookie-message.scss @@ -1,3 +1,4 @@ +// sass-lint:disable no-ids, no-color-literals .cookie-banner { box-sizing: border-box; width: 100%; @@ -10,7 +11,8 @@ } } -#cookie_banner > h1, #cookie_banner > p { +#cookie_banner > h1, +#cookie_banner > p { background-color: govuk-colour("white"); } @@ -19,12 +21,12 @@ } .success-message { - border:5px; - border-style:solid; - border-color:#28a197; - padding:20px; - margin-bottom:50px; - margin-top:16px; + border: 5px; + border-style: solid; + border-color: #28a197; + padding: 20px; + margin-bottom: 50px; + margin-top: 16px; } #cookie-preference-success { diff --git a/manifest.yml b/manifest.yml index 2b1c29e4..d6a5c69c 100644 --- a/manifest.yml +++ b/manifest.yml @@ -2,4 +2,4 @@ applications: - name: beis-spl-planner memory: 1G buildpacks: - - nodejs_buildpack + - https://github.com/cloudfoundry/nodejs-buildpack#v1.8.15 diff --git a/test/unit/lib/blocks.test.js b/test/unit/lib/blocks.test.js index 199630a4..1b8644b4 100644 --- a/test/unit/lib/blocks.test.js +++ b/test/unit/lib/blocks.test.js @@ -37,14 +37,18 @@ describe('Blocks', () => { const expectedResults = { leaveBlocks: { primary: { - initial: { start: 0, end: 1, leave: 'maternity' }, + initial: [ + { start: 0, end: 1, leave: 'maternity' } + ], spl: [ { start: 3, end: 4, leave: 'shared' }, { start: 8, end: 9, leave: 'shared' } ] }, secondary: { - initial: { start: 0, end: 1, leave: 'paternity' }, + initial: [ + { start: 0, end: 1, leave: 'paternity' } + ], spl: [ { start: 3, end: 4, leave: 'shared' }, { start: 6, end: 7, leave: 'shared' } @@ -69,11 +73,13 @@ describe('Blocks', () => { const expectedResults = { leaveBlocks: { primary: { - initial: { start: 0, end: 0, leave: 'maternity' }, + initial: [ + { start: 0, end: 0, leave: 'maternity' } + ], spl: [] }, secondary: { - initial: null, + initial: [], spl: [] } }, @@ -90,14 +96,18 @@ describe('Blocks', () => { const expectedResults = { leaveBlocks: { primary: { - initial: { start: 0, end: 1, leave: 'maternity' }, + initial: [ + { start: 0, end: 1, leave: 'maternity' } + ], spl: [ { start: 3, end: 4, leave: 'shared' }, { start: 8, end: 9, leave: 'shared' } ] }, secondary: { - initial: { start: 0, end: 1, leave: 'paternity' }, + initial: [ + { start: 0, end: 1, leave: 'paternity' } + ], spl: [ { start: 3, end: 4, leave: 'shared' }, { start: 6, end: 7, leave: 'shared' }