@ampath/esm-reports-app 1.0.0-next.41 → 1.0.0-next.45
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/143.css +1 -0
- package/dist/143.css.map +1 -0
- package/dist/143.js +1 -0
- package/dist/143.js.map +1 -0
- package/dist/197.js +1 -1
- package/dist/282.js +1 -1
- package/dist/282.js.map +1 -1
- package/dist/295.css +1 -0
- package/dist/295.css.map +1 -0
- package/dist/295.js +1 -0
- package/dist/295.js.map +1 -0
- package/dist/300.js +1 -1
- package/dist/311.js +1 -1
- package/dist/311.js.map +1 -1
- package/dist/335.js +1 -1
- package/dist/353.js +1 -1
- package/dist/353.js.map +1 -1
- package/dist/410.js +1 -0
- package/dist/410.js.map +1 -0
- package/dist/427.js +2 -0
- package/dist/427.js.LICENSE.txt +35 -0
- package/dist/427.js.map +1 -0
- package/dist/441.js +1 -0
- package/dist/441.js.map +1 -0
- package/dist/478.js +1 -1
- package/dist/478.js.map +1 -1
- package/dist/540.js +1 -1
- package/dist/540.js.map +1 -1
- package/dist/55.js +1 -1
- package/dist/652.js +1 -1
- package/dist/961.js +1 -1
- package/dist/961.js.map +1 -1
- package/dist/99.js +1 -1
- package/dist/ampath-esm-reports-app.js +1 -1
- package/dist/ampath-esm-reports-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +2 -2
- package/src/common/report-error/index.ts +1 -0
- package/src/common/report-error/show-report-error.ts +25 -0
- package/src/common/report-filters/date-range.ts +137 -0
- package/src/common/report-filters/index.ts +4 -0
- package/src/common/report-filters/report-filters.component.tsx +281 -151
- package/src/common/report-filters/report-filters.scss +180 -55
- package/src/common/report-filters/types.ts +66 -0
- package/src/common/report-filters/use-restore-period.ts +43 -0
- package/src/common/report-page/index.ts +2 -0
- package/src/common/report-page/report-page.component.tsx +32 -0
- package/src/common/report-page/report-page.scss +45 -0
- package/src/common/report-page/use-page-scale.ts +50 -0
- package/src/common/report-placeholder/has-report-data.ts +15 -0
- package/src/common/report-placeholder/index.ts +2 -0
- package/src/common/report-placeholder/report-placeholder.component.tsx +56 -0
- package/src/common/report-placeholder/report-placeholder.scss +46 -0
- package/src/common/report-register/index.ts +5 -0
- package/src/common/report-register/register-columns.ts +34 -0
- package/src/common/report-register/register-layout.component.tsx +237 -0
- package/src/common/report-register/register-layout.scss +140 -0
- package/src/common/report-register/register-table.scss +62 -0
- package/src/common/report-register/report-parents.ts +27 -0
- package/src/common/report-register/rows-to-csv.ts +59 -0
- package/src/common/report-register/use-patient-list.ts +92 -0
- package/src/common/report-skeleton/index.ts +1 -0
- package/src/common/report-skeleton/report-skeleton.component.tsx +46 -0
- package/src/common/report-skeleton/report-skeleton.scss +29 -0
- package/src/config-schema.ts +9 -1
- package/src/dashboard/report-catalog.ts +173 -0
- package/src/dashboard/reports-dasboard.tsx +101 -35
- package/src/dashboard/reports-dashboard.module.scss +269 -11
- package/src/index.ts +13 -1
- package/src/reports/datatable-wrapper/datatable-wrapper.component.tsx +50 -48
- package/src/reports/moh-240/moh-240-header.component.tsx +54 -56
- package/src/reports/moh-240/moh-240.columns.ts +64 -0
- package/src/reports/moh-240/moh-240.component.tsx +65 -84
- package/src/reports/moh-240/moh-240.scss +36 -7
- package/src/reports/moh-240/sub-reports/moh-240-register.component.tsx +27 -65
- package/src/reports/moh-240/sub-reports/page-summary.component.tsx +2 -2
- package/src/reports/moh-505/moh-505-header.component.tsx +59 -57
- package/src/reports/moh-505/moh-505.component.tsx +51 -56
- package/src/reports/moh-505/moh-505.scss +116 -2
- package/src/reports/moh-705B/moh-204b-register.component.tsx +41 -42
- package/src/reports/moh-705B/moh-204b.columns.ts +39 -0
- package/src/reports/moh-705B/moh-705b.component.tsx +492 -473
- package/src/reports/moh-705B/moh-705b.scss +110 -24
- package/src/reports/moh-705a/moh-705a.component.tsx +496 -476
- package/src/reports/moh-705a/moh-705a.scss +109 -24
- package/src/reports/moh-705a/registers/moh-204a-register.component.tsx +41 -42
- package/src/reports/moh-705a/registers/moh-204a.columns.ts +57 -0
- package/src/reports/moh-706/moh-706-header.component.tsx +58 -56
- package/src/reports/moh-706/moh-706.component.tsx +84 -55
- package/src/reports/moh-706/moh-706.scss +115 -19
- package/src/reports/moh-710/moh-710.component.tsx +743 -727
- package/src/reports/moh-710/moh-710.scss +140 -23
- package/src/reports/moh-710/registers/moh-511-710-register.component.tsx +48 -43
- package/src/reports/moh-710/registers/moh-511-710.columns.ts +52 -0
- package/src/reports/moh-711/moh-711.component.tsx +94 -106
- package/src/reports/moh-711/moh711.scss +95 -64
- package/src/reports/moh-711/registers/moh-333-register.component.tsx +92 -94
- package/src/reports/moh-711/registers/moh-333.columns.ts +119 -0
- package/src/reports/moh-711/registers/moh-405-register.component.tsx +88 -89
- package/src/reports/moh-711/registers/moh-405.columns.ts +120 -0
- package/src/reports/moh-711/registers/moh-406-register.component.tsx +85 -88
- package/src/reports/moh-711/registers/moh-406.columns.ts +133 -0
- package/src/reports/moh-711/registers/moh-510-register.component.tsx +38 -44
- package/src/reports/moh-711/registers/moh-511-register.component.tsx +42 -43
- package/src/reports/moh-711/registers/moh-511.columns.ts +59 -0
- package/src/reports/moh-711/sections/anc.component.tsx +3 -3
- package/src/reports/moh-711/sections/cervical-cancer.component.tsx +3 -3
- package/src/reports/moh-711/sections/chanis.component.tsx +2 -3
- package/src/reports/moh-711/sections/family-planning.component.tsx +2 -2
- package/src/reports/moh-711/sections/gbv.component.tsx +2 -2
- package/src/reports/moh-711/sections/maternity.component.tsx +3 -8
- package/src/reports/moh-711/sections/medical-social-work.component.tsx +2 -12
- package/src/reports/moh-711/sections/other.component.tsx +2 -2
- package/src/reports/moh-711/sections/physiotherapy.component.tsx +2 -2
- package/src/reports/moh-711/sections/pnc.component.tsx +7 -5
- package/src/reports/moh-711/sections/post-abortion.component.tsx +2 -2
- package/src/reports/moh-711/sections/rehabilitation.component.tsx +2 -2
- package/src/reports/moh-711/sections/report-compiled-by.component.tsx +12 -4
- package/src/reports/moh-711/sections/tb-screening.component.tsx +2 -2
- package/src/reports/moh-717/moh-717.component.tsx +100 -121
- package/src/reports/moh-717/moh717.scss +163 -102
- package/src/reports/moh-717/sections/maternity.component.tsx +1 -1
- package/src/reports/moh-717/sections/orthopaedic-trauma.component.tsx +1 -1
- package/src/reports/moh-717/sections/outpatient.component.tsx +23 -23
- package/src/reports/moh-717/sections/preparedby.component.tsx +7 -2
- package/src/reports/moh-717/sections/special-services.component.tsx +1 -1
- package/src/reports/moh-731/moh-731.component.tsx +127 -110
- package/src/reports/moh-731/moh-731.scss +396 -68
- package/src/reports/moh-731/registers/moh-366.columns.ts +38 -0
- package/src/reports/moh-731/registers/moh-366.component.tsx +76 -83
- package/src/reports/moh-731/registers/prep-register.component.tsx +70 -78
- package/src/reports/moh-731/registers/prep.columns.ts +34 -0
- package/src/reports/moh-731/sections/hiv-and-tb-treatment.component.tsx +1031 -751
- package/src/reports/moh-731/sections/hiv-testing-and-pre-exposure.component.tsx +230 -86
- package/src/reports/moh-731/sections/pmtct.component.tsx +185 -64
- package/src/reports/moh-740/moh-740.component.scss +159 -79
- package/src/reports/moh-740/moh-740.component.tsx +261 -230
- package/src/reports/moh-740/moh-740.resource.ts +17 -10
- package/src/reports/moh-740/registers/moh-222-daily-register/moh-222-daily-register.tsx +77 -96
- package/src/reports/moh-740/registers/moh-270-permanent-register/moh-270-permanent-register.tsx +50 -70
- package/src/reports/moh-740/registers/moh-740-patient-list.tsx +61 -32
- package/src/reports/moh-740/registers/moh-740.columns.ts +52 -0
- package/src/reports/moh-740/shared/utils/indicator-register-map.ts +3 -3
- package/src/reports/moh-740/types/index.ts +3 -3
- package/src/reports/moh-745/moh-745.component.tsx +897 -877
- package/src/reports/moh-745/moh-745.scss +104 -46
- package/src/reports/moh-745/registers/moh-412-register.component.tsx +42 -43
- package/src/reports/moh-745/registers/moh-412.columns.ts +52 -0
- package/src/reports/table-wrapper/table-wrapper.component.tsx +23 -14
- package/src/reports/table-wrapper/table-wrapper.scss +1 -16
- package/src/resources/moh-505.resource.ts +18 -35
- package/src/resources/moh-705.resource.ts +28 -39
- package/src/resources/moh-706.resource.ts +17 -30
- package/src/resources/moh-710.resource.ts +7 -17
- package/src/resources/moh-711.resource.ts +46 -64
- package/src/resources/moh-717.resource.ts +7 -17
- package/src/resources/moh-731.resource.ts +26 -32
- package/src/resources/moh-745.resource.ts +18 -20
- package/src/resources/patient-list-page.ts +66 -0
- package/src/root.component.tsx +28 -26
- package/src/root.scss +36 -0
- package/src/utils/get-base-url.ts +39 -7
- package/tsconfig.json +1 -1
- package/dist/110.js +0 -1
- package/dist/110.js.map +0 -1
- package/dist/184.js +0 -2
- package/dist/184.js.LICENSE.txt +0 -14
- package/dist/184.js.map +0 -1
- package/dist/205.js +0 -1
- package/dist/205.js.map +0 -1
- package/dist/812.js +0 -1
- package/dist/812.js.map +0 -1
- package/dist/822.js +0 -2
- package/dist/822.js.LICENSE.txt +0 -20
- package/dist/822.js.map +0 -1
- package/dist/ampath-esm-reports-app.js.buildmanifest.json +0 -560
- package/dist/main.js +0 -1
- package/dist/main.js.map +0 -1
- package/src/reports/moh-731/registers/moh-366.scss +0 -45
- package/src/reports/moh-731/registers/prep-registers.scss +0 -45
- package/src/reports/moh-740/registers/moh-222-daily-register/moh-222-daily-register.scss +0 -18
- package/src/reports/moh-740/registers/moh-270-permanent-register/moh-270-permanent-register.scss +0 -18
- /package/src/{reports/moh-740/shared/utils → utils}/format-indicator.ts +0 -0
|
@@ -1,185 +1,315 @@
|
|
|
1
|
-
import { useNavigate } from 'react-router-dom';
|
|
2
1
|
import React from 'react';
|
|
3
|
-
import {
|
|
2
|
+
import { Link, useSearchParams } from 'react-router-dom';
|
|
3
|
+
import {
|
|
4
|
+
Breadcrumb,
|
|
5
|
+
BreadcrumbItem,
|
|
6
|
+
Button,
|
|
7
|
+
ContentSwitcher,
|
|
8
|
+
DatePicker,
|
|
9
|
+
DatePickerInput,
|
|
10
|
+
Layer,
|
|
11
|
+
Select,
|
|
12
|
+
SelectItem,
|
|
13
|
+
Switch,
|
|
14
|
+
} from '@carbon/react';
|
|
15
|
+
import { Calendar, ChartColumn, Download, WarningAlt } from '@carbon/react/icons';
|
|
16
|
+
import classNames from 'classnames';
|
|
17
|
+
import dayjs from 'dayjs';
|
|
4
18
|
|
|
19
|
+
import {
|
|
20
|
+
DATE_FORMAT,
|
|
21
|
+
describePeriod,
|
|
22
|
+
firstError,
|
|
23
|
+
formatDate,
|
|
24
|
+
formatMonth,
|
|
25
|
+
monthToDateRange,
|
|
26
|
+
validatePeriod,
|
|
27
|
+
} from './date-range';
|
|
28
|
+
import { type ReportFiltersProps } from './types';
|
|
5
29
|
import styles from './report-filters.scss';
|
|
6
30
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
onGenerate?: (filters: { startDate?: string; endDate?: string; month?: string }) => void;
|
|
12
|
-
isLoding?: boolean;
|
|
13
|
-
}
|
|
31
|
+
const months = Array.from({ length: 12 }, (_, index) => ({
|
|
32
|
+
value: String(index + 1).padStart(2, '0'),
|
|
33
|
+
label: dayjs().month(index).format('MMMM'),
|
|
34
|
+
}));
|
|
14
35
|
|
|
15
|
-
const ReportFiltersComponent: React.FC<
|
|
36
|
+
const ReportFiltersComponent: React.FC<ReportFiltersProps> = ({
|
|
16
37
|
reportName,
|
|
17
|
-
mode = '
|
|
18
|
-
|
|
38
|
+
mode = 'both',
|
|
39
|
+
facilities,
|
|
40
|
+
yearsBack = 6,
|
|
41
|
+
isLoading = false,
|
|
42
|
+
isReportGenerated = false,
|
|
43
|
+
generatedPeriod,
|
|
19
44
|
onGenerate,
|
|
20
|
-
|
|
45
|
+
onPeriodChange,
|
|
21
46
|
}) => {
|
|
22
|
-
|
|
47
|
+
/* A register's breadcrumb links back here carrying the period the report was
|
|
48
|
+
generated for. Seeding the controls from it matters as much as restoring
|
|
49
|
+
the figures: left on their defaults the bar would compare last month
|
|
50
|
+
against the restored period and warn that figures are stale on arrival. */
|
|
51
|
+
const [params] = useSearchParams();
|
|
52
|
+
const urlStart = params.get('startDate');
|
|
53
|
+
const urlEnd = params.get('endDate');
|
|
54
|
+
const urlMonth = params.get('month');
|
|
23
55
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
56
|
+
/* A period covering exactly one calendar month came from the Monthly tab,
|
|
57
|
+
however it is spelled in the URL. */
|
|
58
|
+
const urlWholeMonth =
|
|
59
|
+
urlMonth ??
|
|
60
|
+
(urlStart &&
|
|
61
|
+
urlEnd &&
|
|
62
|
+
dayjs(urlStart).isSame(dayjs(urlStart).startOf('month'), 'day') &&
|
|
63
|
+
dayjs(urlEnd).isSame(dayjs(urlStart).endOf('month'), 'day')
|
|
64
|
+
? dayjs(urlStart).format('YYYY-MM')
|
|
65
|
+
: null);
|
|
27
66
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
67
|
+
const [activeTab, setActiveTab] = React.useState<'monthly' | 'custom'>(
|
|
68
|
+
urlStart && urlEnd && !urlWholeMonth ? 'custom' : 'monthly',
|
|
69
|
+
);
|
|
31
70
|
|
|
32
|
-
|
|
33
|
-
|
|
71
|
+
const [startDateString, setStartDateString] = React.useState<string>(() => urlStart ?? formatDate(new Date()));
|
|
72
|
+
const [endDateString, setEndDateString] = React.useState<string>(() => urlEnd ?? formatDate(new Date()));
|
|
73
|
+
const [monthString, setMonthString] = React.useState<string>(
|
|
74
|
+
() => urlWholeMonth ?? formatMonth(dayjs().subtract(1, 'month')),
|
|
75
|
+
);
|
|
34
76
|
|
|
35
|
-
|
|
36
|
-
};
|
|
77
|
+
const [selectedFacility, setSelectedFacility] = React.useState<string>('');
|
|
37
78
|
|
|
38
|
-
const [
|
|
79
|
+
const [selectedYear, selectedMonth] = monthString.split('-');
|
|
39
80
|
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
81
|
+
/** Recomputed per render so a session open across New Year still offers this year */
|
|
82
|
+
const years = React.useMemo(
|
|
83
|
+
() => Array.from({ length: yearsBack }, (_, index) => String(dayjs().year() - index)),
|
|
84
|
+
[yearsBack],
|
|
85
|
+
);
|
|
43
86
|
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
87
|
+
const showsSwitcher = mode === 'both';
|
|
88
|
+
const usesDateRange = mode === 'range' || (showsSwitcher && activeTab === 'custom');
|
|
89
|
+
const usesMonth = mode === 'monthly' || (showsSwitcher && activeTab === 'monthly');
|
|
90
|
+
|
|
91
|
+
const dateRange = React.useMemo(
|
|
92
|
+
() => [dayjs(startDateString).toDate(), dayjs(endDateString).toDate()],
|
|
93
|
+
[startDateString, endDateString],
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const period = React.useMemo(() => {
|
|
97
|
+
const month = usesDateRange ? undefined : monthString;
|
|
98
|
+
const { startDate, endDate } = month
|
|
99
|
+
? monthToDateRange(month)
|
|
100
|
+
: { startDate: startDateString, endDate: endDateString };
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
startDate,
|
|
104
|
+
endDate,
|
|
105
|
+
month,
|
|
106
|
+
facility: facilities ? selectedFacility : undefined,
|
|
107
|
+
label: describePeriod(startDate, endDate, month),
|
|
108
|
+
};
|
|
109
|
+
}, [usesDateRange, monthString, startDateString, endDateString, facilities, selectedFacility]);
|
|
110
|
+
|
|
111
|
+
/* What the report currently on screen was generated for, so a change to the
|
|
112
|
+
filters can be called out rather than silently leaving stale figures up. */
|
|
113
|
+
const [generatedFor, setGeneratedFor] = React.useState<{ key: string; label: string } | null>(null);
|
|
114
|
+
|
|
115
|
+
const errors = React.useMemo(
|
|
116
|
+
() =>
|
|
117
|
+
validatePeriod({
|
|
118
|
+
usesDateRange,
|
|
119
|
+
year: selectedYear,
|
|
120
|
+
month: selectedMonth,
|
|
121
|
+
startDate: startDateString,
|
|
122
|
+
endDate: endDateString,
|
|
123
|
+
}),
|
|
124
|
+
[usesDateRange, selectedYear, selectedMonth, startDateString, endDateString],
|
|
125
|
+
);
|
|
126
|
+
const isValid = Object.keys(errors).length === 0;
|
|
127
|
+
|
|
128
|
+
/* Reported on change so a report can label its form with the chosen period
|
|
129
|
+
before any figures exist for it. */
|
|
130
|
+
React.useEffect(() => {
|
|
131
|
+
onPeriodChange?.(period);
|
|
132
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
133
|
+
}, [period]);
|
|
134
|
+
|
|
135
|
+
const periodKey = `${period.startDate}|${period.endDate}|${period.facility ?? ''}`;
|
|
136
|
+
|
|
137
|
+
/* Falls back to the period the caller says is on screen, which is what makes
|
|
138
|
+
the warning work after a reload rather than only after a generate. */
|
|
139
|
+
const shownPeriod =
|
|
140
|
+
generatedFor ??
|
|
141
|
+
(generatedPeriod?.startDate && generatedPeriod?.endDate
|
|
142
|
+
? {
|
|
143
|
+
key: `${generatedPeriod.startDate}|${generatedPeriod.endDate}|`,
|
|
144
|
+
label: describePeriod(generatedPeriod.startDate, generatedPeriod.endDate),
|
|
62
145
|
}
|
|
63
|
-
|
|
146
|
+
: null);
|
|
147
|
+
|
|
148
|
+
const isStale = isReportGenerated && shownPeriod !== null && shownPeriod.key !== periodKey;
|
|
149
|
+
|
|
150
|
+
const handleGenerate = () => {
|
|
151
|
+
if (!isValid) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
64
154
|
|
|
65
|
-
|
|
155
|
+
setGeneratedFor({ key: periodKey, label: period.label });
|
|
156
|
+
onGenerate(period);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/** Only commit a complete range, so re-rendering does not interrupt the second pick */
|
|
160
|
+
const handleDateRangeChange = (dates: Array<Date>) => {
|
|
161
|
+
const [start, end] = dates;
|
|
162
|
+
|
|
163
|
+
if (start && end) {
|
|
164
|
+
setStartDateString(dayjs(start).format(DATE_FORMAT));
|
|
165
|
+
setEndDateString(dayjs(end).format(DATE_FORMAT));
|
|
66
166
|
}
|
|
67
167
|
};
|
|
68
168
|
|
|
169
|
+
const dateRangePicker = (
|
|
170
|
+
<DatePicker
|
|
171
|
+
datePickerType="range"
|
|
172
|
+
dateFormat="d/m/Y"
|
|
173
|
+
maxDate={new Date()}
|
|
174
|
+
value={dateRange}
|
|
175
|
+
onChange={handleDateRangeChange}
|
|
176
|
+
className={styles.datePicker}
|
|
177
|
+
>
|
|
178
|
+
<DatePickerInput
|
|
179
|
+
id="report-start-date"
|
|
180
|
+
labelText="Start date"
|
|
181
|
+
placeholder="dd/mm/yyyy"
|
|
182
|
+
size="sm"
|
|
183
|
+
invalid={Boolean(errors.startDate)}
|
|
184
|
+
invalidText={errors.startDate}
|
|
185
|
+
/>
|
|
186
|
+
<DatePickerInput
|
|
187
|
+
id="report-end-date"
|
|
188
|
+
labelText="End date"
|
|
189
|
+
placeholder="dd/mm/yyyy"
|
|
190
|
+
size="sm"
|
|
191
|
+
invalid={Boolean(errors.endDate)}
|
|
192
|
+
invalidText={errors.endDate}
|
|
193
|
+
/>
|
|
194
|
+
</DatePicker>
|
|
195
|
+
);
|
|
196
|
+
|
|
69
197
|
return (
|
|
70
|
-
|
|
71
|
-
<
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
</
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<label htmlFor="endingMonth">End Date:</label>
|
|
82
|
-
|
|
83
|
-
<input
|
|
84
|
-
id="startingMonth"
|
|
85
|
-
type="date"
|
|
86
|
-
className={styles.input}
|
|
87
|
-
value={startDateString}
|
|
88
|
-
onChange={(e) => setStartDateString(e.target.value)}
|
|
89
|
-
/>
|
|
90
|
-
|
|
91
|
-
<input
|
|
92
|
-
id="endingMonth"
|
|
93
|
-
type="date"
|
|
94
|
-
className={styles.input}
|
|
95
|
-
value={endDateString}
|
|
96
|
-
onChange={(e) => setEndDateString(e.target.value)}
|
|
97
|
-
/>
|
|
98
|
-
</div>
|
|
99
|
-
)}
|
|
100
|
-
|
|
101
|
-
{mode === 'monthly' && (
|
|
102
|
-
<div>
|
|
103
|
-
{/* Tabs */}
|
|
104
|
-
<div className={styles.tabsContainer}>
|
|
105
|
-
<Button
|
|
106
|
-
className={activeTab === 'monthly' ? styles.activeTab : styles.tab}
|
|
107
|
-
onClick={() => setActiveTab('monthly')}
|
|
108
|
-
>
|
|
109
|
-
Monthly
|
|
110
|
-
</Button>
|
|
198
|
+
<div className={styles.filters}>
|
|
199
|
+
<Breadcrumb noTrailingSlash className={styles.breadcrumb}>
|
|
200
|
+
<BreadcrumbItem>
|
|
201
|
+
<Link to="/">All reports</Link>
|
|
202
|
+
</BreadcrumbItem>
|
|
203
|
+
{/* The report name doubles as the page heading, so it stays an `h3`
|
|
204
|
+
rather than being repeated in a separate title row. */}
|
|
205
|
+
<BreadcrumbItem isCurrentPage>
|
|
206
|
+
<h3 className={styles.title}>{reportName}</h3>
|
|
207
|
+
</BreadcrumbItem>
|
|
208
|
+
</Breadcrumb>
|
|
111
209
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
210
|
+
<Layer className={styles.filtersContainer}>
|
|
211
|
+
<div className={styles.fields}>
|
|
212
|
+
{showsSwitcher && (
|
|
213
|
+
<div className={classNames(styles.field, styles.periodField)}>
|
|
214
|
+
<span className={styles.fieldLabel}>Period type</span>
|
|
215
|
+
<ContentSwitcher
|
|
216
|
+
selectedIndex={activeTab === 'monthly' ? 0 : 1}
|
|
217
|
+
onChange={({ index }) => setActiveTab(index === 0 ? 'monthly' : 'custom')}
|
|
218
|
+
size="sm"
|
|
219
|
+
className={styles.switcher}
|
|
115
220
|
>
|
|
116
|
-
|
|
117
|
-
|
|
221
|
+
<Switch name="monthly" text="Monthly" />
|
|
222
|
+
<Switch name="custom" text="Custom range" />
|
|
223
|
+
</ContentSwitcher>
|
|
118
224
|
</div>
|
|
225
|
+
)}
|
|
119
226
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
className={styles.input}
|
|
153
|
-
value={endDateString}
|
|
154
|
-
onChange={(e) => setEndDateString(e.target.value)}
|
|
155
|
-
/>
|
|
156
|
-
</div>
|
|
157
|
-
)}
|
|
158
|
-
</div>
|
|
159
|
-
)}
|
|
227
|
+
{usesMonth && (
|
|
228
|
+
<>
|
|
229
|
+
<Select
|
|
230
|
+
id="report-year"
|
|
231
|
+
labelText="Year"
|
|
232
|
+
size="sm"
|
|
233
|
+
className={classNames(styles.field, styles.yearField)}
|
|
234
|
+
invalid={Boolean(errors.year)}
|
|
235
|
+
invalidText={errors.year}
|
|
236
|
+
value={selectedYear}
|
|
237
|
+
onChange={(event) => setMonthString(`${event.target.value}-${selectedMonth}`)}
|
|
238
|
+
>
|
|
239
|
+
{years.map((year) => (
|
|
240
|
+
<SelectItem key={year} value={year} text={year} />
|
|
241
|
+
))}
|
|
242
|
+
</Select>
|
|
243
|
+
<Select
|
|
244
|
+
id="report-month"
|
|
245
|
+
labelText="Month"
|
|
246
|
+
size="sm"
|
|
247
|
+
className={classNames(styles.field, styles.monthField)}
|
|
248
|
+
invalid={Boolean(errors.month)}
|
|
249
|
+
invalidText={errors.month}
|
|
250
|
+
value={selectedMonth}
|
|
251
|
+
onChange={(event) => setMonthString(`${selectedYear}-${event.target.value}`)}
|
|
252
|
+
>
|
|
253
|
+
{months.map((month) => (
|
|
254
|
+
<SelectItem key={month.value} value={month.value} text={month.label} />
|
|
255
|
+
))}
|
|
256
|
+
</Select>
|
|
257
|
+
</>
|
|
258
|
+
)}
|
|
160
259
|
|
|
161
|
-
|
|
162
|
-
<div>
|
|
163
|
-
<label>Facility</label>
|
|
260
|
+
{usesDateRange && <div className={classNames(styles.field, styles.rangeField)}>{dateRangePicker}</div>}
|
|
164
261
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
262
|
+
{facilities && (
|
|
263
|
+
<Select
|
|
264
|
+
id="report-facility"
|
|
265
|
+
labelText="Facility"
|
|
266
|
+
size="sm"
|
|
267
|
+
className={styles.field}
|
|
168
268
|
value={selectedFacility}
|
|
169
|
-
onChange={(
|
|
269
|
+
onChange={(event) => setSelectedFacility(event.target.value)}
|
|
170
270
|
>
|
|
171
|
-
<
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
271
|
+
<SelectItem value="" text="All facilities" />
|
|
272
|
+
{facilities.map((facility) => (
|
|
273
|
+
<SelectItem key={facility.value} value={facility.value} text={facility.label} />
|
|
274
|
+
))}
|
|
275
|
+
</Select>
|
|
276
|
+
)}
|
|
277
|
+
</div>
|
|
176
278
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
279
|
+
<div className={styles.actions}>
|
|
280
|
+
{/* Restating a period that cannot be generated reads as if it will be,
|
|
281
|
+
so the blocking reason takes its place. */}
|
|
282
|
+
{isValid && isStale ? (
|
|
283
|
+
<p className={classNames(styles.summary, styles.summaryStale)}>
|
|
284
|
+
<WarningAlt size={16} />
|
|
285
|
+
Filters changed. The report below is for <strong>{shownPeriod?.label}</strong> — select Generate
|
|
286
|
+
report to refresh it.
|
|
287
|
+
</p>
|
|
288
|
+
) : isValid ? (
|
|
289
|
+
<p className={styles.summary}>
|
|
290
|
+
<Calendar size={16} />
|
|
291
|
+
Reporting period: <strong>{period.label}</strong>
|
|
292
|
+
</p>
|
|
293
|
+
) : (
|
|
294
|
+
<p className={classNames(styles.summary, styles.summaryInvalid)}>
|
|
295
|
+
<WarningAlt size={16} />
|
|
296
|
+
{firstError(errors)}
|
|
297
|
+
</p>
|
|
298
|
+
)}
|
|
299
|
+
<div className={styles.actionButtons}>
|
|
300
|
+
{/* Only offered once there is something on screen to put on paper */}
|
|
301
|
+
{isReportGenerated && !isLoading && (
|
|
302
|
+
<Button kind="tertiary" size="sm" renderIcon={Download} onClick={() => window.print()}>
|
|
303
|
+
Download PDF
|
|
304
|
+
</Button>
|
|
305
|
+
)}
|
|
306
|
+
<Button size="sm" renderIcon={ChartColumn} onClick={handleGenerate} disabled={isLoading || !isValid}>
|
|
307
|
+
{isLoading ? 'Generating…' : 'Generate report'}
|
|
308
|
+
</Button>
|
|
309
|
+
</div>
|
|
180
310
|
</div>
|
|
181
|
-
|
|
182
|
-
|
|
311
|
+
</Layer>
|
|
312
|
+
</div>
|
|
183
313
|
);
|
|
184
314
|
};
|
|
185
315
|
|