@ministryofjustice/hmpps-mpop-frontend-components-lib 0.0.29 → 0.0.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/_all.scss CHANGED
@@ -5,3 +5,4 @@
5
5
  @import "./mpop/components/pop-header/pop-header";
6
6
  @import "./mpop/components/supervision-final-third-progress/supervision-final-third-progress";
7
7
  @import "./mpop/components/person-header/person-header";
8
+ @import "./mpop/components/person-header/risk-flag-badge";
package/dist/index.d.ts CHANGED
@@ -104,6 +104,7 @@ type ContextDetails = {
104
104
  finalThirdEligibility: FinalThirdEligibility;
105
105
  liferCategory?: LiferCategory;
106
106
  recallStatus?: CodeDescription;
107
+ preventConcerns?: boolean;
107
108
  };
108
109
  type CurrentPhase = {
109
110
  supervisionPackage: CodeDescription;
@@ -47,22 +47,12 @@
47
47
  }
48
48
  }
49
49
 
50
- .person-header__risk-panel {
51
- display: flex;
52
- flex-direction: column;
53
- gap: govuk-spacing(2);
54
- background-color: govuk-colour("black", $variant: "tint-95");
55
- padding: govuk-spacing(3) govuk-spacing(4);
56
- }
57
-
58
- .person-header__risk-tags {
59
- display: flex;
60
- flex-wrap: nowrap;
61
- align-items: center;
62
- gap: govuk-spacing(2);
63
- }
64
-
65
50
  .person-header__name .app-status-badge {
66
51
  position: relative;
67
52
  top: -8px;
53
+ }
54
+
55
+ .person-header__risk-panel + .person-header__risk-panel {
56
+ margin-top: 0 !important;
57
+ padding-top: 0 !important;
68
58
  }
@@ -32,19 +32,108 @@
32
32
  border: 2px solid #85994b;
33
33
  }
34
34
 
35
+ .pop-header-risk-badges .moj-badge,
36
+ .pop-header-risk-flags .moj-badge {
37
+ /* Ensure badges in the header preserve original casing - override moj-frontend defaults */
38
+ text-transform: none !important;
39
+ font-weight: 400;
40
+ }
41
+
42
+ .person-header__risk-panel {
43
+ display: flex;
44
+ flex-direction: column;
45
+ gap: govuk-spacing(2);
46
+ background-color: govuk-colour("black", $variant: "tint-95");
47
+ padding: govuk-spacing(3) govuk-spacing(4);
48
+ }
49
+
50
+
35
51
  .pop-header-risk-badges {
36
52
  &:not(:empty) {
37
53
  margin-bottom: govuk-spacing(2);
38
54
  }
39
55
  }
40
56
 
41
- .person-header__risk-tags {
42
- flex-direction: column;
43
- align-items: flex-start;
44
- }
45
57
  .pop-header-risk-badges .moj-badge,
46
- .pop-header-risk-flags .moj-badge {
47
- /* Ensure badges in the header preserve original casing - override moj-frontend defaults */
58
+ .pop-header-risk-flags .moj-badge,
59
+ .person-header__risk-tags .moj-badge {
48
60
  text-transform: none !important;
49
61
  font-weight: 400;
62
+ }
63
+
64
+ .person-header__risk-tags .risk-badge-link {
65
+ font-weight: 400;
66
+ text-transform: none;
67
+ }
68
+
69
+ .person-header__risk-tags .moj-badge strong {
70
+ font-weight: 700;
71
+ }
72
+
73
+ .person-header__risk-tags {
74
+ display: flex;
75
+ flex-wrap: wrap;
76
+ align-items: center;
77
+ gap: 10px;
78
+ }
79
+
80
+ .person-header__risk-tags .moj-badge {
81
+ border-radius: 5px;
82
+ white-space: nowrap;
83
+ }
84
+ .person-header__risk-tags .dps-alert-status {
85
+ display: inline-flex;
86
+ align-items: center;
87
+ justify-content: center;
88
+ background-color: #fff;
89
+ border: 3px solid;
90
+ padding: 5px 10px;
91
+ height: initial;
92
+ font-size: 16px;
93
+ text-align: center;
94
+ line-height: 1;
95
+ user-select: none;
96
+ border-radius: 5px;
97
+ color: #0b0c0c;
98
+ text-decoration: none;
99
+ white-space: nowrap;
100
+ font-weight: 400;
101
+ text-transform: none;
102
+ }
103
+
104
+ /* Risk flag colours */
105
+ $risk-badge-very-high: #711a0d;
106
+ $risk-badge-high: #d4351c;
107
+ $risk-badge-medium: #f2590d;
108
+ $risk-badge-low: #85994b;
109
+
110
+ $risk-badge-colours: (
111
+ 'very-high': $risk-badge-very-high,
112
+ 'high': $risk-badge-high,
113
+ 'medium': $risk-badge-medium,
114
+ 'low': $risk-badge-low,
115
+ );
116
+
117
+ @each $level, $colour in $risk-badge-colours {
118
+ .person-header__risk-tags .risk-badge--#{$level} {
119
+ border-color: $colour;
120
+
121
+ &:hover {
122
+ background-color: $colour;
123
+ color: #fff;
124
+ text-decoration-thickness: 2px;
125
+ }
126
+ }
127
+ }
128
+
129
+ .person-header__risk-tags .dps-alert-status:focus {
130
+ outline: 3px solid transparent;
131
+ color: #0b0c0c;
132
+ background-color: #ffdd00;
133
+ box-shadow: 0 -2px #ffdd00, 0 4px #0b0c0c;
134
+ text-decoration: none;
135
+ }
136
+
137
+ .person-header__risk-tags .risk-badge-more {
138
+ white-space: nowrap;
50
139
  }
@@ -1,80 +1,137 @@
1
1
 
2
-
3
2
  <div class='person-header'>
4
3
  <div class='person-header__band{% if params.photo %} person-header__band--with-photo{% endif %}'>
5
4
  {% if params.photo %}
6
- <img class='person-header__photo' data-qa='personPhoto' src='{{ params.photo }}' alt='Photo of {{ params.name }}'>
5
+ <img
6
+ class='person-header__photo'
7
+ data-qa='personPhoto'
8
+ src='{{ params.photo }}'
9
+ alt='Photo of {{ params.name }}'
10
+ >
7
11
  {% endif %}
8
12
 
9
13
  <div class='person-header__details'>
10
- <h1 class="govuk-heading-xl govuk-!-margin-bottom-2 person-header__name" data-qa="personName">
14
+ <h1
15
+ class='govuk-heading-xl govuk-!-margin-bottom-2 person-header__name'
16
+ data-qa='personName'
17
+ >
11
18
  {{ params.name }}
19
+
12
20
  {% if params.personStatusTag %}
13
- <strong class="app-status-badge govuk-tag govuk-tag--yellow" data-qa="personStatusTag">{{ params.personStatusTag }}</strong>
14
- {%endif%}
21
+ <strong
22
+ class='app-status-badge govuk-tag govuk-tag--yellow'
23
+ data-qa='personStatusTag'
24
+ >
25
+ {{ params.personStatusTag }}
26
+ </strong>
27
+ {% endif %}
15
28
  </h1>
29
+
16
30
  <p class='govuk-caption-l govuk-!-margin-bottom-3 person-header__crn'>
17
- <span class='govuk-!-font-weight-bold'>CRN:</span> <span data-qa='crn'>{{ params.crn }}</span>
31
+ <span class='govuk-!-font-weight-bold'>CRN:</span>
32
+ <span data-qa='crn'>{{ params.crn }}</span>
18
33
  </p>
19
34
 
20
35
  <ul class='govuk-list person-header__meta'>
21
36
  <li>
22
- <span class='govuk-!-font-weight-bold'>Date of birth:</span> <span data-qa='dob'>{{ params.dob }}</span>
37
+ <span class='govuk-!-font-weight-bold'>Date of birth:</span>
38
+ <span data-qa='dob'>{{ params.dob }}</span>
23
39
  </li>
40
+
24
41
  <li>
25
- <span class='govuk-!-font-weight-bold'>Tier:</span> <a class='govuk-link govuk-link--inverse' href='{{ params.historyHref }}' data-qa='tier'>{{ params.tier }}</a>
42
+ <span class='govuk-!-font-weight-bold'>Tier:</span>
43
+ <a
44
+ class='govuk-link govuk-link--inverse'
45
+ href='{{ params.historyHref }}'
46
+ data-qa='tier'
47
+ >
48
+ {{ params.tier }}
49
+ </a>
26
50
  </li>
51
+
27
52
  <li>
28
53
  {% if params.managedByName %}
29
- {% set managedByText = params.managedByName | formatName %}
30
- {% if params.managedByLocation %}
31
- {% set managedByText = managedByText + ' (' + params.managedByLocation + ')' %}
32
- {% endif %}
54
+ {% set managedByText = params.managedByName | formatName %}
55
+
56
+ {% if params.managedByLocation %}
57
+ {% set managedByText = managedByText + ' (' + params.managedByLocation + ')' %}
58
+ {% endif %}
33
59
  {% else %}
34
- {% set managedByText = params.managedBy %}
60
+ {% set managedByText = params.managedBy %}
35
61
  {% endif %}
62
+
36
63
  <span class='govuk-!-font-weight-bold'>Managed by:</span>
64
+
37
65
  {% if params.managedByHref %}
38
- <a class='govuk-link govuk-link--inverse' href='{{ params.managedByHref }}' data-qa='managedBy'>{{ managedByText }}</a>
66
+ <a
67
+ class='govuk-link govuk-link--inverse'
68
+ href='{{ params.managedByHref }}'
69
+ data-qa='managedBy'
70
+ >
71
+ {{ managedByText }}
72
+ </a>
39
73
  {% else %}
40
- <span data-qa='managedBy'>{{ managedByText }}</span>
74
+ <span data-qa='managedBy'>{{ managedByText }}</span>
41
75
  {% endif %}
42
76
  </li>
43
77
  </ul>
44
78
  </div>
45
79
  </div>
46
80
 
81
+ {# Risk badges and risk flags #}
82
+
83
+ {% if (params.riskBadges and (params.riskBadges | trim)) or
84
+ (params.personRiskFlags and
85
+ params.personRiskFlags.groups and
86
+ (params.personRiskFlags.groups | length) > 0) %}
87
+
88
+ {# RSR / ROSH risk badges #}
89
+
47
90
  {% if params.riskBadges and (params.riskBadges | trim) %}
48
- <div class='person-header__risk-panel'>
49
- <div class='person-header__risk-tags' data-qa='riskBadges'>
50
- {{ params.riskBadges | safe }}
51
- </div>
91
+ <div class='person-header__risk-panel'>
92
+ <div
93
+ class='person-header__risk-tags'
94
+ data-qa='riskBadges'
95
+ >
96
+ {{ params.riskBadges | safe }}
52
97
  </div>
98
+ </div>
53
99
  {% endif %}
54
100
 
55
- {% if params.riskBadgeData and params.riskBadgeData.groups and (params.riskBadgeData.groups | length) > 0 %}
56
- <div class='person-header__risk-panel'>
57
- <div class='person-header__risk-tags' data-qa='personRiskBadges'>
58
- {% for group in params.riskBadgeData.groups %}
101
+ {# Person risk flags #}
102
+
103
+ {% if params.personRiskFlags and
104
+ params.personRiskFlags.groups and
105
+ (params.personRiskFlags.groups | length) > 0 %}
106
+
107
+ <div class='person-header__risk-panel'>
108
+ <div
109
+ class='person-header__risk-tags'
110
+ data-qa='personRiskBadges'
111
+ >
112
+ {% for group in params.personRiskFlags.groups %}
59
113
  {% for risk in group.badges %}
60
- <a href='/case/{{ params.crn }}/risk/flag/{{ risk.id }}'
61
- class='risk-badge-link'
62
- data-qa='risk-badge-{{ risk.id }}'>
63
- <span class='moj-badge {{ risk.badgeClass }}'>
64
- {{ risk.text }}
65
- </span>
114
+ <a
115
+ href='/case/{{ params.crn }}/risk/flag/{{ risk.id }}'
116
+ class='govuk-link govuk-link--no-underline govuk-link--text-colour dps-alert-status dps-alert-status--risk {{ risk.badgeClass }}'
117
+ data-qa='risk-badge-{{ risk.id }}'
118
+ >
119
+ {{ risk.text | safe }}
66
120
  </a>
67
121
  {% endfor %}
68
122
  {% endfor %}
69
123
 
70
- {% if params.riskBadgeData.remainingCount > 0 %}
71
- <a href='/case/{{ params.crn }}/risk'
72
- class='govuk-link risk-badge-more'
73
- data-qa='risk-badge-more'>
74
- +{{ params.riskBadgeData.remainingCount }} active risk flags
124
+ {% if params.personRiskFlags.remainingCount > 0 %}
125
+ <a
126
+ href='/case/{{ params.crn }}/risk'
127
+ class='govuk-link risk-badge-more'
128
+ data-qa='risk-badge-more'
129
+ >
130
+ +{{ params.personRiskFlags.remainingCount }} active risk flags
75
131
  </a>
76
132
  {% endif %}
77
133
  </div>
78
134
  </div>
79
135
  {% endif %}
80
- </div>
136
+ {% endif %}
137
+ </div>
@@ -1,13 +1,14 @@
1
- {% if forename and (params.currentYear.appointments.completed | int >= params.currentYear.appointments.allowance | int)%}
1
+ {% set resets = params.currentYear.endDate | supervisionAppointmentsReset(params.context.sentences) %}
2
+ {% set stageEndDate = ("The supervision package resets on " if resets else "The supervision stage ends on ") + (params.currentYear.endDate | dateWithYear) + "." %}
3
+
4
+ {% if params.context.preventConcerns %}
5
+ <p class='govuk-body'>{{ stageEndDate }}</p>
6
+ {% elseif forename and (params.currentYear.appointments.completed | int >= params.currentYear.appointments.allowance | int)%}
2
7
  <p class='govuk-body'>
3
8
  {{ forename }} has used all the supervision package appointments. If new risks emerge or there are exceptional circumstances, you can arrange contingency appointments.
4
9
  </p>
5
- {% if params.currentYear.endDate | supervisionAppointmentsReset(params.context.sentences) %}
6
- <p class='govuk-body'>The supervision package resets on {{ params.currentYear.endDate | dateWithYear }}.</p>
7
- {% else %}
8
- <p class='govuk-body'>The supervision stage ends on {{ params.currentYear.endDate | dateWithYear }}.</p>
9
- {% endif %}
10
- {% elseif params.currentYear.endDate | supervisionAppointmentsReset(params.context.sentences) %}
10
+ <p class='govuk-body'>{{ stageEndDate }}</p>
11
+ {% elseif resets %}
11
12
  <p class='govuk-body'>{{ forename }} has {{ (params.currentYear.appointments.allowance | int) - (params.currentYear.appointments.completed | int) }} supervision appointments remaining until the supervision package resets on {{ params.currentYear.endDate | dateWithYear }}.</p>
12
13
  {% else %}
13
14
  <p class='govuk-body'>{{ forename }} has {{ (params.currentYear.appointments.allowance | int) - (params.currentYear.appointments.completed | int) }} supervision appointments remaining until the supervision stage ends on {{ params.currentYear.endDate | dateWithYear }}.</p>
@@ -1,5 +1,3 @@
1
- {% set sentence = params.context.sentences | getPrimarySentence %}
2
-
3
1
  {% if forename and (params.earlyEngagement.completed | int < params.earlyEngagement.weeks | int) %}
4
2
  <p class='govuk-body'>
5
3
  You should see {{ forename }} every week for the first {{ params.earlyEngagement.weeks }} weeks of the sentence.
@@ -0,0 +1,12 @@
1
+ {% if phaseCode == 'FTHRD' %}
2
+ <p class='govuk-body'>{{ forename }} is in the final third of the sentence. </p>
3
+ {% endif %}
4
+ <p class='govuk-body'>{{ forename }} has a Prevent concerns risk flag and can receive additional appointments while this flag is active. Use your judgement to decide how many they need.</p>
5
+ {% if phaseCode != 'FTHRD' %}
6
+ <p class='govuk-body'>When the flag ends, the supervision package is recalculated on a pro rata basis.</p>
7
+ {% include './_appointments-guidance.njk' %}
8
+
9
+ {% include './_final-third-eligibility.njk' %}
10
+
11
+ {% include './_progress-bar.njk' %}
12
+ {% endif %}
@@ -13,11 +13,13 @@
13
13
  {% endif %}
14
14
 
15
15
  {% set widthPercent = 100 if rawPercent > 100 else (0 if rawPercent < 0 else rawPercent) %}
16
+ {% set remaining = allowance - completed %}
17
+ {% set remaining = 0 if remaining < 0 else remaining %}
16
18
 
17
19
  <p class='govuk-body'>{{ completed }} of {{ allowance }} appointments used</p>
18
20
 
19
21
  <div class="appointment-progress" aria-hidden="true">
20
- <div class="{{ 'appointment-progress__bar-maximum' if phaseCode != 'INIT' and widthPercent == 100 else 'appointment-progress__bar' }} appointment-progress__bar--width-{{ widthPercent }}"></div>
22
+ <div class="{{ 'appointment-progress__bar-maximum' if (phaseCode != 'INIT' and not params.context.preventConcerns) and widthPercent == 100 else 'appointment-progress__bar' }} appointment-progress__bar--width-{{ widthPercent }}"></div>
21
23
  </div>
22
24
  <span class="govuk-visually-hidden">progress indicator, {{ widthPercent }}%</span>
23
25
 
@@ -29,7 +31,7 @@
29
31
 
30
32
  <div class='govuk-grid-column-one-third'>
31
33
  <p class='govuk-body govuk-!-margin-bottom-2'>Remaining</p>
32
- <p class='govuk-body-l govuk-!-font-weight-bold'>{{ allowance - completed }}</p>
34
+ <p class='govuk-body-l govuk-!-font-weight-bold'>{{ remaining }}</p>
33
35
  </div>
34
36
 
35
37
  <div class='govuk-grid-column-one-third'>
@@ -1,5 +1,3 @@
1
- {% set sentence = params.context.sentences | getPrimarySentence %}
2
-
3
1
  <p class='govuk-body'>{{ forename }} has an IOM red RAG status. The maximum number of appointments is the same as tier A.</p>
4
2
 
5
3
  {% include './_appointments-guidance.njk'%}
@@ -1,4 +1,3 @@
1
- {% set sentence = params.context.sentences | getPrimarySentence %}
2
1
  {% include './_appointments-guidance.njk' %}
3
2
 
4
3
  {% if lifeCategoryCode %}
@@ -1,4 +1,4 @@
1
- {% if params.currentYear.appointments.allowance is defined and params.currentYear.appointments.completed is defined and (params.currentYear.appointments.allowance | int) > 0 and (params.currentYear.appointments.completed | int) >= (params.currentYear.appointments.allowance | int) and not params.context.offenderPersonalDisorderPathway %}
1
+ {% if params.currentYear.appointments.allowance is defined and not params.context.preventConcerns and params.currentYear.appointments.completed is defined and (params.currentYear.appointments.allowance | int) > 0 and (params.currentYear.appointments.completed | int) >= (params.currentYear.appointments.allowance | int) and not params.context.offenderPersonalDisorderPathway %}
2
2
  <strong class='app-status-badge govuk-tag govuk-tag--red'>No appointments remaining</strong>
3
3
  {% endif %}
4
4
 
@@ -10,6 +10,10 @@
10
10
  <strong class='app-status-badge govuk-tag govuk-tag--yellow'>In breach</strong>
11
11
  {% endif %}
12
12
 
13
+ {% if params.context.preventConcerns %}
14
+ <strong class='app-status-badge govuk-tag govuk-tag--blue'>Additional appointments needed</strong>
15
+ {% endif %}
16
+
13
17
  {% if params.context.offenderPersonalDisorderPathway %}
14
18
  <strong class='app-status-badge govuk-tag govuk-tag--purple'>Offender personality disorder</strong>
15
19
  {% endif %}
@@ -1,6 +1,6 @@
1
1
  {% from "../supervision-final-third-progress/macro.njk" import supervisionFinalThirdProgress %}
2
2
  {% set standardAppointmentsComplete = params.currentYear.appointments.allowance is defined and params.currentYear.appointments.completed is defined and (params.currentYear.appointments.completed | int) >= (params.currentYear.appointments.allowance| int) %}
3
- {% set hasSpecialStatus = params.context.offenderPersonalDisorderPathway or params.context.sentences | hasBreachedSentence or params.context.sentences | isInCustody or params.context.sentences | isAtLarge or params.context.integratedOffenderManagementRedRated %}
3
+ {% set hasSpecialStatus = params.context.offenderPersonalDisorderPathway or params.context.preventConcerns or params.context.sentences | hasBreachedSentence or params.context.sentences | isInCustody or params.context.sentences | isAtLarge or params.context.integratedOffenderManagementRedRated %}
4
4
  {% set sentenceTypeValue = params.context | sentenceType %}
5
5
  {% set phaseCode = params.currentPhase.phase.code | upper %}
6
6
  {% set showPhaseColumn = phaseCode == 'INIT' or phaseCode == 'STD' or phaseCode == 'FTHRD' or phaseCode == 'IOM' or phaseCode == 'OPD' or phaseCode == 'SPNS' %}
@@ -79,7 +79,11 @@
79
79
 
80
80
  {% if showPhaseColumn and params.currentPhase %}
81
81
  <div class='govuk-grid-column-one-half'>
82
- {% if params.context.offenderPersonalDisorderPathway %}
82
+ {% if params.context.preventConcerns %}
83
+ <h4 class='govuk-heading-s'>{{ 'Final third stage' if phaseCode == 'FTHRD' else 'Supervision appointments' }}</h4>
84
+
85
+ {% include './partials/_prevent-concerns.njk' %}
86
+ {% elseif params.context.offenderPersonalDisorderPathway %}
83
87
  <h4 class='govuk-heading-s'>Supervision appointments</h4>
84
88
  {% include './partials/_opd.njk' %}
85
89
  {% elseif params.context.integratedOffenderManagementRedRated %}
@@ -55,6 +55,7 @@ export type ContextDetails = {
55
55
  finalThirdEligibility: FinalThirdEligibility;
56
56
  liferCategory?: LiferCategory;
57
57
  recallStatus?: CodeDescription;
58
+ preventConcerns?: boolean;
58
59
  };
59
60
  export type CurrentPhase = {
60
61
  supervisionPackage: CodeDescription;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ministryofjustice/hmpps-mpop-frontend-components-lib",
3
- "version": "0.0.29",
3
+ "version": "0.0.31",
4
4
  "description": "MPOP front-end components library",
5
5
  "main": "./dist/index.cjs.js",
6
6
  "module": "./dist/index.esm.js",
@@ -103,8 +103,14 @@
103
103
  "@istanbuljs/load-nyc-config": {
104
104
  "js-yaml": "3.15.2"
105
105
  },
106
- "brace-expansion@1": "1.1.18",
107
- "brace-expansion@2": "2.1.4",
108
- "brace-expansion@5": "5.0.9"
106
+ "brace-expansion@1": "1.1.21",
107
+ "brace-expansion@2": "2.1.7",
108
+ "brace-expansion@5": "5.0.12",
109
+ "jsdom": {
110
+ "undici": "7.29.1"
111
+ },
112
+ "node-gyp": {
113
+ "undici": "6.28.1"
114
+ }
109
115
  }
110
116
  }