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

(feat) Add metrics to the home-page #1063

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from 'react';
import { Tile } from '@carbon/react';
import styles from './active-visits-tile.scss';
import { useTranslation } from 'react-i18next';
import { openmrsFetch, useSession } from '@openmrs/esm-framework';
import useSWR from 'swr';

function useActiveVisits() {
const session = useSession();
const sessionLocation = session?.sessionLocation?.uuid;

const customRepresentation = 'custom:(uuid,startDatetime,stopDatetime)';

const getUrl = () => {
let url = `/ws/rest/v1/visit?v=${customRepresentation}&`;
let urlSearchParams = new URLSearchParams();

urlSearchParams.append('includeInactive', 'false');
urlSearchParams.append('totalCount', 'true');
urlSearchParams.append('location', `${sessionLocation}`);

return url + urlSearchParams.toString();
};

const { data, error, isLoading } = useSWR<{ data: { results: any[]; totalCount: number } }>(getUrl, openmrsFetch);

const responseData = data?.data.results;

return {
data: responseData,
error,
isLoading,
};
}

const ActiveVisitsTile: React.FC = () => {
const { data: activeVisitsData } = useActiveVisits();

const { t } = useTranslation();
return (
<React.Fragment>
<Tile className={styles.tileContainer}>
<div>
<div className={styles.tileContent}>
<div className={styles.tileHeader}>
<header>{t('activeVisits', 'Active Visits')}</header>
</div>
<div className={styles.displayDetails}>
<div>Patients</div>
<div className={styles.displayData}>{activeVisitsData?.length ?? 0}</div>
</div>
</div>
</div>
</Tile>
</React.Fragment>
);
};

export default ActiveVisitsTile;
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
.tileContent {
display: flex;
flex-direction: column;
align-items: space-between;
}

.tileContainer {
background-color: white;
border: 0.0625rem solid #e0e0e0;
height: 7.875rem;
padding: 1rem;
margin: 0.5rem 0.5rem;
flex-grow: 1;
}

.tileHeader {
font-size: 0.875rem;
font-weight: 600;
line-height: 1.28572;
letter-spacing: 0.16px;
color: #525252;
}

.displayDetails {
margin-top: 1.5rem;
}

.displayData {
font-size: 1.75rem;
font-weight: 400;
line-height: 1.28572;
letter-spacing: 0;
color: #161616;
}
11 changes: 10 additions & 1 deletion packages/esm-active-visits-app/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineConfigSchema, getSyncLifecycle } from '@openmrs/esm-framework';
import { defineConfigSchema, getAsyncLifecycle, getSyncLifecycle } from '@openmrs/esm-framework';
import { configSchema } from './config-schema';
import activeVisitsComponent from './active-visits-widget/active-visits.component';
import visitDetailComponent from './visits-summary/visit-detail.component';
Expand All @@ -19,3 +19,12 @@ export function startupApp() {
export const activeVisits = getSyncLifecycle(activeVisitsComponent, options);

export const visitDetail = getSyncLifecycle(visitDetailComponent, options);

export const homeActiveVisitsTile = getAsyncLifecycle(
() => import('./active-visits-metric-tile/active-visits-tile.component'),
options,
);
export const homeTotalVisitsTile = getAsyncLifecycle(
() => import('./total-visits-metric-tile/total-visits-tile.component'),
options,
);
12 changes: 12 additions & 0 deletions packages/esm-active-visits-app/src/routes.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@
"webservices.rest": "^2.2.0"
},
"extensions": [
{
"name": "home-active-visits-tile",
"slot": "home-metrics-tiles-slot",
"component": "homeActiveVisitsTile",
"order": 0
},
{
"name": "home-total-visits-tile",
"slot": "home-metrics-tiles-slot",
"component": "homeTotalVisitsTile",
"order": 0
},
{
"name": "active-visits-widget",
"slot": "homepage-widgets-slot",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react';
import { Tile } from '@carbon/react';
import styles from './total-visits-tile.scss';
import { useTranslation } from 'react-i18next';
import { openmrsFetch } from '@openmrs/esm-framework';
import useSWR from 'swr';
import dayjs from 'dayjs';
import { type Visit } from '../types/index';

const useTotalVisits = () => {
const omrsDateFormat = 'YYYY-MM-DDTHH:mm:ss.SSSZZ';
const currentVisitDate = dayjs(new Date().setHours(0, 0, 0, 0)).format(omrsDateFormat);
const customRepresentation = 'custom:(uuid,startDatetime,stopDatetime)';

const visitsUrl = `/ws/rest/v1/visit?includeInactive=true&v=${customRepresentation}&fromStartDate=${dayjs(
currentVisitDate,
).format('YYYY-MM-DD')}`;

const { data, error, isLoading } = useSWR<{ data: { results: Visit[] } }>(visitsUrl, openmrsFetch);

const responseData = data?.data.results;

return { data: responseData, error, isLoading };
};

const TotalVisitsTile: React.FC = () => {
const { data: appointmentsData } = useTotalVisits();

const { t } = useTranslation();

return (
<React.Fragment>
<Tile className={styles.tileContainer}>
<div>
<div className={styles.tileContent}>
<div className={styles.tileHeader}>
<header>{t('totalVisits', 'Total Visits Today')}</header>
</div>
<div className={styles.displayDetails}>
<div>Patients</div>
<div className={styles.displayData}>{appointmentsData?.length ?? 0}</div>
</div>
</div>
</div>
</Tile>
</React.Fragment>
);
};

export default TotalVisitsTile;
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
.tileContent {
display: flex;
flex-direction: column;
align-items: space-between;
}

.tileContainer {
background-color: white;
border: 0.0625rem solid #e0e0e0;
height: 7.875rem;
padding: 1rem;
margin: 0.5rem 0.5rem;
flex-grow: 1;
}

.tileHeader {
font-size: 0.875rem;
font-weight: 600;
line-height: 1.28572;
letter-spacing: 0.16px;
color: #525252;
}

.displayDetails {
margin-top: 1.5rem;
}

.displayData {
font-size: 1.75rem;
font-weight: 400;
line-height: 1.28572;
letter-spacing: 0;
color: #161616;
}
27 changes: 27 additions & 0 deletions packages/esm-active-visits-app/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { type OpenmrsResource } from '@openmrs/esm-framework';

export interface SearchedPatient {
patientId: number;
uuid: string;
Expand Down Expand Up @@ -26,3 +28,28 @@ export interface Identifier {
preferred: boolean;
voided: boolean;
}

export interface Visit {
uuid: string;
display?: string;
encounters: Array<OpenmrsResource>;
patient?: OpenmrsResource;
visitType: VisitType;
location?: Location;
startDatetime: string;
stopDatetime?: string;
attributes?: Array<OpenmrsResource>;
[anythingElse: string]: any;
}

export interface Location {
uuid: string;
display?: string;
name?: string;
}

export interface VisitType {
uuid: string;
display: string;
name?: string;
}
1 change: 1 addition & 0 deletions packages/esm-active-visits-app/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"tests": "Tests",
"thereIsNoInformationToDisplayHere": "There is no information to display here",
"time": "Time",
"totalVisits": "Total Visits Today",
"visitStartTime": "Visit Time",
"visitSummary": "Visit Summary",
"visitType": "Visit Type"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from 'react';
import { Tile } from '@carbon/react';
import styles from './appointments-tile.scss';
import { useTranslation } from 'react-i18next';
import { openmrsFetch } from '@openmrs/esm-framework';
import useSWR from 'swr';
import dayjs from 'dayjs';

const useAppointmentsData = () => {
const omrsDateFormat = 'YYYY-MM-DDTHH:mm:ss.SSSZZ';
const appointmentDate = dayjs(new Date().setHours(0, 0, 0, 0)).format(omrsDateFormat);

const url = `ws/rest/v1/appointment/all?forDate=${appointmentDate}`;

const { data, error, isLoading } = useSWR<{ data: Array<any> }>(url, openmrsFetch);

const responseData = data?.data;

return { data: responseData, error, isLoading };
};

const AppointmentsTile: React.FC = () => {
const { data: appointmentsData } = useAppointmentsData();

const { t } = useTranslation();

return (
<React.Fragment>
<Tile className={styles.tileContainer}>
<div>
<div className={styles.tileContent}>
<div className={styles.tileHeader}>
<header>{t('scheduledForToday', 'Scheduled For Today')}</header>
</div>
<div className={styles.displayDetails}>
<div>Patients</div>
<div className={styles.displayData}>{appointmentsData?.length ?? 0}</div>
</div>
</div>
</div>
</Tile>
</React.Fragment>
);
};

export default AppointmentsTile;
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.tileContent {
display: flex;
flex-direction: column;
align-items: space-between;
}

.tileContainer {
background-color: white;
border: 0.0625rem solid #e0e0e0;
height: 7.875rem;
padding: 1rem;
margin: 0.5rem 0.5rem;
}

.tileHeader {
font-size: 0.875rem;
font-weight: 600;
line-height: 1.28572;
letter-spacing: 0.16px;
color: #525252;
}

.displayDetails {
margin-top: 1.5rem;
}

.displayData {
font-size: 1.75rem;
font-weight: 400;
line-height: 1.28572;
letter-spacing: 0;
color: #161616;
}
5 changes: 5 additions & 0 deletions packages/esm-appointments-app/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ export const patientAppointmentsCancelConfirmationDialog = getAsyncLifecycle(
options,
);

export const homeAppointmentsTile = getAsyncLifecycle(
() => import('./homepage-tile/appointments-tile.component'),
options,
);

registerWorkspace({
name: 'appointments-form-workspace',
load: getAsyncLifecycle(() => import('./form/appointments-form.component'), options),
Expand Down
6 changes: 6 additions & 0 deletions packages/esm-appointments-app/src/routes.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
"webservices.rest": "^2.2.0"
},
"extensions": [
{
"name": "home-appointments-tile",
"slot": "home-metrics-tiles-slot",
"component": "homeAppointmentsTile",
"order": 0
},
{
"name": "home-appointments",
"slot": "homepage-widgets-slot",
Expand Down
1 change: 1 addition & 0 deletions packages/esm-appointments-app/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@
"saveAndClose": "Save and close",
"scheduled": "Scheduled",
"scheduledAppointments": "Scheduled appointments",
"scheduledForToday": "Scheduled For Today",
"search": "Search",
"searchForAVisitType": "Search for a visit type",
"seeAllAppointments": "See all appointments",
Expand Down