@openmrs/esm-stock-management-app 1.0.1-pre.346 → 1.0.1-pre.350

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,319 @@
1
+ import React, { useCallback, useMemo } from "react";
2
+ import { useTranslation } from "react-i18next";
3
+ import {
4
+ DataTable,
5
+ DataTableSkeleton,
6
+ TabPanel,
7
+ Pagination,
8
+ Table,
9
+ TableBody,
10
+ TableCell,
11
+ TableContainer,
12
+ TableHead,
13
+ TableHeader,
14
+ TableRow,
15
+ TableToolbar,
16
+ TableToolbarContent,
17
+ TableToolbarSearch,
18
+ Tile,
19
+ Button,
20
+ } from "@carbon/react";
21
+ import { isDesktop } from "@openmrs/esm-framework";
22
+ import NewReportActionButton from "./new-report-button.component";
23
+ import styles from "./stock-reports.scss";
24
+ import { useGetReports } from "../stock-reports.resource";
25
+ import { URL_BATCH_JOB_ARTIFACT } from "../../constants";
26
+ import { formatDisplayDateTime } from "../../core/utils/datetimeUtils";
27
+ import {
28
+ BatchJobStatusCancelled,
29
+ BatchJobStatusCompleted,
30
+ BatchJobStatusExpired,
31
+ BatchJobStatusFailed,
32
+ BatchJobStatusPending,
33
+ BatchJobStatusRunning,
34
+ } from "../../core/api/types/BatchJob";
35
+ import {
36
+ CheckmarkOutline,
37
+ Copy,
38
+ Download,
39
+ IncompleteCancel,
40
+ MisuseOutline,
41
+ View,
42
+ WarningAltFilled,
43
+ } from "@carbon/react/icons";
44
+
45
+ const StockReports: React.FC = () => {
46
+ const { t } = useTranslation();
47
+ const {
48
+ reports,
49
+ isLoading,
50
+ currentPage,
51
+ pageSizes,
52
+ totalItems,
53
+ goTo,
54
+ currentPageSize,
55
+ setPageSize,
56
+ } = useGetReports();
57
+ const tableHeaders = useMemo(
58
+ () => [
59
+ {
60
+ id: 0,
61
+ header: t("report", "Report"),
62
+ key: "report",
63
+ },
64
+
65
+ {
66
+ id: 1,
67
+ header: t("parameters", "Parameters"),
68
+ key: "parameters",
69
+ },
70
+ {
71
+ id: 2,
72
+ header: t("dateRequested", "Date Requested"),
73
+ key: "dateRequested",
74
+ },
75
+ {
76
+ id: 3,
77
+ header: t("requestedBy", "Requested By"),
78
+ key: "requestedBy",
79
+ },
80
+ {
81
+ id: 4,
82
+ header: t("status", "Status"),
83
+ key: "status",
84
+ },
85
+ {
86
+ id: 8,
87
+ header: t("actions", "Actions"),
88
+ key: "actions",
89
+ },
90
+ ],
91
+ []
92
+ );
93
+
94
+ const onDownloadReportClick = useCallback(
95
+ (uuid: string, fileExit: string | undefined | null) => {
96
+ if (uuid) {
97
+ window.open(URL_BATCH_JOB_ARTIFACT(uuid, true), "_blank");
98
+ }
99
+ },
100
+ []
101
+ );
102
+
103
+ const tableRows = useMemo(() => {
104
+ return reports?.map((batchJob, index) => ({
105
+ ...batchJob,
106
+ checkbox: "isBatchJobActive",
107
+ id: batchJob?.uuid,
108
+ key: `key-${batchJob?.uuid}`,
109
+ uuid: `${batchJob?.uuid}`,
110
+ batchJobType: batchJob.batchJobType,
111
+ dateRequested: formatDisplayDateTime(batchJob.dateCreated),
112
+ parameters: "",
113
+ report: batchJob.description,
114
+ requestedBy: batchJob?.owners?.map((p, index) => (
115
+ <div
116
+ key={`${batchJob.uuid}-owner-${index}`}
117
+ >{`${p.ownerFamilyName} ${p.ownerGivenName}`}</div>
118
+ )),
119
+ status: (
120
+ <>
121
+ {batchJob.status === BatchJobStatusFailed && (
122
+ <WarningAltFilled
123
+ className="report-failed"
124
+ title={batchJob.status}
125
+ />
126
+ )}
127
+ {batchJob.status === BatchJobStatusCancelled && (
128
+ <MisuseOutline
129
+ className="report-cancelled"
130
+ title={batchJob.status}
131
+ />
132
+ )}
133
+ {batchJob.status === BatchJobStatusCompleted && (
134
+ <CheckmarkOutline
135
+ className="report-completed"
136
+ title={batchJob.status}
137
+ size={16}
138
+ />
139
+ )}
140
+ {batchJob.status === BatchJobStatusExpired && (
141
+ <IncompleteCancel
142
+ className="report-expired"
143
+ title={batchJob.status}
144
+ />
145
+ )}
146
+ </>
147
+ ),
148
+ actions: (
149
+ <div
150
+ key={`${batchJob?.uuid}-actions`}
151
+ style={{ display: "inline-block", whiteSpace: "nowrap" }}
152
+ >
153
+ {batchJob.outputArtifactViewable &&
154
+ batchJob.batchJobType === "hide" && (
155
+ <Button
156
+ key={`${batchJob?.uuid}-actions-view`}
157
+ type="button"
158
+ size="sm"
159
+ className="submitButton clear-padding-margin"
160
+ iconDescription={"Edit"}
161
+ kind="ghost"
162
+ renderIcon={View}
163
+ // onClick={(e) => onViewItem(batchJob.uuid, e)}
164
+ />
165
+ )}
166
+ <Button
167
+ type="button"
168
+ size="sm"
169
+ className="submitButton clear-padding-margin"
170
+ iconDescription={"Copy"}
171
+ kind="ghost"
172
+ renderIcon={Copy}
173
+ // onClick={() => onCloneReportClick(batchJob.uuid)}
174
+ />
175
+ {batchJob?.status === BatchJobStatusCompleted &&
176
+ (batchJob.outputArtifactSize ?? 0) > 0 && (
177
+ <Button
178
+ type="button"
179
+ size="sm"
180
+ className="submitButton clear-padding-margin"
181
+ iconDescription={"Download"}
182
+ kind="ghost"
183
+ renderIcon={Download}
184
+ onClick={() =>
185
+ onDownloadReportClick(
186
+ batchJob.uuid,
187
+ batchJob.outputArtifactFileExt
188
+ )
189
+ }
190
+ />
191
+ )}
192
+ </div>
193
+ ),
194
+ }));
195
+ }, [reports, onDownloadReportClick]);
196
+
197
+ if (isLoading) {
198
+ return <DataTableSkeleton role="progressbar" />;
199
+ }
200
+
201
+ return (
202
+ <div className={styles.tableOverride}>
203
+ <TabPanel>
204
+ {t("ReportDescription", "List of reports requested by users")}
205
+ </TabPanel>
206
+ <div id="table-tool-bar">
207
+ <div></div>
208
+ <div className="right-filters"></div>
209
+ </div>
210
+ <DataTable
211
+ rows={tableRows}
212
+ headers={tableHeaders}
213
+ isSortable={true}
214
+ useZebraStyles={true}
215
+ render={({
216
+ rows,
217
+ headers,
218
+ getHeaderProps,
219
+ getTableProps,
220
+ getRowProps,
221
+ onInputChange,
222
+ }) => (
223
+ <TableContainer>
224
+ <TableToolbar
225
+ style={{
226
+ position: "static",
227
+ overflow: "visible",
228
+ backgroundColor: "color",
229
+ }}
230
+ >
231
+ <TableToolbarContent className={styles.toolbarContent}>
232
+ <TableToolbarSearch persistent onChange={onInputChange} />
233
+ <NewReportActionButton />
234
+ </TableToolbarContent>
235
+ </TableToolbar>
236
+ <Table {...getTableProps()}>
237
+ <TableHead>
238
+ <TableRow>
239
+ {headers.map(
240
+ (header: any) =>
241
+ header.key !== "details" && (
242
+ <TableHeader
243
+ {...getHeaderProps({
244
+ header,
245
+ isSortable: header.isSortable,
246
+ })}
247
+ className={
248
+ isDesktop
249
+ ? styles.desktopHeader
250
+ : styles.tabletHeader
251
+ }
252
+ key={`${header.key}`}
253
+ >
254
+ {header.header?.content ?? header.header}
255
+ </TableHeader>
256
+ )
257
+ )}
258
+ <TableHeader></TableHeader>
259
+ </TableRow>
260
+ </TableHead>
261
+ <TableBody>
262
+ {rows.map((row: any) => {
263
+ return (
264
+ <React.Fragment key={row.id}>
265
+ <TableRow
266
+ className={
267
+ isDesktop ? styles.desktopRow : styles.tabletRow
268
+ }
269
+ {...getRowProps({ row })}
270
+ >
271
+ {row.cells.map(
272
+ (cell: any) =>
273
+ cell?.info?.header !== "details" && (
274
+ <TableCell key={cell.id}>{cell.value}</TableCell>
275
+ )
276
+ )}
277
+ </TableRow>
278
+ </React.Fragment>
279
+ );
280
+ })}
281
+ </TableBody>
282
+ </Table>
283
+ {rows.length === 0 ? (
284
+ <div className={styles.tileContainer}>
285
+ <Tile className={styles.tile}>
286
+ <div className={styles.tileContent}>
287
+ <p className={styles.content}>
288
+ {t("noReportsToDisplay", "No Stock reports to display")}
289
+ </p>
290
+ <p className={styles.helper}>
291
+ {t("checkFilters", "Check the filters above")}
292
+ </p>
293
+ </div>
294
+ </Tile>
295
+ </div>
296
+ ) : null}
297
+ </TableContainer>
298
+ )}
299
+ ></DataTable>
300
+ <Pagination
301
+ page={currentPage}
302
+ pageSize={currentPageSize}
303
+ pageSizes={pageSizes}
304
+ totalItems={totalItems}
305
+ onChange={({ pageSize, page }) => {
306
+ if (pageSize !== currentPageSize) {
307
+ setPageSize(pageSize);
308
+ }
309
+ if (page !== currentPage) {
310
+ goTo(page);
311
+ }
312
+ }}
313
+ className={styles.paginationOverride}
314
+ />
315
+ </div>
316
+ );
317
+ };
318
+
319
+ export default StockReports;
@@ -0,0 +1,61 @@
1
+ @use '@carbon/styles/scss/colors';
2
+ @use '@carbon/styles/scss/type';
3
+ @import '~@openmrs/esm-styleguide/src/vars';
4
+
5
+ .content {
6
+ @include type.type-style('heading-compact-02');
7
+ color: $text-02;
8
+ margin-bottom: 0.5rem;
9
+ }
10
+
11
+ .helper {
12
+ @include type.type-style('body-compact-01');
13
+ color: $text-02;
14
+ }
15
+
16
+ .separator {
17
+ @include type.type-style('body-compact-02');
18
+ color: $text-02;
19
+ width: 80%;
20
+ margin: 1.5rem auto;
21
+ overflow: hidden;
22
+ text-align: center;
23
+
24
+ &::before,
25
+ &::after {
26
+ background-color: $text-03;
27
+ content: '';
28
+ display: inline-block;
29
+ height: 1px;
30
+ position: relative;
31
+ vertical-align: middle;
32
+ width: 50%;
33
+ }
34
+
35
+ &::before {
36
+ right: 0.5rem;
37
+ margin-left: -50%;
38
+ }
39
+
40
+ &::after {
41
+ left: 0.5rem;
42
+ margin-right: -50%;
43
+ }
44
+ }
45
+
46
+ .tileContainer {
47
+ background-color: $ui-02;
48
+ border-top: 1px solid $ui-03;
49
+ padding: 5rem 0;
50
+ }
51
+
52
+ .tile {
53
+ margin: auto;
54
+ width: fit-content;
55
+ }
56
+
57
+ .tileContent {
58
+ display: flex;
59
+ flex-direction: column;
60
+ align-items: center;
61
+ }
@@ -0,0 +1,49 @@
1
+ import {
2
+ openmrsFetch,
3
+ restBaseUrl,
4
+ usePagination,
5
+ } from "@openmrs/esm-framework";
6
+ import useSWR from "swr";
7
+ import { ReportType } from "./ReportType";
8
+ import { useState } from "react";
9
+
10
+ export function useReportTypes() {
11
+ const apiUrl = `${restBaseUrl}/stockmanagement/report?v=default`;
12
+ const { data, error, isLoading } = useSWR<
13
+ { data: { results: ReportType } },
14
+ Error
15
+ >(apiUrl, openmrsFetch);
16
+ return {
17
+ reportTypes: data?.data?.results ?? [],
18
+ isLoading,
19
+ isError: error,
20
+ };
21
+ }
22
+ export function useGetReports() {
23
+ const apiUrl = `${restBaseUrl}/stockmanagement/batchjob?batchJobType=Report&v=default&limit=10&totalCount=true`;
24
+ const { data, error, isLoading } = useSWR<{ data: { results: any } }, Error>(
25
+ apiUrl,
26
+ openmrsFetch
27
+ );
28
+ const pageSizes = [10, 20, 30, 40, 50];
29
+ const [currentPageSize, setPageSize] = useState(10);
30
+
31
+ const {
32
+ goTo,
33
+ results: paginatedItems,
34
+ currentPage,
35
+ } = usePagination(data?.data?.results, currentPageSize);
36
+ return {
37
+ reports: data?.data?.results ?? [],
38
+ isLoading,
39
+ isError: error,
40
+ items: paginatedItems,
41
+ totalItems: data?.data?.results?.length,
42
+ currentPage,
43
+ currentPageSize,
44
+ paginatedItems,
45
+ goTo,
46
+ pageSizes,
47
+ setPageSize,
48
+ };
49
+ }