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) Introduce amrs core esm #21

Merged
merged 4 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
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>
);
};
Loading
Loading