Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(Fix) Added Facility Setup page in the Administration app #550

Merged
merged 14 commits into from
Jan 17, 2025
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
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 { defaultFacility, isLoading: defaultFacilityLoading, error, refetch } = useFacilityInfo();

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 {
// Trigger manual refetch
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>
<p>
<strong>Operational Status:</strong> {facilityData?.operationalStatus}
</p>
</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>SHA Status:</strong> {facilityData?.shaStatus}
</p>
<p>
<strong>SHA Contracted:</strong> {facilityData?.shaContracted}
</p>
<p>
<strong>SHA Expiry Date:</strong> {facilityData?.shaFacilityExpiryDate}
</p>
<br />
</div>
</Tile>
</Layer>
</Column>
</Grid>
</div>
);
};

export default FacilityInfo;
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
@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 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() {
const { authenticated } = useSession();
const url = `${restBaseUrl}/kenyaemr/default-facility`;

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
22 changes: 22 additions & 0 deletions packages/esm-admin-app/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,25 @@ export interface DashboardConfig {
slot: string;
title: string;
}
export interface FrontendModule {
name: string;
version?: string;
}
export interface DefaultFacility {
locationId: number;
uuid: string;
display: string;
operationalStatus: string;
shaContracted: string;
shaFacilityExpiryDate: string;
}

export interface FacilityData {
shaKephLevel?: string;
mflCode?: string;
display?: string;
operationalStatus?: string;
shaStatus?: string;
shaContracted?: string;
shaFacilityExpiryDate?: string;
}
Loading
Loading