From 77b0a25aa743cd37d074af96d2bf6d52577dc6a8 Mon Sep 17 00:00:00 2001 From: Laurent Ouma Date: Fri, 2 Aug 2024 15:51:54 +0300 Subject: [PATCH 1/7] Manifest dashboard summary done --- .../src/component/lab-manifest.component.tsx | 2 +- .../header/lab-manifest-header.component.tsx | 2 +- .../src/hooks/useLabManifestAggregates.ts | 28 ++++++++ .../lab-manifest-metric-card.component.tsx | 33 ++++++++++ ...metric-status-aggegate-value.component.tsx | 11 ++++ .../lab-manifest-metric-value.component.tsx | 11 ++-- .../lab-manifest-metrics-header.component.tsx | 34 ---------- .../lab-manifest-metrics.component.tsx | 64 ++++++++++++++++--- .../src/metrics/lab-manifest-metrics.scss | 47 +++++++++++++- 9 files changed, 179 insertions(+), 53 deletions(-) create mode 100644 packages/esm-lab-manifest-app/src/hooks/useLabManifestAggregates.ts create mode 100644 packages/esm-lab-manifest-app/src/metrics/lab-manifest-metric-card.component.tsx create mode 100644 packages/esm-lab-manifest-app/src/metrics/lab-manifest-metric-status-aggegate-value.component.tsx delete mode 100644 packages/esm-lab-manifest-app/src/metrics/lab-manifest-metrics-header.component.tsx diff --git a/packages/esm-lab-manifest-app/src/component/lab-manifest.component.tsx b/packages/esm-lab-manifest-app/src/component/lab-manifest.component.tsx index 2df4649c0..308bdc8ee 100644 --- a/packages/esm-lab-manifest-app/src/component/lab-manifest.component.tsx +++ b/packages/esm-lab-manifest-app/src/component/lab-manifest.component.tsx @@ -8,7 +8,7 @@ const LabManifestComponent: React.FC = () => { const { t } = useTranslation(); return (
- +
diff --git a/packages/esm-lab-manifest-app/src/header/lab-manifest-header.component.tsx b/packages/esm-lab-manifest-app/src/header/lab-manifest-header.component.tsx index 484ae4d59..d356a8b8d 100644 --- a/packages/esm-lab-manifest-app/src/header/lab-manifest-header.component.tsx +++ b/packages/esm-lab-manifest-app/src/header/lab-manifest-header.component.tsx @@ -18,7 +18,7 @@ export const LabManifestHeader: React.FC = ({ title }) =
-

{t('labManifest', 'Lab manifest Management')}

+

{t('labManifest', 'Lab Manifest')}

{title}

diff --git a/packages/esm-lab-manifest-app/src/hooks/useLabManifestAggregates.ts b/packages/esm-lab-manifest-app/src/hooks/useLabManifestAggregates.ts new file mode 100644 index 000000000..523f007ad --- /dev/null +++ b/packages/esm-lab-manifest-app/src/hooks/useLabManifestAggregates.ts @@ -0,0 +1,28 @@ +import { FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework'; +import useSWR from 'swr'; +import { extractLabManifest } from '../lab-manifest.resources'; +import { LabManifest } from '../types'; +const fetchLabManifestsByStatus = async (status: string) => { + const url = `${restBaseUrl}/labmanifest?v=full&status=${status}`; + const response = await openmrsFetch<{ results: Array }>(url); + return response?.data?.results.map(extractLabManifest); +}; + +// TODO -> oPTIOMOZE BY CACHING AND RETREVING FROM CACHE +const useLabManifestAggregates = (statuses: Array) => { + const { data, error, isValidating } = useSWR( + statuses.length ? `labmanifest-aggregate-${statuses.join(',')}` : null, + async () => { + const results = await Promise.all(statuses.map((status) => fetchLabManifestsByStatus(status))); + return results.flat(); + }, + ); + + return { + isLoading: isValidating, + manifests: data || [], + error, + }; +}; + +export default useLabManifestAggregates; diff --git a/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metric-card.component.tsx b/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metric-card.component.tsx new file mode 100644 index 000000000..41228bb54 --- /dev/null +++ b/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metric-card.component.tsx @@ -0,0 +1,33 @@ +import { Column, Layer, Row } from '@carbon/react'; +import React from 'react'; +import useLabManifestAggregates from '../hooks/useLabManifestAggregates'; +import LabManifestMetricValue from './lab-manifest-metric-value.component'; +import styles from './lab-manifest-metrics.scss'; + +interface MetricCardProps { + title: string; + status?: Array<{ status: string; color?: string }>; +} + +const MetricCard: React.FC = ({ title, status = [] }) => { + const { isLoading, error, manifests } = useLabManifestAggregates(status.map((s) => s.status)); + return ( + +

{title}

+ + +

{manifests?.length}

+
+ + + {status.map(({ status, color }, index) => ( + + ))} + + +
+
+ ); +}; + +export default MetricCard; diff --git a/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metric-status-aggegate-value.component.tsx b/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metric-status-aggegate-value.component.tsx new file mode 100644 index 000000000..d45dacb99 --- /dev/null +++ b/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metric-status-aggegate-value.component.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +type LabManifestStatusAggregateValueProps = { + status?: Array; +}; + +const LabManifestStatusAggregateValue: React.FC = ({ status = [] }) => { + return
LabManifestStatusAggregateValue
; +}; + +export default LabManifestStatusAggregateValue; diff --git a/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metric-value.component.tsx b/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metric-value.component.tsx index 786b7d963..a819992d9 100644 --- a/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metric-value.component.tsx +++ b/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metric-value.component.tsx @@ -1,23 +1,26 @@ +import { SkeletonText } from '@carbon/react'; import React from 'react'; import { useLabManifests } from '../hooks'; -import { SkeletonText } from '@carbon/react'; import styles from './lab-manifest-metrics.scss'; interface LabManifestMetricValueProps { status: string; + color?: string; } -const LabManifestMetricValue: React.FC = ({ status }) => { +const LabManifestMetricValue: React.FC = ({ status, color }) => { const { error, isLoading, manifests } = useLabManifests(status); if (isLoading) { - return ; + return ; } if (error) { return; } return ( - {status}: {manifests.length} + {status}
+
+ {manifests?.length ?? '--'}
); }; diff --git a/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metrics-header.component.tsx b/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metrics-header.component.tsx deleted file mode 100644 index a52095a5c..000000000 --- a/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metrics-header.component.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { Button } from '@carbon/react'; -import { ArrowRight } from '@carbon/react/icons'; -import { launchWorkspace } from '@openmrs/esm-framework'; -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import styles from './lab-manifest-header.scss'; - -const MetricsHeader = () => { - const { t } = useTranslation(); - const metricsTitle = t('labManifestSummary', 'Lab Manifest Summary'); - - const handleAddLabManifest = () => { - launchWorkspace('lab-manifest-form', { - workspaceTitle: 'Lab Manifest Form', - }); - }; - - return ( -
- {metricsTitle} -
- -
-
- ); -}; - -export default MetricsHeader; diff --git a/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metrics.component.tsx b/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metrics.component.tsx index fdb055db6..0459d27d5 100644 --- a/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metrics.component.tsx +++ b/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metrics.component.tsx @@ -1,9 +1,12 @@ +import { Button, Layer } from '@carbon/react'; +import { ArrowRight } from '@carbon/react/icons'; +import { launchWorkspace } from '@openmrs/esm-framework'; import React from 'react'; import { useTranslation } from 'react-i18next'; import { LabManifestFilters } from '../lab-manifest.resources'; -import MetricsHeader from './lab-manifest-metrics-header.component'; -import styles from './lab-manifest-metrics.scss'; import LabManifestMetricValue from './lab-manifest-metric-value.component'; +import styles from './lab-manifest-metrics.scss'; +import MetricCard from './lab-manifest-metric-card.component'; export interface Service { uuid: string; @@ -12,15 +15,56 @@ export interface Service { function LabManifestMetrics() { const { t } = useTranslation(); + + const handleAddLabManifest = () => { + launchWorkspace('lab-manifest-form', { + workspaceTitle: 'Lab Manifest Form', + }); + }; + return ( - <> - -
- {LabManifestFilters.map((f, index) => ( - - ))} -
- + + + + + + + + + + + ); } diff --git a/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metrics.scss b/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metrics.scss index 0ba17571f..e3f410f1d 100644 --- a/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metrics.scss +++ b/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metrics.scss @@ -1,17 +1,58 @@ @use '@carbon/styles/scss/spacing'; @use '@carbon/colors'; +@use '@carbon/styles/scss/type'; @import '~@openmrs/esm-styleguide/src/vars'; .cardContainer { - background-color: $color-gray-30; display: flex; - justify-content: space-between; - padding: spacing.$spacing-05; flex-flow: row wrap; margin-top: spacing.$spacing-05; gap: spacing.$spacing-05; } .metricContainer { + padding: spacing.$spacing-05; +} + +.labManifestStatusMetricValue { max-width: spacing.$spacing-13; } + +.btnLayer { + display: flex; + margin-top: spacing.$spacing-05; + margin-bottom: spacing.$spacing-05; + flex-direction: row; + justify-content: flex-end; + width: 100%; +} + +.metricCardContainer { + border: $color-gray-30 solid spacing.$spacing-01; + padding: spacing.$spacing-03; + flex-grow: 1; + & > p { + @include type.type-style('heading-02'); + margin-bottom: spacing.$spacing-03; + } +} + +.metricCardStatusRow { + display: flex; + flex-flow: row wrap; + gap: spacing.$spacing-05; +} + +.metricCardRow { + display: flex; + flex-flow: row wrap; + gap: spacing.$spacing-05; + justify-content: space-between; + padding-top: spacing.$spacing-05; + align-items: center; +} + +.metricCardAgregateValue { + @include type.type-style('heading-05'); + font-weight: var(--cds-heading-05-font-weight, 100); +} From ae8657d968bdb8df8ff58ecadf1af741f93e97e3 Mon Sep 17 00:00:00 2001 From: Laurent Ouma Date: Mon, 5 Aug 2024 07:12:50 +0300 Subject: [PATCH 2/7] Lab manifest listing ui adaptation done --- .../esm-lab-manifest-app/src/config-schema.ts | 6 +-- .../lab-manifest-metrics.component.tsx | 4 +- .../tables/lab-manifest-table.component.tsx | 43 ++++++++++++++----- .../src/tables/lab-manifest-table.scss | 10 +++++ 4 files changed, 47 insertions(+), 16 deletions(-) diff --git a/packages/esm-lab-manifest-app/src/config-schema.ts b/packages/esm-lab-manifest-app/src/config-schema.ts index 24b129179..2636d7175 100644 --- a/packages/esm-lab-manifest-app/src/config-schema.ts +++ b/packages/esm-lab-manifest-app/src/config-schema.ts @@ -7,15 +7,15 @@ export const configSchema = { _default: [ { id: 1, - type: 'EID Type', + type: 'EID', }, { id: 2, - type: 'VL Type', + type: 'VL', }, { id: 3, - type: 'FLU Type', + type: 'FLU', }, ], }, diff --git a/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metrics.component.tsx b/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metrics.component.tsx index 0459d27d5..fef98065b 100644 --- a/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metrics.component.tsx +++ b/packages/esm-lab-manifest-app/src/metrics/lab-manifest-metrics.component.tsx @@ -3,10 +3,8 @@ import { ArrowRight } from '@carbon/react/icons'; import { launchWorkspace } from '@openmrs/esm-framework'; import React from 'react'; import { useTranslation } from 'react-i18next'; -import { LabManifestFilters } from '../lab-manifest.resources'; -import LabManifestMetricValue from './lab-manifest-metric-value.component'; -import styles from './lab-manifest-metrics.scss'; import MetricCard from './lab-manifest-metric-card.component'; +import styles from './lab-manifest-metrics.scss'; export interface Service { uuid: string; diff --git a/packages/esm-lab-manifest-app/src/tables/lab-manifest-table.component.tsx b/packages/esm-lab-manifest-app/src/tables/lab-manifest-table.component.tsx index 3ad4b7228..382c7c601 100644 --- a/packages/esm-lab-manifest-app/src/tables/lab-manifest-table.component.tsx +++ b/packages/esm-lab-manifest-app/src/tables/lab-manifest-table.component.tsx @@ -1,5 +1,6 @@ import { Button, + ButtonSet, DataTable, DataTableSkeleton, Dropdown, @@ -14,21 +15,25 @@ import { TableRow, Tile, } from '@carbon/react'; -import { View } from '@carbon/react/icons'; +import { Edit, View } from '@carbon/react/icons'; import { ErrorState, formatDate, isDesktop, + launchWorkspace, navigate, parseDate, + useConfig, useLayoutType, usePagination, } from '@openmrs/esm-framework'; import { CardHeader, EmptyDataIllustration, usePaginationInfo } from '@openmrs/esm-patient-common-lib'; import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { LabManifestConfig } from '../config-schema'; import { useLabManifests } from '../hooks'; import { LabManifestFilters } from '../lab-manifest.resources'; +import { MappedLabManifest } from '../types'; import styles from './lab-manifest-table.scss'; const LabManifestsTable = () => { @@ -40,7 +45,7 @@ const LabManifestsTable = () => { const { manifests, error, isLoading } = useLabManifests(currFilter); const { results, totalPages, currentPage, goTo } = usePagination(manifests, pageSize); const { pageSizes } = usePaginationInfo(pageSize, totalPages, currentPage, results.length); - + const { labmanifestTypes } = useConfig(); const headers = [ { header: t('startDate', 'Start date'), @@ -88,6 +93,13 @@ const LabManifestsTable = () => { navigate({ to: window.getOpenmrsSpaBase() + `home/lab-manifest/${manifestUuid}` }); }; + const handleEditManifest = (manifest: MappedLabManifest) => { + launchWorkspace('lab-manifest-form', { + workspaceTitle: 'Lab Manifest Form', + manifest, + }); + }; + const tableRows = results?.map((manifest) => { return { @@ -96,19 +108,30 @@ const LabManifestsTable = () => { endDate: manifest.endDate ? formatDate(parseDate(manifest.endDate)) : '--', courrier: manifest.courierName ? manifest.courierName : '--', labPersonContact: manifest.labPersonContact ?? '--', - type: manifest.manifestType ?? '--', + type: labmanifestTypes.find((type) => `${type.id}` === manifest?.manifestType)?.type ?? '--', status: manifest.manifestStatus ?? '--', dispatch: manifest.dispatchDate ? formatDate(parseDate(manifest.dispatchDate)) : '--', manifestId: manifest.manifestId ?? '--', samples: `${manifest.samples.length}`, actions: ( - - - + ); }; diff --git a/packages/esm-lab-manifest-app/src/header/lab-manifest-header.scss b/packages/esm-lab-manifest-app/src/header/lab-manifest-header.scss index 6931821f9..c082a318d 100644 --- a/packages/esm-lab-manifest-app/src/header/lab-manifest-header.scss +++ b/packages/esm-lab-manifest-app/src/header/lab-manifest-header.scss @@ -99,7 +99,36 @@ svg.iconOverrides { .btnSet { display: flex; justify-content: space-between; - padding: spacing.$spacing-05; + margin-top: spacing.$spacing-05; + margin-bottom: spacing.$spacing-05; + + flex-flow: row wrap; + align-items: center; +} + +.detailHeaderContainer { + margin: spacing.$spacing-05; + flex-flow: row wrap; + justify-content: space-between; +} + +.detailHeaderContent { + background-color: $color-gray-30; + width: fit-content; +} + +.detailHeaderContentLoading { + background-color: $color-gray-30; + max-width: 70%; +} + +.detailHeaderContentRow { + display: flex; flex-flow: row wrap; + gap: spacing.$spacing-08; align-items: center; } + +.samplesCountValue { + @include type.type-style('heading-04'); +} diff --git a/packages/esm-lab-manifest-app/translations/en.json b/packages/esm-lab-manifest-app/translations/en.json index b6a5bae82..d8d33d9a0 100644 --- a/packages/esm-lab-manifest-app/translations/en.json +++ b/packages/esm-lab-manifest-app/translations/en.json @@ -2,18 +2,21 @@ "actions": "Actions", "activeRequests": "Active Requests", "addNewManifest": "Add new Manifest", + "addSelectedSamples": "Add Selected Samples", + "addToManifest": "Add To manifest", "back": "Back", "batchNumber": "Batch Number", "cccKDODNumber": "CCC/KDOD Number", "courrier": "Courrier", "dateRequested": "Date Requested", "dispatch": "Dispatch", + "edit": "Edit", "editManifest": "Edit Manifest", "endDate": "End Date", "lab Manifest": "Lab Manifest", "labManifest": "Lab manifest Management", + "labManifestDashboard": "Lab Manifest Dashboard", "labManifestSamples": "Lab Manifest Samples", - "labManifestSummary": "Lab Manifest Summary", "labPersonContact": "Lab person Contact", "manifestId": "Manifest Id", "manifestSamples": "Manifest samples", diff --git a/packages/esm-patient-clinical-view-app/translations/en.json b/packages/esm-patient-clinical-view-app/translations/en.json index b031ccb59..e2ad1adc9 100644 --- a/packages/esm-patient-clinical-view-app/translations/en.json +++ b/packages/esm-patient-clinical-view-app/translations/en.json @@ -80,6 +80,7 @@ "htsScreening": "HTS Screening", "htsTestType": "Test type", "inPatientSummary": "In Patient Summary", + "ipvOutcome": "IPV Outcome", "labourAndDelivery": "Labour and Delivery", "listingDate": "Listing date", "livingWithClient": "Living with client", From 873d4abd5ca930940057280d6e7b03c4f63c5628 Mon Sep 17 00:00:00 2001 From: Laurent Ouma Date: Mon, 5 Aug 2024 09:18:20 +0300 Subject: [PATCH 4/7] Delete dialog refactored to have samples table for thos selected --- .../sample-delete-confirm-dialog.modal.tsx | 116 +++++++++++++++- .../src/lab-manifest.resources.ts | 12 ++ .../tables/lab-manifest-samples.component.tsx | 124 +++++++++++------- .../src/tables/lab-manifest-table.scss | 25 ++++ 4 files changed, 220 insertions(+), 57 deletions(-) diff --git a/packages/esm-lab-manifest-app/src/forms/sample-delete-confirm-dialog.modal.tsx b/packages/esm-lab-manifest-app/src/forms/sample-delete-confirm-dialog.modal.tsx index 29bf3d33e..f0a54d2d0 100644 --- a/packages/esm-lab-manifest-app/src/forms/sample-delete-confirm-dialog.modal.tsx +++ b/packages/esm-lab-manifest-app/src/forms/sample-delete-confirm-dialog.modal.tsx @@ -1,24 +1,126 @@ -import { Button, ButtonSet, ModalBody, ModalFooter, ModalHeader } from '@carbon/react'; +import { + Button, + ButtonSet, + DataTable, + ModalBody, + ModalFooter, + ModalHeader, + Stack, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableHeader, + TableRow, +} from '@carbon/react'; +import { formatDate, parseDate } from '@openmrs/esm-framework'; import React from 'react'; import { useTranslation } from 'react-i18next'; +import styles from '../tables/lab-manifest-table.scss'; +import { LabManifestSample } from '../types'; interface SampleDeleteConfirmDialogProps { onClose: () => void; onDelete: () => void; + samples?: Array; } -const SampleDeleteConfirmDialog: React.FC = ({ onClose, onDelete }) => { +const SampleDeleteConfirmDialog: React.FC = ({ onClose, onDelete, samples = [] }) => { const { t } = useTranslation(); + const headers = [ + { + header: t('patientIdentifier', 'Patient Identifier'), + key: 'cccKDODNumber', + }, + { + header: t('batchNumber', 'Batch Number'), + key: 'batchNumber', + }, + { + header: t('sampleType', 'Sample type'), + key: 'sampleType', + }, + { + header: t('dateRequested', 'Date Requested'), + key: 'dateRequested', + }, + { + header: t('resultDate', 'Result Date'), + key: 'resultDate', + }, + { + header: t('result', 'Result'), + key: 'result', + }, + ]; + + const tableRows = + samples.map((sample) => { + return { + id: `${sample.uuid}`, + sampleType: sample.sampleType ?? '--', + status: sample.status, + batchNumber: sample.batchNumber ?? '--', + cccKDODNumber: sample?.order?.patient?.identifiers[0]?.identifier ?? '--', + dateRequested: sample.dateSent ? formatDate(parseDate(sample.dateSent)) : '--', + resultDate: sample.resultDate ? formatDate(parseDate(sample.resultDate)) : '--', + result: sample.result ?? '--', + }; + }) ?? []; + return ( - {t('warning', 'Warning!')} - Are you sure you want to delete sample.This action is irriversible? + + {t('removeSampledFromManifest', 'Remove Samples from Manifest')} + + + + + Are you sure yu would like to remove bellow samples from the manifest? + +
+ ( + <> + + + + + {headers.map((header, i) => ( + + {header.header} + + ))} + + + + {rows.map((row, i) => ( + {}}> + {row.cells.map((cell) => ( + {cell.value} + ))} + + ))} + +
+
+ + )} + /> +
+
+
- - - diff --git a/packages/esm-lab-manifest-app/src/lab-manifest.resources.ts b/packages/esm-lab-manifest-app/src/lab-manifest.resources.ts index f769cf759..9afcdd52d 100644 --- a/packages/esm-lab-manifest-app/src/lab-manifest.resources.ts +++ b/packages/esm-lab-manifest-app/src/lab-manifest.resources.ts @@ -1,6 +1,7 @@ import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework'; import { z } from 'zod'; import { LabManifest, MappedLabManifest } from './types'; +import { mutate } from 'swr'; export const LabManifestFilters = [ { @@ -118,6 +119,17 @@ export const addOrderToManifest = async (data: z.infer { + const mutateLinks = [ + `/ws/rest/v1/labmanifest?v=full&status=${manifestStatus}`, + `/ws/rest/v1/kemrorder/validorders?manifestUuid=${manifestUuid}`, + `/ws/rest/v1/labmanifest/${manifestUuid}`, + ]; + mutate((key) => { + return typeof key === 'string' && mutateLinks.some((link) => key.startsWith(link)); + }); +}; + export const removeSampleFromTheManifest = async (orderUuid: string) => { let url = `${restBaseUrl}/labmanifestorder/${orderUuid}`; const abortController = new AbortController(); diff --git a/packages/esm-lab-manifest-app/src/tables/lab-manifest-samples.component.tsx b/packages/esm-lab-manifest-app/src/tables/lab-manifest-samples.component.tsx index 85f8856b6..46739b456 100644 --- a/packages/esm-lab-manifest-app/src/tables/lab-manifest-samples.component.tsx +++ b/packages/esm-lab-manifest-app/src/tables/lab-manifest-samples.component.tsx @@ -10,26 +10,18 @@ import { TableHead, TableHeader, TableRow, + TableSelectAll, + TableSelectRow, } from '@carbon/react'; -import { TrashCan, View } from '@carbon/react/icons'; -import { - ErrorState, - formatDate, - navigate, - parseDate, - showModal, - showSnackbar, - useLayoutType, - usePagination, -} from '@openmrs/esm-framework'; +import { TrashCan } from '@carbon/react/icons'; +import { ErrorState, formatDate, parseDate, showModal, showSnackbar, usePagination } from '@openmrs/esm-framework'; import { CardHeader, EmptyState, usePaginationInfo } from '@openmrs/esm-patient-common-lib'; import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; -import styles from './lab-manifest-table.scss'; import { useLabManifest } from '../hooks'; +import { mutateManifestLinks, removeSampleFromTheManifest } from '../lab-manifest.resources'; import { LabManifestSample } from '../types'; -import { removeSampleFromTheManifest } from '../lab-manifest.resources'; -import { mutate } from 'swr'; +import styles from './lab-manifest-table.scss'; interface LabManifestSamplesProps { manifestUuid: string; @@ -37,7 +29,7 @@ interface LabManifestSamplesProps { const LabManifestSamples: React.FC = ({ manifestUuid }) => { const { error, isLoading, manifest } = useLabManifest(manifestUuid); - const samples = manifest?.samples ?? []; + const samples: Array = manifest?.samples ?? []; const { t } = useTranslation(); const [pageSize, setPageSize] = useState(10); const headerTitle = t('labManifestSamples', 'Lab Manifest Samples'); @@ -82,18 +74,11 @@ const LabManifestSamples: React.FC = ({ manifestUuid }) const handleDeleteManifestSample = (sampleUUid: string) => { const dispose = showModal('sample-delete-confirm-dialog', { onClose: () => dispose(), + samples: samples.filter((s) => s.uuid === sampleUUid), onDelete: async () => { try { await removeSampleFromTheManifest(sampleUUid); - const mutateLinks = [ - `/ws/rest/v1/labmanifest?v=full&status=${manifest.manifestStatus}`, - `/ws/rest/v1/kemrorder/validorders?manifestUuid=${manifest.uuid}`, - `/ws/rest/v1/labmanifest/${manifest?.uuid}`, - ]; - mutate((key) => { - return typeof key === 'string' && mutateLinks.some((link) => key.startsWith(link)); - }); - + mutateManifestLinks(manifest?.uuid, manifest?.manifestStatus); dispose(); showSnackbar({ title: 'Success', kind: 'success', subtitle: 'Sample removed from manifest successfully!' }); } catch (e) { @@ -103,6 +88,25 @@ const LabManifestSamples: React.FC = ({ manifestUuid }) }); }; + const handleDeleteSelectedSamples = (selected: Array) => { + if (selected.length > 0) { + const dispose = showModal('sample-delete-confirm-dialog', { + onClose: () => dispose(), + samples: selected, + onDelete: async () => { + try { + const deleteMany = await Promise.allSettled(selected.map(({ uuid }) => removeSampleFromTheManifest(uuid))); + mutateManifestLinks(manifest?.uuid, manifest?.manifestStatus); + dispose(); + showSnackbar({ title: 'Success', kind: 'success', subtitle: 'Sample removed from manifest successfully!' }); + } catch (e) { + showSnackbar({ title: 'Failure', kind: 'error', subtitle: 'Error removing sample from the manifest' }); + } + }, + }); + } + }; + const tableRows = (results as LabManifestSample[])?.map((sample) => { return { @@ -143,41 +147,61 @@ const LabManifestSamples: React.FC = ({ manifestUuid }) } return (
- {''} ( - - - - - {headers.map((header) => ( - - {header.header?.content ?? header.header} - - ))} - - - - {rows.map((row) => ( - - {row.cells.map((cell) => ( - {cell.value} + render={({ + rows, + headers, + getHeaderProps, + getRowProps, + getSelectionProps, + getTableProps, + getTableContainerProps, + selectedRows, + }) => ( + <> + + + + +
+ + + + {headers.map((header, i) => ( + + {header.header} + ))} - ))} - -
-
+ + + {rows.map((row, i) => ( + {}}> + + {row.cells.map((cell) => ( + {cell.value} + ))} + + ))} + + + + )} /> + Date: Mon, 5 Aug 2024 09:53:23 +0300 Subject: [PATCH 5/7] Previewing selected orders to be added to manifest in the sample dialog form --- .../forms/active-order-selection-preview.tsx | 86 +++++++++++++++++++ .../src/forms/lab-manifest-form.scss | 5 ++ .../lab-manifest-orders-to-manifest.modal.tsx | 19 ++-- .../src/hooks/useActiveRequests.ts | 5 +- .../src/lab-manifest.mock.ts | 5 -- ...lab-manifest-active-requests.component.tsx | 1 + .../esm-lab-manifest-app/src/types/index.ts | 26 +++--- .../esm-lab-manifest-app/translations/en.json | 1 + 8 files changed, 121 insertions(+), 27 deletions(-) create mode 100644 packages/esm-lab-manifest-app/src/forms/active-order-selection-preview.tsx delete mode 100644 packages/esm-lab-manifest-app/src/lab-manifest.mock.ts diff --git a/packages/esm-lab-manifest-app/src/forms/active-order-selection-preview.tsx b/packages/esm-lab-manifest-app/src/forms/active-order-selection-preview.tsx new file mode 100644 index 000000000..42083fe91 --- /dev/null +++ b/packages/esm-lab-manifest-app/src/forms/active-order-selection-preview.tsx @@ -0,0 +1,86 @@ +import React from 'react'; + +import { + DataTable, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableHeader, + TableRow, +} from '@carbon/react'; +import { useTranslation } from 'react-i18next'; +import styles from '../tables/lab-manifest-table.scss'; +import { ActiveRequestOrder } from '../types'; + +interface ActiveOrdersSelectionPreviewProps { + orders?: Array; +} + +const ActiveOrdersSelectionPreview: React.FC = ({ orders = [] }) => { + const { t } = useTranslation(); + + const headers = [ + { + header: t('patientName', 'Patient name'), + key: 'patientName', + }, + { + header: t('cccKDODNumber', 'CCC/KDOD Number'), + key: 'cccKdod', + }, + { + header: t('dateRequested', 'Date Requested'), + key: 'dateRequested', + }, + ]; + + const tableRows = + orders?.map((activeRequest) => { + return { + id: `${activeRequest.orderUuid}`, + patientName: activeRequest.patientName, + cccKdod: activeRequest.cccKdod, + dateRequested: activeRequest.dateRequested, + }; + }) ?? []; + return ( +
+ ( + <> + + + + + {headers.map((header, i) => ( + + {header.header} + + ))} + + + + {rows.map((row, i) => ( + {}}> + {row.cells.map((cell) => ( + {cell.value} + ))} + + ))} + +
+
+ + )} + /> +
+ ); +}; + +export default ActiveOrdersSelectionPreview; diff --git a/packages/esm-lab-manifest-app/src/forms/lab-manifest-form.scss b/packages/esm-lab-manifest-app/src/forms/lab-manifest-form.scss index 2b511513f..c252e85f2 100644 --- a/packages/esm-lab-manifest-app/src/forms/lab-manifest-form.scss +++ b/packages/esm-lab-manifest-app/src/forms/lab-manifest-form.scss @@ -121,3 +121,8 @@ gap: spacing.$spacing-05; } } + +.previewContainer { + margin-top: spacing.$spacing-05; + margin-bottom: spacing.$spacing-05; +} diff --git a/packages/esm-lab-manifest-app/src/forms/lab-manifest-orders-to-manifest.modal.tsx b/packages/esm-lab-manifest-app/src/forms/lab-manifest-orders-to-manifest.modal.tsx index 68a8c15cc..84d34497a 100644 --- a/packages/esm-lab-manifest-app/src/forms/lab-manifest-orders-to-manifest.modal.tsx +++ b/packages/esm-lab-manifest-app/src/forms/lab-manifest-orders-to-manifest.modal.tsx @@ -19,14 +19,16 @@ import { Controller, useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { mutate } from 'swr'; import { z } from 'zod'; +import { useLabManifest } from '../hooks'; import { addOrderToManifest, labManifestOrderToManifestFormSchema, sampleTypes } from '../lab-manifest.resources'; +import { ActiveRequestOrder } from '../types'; +import ActiveOrdersSelectionPreview from './active-order-selection-preview'; import styles from './lab-manifest-form.scss'; -import { useLabManifest } from '../hooks'; interface LabManifestOrdersToManifestFormProps { onClose: () => void; props: { - title?: string; + orders?: Array; selectedOrders: Array<{ labManifest: { uuid: string; @@ -43,7 +45,7 @@ type OrderToManifestFormType = z.infer = ({ onClose, - props: { title, selectedOrders }, + props: { selectedOrders, orders }, }) => { const { t } = useTranslation(); const form = useForm({ @@ -82,7 +84,7 @@ const LabManifestOrdersToManifestForm: React.FC
- {title ?? t('updateSampleDetails', 'Update Sample Details')} + {t('updateSampleDetails', 'Update Sample Details')} @@ -154,16 +156,19 @@ const LabManifestOrdersToManifestForm: React.FC +
+ +
- +
diff --git a/packages/esm-lab-manifest-app/src/hooks/useActiveRequests.ts b/packages/esm-lab-manifest-app/src/hooks/useActiveRequests.ts index 2a11f3ccd..c41dfbb41 100644 --- a/packages/esm-lab-manifest-app/src/hooks/useActiveRequests.ts +++ b/packages/esm-lab-manifest-app/src/hooks/useActiveRequests.ts @@ -1,11 +1,10 @@ import { FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework'; import useSWR from 'swr'; -import { activeRequests } from '../lab-manifest.mock'; -import { ActiveRequests } from '../types'; +import { ActiveRequest } from '../types'; const useActiveRequests = (labManifestUuid: string) => { const url = `${restBaseUrl}/kemrorder/validorders?manifestUuid=${labManifestUuid}`; - const { isLoading, error, data } = useSWR>(url, openmrsFetch); + const { isLoading, error, data } = useSWR>(url, openmrsFetch); return { isLoading, diff --git a/packages/esm-lab-manifest-app/src/lab-manifest.mock.ts b/packages/esm-lab-manifest-app/src/lab-manifest.mock.ts deleted file mode 100644 index 3298ebd0a..000000000 --- a/packages/esm-lab-manifest-app/src/lab-manifest.mock.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { ActiveRequests, MappedLabManifest, LabManifestSample } from './types'; - -export const labManifestSamples: Array = []; - -export const activeRequests: Array = []; diff --git a/packages/esm-lab-manifest-app/src/tables/lab-manifest-active-requests.component.tsx b/packages/esm-lab-manifest-app/src/tables/lab-manifest-active-requests.component.tsx index f0708f6d0..5ccfa46a1 100644 --- a/packages/esm-lab-manifest-app/src/tables/lab-manifest-active-requests.component.tsx +++ b/packages/esm-lab-manifest-app/src/tables/lab-manifest-active-requests.component.tsx @@ -69,6 +69,7 @@ const LabManifestActiveRequests: React.FC = ({ m props: { title: selected.length > 1 ? 'Add Multiple Orders To Manifest' : undefined, selectedOrders: selected, + orders: request.Orders.filter((order) => selected.some((o) => o.order.uuid === order.orderUuid)), }, }); } diff --git a/packages/esm-lab-manifest-app/src/types/index.ts b/packages/esm-lab-manifest-app/src/types/index.ts index 9c20fae00..ffe74c8ca 100644 --- a/packages/esm-lab-manifest-app/src/types/index.ts +++ b/packages/esm-lab-manifest-app/src/types/index.ts @@ -58,22 +58,24 @@ export interface LabManifestSample { order: Order; } -export interface ActiveRequests { - Orders: Array<{ - orderId: number; - orderUuid: string; - patientId: number; - patientUuid: string; - patientName: string; - cccKdod: string; - dateRequested: string; - payload: string; - hasProblem; - }>; +export interface ActiveRequest { + Orders: Array; cccNumberType: number; heiNumberType: number; } +export interface ActiveRequestOrder { + orderId: number; + orderUuid: string; + patientId: number; + patientUuid: string; + patientName: string; + cccKdod: string; + dateRequested: string; + payload: string; + hasProblem; +} + export interface Constiuency { name: string; code: string; diff --git a/packages/esm-lab-manifest-app/translations/en.json b/packages/esm-lab-manifest-app/translations/en.json index d8d33d9a0..1d516a93b 100644 --- a/packages/esm-lab-manifest-app/translations/en.json +++ b/packages/esm-lab-manifest-app/translations/en.json @@ -9,6 +9,7 @@ "cccKDODNumber": "CCC/KDOD Number", "courrier": "Courrier", "dateRequested": "Date Requested", + "deleteSelectedSamples": "Remove Selected Samples", "dispatch": "Dispatch", "edit": "Edit", "editManifest": "Edit Manifest", From ba49dc503b83dfa0c464b66138915dbf2891ce4c Mon Sep 17 00:00:00 2001 From: Laurent Ouma Date: Mon, 5 Aug 2024 10:12:07 +0300 Subject: [PATCH 6/7] Cleaned up code --- .../src/forms/lab-manifest-form.workspace.tsx | 24 ++++++++----------- .../lab-manifest-orders-to-manifest.modal.tsx | 16 ++++++------- ...lab-manifest-active-requests.component.tsx | 1 + .../tables/lab-manifest-samples.component.tsx | 2 +- 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/packages/esm-lab-manifest-app/src/forms/lab-manifest-form.workspace.tsx b/packages/esm-lab-manifest-app/src/forms/lab-manifest-form.workspace.tsx index 955d75299..38ff5d824 100644 --- a/packages/esm-lab-manifest-app/src/forms/lab-manifest-form.workspace.tsx +++ b/packages/esm-lab-manifest-app/src/forms/lab-manifest-form.workspace.tsx @@ -11,15 +11,19 @@ import { } from '@carbon/react'; import { zodResolver } from '@hookform/resolvers/zod'; import { DefaultWorkspaceProps, parseDate, showSnackbar, useConfig, useLayoutType } from '@openmrs/esm-framework'; -import React, { useEffect } from 'react'; +import React from 'react'; import { Controller, useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; -import { LabManifestFilters, labManifestFormSchema, saveLabManifest } from '../lab-manifest.resources'; -import styles from './lab-manifest-form.scss'; -import { County, MappedLabManifest } from '../types'; -import { mutate } from 'swr'; import { LabManifestConfig } from '../config-schema'; +import { + LabManifestFilters, + labManifestFormSchema, + mutateManifestLinks, + saveLabManifest, +} from '../lab-manifest.resources'; +import { County, MappedLabManifest } from '../types'; +import styles from './lab-manifest-form.scss'; interface LabManifestFormProps extends DefaultWorkspaceProps { patientUuid: string; manifest?: MappedLabManifest; @@ -48,15 +52,7 @@ const LabManifestForm: React.FC = ({ closeWorkspace, manif const onSubmit = async (values: ContactListFormType) => { try { await saveLabManifest(values, manifest?.uuid); - const mutateLinks = [ - `/ws/rest/v1/labmanifest?v=full&status=${values.manifestStatus}`, - `/ws/rest/v1/kemrorder/validorders?manifestUuid=${manifest?.uuid}`, - `/ws/rest/v1/labmanifest/${manifest?.uuid}`, - ]; - mutate((key) => { - return typeof key === 'string' && mutateLinks.some((link) => key.startsWith(link)); - }); - + mutateManifestLinks(values?.manifestStatus, manifest?.uuid); closeWorkspace(); showSnackbar({ title: 'Success', kind: 'success', subtitle: 'Lab manifest created successfully!' }); } catch (error) { diff --git a/packages/esm-lab-manifest-app/src/forms/lab-manifest-orders-to-manifest.modal.tsx b/packages/esm-lab-manifest-app/src/forms/lab-manifest-orders-to-manifest.modal.tsx index 84d34497a..903e7072d 100644 --- a/packages/esm-lab-manifest-app/src/forms/lab-manifest-orders-to-manifest.modal.tsx +++ b/packages/esm-lab-manifest-app/src/forms/lab-manifest-orders-to-manifest.modal.tsx @@ -20,7 +20,12 @@ import { useTranslation } from 'react-i18next'; import { mutate } from 'swr'; import { z } from 'zod'; import { useLabManifest } from '../hooks'; -import { addOrderToManifest, labManifestOrderToManifestFormSchema, sampleTypes } from '../lab-manifest.resources'; +import { + addOrderToManifest, + labManifestOrderToManifestFormSchema, + mutateManifestLinks, + sampleTypes, +} from '../lab-manifest.resources'; import { ActiveRequestOrder } from '../types'; import ActiveOrdersSelectionPreview from './active-order-selection-preview'; import styles from './lab-manifest-form.scss'; @@ -66,14 +71,7 @@ const LabManifestOrdersToManifestForm: React.FC { - return typeof key === 'string' && mutateLinks.some((link) => key.startsWith(link)); - }); + mutateManifestLinks(manifest?.manifestStatus, manifest?.uuid); onClose(); } catch (error) { showSnackbar({ title: 'Failure', kind: 'error', subtitle: 'Error adding orders to the manifest' }); diff --git a/packages/esm-lab-manifest-app/src/tables/lab-manifest-active-requests.component.tsx b/packages/esm-lab-manifest-app/src/tables/lab-manifest-active-requests.component.tsx index 5ccfa46a1..5f425c8c4 100644 --- a/packages/esm-lab-manifest-app/src/tables/lab-manifest-active-requests.component.tsx +++ b/packages/esm-lab-manifest-app/src/tables/lab-manifest-active-requests.component.tsx @@ -86,6 +86,7 @@ const LabManifestActiveRequests: React.FC = ({ m - diff --git a/packages/esm-lab-manifest-app/src/forms/lab-manifest-orders-to-manifest.modal.tsx b/packages/esm-lab-manifest-app/src/forms/lab-manifest-orders-to-manifest.modal.tsx index 903e7072d..79929df29 100644 --- a/packages/esm-lab-manifest-app/src/forms/lab-manifest-orders-to-manifest.modal.tsx +++ b/packages/esm-lab-manifest-app/src/forms/lab-manifest-orders-to-manifest.modal.tsx @@ -71,7 +71,7 @@ const LabManifestOrdersToManifestForm: React.FC