@ministryofjustice/hmpps-digital-prison-reporting-frontend 3.23.2 → 3.23.4

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.
@@ -1086,24 +1086,31 @@ class DprAsyncRequestList extends DprPollingStatusClass {
1086
1086
  this.initStatusPollingStatus();
1087
1087
  }
1088
1088
 
1089
- initExpiredPollingStatus() {
1089
+ async initExpiredPollingStatus() {
1090
+ // run expired check on load first
1091
+ await this.checkIfExpired(true);
1092
+
1090
1093
  setInterval(async () => {
1091
- if (this.requestData) {
1092
- const meta = JSON.parse(this.requestData);
1093
- await Promise.all(
1094
- meta.map(async (metaData) => {
1095
- if (metaData.status !== 'EXPIRED') {
1096
- const response = await this.getExpiredStatus('/getRequestedExpiredStatus/', metaData, this.csrfToken);
1097
- if (response.isExpired) {
1098
- window.location.reload();
1099
- }
1100
- }
1101
- }),
1102
- );
1103
- }
1094
+ await this.checkIfExpired();
1104
1095
  }, '60000'); // 1 minute
1105
1096
  }
1106
1097
 
1098
+ async checkIfExpired() {
1099
+ if (this.requestData) {
1100
+ const meta = JSON.parse(this.requestData);
1101
+ await Promise.all(
1102
+ meta.map(async (metaData) => {
1103
+ if (metaData.status !== 'EXPIRED') {
1104
+ const response = await this.getExpiredStatus('/getRequestedExpiredStatus/', metaData, this.csrfToken);
1105
+ if (response.isExpired) {
1106
+ window.location.reload();
1107
+ }
1108
+ }
1109
+ }),
1110
+ );
1111
+ }
1112
+ }
1113
+
1107
1114
  initStatusPollingStatus() {
1108
1115
  setInterval(async () => {
1109
1116
  if (this.requestData) {
@@ -1398,7 +1405,7 @@ class ShowMore extends DprClientClass {
1398
1405
  const textContainer = element.querySelector('.dpr-show-more-content');
1399
1406
  const button = element.querySelector('.dpr-show-hide-button');
1400
1407
  const textContent = element.getAttribute('data-content');
1401
- const shortString = textContent.substring(0, 200);
1408
+ const shortString = textContent.replaceAll(/<[^>]+>/g, '').substring(0, 200);
1402
1409
 
1403
1410
  if (textContent.length > 200) {
1404
1411
  textContainer.innerHTML = `${shortString}... `;
@@ -14,7 +14,6 @@ import DefinitionUtils from '../../utils/definitionUtils'
14
14
  import { getDuplicateRequestIds } from '../../utils/reportSummaryHelper'
15
15
  import { Services } from '../../types/Services'
16
16
  import { RenderFiltersReturnValue } from './types'
17
-
18
17
  /**
19
18
  * Initialises the filters & Sort from the definition data
20
19
  *
@@ -22,7 +22,7 @@
22
22
  html: reportDetailsHtml,
23
23
  classes: 'dpr-meta-data-details'
24
24
  }) }}
25
-
25
+
26
26
  <div class="dpr-async-controls dpr-filters" data-dpr-module="async-filters">
27
27
  <form action="{{ endpoint }}" method="post" id="async-filters-form">
28
28
  <input type="hidden" name="dataProductDefinitionsPath" value="{{ definitionPath }}">
@@ -29,11 +29,12 @@ const reportStatusHelper_1 = require("../../utils/reportStatusHelper");
29
29
  const ReportListHelper = __importStar(require("../../utils/reportsListHelper"));
30
30
  const formatCardData = (requestedReportsData) => {
31
31
  const reportData = JSON.parse(JSON.stringify(requestedReportsData));
32
- const { executionId, reportId, variantId, name: text, description, query, status, timestamp, dataProductDefinitionsPath, } = reportData;
32
+ const { executionId, reportId, variantId, name: text, description, query, status, timestamp, reportName, dataProductDefinitionsPath, } = reportData;
33
33
  const summary = query.summary;
34
34
  return {
35
35
  id: executionId,
36
36
  text,
37
+ reportName,
37
38
  description,
38
39
  ...(0, exports.setDataFromStatus)(status, reportData),
39
40
  tag: 'MIS',
@@ -15,6 +15,7 @@ export const formatCardData = (requestedReportsData: AsyncReportData): CardData
15
15
  query,
16
16
  status,
17
17
  timestamp,
18
+ reportName,
18
19
  dataProductDefinitionsPath,
19
20
  } = reportData
20
21
  const summary = query.summary as { name: string; value: string }[]
@@ -22,6 +23,7 @@ export const formatCardData = (requestedReportsData: AsyncReportData): CardData
22
23
  return {
23
24
  id: executionId,
24
25
  text,
26
+ reportName,
25
27
  description,
26
28
  ...setDataFromStatus(status, reportData),
27
29
  tag: 'MIS',
@@ -4,7 +4,7 @@
4
4
  {% set tooltip %}{{ 'Remove Bookmark' if bookmarked else 'Add Bookmark' }}{% endset %}
5
5
 
6
6
  <div class='dpr-bookmark dpr-bookmark-inline dpr-bookmark-tooltip' tooltip="{{ tooltip }}" data-dpr-module="bookmark-toggle">
7
- <input class="bookmark-input" aria-label="bookmark toggle" type='checkbox' id='{{ variantId }}-{{id}}' data-variant-id='{{ variantId }}' data-report-id='{{ reportId }}' data-csrf-token='{{ csrfToken }}' {{ checked }}/>
8
- <label tabindex="0" for='{{ variantId }}-{{id}}' class=''><span class="dpr-visually-hidden">Bookmark toggle</span></label>
7
+ <input class="bookmark-input" aria-label="bookmark toggle" type='checkbox' id='{{ reportId }}-{{ variantId }}-{{id}}' data-variant-id='{{ variantId }}' data-report-id='{{ reportId }}' data-csrf-token='{{ csrfToken }}' {{ checked }}/>
8
+ <label id="${variantId}-${reportId}-${id}-bookmark-label" tabindex="0" for='{{ reportId }}-{{ variantId }}-{{id}}' class=''><span class="dpr-visually-hidden">Bookmark toggle</span></label>
9
9
  </div>
10
10
  {% endmacro %}
@@ -7,6 +7,7 @@
7
7
  {% set nextHref = pagination.next %}
8
8
  {% set pages = pagination.pages %}
9
9
  {% set sizes = pagination.sizes %}
10
+ {% set pageSize = pagination.pageSize %}
10
11
 
11
12
  <div class="dpr-pagination">
12
13
  <div class="govuk-grid-column-three-quarters govuk-!-padding-left-0">
@@ -20,6 +21,7 @@
20
21
  items: pages
21
22
  }) }}
22
23
  </div>
24
+
23
25
  <div class="govuk-grid-column-one-quarter govuk-!-padding-0 data-table-page-size-container">
24
26
  <div class="data-table-page-size">
25
27
  <div class="govuk-input__wrapper">
@@ -28,24 +28,28 @@ const AsyncReport_1 = require("../../types/AsyncReport");
28
28
  const reportStatusHelper_1 = require("../../utils/reportStatusHelper");
29
29
  const ReportListHelper = __importStar(require("../../utils/reportsListHelper"));
30
30
  const formatCardData = (reportData) => {
31
- const { executionId: id, variantName: text, description, query, url, timestamp, executionId, reportId, variantId, dataProductDefinitionsPath, } = reportData;
31
+ const { executionId: id, variantName: text, description, query, url, timestamp, executionId, reportId, variantId, reportName, dataProductDefinitionsPath, } = reportData;
32
32
  let { status } = reportData;
33
33
  let href;
34
+ let ts;
34
35
  if (status === AsyncReport_1.RequestStatus.EXPIRED) {
35
36
  href = `${url.request.fullUrl}`;
37
+ ts = `Expired at: ${new Date(timestamp.expired).toLocaleString()}`;
36
38
  }
37
39
  else {
38
40
  status = AsyncReport_1.RequestStatus.READY;
41
+ ts = `Last viewed: ${new Date(timestamp.lastViewed).toLocaleString()}`;
39
42
  href = url.report.fullUrl;
40
43
  }
41
44
  return {
42
45
  id,
43
46
  text,
47
+ reportName,
44
48
  description,
45
49
  tag: 'MIS',
46
50
  summary: query.summary,
47
51
  href,
48
- timestamp: `Last viewed: ${new Date(timestamp.lastViewed).toLocaleString()}`,
52
+ timestamp: ts,
49
53
  status,
50
54
  meta: {
51
55
  reportId,
@@ -16,26 +16,31 @@ export const formatCardData = (reportData: RecentlyViewedReportData): CardData =
16
16
  executionId,
17
17
  reportId,
18
18
  variantId,
19
+ reportName,
19
20
  dataProductDefinitionsPath,
20
21
  } = reportData
21
22
  let { status } = reportData
22
23
 
23
24
  let href
25
+ let ts
24
26
  if (status === RequestStatus.EXPIRED) {
25
27
  href = `${url.request.fullUrl}`
28
+ ts = `Expired at: ${new Date(timestamp.expired).toLocaleString()}`
26
29
  } else {
27
30
  status = RequestStatus.READY
31
+ ts = `Last viewed: ${new Date(timestamp.lastViewed).toLocaleString()}`
28
32
  href = url.report.fullUrl
29
33
  }
30
34
 
31
35
  return {
32
36
  id,
33
37
  text,
38
+ reportName,
34
39
  description,
35
40
  tag: 'MIS',
36
41
  summary: query.summary as { name: string; value: string }[],
37
42
  href,
38
- timestamp: `Last viewed: ${new Date(timestamp.lastViewed).toLocaleString()}`,
43
+ timestamp: ts,
39
44
  status,
40
45
  meta: {
41
46
  reportId,
@@ -53,7 +53,6 @@ export default {
53
53
  const rows = sortedVariants.map((v: variantData) => {
54
54
  const { variantId, variantName, variantDescription, reportName, reportId, reportDescription } = v
55
55
  const description = variantDescription || reportDescription
56
-
57
56
  return [
58
57
  { text: reportName },
59
58
  { html: `<a href="/async-reports/${reportId}/${variantId}/request${pathSuffix}">${variantName}</a>` },
@@ -1,5 +1,5 @@
1
1
  {% macro dprShowMore(text) %}
2
- <div class="dpr-show-more" data-content='{{ text }}' data-dpr-module="show-more">
2
+ <div class="dpr-show-more" data-content="{{ text }}" data-dpr-module="show-more">
3
3
  <p><span class='dpr-show-more-content'>{{ text }}</span><a class="dpr-show-hide-button" href="#">Show more</a></p>
4
4
  </div>
5
5
  {% endmacro %}
@@ -34,6 +34,7 @@ export interface CardData {
34
34
  id: string
35
35
  href: string
36
36
  text: string
37
+ reportName: string
37
38
  description: string
38
39
  timestamp?: string
39
40
  tag?: string
@@ -53,7 +53,7 @@ class BookmarkService extends userStoreService_1.default {
53
53
  const tooltip = !checked ? 'Add Bookmark' : 'Remove Bookmark';
54
54
  return `<div class='dpr-bookmark dpr-bookmark-tooltip dpr-bookmark-table' tooltip="${tooltip}" data-dpr-module="bookmark-toggle">
55
55
  <input class="bookmark-input" aria-label="bookmark toggle" type='checkbox' id='${reportId}-${variantId}-${id}' data-report-id='${reportId}' data-variant-id='${variantId}' data-csrf-token='${csrfToken}' ${checked} />
56
- <label id="${variantId}-${reportId}-bookmark-label" for='${reportId}-${variantId}-${id}'><span class="dpr-visually-hidden">Bookmark toggle</span></label>
56
+ <label id="${variantId}-${reportId}-${id}-bookmark-label" for='${reportId}-${variantId}-${id}'><span class="dpr-visually-hidden">Bookmark toggle</span></label>
57
57
  </div>`;
58
58
  }
59
59
  }
@@ -61,7 +61,7 @@ export default class BookmarkService extends UserStoreService {
61
61
  const tooltip = !checked ? 'Add Bookmark' : 'Remove Bookmark'
62
62
  return `<div class='dpr-bookmark dpr-bookmark-tooltip dpr-bookmark-table' tooltip="${tooltip}" data-dpr-module="bookmark-toggle">
63
63
  <input class="bookmark-input" aria-label="bookmark toggle" type='checkbox' id='${reportId}-${variantId}-${id}' data-report-id='${reportId}' data-variant-id='${variantId}' data-csrf-token='${csrfToken}' ${checked} />
64
- <label id="${variantId}-${reportId}-bookmark-label" for='${reportId}-${variantId}-${id}'><span class="dpr-visually-hidden">Bookmark toggle</span></label>
64
+ <label id="${variantId}-${reportId}-${id}-bookmark-label" for='${reportId}-${variantId}-${id}'><span class="dpr-visually-hidden">Bookmark toggle</span></label>
65
65
  </div>`
66
66
  }
67
67
  }
@@ -30,6 +30,7 @@ class RecentlyViewedStoreService extends userStoreService_1.default {
30
30
  return this.recentlyViewedReports;
31
31
  }
32
32
  async getAllReportsByVariantId(variantId) {
33
+ await this.getRecentlyViewedState();
33
34
  return this.recentlyViewedReports.filter((report) => {
34
35
  return report.variantId === variantId;
35
36
  });
@@ -37,6 +37,7 @@ export default class RecentlyViewedStoreService extends UserStoreService {
37
37
  }
38
38
 
39
39
  async getAllReportsByVariantId(variantId: string) {
40
+ await this.getRecentlyViewedState()
40
41
  return this.recentlyViewedReports.filter((report) => {
41
42
  return report.variantId === variantId
42
43
  })
@@ -85,6 +85,7 @@ class AsyncReportStoreService extends userStoreService_1.default {
85
85
  return this.requestedReports;
86
86
  }
87
87
  async getAllReportsByVariantId(variantId) {
88
+ await this.getRequestedReportsState();
88
89
  return this.requestedReports.filter((report) => {
89
90
  return report.variantId === variantId;
90
91
  });
@@ -117,6 +117,7 @@ export default class AsyncReportStoreService extends UserStoreService {
117
117
  }
118
118
 
119
119
  async getAllReportsByVariantId(variantId: string) {
120
+ await this.getRequestedReportsState()
120
121
  return this.requestedReports.filter((report) => {
121
122
  return report.variantId === variantId
122
123
  })
@@ -12,9 +12,10 @@ const formatCards = async (bookmarksData, maxRows) => {
12
12
  exports.formatCards = formatCards;
13
13
  const formatCardData = (bookmarkData) => {
14
14
  const reportData = JSON.parse(JSON.stringify(bookmarkData));
15
- const { variantId, name, description, href } = reportData;
15
+ const { variantId, name, description, href, reportName } = reportData;
16
16
  return {
17
17
  id: variantId,
18
+ reportName,
18
19
  text: name,
19
20
  description,
20
21
  href,
@@ -16,10 +16,11 @@ export const formatCards = async (bookmarksData: BookmarkedReportData[], maxRows
16
16
 
17
17
  export const formatCardData = (bookmarkData: BookmarkedReportData): CardData => {
18
18
  const reportData: BookmarkedReportData = JSON.parse(JSON.stringify(bookmarkData))
19
- const { variantId, name, description, href } = reportData
19
+ const { variantId, name, description, href, reportName } = reportData
20
20
 
21
21
  return {
22
22
  id: variantId,
23
+ reportName,
23
24
  text: name,
24
25
  description,
25
26
  href,
@@ -61,7 +61,10 @@ const getExpiredStatus = async ({ req, res, services }) => {
61
61
  catch (error) {
62
62
  const { data } = error;
63
63
  errorMessage = (data !== null && data !== void 0 ? data : {}).userMessage;
64
- status = currentStatus === AsyncReport_1.RequestStatus.READY ? AsyncReport_1.RequestStatus.EXPIRED : AsyncReport_1.RequestStatus.FAILED;
64
+ status =
65
+ currentStatus === AsyncReport_1.RequestStatus.READY || currentStatus === AsyncReport_1.RequestStatus.FINISHED
66
+ ? AsyncReport_1.RequestStatus.EXPIRED
67
+ : AsyncReport_1.RequestStatus.FAILED;
65
68
  }
66
69
  const result = {
67
70
  status,
@@ -80,7 +80,10 @@ export const getExpiredStatus = async ({ req, res, services }: AsyncReportUtilsP
80
80
  } catch (error) {
81
81
  const { data } = error
82
82
  errorMessage = (data ?? {}).userMessage
83
- status = currentStatus === RequestStatus.READY ? RequestStatus.EXPIRED : RequestStatus.FAILED
83
+ status =
84
+ currentStatus === RequestStatus.READY || currentStatus === RequestStatus.FINISHED
85
+ ? RequestStatus.EXPIRED
86
+ : RequestStatus.FAILED
84
87
  }
85
88
 
86
89
  const result: GetStatusUtilsResponse = {
@@ -24,8 +24,9 @@ const getMeta = (cardData) => {
24
24
  };
25
25
  exports.getMeta = getMeta;
26
26
  const createShowMoreHtml = (text) => {
27
- return `<div class="dpr-show-more" data-content='${text}' data-dpr-module="show-more">
28
- <p><span class='dpr-show-more-content'>${text}</span><a class="dpr-show-hide-button" href="#">Show more</a></p>
27
+ const sanitizedString = text.replace(/"/g, "'");
28
+ return `<div class="dpr-show-more" data-content="${sanitizedString}" data-dpr-module="show-more">
29
+ <p><span class='dpr-show-more-content'>${sanitizedString}</span><a class="dpr-show-hide-button" href="#">Show more</a></p>
29
30
  </div>`;
30
31
  };
31
32
  exports.createShowMoreHtml = createShowMoreHtml;
@@ -36,6 +37,7 @@ const formatTable = (cardData, type) => {
36
37
  return {
37
38
  rows,
38
39
  head: [
40
+ { text: 'Product' },
39
41
  { text: 'Name' },
40
42
  { text: 'Description' },
41
43
  { text: 'Applied Filters', classes: `dpr-req-filters-summary` },
@@ -77,6 +79,7 @@ const formatTableData = (card, type) => {
77
79
  break;
78
80
  }
79
81
  return [
82
+ { text: card.reportName },
80
83
  { html: `<a href='${card.href}'>${card.text}</a>` },
81
84
  { html: (0, reportSummaryHelper_1.createDetailsHtml)('Description', card.description) },
82
85
  { html: (0, reportSummaryHelper_1.createDetailsHtml)('Applied Filters', (0, reportSummaryHelper_1.createSummaryHtml)(card)) },
@@ -87,9 +90,9 @@ const formatTableData = (card, type) => {
87
90
  ];
88
91
  };
89
92
  exports.formatTableData = formatTableData;
90
- const formatCards = async (reports, filterFunction, formatFuntion) => {
93
+ const formatCards = async (reports, filterFunction, formatFunction) => {
91
94
  return reports.filter(filterFunction).map((report) => {
92
- return formatFuntion(report);
95
+ return formatFunction(report);
93
96
  });
94
97
  };
95
98
  exports.formatCards = formatCards;
@@ -25,8 +25,10 @@ export const getMeta = (cardData: CardData[]) => {
25
25
  }
26
26
 
27
27
  export const createShowMoreHtml = (text: string) => {
28
- return `<div class="dpr-show-more" data-content='${text}' data-dpr-module="show-more">
29
- <p><span class='dpr-show-more-content'>${text}</span><a class="dpr-show-hide-button" href="#">Show more</a></p>
28
+ const sanitizedString = text.replace(/"/g, "'")
29
+
30
+ return `<div class="dpr-show-more" data-content="${sanitizedString}" data-dpr-module="show-more">
31
+ <p><span class='dpr-show-more-content'>${sanitizedString}</span><a class="dpr-show-hide-button" href="#">Show more</a></p>
30
32
  </div>`
31
33
  }
32
34
 
@@ -38,6 +40,7 @@ export const formatTable = (cardData: CardData[], type: 'requested' | 'viewed')
38
40
  return {
39
41
  rows,
40
42
  head: [
43
+ { text: 'Product' },
41
44
  { text: 'Name' },
42
45
  { text: 'Description' },
43
46
  { text: 'Applied Filters', classes: `dpr-req-filters-summary` },
@@ -81,6 +84,7 @@ export const formatTableData = (card: CardData, type: 'requested' | 'viewed') =>
81
84
  }
82
85
 
83
86
  return [
87
+ { text: card.reportName },
84
88
  { html: `<a href='${card.href}'>${card.text}</a>` },
85
89
  { html: createDetailsHtml('Description', card.description) },
86
90
  { html: createDetailsHtml('Applied Filters', createSummaryHtml(card)) },
@@ -94,9 +98,9 @@ export const formatTableData = (card: CardData, type: 'requested' | 'viewed') =>
94
98
  export const formatCards = async (
95
99
  reports: AsyncReportData[] | RecentlyViewedReportData[],
96
100
  filterFunction: (report: AsyncReportData | RecentlyViewedReportData) => boolean,
97
- formatFuntion: (reportData: RecentlyViewedReportData | AsyncReportData) => CardData,
101
+ formatFunction: (reportData: RecentlyViewedReportData | AsyncReportData) => CardData,
98
102
  ): Promise<CardData[]> => {
99
103
  return reports.filter(filterFunction).map((report: AsyncReportData | RecentlyViewedReportData) => {
100
- return formatFuntion(report)
104
+ return formatFunction(report)
101
105
  })
102
106
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ministryofjustice/hmpps-digital-prison-reporting-frontend",
3
3
  "description": "The Digital Prison Reporting Frontend contains templates and code to help display data effectively in UI applications.",
4
- "version": "3.23.2",
4
+ "version": "3.23.4",
5
5
  "main": "dpr/assets/js/all.mjs",
6
6
  "sass": "dpr/all.scss",
7
7
  "engines": {
package/package.zip CHANGED
Binary file