@ampath/esm-reports-app 1.0.0-next.7 → 1.0.0-next.71
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/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/299.js +2 -0
- package/dist/299.js.LICENSE.txt +3 -0
- package/dist/299.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/354.js +2 -0
- package/dist/354.js.LICENSE.txt +20 -0
- package/dist/354.js.map +1 -0
- package/dist/410.js +1 -0
- package/dist/410.js.map +1 -0
- package/dist/457.js +2 -0
- package/dist/457.js.LICENSE.txt +261 -0
- package/dist/457.js.map +1 -0
- package/dist/478.js +1 -1
- package/dist/478.js.map +1 -1
- package/dist/480.js +1 -0
- package/dist/540.js +1 -1
- package/dist/540.js.map +1 -1
- package/dist/55.js +1 -1
- package/dist/580.js +1 -0
- package/dist/580.js.map +1 -0
- package/dist/600.js +2 -0
- package/dist/600.js.LICENSE.txt +35 -0
- package/dist/600.js.map +1 -0
- package/dist/652.js +1 -1
- package/dist/781.js +1 -0
- package/dist/781.js.map +1 -0
- package/dist/919.css +1 -0
- package/dist/919.css.map +1 -0
- package/dist/919.js +1 -0
- package/dist/919.js.map +1 -0
- package/dist/961.js +1 -1
- package/dist/961.js.map +1 -1
- package/dist/99.js +1 -1
- package/dist/99.js.map +1 -0
- 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 +4 -2
- package/src/common/daily-summary-table.tsx +55 -0
- package/src/common/linelist-table/index.ts +3 -0
- package/src/common/linelist-table/linelist-columns.ts +48 -0
- package/src/common/linelist-table/linelist-table.component.tsx +442 -0
- package/src/common/linelist-table/linelist-table.scss +306 -0
- package/src/common/linelist-table/linelist-table.test.tsx +120 -0
- package/src/common/linelist-table/use-all-rows.ts +93 -0
- package/src/common/linelist-table/use-column-layout.ts +123 -0
- 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 +317 -99
- package/src/common/report-filters/report-filters.scss +255 -32
- package/src/common/report-filters/types.ts +79 -0
- package/src/common/report-filters/use-restore-period.ts +43 -0
- package/src/common/report-header/index.ts +1 -0
- package/src/common/report-header/use-location-details.ts +58 -0
- package/src/common/report-page/download-sheet-pdf.ts +59 -0
- package/src/common/report-page/index.ts +3 -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 +60 -0
- package/src/common/report-placeholder/has-report-data.ts +15 -0
- package/src/common/report-placeholder/index.ts +3 -0
- package/src/common/report-placeholder/report-placeholder.component.tsx +81 -0
- package/src/common/report-placeholder/report-placeholder.scss +48 -0
- package/src/common/report-placeholder/use-generated-location.ts +62 -0
- package/src/common/report-register/index.ts +6 -0
- package/src/common/report-register/register-columns.ts +46 -0
- package/src/common/report-register/register-layout.component.tsx +265 -0
- package/src/common/report-register/register-layout.scss +150 -0
- package/src/common/report-register/register-table.scss +71 -0
- package/src/common/report-register/report-parents.ts +27 -0
- package/src/common/report-register/rows-to-csv.ts +89 -0
- package/src/common/report-register/use-location-name.ts +54 -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 +189 -18
- package/src/dashboard/reports-dashboard.module.scss +357 -14
- package/src/index.ts +13 -1
- package/src/registers/moh-240/moh-240-register.scss +41 -0
- package/src/registers/moh-240/moh-240.component.test.tsx +166 -0
- package/src/registers/moh-240/moh-240.component.tsx +164 -0
- package/src/registers/moh-362/moh-362-lab-register.component.tsx +392 -0
- package/src/registers/moh-362/moh-362-linkage-register.component.tsx +120 -0
- package/src/registers/moh-362/moh-362-register.scss +222 -0
- package/src/registers/moh-362/moh-362.component.tsx +99 -0
- package/src/registers/moh-362/moh-362.scss +11 -0
- package/src/registers/moh-362/moh-362.types.ts +97 -0
- package/src/registers/register-catalog.ts +96 -0
- package/src/registers/tb-lab-register/tb-lab-register-table.component.tsx +116 -0
- package/src/registers/tb-lab-register/tb-lab-register-table.scss +123 -0
- package/src/registers/tb-lab-register/tb-lab-register.columns.ts +164 -0
- package/src/registers/tb-lab-register/tb-lab-register.component.test.tsx +232 -0
- package/src/registers/tb-lab-register/tb-lab-register.component.tsx +153 -0
- package/src/reports/datatable-wrapper/datatable-wrapper.component.tsx +63 -48
- package/src/reports/moh-240/moh-240-header.component.tsx +62 -0
- package/src/reports/moh-240/moh-240.columns.ts +113 -0
- package/src/reports/moh-240/moh-240.component.tsx +72 -9
- package/src/reports/moh-240/moh-240.scss +40 -0
- package/src/reports/moh-240/sub-reports/moh-240-register.component.tsx +31 -37
- package/src/reports/moh-240/sub-reports/page-summary.component.tsx +143 -106
- package/src/reports/moh-505/moh-505-header.component.tsx +67 -0
- package/src/reports/moh-505/moh-505.component.tsx +441 -0
- package/src/reports/moh-505/moh-505.scss +117 -0
- package/src/reports/moh-705B/moh-204b-register.component.tsx +244 -0
- package/src/reports/moh-705B/moh-204b.columns.ts +39 -0
- package/src/reports/moh-705B/moh-705b.component.tsx +659 -0
- package/src/reports/moh-705B/moh-705b.scss +172 -0
- package/src/reports/moh-705a/moh-705a.component.tsx +661 -0
- package/src/reports/moh-705a/moh-705a.scss +172 -0
- package/src/reports/moh-705a/registers/moh-204a-register.component.tsx +329 -0
- package/src/reports/moh-705a/registers/moh-204a.columns.ts +57 -0
- package/src/reports/moh-705a/type.ts +6 -0
- package/src/reports/moh-706/moh-706-header.component.tsx +66 -0
- package/src/reports/moh-706/moh-706.component.tsx +150 -24
- package/src/reports/moh-706/moh-706.scss +119 -11
- package/src/reports/moh-706/sub-reports/bacteriology/bacteriology.component.tsx +54 -62
- package/src/reports/moh-706/sub-reports/blood-chemistry/blood-chemistry.component.tsx +228 -317
- package/src/reports/moh-706/sub-reports/drug-susceptibility-testing/drug-susceptibility-testing.component.tsx +31 -46
- package/src/reports/moh-706/sub-reports/haematology/haematology.component.tsx +47 -55
- package/src/reports/moh-706/sub-reports/histology-and-cytology/histology-and-cytology.component.tsx +43 -58
- package/src/reports/moh-706/sub-reports/parasitology/parasitology.component.tsx +31 -39
- package/src/reports/moh-706/sub-reports/serology/serology.component.tsx +26 -34
- package/src/reports/moh-706/sub-reports/specimen-referral-to-higher-levels/specimen-referral-to-higher-levels.component.tsx +9 -24
- package/src/reports/moh-706/sub-reports/urine-analysis/urine-analysis.component.tsx +43 -51
- package/src/reports/moh-710/moh-710.component.tsx +799 -632
- package/src/reports/moh-710/moh-710.scss +142 -21
- package/src/reports/moh-710/registers/moh-510/moh-510.columns.ts +40 -0
- package/src/reports/moh-710/registers/moh-510/moh-510.component.tsx +144 -0
- package/src/reports/moh-710/registers/moh-510/moh-510.scss +130 -0
- package/src/reports/moh-710/registers/moh-511-710-register.component.tsx +268 -0
- package/src/reports/moh-710/registers/moh-511-710.columns.ts +52 -0
- package/src/reports/moh-710/registers/type.ts +31 -0
- package/src/reports/moh-711/moh-711.component.tsx +138 -1269
- package/src/reports/moh-711/moh711.scss +135 -20
- package/src/reports/moh-711/registers/moh-333-register.component.tsx +637 -0
- package/src/reports/moh-711/registers/moh-333.columns.ts +119 -0
- package/src/reports/moh-711/registers/moh-405-register.component.tsx +520 -0
- package/src/reports/moh-711/registers/moh-405.columns.ts +120 -0
- package/src/reports/moh-711/registers/moh-406-register.component.tsx +530 -0
- package/src/reports/moh-711/registers/moh-406.columns.ts +133 -0
- package/src/reports/moh-711/registers/moh-510-register.component.tsx +55 -0
- package/src/reports/moh-711/registers/moh-511-register.component.tsx +270 -0
- package/src/reports/moh-711/registers/moh-511.columns.ts +59 -0
- package/src/reports/moh-711/registers/type.ts +192 -0
- package/src/reports/moh-711/sections/anc.component.tsx +159 -0
- package/src/reports/moh-711/sections/cervical-cancer.component.tsx +148 -0
- package/src/reports/moh-711/sections/chanis.component.tsx +366 -0
- package/src/reports/moh-711/sections/family-planning.component.tsx +221 -0
- package/src/reports/moh-711/sections/gbv.component.tsx +115 -0
- package/src/reports/moh-711/sections/maternity.component.tsx +321 -0
- package/src/reports/moh-711/sections/medical-social-work.component.tsx +73 -0
- package/src/reports/moh-711/sections/other.component.tsx +47 -0
- package/src/reports/moh-711/sections/physiotherapy.component.tsx +61 -0
- package/src/reports/moh-711/sections/pnc.component.tsx +127 -0
- package/src/reports/moh-711/sections/post-abortion.component.tsx +42 -0
- package/src/reports/moh-711/sections/rehabilitation.component.tsx +57 -0
- package/src/reports/moh-711/sections/report-compiled-by.component.tsx +50 -0
- package/src/reports/moh-711/sections/tb-screening.component.tsx +57 -0
- package/src/reports/moh-717/moh-717.component.tsx +149 -985
- package/src/reports/moh-717/moh717.scss +217 -21
- package/src/reports/moh-717/registers/nutrition-register.component.tsx +48 -0
- package/src/reports/moh-717/sections/finance.component.tsx +42 -0
- package/src/reports/moh-717/sections/inpatient.component.tsx +374 -0
- package/src/reports/moh-717/sections/maternity.component.tsx +126 -0
- package/src/reports/moh-717/sections/medical-records.component.tsx +35 -0
- package/src/reports/moh-717/sections/mortuary.component.tsx +43 -0
- package/src/reports/moh-717/sections/operations.component.tsx +53 -0
- package/src/reports/moh-717/sections/orthopaedic-trauma.component.tsx +98 -0
- package/src/reports/moh-717/sections/outpatient.component.tsx +597 -0
- package/src/reports/moh-717/sections/pharmacy.component.tsx +43 -0
- package/src/reports/moh-717/sections/preparedby.component.tsx +52 -0
- package/src/reports/moh-717/sections/special-services.component.tsx +114 -0
- package/src/reports/moh-731/linelist/moh-731-indicators.ts +1122 -0
- package/src/reports/moh-731/linelist/moh-731-linelist-sections.ts +472 -0
- package/src/reports/moh-731/linelist/moh-731-linelist.component.tsx +102 -0
- package/src/reports/moh-731/linelist/use-open-moh-731-linelist.ts +50 -0
- package/src/reports/moh-731/moh-731.component.tsx +266 -0
- package/src/reports/moh-731/moh-731.scss +499 -0
- package/src/reports/moh-731/registers/moh-366.columns.ts +38 -0
- package/src/reports/moh-731/registers/moh-366.component.tsx +471 -0
- package/src/reports/moh-731/registers/prep-register.component.tsx +284 -0
- package/src/reports/moh-731/registers/prep.columns.ts +34 -0
- package/src/reports/moh-731/sections/hiv-and-tb-treatment.component.tsx +1206 -0
- package/src/reports/moh-731/sections/hiv-testing-and-pre-exposure.component.tsx +727 -0
- package/src/reports/moh-731/sections/pmtct.component.tsx +572 -0
- package/src/reports/moh-731/summary/moh-731-summary.component.tsx +313 -0
- package/src/reports/moh-731/summary/moh-731-summary.config.ts +529 -0
- package/src/reports/moh-731/summary/moh-731-summary.scss +242 -0
- package/src/reports/moh-740/moh-740.component.scss +202 -0
- package/src/reports/moh-740/moh-740.component.tsx +1332 -0
- package/src/reports/moh-740/moh-740.resource.ts +54 -0
- package/src/reports/moh-740/registers/moh-222-daily-register/moh-222-daily-register.tsx +149 -0
- package/src/reports/moh-740/registers/moh-270-permanent-register/moh-270-permanent-register.tsx +123 -0
- package/src/reports/moh-740/registers/moh-740-patient-list.scss +0 -0
- package/src/reports/moh-740/registers/moh-740-patient-list.tsx +60 -0
- package/src/reports/moh-740/registers/moh-740.columns.ts +54 -0
- package/src/reports/moh-740/shared/data-cell/data-cell.scss +6 -0
- package/src/reports/moh-740/shared/data-cell/data-cell.tsx +23 -0
- package/src/reports/moh-740/shared/utils/indicator-register-map.ts +159 -0
- package/src/reports/moh-740/types/index.ts +223 -0
- package/src/reports/moh-745/moh-745.component.tsx +1021 -0
- package/src/reports/moh-745/moh-745.scss +132 -0
- package/src/reports/moh-745/registers/moh-412-register.component.tsx +249 -0
- package/src/reports/moh-745/registers/moh-412.columns.ts +52 -0
- package/src/reports/moh-745/registers/type.ts +181 -0
- package/src/reports/table-wrapper/table-row-mapper.component.tsx +57 -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-362.resource.ts +27 -0
- package/src/resources/moh-505.resource.ts +69 -0
- package/src/resources/moh-705.resource.ts +106 -0
- package/src/resources/moh-706.resource.ts +80 -0
- package/src/resources/moh-710.resource.ts +32 -9
- package/src/resources/moh-711.resource.ts +137 -9
- package/src/resources/moh-717.resource.ts +26 -9
- package/src/resources/moh-731.resource.ts +90 -0
- package/src/resources/moh-745.resource.ts +76 -0
- package/src/resources/patient-list-page.ts +65 -0
- package/src/root.component.tsx +59 -8
- package/src/root.scss +36 -0
- package/src/routes.json +3 -1
- package/src/utils/format-indicator.ts +39 -0
- package/src/utils/get-base-url.ts +39 -7
- package/src/utils/utils.ts +36 -0
- package/tsconfig.json +1 -1
- package/webpack.config.js +22 -1
- package/dist/138.js +0 -2
- package/dist/138.js.LICENSE.txt +0 -20
- package/dist/138.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/711.js +0 -1
- package/dist/711.js.map +0 -1
- package/dist/812.js +0 -1
- package/dist/812.js.map +0 -1
- package/dist/ampath-esm-reports-app.js.buildmanifest.json +0 -539
- package/dist/main.js +0 -1
- package/dist/main.js.map +0 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a report has been generated and came back with something to show.
|
|
3
|
+
*
|
|
4
|
+
* Reports flatten their response with `Object.assign({}, ...data.result)`, but
|
|
5
|
+
* they initialise that state variously as `{}`, `[]` or a value restored from
|
|
6
|
+
* sessionStorage, so "has it run yet" is really "does it have any keys". An
|
|
7
|
+
* empty array has none, which is what makes this work across all of them.
|
|
8
|
+
*/
|
|
9
|
+
export function hasReportData(data: unknown): boolean {
|
|
10
|
+
if (!data || typeof data !== 'object') {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return Object.keys(data).length > 0;
|
|
15
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Layer } from '@carbon/react';
|
|
3
|
+
import { Location, Report } from '@carbon/react/icons';
|
|
4
|
+
import { useSession } from '@openmrs/esm-framework';
|
|
5
|
+
|
|
6
|
+
import styles from './report-placeholder.scss';
|
|
7
|
+
|
|
8
|
+
interface ReportPlaceholderProps {
|
|
9
|
+
/** Named so the guidance says which return is about to be produced */
|
|
10
|
+
reportName?: string;
|
|
11
|
+
/** Shown when a report was generated but came back with nothing in it */
|
|
12
|
+
isEmptyResult?: boolean;
|
|
13
|
+
/** A report was generated, but for another location than the current one */
|
|
14
|
+
isLocationChanged?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Stands in for the report template until a report has actually been generated,
|
|
19
|
+
* and explains how to produce one.
|
|
20
|
+
*
|
|
21
|
+
* Report templates are large pre-printed MOH forms; rendering one with every
|
|
22
|
+
* value blank reads as "the facility reported zeroes" rather than "you have not
|
|
23
|
+
* run this yet", so the template is held back until there is data to put in it.
|
|
24
|
+
* That leaves the page empty, hence the guidance here.
|
|
25
|
+
*/
|
|
26
|
+
const ReportPlaceholder: React.FC<ReportPlaceholderProps> = ({
|
|
27
|
+
reportName,
|
|
28
|
+
isEmptyResult = false,
|
|
29
|
+
isLocationChanged = false,
|
|
30
|
+
}) => {
|
|
31
|
+
const session = useSession();
|
|
32
|
+
const location = session?.sessionLocation?.display;
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<Layer className={styles.placeholder}>
|
|
36
|
+
{isLocationChanged ? (
|
|
37
|
+
<Location size={32} className={styles.icon} />
|
|
38
|
+
) : (
|
|
39
|
+
<Report size={32} className={styles.icon} />
|
|
40
|
+
)}
|
|
41
|
+
|
|
42
|
+
{isLocationChanged ? (
|
|
43
|
+
<>
|
|
44
|
+
<h5>Location changed</h5>
|
|
45
|
+
<p>
|
|
46
|
+
The last {reportName ?? 'report'} was generated for a different location, so it is not shown here. Select{' '}
|
|
47
|
+
<strong>Generate report</strong> to produce it for{' '}
|
|
48
|
+
{location ? <strong>{location}</strong> : 'this location'}.
|
|
49
|
+
</p>
|
|
50
|
+
</>
|
|
51
|
+
) : isEmptyResult ? (
|
|
52
|
+
<>
|
|
53
|
+
<h5>No records for this period</h5>
|
|
54
|
+
<p>
|
|
55
|
+
{reportName ?? 'The report'} ran successfully but returned no data for the period you selected. Try a
|
|
56
|
+
different reporting period, or confirm that data has been entered for this facility.
|
|
57
|
+
</p>
|
|
58
|
+
</>
|
|
59
|
+
) : (
|
|
60
|
+
<>
|
|
61
|
+
<h5>No report generated yet</h5>
|
|
62
|
+
<p>
|
|
63
|
+
{reportName ? `The ${reportName} form` : 'The report'} will appear here, filled in with your
|
|
64
|
+
facility’s figures, once you generate it.
|
|
65
|
+
</p>
|
|
66
|
+
<ol className={styles.steps}>
|
|
67
|
+
<li>Choose the reporting period in the filters above.</li>
|
|
68
|
+
<li>
|
|
69
|
+
Select <strong>Generate report</strong>.
|
|
70
|
+
</li>
|
|
71
|
+
<li>
|
|
72
|
+
Use <strong>Download PDF</strong> to save or print the completed form.
|
|
73
|
+
</li>
|
|
74
|
+
</ol>
|
|
75
|
+
</>
|
|
76
|
+
)}
|
|
77
|
+
</Layer>
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export default ReportPlaceholder;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
@use '@carbon/layout';
|
|
2
|
+
@use '@carbon/type';
|
|
3
|
+
@use '@openmrs/esm-styleguide/src/vars' as *;
|
|
4
|
+
|
|
5
|
+
.placeholder {
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
align-items: center;
|
|
9
|
+
gap: layout.$spacing-03;
|
|
10
|
+
padding: layout.$spacing-07 layout.$spacing-05;
|
|
11
|
+
/* the same gap below the filter card as a generated report's panel has */
|
|
12
|
+
margin-top: layout.$spacing-05;
|
|
13
|
+
margin-bottom: layout.$spacing-05;
|
|
14
|
+
text-align: center;
|
|
15
|
+
background-color: $ui-02;
|
|
16
|
+
border: 1px solid $ui-03;
|
|
17
|
+
|
|
18
|
+
h5 {
|
|
19
|
+
@include type.type-style('productive-heading-02');
|
|
20
|
+
color: $color-gray-100;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
p {
|
|
24
|
+
@include type.type-style('body-long-01');
|
|
25
|
+
color: $text-02;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.icon {
|
|
30
|
+
fill: $ui-04;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* The steps that produce a report. Left aligned inside the centred card: a
|
|
34
|
+
numbered sequence is read down its left edge, not from a ragged centre. */
|
|
35
|
+
|
|
36
|
+
.steps {
|
|
37
|
+
max-width: 28rem;
|
|
38
|
+
margin-top: layout.$spacing-03;
|
|
39
|
+
padding-left: layout.$spacing-06;
|
|
40
|
+
text-align: left;
|
|
41
|
+
list-style: decimal;
|
|
42
|
+
|
|
43
|
+
li {
|
|
44
|
+
@include type.type-style('body-long-01');
|
|
45
|
+
color: $text-02;
|
|
46
|
+
margin-bottom: layout.$spacing-02;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
import { useSession } from '@openmrs/esm-framework';
|
|
3
|
+
|
|
4
|
+
function readLocation(storageKey?: string): string {
|
|
5
|
+
if (!storageKey) {
|
|
6
|
+
return '';
|
|
7
|
+
}
|
|
8
|
+
try {
|
|
9
|
+
return sessionStorage.getItem(storageKey) ?? '';
|
|
10
|
+
} catch {
|
|
11
|
+
return '';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function writeLocation(storageKey: string | undefined, locationUuid: string) {
|
|
16
|
+
if (!storageKey) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
sessionStorage.setItem(storageKey, locationUuid);
|
|
21
|
+
} catch {
|
|
22
|
+
/* storage unavailable: the report just won't survive a reload */
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Which location the report on screen was generated for, compared with the
|
|
28
|
+
* one the user is at now.
|
|
29
|
+
*
|
|
30
|
+
* A report's figures belong to the location they were fetched for, but its
|
|
31
|
+
* masthead names the session location. After switching location, the old
|
|
32
|
+
* figures would be shown -- and labelled -- as the new location's. So a report
|
|
33
|
+
* only counts as generated while it is for the current location; otherwise the
|
|
34
|
+
* page falls back to the placeholder and asks for it to be generated again.
|
|
35
|
+
*
|
|
36
|
+
* Reports that keep their figures in session storage pass `storageKey`, so the
|
|
37
|
+
* location is restored alongside them. Figures restored without a location
|
|
38
|
+
* (saved before this was recorded) are treated as not generated, rather than
|
|
39
|
+
* assumed to be for wherever the user happens to be.
|
|
40
|
+
*/
|
|
41
|
+
export function useGeneratedLocation(storageKey?: string) {
|
|
42
|
+
const session = useSession();
|
|
43
|
+
const currentLocation = session?.sessionLocation?.uuid ?? '';
|
|
44
|
+
const [generatedLocation, setGeneratedLocation] = useState(() => readLocation(storageKey));
|
|
45
|
+
|
|
46
|
+
/** Call once a report has been fetched, with the location it was fetched for */
|
|
47
|
+
const markGenerated = useCallback(
|
|
48
|
+
(locationUuid: string) => {
|
|
49
|
+
setGeneratedLocation(locationUuid);
|
|
50
|
+
writeLocation(storageKey, locationUuid);
|
|
51
|
+
},
|
|
52
|
+
[storageKey],
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
/** The report held is for the location the user is at now */
|
|
57
|
+
isForCurrentLocation: Boolean(generatedLocation) && generatedLocation === currentLocation,
|
|
58
|
+
/** A report is held, but for a different location than the current one */
|
|
59
|
+
hasLocationChanged: Boolean(generatedLocation) && generatedLocation !== currentLocation,
|
|
60
|
+
markGenerated,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** One exported column: the heading it carries on the register, and its value */
|
|
2
|
+
export interface RegisterColumn {
|
|
3
|
+
header: string;
|
|
4
|
+
value: (row: Record<string, any>, index: number) => unknown;
|
|
5
|
+
/**
|
|
6
|
+
* Keep the cell as text in the CSV so Excel never reinterprets it:
|
|
7
|
+
* identifiers lose leading zeros and phone numbers become `#NAME?`
|
|
8
|
+
* formulas when the spreadsheet parses them as numbers.
|
|
9
|
+
*/
|
|
10
|
+
text?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Allow the cell to wrap on screen. Registers keep their columns
|
|
13
|
+
* `nowrap` so short cells stay compact; prose columns (diagnoses,
|
|
14
|
+
* referrals, comments) would otherwise stretch the whole row wide.
|
|
15
|
+
*/
|
|
16
|
+
wrap?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** The age bands the registers tabulate against, in the order they appear */
|
|
20
|
+
export const AGE_BANDS = [
|
|
21
|
+
{ label: '< 1 yrs', min: 0, max: 0 },
|
|
22
|
+
{ label: '1-4 yrs', min: 1, max: 4 },
|
|
23
|
+
{ label: '5-9 yrs', min: 5, max: 9 },
|
|
24
|
+
{ label: '10-14 yrs', min: 10, max: 14 },
|
|
25
|
+
{ label: '15-19 yrs', min: 15, max: 19 },
|
|
26
|
+
{ label: '20-24 yrs', min: 20, max: 24 },
|
|
27
|
+
{ label: '25+ yrs', min: 25, max: Number.POSITIVE_INFINITY },
|
|
28
|
+
] as const;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Expands one indicator into the fourteen age-band by gender columns the
|
|
32
|
+
* register prints for it.
|
|
33
|
+
*
|
|
34
|
+
* The registers spell these out as fourteen near-identical cells; describing
|
|
35
|
+
* the rule once keeps the export in step with the table without restating it
|
|
36
|
+
* fourteen times per block.
|
|
37
|
+
*/
|
|
38
|
+
export function ageBandColumns(heading: string, value: (row: Record<string, any>) => unknown): RegisterColumn[] {
|
|
39
|
+
return AGE_BANDS.flatMap((band) =>
|
|
40
|
+
(['M', 'F'] as const).map((gender) => ({
|
|
41
|
+
header: `${heading} ${band.label} (${gender})`,
|
|
42
|
+
value: (row: Record<string, any>) =>
|
|
43
|
+
row.age >= band.min && row.age <= band.max && row.gender === gender ? value(row) : '',
|
|
44
|
+
})),
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Breadcrumb, BreadcrumbItem, Button, Layer, Pagination } from '@carbon/react';
|
|
3
|
+
import { Link, useSearchParams } from 'react-router-dom';
|
|
4
|
+
import { ArrowLeft, Calendar, Document, Download, Location } from '@carbon/react/icons';
|
|
5
|
+
import { formatDate } from '@openmrs/esm-framework';
|
|
6
|
+
|
|
7
|
+
import { formatIndicatorName } from '../../utils/format-indicator';
|
|
8
|
+
import { showReportError } from '../report-error';
|
|
9
|
+
import { columnsToCsv, downloadCsv, rowsToCsv } from './rows-to-csv';
|
|
10
|
+
import { type RegisterColumn } from './register-columns';
|
|
11
|
+
import { REPORT_PARENTS } from './report-parents';
|
|
12
|
+
|
|
13
|
+
import { RegisterSkeleton } from '../report-skeleton';
|
|
14
|
+
import styles from './register-layout.scss';
|
|
15
|
+
|
|
16
|
+
interface RegisterLayoutProps {
|
|
17
|
+
/** The report this register was opened from, e.g. `MOH-731 Report` */
|
|
18
|
+
parentLabel: string;
|
|
19
|
+
/** Route back to that report, e.g. `/moh-731` */
|
|
20
|
+
parentPath?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Returns to the report in place, for a register that is a view on the
|
|
23
|
+
* report's own route rather than a route of its own.
|
|
24
|
+
*
|
|
25
|
+
* MOH-740 toggles its registers in place, so there is nowhere to navigate:
|
|
26
|
+
* a breadcrumb would link the page to itself. Given this, the trail is
|
|
27
|
+
* replaced by a button that puts the report back.
|
|
28
|
+
*/
|
|
29
|
+
onBack?: () => void;
|
|
30
|
+
/** Register name, e.g. `HIV Care Treatment Daily Activity Register MOH 366` */
|
|
31
|
+
title: string;
|
|
32
|
+
/**
|
|
33
|
+
* The page heading, when the register can name the figure itself. Without
|
|
34
|
+
* it the heading is built from the link's `label` and `gender`.
|
|
35
|
+
*/
|
|
36
|
+
heading?: string;
|
|
37
|
+
/** The location the register was generated for, shown beside the period */
|
|
38
|
+
location?: string;
|
|
39
|
+
isLoading: boolean;
|
|
40
|
+
/** `true` once loaded with nothing to show */
|
|
41
|
+
isEmpty: boolean;
|
|
42
|
+
/** Paging state from `usePatientList` */
|
|
43
|
+
page?: number;
|
|
44
|
+
pageSize?: number;
|
|
45
|
+
total?: number;
|
|
46
|
+
/** `false` when the total is a lower bound the endpoint did not confirm */
|
|
47
|
+
isTotalExact?: boolean;
|
|
48
|
+
onPageChange?: (change: { page: number; pageSize: number }) => void;
|
|
49
|
+
/** Every row across every page, used to build the export */
|
|
50
|
+
fetchAll?: () => Promise<Array<Record<string, any>>>;
|
|
51
|
+
/** The register's own columns. Without them the export falls back to the
|
|
52
|
+
response's raw fields, which will not match the printed form. */
|
|
53
|
+
columns?: RegisterColumn[];
|
|
54
|
+
/**
|
|
55
|
+
* The printed form's own sub-headings (e.g. its column letters), written as
|
|
56
|
+
* a second CSV row so exports stay line-numbered against the paper form.
|
|
57
|
+
*/
|
|
58
|
+
subHeaders?: string[];
|
|
59
|
+
/** The register table. Optional: MOH 510 has no table implemented yet. */
|
|
60
|
+
children?: React.ReactNode;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The frame every drill-down register renders inside.
|
|
65
|
+
*
|
|
66
|
+
* Registers are opened from a value box on a report, so the trail back matters
|
|
67
|
+
* more than a bare Back button: the breadcrumb shows where the figure came
|
|
68
|
+
* from and lets the user step to either level. The table itself is wide -- the
|
|
69
|
+
* MOH 366 register runs to 41 columns -- so it scrolls within the page rather
|
|
70
|
+
* than forcing the whole layout sideways.
|
|
71
|
+
*/
|
|
72
|
+
const GENDERS: Record<string, string> = { M: 'Male', F: 'Female' };
|
|
73
|
+
|
|
74
|
+
const RegisterLayout: React.FC<RegisterLayoutProps> = ({
|
|
75
|
+
parentLabel,
|
|
76
|
+
parentPath,
|
|
77
|
+
onBack,
|
|
78
|
+
title,
|
|
79
|
+
heading: headingProp,
|
|
80
|
+
location,
|
|
81
|
+
isLoading,
|
|
82
|
+
isEmpty,
|
|
83
|
+
page,
|
|
84
|
+
pageSize,
|
|
85
|
+
total,
|
|
86
|
+
isTotalExact = true,
|
|
87
|
+
onPageChange,
|
|
88
|
+
fetchAll,
|
|
89
|
+
columns,
|
|
90
|
+
subHeaders,
|
|
91
|
+
children,
|
|
92
|
+
}) => {
|
|
93
|
+
/* The same register serves every indicator that links to it, so without this
|
|
94
|
+
each drill-down looks identical no matter which box was clicked. */
|
|
95
|
+
const [params] = useSearchParams();
|
|
96
|
+
const [isExporting, setIsExporting] = React.useState(false);
|
|
97
|
+
const indicator = params.get('indicator') ?? params.get('indicators');
|
|
98
|
+
/* The report passes the row label it displayed, so the crumb reads the way
|
|
99
|
+
the figure was labelled on the form. `formatIndicatorName` is the fallback
|
|
100
|
+
for links made before the label was carried through. */
|
|
101
|
+
const label = params.get('label');
|
|
102
|
+
const gender = params.get('gender');
|
|
103
|
+
const startDate = params.get('startDate');
|
|
104
|
+
const endDate = params.get('endDate');
|
|
105
|
+
const locationUuids = params.get('locationUuids');
|
|
106
|
+
|
|
107
|
+
/* Where the drill-down came from. Registers shared by two reports cannot
|
|
108
|
+
name their parent as a constant, so the link says which one it was; the
|
|
109
|
+
props remain the fallback for links made before it was carried. */
|
|
110
|
+
const from = params.get('from');
|
|
111
|
+
const parent = (from && REPORT_PARENTS[from]) || { label: parentLabel, path: parentPath ?? '' };
|
|
112
|
+
|
|
113
|
+
/* The crumb has to carry the period, not just the path: a report that does
|
|
114
|
+
not keep its figures in session storage regenerates from its URL, and
|
|
115
|
+
linking to the bare path would land the user on the placeholder instead of
|
|
116
|
+
the figures the register was opened from. */
|
|
117
|
+
const parentQuery = new URLSearchParams();
|
|
118
|
+
if (locationUuids) parentQuery.append('locationUuids', locationUuids);
|
|
119
|
+
if (startDate) parentQuery.append('startDate', startDate);
|
|
120
|
+
if (endDate) parentQuery.append('endDate', endDate);
|
|
121
|
+
/* back to the report tab the register was opened from, e.g. a summary view */
|
|
122
|
+
const view = params.get('view');
|
|
123
|
+
if (view) parentQuery.append('view', view);
|
|
124
|
+
const section = params.get('section');
|
|
125
|
+
if (section) parentQuery.append('section', section);
|
|
126
|
+
const parentLink = parentQuery.toString() ? `${parent.path}?${parentQuery}` : parent.path;
|
|
127
|
+
|
|
128
|
+
const heading =
|
|
129
|
+
headingProp ??
|
|
130
|
+
(label
|
|
131
|
+
? `${label}${gender && GENDERS[gender] ? ` (${GENDERS[gender]})` : ''}`
|
|
132
|
+
: formatIndicatorName(indicator ?? ''));
|
|
133
|
+
|
|
134
|
+
/* Exports every page, not the one on screen: the DOM only holds the current
|
|
135
|
+
page once paginated, so a scrape would silently export 50 of 2,000. */
|
|
136
|
+
const exportCsv = async () => {
|
|
137
|
+
if (!fetchAll) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
setIsExporting(true);
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const all = await fetchAll();
|
|
145
|
+
const slug = [title, label ?? indicator, startDate, endDate]
|
|
146
|
+
.filter(Boolean)
|
|
147
|
+
.join('-')
|
|
148
|
+
.replace(/[^a-z0-9]+/gi, '-')
|
|
149
|
+
.replace(/^-|-$/g, '')
|
|
150
|
+
.toLowerCase();
|
|
151
|
+
|
|
152
|
+
downloadCsv(columns ? columnsToCsv(all, columns, { subHeaderRow: subHeaders }) : rowsToCsv(all), `${slug}.csv`);
|
|
153
|
+
} catch (error) {
|
|
154
|
+
showReportError('the register export', error);
|
|
155
|
+
} finally {
|
|
156
|
+
setIsExporting(false);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const canExport = !isLoading && !isEmpty;
|
|
161
|
+
const pageSizeOrDefault = pageSize ?? 50;
|
|
162
|
+
|
|
163
|
+
const period =
|
|
164
|
+
startDate && endDate
|
|
165
|
+
? `${formatDate(new Date(startDate), { mode: 'standard', time: false, noToday: true })} to ${formatDate(
|
|
166
|
+
new Date(endDate),
|
|
167
|
+
{ mode: 'standard', time: false, noToday: true },
|
|
168
|
+
)}`
|
|
169
|
+
: null;
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<div className={styles.register}>
|
|
173
|
+
{onBack ? (
|
|
174
|
+
<div className={styles.breadcrumb}>
|
|
175
|
+
<Button kind="ghost" size="sm" renderIcon={ArrowLeft} onClick={onBack} className={styles.back}>
|
|
176
|
+
{parent.label}
|
|
177
|
+
</Button>
|
|
178
|
+
<h3 className={styles.title}>{heading || title}</h3>
|
|
179
|
+
</div>
|
|
180
|
+
) : (
|
|
181
|
+
<Breadcrumb noTrailingSlash className={styles.breadcrumb}>
|
|
182
|
+
<BreadcrumbItem>
|
|
183
|
+
<Link to="/">All reports</Link>
|
|
184
|
+
</BreadcrumbItem>
|
|
185
|
+
<BreadcrumbItem>
|
|
186
|
+
<Link to={parentLink}>{parent.label}</Link>
|
|
187
|
+
</BreadcrumbItem>
|
|
188
|
+
<BreadcrumbItem isCurrentPage>
|
|
189
|
+
<h3 className={styles.title}>{heading || title}</h3>
|
|
190
|
+
</BreadcrumbItem>
|
|
191
|
+
</Breadcrumb>
|
|
192
|
+
)}
|
|
193
|
+
|
|
194
|
+
{/* Register, period and the export sit on one line: they all describe the
|
|
195
|
+
table below, and splitting them stacked two sparse rows above it. */}
|
|
196
|
+
{(indicator || period || canExport) && (
|
|
197
|
+
<div className={styles.context}>
|
|
198
|
+
{/* Register and period read as one caption for the table, so they
|
|
199
|
+
share a panel rather than sitting as two loose fragments. */}
|
|
200
|
+
<span className={styles.summary}>
|
|
201
|
+
<span className={styles.summaryTitle}>{title}</span>
|
|
202
|
+
<span className={styles.period}>
|
|
203
|
+
{period && (
|
|
204
|
+
<>
|
|
205
|
+
<Calendar size={14} />
|
|
206
|
+
{period}
|
|
207
|
+
</>
|
|
208
|
+
)}
|
|
209
|
+
{location && (
|
|
210
|
+
<span className={styles.location}>
|
|
211
|
+
<Location size={14} />
|
|
212
|
+
{location}
|
|
213
|
+
</span>
|
|
214
|
+
)}
|
|
215
|
+
{typeof total === 'number' && total > 0 && (
|
|
216
|
+
<span>{isTotalExact ? `${total.toLocaleString()} patients` : `${total.toLocaleString()}+ patients`}</span>
|
|
217
|
+
)}
|
|
218
|
+
</span>
|
|
219
|
+
</span>
|
|
220
|
+
{canExport && fetchAll && (
|
|
221
|
+
<Button
|
|
222
|
+
kind="tertiary"
|
|
223
|
+
size="sm"
|
|
224
|
+
renderIcon={Download}
|
|
225
|
+
onClick={exportCsv}
|
|
226
|
+
disabled={isExporting}
|
|
227
|
+
className={styles.export}
|
|
228
|
+
>
|
|
229
|
+
{isExporting ? 'Preparing…' : 'Export CSV'}
|
|
230
|
+
</Button>
|
|
231
|
+
)}
|
|
232
|
+
</div>
|
|
233
|
+
)}
|
|
234
|
+
|
|
235
|
+
{isLoading && <RegisterSkeleton />}
|
|
236
|
+
|
|
237
|
+
{!isLoading && isEmpty && (
|
|
238
|
+
<Layer className={styles.empty}>
|
|
239
|
+
<Document size={32} className={styles.emptyIcon} />
|
|
240
|
+
<h4>No records to list</h4>
|
|
241
|
+
<p>No clients matched this indicator for the selected period and facility.</p>
|
|
242
|
+
</Layer>
|
|
243
|
+
)}
|
|
244
|
+
|
|
245
|
+
{canExport && (
|
|
246
|
+
<>
|
|
247
|
+
<div className={styles.tableScroll}>{children}</div>
|
|
248
|
+
{onPageChange && typeof total === 'number' && total > pageSizeOrDefault && (
|
|
249
|
+
<Pagination
|
|
250
|
+
className={styles.pagination}
|
|
251
|
+
page={page}
|
|
252
|
+
pageSize={pageSizeOrDefault}
|
|
253
|
+
pageSizes={[25, 50, 100, 200]}
|
|
254
|
+
totalItems={total}
|
|
255
|
+
size="sm"
|
|
256
|
+
onChange={onPageChange}
|
|
257
|
+
/>
|
|
258
|
+
)}
|
|
259
|
+
</>
|
|
260
|
+
)}
|
|
261
|
+
</div>
|
|
262
|
+
);
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
export default RegisterLayout;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
@use '@carbon/layout';
|
|
2
|
+
@use '@carbon/type';
|
|
3
|
+
@use '@openmrs/esm-styleguide/src/vars' as *;
|
|
4
|
+
|
|
5
|
+
.register {
|
|
6
|
+
padding: layout.$spacing-05 0 layout.$spacing-07;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.breadcrumb {
|
|
10
|
+
margin-bottom: layout.$spacing-05;
|
|
11
|
+
|
|
12
|
+
:global(.cds--breadcrumb-item) {
|
|
13
|
+
display: inline-flex;
|
|
14
|
+
align-items: baseline;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* The register name doubles as the page heading, so it stays an `h3` rather
|
|
19
|
+
than being repeated in a title row of its own. */
|
|
20
|
+
|
|
21
|
+
.title {
|
|
22
|
+
@include type.type-style('productive-heading-03');
|
|
23
|
+
display: inline;
|
|
24
|
+
color: $color-gray-100;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* Registers run to dozens of columns, so the table scrolls inside the page
|
|
28
|
+
instead of pushing the whole layout sideways. */
|
|
29
|
+
|
|
30
|
+
.tableScroll {
|
|
31
|
+
overflow-x: auto;
|
|
32
|
+
padding: layout.$spacing-05;
|
|
33
|
+
background-color: $ui-02;
|
|
34
|
+
border: 1px solid $ui-03;
|
|
35
|
+
border-top: none;
|
|
36
|
+
|
|
37
|
+
/* Each register carries its own table margins -- 1rem here, 0.5rem there,
|
|
38
|
+
0.75rem elsewhere -- which land inside this frame and give every register a
|
|
39
|
+
different gap to the border. The padding above is the spacing now, so those
|
|
40
|
+
are levelled out. */
|
|
41
|
+
|
|
42
|
+
table,
|
|
43
|
+
> div {
|
|
44
|
+
margin: 0;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
.empty {
|
|
50
|
+
display: flex;
|
|
51
|
+
flex-direction: column;
|
|
52
|
+
align-items: center;
|
|
53
|
+
gap: layout.$spacing-03;
|
|
54
|
+
padding: layout.$spacing-07 layout.$spacing-05;
|
|
55
|
+
text-align: center;
|
|
56
|
+
background-color: $ui-02;
|
|
57
|
+
border: 1px solid $ui-03;
|
|
58
|
+
border-top: none;
|
|
59
|
+
|
|
60
|
+
h4 {
|
|
61
|
+
@include type.type-style('productive-heading-02');
|
|
62
|
+
color: $color-gray-100;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
p {
|
|
66
|
+
@include type.type-style('body-long-01');
|
|
67
|
+
color: $text-02;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.emptyIcon {
|
|
72
|
+
fill: $ui-04;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* The caption and the table below it are one card: the bar carries the register
|
|
76
|
+
name, the period it covers and the export, and sits flush on whatever follows
|
|
77
|
+
-- skeleton, empty state or table -- so the page reads as a single object
|
|
78
|
+
rather than three stacked fragments. */
|
|
79
|
+
|
|
80
|
+
.context {
|
|
81
|
+
display: flex;
|
|
82
|
+
flex-wrap: wrap;
|
|
83
|
+
align-items: center;
|
|
84
|
+
justify-content: space-between;
|
|
85
|
+
gap: layout.$spacing-03 layout.$spacing-05;
|
|
86
|
+
padding: layout.$spacing-04 layout.$spacing-05;
|
|
87
|
+
background-color: $ui-02;
|
|
88
|
+
border: 1px solid $ui-03;
|
|
89
|
+
border-bottom: none;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Register name over the period it covers: side by side they made one long
|
|
93
|
+
line that crowded the action; stacked, the name leads and the period reads
|
|
94
|
+
as its caption. */
|
|
95
|
+
|
|
96
|
+
.summary {
|
|
97
|
+
display: flex;
|
|
98
|
+
flex-direction: column;
|
|
99
|
+
gap: layout.$spacing-02;
|
|
100
|
+
min-width: 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.summaryTitle {
|
|
104
|
+
@include type.type-style('body-short-01');
|
|
105
|
+
font-weight: 600;
|
|
106
|
+
color: $color-gray-100;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.period {
|
|
110
|
+
@include type.type-style('label-01');
|
|
111
|
+
display: inline-flex;
|
|
112
|
+
flex-wrap: wrap;
|
|
113
|
+
align-items: center;
|
|
114
|
+
gap: layout.$spacing-02;
|
|
115
|
+
color: $text-02;
|
|
116
|
+
|
|
117
|
+
svg {
|
|
118
|
+
fill: var(--brand-01);
|
|
119
|
+
transform: translateY(0.125rem);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* Location sits after the period as one unit, so on a narrow screen it wraps
|
|
124
|
+
to its own line whole rather than leaving its pin behind */
|
|
125
|
+
.location {
|
|
126
|
+
display: inline-flex;
|
|
127
|
+
align-items: center;
|
|
128
|
+
gap: layout.$spacing-02;
|
|
129
|
+
margin-left: layout.$spacing-03;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.export {
|
|
133
|
+
flex-shrink: 0;
|
|
134
|
+
margin-left: auto;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/* Sits directly under the table, completing the card */
|
|
138
|
+
|
|
139
|
+
.pagination {
|
|
140
|
+
border: 1px solid $ui-03;
|
|
141
|
+
border-top: none;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* A register rendered as a view on its report's own route has nowhere to
|
|
145
|
+
navigate, so the trail is a button and the register name beside it. */
|
|
146
|
+
|
|
147
|
+
.back {
|
|
148
|
+
padding-inline: 0.25rem;
|
|
149
|
+
min-block-size: auto;
|
|
150
|
+
}
|