diff --git a/app/move/controllers/view.js b/app/move/controllers/view.js index 2380b6f1f6..43b8793949 100644 --- a/app/move/controllers/view.js +++ b/app/move/controllers/view.js @@ -28,7 +28,9 @@ module.exports = function view(req, res) { assessment_answers: assessmentAnswers = [], person_escort_record: personEscortRecord, } = profile || {} - const personEscortRecordIsComplete = personEscortRecord?.status === 'complete' + const personEscortRecordIsEnabled = FEATURE_FLAGS.PERSON_ESCORT_RECORD + const personEscortRecordIsComplete = + personEscortRecord?.status !== 'in_progress' const personEscortRecordUrl = `${originalUrl}/person-escort-record` const showPersonEscortRecordBanner = FEATURE_FLAGS.PERSON_ESCORT_RECORD && @@ -44,19 +46,47 @@ module.exports = function view(req, res) { update: updateUrls, } + const perMap = { + risk: 'risk-information', + health: 'health-information', + } + + console.log(personEscortRecord) + const locals = { move, personEscortRecord, + personEscortRecordIsEnabled, personEscortRecordIsComplete, personEscortRecordUrl, personEscortRecordtaskList, showPersonEscortRecordBanner, + personEscortRecordTagList: presenters.frameworkFlagsToTagList( + personEscortRecord?.flags + ), moveSummary: presenters.moveToMetaListComponent(move, updateActions), personalDetailsSummary: presenters.personToSummaryListComponent(person), tagList: presenters.assessmentToTagList(assessmentAnswers), assessment: presenters .assessmentAnswersByCategory(assessmentAnswers) - .map(presenters.assessmentCategoryToPanelComponent), + .map(presenters.assessmentCategoryToPanelComponent) + .map(assessment => { + if (!personEscortRecord?.id) { + return { + ...assessment, + current: assessment.panels, + isEditable: true, + } + } + + return { + ...assessment, + current: [], + previous: assessment.panels, + perSection: perMap[assessment.key], + isEditable: false, + } + }), canCancelMove: (userPermissions.includes('move:cancel') && move.status === 'requested' && diff --git a/app/move/views/_includes/assessment.njk b/app/move/views/_includes/assessment.njk index 8c18716975..a1a72c739d 100644 --- a/app/move/views/_includes/assessment.njk +++ b/app/move/views/_includes/assessment.njk @@ -6,26 +6,74 @@ }) }} - {% for panel in assessmentCategory.panels %} - {% call appPanel(panel) %} - {{ appMetaList({ - classes: "app-meta-list--divider", - items: panel.items - }) }} - {% endcall %} - {% else %} + {% if not personEscortRecordIsComplete and not assessmentCategory.isEditable %} {{ appMessage({ classes: "app-message--muted govuk-!-margin-top-2", allowDismiss: false, content: { - text: t('assessment::no_items.text', { - context: assessmentCategory.key - }) + text: "Person Escort Record " + assessmentCategory.key + " information must be completed to display this information" } }) }} - {% endfor %} + {% else %} + {% for panel in assessmentCategory.current %} + {% call appPanel(panel) %} + {{ appMetaList({ + classes: "app-meta-list--divider", + items: panel.items + }) }} + {% endcall %} + {% else %} + {{ appMessage({ + classes: "app-message--muted govuk-!-margin-top-2", + allowDismiss: false, + content: { + text: t('assessment::no_items.text', { + context: assessmentCategory.key + }) + } + }) }} + {% endfor %} + {% endif %} + + {% if assessmentCategory.previous | length %} + {% set html %} + {% for panel in assessmentCategory.panels %} + {% call appPanel(panel) %} + {{ appMetaList({ + classes: "app-meta-list--divider", + items: panel.items + }) }} + {% endcall %} + {% endfor %} + {% endset %} + + {{ govukDetails({ + html: html, + classes: "govuk-!-font-size-16 govuk-!-margin-top-4", + summaryText: "View previous information" + }) }} + {% else %} + {# {% for panel in assessmentCategory.panels %} + {% call appPanel(panel) %} + {{ appMetaList({ + classes: "app-meta-list--divider", + items: panel.items + }) }} + {% endcall %} + {% else %} + {{ appMessage({ + classes: "app-message--muted govuk-!-margin-top-2", + allowDismiss: false, + content: { + text: t('assessment::no_items.text', { + context: assessmentCategory.key + }) + } + }) }} + {% endfor %} #} + {% endif %} - {% if not personEscortRecord %} + {% if assessmentCategory.isEditable %} {{ updateLink(updateLinks[assessmentCategory.key]) }} {% endif %} diff --git a/app/move/views/view.njk b/app/move/views/view.njk index 5c8383617d..8583f809f5 100644 --- a/app/move/views/view.njk +++ b/app/move/views/view.njk @@ -168,7 +168,21 @@ }) }} - {% if tagList | length %} + {# TODO: Remove feature flag once PER feature is live #} + {% if personEscortRecordIsEnabled %} +
+ {% if personEscortRecordTagList | length and personEscortRecordIsComplete %} + {% for tag in personEscortRecordTagList %} + {{ appTag(tag) }} + {% endfor %} + {% else %} + {{ govukInsetText({ + classes: "govuk-inset-text--compact govuk-!-margin-0", + text: t("person-escort-record::flags.incomplete") + }) }} + {% endif %} +
+ {% elif tagList | length %}
{% for tag in tagList %} {{ appTag(tag) }} diff --git a/common/assets/scss/overrides/_govuk-frontend.scss b/common/assets/scss/overrides/_govuk-frontend.scss index 196e1cb00a..34e9bc25e1 100644 --- a/common/assets/scss/overrides/_govuk-frontend.scss +++ b/common/assets/scss/overrides/_govuk-frontend.scss @@ -13,3 +13,10 @@ .govuk-hint * { color: $govuk-secondary-text-colour; } + +.govuk-inset-text--compact { + @include govuk-font($size: 16); + border-left-width: 4px; + color: $govuk-secondary-text-colour; + padding: govuk-spacing(1) govuk-spacing(2); +} diff --git a/common/components/card/_card.scss b/common/components/card/_card.scss index acc7a51ece..70ce0c7ed7 100644 --- a/common/components/card/_card.scss +++ b/common/components/card/_card.scss @@ -13,6 +13,10 @@ $_image-width: 80px; padding-top: 0; margin-top: -8px; } + + .govuk-inset-text { + margin: govuk-spacing(3) 0 0; + } } .app-card--compact { diff --git a/common/components/card/template.njk b/common/components/card/template.njk index b1ccb486f8..195fdc7bad 100644 --- a/common/components/card/template.njk +++ b/common/components/card/template.njk @@ -1,5 +1,6 @@ {% from "tag/macro.njk" import appTag %} {% from "moj/components/badge/macro.njk" import mojBadge %} +{% from "govuk/components/inset-text/macro.njk" import govukInsetText %} {% set headingLevel = params.headingLevel if params.headingLevel else 2 %} @@ -61,5 +62,9 @@ {% endfor %} {% endif %} + + {% if params.message %} + {{ govukInsetText(params.message) }} + {% endif %}
diff --git a/common/components/tag/_tag.scss b/common/components/tag/_tag.scss index 80c1424fa5..8873eb8ff9 100644 --- a/common/components/tag/_tag.scss +++ b/common/components/tag/_tag.scss @@ -81,3 +81,27 @@ a.app-tag { border-color: govuk-colour("red"); } } + +.app-tag--warning { + background-color: govuk-colour("yellow"); + color: govuk-colour("black"); + + &:focus { + background-color: govuk-colour("yellow"); + color: govuk-colour("black"); + } + + @include govuk-media-query($media-type: print) { + border-color: govuk-colour("yellow"); + } +} + +a.app-tag--warning { + &:active, + &:link, + &:hover, + &:visited, + &:focus { + color: govuk-colour("black"); + } +} diff --git a/common/lib/api-client/models.js b/common/lib/api-client/models.js index 220565ea59..8b8482a50a 100644 --- a/common/lib/api-client/models.js +++ b/common/lib/api-client/models.js @@ -55,6 +55,8 @@ module.exports = { 'profile', 'profile.documents', 'profile.person_escort_record', + 'profile.person_escort_record.responses', + // 'profile.person_escort_record.flags', 'profile.person', 'profile.person.ethnicity', 'profile.person.gender', @@ -385,6 +387,10 @@ module.exports = { jsonApi: 'hasMany', type: 'framework_responses', }, + flags: { + jsonApi: 'hasMany', + type: 'framework_flags', + }, }, options: { defaultInclude: [ @@ -393,6 +399,7 @@ module.exports = { 'framework', 'responses', 'responses.question', + 'flags', ], }, }, @@ -406,6 +413,17 @@ module.exports = { }, }, }, + framework_flag: { + fields: { + name: '', + flag_type: '', + question_value: '', + // question: { + // jsonApi: 'hasOne', + // type: 'framework_questions', + // }, + }, + }, framework_question: { fields: { key: '', @@ -431,6 +449,10 @@ module.exports = { jsonApi: 'hasOne', type: 'framework_questions', }, + flags: { + jsonApi: 'hasMany', + type: 'framework_flags', + }, }, options: { defaultInclude: ['person_escort_record', 'question'], diff --git a/common/presenters/framework-flags-to-tag-list.js b/common/presenters/framework-flags-to-tag-list.js new file mode 100644 index 0000000000..0b26a2bfe7 --- /dev/null +++ b/common/presenters/framework-flags-to-tag-list.js @@ -0,0 +1,22 @@ +const { sortBy, uniqBy, kebabCase } = require('lodash') + +const { TAG_CATEGORY_WHITELIST } = require('../../config') + +function frameworkFlagsToTagList(flags, hrefPrefix = '') { + const tags = uniqBy(flags, 'name') + .filter(({ flag_type: category }) => TAG_CATEGORY_WHITELIST[category]) + .map(({ flag_type: category, name }) => { + const whitelisted = TAG_CATEGORY_WHITELIST[category] + + return { + text: name, + href: `${hrefPrefix}#${kebabCase(name)}`, + classes: whitelisted ? whitelisted.tagClass : 'app-tag--inactive', + sortOrder: whitelisted ? whitelisted.sortOrder : null, + } + }) + + return sortBy(tags, ['sortOrder', 'text']) +} + +module.exports = frameworkFlagsToTagList diff --git a/common/presenters/index.js b/common/presenters/index.js index 1b647cee95..311d6d6e26 100644 --- a/common/presenters/index.js +++ b/common/presenters/index.js @@ -9,6 +9,7 @@ const assessmentToTagList = require('./assessment-to-tag-list') const courtCaseToCardComponent = require('./court-case-to-card-component') const courtHearingToSummaryListComponent = require('./court-hearing-to-summary-list-component') const frameworkFieldToSummaryListRow = require('./framework-field-summary-list-row') +const frameworkFlagsToTagList = require('./framework-flags-to-tag-list') const frameworkStepToSummary = require('./framework-step-to-summary') const frameworkToTaskListComponent = require('./framework-to-task-list-component') const moveToCardComponent = require('./move-to-card-component') @@ -34,6 +35,7 @@ module.exports = { courtCaseToCardComponent, courtHearingToSummaryListComponent, frameworkFieldToSummaryListRow, + frameworkFlagsToTagList, frameworkStepToSummary, frameworkToTaskListComponent, moveToCardComponent, diff --git a/common/presenters/profile-to-card-component.js b/common/presenters/profile-to-card-component.js index 68122852b0..106ba083db 100644 --- a/common/presenters/profile-to-card-component.js +++ b/common/presenters/profile-to-card-component.js @@ -4,6 +4,7 @@ const i18n = require('../../config/i18n') const filters = require('../../config/nunjucks/filters') const assessmentToTagList = require('./assessment-to-tag-list') +const frameworkFlagsToTagList = require('./framework-flags-to-tag-list') function profileToCardComponent({ showImage = true, @@ -12,7 +13,12 @@ function profileToCardComponent({ } = {}) { return function item(profile) { profile = profile || {} - const { href, assessment_answers: assessmentAnswers, person = {} } = profile + const { + href, + assessment_answers: assessmentAnswers, + person = {}, + person_escort_record: personEscortRecord, + } = profile const { id, gender, @@ -54,8 +60,15 @@ function profileToCardComponent({ } if (showTags) { - card.tags = { - items: assessmentToTagList(assessmentAnswers, href), + if (personEscortRecord?.status === 'in_progress') { + card.message = { + classes: 'govuk-inset-text--compact', + text: i18n.t('person-escort-record::flags.incomplete'), + } + } else { + card.tags = { + items: frameworkFlagsToTagList(personEscortRecord.flags, href), + } } } diff --git a/common/services/move.js b/common/services/move.js index 5bc2ecbeca..c443f75b14 100644 --- a/common/services/move.js +++ b/common/services/move.js @@ -58,11 +58,28 @@ function getAll({ const noMoveIdMessage = 'No move ID supplied' const moveService = { transform(move) { - return { + const transformed = { ...move, profile: profileService.transform(move.profile), person: personService.transform(move.person), } + + if (transformed?.profile?.person_escort_record) { + transformed.profile.person_escort_record.flags = JSON.parse( + '[{"id":"167d21c3-6dc8-4acd-9c78-a8c03bce591a","type":"framework_flags","flag_type":"alert","name":"Escape risk","question_value":"Yes"},{"id":"5f533c14-bac9-4e90-b136-32cbde7979e9","type":"framework_flags","flag_type":"alert","name":"Violent","question_value":"Yes"},{"id":"691eabe9-2dd7-4e37-939d-b0c5409bff98","type":"framework_flags","flag_type":"alert","name":"Violent","question_value":"Yes"},{"id":"32039425-41c2-4d96-92bb-b7dff5264f55","type":"framework_flags","flag_type":"warning","name":"Requires special vehicle","question_value":"Yes"},{"id":"edf9d880-5f84-46b0-948c-0429297cdfd8","type":"framework_flags","flag_type":"attention","name":"Medical","question_value":"Yes"}]' + ) + + const personEscortRecordIsComplete = + transformed?.profile?.personEscortRecord?.meta?.section_progress.filter( + section => section.status !== 'completed' + ).length === 0 + + if (personEscortRecordIsComplete) { + transformed.profile.person_escort_record.status = 'completed' + } + } + + return transformed }, format(data) { const booleansAndNulls = ['move_agreed'] diff --git a/config/index.js b/config/index.js index 781976cc99..76538ba3c0 100644 --- a/config/index.js +++ b/config/index.js @@ -148,6 +148,22 @@ module.exports = { tagClass: '', sortOrder: 2, }, + alert: { + tagClass: 'app-tag--destructive', + sortOrder: 1, + }, + warning: { + tagClass: 'app-tag--warning', + sortOrder: 2, + }, + attention: { + tagClass: '', + sortOrder: 3, + }, + information: { + tagClass: '', + sortOrder: 4, + }, }, LOCATIONS_BATCH_SIZE: process.env.LOCATIONS_BATCH_SIZE || 40, E2E: { diff --git a/locales/en/person-escort-record.json b/locales/en/person-escort-record.json index 600f33b7dd..652848833a 100644 --- a/locales/en/person-escort-record.json +++ b/locales/en/person-escort-record.json @@ -12,5 +12,8 @@ "heading":"Confirm and complete this Person Escort Record", "content":"Once you are confident the information below is correct and won’t change, provide your confirmation that this record is correct in order to complete it.\n\n!!! warning\nOnce you confirm and complete the Person Escort Record you will no longer be able to update the information.\n!!!" } + }, + "flags": { + "incomplete": "Concerns will display once Person Escort Record has been completed" } } diff --git a/package-lock.json b/package-lock.json index 1c741369e5..83ecee05c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1728,6 +1728,10 @@ "version": "github:ministryofjustice/hmpps-book-secure-move-frameworks#ec94b35cac98f2edc10d2cac98faed0d02796093", "from": "github:ministryofjustice/hmpps-book-secure-move-frameworks#v0.1.0" }, + "@hmpps-book-secure-move-frameworks/0.1.1": { + "version": "github:ministryofjustice/hmpps-book-secure-move-frameworks#8c341e2284402e60c04b1dbaf2e9c5f603585d35", + "from": "github:ministryofjustice/hmpps-book-secure-move-frameworks#add-flags" + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", diff --git a/package.json b/package.json index 8ab413af9f..9980a402f2 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ ], "dependencies": { "@hmpps-book-secure-move-frameworks/0.1.0": "github:ministryofjustice/hmpps-book-secure-move-frameworks#v0.1.0", + "@hmpps-book-secure-move-frameworks/0.1.1": "github:ministryofjustice/hmpps-book-secure-move-frameworks#add-flags", "@ministryofjustice/frontend": "0.0.19-alpha", "@sentry/node": "5.19.2", "accessible-autocomplete": "2.0.3",