@ministryofjustice/hmpps-digital-prison-reporting-frontend 3.12.2 → 3.13.0

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.
@@ -792,6 +792,7 @@ class AsyncFilters extends DprQueryParamClass {
792
792
 
793
793
  const params = new URLSearchParams(search);
794
794
  params.delete('retryId');
795
+ params.delete('refreshId');
795
796
  const href = `${origin}${pathname}?${params.toString()}`;
796
797
 
797
798
  document.getElementById('async-filters-form-href').value = href;
@@ -816,9 +817,13 @@ class AsyncFilters extends DprQueryParamClass {
816
817
  initRetryInputFromQueryParams () {
817
818
  this.queryParams = new URLSearchParams(window.location.search);
818
819
  const retryId = this.queryParams.get('retryId');
820
+ const refreshId = this.queryParams.get('refreshId');
819
821
  if (retryId) {
820
822
  document.getElementById('async-filters-retry-id').value = retryId;
821
823
  }
824
+ if (refreshId) {
825
+ document.getElementById('async-filters-refresh-id').value = refreshId;
826
+ }
822
827
  }
823
828
  }
824
829
 
@@ -992,7 +997,7 @@ class dprAsyncPolling extends DprClientClass {
992
997
 
993
998
  async initialise () {
994
999
  this.POLLING_STATUSES = ['SUBMITTED', 'STARTED', 'PICKED'];
995
- this.POLLING_FREQUENCY = '5000'; // 5 seconds
1000
+ this.POLLING_FREQUENCY = '3000'; // 3 seconds
996
1001
 
997
1002
  this.statusSection = document.getElementById('async-request-polling-status');
998
1003
  this.retryRequestButton = document.getElementById('retry-async-request');
@@ -122,12 +122,16 @@ exports.default = {
122
122
  }
123
123
  });
124
124
  const token = ((_a = res.locals.user) === null || _a === void 0 ? void 0 : _a.token) ? res.locals.user.token : 'token';
125
- const { reportId, variantId, retryId } = req.body;
125
+ const { reportId, variantId, retryId, refreshId } = req.body;
126
126
  const response = await dataSources.requestAsyncReport(token, reportId, variantId, query);
127
127
  const { executionId, tableId } = response;
128
128
  if (retryId) {
129
- await asyncReportsStore.setReportRetriedTimestamp(retryId);
130
- await recentlyViewedStoreService.setReportRetriedTimestamp(retryId);
129
+ await asyncReportsStore.setReportTimestamp(retryId, 'retry');
130
+ await recentlyViewedStoreService.setReportTimestamp(retryId, 'retry');
131
+ }
132
+ if (refreshId) {
133
+ await asyncReportsStore.setReportTimestamp(refreshId, 'refresh');
134
+ await recentlyViewedStoreService.setReportTimestamp(refreshId, 'refresh');
131
135
  }
132
136
  if (executionId && tableId) {
133
137
  const reportData = await asyncReportsStore.addReport({
@@ -136,13 +136,18 @@ export default {
136
136
  })
137
137
 
138
138
  const token = res.locals.user?.token ? res.locals.user.token : 'token'
139
- const { reportId, variantId, retryId } = req.body
139
+ const { reportId, variantId, retryId, refreshId } = req.body
140
140
  const response = await dataSources.requestAsyncReport(token, reportId, variantId, query)
141
141
  const { executionId, tableId } = response
142
142
 
143
143
  if (retryId) {
144
- await asyncReportsStore.setReportRetriedTimestamp(retryId)
145
- await recentlyViewedStoreService.setReportRetriedTimestamp(retryId)
144
+ await asyncReportsStore.setReportTimestamp(retryId, 'retry')
145
+ await recentlyViewedStoreService.setReportTimestamp(retryId, 'retry')
146
+ }
147
+
148
+ if (refreshId) {
149
+ await asyncReportsStore.setReportTimestamp(refreshId, 'refresh')
150
+ await recentlyViewedStoreService.setReportTimestamp(refreshId, 'refresh')
146
151
  }
147
152
 
148
153
  if (executionId && tableId) {
@@ -31,6 +31,7 @@
31
31
  <input type="hidden" name="href" id="async-filters-form-href" value="">
32
32
  <input type="hidden" name="search" id="async-filters-form-search" value="">
33
33
  <input type="hidden" name="retryId" id="async-filters-retry-id" value="">
34
+ <input type="hidden" name="refreshId" id="async-filters-refesh-id" value="">
34
35
 
35
36
  <div class="dpr-async-controls__section">
36
37
  <h3 class="govuk-heading-s">Filters</h3>
@@ -3,68 +3,26 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.renderReport = exports.initDataSources = void 0;
7
- /* eslint-disable no-underscore-dangle */
8
6
  const parseurl_1 = __importDefault(require("parseurl"));
9
7
  const utils_1 = __importDefault(require("../data-table/utils"));
10
8
  const utils_2 = __importDefault(require("../async-columns/utils"));
11
9
  const utils_3 = __importDefault(require("../pagination/utils"));
12
- const utils_4 = __importDefault(require("../icon-button-list/utils"));
13
- const initDataSources = ({ req, res, next, asyncReportsStore, dataSources }) => {
14
- var _a;
15
- const token = ((_a = res.locals.user) === null || _a === void 0 ? void 0 : _a.token) ? res.locals.user.token : 'token';
16
- const { reportId, variantId: reportVariantId, tableId } = req.params;
17
- const { selectedPage = 1, pageSize = 10 } = req.query;
18
- const dataProductDefinitionsPath = req.query.dataProductDefinitionsPath;
19
- const reportDefinitionPromise = dataSources.getDefinition(token, reportId, reportVariantId, dataProductDefinitionsPath);
20
- const reportDataPromise = dataSources.getAsyncReport(token, reportId, reportVariantId, tableId, {
21
- selectedPage: +selectedPage,
22
- pageSize: +pageSize,
23
- dataProductDefinitionsPath,
24
- });
25
- const reportDataCountPromise = dataSources.getAsyncCount(token, tableId);
26
- const stateDataPromise = asyncReportsStore.getReportByTableId(tableId);
27
- return [reportDefinitionPromise, reportDataPromise, reportDataCountPromise, stateDataPromise];
10
+ exports.default = {
11
+ getRenderData: (req, definition, reportData, count, reportStateData) => {
12
+ const { columns: reqColumns } = req.query;
13
+ const { fields } = definition.variant.specification;
14
+ const columns = utils_2.default.getColumns(fields, reqColumns);
15
+ const url = (0, parseurl_1.default)(req);
16
+ const pagination = utils_3.default.getPaginationData(url, count);
17
+ const rows = utils_1.default.mapData(reportData, fields, columns.value);
18
+ const head = utils_1.default.mapAsyncHeader(fields, columns.value);
19
+ const { query } = reportStateData;
20
+ return {
21
+ rows,
22
+ head,
23
+ columns,
24
+ pagination,
25
+ appliedFilters: query.summary,
26
+ };
27
+ },
28
28
  };
29
- exports.initDataSources = initDataSources;
30
- const renderReport = async ({ req, res, next, asyncReportsStore, recentlyViewedStoreService, dataSources, }) => {
31
- const { columns: reqColumns } = req.query;
32
- const dataPromises = (0, exports.initDataSources)({ req, res, next, dataSources, asyncReportsStore });
33
- let renderData = {};
34
- let reportStateData;
35
- if (dataPromises) {
36
- await Promise.all(dataPromises).then((resolvedData) => {
37
- const definition = resolvedData[0];
38
- const reportData = resolvedData[1];
39
- const count = resolvedData[2];
40
- reportStateData = resolvedData[3];
41
- const fieldDefinition = definition.variant.specification.fields;
42
- const { classification } = definition.variant;
43
- const columns = utils_2.default.getColumns(fieldDefinition, reqColumns);
44
- const url = (0, parseurl_1.default)(req);
45
- const pagination = utils_3.default.getPaginationData(url, count);
46
- const actions = utils_4.default.initReportActions(definition.variant, reportStateData);
47
- const rows = utils_1.default.mapData(reportData, fieldDefinition, columns.value);
48
- const head = utils_1.default.mapAsyncHeader(fieldDefinition, columns.value);
49
- const { reportName, name: variantName, query, description } = reportStateData;
50
- renderData = {
51
- variantName,
52
- reportName,
53
- description,
54
- rows,
55
- head,
56
- columns,
57
- pagination,
58
- actions,
59
- appliedFilters: query.summary,
60
- classification,
61
- };
62
- });
63
- }
64
- if (Object.keys(renderData).length && Object.keys(reportStateData).length) {
65
- await asyncReportsStore.updateLastViewed(reportStateData.executionId);
66
- await recentlyViewedStoreService.setRecentlyViewed(reportStateData);
67
- }
68
- return { renderData };
69
- };
70
- exports.renderReport = renderReport;
@@ -1,88 +1,36 @@
1
- /* eslint-disable no-underscore-dangle */
2
1
  import parseUrl from 'parseurl'
2
+ import { Request } from 'express'
3
3
  import DataTableUtils from '../data-table/utils'
4
4
  import ColumnUtils from '../async-columns/utils'
5
5
  import PaginationUtils from '../pagination/utils'
6
- import ReportActionsUtils from '../icon-button-list/utils'
7
6
  import { components } from '../../types/api'
8
7
  import Dict = NodeJS.Dict
9
-
10
- import { AsyncReportUtilsParams } from '../../types/AsyncReportUtils'
11
8
  import { AsyncReportData } from '../../types/AsyncReport'
12
9
 
13
- export const initDataSources = ({ req, res, next, asyncReportsStore, dataSources }: AsyncReportUtilsParams) => {
14
- const token = res.locals.user?.token ? res.locals.user.token : 'token'
15
- const { reportId, variantId: reportVariantId, tableId } = req.params
16
- const { selectedPage = 1, pageSize = 10 } = req.query
17
- const dataProductDefinitionsPath = <string>req.query.dataProductDefinitionsPath
18
- const reportDefinitionPromise = dataSources.getDefinition(
19
- token,
20
- reportId,
21
- reportVariantId,
22
- dataProductDefinitionsPath,
23
- )
24
- const reportDataPromise = dataSources.getAsyncReport(token, reportId, reportVariantId, tableId, {
25
- selectedPage: +selectedPage,
26
- pageSize: +pageSize,
27
- dataProductDefinitionsPath,
28
- })
29
- const reportDataCountPromise = dataSources.getAsyncCount(token, tableId)
30
- const stateDataPromise = asyncReportsStore.getReportByTableId(tableId)
31
-
32
- return [reportDefinitionPromise, reportDataPromise, reportDataCountPromise, stateDataPromise]
33
- }
34
-
35
- export const renderReport = async ({
36
- req,
37
- res,
38
- next,
39
- asyncReportsStore,
40
- recentlyViewedStoreService,
41
- dataSources,
42
- }: AsyncReportUtilsParams) => {
43
- const { columns: reqColumns } = req.query
44
-
45
- const dataPromises = initDataSources({ req, res, next, dataSources, asyncReportsStore })
46
-
47
- let renderData = {}
48
- let reportStateData: AsyncReportData
49
- if (dataPromises) {
50
- await Promise.all(dataPromises).then((resolvedData) => {
51
- const definition = resolvedData[0] as unknown as components['schemas']['SingleVariantReportDefinition']
52
- const reportData = <Array<Dict<string>>>resolvedData[1]
53
- const count = <number>resolvedData[2]
54
- reportStateData = <AsyncReportData>resolvedData[3]
55
-
56
- const fieldDefinition = definition.variant.specification.fields
57
- const { classification } = definition.variant
58
-
59
- const columns = ColumnUtils.getColumns(fieldDefinition, <string[]>reqColumns)
60
- const url = parseUrl(req)
61
- const pagination = PaginationUtils.getPaginationData(url, count)
62
- const actions = ReportActionsUtils.initReportActions(definition.variant, reportStateData)
63
- const rows = DataTableUtils.mapData(reportData, fieldDefinition, columns.value)
64
- const head = DataTableUtils.mapAsyncHeader(fieldDefinition, columns.value)
65
- const { reportName, name: variantName, query, description } = reportStateData
66
-
67
- renderData = {
68
- variantName,
69
- reportName,
70
- description,
71
- rows,
72
- head,
73
- columns,
74
- pagination,
75
- actions,
76
- appliedFilters: query.summary,
77
- classification,
78
- }
79
- })
80
- }
81
-
82
- if (Object.keys(renderData).length && Object.keys(reportStateData).length) {
83
- await asyncReportsStore.updateLastViewed(reportStateData.executionId)
84
- await recentlyViewedStoreService.setRecentlyViewed(reportStateData)
85
- }
86
-
87
- return { renderData }
10
+ export default {
11
+ getRenderData: (
12
+ req: Request,
13
+ definition: components['schemas']['SingleVariantReportDefinition'],
14
+ reportData: Array<Dict<string>>,
15
+ count: number,
16
+ reportStateData: AsyncReportData,
17
+ ) => {
18
+ const { columns: reqColumns } = req.query
19
+ const { fields } = definition.variant.specification
20
+
21
+ const columns = ColumnUtils.getColumns(fields, <string[]>reqColumns)
22
+ const url = parseUrl(req)
23
+ const pagination = PaginationUtils.getPaginationData(url, count)
24
+ const rows = DataTableUtils.mapData(reportData, fields, columns.value)
25
+ const head = DataTableUtils.mapAsyncHeader(fields, columns.value)
26
+ const { query } = reportStateData
27
+
28
+ return {
29
+ rows,
30
+ head,
31
+ columns,
32
+ pagination,
33
+ appliedFilters: query.summary,
34
+ }
35
+ },
88
36
  }
@@ -1,46 +1,30 @@
1
- {% from "../data-table/view.njk" import dprDataTable %}
2
1
  {% from "../async-columns/view.njk" import dprAsyncColumns %}
3
2
  {% from "../pagination/view.njk" import dprPagination %}
4
3
  {% from "../async-data-table/view.njk" import dprAsyncDataTable %}
5
4
  {% from "../async-applied-filters/view.njk" import dprAsyncAppliedFiltersList %}
6
- {% from "../icon-button-list/view.njk" import dprIconButtonList %}
7
5
  {% from "govuk/components/details/macro.njk" import govukDetails %}
8
6
 
9
7
  {% macro dprAsyncReportList(options) %}
10
8
 
11
9
  {% set columns = options.columns %}
12
- {% set appliedFilters = options.appliedFilters %}
13
10
  {% set pagination = options.pagination %}
14
- {% set reportName = options.reportName %}
15
- {% set variantName = options.variantName %}
16
- {% set description = options.description %}
17
- {% set actions = options.actions %}
18
-
19
- <div class="govuk-width-container report-list-container">
20
- <div class="drp-async-reports-heading-container">
21
- <h1 class="govuk-heading-l govuk-!-margin-bottom-2">{{ reportName }} - {{ variantName }}</h1>
22
- <p class="govuk-body-s govuk-!-margin-bottom-0">
23
- <i>{{ description }}</i>
24
- </p>
25
- {{ dprIconButtonList(actions) }}
26
- </div>
11
+ {% set appliedFilters = options.appliedFilters %}
27
12
 
28
- {{ dprAsyncAppliedFiltersList(appliedFilters) }}
13
+ {{ dprAsyncAppliedFiltersList(appliedFilters) }}
29
14
 
30
- <div class="columns-select-wrapper">
31
- {% set columnsHtml %}
32
- {{ dprAsyncColumns(columns) }}
33
- {% endset %}
15
+ <div class="columns-select-wrapper">
16
+ {% set columnsHtml %}
17
+ {{ dprAsyncColumns(columns) }}
18
+ {% endset %}
34
19
 
35
- {{ govukDetails({
20
+ {{ govukDetails({
36
21
  summaryText: "Columns",
37
22
  html: columnsHtml
38
23
  }) }}
39
- </div>
24
+ </div>
40
25
 
41
- <div class='dpr-table-container'>
42
- {{ dprAsyncDataTable(options) }}
43
- {{ dprPagination(pagination) }}
44
- </div>
26
+ <div class='dpr-table-container'>
27
+ {{ dprAsyncDataTable(options) }}
28
+ {{ dprPagination(pagination) }}
45
29
  </div>
46
30
  {% endmacro %}
@@ -9,7 +9,7 @@ const formatCards = async (recentlyViewedStoreService, asyncReportsStore, dataSo
9
9
  const requestedReportsData = await recentlyViewedStoreService.getAllReports();
10
10
  return Promise.all(requestedReportsData
11
11
  .filter((report) => {
12
- return !report.timestamp.retried;
12
+ return !report.timestamp.retried && !report.timestamp.refresh;
13
13
  })
14
14
  .map((report) => {
15
15
  return formatCardData(report, dataSources, token, recentlyViewedStoreService, asyncReportsStore);
@@ -19,15 +19,21 @@ const formatCardData = async (reportData, dataSources, token, recentlyViewedStor
19
19
  const { executionId: id, variantName: text, description, query, url, timestamp, executionId, reportId, variantId, dataProductDefinitionsPath, } = reportData;
20
20
  let { status } = reportData;
21
21
  const statusResponse = await utils_1.default.getStatus(token, reportId, variantId, executionId, status, dataSources, asyncReportsStore, dataProductDefinitionsPath);
22
- status = statusResponse.status === AsyncReport_1.RequestStatus.EXPIRED ? statusResponse.status : AsyncReport_1.RequestStatus.READY;
23
- const summary = query.summary;
24
- const href = status ? `${url.request.fullUrl}&retryId=${executionId}` : url.report.fullUrl;
22
+ let href;
23
+ if (statusResponse.status === AsyncReport_1.RequestStatus.EXPIRED) {
24
+ status = statusResponse.status;
25
+ href = `${url.request.fullUrl}&retryId=${executionId}`;
26
+ }
27
+ else {
28
+ status = AsyncReport_1.RequestStatus.READY;
29
+ href = url.report.fullUrl;
30
+ }
25
31
  return {
26
32
  id,
27
33
  text,
28
34
  description,
29
35
  tag: 'MIS',
30
- summary,
36
+ summary: query.summary,
31
37
  href,
32
38
  timestamp: `Last viewed: ${new Date(timestamp.lastViewed).toLocaleString()}`,
33
39
  status,
@@ -17,7 +17,7 @@ const formatCards = async (
17
17
  return Promise.all(
18
18
  requestedReportsData
19
19
  .filter((report: RecentlyViewedReportData) => {
20
- return !report.timestamp.retried
20
+ return !report.timestamp.retried && !report.timestamp.refresh
21
21
  })
22
22
  .map((report: RecentlyViewedReportData) => {
23
23
  return formatCardData(report, dataSources, token, recentlyViewedStoreService, asyncReportsStore)
@@ -57,16 +57,21 @@ const formatCardData = async (
57
57
  dataProductDefinitionsPath,
58
58
  )
59
59
 
60
- status = statusResponse.status === RequestStatus.EXPIRED ? statusResponse.status : RequestStatus.READY
61
- const summary = query.summary as { name: string; value: string }[]
62
- const href = status ? `${url.request.fullUrl}&retryId=${executionId}` : url.report.fullUrl
60
+ let href
61
+ if (statusResponse.status === RequestStatus.EXPIRED) {
62
+ status = statusResponse.status
63
+ href = `${url.request.fullUrl}&retryId=${executionId}`
64
+ } else {
65
+ status = RequestStatus.READY
66
+ href = url.report.fullUrl
67
+ }
63
68
 
64
69
  return {
65
70
  id,
66
71
  text,
67
72
  description,
68
73
  tag: 'MIS',
69
- summary,
74
+ summary: query.summary as { name: string; value: string }[],
70
75
  href,
71
76
  timestamp: `Last viewed: ${new Date(timestamp.lastViewed).toLocaleString()}`,
72
77
  status,
@@ -6,7 +6,7 @@ const FULL_BUTTON_LIST = [
6
6
  icon: 'refresh',
7
7
  disabled: false,
8
8
  tooltipText: 'Refresh report',
9
- ariaLabelText: 'Print report',
9
+ ariaLabelText: 'Refresh report',
10
10
  },
11
11
  {
12
12
  id: 'printable',
@@ -59,7 +59,7 @@ exports.default = {
59
59
  actions.push({
60
60
  type: 'refresh',
61
61
  data: {
62
- href: reportData.url.request.fullUrl,
62
+ href: `${reportData.url.request.fullUrl}&refreshId=${reportData.executionId}`,
63
63
  },
64
64
  });
65
65
  // PRINT
@@ -8,7 +8,7 @@ const FULL_BUTTON_LIST = [
8
8
  icon: 'refresh',
9
9
  disabled: false,
10
10
  tooltipText: 'Refresh report',
11
- ariaLabelText: 'Print report',
11
+ ariaLabelText: 'Refresh report',
12
12
  },
13
13
  {
14
14
  id: 'printable',
@@ -64,7 +64,7 @@ export default {
64
64
  actions.push({
65
65
  type: 'refresh',
66
66
  data: {
67
- href: reportData.url.request.fullUrl,
67
+ href: `${reportData.url.request.fullUrl}&refreshId=${reportData.executionId}`,
68
68
  },
69
69
  })
70
70
 
@@ -27,8 +27,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  const utils_1 = __importDefault(require("../components/async-filters/utils"));
30
- const AsyncReportListUtils = __importStar(require("../components/async-report-list/utils"));
31
30
  const utils_2 = __importDefault(require("../components/async-polling/utils"));
31
+ const AsyncReportUtils = __importStar(require("../utils/renderAsyncReport"));
32
32
  const asyncReportsUtils_1 = __importDefault(require("../components/async-reports-list/utils/asyncReportsUtils"));
33
33
  const recentlyViewedUtils_1 = __importDefault(require("../components/async-reports-list/utils/recentlyViewedUtils"));
34
34
  function routes({ router, asyncReportsStore, recentlyViewedStoreService, dataSources, layoutPath, templatePath = 'dpr/views/', }) {
@@ -120,9 +120,9 @@ function routes({ router, asyncReportsStore, recentlyViewedStoreService, dataSou
120
120
  next();
121
121
  }
122
122
  };
123
- const getReportListHandler = async (req, res, next) => {
123
+ const getReportHandler = async (req, res, next) => {
124
124
  try {
125
- const reportRenderData = await AsyncReportListUtils.renderReport({
125
+ const reportRenderData = await AsyncReportUtils.getReport({
126
126
  req,
127
127
  res,
128
128
  dataSources,
@@ -145,7 +145,7 @@ function routes({ router, asyncReportsStore, recentlyViewedStoreService, dataSou
145
145
  router.post('/requestReport/', asyncRequestHandler, asyncErrorHandler);
146
146
  router.post('/cancelRequest/', cancelRequestHandler, asyncErrorHandler);
147
147
  router.get('/async-reports/:reportId/:variantId/request/:executionId', pollingHandler, asyncErrorHandler);
148
- router.get('/async-reports/:reportId/:variantId/request/:tableId/report', getReportListHandler, asyncErrorHandler);
148
+ router.get('/async-reports/:reportId/:variantId/request/:tableId/report', getReportHandler, asyncErrorHandler);
149
149
  router.get('/async-reports/requested', async (req, res) => {
150
150
  res.render(`${templatePath}/async-reports`, {
151
151
  title: 'Requested Reports',
@@ -1,10 +1,10 @@
1
1
  import type { RequestHandler, Router } from 'express'
2
2
  import AsyncFiltersUtils from '../components/async-filters/utils'
3
- import * as AsyncReportListUtils from '../components/async-report-list/utils'
4
3
  import AsyncPollingUtils from '../components/async-polling/utils'
5
4
  import AsyncReportStoreService from '../services/requestedReportsService'
6
5
  import ReportingService from '../services/reportingService'
7
6
  import RecentlyViewedStoreService from '../services/recentlyViewedService'
7
+ import * as AsyncReportUtils from '../utils/renderAsyncReport'
8
8
 
9
9
  import AsyncReportslistUtils from '../components/async-reports-list/utils/asyncReportsUtils'
10
10
  import RecentReportslistUtils from '../components/async-reports-list/utils/recentlyViewedUtils'
@@ -112,9 +112,9 @@ export default function routes({
112
112
  }
113
113
  }
114
114
 
115
- const getReportListHandler: RequestHandler = async (req, res, next) => {
115
+ const getReportHandler: RequestHandler = async (req, res, next) => {
116
116
  try {
117
- const reportRenderData = await AsyncReportListUtils.renderReport({
117
+ const reportRenderData = await AsyncReportUtils.getReport({
118
118
  req,
119
119
  res,
120
120
  dataSources,
@@ -137,7 +137,7 @@ export default function routes({
137
137
  router.post('/requestReport/', asyncRequestHandler, asyncErrorHandler)
138
138
  router.post('/cancelRequest/', cancelRequestHandler, asyncErrorHandler)
139
139
  router.get('/async-reports/:reportId/:variantId/request/:executionId', pollingHandler, asyncErrorHandler)
140
- router.get('/async-reports/:reportId/:variantId/request/:tableId/report', getReportListHandler, asyncErrorHandler)
140
+ router.get('/async-reports/:reportId/:variantId/request/:tableId/report', getReportHandler, asyncErrorHandler)
141
141
 
142
142
  router.get('/async-reports/requested', async (req, res) => {
143
143
  res.render(`${templatePath}/async-reports`, {
@@ -82,14 +82,15 @@ class RecentlyViewedStoreService extends userStoreService_1.default {
82
82
  this.recentlyViewedReports[index] = report;
83
83
  await this.saveRecentlyViewedState();
84
84
  }
85
- async setReportRetriedTimestamp(id) {
86
- const retriedReport = await this.getReportByExecutionId(id);
87
- if (retriedReport) {
85
+ async setReportTimestamp(executionId, type) {
86
+ const report = await this.getReportByExecutionId(executionId);
87
+ if (report) {
88
88
  const timestamp = {
89
- ...retriedReport.timestamp,
90
- retried: new Date(),
89
+ ...report.timestamp,
90
+ ...(type === 'retry' && { retried: new Date() }),
91
+ ...(type === 'refresh' && { refresh: new Date() }),
91
92
  };
92
- await this.updateReport(id, { timestamp });
93
+ await this.updateReport(executionId, { timestamp });
93
94
  }
94
95
  }
95
96
  async setToExpired(id) {
@@ -106,14 +106,15 @@ export default class RecentlyViewedStoreService extends UserStoreService {
106
106
  await this.saveRecentlyViewedState()
107
107
  }
108
108
 
109
- async setReportRetriedTimestamp(id: string) {
110
- const retriedReport = await this.getReportByExecutionId(id)
111
- if (retriedReport) {
109
+ async setReportTimestamp(executionId: string, type: string) {
110
+ const report = await this.getReportByExecutionId(executionId)
111
+ if (report) {
112
112
  const timestamp: AsyncReportsTimestamp = {
113
- ...retriedReport.timestamp,
114
- retried: new Date(),
113
+ ...report.timestamp,
114
+ ...(type === 'retry' && { retried: new Date() }),
115
+ ...(type === 'refresh' && { refresh: new Date() }),
115
116
  }
116
- await this.updateReport(id, { timestamp })
117
+ await this.updateReport(executionId, { timestamp })
117
118
  }
118
119
  }
119
120
 
@@ -97,14 +97,15 @@ class AsyncReportStoreService extends userStoreService_1.default {
97
97
  this.requestedReports[index] = report;
98
98
  await this.saveRequestedReportState();
99
99
  }
100
- async setReportRetriedTimestamp(id) {
101
- const retriedReport = await this.getReportByExecutionId(id);
102
- if (retriedReport) {
100
+ async setReportTimestamp(executionId, type) {
101
+ const report = await this.getReportByExecutionId(executionId);
102
+ if (report) {
103
103
  const timestamp = {
104
- ...retriedReport.timestamp,
105
- retried: new Date(),
104
+ ...report.timestamp,
105
+ ...(type === 'retry' && { retried: new Date() }),
106
+ ...(type === 'refresh' && { refresh: new Date() }),
106
107
  };
107
- await this.updateReport(id, { timestamp });
108
+ await this.updateReport(executionId, { timestamp });
108
109
  }
109
110
  }
110
111
  async updateLastViewed(id) {
@@ -129,14 +129,15 @@ export default class AsyncReportStoreService extends UserStoreService {
129
129
  await this.saveRequestedReportState()
130
130
  }
131
131
 
132
- async setReportRetriedTimestamp(id: string) {
133
- const retriedReport = await this.getReportByExecutionId(id)
134
- if (retriedReport) {
132
+ async setReportTimestamp(executionId: string, type: string) {
133
+ const report = await this.getReportByExecutionId(executionId)
134
+ if (report) {
135
135
  const timestamp: AsyncReportsTimestamp = {
136
- ...retriedReport.timestamp,
137
- retried: new Date(),
136
+ ...report.timestamp,
137
+ ...(type === 'retry' && { retried: new Date() }),
138
+ ...(type === 'refresh' && { refresh: new Date() }),
138
139
  }
139
- await this.updateReport(id, { timestamp })
140
+ await this.updateReport(executionId, { timestamp })
140
141
  }
141
142
  }
142
143
 
@@ -62,4 +62,5 @@ export interface AsyncReportsTimestamp {
62
62
  failed?: Date
63
63
  retried?: Date
64
64
  aborted?: Date
65
+ refesh?: Date
65
66
  }
@@ -12,6 +12,7 @@ export interface RecentlyViewedReportData {
12
12
  lastViewed: Date
13
13
  expired?: Date
14
14
  retried?: Date
15
+ refresh?: Date
15
16
  }
16
17
  status?: RequestStatus
17
18
  url: AsyncReportUrlData
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getReport = exports.initDataSources = void 0;
7
+ const utils_1 = __importDefault(require("../components/async-report-list/utils"));
8
+ const utils_2 = __importDefault(require("../components/icon-button-list/utils"));
9
+ const initDataSources = ({ req, res, next, asyncReportsStore, dataSources }) => {
10
+ var _a;
11
+ const token = ((_a = res.locals.user) === null || _a === void 0 ? void 0 : _a.token) ? res.locals.user.token : 'token';
12
+ const { reportId, variantId: reportVariantId, tableId } = req.params;
13
+ const { selectedPage = 1, pageSize = 10 } = req.query;
14
+ const dataProductDefinitionsPath = req.query.dataProductDefinitionsPath;
15
+ const reportDefinitionPromise = dataSources.getDefinition(token, reportId, reportVariantId, dataProductDefinitionsPath);
16
+ const reportDataPromise = dataSources.getAsyncReport(token, reportId, reportVariantId, tableId, {
17
+ selectedPage: +selectedPage,
18
+ pageSize: +pageSize,
19
+ dataProductDefinitionsPath,
20
+ });
21
+ const reportDataCountPromise = dataSources.getAsyncCount(token, tableId);
22
+ const stateDataPromise = asyncReportsStore.getReportByTableId(tableId);
23
+ return [reportDefinitionPromise, reportDataPromise, reportDataCountPromise, stateDataPromise];
24
+ };
25
+ exports.initDataSources = initDataSources;
26
+ const getReport = async ({ req, res, next, asyncReportsStore, recentlyViewedStoreService, dataSources, }) => {
27
+ const dataPromises = (0, exports.initDataSources)({ req, res, next, dataSources, asyncReportsStore });
28
+ let renderData = {};
29
+ let reportStateData;
30
+ if (dataPromises) {
31
+ await Promise.all(dataPromises).then((resolvedData) => {
32
+ const definition = resolvedData[0];
33
+ const reportData = resolvedData[1];
34
+ const count = resolvedData[2];
35
+ reportStateData = resolvedData[3];
36
+ const { classification } = definition.variant;
37
+ const { template } = definition.variant.specification;
38
+ const { reportName, name: variantName, description, timestamp } = reportStateData;
39
+ const actions = utils_2.default.initReportActions(definition.variant, reportStateData);
40
+ renderData = {
41
+ variantName,
42
+ reportName,
43
+ description,
44
+ classification,
45
+ template,
46
+ actions,
47
+ requestedTimestamp: new Date(timestamp.requested).toLocaleString(),
48
+ };
49
+ switch (template) {
50
+ case 'list':
51
+ renderData = {
52
+ ...renderData,
53
+ ...utils_1.default.getRenderData(req, definition, reportData, count, reportStateData),
54
+ };
55
+ break;
56
+ case 'listWithSections':
57
+ // TODO: add list eith sections utils here
58
+ break;
59
+ default:
60
+ renderData = {
61
+ ...renderData,
62
+ ...utils_1.default.getRenderData(req, definition, reportData, count, reportStateData),
63
+ };
64
+ break;
65
+ }
66
+ });
67
+ }
68
+ if (Object.keys(renderData).length && Object.keys(reportStateData).length) {
69
+ await asyncReportsStore.updateLastViewed(reportStateData.executionId);
70
+ await recentlyViewedStoreService.setRecentlyViewed(reportStateData);
71
+ }
72
+ return { renderData };
73
+ };
74
+ exports.getReport = getReport;
@@ -0,0 +1,90 @@
1
+ import { components } from '../types/api'
2
+ import Dict = NodeJS.Dict
3
+ import { AsyncReportUtilsParams } from '../types/AsyncReportUtils'
4
+ import { AsyncReportData } from '../types/AsyncReport'
5
+ import AsyncReportListUtils from '../components/async-report-list/utils'
6
+ import ReportActionsUtils from '../components/icon-button-list/utils'
7
+
8
+ export const initDataSources = ({ req, res, next, asyncReportsStore, dataSources }: AsyncReportUtilsParams) => {
9
+ const token = res.locals.user?.token ? res.locals.user.token : 'token'
10
+ const { reportId, variantId: reportVariantId, tableId } = req.params
11
+ const { selectedPage = 1, pageSize = 10 } = req.query
12
+ const dataProductDefinitionsPath = <string>req.query.dataProductDefinitionsPath
13
+ const reportDefinitionPromise = dataSources.getDefinition(
14
+ token,
15
+ reportId,
16
+ reportVariantId,
17
+ dataProductDefinitionsPath,
18
+ )
19
+ const reportDataPromise = dataSources.getAsyncReport(token, reportId, reportVariantId, tableId, {
20
+ selectedPage: +selectedPage,
21
+ pageSize: +pageSize,
22
+ dataProductDefinitionsPath,
23
+ })
24
+ const reportDataCountPromise = dataSources.getAsyncCount(token, tableId)
25
+ const stateDataPromise = asyncReportsStore.getReportByTableId(tableId)
26
+
27
+ return [reportDefinitionPromise, reportDataPromise, reportDataCountPromise, stateDataPromise]
28
+ }
29
+
30
+ export const getReport = async ({
31
+ req,
32
+ res,
33
+ next,
34
+ asyncReportsStore,
35
+ recentlyViewedStoreService,
36
+ dataSources,
37
+ }: AsyncReportUtilsParams) => {
38
+ const dataPromises = initDataSources({ req, res, next, dataSources, asyncReportsStore })
39
+
40
+ let renderData = {}
41
+ let reportStateData: AsyncReportData
42
+ if (dataPromises) {
43
+ await Promise.all(dataPromises).then((resolvedData) => {
44
+ const definition = resolvedData[0] as unknown as components['schemas']['SingleVariantReportDefinition']
45
+ const reportData = <Array<Dict<string>>>resolvedData[1]
46
+ const count = <number>resolvedData[2]
47
+ reportStateData = <AsyncReportData>resolvedData[3]
48
+
49
+ const { classification } = definition.variant
50
+ const { template } = definition.variant.specification
51
+ const { reportName, name: variantName, description, timestamp } = reportStateData
52
+ const actions = ReportActionsUtils.initReportActions(definition.variant, reportStateData)
53
+
54
+ renderData = {
55
+ variantName,
56
+ reportName,
57
+ description,
58
+ classification,
59
+ template,
60
+ actions,
61
+ requestedTimestamp: new Date(timestamp.requested).toLocaleString(),
62
+ }
63
+
64
+ switch (template) {
65
+ case 'list':
66
+ renderData = {
67
+ ...renderData,
68
+ ...AsyncReportListUtils.getRenderData(req, definition, reportData, count, reportStateData),
69
+ }
70
+ break
71
+ case 'listWithSections':
72
+ // TODO: add list eith sections utils here
73
+ break
74
+ default:
75
+ renderData = {
76
+ ...renderData,
77
+ ...AsyncReportListUtils.getRenderData(req, definition, reportData, count, reportStateData),
78
+ }
79
+ break
80
+ }
81
+ })
82
+ }
83
+
84
+ if (Object.keys(renderData).length && Object.keys(reportStateData).length) {
85
+ await asyncReportsStore.updateLastViewed(reportStateData.executionId)
86
+ await recentlyViewedStoreService.setRecentlyViewed(reportStateData)
87
+ }
88
+
89
+ return { renderData }
90
+ }
@@ -1,10 +1,46 @@
1
1
  {% extends layoutPath %}
2
2
  {% from "../components/async-report-list/view.njk" import dprAsyncReportList %}
3
+ {% from "../components/icon-button-list/view.njk" import dprIconButtonList %}
3
4
 
4
5
  {% set mainClasses = "app-container govuk-body" %}
5
6
 
6
7
  {% block content %}
8
+ {% set template = renderData.template %}
9
+ {% set reportName = renderData.reportName %}
10
+ {% set variantName = renderData.variantName %}
11
+ {% set description = renderData.description %}
12
+ {% set actions = renderData.actions %}
13
+ {% set requestedTimestamp = renderData.requestedTimestamp %}
14
+
7
15
  <div class="govuk-width-container">
8
- {{ dprAsyncReportList(renderData) }}
16
+ <div class="govuk-width-container report-list-container">
17
+
18
+ <div class="drp-async-reports-heading-container">
19
+ <h1 class="govuk-heading-l govuk-!-margin-bottom-2">{{ reportName }} - {{ variantName }}</h1>
20
+ <p class="govuk-body-s govuk-!-margin-bottom-2">
21
+ <i>{{ description }}</i>
22
+ </p>
23
+ <p class="govuk-body-s govuk-!-margin-bottom-0">
24
+ <strong>Requested at:</strong>
25
+ {{ requestedTimestamp }}
26
+ </p>
27
+
28
+ {{ dprIconButtonList(actions) }}
29
+ </div>
30
+
31
+ {% if template === 'list' %}
32
+
33
+ {{ dprAsyncReportList(renderData) }}
34
+
35
+ {% elif(template === 'listWithSection') %}
36
+
37
+ {# TODO: Add template view here #}
38
+
39
+ {% else %}
40
+
41
+ {{ dprAsyncReportList(renderData) }}
42
+
43
+ {% endif %}
44
+ </div>
9
45
  </div>
10
46
  {% endblock %}
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.12.2",
4
+ "version": "3.13.0",
5
5
  "main": "dpr/assets/js/all.mjs",
6
6
  "sass": "dpr/all.scss",
7
7
  "engines": {
package/package.zip CHANGED
Binary file