Skip to content

Commit

Permalink
Merge branch 'AMPATH:main' into main
Browse files Browse the repository at this point in the history
Rugute authored Mar 6, 2024
2 parents fd1f9ee + 667b9fe commit a97f3ab
Showing 47 changed files with 2,080 additions and 39 deletions.
Binary file modified .DS_Store
Binary file not shown.
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -7,9 +7,10 @@
"packages/*"
],
"scripts": {
"start": "openmrs develop --spa-path '/amrs/spa/' --api-url '/amrs/' --backend 'http://amrs.ampath.or.ke:8080' ",
"ci:publish": "yarn workspaces foreach --all --topological --exclude @ampath/esm-3.x-app npm publish --access public --tag latest",
"ci:prepublish": "yarn workspaces foreach --all --topological --exclude @ampath/esm-3.x-app npm publish --access public --tag next",
"start": "openmrs develop --backend http://amrs.ampath.or.ke:8080 --sources packages/esm-*-app --api-url /amrs --spa-path /amrs/spa/ --port 8040",
"start:core": "openmrs develop --backend http://amrs.ampath.or.ke:8080 --sources packages/esm-ampath-core-app --api-url /amrs --spa-path /amrs/spa/ --port 8030",
"ci:publish": "yarn workspaces foreach --all --topological --exclude @ampth/esm-3.x-app npm publish --access public --tag latest",
"ci:prepublish": "yarn workspaces foreach --all --topological --exclude @ampth/esm-3.x-app npm publish --access public --tag next",
"release": "yarn workspaces foreach --all --topological version",
"verify": "turbo lint typescript test --color --concurrency=2",
"prettier": "prettier --config prettier.config.js --write \"packages/**/*.{ts,tsx,css,scss}\" \"e2e/**/*.ts\" --list-different",
@@ -35,6 +36,7 @@
},
"devDependencies": {
"@carbon/react": "~1.37.0",
"@ohri/openmrs-esm-ohri-commons-lib": "next",
"@openmrs/esm-framework": "next",
"@openmrs/esm-patient-common-lib": "next",
"@playwright/test": "1.40.1",
3 changes: 3 additions & 0 deletions packages/esm-ampath-core-app/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const rootConfig = require('../../jest.config.js');

module.exports = rootConfig;
55 changes: 55 additions & 0 deletions packages/esm-ampath-core-app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"name": "@ampath/esm-ampath-core-app",
"version": "1.1.0",
"description": "AMPATH Core App",
"browser": "dist/ampath-esm-core-app.js",
"main": "src/index.ts",
"source": true,
"license": "MPL-2.0",
"homepage": "https://github.com/AMPATH/ampath-esm-3.x#readme",
"scripts": {
"start": "openmrs develop",
"serve": "webpack serve --mode=development",
"debug": "npm run serve",
"build": "webpack --mode production",
"analyze": "webpack --mode=production --env.analyze=true",
"lint": "cross-env TIMING=1 eslint src --ext ts,tsx",
"test": "cross-env TZ=UTC jest --config jest.config.js --verbose false --passWithNoTests --color",
"test:watch": "cross-env TZ=UTC jest --watch --config jest.config.js --color",
"coverage": "yarn test --coverage",
"typescript": "tsc",
"extract-translations": "i18next 'src/**/*.component.tsx'"
},
"browserslist": [
"extends browserslist-config-openmrs"
],
"keywords": [
"openmrs",
"ampath"
],
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/AMPATH/ampath-esm-3.x.git"
},
"bugs": {
"url": "https://github.com/AMPATH/ampath-esm-3.x/issues"
},
"dependencies": {
"@carbon/react": "~1.37.0",
"lodash-es": "^4.17.15"
},
"peerDependencies": {
"@ohri/openmrs-esm-ohri-commons-lib": "2.x",
"@openmrs/esm-framework": "5.x",
"react": "^18.1.0",
"react-i18next": "11.x",
"react-router-dom": "6.x",
"swr": "2.x"
},
"devDependencies": {
"webpack": "^5.74.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React, { useEffect, useMemo, useState } from 'react';
import { attach, detach, ExtensionSlot, isDesktop, useExtensionSlotMeta, useLayoutType } from '@openmrs/esm-framework';
import styles from './ampath-dashboard.scss';
import { useParams } from 'react-router-dom';

const AmpathDashboard = () => {
const { view } = useParams();
const [dashboards, setDashboards] = useState([]);
const metaLinks = useExtensionSlotMeta('dashboard-links-slot') as Record<string, any>;
const metaFolders = useExtensionSlotMeta('dashboard-slot') as Record<string, any>;
const [currentDashboard, setCurrentDashboard] = useState(null);
const layout = useLayoutType();

useEffect(() => {
if (view) {
setCurrentDashboard(dashboards.find((db) => db.name == view));
} else if (!currentDashboard) {
setCurrentDashboard(dashboards[0]);
}
}, [view, dashboards]);

useEffect(() => {
if (!isDesktop(layout)) {
attach('nav-menu-slot', 'ampath-nav-items-ext');
}
return () => detach('nav-menu-slot', 'ampath-nav-items-ext');
}, [layout]);

useEffect(() => {
const programSpecificLinks = metaFolders ? Object.values(metaFolders).filter((link) => link.isLink) : [];
const linksWithDashboardMeta = [
...Object.values(metaLinks).filter((link) => Object.keys(link).length),
...programSpecificLinks,
];
if (linksWithDashboardMeta.length) {
setDashboards([...dashboards, ...linksWithDashboardMeta]);
}
}, [metaLinks, metaFolders]);

const state = useMemo(() => {
if (currentDashboard) {
return { programme: currentDashboard?.config?.programme, dashboardTitle: currentDashboard.title };
}
return null;
}, [currentDashboard]);

return (
<div className={styles.dashboardContainer}>
{Object.values(metaFolders).map((f, index) => {
return (
<GroupAbleMenuItem
groupSlot={f.slot}
dashboards={dashboards}
setDashboards={setDashboards}
updateDashboardState={index == Object.keys(metaFolders).length - 1}
key={index}
/>
);
})}
{isDesktop(layout) && <ExtensionSlot name="ampath-nav-items-slot" key={layout} />}
<div className={` ${styles.dashboardContent}`}>
{currentDashboard && <ExtensionSlot name={currentDashboard.slot} state={state} />}
</div>
</div>
);
};

const GroupAbleMenuItem = ({ groupSlot, dashboards, setDashboards, updateDashboardState }) => {
const meta = useExtensionSlotMeta(groupSlot);
useEffect(() => {
if (meta && Object.keys(meta).length) {
dashboards.push(...Object.values(meta).filter((entry) => Object.keys(entry).length));
updateDashboardState && setDashboards([...dashboards]);
}
}, [meta]);

return <></>;
};

export default AmpathDashboard;
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
@import '../root.scss';

.dashboardContainer {
display: -webkit-flex;
height: 90vh;
position: relative;
overflow-y: hidden;
}

.dashboardContent {
margin: 0;
flex-basis: 75%;
flex-grow: 1;
flex-shrink: 1;
position: relative;
overflow-y: auto;
margin-left: 16rem;
padding-left: 0;
padding-right: 0;
max-width: 100% !important;
}

.noMarker {
list-style-type: none;
}

.noMarker ul li a {
padding-left: 40px !important;
font: lighter;
}

.currentNavItem > a {
background-color: #cecece !important;
color: #161616 !important;
border-left-color: var(--brand-01) !important;
font: bolder;
}

.hide {
display: none;
}

@media (max-width: 1200px) {
.dashboardContent {
margin-left: 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { SideNav, SideNavItems } from '@carbon/react';
import styles from './ampath-dashboard-side-nav.scss';
import { ExtensionSlot } from '@openmrs/esm-framework';
import { useTranslation } from 'react-i18next';

const AMPATHDashboardSideNav = () => {
const { t } = useTranslation();
return (
<SideNav isFixedNav expanded={true} isChildOfHeader={false} aria-label="Side navigation" className={styles.sideNav}>
<SideNavItems>
<ExtensionSlot name="dashboard-links-slot" />

<p className={styles.sideNavTextHeader}>{t('programmes', 'Programmes')}</p>

<ExtensionSlot name="dashboard-slot" />
</SideNavItems>
</SideNav>
);
};

export default AMPATHDashboardSideNav;
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@import '../../root.scss';

.sideNav {
width: 16rem;
// padding-top: 2rem;
min-height: 8rem;
overscroll-behavior: contain;
margin: 48px 13px 15px 0;
border-right: 1px #e0e0e0;
}

.sideNavTextHeader {
font-size: 14px;
padding: 0.5rem 1rem 0.5rem;
}

.sideNav :global(a.cds--side-nav__link[aria-current='page']:before),
.sideNav :global(.cds--side-nav__submenu[aria-expanded='false']:before) {
// background-color: $brand-teal-01;
}

.sideNav :global(ul.cds--side-nav__items) {
border-style: solid !important;
border-right: 1px #e0e0e0;
}

@media (max-width: 1200px) {
.sideNav :global(.cds--side-nav) {
z-index: -100 !important;
}

.sideNav :global(.cds--side-nav__items) {
padding: 0 !important;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React, { useEffect } from 'react';
import { ExtensionSlot } from '@openmrs/esm-framework';

const StockManagementDashboard = () => {
return <ExtensionSlot name="ampath-dashboard-stock-management-slot" state={{}} />;
};

export default StockManagementDashboard;
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React, { useEffect } from 'react';
import styles from './clinical-view-section.scss';
import { useTranslation } from 'react-i18next';
import { Information } from '@carbon/react/icons';
import { Tooltip } from '@carbon/react';
import { ExtensionSlot } from '@openmrs/esm-framework';
import { type DashboardGroupExtensionProps } from './dashboard-group.component';
import { registerNavGroup } from '@openmrs/esm-patient-common-lib';

export const ClinicalViewSection: React.FC<DashboardGroupExtensionProps> = ({ title, basePath }) => {
const slotName = 'clinical-view-section';
const { t } = useTranslation();
useEffect(() => {
registerNavGroup(slotName);
}, [slotName]);
return (
<>
<div className={styles.container}>
<span className={styles.dividerText}>{t('clinicalViews', 'Clinical views')}</span>
<Tooltip
align="top"
label={t(
'customViews',
"In this section, you'll find custom clinical views tailored to patients' conditions and enrolled care programs.",
)}>
<button style={{ border: 'none' }} className="sb-tooltip-trigger" type="button">
<Information />
</button>
</Tooltip>
</div>
<ExtensionSlot style={{ width: '100%', minWidth: '15rem' }} name={slotName ?? title} state={{ basePath }} />
</>
);
};

export default ClinicalViewSection;
20 changes: 20 additions & 0 deletions packages/esm-ampath-core-app/src/clinical-view-section.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@use '@carbon/colors';
@use '@carbon/layout';
@use '@carbon/type';

.container {
height: layout.$layout-03;
display: flex;
align-items: center;
padding: layout.$layout-02 layout.$layout-01;
@include type.type-style('label-02');
color: colors.$gray-70;
column-gap: layout.$layout-01;
background-color: colors.$gray-10;
margin-top: layout.$layout-05;
}

.dividerText {
font-size: 1rem;
font-weight: 650;
}
12 changes: 12 additions & 0 deletions packages/esm-ampath-core-app/src/config-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Type } from '@openmrs/esm-framework';
export const configSchema = {
basicAuthBase64: {
_type: Type.String,
_description: 'Basic auth base64 string for the API call e.g Basic someBase64String==',
_default: '',
},
};

export type PreAppointmentsConfig = {
basicAuthBase64: string;
};
39 changes: 39 additions & 0 deletions packages/esm-ampath-core-app/src/dashboard-group.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useEffect } from 'react';
import { ExtensionSlot, useLayoutType } from '@openmrs/esm-framework';
import { SideNavItems, SideNavMenu, SideNavDivider } from '@carbon/react';
import { Add } from '@carbon/react/icons';
import { registerNavGroup } from '@openmrs/esm-patient-common-lib';
import styles from './dashboard-group.scss';

export interface DashboardGroupExtensionProps {
title: string;
slotName?: string;
basePath: string;
isExpanded?: boolean;
isChild?: boolean;
}

export const DashboardGroupExtension: React.FC<DashboardGroupExtensionProps> = ({
title,
slotName,
basePath,
isExpanded,
isChild,
}) => {
const isTablet = useLayoutType() === 'tablet';
useEffect(() => {
registerNavGroup(slotName);
}, [slotName]);

return (
<SideNavItems className={styles.sideMenuItems} isSideNavExpanded={true}>
<SideNavMenu
className={isChild && styles.sideNavMenu}
large={isTablet}
defaultExpanded={isExpanded ?? true}
title={title}>
<ExtensionSlot style={{ width: '100%', minWidth: '15rem' }} name={slotName ?? title} state={{ basePath }} />
</SideNavMenu>
</SideNavItems>
);
};
21 changes: 21 additions & 0 deletions packages/esm-ampath-core-app/src/dashboard-group.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@use '@carbon/layout';
@use '@carbon/colors';
@use '@carbon/type';

.sideMenuItems {
padding: 0;

:global(.cds--side-nav__link.active-left-nav-link) {
padding: 0 1.75rem;
}
}

.childMenuItems {
padding-left: layout.$spacing-02;
}

.sideNavMenu {
:global(.cds--side-nav__submenu) {
padding-left: layout.$spacing-06;
}
}
9 changes: 9 additions & 0 deletions packages/esm-ampath-core-app/src/dashboard.meta.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { InventoryManagement } from '@carbon/react/icons';

export const stockManagementDashboardMeta = {
name: 'stock-management',
slot: 'ampath-dashboard-stock-management-slot',
config: { columns: 1, type: 'grid', icon: InventoryManagement },
isLink: true,
title: 'Stock Management',
};
4 changes: 4 additions & 0 deletions packages/esm-ampath-core-app/src/declarations.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare module '@carbon/react';
declare module '*.css';
declare module '*.scss';
declare type SideNavProps = {};
27 changes: 27 additions & 0 deletions packages/esm-ampath-core-app/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { defineConfigSchema, getAsyncLifecycle, getSyncLifecycle } from '@openmrs/esm-framework';
import { configSchema } from './config-schema';
import ClinicalViewSection from './clinical-view-section.component';

const moduleName = '@ampath/esm-ampath-core-app';

const options = {
featureName: 'esm-ampath-core-app',
moduleName,
};
export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');

export function startupApp() {
defineConfigSchema(moduleName, configSchema);
}

export const dashboard = getAsyncLifecycle(() => import('./root'), options);

export const ampathNavItems = getAsyncLifecycle(
() => import('./ampath-dashboard/side-menu/ampath-dashboard-side-nav.component'),
{
featureName: 'nav-items',
moduleName,
},
);

export const clinicalViewPatientDashboard = getSyncLifecycle(ClinicalViewSection, options);
15 changes: 15 additions & 0 deletions packages/esm-ampath-core-app/src/root.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
import AmpathDashboard from './ampath-dashboard/ampath-dashboard.component';

export default function Root() {
return (
<BrowserRouter basename={window['getOpenmrsSpaBase']()}>
<Routes>
<Route path="/dashboard" element={<AmpathDashboard />} />
<Route path="/dashboard/:view" element={<AmpathDashboard />} />
<Route path="/home" element={<Navigate to={'/dashboard/home'} replace />} />
</Routes>
</BrowserRouter>
);
}
Empty file.
15 changes: 15 additions & 0 deletions packages/esm-ampath-core-app/src/root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
import AMPATHDashboard from './ampath-dashboard/ampath-dashboard.component';

export default function Root() {
return (
<BrowserRouter basename={window['getOpenmrsSpaBase']()}>
<Routes>
<Route path="/dashboard" element={<AMPATHDashboard />} />
<Route path="/dashboard/:view" element={<AMPATHDashboard />} />
<Route path="/home" element={<Navigate to={'/dashboard/home'} replace />} />
</Routes>
</BrowserRouter>
);
}
17 changes: 17 additions & 0 deletions packages/esm-ampath-core-app/src/routes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "https://json.openmrs.org/routes.schema.json",
"backendDependencies": {
"webservices.rest": "^2.24.0"
},
"pages": [

],
"extensions": [
{
"name": "clinical-view-section",
"component": "clinicalViewPatientDashboard",
"slot": "patient-chart-dashboard-slot",
"order": 20
}
]
}
5 changes: 5 additions & 0 deletions packages/esm-ampath-core-app/translations/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"clinicalViews": "Clinical views",
"customViews": "In this section, you'll find custom clinical views tailored to patients' conditions and enrolled care programs.",
"programmes": "Programmes"
}
5 changes: 5 additions & 0 deletions packages/esm-ampath-core-app/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../../tsconfig.json",
"include": ["src/**/*"],
"exclude": ["src/**/*.test.tsx"]
}
1 change: 1 addition & 0 deletions packages/esm-ampath-core-app/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('openmrs/default-webpack-config');
3 changes: 3 additions & 0 deletions packages/esm-otz-app/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const rootConfig = require('../../jest.config.js');

module.exports = rootConfig;
54 changes: 54 additions & 0 deletions packages/esm-otz-app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "@ampath/esm-otz-app",
"version": "6.0.0",
"description": "AMPATH OTZ App",
"browser": "dist/ampath-esm-otz-app.js",
"main": "src/index.ts",
"source": true,
"license": "MPL-2.0",
"homepage": "https://github.com/AMPATH/ampath-esm-3.x#readme",
"scripts": {
"start": "openmrs develop",
"serve": "webpack serve --mode=development",
"debug": "npm run serve",
"build": "webpack --mode production",
"analyze": "webpack --mode=production --env.analyze=true",
"lint": "cross-env TIMING=1 eslint src --ext ts,tsx",
"test": "cross-env TZ=UTC jest --config jest.config.js --verbose false --passWithNoTests --color",
"test:watch": "cross-env TZ=UTC jest --watch --config jest.config.js --color",
"coverage": "yarn test --coverage",
"typescript": "tsc",
"extract-translations": "i18next 'src/**/*.component.tsx'"
},
"browserslist": [
"extends browserslist-config-openmrs"
],
"keywords": [
"openmrs",
"ampath"
],
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/AMPATH/ampath-esm-3.x.git"
},
"bugs": {
"url": "https://github.com/AMPATH/ampath-esm-3.x/issues"
},
"dependencies": {
"@carbon/react": "~1.37.0",
"lodash-es": "^4.17.15"
},
"peerDependencies": {
"@openmrs/esm-framework": "5.x",
"react": "^18.1.0",
"react-i18next": "11.x",
"react-router-dom": "6.x",
"swr": "2.x"
},
"devDependencies": {
"webpack": "^5.74.0"
}
}
12 changes: 12 additions & 0 deletions packages/esm-otz-app/src/config-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Type } from '@openmrs/esm-framework';
export const configSchema = {
basicAuthBase64: {
_type: Type.String,
_description: 'Basic auth base64 string for the API call e.g Basic someBase64String==',
_default: '',
},
};

export type PreAppointmentsConfig = {
basicAuthBase64: string;
};
15 changes: 15 additions & 0 deletions packages/esm-otz-app/src/dashboard.meta.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Coronavirus } from '@carbon/react/icons';

export const otzPatientChartMeta = {
title: 'OTZ',
slotName: 'ampath-otz-patient-chart-slot',
isExpanded: false,
};

export const otzDashboardMeta = {
name: 'otz-cases',
slot: 'otz-cases-dashboard-slot',
config: { columns: 1, type: 'grid', programme: 'otz', dashboardTitle: 'OTZ clubs', icon: Coronavirus },
title: 'OTZ clubs',
dashboardIcon: Coronavirus,
};
27 changes: 27 additions & 0 deletions packages/esm-otz-app/src/dashboard.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@import './root.scss';

.noMarker {
list-style-type: none;
}

.noMarker button {
margin-top: 2px;
padding-top: 2px;
padding-bottom: 2px;
font: lighter;
}

.noMarker ul li a {
padding-left: 40px !important;
}

.currentNavItem > a {
background-color: #cecece !important;
color: #161616 !important;
border-left-color: var(--brand-01) !important;
font: bolder;
}

.hide {
display: none;
}
4 changes: 4 additions & 0 deletions packages/esm-otz-app/src/declarations.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare module '@carbon/react';
declare module '*.css';
declare module '*.scss';
declare type SideNavProps = {};
33 changes: 33 additions & 0 deletions packages/esm-otz-app/src/header/otz-header.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Calendar, Location } from '@carbon/react/icons';
import { useSession, formatDate } from '@openmrs/esm-framework';
import OTZIllustration from './otz-illustration.component';
import styles from './otz-header.scss';

export const OTZHeader: React.FC = () => {
const { t } = useTranslation();
const userSession = useSession();
const userLocation = userSession?.sessionLocation?.display;

return (
<div className={styles.header}>
<div className={styles['left-justified-items']}>
<OTZIllustration />
<div className={styles['page-labels']}>
{/* <p>{t('laboratory', 'Laboratory')}</p> */}
<p className={styles['page-name']}>{t('otz', 'OTZ Dashboard')}</p>
</div>
</div>
<div className={styles['right-justified-items']}>
<div className={styles['date-and-location']}>
<Location size={16} />
<span className={styles.value}>{userLocation}</span>
<span className={styles.middot}>&middot;</span>
<Calendar size={16} />
<span className={styles.value}>{formatDate(new Date(), { mode: 'standard' })}</span>
</div>
</div>
</div>
);
};
68 changes: 68 additions & 0 deletions packages/esm-otz-app/src/header/otz-header.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
@use '@carbon/styles/scss/spacing';
@use '@carbon/styles/scss/type';
@import '~@openmrs/esm-styleguide/src/vars';

.header {
@include type.type-style('body-compact-02');
color: $text-02;
height: spacing.$spacing-12;
background-color: $ui-02;
display: flex;
justify-content: space-between;
}

.left-justified-items {
display: flex;
flex-direction: row;
align-items: center;
margin-left: 0.75rem;
}

.right-justified-items {
@include type.type-style('body-compact-02');
color: $text-02;
padding-top: 1rem;
}

.page-name {
@include type.type-style('heading-04');
}

.page-labels {
margin-left: 1rem;

p:first-of-type {
margin-bottom: 0.25rem;
}
}

.date-and-location {
display: flex;
justify-content: flex-end;
align-items: center;
margin-right: 1rem;
}

.value {
margin-left: 0.25rem;
}

.middot {
margin: 0 0.5rem;
}

.view {
@include type.type-style('label-01');
}

svg.iconOverrides {
width: 72 !important;
height: 72 !important;
fill: var(--brand-03);
}

.svgContainer svg {
width: 72px;
height: 72px;
fill: var(--brand-03);
}
13 changes: 13 additions & 0 deletions packages/esm-otz-app/src/header/otz-illustration.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';
import { Medication } from '@carbon/react/icons';
import styles from './otz-header.scss';

const OTZIllustration: React.FC = () => {
return (
<div className={styles.svgContainer}>
<Medication className={styles.iconOverrides} />
</div>
);
};

export default OTZIllustration;
41 changes: 41 additions & 0 deletions packages/esm-otz-app/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { defineConfigSchema, getSyncLifecycle } from '@openmrs/esm-framework';
import { configSchema } from './config-schema';
import Root from './root.component';
import { otzPatientChartMeta } from './dashboard.meta';
import { createDashboardGroup, createDashboardLink } from '@openmrs/esm-patient-common-lib';
import OTZHomePatientTabs from './views/dashboard/patient-list-tabs/otz-patient-list-tabs.component';
import { createLeftPanelLink } from './left-panel-link.component';
import OTZSummaryTiles from './views/dashboard/summary-tiles/otz-summary-tiles.component';

export const moduleName = '@ampath/esm-otz-app';

const options = {
featureName: 'esm-otz-app',
moduleName,
};

export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');

export const root = getSyncLifecycle(Root, options);

export function startupApp() {
defineConfigSchema(moduleName, configSchema);
}

export const otzLeftPanelLink = getSyncLifecycle(
createLeftPanelLink({
name: 'otz',
title: 'OTZ',
slot: 'otz-dashboard-slot',
}),
options,
);
export const otzDashboardTiles = getSyncLifecycle(OTZSummaryTiles, {
featureName: 'otz-home-tiles',
moduleName,
});

export const otzDashboardTabs = getSyncLifecycle(OTZHomePatientTabs, {
featureName: 'otz-home-tabs',
moduleName,
});
32 changes: 32 additions & 0 deletions packages/esm-otz-app/src/left-panel-link.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React, { useMemo } from 'react';
import last from 'lodash-es/last';
import { BrowserRouter, useLocation } from 'react-router-dom';
import { ConfigurableLink } from '@openmrs/esm-framework';

export interface LinkConfig {
name: string;
title: string;
slot?: string;
}

function LinkExtension({ config }: { config: LinkConfig }) {
const { name, title } = config;
const location = useLocation();
const spaBasePath = window.getOpenmrsSpaBase() + 'home';

const urlSegment = useMemo(() => decodeURIComponent(last(location.pathname.split('/'))), [location.pathname]);

return (
<ConfigurableLink
to={spaBasePath + '/' + name}
className={`cds--side-nav__link ${name === urlSegment && 'active-left-nav-link'}`}>
{title}
</ConfigurableLink>
);
}

export const createLeftPanelLink = (config: LinkConfig) => () => (
<BrowserRouter>
<LinkExtension config={config} />
</BrowserRouter>
);
16 changes: 16 additions & 0 deletions packages/esm-otz-app/src/otz.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import OTZHomePatientTabs from './views/dashboard/patient-list-tabs/otz-patient-list-tabs.component';
import OTZSummaryTiles from './views/dashboard/summary-tiles/otz-summary-tiles.component';
import { OTZHeader } from './header/otz-header.component';

const OtzDashboard: React.FC = () => {
return (
<div className={`omrs-main-content`}>
<OTZHeader />
{/* <OTZSummaryTiles /> */}
<OTZHomePatientTabs />
</div>
);
};

export default OtzDashboard;
17 changes: 17 additions & 0 deletions packages/esm-otz-app/src/root.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import OtzDashboard from './otz.component';

const Root: React.FC = () => {
const basePath = window.getOpenmrsSpaBase() + 'home/otz';

return (
<BrowserRouter basename={basePath}>
<Routes>
<Route path="/" element={<OtzDashboard />} />
</Routes>
</BrowserRouter>
);
};

export default Root;
Empty file.
45 changes: 45 additions & 0 deletions packages/esm-otz-app/src/routes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"$schema": "https://json.openmrs.org/routes.schema.json",
"backendDependencies": {
"webservices.rest": "^2.2.0"
},
"pages": [
{
"component": "root",
"route": "otz"
}
],
"extensions": [
{
"component": "root",
"name": "otz-root",
"slot": "otz-dashboard-slot"
},
{
"component": "otzLeftPanelLink",
"name": "otz-left-panel-link",
"slot": "homepage-dashboard-slot",
"meta": {
"name": "otz",
"title": "OTZ",
"slot": "otz-dashboard-slot",
"config": {
"columns": 1,
"type": "grid",
"programme": "otz",
"dashboardTitle": "OTZ Dashboard"
}
}
},
{
"name": "otz-home-tiles-ext",
"slot": "otz-home-tiles-slot",
"component": "otzDashboardTiles"
},
{
"name": "otz-home-tabs-ext",
"slot": "otz-home-tabs-slot",
"component": "otzDashboardTabs"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import {
EmptyStateComingSoon,
findObs,
getObsFromEncounter,
getObsFromEncounters,
OHRIPatientListTabs,
} from '@ohri/openmrs-esm-ohri-commons-lib';
import { moduleName } from '../../../index';
import { useConfig } from '@openmrs/esm-framework';
import dayjs from 'dayjs';

function OTZHomePatientTabs() {
const { t } = useTranslation();
const config = useConfig();

return <>{/* <EmptyStateComingSoon displayText="" headerTitle="" /> */}</>;
}

export default OTZHomePatientTabs;
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { OHRIProgrammeSummaryTiles, getReportingCohort } from '@ohri/openmrs-esm-ohri-commons-lib';
import React, { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useConfig } from '@openmrs/esm-framework';

function OTZSummaryTiles() {
const { t } = useTranslation();
const config = useConfig();
const [activeClientsCount, setActiveClientsCount] = useState(100);
const [covidVaccinatedClientsCount, setCovidVaccinatedClients] = useState(0);
const [covid19PositiveClientsCount, setCovid19PositiveClientsCount] = useState(0);
const [PeopleWithCovidOutcome, setPeopleWithCovidOutcome] = useState(0);

// useEffect(() => {
// getReportingCohort(config.cohorts.covidVaccinatedClients).then((data) => {
// setCovidVaccinatedClients(data.members.length);
// });
// getReportingCohort(config.cohorts.covid19PositiveClients).then((data) => {
// setCovid19PositiveClientsCount(data.members.length);
// });
// getReportingCohort(config.cohorts.covidOutcomesCohortUUID).then((data) => {
// setPeopleWithCovidOutcome(data.members.length);
// });
// }, []);
const tiles = useMemo(
() => [
{
title: t('assessments', 'Assessments'),
linkAddress: '#',
subTitle: t('testsConducted', 'Completed assessments'),
value: activeClientsCount,
},
{
title: t('cases', 'Cases'),
linkAddress: '#',
subTitle: t('peopleTestedPositive', 'People tested positive'),
value: covid19PositiveClientsCount,
},
{
title: t('vaccinations', 'Vaccinations'),
linkAddress: '#',
subTitle: t('peopleVaccinated', 'People vaccinated'),
value: covidVaccinatedClientsCount,
},
{
title: t('outcomes', 'Outcomes'),
linkAddress: '#',
subTitle: t('PeopleWithCovidOutcome', 'People with covid outcome'),
value: PeopleWithCovidOutcome,
},
],
[],
);
return <OHRIProgrammeSummaryTiles tiles={tiles} />;
}

export default OTZSummaryTiles;
11 changes: 11 additions & 0 deletions packages/esm-otz-app/translations/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"assessments": "Assessments",
"cases": "Cases",
"otz": "OTZ Dashboard",
"outcomes": "Outcomes",
"peopleTestedPositive": "People tested positive",
"peopleVaccinated": "People vaccinated",
"PeopleWithCovidOutcome": "People with covid outcome",
"testsConducted": "Completed assessments",
"vaccinations": "Vaccinations"
}
5 changes: 5 additions & 0 deletions packages/esm-otz-app/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../../tsconfig.json",
"include": ["src/**/*"],
"exclude": ["src/**/*.test.tsx"]
}
1 change: 1 addition & 0 deletions packages/esm-otz-app/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('openmrs/default-webpack-config');
1,160 changes: 1,124 additions & 36 deletions yarn.lock

Large diffs are not rendered by default.

0 comments on commit a97f3ab

Please sign in to comment.