Skip to content

Commit

Permalink
(Fix) Added Facility Setup page in the Administration app (#550)
Browse files Browse the repository at this point in the history
* renamed labels in claims form

* Added facility setup page

* (fix) completed frontend work for facility setup page

* removed unused files and codes

* removed facility data from system info page

* color renaming

* added missing attributes

* styled content

* added synchronize parameter

---------

Co-authored-by: Felix Kiprotich <fkiprotich845>
  • Loading branch information
FelixKiprotich350 authored Jan 17, 2025
1 parent f5c6168 commit 7e88a60
Show file tree
Hide file tree
Showing 16 changed files with 368 additions and 72 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import React, { useEffect, useState } from 'react';
import { Tile, Grid, Column, Layer, InlineLoading, Button } from '@carbon/react';
import { useFacilityInfo } from '../hook/useFacilityInfo';
import styles from './facility-info.scss';
import { useTranslation } from 'react-i18next';
import { showNotification, showSnackbar } from '@openmrs/esm-framework';
import { FacilityData } from '../../types';

const FacilityInfo: React.FC = () => {
const { t } = useTranslation();
const [shouldSynchronize, setshouldSynchronize] = useState<boolean>(false);
const { defaultFacility, isLoading: defaultFacilityLoading, error, refetch } = useFacilityInfo(shouldSynchronize);

const [facilityData, setFacilityData] = useState<FacilityData>(defaultFacility);
useEffect(() => {
setFacilityData(defaultFacility);
if (defaultFacility?.operationalStatus !== 'Operational') {
showNotification({ kind: 'error', title: 'Error', description: 'The facility SHA status is is not operational' });
}
}, [defaultFacility]);

const synchronizeFacilityData = async () => {
try {
setshouldSynchronize(true);
await refetch();
showSnackbar({
title: t('syncingHieSuccess', 'Synchronization Complete'),
kind: 'success',
isLowContrast: true,
});
} catch (error) {
const errorMessage = error?.responseBody?.error?.message ?? 'An error occurred while synchronizing with HIE';
showSnackbar({
title: t('syncingHieError', 'Syncing with HIE Failed'),
subtitle: errorMessage,
kind: 'error',
isLowContrast: true,
});
}
};

return (
<div className={styles.facilityInfoContainer}>
<div>
<Layer className={styles.btnLayer}>
{defaultFacilityLoading ? (
<InlineLoading
description={t('synchronizingFacilityData', 'Please wait, Synchronizing Info.')}
size="md"
className={styles.loading}
withOverlay
/>
) : (
<Button kind="primary" onClick={synchronizeFacilityData}>
{t('synchronizeWithHie', 'Synchronize with HIE')}
</Button>
)}
</Layer>
</div>

<Grid narrow>
{/* General Info Column */}
<Column sm={4} md={4} lg={8}>
<Tile className={styles.card}>
<h3 className={styles.cardTitle}>General Information</h3>
<hr className={styles.cardDivider} />
<div className={styles.cardContent}>
<p>
<strong>Facility Name:</strong> {facilityData?.display || 'N/A'}
</p>
<p>
<strong>Facility KMHFR Code:</strong> {facilityData?.mflCode}
</p>
<p>
<strong>Keph Level:</strong> {facilityData?.shaKephLevel}
</p>
<br />
<br />
</div>
</Tile>
</Column>

{/* SHA Info Column */}
<Column sm={4} md={4} lg={8}>
<Layer>
<Tile className={styles.card}>
<h3 className={styles.cardTitle}>SHA Information</h3>
<hr className={styles.cardDivider} />
<div className={styles.cardContent}>
<p>
<strong>Facility Registry Code:</strong> {facilityData?.shaFacilityId}
</p>
<p>
<strong>SHA License Number:</strong> {facilityData?.shaFacilityLicenseNumber}
</p>
<p>
<strong>SHA Status:</strong> {facilityData?.operationalStatus}
</p>
<p>
<strong>SHA Contracted:</strong> {facilityData?.shaContracted}
</p>
<p>
<strong>SHA Expiry Date:</strong> {facilityData?.shaFacilityExpiryDate}
</p>
</div>
</Tile>
</Layer>
</Column>
</Grid>
</div>
);
};

export default FacilityInfo;
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
@use '@carbon/layout';
@use '@carbon/type';
@use '@carbon/colors';

.omrs-main-content {
background-color: colors.$white;
}

.bottomBorder {
margin-bottom: layout.$spacing-05;
}

.btnLayer {
display: flex;
padding-top: layout.$spacing-05;
padding-right: layout.$spacing-05;
padding-bottom: layout.$spacing-05;
margin-top: layout.$spacing-05;
flex-direction: row;
justify-content: flex-end;
background-color: white;
width: 100%;
}

.tableLayer {
padding-left: layout.$spacing-05;
padding-right: layout.$spacing-05;
background: white;
padding-top: layout.$spacing-01;
}

.loading {
display: flex;
padding-top: layout.$spacing-05;
padding-right: layout.$spacing-05;
padding-bottom: layout.$spacing-05;
margin-top: layout.$spacing-05;
flex-direction: row;
justify-content: flex-end;
background-color: white;
width: 100%;
}

.facilityInfoContainer {
margin: layout.$spacing-05;
}

.card {
padding: 1rem;
background-color: colors.$gray-10;
border: 1px solid #e0e0e0;
border-radius: 2px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.cardTitle {
margin-bottom: 1rem;
font-size: 1.25rem;
font-weight: 600;
}

.cardDivider {
border: none;
border-top: 1px solid #e0e0e0;
margin: 0.5rem 0;
}
.cardContent {
font-size: 1rem;
line-height: 1.8;
}

.cardContent p {
display: flex;
justify-content: space-between;
margin-bottom: 0.5rem;
}
.cardContent br {
display: flex;
justify-content: space-between;
margin-bottom: 0.5rem;
}

.cardContent strong {
min-width: 150px;
display: inline-block;
color: #393939;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Layer, Button, MenuItem, InlineLoading } from '@carbon/react';
import styles from './facility-setup.scss';
import FacilityInfo from './facility-info.component';
import Header from './header/header.component';
import { showSnackbar } from '@openmrs/esm-framework';

const FacilitySetup: React.FC = () => {
const { t } = useTranslation();
return (
<div className="omrs-main-content">
<Header title="Facility Setup" />

<Layer className={styles.tableLayer}>
<FacilityInfo />
</Layer>
</div>
);
};

export default FacilitySetup;
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
@use '@carbon/layout';
@use '@carbon/type';
@use '@carbon/colors';

.omrs-main-content {
background-color: white;
}

.btnLayer {
display: flex;
padding-top: layout.$spacing-05;
padding-right: layout.$spacing-05;
padding-bottom: layout.$spacing-05;
margin-top: layout.$spacing-05;
flex-direction: row;
justify-content: flex-end;
background-color: white;
width: 100%;
}

.tableLayer {
padding-left: layout.$spacing-05;
padding-right: layout.$spacing-05;
background: white;
padding-top: layout.$spacing-01;
}

.loading {
display: flex;
padding-top: layout.$spacing-05;
padding-right: layout.$spacing-05;
padding-bottom: layout.$spacing-05;
margin-top: layout.$spacing-05;
flex-direction: row;
justify-content: flex-end;
background-color: white;
width: 100%;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Development } from '@carbon/react/icons';
import { useSession, PageHeader } from '@openmrs/esm-framework';
import styles from './header.scss';

interface HeaderProps {
title: string;
}

const Header: React.FC<HeaderProps> = ({ title }) => {
const { t } = useTranslation();
const session = useSession();
const location = session?.sessionLocation?.display;

return (
<div className={styles.header}>
<PageHeader title={title} illustration={<Development size={32} />} className={styles.header} />
</div>
);
};

export default Header;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@use '@carbon/layout';
@use '@carbon/type';
@use '@carbon/colors';

.header {
@include type.type-style('body-compact-02');
height: layout.$spacing-12;
display: flex;
justify-content: space-between;
padding: layout.$spacing-05;
background: white;
border: 1px solid colors.$gray-20;
}
.svgContainer svg {
width: layout.$spacing-10;
height: layout.$spacing-10;
margin-right: layout.$spacing-06;
fill: var(--brand-03);
}
21 changes: 21 additions & 0 deletions packages/esm-admin-app/src/components/hook/useFacilityInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { FetchResponse, openmrsFetch, restBaseUrl, useSession } from '@openmrs/esm-framework';
import useSWR from 'swr';
import { FacilityData } from '../../types';

export function useFacilityInfo(shouldSynchronize: boolean = false) {
const { authenticated } = useSession();
const url = `${restBaseUrl}/kenyaemr/default-facility?synchronize=${shouldSynchronize}`;

const { data, isLoading, error, mutate } = useSWR<FetchResponse<FacilityData>>(
authenticated ? url : null,
openmrsFetch,
{},
);

return {
isLoading,
defaultFacility: data?.data,
error,
refetch: mutate, // Expose mutate as refetch
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const LogTable: React.FC<LogTableProps> = ({ logData, isLoading }) => {

return (
<div className={styles.table}>
<CardHeader title={t('etlOperationLog', 'ETL Operations Log')} children={''} />
<CardHeader title={t('facilityInfo', 'Facility Info')} children={''} />
<div className={styles.logTable}>
{isLoading && logData.length === 0 ? (
<DataTableSkeleton
Expand Down
4 changes: 4 additions & 0 deletions packages/esm-admin-app/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@ export const etlAdministrationLeftPannelLink = getSyncLifecycle(
createLeftPanelLink({ title: 'ETL Administration', name: 'etl-administration' }),
options,
);
export const facilitySetupLeftPanelLink = getSyncLifecycle(
createLeftPanelLink({ title: 'Facility Setup', name: 'facility-setup' }),
options,
);
2 changes: 2 additions & 0 deletions packages/esm-admin-app/src/root.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import styles from './root.scss';
import LeftPanel from './components/side-menu/left-pannel.component';
import UserManagentLandingPage from './components/users/manage-users/manage-user.component';
import EtlAdminDashboard from './components/dashboard/etl-dashboard.component';
import FacilitySetup from './components/facility-setup/facility-setup.component';

const Root: React.FC = () => {
const spaBasePath = window.spaBase;
Expand All @@ -26,6 +27,7 @@ const Root: React.FC = () => {
<Route path="/" element={<UserManagentLandingPage />} />
<Route path="/user-management" element={<UserManagentLandingPage />} />
<Route path="/etl-administration" element={<EtlAdminDashboard />} />
<Route path="/facility-setup" element={<FacilitySetup />} />
</Routes>
</main>
</BrowserRouter>
Expand Down
5 changes: 5 additions & 0 deletions packages/esm-admin-app/src/routes.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
"component": "etlAdministrationLeftPannelLink",
"name": "etl-administration-left-panel-link",
"slot": "admin-left-panel-slot"
},
{
"component": "facilitySetupLeftPanelLink",
"name": "facility-setup-left-panel-link",
"slot": "admin-left-panel-slot"
}
],
"workspaces": [
Expand Down
Loading

0 comments on commit 7e88a60

Please sign in to comment.