diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml
index 9fc4b2c..3686969 100644
--- a/.github/workflows/node.js.yml
+++ b/.github/workflows/node.js.yml
@@ -11,7 +11,7 @@ on:
env:
ESM_NAME: "@openmrs/esm-template-app"
- JS_NAME: "openmrs-esm-template-app.js"
+ JS_NAME: "openmrs-esm-patient-medical-supply-orders-app.js"
jobs:
build:
diff --git a/README.md b/README.md
index f0d7581..c414902 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,8 @@
-![Node.js CI](https://github.com/openmrs/openmrs-esm-template-app/workflows/Node.js%20CI/badge.svg)
+![Node.js CI](https://github.com/openmrs/openmrs-esm-patient-medical-supply-orders-app/workflows/Node.js%20CI/badge.svg)
-# OpenMRS ESM Template App
+# Patient Medical Supply Orders App
-This repository provides a starting point for creating your own
-[OpenMRS Microfrontend](https://wiki.openmrs.org/display/projects/OpenMRS+3.0%3A+A+Frontend+Framework+that+enables+collaboration+and+better+User+Experience).
-
-For more information, please see the
-[OpenMRS Frontend Developer Documentation](https://o3-docs.openmrs.org/#/).
-
-In particular, the [Setup](https://o3-docs.openmrs.org/docs/frontend-modules/setup) section can help you get started developing microfrontends in general. The [Creating a microfrontend](https://o3-docs.openmrs.org/docs/recipes/create-a-frontend-module) section provides information about how to use this repository to create your own microfrontend.
+This repository adds a new Patient Medical Supply order type to the Patient Chart Order Basket. The users will be able to place Medical Supply Orders and define the quantity and the Quantity Units for the order placed.
## Running this code
@@ -18,28 +12,7 @@ yarn start # to run the dev server
```
Once it is running, a browser window
-should open with the OpenMRS 3 application. Log in and then navigate to `/openmrs/spa/root`.
-
-## Adapting the code
-
-1. Start by finding and replacing all instances of "template" with the name
- of your microfrontend.
-2. Update `index.ts` as appropriate, at least changing the feature name and the page name and route.
-3. Rename the `root.*` family of files to have the name of your first page.
-4. Delete the contents of the objects in `config-schema`. Start filling them back in once you have a clear idea what will need to be configured.
-5. Delete the `greeter` and `patient-getter` directories, and the contents of `root.component.tsx`.
-6. Delete the contents of `translations/en.json`.
-7. Open up `.github/workflows` and adapt it to your needs. If you're writing
- a microfrontend that will be managed by the community, you might be able to
- just replace all instances of `template` with your microfrontend's name.
- However, if you're writing a microfrontend for a specific organization or
- implementation, you will probably need to configure GitHub Actions differently.
-8. Delete the contents of this README and write a short explanation of what
- you intend to build. Links to planning or design documents can be very helpful.
-
-At this point, you should be able to write your first page as a React application.
-
-Check out the [Medication dispensing app](https://github.com/openmrs/openmrs-esm-dispensing-app) for an example of a non-trivial app built using the Template.
+should open with the OpenMRS 3 application.
## Integrating it into your application
diff --git a/package.json b/package.json
index dfe321f..e570155 100644
--- a/package.json
+++ b/package.json
@@ -1,9 +1,9 @@
{
- "name": "@openmrs/esm-template-app",
+ "name": "@openmrs/esm-patient-medical-supply-orders-app",
"version": "4.0.0",
"license": "MPL-2.0",
"description": "An OpenMRS seed application for building microfrontends",
- "browser": "dist/openmrs-esm-template-app.js",
+ "browser": "dist/openmrs-esm-patient-medical-supply-orders-app.js",
"main": "src/index.ts",
"source": true,
"scripts": {
@@ -30,29 +30,36 @@
],
"repository": {
"type": "git",
- "url": "git+https://github.com/openmrs/openmrs-esm-template-app.git"
+ "url": "git+https://github.com/openmrs/openmrsesm-patient-medical-supply-orders-app.git"
},
- "homepage": "https://github.com/openmrs/openmrs-esm-template-app#readme",
+ "homepage": "https://github.com/openmrs/openmrsesm-patient-medical-supply-orders-app#readme",
"publishConfig": {
"access": "public"
},
"bugs": {
- "url": "https://github.com/openmrs/openmrs-esm-template-app/issues"
+ "url": "https://github.com/openmrs/openmrsesm-patient-medical-supply-orders-app/issues"
},
"dependencies": {
- "@carbon/react": "^1.68.0",
- "lodash-es": "^4.17.21"
+ "@hookform/resolvers": "^3.9.1",
+ "lodash-es": "^4.17.21",
+ "react-hook-form": "^7.53.2",
+ "zod": "^3.23.8"
},
"peerDependencies": {
- "@openmrs/esm-framework": "*",
+ "@carbon/react": "^1.x",
+ "@openmrs/esm-framework": "5.x",
+ "@openmrs/esm-patient-common-lib": "8.x",
"dayjs": "1.x",
"react": "18.x",
"react-i18next": "11.x",
"react-router-dom": "6.x",
- "rxjs": "6.x"
+ "rxjs": "6.x",
+ "swr": "2.x"
},
"devDependencies": {
+ "@carbon/react": "^1.71.0",
"@openmrs/esm-framework": "next",
+ "@openmrs/esm-patient-common-lib": "next",
"@openmrs/esm-styleguide": "next",
"@playwright/test": "^1.42.1",
"@swc/cli": "^0.3.12",
@@ -93,6 +100,7 @@
"react-router-dom": "^6.14.1",
"rxjs": "^6.6.7",
"swc-loader": "^0.2.3",
+ "swr": "^2.2.5",
"turbo": "^2.2.3",
"typescript": "^4.9.5",
"webpack": "^5.88.1",
@@ -102,5 +110,8 @@
"packages/**/src/**/*.{ts,tsx}": "eslint --cache --fix --max-warnings 0",
"*.{css,scss,ts,tsx}": "prettier --write --list-different"
},
- "packageManager": "yarn@4.3.1"
+ "packageManager": "yarn@4.3.1",
+ "resolutions": {
+ "@openmrs/esm-patient-common-lib": "portal:/Users/vasharma05/Projects/patient-chart/packages/esm-patient-common-lib"
+ }
}
diff --git a/src/boxes/extensions/blue-box.component.tsx b/src/boxes/extensions/blue-box.component.tsx
deleted file mode 100644
index 8fc2150..0000000
--- a/src/boxes/extensions/blue-box.component.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * This component demonstrates the creation of an extension.
- *
- * Check out the Extension System docs:
- * https://o3-docs.vercel.app/docs/extension-system
- */
-
-import React from 'react';
-import styles from './box.scss';
-
-const BlueBox: React.FC = () => {
- return
;
-};
-
-export default BlueBox;
diff --git a/src/boxes/extensions/box.scss b/src/boxes/extensions/box.scss
deleted file mode 100644
index 740603d..0000000
--- a/src/boxes/extensions/box.scss
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Extensions should supply the minimum amount of styling
- * necessary. Here, the extensions set only their colors.
- * Their sizes and other general features of their display
- * is controlled by the slot. */
-@use "@carbon/layout";
-@use '@openmrs/esm-styleguide/src/vars' as *;
-
- .blue {
- background-color: darkblue;
-}
-
-.red {
- background-color: darkred;
-}
-
-/* Brand colors are special. They must be included using a
- * SASS mix-in (shown here) or using a CSS variable like
- * `var(--brand-01)`. */
-.brand {
- @include brand-01(background-color);
- color: white;
- padding: layout.$spacing-03;
-}
diff --git a/src/boxes/extensions/brand-box.component.tsx b/src/boxes/extensions/brand-box.component.tsx
deleted file mode 100644
index 2389aa9..0000000
--- a/src/boxes/extensions/brand-box.component.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * This component demonstrates the creation of an extension.
- *
- * Check out the Extension System docs:
- * https://o3-docs.vercel.app/docs/extension-system
- */
-
-import React from 'react';
-import styles from './box.scss';
-
-const RedBox: React.FC = () => {
- return
;
-};
-
-export default RedBox;
diff --git a/src/boxes/extensions/red-box.component.tsx b/src/boxes/extensions/red-box.component.tsx
deleted file mode 100644
index 0219b70..0000000
--- a/src/boxes/extensions/red-box.component.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * This component demonstrates the creation of an extension.
- *
- * Check out the Extension System docs:
- * https://o3-docs.vercel.app/docs/extension-system
- */
-
-import React from 'react';
-import styles from './box.scss';
-
-const RedBox: React.FC = () => {
- return
;
-};
-
-export default RedBox;
diff --git a/src/boxes/slot/boxes.component.tsx b/src/boxes/slot/boxes.component.tsx
deleted file mode 100644
index 3d516fc..0000000
--- a/src/boxes/slot/boxes.component.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import React from 'react';
-import { useTranslation } from 'react-i18next';
-import { Extension, ExtensionSlot } from '@openmrs/esm-framework';
-import styles from './boxes.scss';
-
-export const Boxes: React.FC = () => {
- const { t } = useTranslation();
-
- return (
-
-
{t('extensionSystem', 'Extension system')}
-
- {t(
- 'extensionExplainer',
- 'Here are some colored boxes. Because they are attached as extensions within a slot, an admin can change what boxes are shown using configuration. These boxes happen to be defined in this module, but they could attach to this slot even if they were in a different module.',
- )}
-
-
-
-
-
-
-
- );
-};
diff --git a/src/boxes/slot/boxes.scss b/src/boxes/slot/boxes.scss
deleted file mode 100644
index 0414982..0000000
--- a/src/boxes/slot/boxes.scss
+++ /dev/null
@@ -1,29 +0,0 @@
-/* General features of extension styling are controlled
- * at the slot level. The boxes should know as little as
- * possible about the context where they are used, so
- * they do not set their own sizes.
- *
- * `> * > *` is used to target the outermost DOM node of
- * the extension.
- */
- @use '@carbon/layout';
-
-.box > * > * {
- height: layout.$spacing-10;
- width: layout.$spacing-10;
-}
-
-.boxes {
- margin: layout.$spacing-07 0;
- display: flex;
- gap: layout.$spacing-05;
-}
-
-.container {
- max-width: 50rem;
- margin-top: layout.$spacing-09;
-
- > * + * {
- margin-top: layout.$spacing-06;
- }
-}
diff --git a/src/config-schema.ts b/src/config-schema.ts
index d07a785..9b3df4a 100644
--- a/src/config-schema.ts
+++ b/src/config-schema.ts
@@ -1,43 +1,72 @@
-import { Type, validator } from '@openmrs/esm-framework';
+import { Type, validator, validators } from '@openmrs/esm-framework';
+import _default from 'react-hook-form/dist/utils/createSubject';
-/**
- * This is the config schema. It expects a configuration object which
- * looks like this:
- *
- * ```json
- * { "casualGreeting": true, "whoToGreet": ["Mom"] }
- * ```
- *
- * In OpenMRS Microfrontends, all config parameters are optional. Thus,
- * all elements must have a reasonable default. A good default is one
- * that works well with the reference application.
- *
- * To understand the schema below, please read the configuration system
- * documentation:
- * https://openmrs.github.io/openmrs-esm-core/#/main/config
- * Note especially the section "How do I make my module configurable?"
- * https://openmrs.github.io/openmrs-esm-core/#/main/config?id=im-developing-an-esm-module-how-do-i-make-it-configurable
- * and the Schema Reference
- * https://openmrs.github.io/openmrs-esm-core/#/main/config?id=schema-reference
- */
export const configSchema = {
- casualGreeting: {
- _type: Type.Boolean,
- _default: false,
- _description: 'Whether to use a casual greeting (or a formal one).',
- },
- whoToGreet: {
+ orderTypes: {
_type: Type.Array,
- _default: ['World'],
- _description: 'Who should be greeted. Names will be separated by a comma and space.',
_elements: {
- _type: Type.String,
+ _type: Type.Object,
+ orderTypeUuid: {
+ _type: Type.UUID,
+ _description: 'The UUID of the order type with the listed in the order basket',
+ },
+ orderableConceptClasses: {
+ _type: Type.Array,
+ _description:
+ 'The concept class of the orderable concepts. By default it will look for concept class in the order type response',
+ _elements: {
+ _type: Type.UUID,
+ },
+ },
+ orderableConceptSets: {
+ _type: Type.Array,
+ _description:
+ "UUIDs of concepts that represent orderable concepts. Either the `conceptClass` should be given, or the `orderableConcepts`. If the orderableConcepts are not given, then it'll search concepts by concept class.",
+ _elements: {
+ _type: Type.UUID,
+ },
+ },
+ },
+ _default: [
+ {
+ orderTypeUuid: '67a92bd6-0f88-11ea-8d71-362b9e155667',
+ orderableConceptClasses: [],
+ orderableConceptSets: [],
+ },
+ ],
+ },
+ quantityUnits: {
+ _type: Type.Object,
+ _description: 'Concept to be used for fetching quantity units',
+ _default: {
+ conceptUuid: '162402AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
+ map: 'setMembers',
+ },
+ // _elements: {
+ conceptUuid: {
+ _type: Type.UUID,
+ _description: 'UUID for the quantity units concepts',
+ _default: '162402AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
+ },
+ map: {
+ _type: Type.UUID,
+ _description:
+ "Whether to use the concept answers of the setMembers of the concept. One of 'answers' or 'setMembers'.",
+ _default: 'setMembers',
+ _validators: [validators.oneOf(['answers', 'setMembers'])],
},
- _validators: [validator((v) => v.length > 0, 'At least one person must be greeted.')],
},
+ // },
};
-export type Config = {
- casualGreeting: boolean;
- whoToGreet: Array;
+export type ConfigObject = {
+ orderTypes: Array<{
+ orderTypeUuid: string;
+ orderableConceptClasses: Array;
+ orderableConceptSets: Array;
+ }>;
+ quantityUnits: {
+ conceptUuid: string;
+ map: 'answers' | 'setMembers';
+ };
};
diff --git a/src/constants.ts b/src/constants.ts
new file mode 100644
index 0000000..8a8d205
--- /dev/null
+++ b/src/constants.ts
@@ -0,0 +1 @@
+export const moduleName = '@openmrs/esm-patient-medical-supply-orders-app';
diff --git a/src/greeter/greeter.component.tsx b/src/greeter/greeter.component.tsx
deleted file mode 100644
index e2ecc20..0000000
--- a/src/greeter/greeter.component.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * This component demonstrates usage of the config object. Its structure
- * comes from `../config-schema.ts`. For more information about the
- * configuration system, read the docs: https://o3-docs.vercel.app/docs/configuration-system
- */
-import React from 'react';
-import { Tile } from '@carbon/react';
-import { Trans, useTranslation } from 'react-i18next';
-import { useConfig } from '@openmrs/esm-framework';
-import { type Config } from '../config-schema';
-import styles from './greeter.scss';
-
-const Greeter: React.FC = () => {
- const { t } = useTranslation();
- const config: Config = useConfig();
-
- return (
-
-
{t('configSystem', 'Configuration system')}
-
-
- The greeting shown below is driven by the configuration system. To change the configuration properties, click
- the spanner icon in the navbar to pull up the Implementer Tools panel. Then, type template into the{' '}
- Search configuration input. This should filter the configuration properties to show only those that
- are relevant to this module. You can change the values of these properties and click Save to see the
- changes reflected in the UI
-
- .
-
-
-
- {config.casualGreeting ? hey : hello }{' '}
- {/* t('world') */}
- {config.whoToGreet.join(', ')}!
-
-
-
-
- );
-};
-
-export default Greeter;
diff --git a/src/greeter/greeter.scss b/src/greeter/greeter.scss
deleted file mode 100644
index 14621e2..0000000
--- a/src/greeter/greeter.scss
+++ /dev/null
@@ -1,20 +0,0 @@
-@use '@carbon/layout';
-@use '@carbon/type';
-
-.container {
- max-width: 50rem;
-
- > * + * {
- margin-top: layout.$spacing-05;
- }
-}
-
-.greeting {
- text-transform: capitalize;
-}
-
-.tile {
- border: 1px solid lightgray;
- max-width: 15rem;
- @include type.type-style('heading-compact-01');
-}
diff --git a/src/greeter/greeter.test.tsx b/src/greeter/greeter.test.tsx
deleted file mode 100644
index c71ace6..0000000
--- a/src/greeter/greeter.test.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import React from 'react';
-import { render, screen } from '@testing-library/react';
-import { useConfig } from '@openmrs/esm-framework';
-import { Config } from '../config-schema';
-import Greeter from './greeter.component';
-
-const mockUseConfig = jest.mocked(useConfig);
-
-it('displays the expected default text', () => {
- const config: Config = { casualGreeting: false, whoToGreet: ['World'] };
- mockUseConfig.mockReturnValue(config);
-
- render( );
-
- expect(screen.getByText(/world/i)).toHaveTextContent('hello World!');
-});
-
-it('casually greets my friends', () => {
- const config: Config = {
- casualGreeting: true,
- whoToGreet: ['Ariel', 'Barak', 'Callum'],
- };
- mockUseConfig.mockReturnValue(config);
-
- render( );
-
- expect(screen.getByText(/ariel/i)).toHaveTextContent('hey Ariel, Barak, Callum!');
-});
diff --git a/src/index.ts b/src/index.ts
index f889251..ccfcfba 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -6,44 +6,39 @@
*/
import { getAsyncLifecycle, defineConfigSchema } from '@openmrs/esm-framework';
import { configSchema } from './config-schema';
-
-const moduleName = '@openmrs/esm-template-app';
+import { moduleName } from './constants';
const options = {
- featureName: 'root-world',
+ featureName: 'patient-medical-supply-order',
moduleName,
};
-/**
- * This tells the app shell how to obtain translation files: that they
- * are JSON files in the directory `../translations` (which you should
- * see in the directory structure).
- */
export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
-/**
- * This function performs any setup that should happen at microfrontend
- * load-time (such as defining the config schema) and then returns an
- * object which describes how the React application(s) should be
- * rendered.
- */
export function startupApp() {
defineConfigSchema(moduleName, configSchema);
}
-/**
- * This named export tells the app shell that the default export of `root.component.tsx`
- * should be rendered when the route matches `root`. The full route
- * will be `openmrsSpaBase() + 'root'`, which is usually
- * `/openmrs/spa/root`.
- */
-export const root = getAsyncLifecycle(() => import('./root.component'), options);
-
-/**
- * The following are named exports for the extensions defined in this frontend modules. See the `routes.json` file to see how these are used.
- */
-export const redBox = getAsyncLifecycle(() => import('./boxes/extensions/red-box.component'), options);
-
-export const blueBox = getAsyncLifecycle(() => import('./boxes/extensions/blue-box.component'), options);
-
-export const brandBox = getAsyncLifecycle(() => import('./boxes/extensions/brand-box.component'), options);
+export const medicalSupplyOrderPanel = getAsyncLifecycle(
+ () => import('./medical-orders/medical-supply-order-type.component'),
+ options,
+);
+
+// t('searchMedicalSupplyOrderables', 'Search medical supply orderables')
+export const searchMedicalSupplyOrderables = getAsyncLifecycle(
+ () =>
+ import(
+ './medical-orders/medical-supply-orderable-concept-search/medical-supply-orderable-concept-search.workspace'
+ ),
+ options,
+);
+
+export const modifyMedicalSupplyOrderMenuItem = getAsyncLifecycle(
+ () => import('./medical-orders/action-menu-items/modify-medical-supply-order-menu-item.extension'),
+ options,
+);
+
+export const medicalSupplyOrderDetailTable = getAsyncLifecycle(
+ () => import('./medical-orders/medical-supply-detail-table/medical-supply-detail.extension'),
+ options,
+);
diff --git a/src/medical-orders/action-menu-items/modify-medical-supply-order-menu-item.extension.tsx b/src/medical-orders/action-menu-items/modify-medical-supply-order-menu-item.extension.tsx
new file mode 100644
index 0000000..c9a5dd7
--- /dev/null
+++ b/src/medical-orders/action-menu-items/modify-medical-supply-order-menu-item.extension.tsx
@@ -0,0 +1,73 @@
+import { OverflowMenuItem } from '@carbon/react';
+import { useLaunchWorkspaceRequiringVisit, useOrderBasket, type Order } from '@openmrs/esm-patient-common-lib';
+import React from 'react';
+import { type MedicalSupplyOrderBasketItem } from '../types';
+import { buildMedicalSupplyOrderItem } from '../resources';
+import { useTranslation } from 'react-i18next';
+import { OverflowMenu } from '@carbon/react';
+import { Layer } from '@carbon/react';
+
+interface ModifyMedicalSupplyOrderMenuItemProps {
+ orderItem: Order;
+ className: string;
+ responsiveSize: string;
+}
+
+export default function ModifyMedicalSupplyOrderMenuItem({
+ className,
+ orderItem,
+ responsiveSize,
+}: ModifyMedicalSupplyOrderMenuItemProps) {
+ const { t } = useTranslation();
+ const openMedicalSupplyOrderFormWorkspace = useLaunchWorkspaceRequiringVisit(
+ 'medical-supply-orderable-concept-workspace',
+ );
+ const launchOrderBasket = useLaunchWorkspaceRequiringVisit('order-basket');
+
+ const { orders, setOrders } = useOrderBasket(orderItem.orderType.uuid);
+ const alreadyInBasket = orders.some((x) => x.uuid === orderItem.uuid);
+
+ const handleModifyOrder = () => {
+ const order = buildMedicalSupplyOrderItem(orderItem, 'REVISE');
+ setOrders([...orders, order]);
+ openMedicalSupplyOrderFormWorkspace({
+ orderTypeUuid: orderItem.orderType.uuid,
+ order,
+ });
+ };
+
+ const handleCancelOrder = () => {
+ const order = buildMedicalSupplyOrderItem(orderItem, 'DISCONTINUE');
+ setOrders([...orders, order]);
+ launchOrderBasket();
+ };
+
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/src/medical-orders/medical-supply-detail-table/medical-supply-detail.extension.tsx b/src/medical-orders/medical-supply-detail-table/medical-supply-detail.extension.tsx
new file mode 100644
index 0000000..e150e23
--- /dev/null
+++ b/src/medical-orders/medical-supply-detail-table/medical-supply-detail.extension.tsx
@@ -0,0 +1,28 @@
+import React, { useMemo } from 'react';
+import styles from './medical-supply-detail.scss';
+import { type Order } from '@openmrs/esm-patient-common-lib';
+import { useTranslation } from 'react-i18next';
+import { useLayoutType } from '@openmrs/esm-framework';
+
+interface TestOrderProps {
+ orderItem: Order;
+}
+
+const MedicalSupplyOrderDetailTable: React.FC = ({ orderItem }) => {
+ const { t } = useTranslation();
+ return (
+
+
+ {t('quantity', 'Quantity')} {orderItem.quantity ?? 0}{' '}
+ {orderItem.quantityUnits?.display}
+
+ {orderItem.instructions && (
+
+ {t('instructions', 'Instructions')} {orderItem.instructions}
+
+ )}
+
+ );
+};
+
+export default MedicalSupplyOrderDetailTable;
diff --git a/src/medical-orders/medical-supply-detail-table/medical-supply-detail.scss b/src/medical-orders/medical-supply-detail-table/medical-supply-detail.scss
new file mode 100644
index 0000000..279f2d6
--- /dev/null
+++ b/src/medical-orders/medical-supply-detail-table/medical-supply-detail.scss
@@ -0,0 +1,11 @@
+@use '@carbon/layout';
+@use '@carbon/type';
+@use '@openmrs/esm-styleguide/src/vars' as *;
+
+.label01 {
+ @include type.type-style('label-01');
+}
+
+.order {
+ @include type.type-style('body-short-02');
+}
\ No newline at end of file
diff --git a/src/medical-orders/medical-supply-order-form/medical-supply-order-form.component.tsx b/src/medical-orders/medical-supply-order-form/medical-supply-order-form.component.tsx
new file mode 100644
index 0000000..c0573de
--- /dev/null
+++ b/src/medical-orders/medical-supply-order-form/medical-supply-order-form.component.tsx
@@ -0,0 +1,353 @@
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import classNames from 'classnames';
+import {
+ type DefaultPatientWorkspaceProps,
+ launchPatientWorkspace,
+ useOrderBasket,
+ useOrderType,
+ priorityOptions,
+} from '@openmrs/esm-patient-common-lib';
+import { translateFrom, useLayoutType, useSession, useConfig, ExtensionSlot } from '@openmrs/esm-framework';
+import {
+ Button,
+ ButtonSet,
+ Column,
+ ComboBox,
+ Form,
+ Grid,
+ InlineNotification,
+ Layer,
+ TextArea,
+ TextInput,
+} from '@carbon/react';
+import { useTranslation } from 'react-i18next';
+import { Controller, type FieldErrors, useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { z } from 'zod';
+import styles from './medical-supply-order-form.scss';
+import { type Concept, ordersEqual, prepOrderPostData, useQuantityUnits } from '../resources';
+import { moduleName } from '../../constants';
+import { type MedicalSupplyOrderBasketItem } from '../types';
+import { NumberInput } from '@carbon/react';
+import { Select } from '@carbon/react';
+import { SelectSkeleton } from '@carbon/react';
+
+export interface OrderFormProps extends DefaultPatientWorkspaceProps {
+ initialOrder: MedicalSupplyOrderBasketItem;
+ orderTypeUuid: string;
+ orderableConceptSets: Array;
+}
+
+// Designs:
+// https://app.zeplin.io/project/60d5947dd636aebbd63dce4c/screen/640b06c440ee3f7af8747620
+// https://app.zeplin.io/project/60d5947dd636aebbd63dce4c/screen/640b06d286e0aa7b0316db4a
+export function OrderForm({
+ initialOrder,
+ closeWorkspace,
+ closeWorkspaceWithSavedChanges,
+ promptBeforeClosing,
+ orderTypeUuid,
+}: OrderFormProps) {
+ const { t } = useTranslation();
+ const isTablet = useLayoutType() === 'tablet';
+ const session = useSession();
+ const isEditing = useMemo(() => initialOrder && initialOrder.action === 'REVISE', [initialOrder]);
+ const { orders, setOrders } = useOrderBasket(orderTypeUuid, prepOrderPostData);
+ const [showErrorNotification, setShowErrorNotification] = useState(false);
+ const { orderType } = useOrderType(orderTypeUuid);
+ const { concepts, isLoadingQuantityUnits, errorFetchingQuantityUnits } = useQuantityUnits();
+
+ const OrderFormSchema = useMemo(
+ () =>
+ z.object({
+ instructions: z.string().optional(),
+ urgency: z.string().refine((value) => value !== '', {
+ message: t('addLabOrderPriorityRequired', 'Priority is required'),
+ }),
+ quantity: z.number({
+ required_error: t('quantityRequired', 'Quantity is required'),
+ invalid_type_error: t('quantityRequired', 'Quantity is required'),
+ }),
+ quantityUnits: z.object(
+ {
+ display: z.string(),
+ uuid: z.string(),
+ },
+ {
+ required_error: t('quantityUnitsRequired', 'Quantity units is required'),
+ invalid_type_error: t('quantityUnitsRequired', 'Quantity units is required'),
+ },
+ ),
+ accessionNumber: z.string().optional(),
+ concept: z.object(
+ { display: z.string(), uuid: z.string() },
+ {
+ required_error: t('addOrderableConceptRequired', 'Orderable concept is required'),
+ invalid_type_error: t('addOrderableConceptRequired', 'Orderable concept is required'),
+ },
+ ),
+ }),
+ [t],
+ );
+
+ const {
+ control,
+ handleSubmit,
+ formState: { errors, defaultValues, isDirty },
+ } = useForm({
+ mode: 'all',
+ resolver: zodResolver(OrderFormSchema),
+ defaultValues: {
+ ...initialOrder,
+ },
+ });
+
+ const filterItemsByName = useCallback((menu) => {
+ return menu?.item?.value?.toLowerCase().includes(menu?.inputValue?.toLowerCase());
+ }, []);
+
+ const handleFormSubmission = useCallback(
+ (data: MedicalSupplyOrderBasketItem) => {
+ const finalizedOrder: MedicalSupplyOrderBasketItem = {
+ ...initialOrder,
+ ...data,
+ };
+ finalizedOrder.orderer = session.currentProvider.uuid;
+
+ const newOrders = [...orders];
+ const existingOrder = orders.find((order) => ordersEqual(order, finalizedOrder));
+
+ if (existingOrder) {
+ newOrders[orders.indexOf(existingOrder)] = {
+ ...finalizedOrder,
+ // Incomplete orders should be marked completed on saving the form
+ isOrderIncomplete: false,
+ };
+ } else {
+ newOrders.push(finalizedOrder);
+ }
+
+ setOrders(newOrders);
+
+ closeWorkspaceWithSavedChanges({
+ onWorkspaceClose: () => launchPatientWorkspace('order-basket'),
+ });
+ },
+ [orders, setOrders, session?.currentProvider?.uuid, closeWorkspaceWithSavedChanges, initialOrder],
+ );
+
+ const cancelOrder = useCallback(() => {
+ setOrders(orders.filter((order) => order.concept.uuid !== defaultValues.concept.conceptUuid));
+ closeWorkspace({
+ onWorkspaceClose: () => launchPatientWorkspace('order-basket'),
+ });
+ }, [closeWorkspace, orders, setOrders, defaultValues]);
+
+ const onError = (errors: FieldErrors) => {
+ if (errors) {
+ setShowErrorNotification(true);
+ }
+ };
+
+ useEffect(() => {
+ promptBeforeClosing(() => isDirty);
+ }, [isDirty, promptBeforeClosing]);
+
+ const responsiveSize = isTablet ? 'lg' : 'sm';
+
+ return (
+ <>
+
+ >
+ );
+}
+
+function InputWrapper({ children }) {
+ const isTablet = useLayoutType() === 'tablet';
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/medical-orders/medical-supply-order-form/medical-supply-order-form.scss b/src/medical-orders/medical-supply-order-form/medical-supply-order-form.scss
new file mode 100644
index 0000000..031f728
--- /dev/null
+++ b/src/medical-orders/medical-supply-order-form/medical-supply-order-form.scss
@@ -0,0 +1,89 @@
+@use '@carbon/layout';
+@use '@carbon/type';
+@use '@openmrs/esm-styleguide/src/vars' as *;
+
+.orderForm {
+ flex-grow: 1;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+
+ :global(.cds--css-grid) {
+ max-width: unset;
+ }
+
+ :global(.cds--css-grid-column) {
+ margin: 0 !important;
+ }
+
+ :global(.cds--subgrid) {
+ margin: 0 calc(layout.$spacing-03 * -1) !important;
+ }
+
+ :global(.cds--number--nosteppers.cds--number input[type='number']) {
+ padding-right: layout.$spacing-05;
+ }
+
+ :global(.cds--number__control-btn) {
+ border: 0;
+ }
+}
+
+.form {
+ margin: layout.$spacing-05;
+}
+
+.gridRow {
+ padding: 0px;
+
+ :global(.cds--number) {
+ display: inline-block;
+ }
+}
+
+.field {
+ margin-bottom: layout.$spacing-05;
+}
+
+.buttonSet {
+ :global(.cds--btn) {
+ width: 50%;
+ max-width: unset;
+ }
+}
+
+.errorContainer {
+ margin: layout.$spacing-05;
+}
+
+.errorNotification {
+ margin: layout.$spacing-03 0;
+}
+
+.testTypeLabel {
+ @include type.type-style('label-01');
+ color: $text-02;
+}
+
+/* Tablet */
+:global(.omrs-breakpoint-lt-desktop) {
+ .orderForm {
+ height: calc(100vh - layout.$spacing-12 - var(--workspace-header-height));
+ background-color: #ededed;
+ }
+
+ .buttonSet {
+ padding: layout.$spacing-06 layout.$spacing-05;
+ background-color: $ui-02;
+ }
+
+ .errorContainer {
+ margin: layout.$spacing-05;
+ margin-bottom: calc(var(--bottom-nav-height) + layout.$spacing-05);
+ }
+}
+
+.testType {
+ @include type.type-style('body-compact-02');
+ margin-top: layout.$spacing-03;
+}
diff --git a/src/medical-orders/medical-supply-order-panel.scss b/src/medical-orders/medical-supply-order-panel.scss
new file mode 100644
index 0000000..f08157f
--- /dev/null
+++ b/src/medical-orders/medical-supply-order-panel.scss
@@ -0,0 +1,81 @@
+@use '@carbon/colors';
+@use '@carbon/layout';
+@use '@carbon/type';
+@use '@openmrs/esm-styleguide/src/vars' as *;
+
+.desktopTile {
+ border-left: layout.$spacing-02 solid colors.$cyan-20;
+ background-color: $ui-02;
+ border-top: 1px solid colors.$cyan-20;
+ border-right: none;
+ padding: 0;
+}
+
+.tabletTile {
+ @extend .desktopTile;
+ border-top: 1px solid #3778c1;
+}
+
+.collapsedTile {
+ border-bottom: none;
+ min-height: 0;
+
+ .orderBasketHeader {
+ margin-bottom: 0;
+ }
+}
+
+.tabletTile .collapsedTile {
+ border-bottom: 1px solid $grey-2;
+}
+
+.container {
+ background-color: $ui-02;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ text-align: left;
+}
+
+.desktopTile .container {
+ h4 {
+ @include type.type-style('heading-compact-02');
+ color: $text-02;
+ }
+}
+
+.tabletTile .container {
+ padding: layout.$spacing-03;
+
+ h4 {
+ @include type.type-style('heading-03');
+ color: $ui-05;
+ }
+}
+
+.heading {
+ margin-left: layout.$spacing-03;
+}
+
+.iconAndLabel {
+ display: flex;
+ align-items: center;
+ margin: layout.$spacing-03;
+}
+
+.buttonContainer {
+ display: flex;
+ align-items: center;
+}
+
+.addButton {
+ svg {
+ fill: currentColor !important;
+ }
+}
+
+.chevron {
+ svg {
+ fill: currentColor;
+ }
+}
diff --git a/src/medical-orders/medical-supply-order-type.component.tsx b/src/medical-orders/medical-supply-order-type.component.tsx
new file mode 100644
index 0000000..af76be8
--- /dev/null
+++ b/src/medical-orders/medical-supply-order-type.component.tsx
@@ -0,0 +1,235 @@
+import React, { type ComponentProps, useCallback, useEffect, useMemo, useState } from 'react';
+import { Button, Tile } from '@carbon/react';
+import classNames from 'classnames';
+import styles from './medical-supply-order-panel.scss';
+import {
+ AddIcon,
+ ChevronDownIcon,
+ ChevronUpIcon,
+ closeWorkspace,
+ type DefaultWorkspaceProps,
+ useConfig,
+ useLayoutType,
+} from '@openmrs/esm-framework';
+import { useTranslation } from 'react-i18next';
+import { launchPatientWorkspace, useOrderBasket, useOrderType } from '@openmrs/esm-patient-common-lib';
+import OrderBasketItemTile from './order-basket-item-tile.component';
+import { prepOrderPostData } from './resources';
+import { type ConfigObject } from '../config-schema';
+import type { MedicalSupplyOrderBasketItem } from './types';
+
+const MedicalSupplyOrderPanel: React.FC = () => {
+ const { orderTypes } = useConfig();
+ return (
+ <>
+ {orderTypes.map(({ orderTypeUuid, orderableConceptClasses, orderableConceptSets }) => (
+
+ ))}
+ >
+ );
+};
+
+interface MedicalSupplyOrderTypeProps {
+ orderTypeUuid: string;
+ orderableConceptClasses: Array;
+ orderableConceptSets: Array;
+}
+
+const MedicalSupplyOrderType: React.FC = ({
+ orderTypeUuid,
+ orderableConceptSets,
+ orderableConceptClasses,
+}) => {
+ const { t } = useTranslation();
+ const isTablet = useLayoutType() === 'tablet';
+ const { orderType, isLoadingOrderType } = useOrderType(orderTypeUuid);
+
+ const { orders, setOrders } = useOrderBasket(orderTypeUuid, prepOrderPostData);
+ const [isExpanded, setIsExpanded] = useState(orders.length > 0);
+ const {
+ incompleteOrderBasketItems,
+ newOrderBasketItems,
+ renewedOrderBasketItems,
+ revisedOrderBasketItems,
+ discontinuedOrderBasketItems,
+ } = useMemo(() => {
+ const incompleteOrderBasketItems: Array = [];
+ const newOrderBasketItems: Array = [];
+ const renewedOrderBasketItems: Array = [];
+ const revisedOrderBasketItems: Array = [];
+ const discontinuedOrderBasketItems: Array = [];
+
+ orders.forEach((order) => {
+ if (order?.isOrderIncomplete) {
+ incompleteOrderBasketItems.push(order);
+ } else if (order.action === 'NEW') {
+ newOrderBasketItems.push(order);
+ } else if (order.action === 'RENEW') {
+ renewedOrderBasketItems.push(order);
+ } else if (order.action === 'REVISE') {
+ revisedOrderBasketItems.push(order);
+ } else if (order.action === 'DISCONTINUE') {
+ discontinuedOrderBasketItems.push(order);
+ }
+ });
+
+ return {
+ incompleteOrderBasketItems,
+ newOrderBasketItems,
+ renewedOrderBasketItems,
+ revisedOrderBasketItems,
+ discontinuedOrderBasketItems,
+ };
+ }, [orders]);
+
+ const openConceptSearch = () => {
+ closeWorkspace('order-basket', {
+ ignoreChanges: true,
+ onWorkspaceClose: () =>
+ launchPatientWorkspace('medical-supply-orderable-concept-workspace', {
+ orderTypeUuid,
+ }),
+ });
+ };
+
+ const openOrderForm = (order: MedicalSupplyOrderBasketItem) => {
+ closeWorkspace('order-basket', {
+ ignoreChanges: true,
+ onWorkspaceClose: () =>
+ launchPatientWorkspace('medical-supply-orderable-concept-workspace', {
+ order,
+ orderTypeUuid,
+ }),
+ });
+ };
+
+ const removeOrder = useCallback(
+ (order: MedicalSupplyOrderBasketItem) => {
+ const newOrders = [...orders];
+ newOrders.splice(orders.indexOf(order), 1);
+ setOrders(newOrders);
+ },
+ [orders, setOrders],
+ );
+
+ useEffect(() => {
+ setIsExpanded(orders.length > 0);
+ }, [orders]);
+
+ if (isLoadingOrderType) {
+ return null;
+ }
+
+ return (
+
+
+
+ {/* */}
+ {/* TODO: Add Icon */}
+
{`${orderType?.display} (${orders.length})`}
+
+
+
) => }
+ iconDescription="Add medication"
+ onClick={openConceptSearch}
+ size={isTablet ? 'md' : 'sm'}
+ >
+ {t('add', 'Add')}
+
+
) =>
+ isExpanded ? :
+ }
+ iconDescription="View"
+ disabled={orders.length === 0}
+ onClick={() => setIsExpanded(!isExpanded)}
+ >
+ {t('add', 'Add')}
+
+
+
+ {isExpanded && (
+ <>
+ {incompleteOrderBasketItems.length > 0 && (
+ <>
+ {incompleteOrderBasketItems.map((order, index) => (
+ openOrderForm(order)}
+ onRemoveClick={() => removeOrder(order)}
+ />
+ ))}
+ >
+ )}
+ {newOrderBasketItems.length > 0 && (
+ <>
+ {newOrderBasketItems.map((order, index) => (
+ openOrderForm(order)}
+ onRemoveClick={() => removeOrder(order)}
+ />
+ ))}
+ >
+ )}
+
+ {renewedOrderBasketItems.length > 0 && (
+ <>
+ {renewedOrderBasketItems.map((item, index) => (
+ openOrderForm(item)}
+ onRemoveClick={() => removeOrder(item)}
+ />
+ ))}
+ >
+ )}
+
+ {revisedOrderBasketItems.length > 0 && (
+ <>
+ {revisedOrderBasketItems.map((item, index) => (
+ openOrderForm(item)}
+ onRemoveClick={() => removeOrder(item)}
+ />
+ ))}
+ >
+ )}
+
+ {discontinuedOrderBasketItems.length > 0 && (
+ <>
+ {discontinuedOrderBasketItems.map((item, index) => (
+ openOrderForm(item)}
+ onRemoveClick={() => removeOrder(item)}
+ />
+ ))}
+ >
+ )}
+ >
+ )}
+
+ );
+};
+
+export default MedicalSupplyOrderPanel;
diff --git a/src/medical-orders/medical-supply-orderable-concept-search/medical-supply-orderable-concept-search.scss b/src/medical-orders/medical-supply-orderable-concept-search/medical-supply-orderable-concept-search.scss
new file mode 100644
index 0000000..78462cb
--- /dev/null
+++ b/src/medical-orders/medical-supply-orderable-concept-search/medical-supply-orderable-concept-search.scss
@@ -0,0 +1,27 @@
+@use '@carbon/layout';
+
+.backButton {
+ padding: layout.$spacing-03;
+
+ button {
+ display: flex;
+ margin-left: -(layout.$spacing-03);
+
+ svg {
+ order: 1;
+ margin-right: layout.$spacing-03;
+ margin-left: 0 !important;
+ fill: currentColor !important;
+ }
+
+ span {
+ order: 2;
+ }
+ }
+}
+
+.workspaceWrapper {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+}
diff --git a/src/medical-orders/medical-supply-orderable-concept-search/medical-supply-orderable-concept-search.workspace.tsx b/src/medical-orders/medical-supply-orderable-concept-search/medical-supply-orderable-concept-search.workspace.tsx
new file mode 100644
index 0000000..1e77a63
--- /dev/null
+++ b/src/medical-orders/medical-supply-orderable-concept-search/medical-supply-orderable-concept-search.workspace.tsx
@@ -0,0 +1,204 @@
+import {
+ ArrowLeftIcon,
+ getDefaultsFromConfigSchema,
+ ResponsiveWrapper,
+ useConfig,
+ useDebounce,
+ useLayoutType,
+ type DefaultWorkspaceProps,
+} from '@openmrs/esm-framework';
+import {
+ launchPatientWorkspace,
+ type OrderBasketItem,
+ useOrderBasket,
+ useOrderType,
+ usePatientChartStore,
+} from '@openmrs/esm-patient-common-lib';
+import React, { type ComponentProps, useCallback, useMemo, useRef, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import styles from './medical-supply-orderable-concept-search.scss';
+import { Button, Search } from '@carbon/react';
+import OrderableConceptSearchResults from './search-results.component';
+import { type ConfigObject } from '../../config-schema';
+import { OrderForm } from '../medical-supply-order-form/medical-supply-order-form.component';
+import { prepOrderPostData } from '../resources';
+import { type MedicalSupplyOrderBasketItem } from '../types';
+
+interface OrderableConceptSearchWorkspaceProps extends DefaultWorkspaceProps {
+ order: MedicalSupplyOrderBasketItem;
+ orderTypeUuid: string;
+ orderableConceptClasses: Array;
+ orderableConceptSets: Array;
+}
+
+export const careSettingUuid = '6f0c9a92-6f24-11e3-af88-005056821db0';
+
+type DrugsOrOrders = Pick;
+
+export function ordersEqual(order1: DrugsOrOrders, order2: DrugsOrOrders) {
+ return order1.action === order2.action;
+}
+
+const OrderableConceptSearchWorkspace: React.FC = ({
+ order: initialOrder,
+ orderTypeUuid,
+ closeWorkspace,
+ closeWorkspaceWithSavedChanges,
+ promptBeforeClosing,
+}) => {
+ const { t } = useTranslation();
+ const isTablet = useLayoutType() === 'tablet';
+ const { orders } = useOrderBasket(orderTypeUuid, prepOrderPostData);
+ const { patientUuid } = usePatientChartStore();
+ const { orderType } = useOrderType(orderTypeUuid);
+ const { orderTypes } = useConfig();
+
+ const [currentOrder, setCurrentOrder] = useState(initialOrder);
+
+ const { orderableConceptClasses, orderableConceptSets } = useMemo(
+ () => orderTypes.find((orderType) => orderType.orderTypeUuid === orderTypeUuid),
+
+ [orderTypeUuid, orderTypes],
+ );
+
+ const conceptClasses = useMemo(
+ () =>
+ orderableConceptClasses?.length
+ ? orderableConceptClasses
+ : (orderType?.conceptClasses.map(({ uuid }) => uuid) ?? []),
+ [orderType?.conceptClasses, orderableConceptClasses],
+ );
+
+ const cancelDrugOrder = useCallback(() => {
+ closeWorkspace({
+ onWorkspaceClose: () => launchPatientWorkspace('order-basket'),
+ });
+ }, [closeWorkspace]);
+
+ const openOrderForm = useCallback(
+ (order: MedicalSupplyOrderBasketItem) => {
+ const existingOrder = orders.find((prevOrder) => ordersEqual(prevOrder, order));
+ if (existingOrder) {
+ setCurrentOrder(existingOrder);
+ } else {
+ setCurrentOrder(order);
+ }
+ },
+ [orders],
+ );
+
+ return (
+
+ {!isTablet && (
+
+
) => }
+ size="sm"
+ >
+ {t('backToOrderBasket', 'Back to order basket')}
+
+
+ )}
+ {currentOrder ? (
+
{}}
+ />
+ ) : (
+
+ )}
+
+ );
+};
+
+interface ConceptSearchProps {
+ closeWorkspace: DefaultWorkspaceProps['closeWorkspace'];
+ openOrderForm: (search: OrderBasketItem) => void;
+ orderTypeUuid: string;
+ orderableConceptClasses: Array;
+ orderableConceptSets: Array;
+}
+
+function ConceptSearch({
+ closeWorkspace,
+ orderTypeUuid,
+ openOrderForm,
+ orderableConceptClasses,
+ orderableConceptSets,
+}: ConceptSearchProps) {
+ const { t } = useTranslation();
+ const { orderType } = useOrderType(orderTypeUuid);
+ const isTablet = useLayoutType() === 'tablet';
+ const [searchTerm, setSearchTerm] = useState('');
+ const debouncedSearchTerm = useDebounce(searchTerm, 300);
+ const searchInputRef = useRef(null);
+
+ const cancelDrugOrder = useCallback(() => {
+ closeWorkspace({
+ onWorkspaceClose: () => launchPatientWorkspace('order-basket'),
+ });
+ }, [closeWorkspace]);
+
+ const focusAndClearSearchInput = () => {
+ setSearchTerm('');
+ searchInputRef.current?.focus();
+ };
+
+ const handleSearchTermChange = (event: React.ChangeEvent) =>
+ setSearchTerm(event.target.value ?? '');
+
+ return (
+
+
+
+
+
{}}
+ orderableConceptClasses={orderableConceptClasses}
+ orderableConceptSets={orderableConceptSets}
+ />
+ {isTablet && (
+
+
{t('or', 'or')}
+
+ {t('returnToOrderBasket', 'Return to order basket')}
+
+
+ )}
+
+ );
+}
+
+export default OrderableConceptSearchWorkspace;
diff --git a/src/medical-orders/medical-supply-orderable-concept-search/search-results.component.tsx b/src/medical-orders/medical-supply-orderable-concept-search/search-results.component.tsx
new file mode 100644
index 0000000..7e1357d
--- /dev/null
+++ b/src/medical-orders/medical-supply-orderable-concept-search/search-results.component.tsx
@@ -0,0 +1,245 @@
+import React, { type ComponentProps, useCallback } from 'react';
+import {
+ launchPatientWorkspace,
+ useOrderBasket,
+ type OrderBasketItem,
+ useOrderableConceptSets,
+ type OrderableConcept,
+} from '@openmrs/esm-patient-common-lib';
+import { useTranslation } from 'react-i18next';
+import {
+ ArrowRightIcon,
+ type DefaultWorkspaceProps,
+ ShoppingCartArrowDownIcon,
+ useLayoutType,
+ useSession,
+} from '@openmrs/esm-framework';
+import { ShoppingCartArrowUp } from '@carbon/react/icons';
+import { useMemo } from 'react';
+import classNames from 'classnames';
+import { Tile, Button, SkeletonText, ButtonSkeleton } from '@carbon/react';
+import styles from './search-results.scss';
+import { createEmptyOrder, prepOrderPostData } from '../resources';
+
+interface OrderableConceptSearchResultsProps {
+ searchTerm: string;
+ openOrderForm: (order: OrderBasketItem) => void;
+ focusAndClearSearchInput: () => void;
+ cancelOrder: () => void;
+ orderableConceptClasses: Array;
+ orderableConceptSets: Array;
+ orderTypeUuid: string;
+ closeWorkspace: DefaultWorkspaceProps['closeWorkspace'];
+}
+
+const OrderableConceptSearchResults: React.FC = ({
+ searchTerm,
+ openOrderForm,
+ focusAndClearSearchInput,
+ cancelOrder,
+ orderableConceptClasses,
+ orderableConceptSets,
+ orderTypeUuid,
+ closeWorkspace,
+}) => {
+ const { t } = useTranslation();
+ const isTablet = useLayoutType() === 'tablet';
+ const { concepts, isLoading, error } = useOrderableConceptSets(
+ searchTerm,
+ orderableConceptClasses,
+ orderableConceptSets,
+ );
+
+ if (isLoading) {
+ return ;
+ }
+
+ if (error) {
+ return (
+
+
+
+ {t('errorFetchingTestTypes', 'Error fetching results for "{{searchTerm}}"', {
+ searchTerm,
+ })}
+
+
+ {t('trySearchingAgain', 'Please try searching again')}
+
+
+
+ );
+ }
+
+ if (concepts?.length) {
+ return (
+ <>
+
+ {searchTerm && (
+
+
+ {t('searchResultsMatchesForTerm', '{{count}} results for "{{searchTerm}}"', {
+ count: concepts?.length,
+ searchTerm,
+ })}
+
+
+ {t('clearSearchResults', 'Clear results')}
+
+
+ )}
+
+ {concepts.map((concept) => (
+
+ ))}
+
+
+ {isTablet && (
+
+
{t('or', 'or')}
+
+ {t('returnToOrderBasket', 'Return to order basket')}
+
+
+ )}
+ >
+ );
+ }
+
+ return (
+
+
+
+ {t('noResultsForTestTypeSearch', 'No results to display for "{{searchTerm}}"', {
+ searchTerm,
+ })}
+
+
+ {t('tryTo', 'Try to')} {' '}
+
+ {t('searchAgain', 'search again')}
+ {' '}
+ {t('usingADifferentTerm', 'using a different term')}
+
+
+
+ );
+};
+
+const TestTypeSearchSkeleton = () => {
+ const isTablet = useLayoutType() === 'tablet';
+ const tileClassName = classNames({
+ [styles.tabletSearchResultTile]: isTablet,
+ [styles.desktopSearchResultTile]: !isTablet,
+ [styles.skeletonTile]: true,
+ });
+ const buttonSize = isTablet ? 'md' : 'sm';
+
+ return (
+
+
+
+
+
+ {[...Array(4)].map((_, index) => (
+
+
+
+ ))}
+
+ );
+};
+
+interface TestTypeSearchResultItemProps {
+ concept: OrderableConcept;
+ openOrderForm: (searchResult: OrderBasketItem) => void;
+ orderTypeUuid: string;
+ closeWorkspace: DefaultWorkspaceProps['closeWorkspace'];
+}
+
+const TestTypeSearchResultItem: React.FC = ({
+ concept,
+ openOrderForm,
+ orderTypeUuid,
+ closeWorkspace,
+}) => {
+ const { t } = useTranslation();
+ const isTablet = useLayoutType() === 'tablet';
+ const session = useSession();
+ const { orders, setOrders } = useOrderBasket(orderTypeUuid, prepOrderPostData);
+
+ const orderAlreadyInBasket = useMemo(
+ () => orders?.some((order) => order.concept.uuid === concept.uuid),
+ [orders, concept],
+ );
+
+ const createOrderBasketItem = useCallback(
+ (testType: OrderableConcept) => {
+ return createEmptyOrder(testType, session.currentProvider?.uuid);
+ },
+ [session.currentProvider.uuid],
+ );
+
+ const addToBasket = useCallback(() => {
+ const orderBasketItem = createOrderBasketItem(concept);
+ orderBasketItem.isOrderIncomplete = true;
+ setOrders([...orders, orderBasketItem]);
+ closeWorkspace({
+ ignoreChanges: true,
+ onWorkspaceClose: () => launchPatientWorkspace('order-basket'),
+ });
+ }, [orders, setOrders, createOrderBasketItem, concept, closeWorkspace]);
+
+ const removeFromBasket = useCallback(() => {
+ setOrders(orders.filter((order) => order?.concept?.uuid !== concept?.uuid));
+ }, [setOrders, orders, concept?.uuid]);
+
+ return (
+
+
+
+ {concept.display} {' '}
+
+
+
+ {orderAlreadyInBasket ? (
+
}
+ onClick={removeFromBasket}
+ >
+ {t('removeFromBasket', 'Remove from basket')}
+
+ ) : (
+
) => (
+
+ )}
+ onClick={addToBasket}
+ >
+ {t('directlyAddToBasket', 'Add to basket')}
+
+ )}
+
) => }
+ onClick={() => openOrderForm(createOrderBasketItem(concept))}
+ >
+ {t('goToDrugOrderForm', 'Order form')}
+
+
+
+ );
+};
+
+export default OrderableConceptSearchResults;
diff --git a/src/medical-orders/medical-supply-orderable-concept-search/search-results.scss b/src/medical-orders/medical-supply-orderable-concept-search/search-results.scss
new file mode 100644
index 0000000..358d5c8
--- /dev/null
+++ b/src/medical-orders/medical-supply-orderable-concept-search/search-results.scss
@@ -0,0 +1,152 @@
+@use '@carbon/colors';
+@use '@carbon/layout';
+@use '@carbon/type';
+@use '@openmrs/esm-styleguide/src/vars' as *;
+
+/** For TestTypeSearchResults */
+.container {
+ margin: layout.$spacing-03 layout.$spacing-05 0;
+}
+
+.separator {
+ @include type.type-style('body-01');
+ color: colors.$gray-90;
+ width: 12rem;
+ margin: layout.$spacing-05 auto layout.$spacing-03;
+ overflow: hidden;
+ text-align: center;
+
+ &::before,
+ &::after {
+ background-color: colors.$gray-40;
+ content: '';
+ display: inline-block;
+ height: 1px;
+ position: relative;
+ vertical-align: middle;
+ width: 50%;
+ }
+
+ &::before {
+ right: layout.$spacing-05;
+ margin-left: -50%;
+ }
+
+ &::after {
+ left: layout.$spacing-05;
+ margin-right: -50%;
+ }
+}
+.separatorContainer {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ margin-top: layout.$spacing-05;
+}
+
+.searchResultsCount {
+ @include type.type-style('body-compact-01');
+ color: $text-02;
+}
+
+.orderBasketSearchResultsHeader {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: layout.$spacing-03;
+ align-items: center;
+}
+
+/** For TestTypeSearchResultItem */
+.searchResultTile {
+ padding: 0;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ border: 1px solid $grey-2;
+ &:not(:last-of-type) {
+ margin-bottom: layout.$spacing-03;
+ }
+}
+
+.tabletSearchResultTile {
+ &:not(:last-of-type) {
+ margin-bottom: layout.$spacing-04;
+ }
+}
+
+.searchResultTileContent {
+ padding: layout.$spacing-03 layout.$spacing-05;
+}
+
+.searchResultActions {
+ border-top: 1px solid $grey-2;
+ padding: 0 layout.$spacing-05;
+ display: flex;
+ justify-content: flex-end;
+
+ svg {
+ fill: currentColor !important;
+ }
+}
+
+.searchResultSkeletonWrapper {
+ margin: layout.$spacing-03 layout.$spacing-05 layout.$spacing-03;
+
+ :global(.cds--skeleton__text) {
+ margin: 0;
+ }
+
+ .searchResultCntSkeleton {
+ margin-right: layout.$spacing-07;
+ }
+
+ .skeletonTile {
+ display: flex;
+ align-items: center;
+ }
+}
+
+.emptyState {
+ margin: layout.$spacing-05;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: layout.$spacing-07;
+ border: 1px solid $ui-03;
+ text-align: center;
+ background-color: $ui-01;
+}
+
+:global(.omrs-breakpoint-lt-small-desktop) .emptyState {
+ background-color: $ui-02;
+}
+
+.link {
+ color: $interactive-01;
+ text-decoration: underline;
+ cursor: pointer;
+}
+
+.resultsContainer::-webkit-scrollbar {
+ width: layout.$spacing-03;
+}
+
+.resultsContainer::-webkit-scrollbar-thumb {
+ background: $ui-04;
+ border-radius: layout.$spacing-02;
+}
+
+.heading {
+ @include type.type-style('heading-01');
+ color: colors.$gray-70;
+ margin-bottom: layout.$spacing-03;
+}
+
+.bodyShort01 {
+ @include type.type-style('body-compact-01');
+}
+
+.text02 {
+ color: $text-02;
+}
diff --git a/src/medical-orders/order-basket-item-tile.component.tsx b/src/medical-orders/order-basket-item-tile.component.tsx
new file mode 100644
index 0000000..4d765d2
--- /dev/null
+++ b/src/medical-orders/order-basket-item-tile.component.tsx
@@ -0,0 +1,110 @@
+import { TrashCanIcon, useLayoutType, WarningIcon } from '@openmrs/esm-framework';
+import { type OrderBasketItem } from '@openmrs/esm-patient-common-lib';
+import React, { type ComponentProps, useRef } from 'react';
+import { useTranslation } from 'react-i18next';
+import styles from './order-basket-item-tile.scss';
+import { Button, Tile, ClickableTile } from '@carbon/react';
+import classNames from 'classnames';
+import { type MedicalSupplyOrderBasketItem } from './types';
+import { ordersEqual } from './resources';
+
+export interface OrderBasketItemTileProps {
+ orderBasketItem: MedicalSupplyOrderBasketItem;
+ onItemClick: () => void;
+ onRemoveClick: () => void;
+}
+
+const OrderBasketItemTile: React.FC = ({ orderBasketItem, onItemClick, onRemoveClick }) => {
+ const { t } = useTranslation();
+ const isTablet = useLayoutType() === 'tablet';
+
+ // This here is really dirty, but required.
+ // If the ref's value is false, we won't react to the ClickableTile's handleClick function.
+ // Why is this necessary?
+ // The "Remove" button is nested inside the ClickableTile. If the button's clicked, the tile also raises the
+ // handleClick event later. Not sure if this is a bug, but this shouldn't be possible in our flows.
+ // Hence, we manually prevent the handleClick callback from being invoked as soon as the button is pressed once.
+ const shouldOnClickBeCalled = useRef(true);
+
+ const labTile = (
+
+
+
+
+
{orderBasketItem.concept?.display}
+
+
+ {t('quantity', 'Quantity').toLocaleUpperCase()} {' '}
+ {t('medicalSupplyOrderDescription', '{{quantity}} {{quantityUnits}}', {
+ quantity: orderBasketItem.quantity,
+ quantityUnits: orderBasketItem.quantityUnits?.display,
+ })}
+
+
+
+
+ {!!orderBasketItem.orderError && (
+ <>
+
+
+
+
+ {t('error', 'Error').toUpperCase()}
+ {orderBasketItem.orderError.responseBody?.error?.message ?? orderBasketItem.orderError.message}
+
+ >
+ )}
+
+
+
) => }
+ iconDescription={t('removeFromBasket', 'Remove from basket')}
+ onClick={() => {
+ shouldOnClickBeCalled.current = false;
+ onRemoveClick();
+ }}
+ tooltipPosition="left"
+ />
+
+ );
+ return orderBasketItem.action === 'DISCONTINUE' ? (
+ {labTile}
+ ) : (
+ shouldOnClickBeCalled.current && onItemClick()}
+ >
+ {labTile}
+
+ );
+};
+
+function OrderActionLabel({ orderBasketItem }: { orderBasketItem: OrderBasketItem }) {
+ const { t } = useTranslation();
+
+ if (orderBasketItem.isOrderIncomplete) {
+ return {t('orderActionIncomplete', 'Incomplete')} ;
+ }
+
+ switch (orderBasketItem.action) {
+ case 'NEW':
+ return {t('orderActionNew', 'New')} ;
+ case 'RENEW':
+ return {t('orderActionRenew', 'Renew')} ;
+ case 'REVISE':
+ return {t('orderActionRevise', 'Modify')} ;
+ case 'DISCONTINUE':
+ return {t('orderActionDiscontinue', 'Discontinue')} ;
+ default:
+ return <>>;
+ }
+}
+
+export default OrderBasketItemTile;
diff --git a/src/medical-orders/order-basket-item-tile.scss b/src/medical-orders/order-basket-item-tile.scss
new file mode 100644
index 0000000..2768bbc
--- /dev/null
+++ b/src/medical-orders/order-basket-item-tile.scss
@@ -0,0 +1,89 @@
+@use '@carbon/layout';
+@use '@carbon/type';
+@use '@openmrs/esm-styleguide/src/vars' as *;
+
+.clickableTileDesktop {
+ padding: layout.$spacing-03 layout.$spacing-04;
+ border-bottom: 1px solid $ui-03;
+}
+
+.clickableTileTablet {
+ padding: layout.$spacing-04 layout.$spacing-05;
+ background-color: $ui-02;
+ border-bottom: 1px solid $ui-03;
+}
+
+.orderBasketItemTile {
+ display: flex;
+ justify-content: space-between;
+ align-items: baseline;
+}
+
+.label {
+ display: inline-block;
+ @include type.type-style('label-01');
+ margin-bottom: layout.$spacing-03;
+}
+
+.orderActionNewLabel {
+ @extend .label;
+ color: black;
+ background-color: #c4f4cc;
+ padding: 0 layout.$spacing-02;
+}
+
+.orderActionIncompleteLabel {
+ @extend .label;
+ color: white;
+ background-color: $danger;
+ padding: 0 layout.$spacing-02;
+}
+
+.orderActionRenewLabel {
+ @extend .label;
+ color: $support-02;
+}
+
+.orderActionRevisedLabel {
+ @extend .label;
+ color: #943d00;
+}
+
+.orderActionDiscontinueLabel {
+ @extend .label;
+ color: $danger;
+}
+
+.orderErrorText {
+ color: $danger;
+ display: flex;
+ align-items: center;
+}
+
+.name {
+ @include type.type-style('heading-compact-01');
+ color: black;
+}
+
+.description {
+ margin-top: layout.$spacing-03;
+ @include type.type-style('body-short-02');
+}
+
+.removeButton {
+ svg {
+ fill: $danger !important;
+ }
+}
+
+.label01 {
+ @include type.type-style('label-01');
+}
+
+.clipTextWithEllipsis {
+ font-size: layout.$spacing-05;
+ line-height: 1.5;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+}
diff --git a/src/medical-orders/resources.ts b/src/medical-orders/resources.ts
new file mode 100644
index 0000000..c927bd3
--- /dev/null
+++ b/src/medical-orders/resources.ts
@@ -0,0 +1,137 @@
+import {
+ type OrderBasketItem,
+ priorityOptions,
+ type OrderUrgency,
+ type OrderPost,
+ type OrderableConcept,
+ type Order,
+ type OrderAction,
+} from '@openmrs/esm-patient-common-lib';
+import { type MedicalSupplyOrderBasketItem } from './types';
+import { openmrsFetch, type OpenmrsResource, restBaseUrl, type FetchResponse, useConfig } from '@openmrs/esm-framework';
+import useSWRImmutable from 'swr/immutable';
+import { useEffect, useMemo } from 'react';
+import { type ConfigObject } from '../config-schema';
+
+export function createEmptyOrder(concept: OrderableConcept, orderer: string): MedicalSupplyOrderBasketItem {
+ return {
+ action: 'NEW',
+ urgency: priorityOptions[0].value as OrderUrgency,
+ display: concept.label,
+ concept,
+ orderer,
+ };
+}
+
+export function ordersEqual(order1: OrderBasketItem, order2: OrderBasketItem) {
+ return order1.action === order2.action && order1.concept.uuid === order2.concept.uuid;
+}
+
+const careSettingUuid = '6f0c9a92-6f24-11e3-af88-005056821db0';
+
+export function prepOrderPostData(
+ order: MedicalSupplyOrderBasketItem,
+ patientUuid: string,
+ encounterUuid: string | null,
+): OrderPost {
+ if (order.action === 'NEW' || order.action === 'RENEW') {
+ return {
+ action: 'NEW',
+ type: 'medicalsupplyorder',
+ patient: patientUuid,
+ careSetting: careSettingUuid,
+ orderer: order.orderer,
+ encounter: encounterUuid,
+ concept: order.concept.uuid,
+ instructions: order.instructions,
+ // orderReason: order.orderReason,
+ accessionNumber: order.accessionNumber,
+ urgency: order.urgency,
+ orderType: order.orderType,
+ quantity: order.quantity,
+ quantityUnits: order.quantityUnits.uuid,
+ };
+ } else if (order.action === 'REVISE') {
+ return {
+ action: 'REVISE',
+ type: 'medicalsupplyorder',
+ patient: patientUuid,
+ careSetting: order.careSetting,
+ orderer: order.orderer,
+ encounter: encounterUuid,
+ concept: order?.concept?.uuid,
+ instructions: order.instructions,
+ previousOrder: order.previousOrder,
+ accessionNumber: order.accessionNumber,
+ urgency: order.urgency,
+ orderType: order.orderType,
+ quantity: order.quantity,
+ quantityUnits: order.quantityUnits.uuid,
+ };
+ } else if (order.action === 'DISCONTINUE') {
+ return {
+ action: 'DISCONTINUE',
+ type: 'medicalsupplyorder',
+ patient: patientUuid,
+ careSetting: order.careSetting,
+ orderer: order.orderer,
+ encounter: encounterUuid,
+ concept: order?.concept?.uuid,
+ previousOrder: order.previousOrder,
+ accessionNumber: order.accessionNumber,
+ urgency: order.urgency,
+ orderType: order.orderType,
+ };
+ } else {
+ throw new Error(`Unknown order action: ${order.action}.`);
+ }
+}
+
+export interface Concept extends OpenmrsResource {
+ setMembers: Array;
+ answers: Array;
+}
+
+export function useQuantityUnits() {
+ const config = useConfig();
+ const { quantityUnits } = config;
+ const { data, isLoading, isValidating, error } = useSWRImmutable>(
+ quantityUnits.conceptUuid ? `${restBaseUrl}/concept/${quantityUnits.conceptUuid}` : null,
+ openmrsFetch,
+ );
+
+ useEffect(() => {
+ if (error) {
+ reportError(error);
+ }
+ }, [error]);
+
+ const results = useMemo(
+ () => ({
+ concepts: quantityUnits.map == 'setMembers' ? data?.data.setMembers : data?.data.answers,
+ isLoadingQuantityUnits: isLoading,
+ errorFetchingQuantityUnits: error,
+ }),
+ [data, isLoading, error, quantityUnits],
+ );
+ return results;
+}
+
+export function buildMedicalSupplyOrderItem(order: Order, action: OrderAction): MedicalSupplyOrderBasketItem {
+ return {
+ action: action,
+ uuid: order.uuid,
+ display: order.display,
+ previousOrder: action !== 'NEW' ? order.uuid : null,
+ orderer: order.orderer.uuid,
+ careSetting: order.careSetting.uuid,
+ instructions: order.instructions || '',
+ urgency: order.urgency,
+ accessionNumber: order.accessionNumber || '',
+ concept: order.concept,
+ orderNumber: order.orderNumber,
+ orderType: order.orderType.uuid,
+ quantity: order.quantity,
+ quantityUnits: order.quantityUnits,
+ };
+}
diff --git a/src/medical-orders/types.ts b/src/medical-orders/types.ts
new file mode 100644
index 0000000..5eaac18
--- /dev/null
+++ b/src/medical-orders/types.ts
@@ -0,0 +1,7 @@
+import type { OpenmrsResource } from '@openmrs/esm-framework';
+import type { OrderBasketItem } from '@openmrs/esm-patient-common-lib';
+
+export interface MedicalSupplyOrderBasketItem extends OrderBasketItem {
+ quantity?: number;
+ quantityUnits?: OpenmrsResource;
+}
diff --git a/src/patient-getter/patient-getter.component.tsx b/src/patient-getter/patient-getter.component.tsx
deleted file mode 100644
index 9647a74..0000000
--- a/src/patient-getter/patient-getter.component.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * Components that make queries delegate the query-making logic to a
- * `.resource.ts` function. This component leverages the`usePatient`
- * hook to fetch a patient from the backend. When the button is clicked,
- * the `patientName` variable is set to "test", which triggers the hook
- * to make a request to the backend. The hook returns patient data from the
- * request, which is then rendered in the UI. The hook also returns a boolean
- * property called `isLoading` that is set to true while the request is being
- * made. This component renders a loading indicator while `isLoading` is true.
- */
-
-import React, { useState } from 'react';
-import { Button, InlineLoading, Tile } from '@carbon/react';
-import { useTranslation } from 'react-i18next';
-import { usePatient } from './patient-getter.resource';
-import styles from './patient-getter.scss';
-
-function PatientGetter() {
- const { t } = useTranslation();
- const [patientName, setPatientName] = useState(null);
- const { patient, isLoading } = usePatient(patientName);
-
- return (
-
-
{t('dataFetching', 'Data fetching')}
-
{t('patientGetterExplainer', 'Try clicking the button below to fetch a patient from the backend')}:
-
setPatientName('test')}>{t('getPatient', 'Get a patient named')} 'test'
- {isLoading ?
: null}
- {patient ? (
-
- {patient
- ? `${patient.name[0].given} ${patient.name[0].family} / ${patient.gender} / ${patient.birthDate}`
- : null}
-
- ) : null}
-
- );
-}
-
-export default PatientGetter;
diff --git a/src/patient-getter/patient-getter.resource.ts b/src/patient-getter/patient-getter.resource.ts
deleted file mode 100644
index 6ffda42..0000000
--- a/src/patient-getter/patient-getter.resource.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import useSWR from 'swr';
-import { fhirBaseUrl, openmrsFetch } from '@openmrs/esm-framework';
-
-/**
- * This hook searches for a patient using the provided search term from the
- * OpenMRS FHIR API.It leverages the useSWR hook from the SWR library
- * https://swr.vercel.app/docs/data-fetching to fetch data. SWR provides a
- * number of benefits over the standard React useEffect hook, including:
- *
- * - Fast, lightweight and reusable data fetching
- * - Built-in cache and request deduplication
- * - Real-time updates
- * - Simplified error and loading state handling, and more.
- *
- * We recommend using SWR for data fetching in your OpenMRS frontend modules.
- *
- * See the docs for the underlying fhir.js Client object: https://github.com/FHIR/fhir.js#api
- * See the OpenMRS FHIR Module docs: https://wiki.openmrs.org/display/projects/OpenMRS+FHIR+Module
- * See the OpenMRS REST API docs: https://rest.openmrs.org/#openmrs-rest-api
- *
- * @param query A patient name or ID
- * @returns The first matching patient
- */
-
-export function usePatient(query: string) {
- const url = `${fhirBaseUrl}/Patient?name=${query}&_summary=data`;
- const { data, error, isLoading } = useSWR<
- {
- data: { entry: Array<{ resource: fhir.Patient }> };
- },
- Error
- >(query ? url : null, openmrsFetch);
-
- return {
- patient: data ? data?.data?.entry[0].resource : null,
- error: error,
- isLoading,
- };
-}
diff --git a/src/patient-getter/patient-getter.scss b/src/patient-getter/patient-getter.scss
deleted file mode 100644
index 61e6021..0000000
--- a/src/patient-getter/patient-getter.scss
+++ /dev/null
@@ -1,16 +0,0 @@
-@use '@carbon/layout';
-@use '@carbon/type';
-
-.container {
- margin-top: layout.$spacing-09;
-
- > * + * {
- margin-top: layout.$spacing-06;
- }
-}
-
-.tile {
- border: 1px solid lightgray;
- max-width: 20rem;
- @include type.type-style('heading-compact-01');
-}
diff --git a/src/patient-getter/patient-getter.test.tsx b/src/patient-getter/patient-getter.test.tsx
deleted file mode 100644
index d7eb1c8..0000000
--- a/src/patient-getter/patient-getter.test.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import React from 'react';
-import { render, screen, waitFor } from '@testing-library/react';
-import userEvent from '@testing-library/user-event';
-import PatientGetter from './patient-getter.component';
-
-/**
- * This is an idiomatic mock of a backend resource. We generally mock resource fetching functions like `usePatient`, rather than mocking `fetch` or anything lower-level.
- */
-jest.mock('./patient-getter.resource.ts', () => ({
- usePatient: jest.fn(() => ({
- patient: {
- birthDate: '1997-05-21',
- gender: 'male',
- name: [
- {
- family: 'Testguy',
- given: 'Joeboy',
- id: 'abc123',
- },
- ],
- },
- })),
-}));
-
-it('gets a patient when the button is clicked', async () => {
- render( );
-
- const user = userEvent.setup();
- const heading = screen.getByRole('heading', { name: /data fetching/i });
- const button = screen.getByRole('button', {
- name: /get a patient named 'test'/i,
- });
-
- expect(heading).toBeInTheDocument();
- expect(button).toBeInTheDocument();
-
- await waitFor(() => user.click(button));
-
- expect(screen.getByText(/Joeboy Testguy \/ male \/ 1997-05-21/)).toBeInTheDocument();
-});
diff --git a/src/resources/resources.component.tsx b/src/resources/resources.component.tsx
deleted file mode 100644
index f46eeb1..0000000
--- a/src/resources/resources.component.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import React from 'react';
-import { ClickableTile } from '@carbon/react';
-import { ChevronRight } from '@carbon/react/icons';
-import { useTranslation } from 'react-i18next';
-import styles from './resources.scss';
-
-function Resources() {
- const { t } = useTranslation();
-
- return (
-
-
{t('resources', 'Resources')}
-
{t('usefulLinks', 'Below are some links to useful resources')}:
-
-
-
-
-
-
-
- );
-}
-
-function Card({ title, subtitle, link }: { title: string; subtitle: string; link: string }) {
- return (
-
-
-
-
{title}
-
-
-
{subtitle}
-
-
- );
-}
-
-export default Resources;
diff --git a/src/resources/resources.scss b/src/resources/resources.scss
deleted file mode 100644
index f258158..0000000
--- a/src/resources/resources.scss
+++ /dev/null
@@ -1,62 +0,0 @@
-@use '@carbon/layout';
-@use '@carbon/type';
-
-.container {
- padding: layout.$spacing-07;
-}
-
-.heading {
- @include type.type-style('heading-04');
- margin: layout.$spacing-05 0;
-}
-
-.explainer {
- display: block;
-}
-
-.resources {
- margin-top: layout.$spacing-10;
- margin-bottom: layout.$spacing-09;
-
- > * + * {
- margin-right: layout.$spacing-05;
- }
-}
-
-.cardsContainer {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
- column-gap: layout.$spacing-06;
- margin-top: layout.$spacing-07;
- max-width: 75%;
-}
-
-.card {
- margin: layout.$spacing-03 0;
- display: flex;
- align-items: center;
-
- &:hover {
- border: 1px solid lightgray;
- }
-
- svg {
- margin-left: layout.$spacing-02;
- }
-}
-
-.cardContent {
- display: flex;
- flex-direction: column;
-}
-
-.title {
- display: flex;
- align-items: center;
- margin-bottom: layout.$spacing-05;
-
- h4 {
- @include type.type-style('heading-02');
- }
-}
-
diff --git a/src/root.component.tsx b/src/root.component.tsx
deleted file mode 100644
index 7d05489..0000000
--- a/src/root.component.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * From here, the application is pretty typical React, but with lots of
- * support from `@openmrs/esm-framework`. Check out `Greeter` to see
- * usage of the configuration system, and check out `PatientGetter` to
- * see data fetching using the OpenMRS FHIR API.
- *
- * Check out the Config docs:
- * https://openmrs.github.io/openmrs-esm-core/#/main/config
- */
-
-import React from 'react';
-import { useTranslation } from 'react-i18next';
-import { Boxes } from './boxes/slot/boxes.component';
-import Greeter from './greeter/greeter.component';
-import PatientGetter from './patient-getter/patient-getter.component';
-import Resources from './resources/resources.component';
-import styles from './root.scss';
-
-const Root: React.FC = () => {
- const { t } = useTranslation();
-
- return (
-
-
{t('welcomeText', 'Welcome to the O3 Template app')}
-
- {t('explainer', 'The following examples demonstrate some key features of the O3 framework')}.
-
- {/* Greeter: demonstrates the configuration system */}
-
- {/* Boxes: demonstrates the extension system */}
-
- {/* PatientGetter: demonstrates data fetching */}
-
-
-
- );
-};
-
-export default Root;
diff --git a/src/root.scss b/src/root.scss
deleted file mode 100644
index 44d82fe..0000000
--- a/src/root.scss
+++ /dev/null
@@ -1,15 +0,0 @@
-@use '@carbon/layout';
-@use '@carbon/type';
-
-.container {
- padding: layout.$spacing-07;
-}
-
-.welcome {
- @include type.type-style('heading-04');
- margin: layout.$spacing-05 0;
-}
-
-.explainer {
- margin-bottom: layout.$spacing-07;
-}
diff --git a/src/root.test.tsx b/src/root.test.tsx
deleted file mode 100644
index 4ee3c8e..0000000
--- a/src/root.test.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- * This is the root test for this page. It simply checks that the page
- * renders. If the components of your page are highly interdependent,
- * (e.g., if the `Root` component had state that communicated
- * information between `Greeter` and `PatientGetter`) then you might
- * want to do most of your testing here. If those components are
- * instead quite independent (as is the case in this example), then
- * it would make more sense to test those components independently.
- *
- * The key thing to remember, always, is: write tests that behave like
- * users. They should *look* for elements by their visual
- * characteristics, *interact* with them, and (mostly) *assert* based
- * on things that would be visually apparent to a user.
- *
- * To learn more about how we do testing, see the following resources:
- * https://o3-docs.vercel.app/docs/frontend-modules/testing
- * https://kentcdodds.com/blog/how-to-know-what-to-test
- * https://kentcdodds.com/blog/testing-implementation-details
- * https://kentcdodds.com/blog/common-mistakes-with-react-testing-library
- *
- * Kent C. Dodds is the inventor of `@testing-library`:
- * https://testing-library.com/docs/guiding-principles
- */
-import React from 'react';
-import { render, screen } from '@testing-library/react';
-import { useConfig } from '@openmrs/esm-framework';
-import { Config } from './config-schema';
-import Root from './root.component';
-
-/**
- * This is an idiomatic way of dealing with mocked files. Note that
- * `useConfig` is already mocked; the Jest moduleNameMapper (see the
- * Jest config) has mapped the `@openmrs/esm-framework` import to a
- * mock file. This line just tells TypeScript that the object is, in
- * fact, a mock, and so will have methods like `mockReturnValue`.
- */
-const mockUseConfig = jest.mocked(useConfig);
-
-it('renders a landing page for the Template app', () => {
- const config: Config = { casualGreeting: false, whoToGreet: ['World'] };
- mockUseConfig.mockReturnValue(config);
-
- render( );
-
- expect(screen.getByRole('heading', { name: /welcome to the o3 template app/i })).toBeInTheDocument();
- expect(screen.getByRole('heading', { name: /configuration system/i })).toBeInTheDocument();
- expect(screen.getByRole('heading', { name: /extension system/i })).toBeInTheDocument();
- expect(screen.getByRole('heading', { name: /data fetching/i })).toBeInTheDocument();
- expect(screen.getByRole('heading', { name: /resources/i })).toBeInTheDocument();
- expect(screen.getByRole('button', { name: /get a patient named 'test'/i })).toBeInTheDocument();
-});
diff --git a/src/routes.json b/src/routes.json
index e90cc22..2c6e103 100644
--- a/src/routes.json
+++ b/src/routes.json
@@ -6,25 +6,31 @@
},
"extensions": [
{
- "name": "Red box",
- "component": "redBox",
- "slot": "Boxes"
+ "name": "medical-supply-order-panel",
+ "component": "medicalSupplyOrderPanel",
+ "slot": "order-basket-slot",
+ "order": 3
},
{
- "name": "Blue box",
- "component": "blueBox",
- "slot": "Boxes"
+ "name": "medicalsupplyorder-action-menu-item",
+ "component": "modifyMedicalSupplyOrderMenuItem",
+ "slot": "medicalsupplyorder-action-menu-items-slot",
+ "order": 0
},
- {
- "name": "Brand box",
- "component": "blueBox",
- "slot": "Boxes"
+ {
+ "name": "medicalsupplyorder-detail-table",
+ "component": "medicalSupplyOrderDetailTable",
+ "slot": "medicalsupplyorder-detail-slot"
}
],
"pages": [
- {
- "component": "root",
- "route": "root"
+ ],
+ "workspaces": [
+ {
+ "name": "medical-supply-orderable-concept-workspace",
+ "title": "searchMedicalSupplyOrderables",
+ "component": "searchMedicalSupplyOrderables",
+ "type": "order"
}
]
}
diff --git a/yarn.lock b/yarn.lock
index 2ddde0e..2f505b1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1320,7 +1320,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.5, @babel/runtime@npm:^7.14.8, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.17.2, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.19.0, @babel/runtime@npm:^7.22.15, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.24.7, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.4":
+"@babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.5, @babel/runtime@npm:^7.14.8, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.17.2, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.19.0, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.24.7, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.4":
version: 7.25.7
resolution: "@babel/runtime@npm:7.25.7"
dependencies:
@@ -1329,6 +1329,15 @@ __metadata:
languageName: node
linkType: hard
+"@babel/runtime@npm:^7.24.5":
+ version: 7.26.0
+ resolution: "@babel/runtime@npm:7.26.0"
+ dependencies:
+ regenerator-runtime: "npm:^0.14.0"
+ checksum: 10/9f4ea1c1d566c497c052d505587554e782e021e6ccd302c2ad7ae8291c8e16e3f19d4a7726fb64469e057779ea2081c28b7dbefec6d813a22f08a35712c0f699
+ languageName: node
+ linkType: hard
+
"@babel/template@npm:^7.25.7, @babel/template@npm:^7.3.3":
version: 7.25.7
resolution: "@babel/template@npm:7.25.7"
@@ -1405,6 +1414,15 @@ __metadata:
languageName: node
linkType: hard
+"@carbon/colors@npm:^11.28.0":
+ version: 11.28.0
+ resolution: "@carbon/colors@npm:11.28.0"
+ dependencies:
+ "@ibm/telemetry-js": "npm:^1.5.0"
+ checksum: 10/557419fe3a6bda7ba8e8936662ffd7599003dc05d849811ffd19fd0d52762f9f3100e5ae2193486b5bcaed46e1cd65c6683c4bee48a1a9bc80c330c7c6d691ca
+ languageName: node
+ linkType: hard
+
"@carbon/feature-flags@npm:^0.16.0":
version: 0.16.0
resolution: "@carbon/feature-flags@npm:0.16.0"
@@ -1421,6 +1439,15 @@ __metadata:
languageName: node
linkType: hard
+"@carbon/feature-flags@npm:^0.24.0":
+ version: 0.24.0
+ resolution: "@carbon/feature-flags@npm:0.24.0"
+ dependencies:
+ "@ibm/telemetry-js": "npm:^1.5.0"
+ checksum: 10/abfe35e02d45208e64ead6e22c76c1f97c3099013bb0f0bbca46893c33f4571c9c6f0a2cacc6e8b5cd78aa90696beef89145b7bfc06fcab06c8ca3ff7cd11e02
+ languageName: node
+ linkType: hard
+
"@carbon/grid@npm:^11.28.0, @carbon/grid@npm:^11.28.1":
version: 11.28.1
resolution: "@carbon/grid@npm:11.28.1"
@@ -1431,6 +1458,16 @@ __metadata:
languageName: node
linkType: hard
+"@carbon/grid@npm:^11.29.0":
+ version: 11.29.0
+ resolution: "@carbon/grid@npm:11.29.0"
+ dependencies:
+ "@carbon/layout": "npm:^11.28.0"
+ "@ibm/telemetry-js": "npm:^1.5.0"
+ checksum: 10/27dfde60051e5db01d7f0c7f76adfa7f9c4df3e9fdff9bf3ed3e999221b9110959db162aaa06ac054474af79c6c8a47dcfd0e1112f5171342938fb9c4c02657d
+ languageName: node
+ linkType: hard
+
"@carbon/icon-helpers@npm:^10.53.0":
version: 10.53.1
resolution: "@carbon/icon-helpers@npm:10.53.1"
@@ -1440,7 +1477,16 @@ __metadata:
languageName: node
linkType: hard
-"@carbon/icons-react@npm:^11.26.0, @carbon/icons-react@npm:^11.51.0":
+"@carbon/icon-helpers@npm:^10.54.0":
+ version: 10.54.0
+ resolution: "@carbon/icon-helpers@npm:10.54.0"
+ dependencies:
+ "@ibm/telemetry-js": "npm:^1.5.0"
+ checksum: 10/558988f429fd42e2a03fcf5fbf97cb3fee3a75a41cce9d93a1eefa1978498bd4445b8de622ede66e60e48dc46459f1af83dfe767ba5bd8eb6f436e8307618f27
+ languageName: node
+ linkType: hard
+
+"@carbon/icons-react@npm:^11.26.0":
version: 11.51.0
resolution: "@carbon/icons-react@npm:11.51.0"
dependencies:
@@ -1453,6 +1499,19 @@ __metadata:
languageName: node
linkType: hard
+"@carbon/icons-react@npm:^11.53.0":
+ version: 11.53.0
+ resolution: "@carbon/icons-react@npm:11.53.0"
+ dependencies:
+ "@carbon/icon-helpers": "npm:^10.54.0"
+ "@ibm/telemetry-js": "npm:^1.5.0"
+ prop-types: "npm:^15.7.2"
+ peerDependencies:
+ react: ">=16"
+ checksum: 10/32eafc698024b3228e8d42c151f22384fdd82206e7259023a92994b19d268c171a0ed8ff85a00718d2ef7bc1fbaccce584e3ea4c7a766ef1b464eccd8fb4537b
+ languageName: node
+ linkType: hard
+
"@carbon/layout@npm:^11.19.0, @carbon/layout@npm:^11.27.0, @carbon/layout@npm:^11.27.1":
version: 11.27.1
resolution: "@carbon/layout@npm:11.27.1"
@@ -1462,6 +1521,15 @@ __metadata:
languageName: node
linkType: hard
+"@carbon/layout@npm:^11.28.0":
+ version: 11.28.0
+ resolution: "@carbon/layout@npm:11.28.0"
+ dependencies:
+ "@ibm/telemetry-js": "npm:^1.5.0"
+ checksum: 10/4ce8fbe84aa869121e0c83ae81c6beeeee87162165fd452fc51012715a3bf3ebb4e096dd4fb9a6f64ef45855d680e0953f13a79978cad855c0c64450a884e554
+ languageName: node
+ linkType: hard
+
"@carbon/motion@npm:^11.23.0":
version: 11.23.1
resolution: "@carbon/motion@npm:11.23.1"
@@ -1471,24 +1539,32 @@ __metadata:
languageName: node
linkType: hard
-"@carbon/react@npm:^1.68.0":
- version: 1.68.0
- resolution: "@carbon/react@npm:1.68.0"
+"@carbon/motion@npm:^11.24.0":
+ version: 11.24.0
+ resolution: "@carbon/motion@npm:11.24.0"
+ dependencies:
+ "@ibm/telemetry-js": "npm:^1.5.0"
+ checksum: 10/ac358bf2a09ea7d77aae5dbe92abafaef817012abd0bb25a5ef33a77e7d88d32c33d796511f7e36a443ce32b7441eacea40869e2552042143fe3262cb8c4ea3d
+ languageName: node
+ linkType: hard
+
+"@carbon/react@npm:^1.12.0, @carbon/react@npm:^1.71.0":
+ version: 1.71.1
+ resolution: "@carbon/react@npm:1.71.1"
dependencies:
"@babel/runtime": "npm:^7.24.7"
- "@carbon/feature-flags": "npm:^0.23.0"
- "@carbon/icons-react": "npm:^11.51.0"
- "@carbon/layout": "npm:^11.27.0"
- "@carbon/styles": "npm:^1.67.0"
+ "@carbon/feature-flags": "npm:^0.24.0"
+ "@carbon/icons-react": "npm:^11.53.0"
+ "@carbon/layout": "npm:^11.28.0"
+ "@carbon/styles": "npm:^1.70.0"
"@floating-ui/react": "npm:^0.26.0"
"@ibm/telemetry-js": "npm:^1.5.0"
classnames: "npm:2.5.1"
copy-to-clipboard: "npm:^3.3.1"
- downshift: "npm:8.5.0"
+ downshift: "npm:9.0.8"
flatpickr: "npm:4.6.13"
invariant: "npm:^2.2.3"
lodash.debounce: "npm:^4.0.8"
- lodash.findlast: "npm:^4.5.0"
lodash.omit: "npm:^4.5.0"
lodash.throttle: "npm:^4.1.1"
prop-types: "npm:^15.7.2"
@@ -1501,7 +1577,7 @@ __metadata:
react: ^16.8.6 || ^17.0.1 || ^18.2.0
react-dom: ^16.8.6 || ^17.0.1 || ^18.2.0
sass: ^1.33.0
- checksum: 10/da7fa06865f2c83c0f41c756b9bda66a31bf533202bf7cb19ca3721570c84272bb0dea06a37f7f77fc2963f340a11a6fcd15161e1b8217018ab30fd2d60a8034
+ checksum: 10/51fde69cfc9962d4f3671dcb93c8f8da02cf3715f5a8e34a4724731e3dc519860bb2edffd8996134f41ca667051e18b56d0cd98ee7b1d57c76ef4805ae720b83
languageName: node
linkType: hard
@@ -1538,7 +1614,7 @@ __metadata:
languageName: node
linkType: hard
-"@carbon/styles@npm:^1.37.0, @carbon/styles@npm:^1.67.0":
+"@carbon/styles@npm:^1.37.0":
version: 1.67.0
resolution: "@carbon/styles@npm:1.67.0"
dependencies:
@@ -1560,6 +1636,36 @@ __metadata:
languageName: node
linkType: hard
+"@carbon/styles@npm:^1.70.0":
+ version: 1.70.0
+ resolution: "@carbon/styles@npm:1.70.0"
+ dependencies:
+ "@carbon/colors": "npm:^11.28.0"
+ "@carbon/feature-flags": "npm:^0.24.0"
+ "@carbon/grid": "npm:^11.29.0"
+ "@carbon/layout": "npm:^11.28.0"
+ "@carbon/motion": "npm:^11.24.0"
+ "@carbon/themes": "npm:^11.43.0"
+ "@carbon/type": "npm:^11.33.0"
+ "@ibm/plex": "npm:6.0.0-next.6"
+ "@ibm/plex-mono": "npm:0.0.3-alpha.0"
+ "@ibm/plex-sans": "npm:0.0.3-alpha.0"
+ "@ibm/plex-sans-arabic": "npm:0.0.3-alpha.0"
+ "@ibm/plex-sans-devanagari": "npm:0.0.3-alpha.0"
+ "@ibm/plex-sans-hebrew": "npm:0.0.3-alpha.0"
+ "@ibm/plex-sans-thai": "npm:0.0.3-alpha.0"
+ "@ibm/plex-sans-thai-looped": "npm:0.0.3-alpha.0"
+ "@ibm/plex-serif": "npm:0.0.3-alpha.0"
+ "@ibm/telemetry-js": "npm:^1.5.0"
+ peerDependencies:
+ sass: ^1.33.0
+ peerDependenciesMeta:
+ sass:
+ optional: true
+ checksum: 10/6a6c7c6c8c226b95bcac86633db516bb7099a4bb203bf68cced7f259ceaf0e0ff0c205e620be02abf2424e380c752ff57ebd9179ac2d242ccc252a36ad2b1532
+ languageName: node
+ linkType: hard
+
"@carbon/telemetry@npm:0.1.0":
version: 0.1.0
resolution: "@carbon/telemetry@npm:0.1.0"
@@ -1582,6 +1688,19 @@ __metadata:
languageName: node
linkType: hard
+"@carbon/themes@npm:^11.43.0":
+ version: 11.43.0
+ resolution: "@carbon/themes@npm:11.43.0"
+ dependencies:
+ "@carbon/colors": "npm:^11.28.0"
+ "@carbon/layout": "npm:^11.28.0"
+ "@carbon/type": "npm:^11.33.0"
+ "@ibm/telemetry-js": "npm:^1.5.0"
+ color: "npm:^4.0.0"
+ checksum: 10/ffbe3316dcdd3920a2e5139e550a1195734f591b3de1d79b41f8ca606c85ae0365660d9cc79b1562e82730c860fb7b001da1e3101ca49ca9e99867bfa8d7e50f
+ languageName: node
+ linkType: hard
+
"@carbon/type@npm:^11.32.0":
version: 11.32.1
resolution: "@carbon/type@npm:11.32.1"
@@ -1593,6 +1712,17 @@ __metadata:
languageName: node
linkType: hard
+"@carbon/type@npm:^11.33.0":
+ version: 11.33.0
+ resolution: "@carbon/type@npm:11.33.0"
+ dependencies:
+ "@carbon/grid": "npm:^11.29.0"
+ "@carbon/layout": "npm:^11.28.0"
+ "@ibm/telemetry-js": "npm:^1.5.0"
+ checksum: 10/bbaa5f9bafd6b47e2b65fe03333e4c70999bdca5a227365a2ff39cafc0b9a0dca4df41b85ea53c36ecb355090cdb43ec8c6aa43a398b9089162938b5c6b35167
+ languageName: node
+ linkType: hard
+
"@carbon/utils-position@npm:^1.3.0":
version: 1.3.0
resolution: "@carbon/utils-position@npm:1.3.0"
@@ -1958,6 +2088,15 @@ __metadata:
languageName: node
linkType: hard
+"@hookform/resolvers@npm:^3.9.1":
+ version: 3.9.1
+ resolution: "@hookform/resolvers@npm:3.9.1"
+ peerDependencies:
+ react-hook-form: ^7.0.0
+ checksum: 10/4e7a24f79ead48db5c869c17b5f47412a7d628939f743bdc1248ea3a291b1a9dd777cd02f84326e91d258283a6351566f13bca5cb30f996e5a9db7ee490b5cc5
+ languageName: node
+ linkType: hard
+
"@humanwhocodes/config-array@npm:^0.13.0":
version: 0.13.0
resolution: "@humanwhocodes/config-array@npm:0.13.0"
@@ -1983,6 +2122,62 @@ __metadata:
languageName: node
linkType: hard
+"@ibm/plex-mono@npm:0.0.3-alpha.0":
+ version: 0.0.3-alpha.0
+ resolution: "@ibm/plex-mono@npm:0.0.3-alpha.0"
+ checksum: 10/fbdfb70762dead35bd12fd69344133f3290bd4ede4fd3607f6949e80e3c516190e772afc5f8ba060426911bf1b89744f02e7b0fdd25cca818086f3ce312fcad4
+ languageName: node
+ linkType: hard
+
+"@ibm/plex-sans-arabic@npm:0.0.3-alpha.0":
+ version: 0.0.3-alpha.0
+ resolution: "@ibm/plex-sans-arabic@npm:0.0.3-alpha.0"
+ checksum: 10/c390dd9788a36f4cb2abb2fcf63deb2a3c8b9e7aa8a7e6263ff6484b2fe99044258e2daeb36e7c0b0eeaea17e4128a8ce567208458f851ee6b05ec8c54f84edb
+ languageName: node
+ linkType: hard
+
+"@ibm/plex-sans-devanagari@npm:0.0.3-alpha.0":
+ version: 0.0.3-alpha.0
+ resolution: "@ibm/plex-sans-devanagari@npm:0.0.3-alpha.0"
+ checksum: 10/ef3cd967100210a822bea7b36c5ac54f915a319d5e23fa1175ea63d0405c826023f241d54b4f7beb5928603fbe01a5bae22839dad6922330bb84921eb289d193
+ languageName: node
+ linkType: hard
+
+"@ibm/plex-sans-hebrew@npm:0.0.3-alpha.0":
+ version: 0.0.3-alpha.0
+ resolution: "@ibm/plex-sans-hebrew@npm:0.0.3-alpha.0"
+ checksum: 10/e67ed6e081dbf9a522eca8e35471a329c788e6a03042df89649b034eaa2e66898bc44b72c0c0f57d93d24b37796cfc92729cee7754eb83ec2cd27f1fa9bdeea6
+ languageName: node
+ linkType: hard
+
+"@ibm/plex-sans-thai-looped@npm:0.0.3-alpha.0":
+ version: 0.0.3-alpha.0
+ resolution: "@ibm/plex-sans-thai-looped@npm:0.0.3-alpha.0"
+ checksum: 10/11272b1353611fed07788a870793ca6f45c644f47faa99880d5278552a7acd85b0696ca02336b7aa8e29bf5d6353538d322149cb8b6b2e65985c38f0af6359fe
+ languageName: node
+ linkType: hard
+
+"@ibm/plex-sans-thai@npm:0.0.3-alpha.0":
+ version: 0.0.3-alpha.0
+ resolution: "@ibm/plex-sans-thai@npm:0.0.3-alpha.0"
+ checksum: 10/baac49d77d2075ee6ecbd5ed22d938b1afca898e5d8d9948f079613d2be011216acf52a6ae555e3cf732d8aa60b7b89b1eaef4380590a66270d7b166067a271a
+ languageName: node
+ linkType: hard
+
+"@ibm/plex-sans@npm:0.0.3-alpha.0":
+ version: 0.0.3-alpha.0
+ resolution: "@ibm/plex-sans@npm:0.0.3-alpha.0"
+ checksum: 10/5b0b0521dbeb7c32eb13a932b53baef0013b96d5d39547b35c69a991707a3f75cec37383c9f239229fdedfb91fda8c8005f25fdddcb900937d6de7dbd456175a
+ languageName: node
+ linkType: hard
+
+"@ibm/plex-serif@npm:0.0.3-alpha.0":
+ version: 0.0.3-alpha.0
+ resolution: "@ibm/plex-serif@npm:0.0.3-alpha.0"
+ checksum: 10/462dcf33937f50f5a0ecf320f1d930c612e92293aa40dda08c05f6630d8795e4233023bb4f8ed3d340d2dbbd82a4b7ec5ae5e511f4260b16ff9ade6f481e48e8
+ languageName: node
+ linkType: hard
+
"@ibm/plex@npm:6.0.0-next.6":
version: 6.0.0-next.6
resolution: "@ibm/plex@npm:6.0.0-next.6"
@@ -3157,9 +3352,9 @@ __metadata:
languageName: node
linkType: hard
-"@openmrs/esm-api@npm:5.8.2-pre.2394":
- version: 5.8.2-pre.2394
- resolution: "@openmrs/esm-api@npm:5.8.2-pre.2394"
+"@openmrs/esm-api@npm:5.8.2-pre.2512":
+ version: 5.8.2-pre.2512
+ resolution: "@openmrs/esm-api@npm:5.8.2-pre.2512"
dependencies:
"@types/fhir": "npm:0.0.31"
lodash-es: "npm:^4.17.21"
@@ -3168,17 +3363,17 @@ __metadata:
"@openmrs/esm-error-handling": 5.x
"@openmrs/esm-navigation": 5.x
"@openmrs/esm-offline": 5.x
- checksum: 10/f7582413ea751b37e9d69facc7a9a88b231b8e7ac19018117de80fdc3e6d6d819782b7b33b69b5ce0c5c6a953318f2a8bb2537f6bd621a5fb3adaa070ed56b81
+ checksum: 10/4d90a9e945af9ac07234a3c64a042024b1fe30d8ec7caddc960a560bb80c132390e73f2aa035a03533d919736f1b6714c1847e0699382a26ce32fcb32a525f4e
languageName: node
linkType: hard
-"@openmrs/esm-app-shell@npm:5.8.2-pre.2394":
- version: 5.8.2-pre.2394
- resolution: "@openmrs/esm-app-shell@npm:5.8.2-pre.2394"
+"@openmrs/esm-app-shell@npm:5.8.2-pre.2512":
+ version: 5.8.2-pre.2512
+ resolution: "@openmrs/esm-app-shell@npm:5.8.2-pre.2512"
dependencies:
"@carbon/react": "npm:~1.37.0"
- "@openmrs/esm-framework": "npm:5.8.2-pre.2394"
- "@openmrs/esm-styleguide": "npm:5.8.2-pre.2394"
+ "@openmrs/esm-framework": "npm:5.8.2-pre.2512"
+ "@openmrs/esm-styleguide": "npm:5.8.2-pre.2512"
dayjs: "npm:^1.10.4"
dexie: "npm:^3.0.3"
html-webpack-plugin: "npm:^5.5.0"
@@ -3203,13 +3398,13 @@ __metadata:
workbox-strategies: "npm:^6.1.5"
workbox-webpack-plugin: "npm:^6.1.5"
workbox-window: "npm:^6.1.5"
- checksum: 10/46f4edd485ece64a5c9772ea82e670004873f2f63f471d1a3a04ea75af71ddd94cbcdb4848609a5d7fb14f309ab525c1018b58a41748910fb7e2bc778125cafe
+ checksum: 10/a9ff941a13e6d76cafa658f47d04ef6641d85abb94386b481e78509fd734ccae15ff7a2e31c713c711f37a966ea1bc9e49040cb23549f0788ae2e1aa4fed3c1b
languageName: node
linkType: hard
-"@openmrs/esm-config@npm:5.8.2-pre.2394":
- version: 5.8.2-pre.2394
- resolution: "@openmrs/esm-config@npm:5.8.2-pre.2394"
+"@openmrs/esm-config@npm:5.8.2-pre.2512":
+ version: 5.8.2-pre.2512
+ resolution: "@openmrs/esm-config@npm:5.8.2-pre.2512"
dependencies:
ramda: "npm:^0.26.1"
peerDependencies:
@@ -3217,44 +3412,44 @@ __metadata:
"@openmrs/esm-state": 5.x
"@openmrs/esm-utils": 5.x
single-spa: 5.x
- checksum: 10/ea16fe8de8651053dad9211cfd6277207ea50b7fe9acef81912888713391e26b51890a36dfb4d9a2d051ea4bb65f6ea800c17c2a65c3e8067674b19b84e0f1c8
+ checksum: 10/388c64bce3c03c42611423ce2659d0e257819a4f90348dc215b044539548b890f97c647417cea68d2a04b58087386da65068ec9e4cdd50df4c4989eb361deff6
languageName: node
linkType: hard
-"@openmrs/esm-context@npm:5.8.2-pre.2394":
- version: 5.8.2-pre.2394
- resolution: "@openmrs/esm-context@npm:5.8.2-pre.2394"
+"@openmrs/esm-context@npm:5.8.2-pre.2512":
+ version: 5.8.2-pre.2512
+ resolution: "@openmrs/esm-context@npm:5.8.2-pre.2512"
dependencies:
immer: "npm:^10.0.4"
peerDependencies:
"@openmrs/esm-globals": 5.x
"@openmrs/esm-state": 5.x
- checksum: 10/613bed3f50daed125ec79e343af5282dd1c6a3b5c00b44717d81614631d967189c106346a624f836db261e926c6534e0e27b273f35d31b7e5080170240f32a54
+ checksum: 10/591d5baaa804e6096b66097ef00764250207846e317bb9b164a51df0205d9a90dcaa5ffb844a6836ef3587fb4a624120d44fd8b1584a5522873806153c814947
languageName: node
linkType: hard
-"@openmrs/esm-dynamic-loading@npm:5.8.2-pre.2394":
- version: 5.8.2-pre.2394
- resolution: "@openmrs/esm-dynamic-loading@npm:5.8.2-pre.2394"
+"@openmrs/esm-dynamic-loading@npm:5.8.2-pre.2512":
+ version: 5.8.2-pre.2512
+ resolution: "@openmrs/esm-dynamic-loading@npm:5.8.2-pre.2512"
peerDependencies:
"@openmrs/esm-globals": 5.x
"@openmrs/esm-translations": 5.x
- checksum: 10/acbdab4383978a819be648ecf88168552a0f92850ea370872cbd97a6f791fe7fa16cf89485fa6f8112dc40fcc27d6f1df4137f0413f7aa81fa8ff6726acde37d
+ checksum: 10/752e9ead0250243f07c49dc31b8c7d7bf16f8692ba2d18d11a42f02b600ff81eb587d7097941c8bc3687b3d12e8423835016ee7f16f2338377746ef5317ccf3e
languageName: node
linkType: hard
-"@openmrs/esm-error-handling@npm:5.8.2-pre.2394":
- version: 5.8.2-pre.2394
- resolution: "@openmrs/esm-error-handling@npm:5.8.2-pre.2394"
+"@openmrs/esm-error-handling@npm:5.8.2-pre.2512":
+ version: 5.8.2-pre.2512
+ resolution: "@openmrs/esm-error-handling@npm:5.8.2-pre.2512"
peerDependencies:
"@openmrs/esm-globals": 5.x
- checksum: 10/fe859d529bfb76b1546cc9cbf2e23661bce0f27eb3bb92cd8df74cdfe048fd16e978e1ddd98431aad9f5ebe055245c3c699d63da52af28c3309e097cd2422e81
+ checksum: 10/40e31ef31b26b663199d7ddbdce60e35e4b066271fd0462931ce87fe0515addb0ee46e57dc3a807a7af14ed40656d3ab2843e099aa968d31fc03b2c91045e75d
languageName: node
linkType: hard
-"@openmrs/esm-expression-evaluator@npm:5.8.2-pre.2394":
- version: 5.8.2-pre.2394
- resolution: "@openmrs/esm-expression-evaluator@npm:5.8.2-pre.2394"
+"@openmrs/esm-expression-evaluator@npm:5.8.2-pre.2512":
+ version: 5.8.2-pre.2512
+ resolution: "@openmrs/esm-expression-evaluator@npm:5.8.2-pre.2512"
dependencies:
"@jsep-plugin/arrow": "npm:^1.0.5"
"@jsep-plugin/new": "npm:^1.0.3"
@@ -3263,13 +3458,13 @@ __metadata:
"@jsep-plugin/template": "npm:^1.0.4"
"@jsep-plugin/ternary": "npm:^1.1.3"
jsep: "npm:^1.3.9"
- checksum: 10/36e622414e58185bd18d1fbaece669fc2e267015de0b2184a09246f539b3d974544c1445e0b72c61625de7fde64dd7f08b7c5134539a122334185b0326b1d9b1
+ checksum: 10/126f04a486abe37a0df09d876d7675bbd08073f1c3a0d073487178690e7073948d4bf3ec9ee3b523e0a297884790e0c8c193d52a698a712bb6cbe4e4aeba21f4
languageName: node
linkType: hard
-"@openmrs/esm-extensions@npm:5.8.2-pre.2394":
- version: 5.8.2-pre.2394
- resolution: "@openmrs/esm-extensions@npm:5.8.2-pre.2394"
+"@openmrs/esm-extensions@npm:5.8.2-pre.2512":
+ version: 5.8.2-pre.2512
+ resolution: "@openmrs/esm-extensions@npm:5.8.2-pre.2512"
dependencies:
lodash-es: "npm:^4.17.21"
peerDependencies:
@@ -3280,44 +3475,44 @@ __metadata:
"@openmrs/esm-state": 5.x
"@openmrs/esm-utils": 5.x
single-spa: 5.x
- checksum: 10/1e7c48684b6d6e33a66d88fe60591e4bc4a142d67224d34b7f4beb84e40d73c46ece1c52068f4bb30f729cb1d4590d9eb0042ef7091f4c34bfc6d19b01145aa5
+ checksum: 10/490092a6aab04559ca5a890c372d7dc18ee65cfd2fd2e1db1a68cddbbb44f83f09f2c183a4028ee0d225c46c784d8358fa04c153fa7fc3da7d3b69bbda2edb1c
languageName: node
linkType: hard
-"@openmrs/esm-feature-flags@npm:5.8.2-pre.2394":
- version: 5.8.2-pre.2394
- resolution: "@openmrs/esm-feature-flags@npm:5.8.2-pre.2394"
+"@openmrs/esm-feature-flags@npm:5.8.2-pre.2512":
+ version: 5.8.2-pre.2512
+ resolution: "@openmrs/esm-feature-flags@npm:5.8.2-pre.2512"
dependencies:
ramda: "npm:^0.26.1"
peerDependencies:
"@openmrs/esm-globals": 5.x
"@openmrs/esm-state": 5.x
single-spa: 5.x
- checksum: 10/887eab5628d3b84f8794dc6d88fa471e0b770e48bbd112e8827083591d8bacc9d97bee3403dbdf8794e9428acb50fb8f2296be58a75be7aae0e18d5ea25a8578
- languageName: node
- linkType: hard
-
-"@openmrs/esm-framework@npm:5.8.2-pre.2394, @openmrs/esm-framework@npm:next":
- version: 5.8.2-pre.2394
- resolution: "@openmrs/esm-framework@npm:5.8.2-pre.2394"
- dependencies:
- "@openmrs/esm-api": "npm:5.8.2-pre.2394"
- "@openmrs/esm-config": "npm:5.8.2-pre.2394"
- "@openmrs/esm-context": "npm:5.8.2-pre.2394"
- "@openmrs/esm-dynamic-loading": "npm:5.8.2-pre.2394"
- "@openmrs/esm-error-handling": "npm:5.8.2-pre.2394"
- "@openmrs/esm-expression-evaluator": "npm:5.8.2-pre.2394"
- "@openmrs/esm-extensions": "npm:5.8.2-pre.2394"
- "@openmrs/esm-feature-flags": "npm:5.8.2-pre.2394"
- "@openmrs/esm-globals": "npm:5.8.2-pre.2394"
- "@openmrs/esm-navigation": "npm:5.8.2-pre.2394"
- "@openmrs/esm-offline": "npm:5.8.2-pre.2394"
- "@openmrs/esm-react-utils": "npm:5.8.2-pre.2394"
- "@openmrs/esm-routes": "npm:5.8.2-pre.2394"
- "@openmrs/esm-state": "npm:5.8.2-pre.2394"
- "@openmrs/esm-styleguide": "npm:5.8.2-pre.2394"
- "@openmrs/esm-translations": "npm:5.8.2-pre.2394"
- "@openmrs/esm-utils": "npm:5.8.2-pre.2394"
+ checksum: 10/605da6e8bd009fd2f5ba1ac72aaa79366ed2b23c376ce9ff9805d58e98d63a7c501ef141e5b3a2495309b617f8423e8c3ab5914d65d6dd2b2f193d3df74b7b53
+ languageName: node
+ linkType: hard
+
+"@openmrs/esm-framework@npm:5.8.2-pre.2512, @openmrs/esm-framework@npm:next":
+ version: 5.8.2-pre.2512
+ resolution: "@openmrs/esm-framework@npm:5.8.2-pre.2512"
+ dependencies:
+ "@openmrs/esm-api": "npm:5.8.2-pre.2512"
+ "@openmrs/esm-config": "npm:5.8.2-pre.2512"
+ "@openmrs/esm-context": "npm:5.8.2-pre.2512"
+ "@openmrs/esm-dynamic-loading": "npm:5.8.2-pre.2512"
+ "@openmrs/esm-error-handling": "npm:5.8.2-pre.2512"
+ "@openmrs/esm-expression-evaluator": "npm:5.8.2-pre.2512"
+ "@openmrs/esm-extensions": "npm:5.8.2-pre.2512"
+ "@openmrs/esm-feature-flags": "npm:5.8.2-pre.2512"
+ "@openmrs/esm-globals": "npm:5.8.2-pre.2512"
+ "@openmrs/esm-navigation": "npm:5.8.2-pre.2512"
+ "@openmrs/esm-offline": "npm:5.8.2-pre.2512"
+ "@openmrs/esm-react-utils": "npm:5.8.2-pre.2512"
+ "@openmrs/esm-routes": "npm:5.8.2-pre.2512"
+ "@openmrs/esm-state": "npm:5.8.2-pre.2512"
+ "@openmrs/esm-styleguide": "npm:5.8.2-pre.2512"
+ "@openmrs/esm-translations": "npm:5.8.2-pre.2512"
+ "@openmrs/esm-utils": "npm:5.8.2-pre.2512"
dayjs: "npm:^1.10.7"
peerDependencies:
dayjs: 1.x
@@ -3328,35 +3523,35 @@ __metadata:
rxjs: 6.x
single-spa: 5.x
swr: 2.x
- checksum: 10/54283a817b65e1e537a42b63c86e70770d1b4a32bc186fe6386b58f230241bd34726df2f531a191cb02d1ff27adb0405c6ac4e9ffe4469ea2b8a36bd7ff5f515
+ checksum: 10/c4a21266c94baffa51123f689c180092077e8eebbfd07e7cd7ffa3ed0d994f189a07e0d83d56156e3f8993d38815848e461c8e4a3239c016e68154a42745ade1
languageName: node
linkType: hard
-"@openmrs/esm-globals@npm:5.8.2-pre.2394":
- version: 5.8.2-pre.2394
- resolution: "@openmrs/esm-globals@npm:5.8.2-pre.2394"
+"@openmrs/esm-globals@npm:5.8.2-pre.2512":
+ version: 5.8.2-pre.2512
+ resolution: "@openmrs/esm-globals@npm:5.8.2-pre.2512"
dependencies:
"@types/fhir": "npm:0.0.31"
peerDependencies:
single-spa: 5.x
- checksum: 10/bbfa794eed08e5bbd117c408bb3a82e42594bc7d268c69ad001b2c225cadb3d83323a1e7225b3755586f6d93a48c2b80c5e63957ce9bb82e4e110e9ba376f453
+ checksum: 10/59ca11ae3d301e5ada6004773127873d3b7d9e3be1a9dfbba7aea70e36649ff1a620ac88213b3873dfaf3f77d238a19edc9898a07b788f23773e0d20b836d8d5
languageName: node
linkType: hard
-"@openmrs/esm-navigation@npm:5.8.2-pre.2394":
- version: 5.8.2-pre.2394
- resolution: "@openmrs/esm-navigation@npm:5.8.2-pre.2394"
+"@openmrs/esm-navigation@npm:5.8.2-pre.2512":
+ version: 5.8.2-pre.2512
+ resolution: "@openmrs/esm-navigation@npm:5.8.2-pre.2512"
dependencies:
path-to-regexp: "npm:6.1.0"
peerDependencies:
"@openmrs/esm-state": 5.x
- checksum: 10/bfeb51fd48334091095f5d51b937b84e4e4eb116be9e2fd924eda1566b617452db4f87ec17f0299ff6d016883f86190ddd023b79b5836f36a9127eb2f71ac191
+ checksum: 10/4a65fba4466d777196eefc64775ca2efb0a98cf088fef654385850221140420022ecfcde8e942731ac99ec8061d201e105568df26af9b4dc29bb82a6a8144760
languageName: node
linkType: hard
-"@openmrs/esm-offline@npm:5.8.2-pre.2394":
- version: 5.8.2-pre.2394
- resolution: "@openmrs/esm-offline@npm:5.8.2-pre.2394"
+"@openmrs/esm-offline@npm:5.8.2-pre.2512":
+ version: 5.8.2-pre.2512
+ resolution: "@openmrs/esm-offline@npm:5.8.2-pre.2512"
dependencies:
dexie: "npm:^3.0.3"
lodash-es: "npm:^4.17.21"
@@ -3367,13 +3562,96 @@ __metadata:
"@openmrs/esm-globals": 5.x
"@openmrs/esm-state": 5.x
rxjs: 6.x
- checksum: 10/628e7af1ae9159eaa26fba48a79615c6e551c918f9e592d85cacb5d6dfac6199cfa2ec091695e2b71fa16973731035eece6366889b25898cb8589cf525d71c16
+ checksum: 10/9b7d1de0b2df5c565b2c1f76ec38b9b8b94b324873f992d7c6239f9f214fac248537dce7023de81f0efcfd62b76ff109745dc97d261b8ed44598d7a4af63785c
languageName: node
linkType: hard
-"@openmrs/esm-react-utils@npm:5.8.2-pre.2394":
- version: 5.8.2-pre.2394
- resolution: "@openmrs/esm-react-utils@npm:5.8.2-pre.2394"
+"@openmrs/esm-patient-common-lib@portal:/Users/vasharma05/Projects/patient-chart/packages/esm-patient-common-lib::locator=%40openmrs%2Fesm-patient-medical-supply-orders-app%40workspace%3A.":
+ version: 0.0.0-use.local
+ resolution: "@openmrs/esm-patient-common-lib@portal:/Users/vasharma05/Projects/patient-chart/packages/esm-patient-common-lib::locator=%40openmrs%2Fesm-patient-medical-supply-orders-app%40workspace%3A."
+ dependencies:
+ "@carbon/react": "npm:^1.12.0"
+ lodash-es: "npm:^4.17.21"
+ uuid: "npm:^8.3.2"
+ peerDependencies:
+ "@openmrs/esm-framework": 5.x
+ react: 18.x
+ single-spa: 6.x
+ languageName: node
+ linkType: soft
+
+"@openmrs/esm-patient-medical-supply-orders-app@workspace:.":
+ version: 0.0.0-use.local
+ resolution: "@openmrs/esm-patient-medical-supply-orders-app@workspace:."
+ dependencies:
+ "@carbon/react": "npm:^1.71.0"
+ "@hookform/resolvers": "npm:^3.9.1"
+ "@openmrs/esm-framework": "npm:next"
+ "@openmrs/esm-patient-common-lib": "npm:next"
+ "@openmrs/esm-styleguide": "npm:next"
+ "@playwright/test": "npm:^1.42.1"
+ "@swc/cli": "npm:^0.3.12"
+ "@swc/core": "npm:^1.3.68"
+ "@swc/jest": "npm:^0.2.36"
+ "@testing-library/dom": "npm:^10.1.0"
+ "@testing-library/jest-dom": "npm:^6.4.5"
+ "@testing-library/react": "npm:^15.0.6"
+ "@testing-library/user-event": "npm:^14.5.2"
+ "@types/jest": "npm:^29.5.12"
+ "@types/react": "npm:^18.2.14"
+ "@types/react-dom": "npm:^18.2.6"
+ "@types/react-router": "npm:^5.1.20"
+ "@types/react-router-dom": "npm:^5.3.3"
+ "@types/webpack-env": "npm:^1.18.1"
+ "@typescript-eslint/eslint-plugin": "npm:^7.8.0"
+ "@typescript-eslint/parser": "npm:^7.8.0"
+ css-loader: "npm:^6.8.1"
+ dotenv: "npm:^16.0.3"
+ eslint: "npm:^8.50.0"
+ eslint-config-prettier: "npm:^8.8.0"
+ eslint-plugin-jest-dom: "npm:^5.4.0"
+ eslint-plugin-prettier: "npm:^5.1.3"
+ eslint-plugin-react-hooks: "npm:^4.6.2"
+ husky: "npm:^8.0.3"
+ i18next: "npm:^23.2.8"
+ i18next-parser: "npm:^9.0.2"
+ identity-obj-proxy: "npm:^3.0.0"
+ jest: "npm:^29.7.0"
+ jest-cli: "npm:^29.7.0"
+ jest-environment-jsdom: "npm:^29.7.0"
+ lint-staged: "npm:^15.2.2"
+ lodash-es: "npm:^4.17.21"
+ openmrs: "npm:next"
+ prettier: "npm:^3.3.3"
+ react: "npm:^18.2.0"
+ react-dom: "npm:^18.2.0"
+ react-hook-form: "npm:^7.53.2"
+ react-i18next: "npm:^11.18.6"
+ react-router-dom: "npm:^6.14.1"
+ rxjs: "npm:^6.6.7"
+ swc-loader: "npm:^0.2.3"
+ swr: "npm:^2.2.5"
+ turbo: "npm:^2.2.3"
+ typescript: "npm:^4.9.5"
+ webpack: "npm:^5.88.1"
+ webpack-cli: "npm:^5.1.4"
+ zod: "npm:^3.23.8"
+ peerDependencies:
+ "@carbon/react": ^1.x
+ "@openmrs/esm-framework": 5.x
+ "@openmrs/esm-patient-common-lib": 8.x
+ dayjs: 1.x
+ react: 18.x
+ react-i18next: 11.x
+ react-router-dom: 6.x
+ rxjs: 6.x
+ swr: 2.x
+ languageName: unknown
+ linkType: soft
+
+"@openmrs/esm-react-utils@npm:5.8.2-pre.2512":
+ version: 5.8.2-pre.2512
+ resolution: "@openmrs/esm-react-utils@npm:5.8.2-pre.2512"
dependencies:
lodash-es: "npm:^4.17.21"
single-spa-react: "npm:^6.0.0"
@@ -3394,13 +3672,13 @@ __metadata:
react-i18next: 11.x
rxjs: 6.x
swr: 2.x
- checksum: 10/a243763f4ba28a909f7869de5b5636d885177bcb2f58b45b293aef39efecb74745eeae8889433aa21edf11b6e4f5030094f4d7bae2e25b6bb3013fe4fa9bf6ad
+ checksum: 10/f5ab3e90de2bf371e053d345a69db87ce9d846870e690f6fc3b63ee89ab6c20904c0237e77d357bdc645f2cb72cd74a0561837ca382d3679df9385834fcc91bc
languageName: node
linkType: hard
-"@openmrs/esm-routes@npm:5.8.2-pre.2394":
- version: 5.8.2-pre.2394
- resolution: "@openmrs/esm-routes@npm:5.8.2-pre.2394"
+"@openmrs/esm-routes@npm:5.8.2-pre.2512":
+ version: 5.8.2-pre.2512
+ resolution: "@openmrs/esm-routes@npm:5.8.2-pre.2512"
peerDependencies:
"@openmrs/esm-config": 5.x
"@openmrs/esm-dynamic-loading": 5.x
@@ -3409,25 +3687,25 @@ __metadata:
"@openmrs/esm-globals": 5.x
"@openmrs/esm-utils": 5.x
single-spa: 6.x
- checksum: 10/edd972792a6dc4be07f26af19878a0dc2d39a4e7cf5727eaf6e1375ae767f0d1160a426603ba67702dafba122af75311769d65ef70a8227e18554d38af7dd4f8
+ checksum: 10/c5e1658e0a68dfc525f018e8cea0cd35944c24893ee0e67b018e015ec31cacd61869e0caafcfb686b5a2fecd06100c921f364dde5d04b4f216880e2a90fa6259
languageName: node
linkType: hard
-"@openmrs/esm-state@npm:5.8.2-pre.2394":
- version: 5.8.2-pre.2394
- resolution: "@openmrs/esm-state@npm:5.8.2-pre.2394"
+"@openmrs/esm-state@npm:5.8.2-pre.2512":
+ version: 5.8.2-pre.2512
+ resolution: "@openmrs/esm-state@npm:5.8.2-pre.2512"
dependencies:
zustand: "npm:^4.5.5"
peerDependencies:
"@openmrs/esm-globals": 5.x
"@openmrs/esm-utils": 5.x
- checksum: 10/4caec12e57cd18e423cd2f49e7a51cc4513d17ea523e7b41b5cc0f23ea40b43c3266e947a3ebdd5e593172200fa8002e36fe05b17399bd443601b03b3948cfaf
+ checksum: 10/31be7cf2a73b5673140703ddfa2109f5d75c68151b9084ba978916bb96c5877dc641f835404847296616095e488b4685c317c062d11c8c72e2d9eaebd7e66777
languageName: node
linkType: hard
-"@openmrs/esm-styleguide@npm:5.8.2-pre.2394, @openmrs/esm-styleguide@npm:next":
- version: 5.8.2-pre.2394
- resolution: "@openmrs/esm-styleguide@npm:5.8.2-pre.2394"
+"@openmrs/esm-styleguide@npm:5.8.2-pre.2512":
+ version: 5.8.2-pre.2512
+ resolution: "@openmrs/esm-styleguide@npm:5.8.2-pre.2512"
dependencies:
"@carbon/charts": "npm:^1.12.0"
"@carbon/react": "npm:~1.37.0"
@@ -3451,85 +3729,54 @@ __metadata:
react-dom: 18.x
react-i18next: 11.x
rxjs: 6.x
- checksum: 10/003214fbaac3028ea3436357ff38f2ae4bbfa3ccb3951cc082c197598e1486d29e83943ff5a0e96002812a00e1d52808ddb036b7abd9fa9d6d1d975da7b28f27
+ checksum: 10/aa4005f8a6001fded29bd1bda0c78e05053e04422cbd6644512d1393309eb235fc4ad76c0b1fe4efd1939eecbe5310a1c1e36334e821c0ae5d91c6bb8c083dfa
languageName: node
linkType: hard
-"@openmrs/esm-template-app@workspace:.":
- version: 0.0.0-use.local
- resolution: "@openmrs/esm-template-app@workspace:."
+"@openmrs/esm-styleguide@npm:next":
+ version: 5.8.2-pre.2394
+ resolution: "@openmrs/esm-styleguide@npm:5.8.2-pre.2394"
dependencies:
- "@carbon/react": "npm:^1.68.0"
- "@openmrs/esm-framework": "npm:next"
- "@openmrs/esm-styleguide": "npm:next"
- "@playwright/test": "npm:^1.42.1"
- "@swc/cli": "npm:^0.3.12"
- "@swc/core": "npm:^1.3.68"
- "@swc/jest": "npm:^0.2.36"
- "@testing-library/dom": "npm:^10.1.0"
- "@testing-library/jest-dom": "npm:^6.4.5"
- "@testing-library/react": "npm:^15.0.6"
- "@testing-library/user-event": "npm:^14.5.2"
- "@types/jest": "npm:^29.5.12"
- "@types/react": "npm:^18.2.14"
- "@types/react-dom": "npm:^18.2.6"
- "@types/react-router": "npm:^5.1.20"
- "@types/react-router-dom": "npm:^5.3.3"
- "@types/webpack-env": "npm:^1.18.1"
- "@typescript-eslint/eslint-plugin": "npm:^7.8.0"
- "@typescript-eslint/parser": "npm:^7.8.0"
- css-loader: "npm:^6.8.1"
- dotenv: "npm:^16.0.3"
- eslint: "npm:^8.50.0"
- eslint-config-prettier: "npm:^8.8.0"
- eslint-plugin-jest-dom: "npm:^5.4.0"
- eslint-plugin-prettier: "npm:^5.1.3"
- eslint-plugin-react-hooks: "npm:^4.6.2"
- husky: "npm:^8.0.3"
- i18next: "npm:^23.2.8"
- i18next-parser: "npm:^9.0.2"
- identity-obj-proxy: "npm:^3.0.0"
- jest: "npm:^29.7.0"
- jest-cli: "npm:^29.7.0"
- jest-environment-jsdom: "npm:^29.7.0"
- lint-staged: "npm:^15.2.2"
+ "@carbon/charts": "npm:^1.12.0"
+ "@carbon/react": "npm:~1.37.0"
+ "@internationalized/date": "npm:^3.5.5"
+ core-js-pure: "npm:^3.36.0"
+ d3: "npm:^7.8.0"
+ geopattern: "npm:^1.2.3"
lodash-es: "npm:^4.17.21"
- openmrs: "npm:next"
- prettier: "npm:^3.3.3"
- react: "npm:^18.2.0"
- react-dom: "npm:^18.2.0"
- react-i18next: "npm:^11.18.6"
- react-router-dom: "npm:^6.14.1"
- rxjs: "npm:^6.6.7"
- swc-loader: "npm:^0.2.3"
- turbo: "npm:^2.2.3"
- typescript: "npm:^4.9.5"
- webpack: "npm:^5.88.1"
- webpack-cli: "npm:^5.1.4"
+ react-aria-components: "npm:^1.3.3"
+ react-avatar: "npm:^5.0.3"
peerDependencies:
- "@openmrs/esm-framework": "*"
+ "@openmrs/esm-error-handling": 5.x
+ "@openmrs/esm-extensions": 5.x
+ "@openmrs/esm-navigation": 5.x
+ "@openmrs/esm-react-utils": 5.x
+ "@openmrs/esm-state": 5.x
+ "@openmrs/esm-translations": 5.x
dayjs: 1.x
+ i18next: 21.x
react: 18.x
+ react-dom: 18.x
react-i18next: 11.x
- react-router-dom: 6.x
rxjs: 6.x
- languageName: unknown
- linkType: soft
+ checksum: 10/003214fbaac3028ea3436357ff38f2ae4bbfa3ccb3951cc082c197598e1486d29e83943ff5a0e96002812a00e1d52808ddb036b7abd9fa9d6d1d975da7b28f27
+ languageName: node
+ linkType: hard
-"@openmrs/esm-translations@npm:5.8.2-pre.2394":
- version: 5.8.2-pre.2394
- resolution: "@openmrs/esm-translations@npm:5.8.2-pre.2394"
+"@openmrs/esm-translations@npm:5.8.2-pre.2512":
+ version: 5.8.2-pre.2512
+ resolution: "@openmrs/esm-translations@npm:5.8.2-pre.2512"
dependencies:
i18next: "npm:21.10.0"
peerDependencies:
i18next: 21.x
- checksum: 10/f58f8abfabc171c47d1c9236b425e0279e32cadd296999503caaab0b205ac8309bb44274b0b8c7e97a4f27cf7819123829169c1e9686e23a0b329a7d3e3d1c75
+ checksum: 10/bf1bb68fc43857a78a2bbd729291ef12943de764922581d3b07d3abed6ded823335749fd5219f5b6e3a2a9f1262c81798c16b7e69ab7ac579c49930597edb2e1
languageName: node
linkType: hard
-"@openmrs/esm-utils@npm:5.8.2-pre.2394":
- version: 5.8.2-pre.2394
- resolution: "@openmrs/esm-utils@npm:5.8.2-pre.2394"
+"@openmrs/esm-utils@npm:5.8.2-pre.2512":
+ version: 5.8.2-pre.2512
+ resolution: "@openmrs/esm-utils@npm:5.8.2-pre.2512"
dependencies:
"@formatjs/intl-durationformat": "npm:^0.2.4"
"@internationalized/date": "npm:^3.5.5"
@@ -3539,13 +3786,13 @@ __metadata:
dayjs: 1.x
i18next: 21.x
rxjs: 6.x
- checksum: 10/b1a4cd7ea2ba59213f349e524123d2b943cf4c222c6582e0006218b7ffefbc821878972f3b50af49f6d0c108efbc33a935be32bb0bfadcc108b3ef7e2967a042
+ checksum: 10/4ee20e019c2f390e03dc4264b1b1eb5c4f38c2a912fce53d4ae97475dea08d5f5bc5ab339e38f11f1b3886ac25f0a60d8a90590d2b678c4718a587ff1ae0a03e
languageName: node
linkType: hard
-"@openmrs/webpack-config@npm:5.8.2-pre.2394":
- version: 5.8.2-pre.2394
- resolution: "@openmrs/webpack-config@npm:5.8.2-pre.2394"
+"@openmrs/webpack-config@npm:5.8.2-pre.2512":
+ version: 5.8.2-pre.2512
+ resolution: "@openmrs/webpack-config@npm:5.8.2-pre.2512"
dependencies:
"@swc/core": "npm:^1.3.58"
clean-webpack-plugin: "npm:^4.0.0"
@@ -3563,7 +3810,7 @@ __metadata:
webpack-stats-plugin: "npm:^1.0.3"
peerDependencies:
webpack: 5.x
- checksum: 10/a14be9ac9fc75f6f384d7bdd6e80a1e5653d426518dedc498cc1545b7a0b7b36f43ee5da8c54b51716b1684ab24427199f459d0e0da59556865ace5e99f76c28
+ checksum: 10/620cb630f9097ea71be8726999c9f97265fe7234215e404e089606a6066c78b64f21547f30bf927bbf7101301e6c0cc0915073ddf57e9ef7a18d1b507a18ae1a
languageName: node
linkType: hard
@@ -8333,7 +8580,7 @@ __metadata:
languageName: node
linkType: hard
-"compute-scroll-into-view@npm:^3.0.3":
+"compute-scroll-into-view@npm:^3.1.0":
version: 3.1.0
resolution: "compute-scroll-into-view@npm:3.1.0"
checksum: 10/cc5211d49bced5ad23385da5c2eaf69b6045628581b0dcb9f4dd407bfee51bbd26d2bce426be26edf2feaf8c243706f5a7c3759827d89cc5a01a5cf7d299a5eb
@@ -9571,18 +9818,18 @@ __metadata:
languageName: node
linkType: hard
-"downshift@npm:8.5.0":
- version: 8.5.0
- resolution: "downshift@npm:8.5.0"
+"downshift@npm:9.0.8":
+ version: 9.0.8
+ resolution: "downshift@npm:9.0.8"
dependencies:
- "@babel/runtime": "npm:^7.22.15"
- compute-scroll-into-view: "npm:^3.0.3"
+ "@babel/runtime": "npm:^7.24.5"
+ compute-scroll-into-view: "npm:^3.1.0"
prop-types: "npm:^15.8.1"
- react-is: "npm:^18.2.0"
+ react-is: "npm:18.2.0"
tslib: "npm:^2.6.2"
peerDependencies:
react: ">=16.12.0"
- checksum: 10/275f2b6868bf61aae276780c54e7511b2e4b3966c568ad9760df00bf306fef3a8aa76eae56a66d09526b062d8747a17358d21d9d8f60107f87e9f7398c85c92d
+ checksum: 10/9dc4577e780c54742ba4dde11f481f0d839f001b309200fbe4db112385b227ccd9cd2ef97d9e995379fa70249f0664a562240e415b9966f18c8a5cb7ce435f2c
languageName: node
linkType: hard
@@ -14475,11 +14722,11 @@ __metadata:
linkType: hard
"openmrs@npm:next":
- version: 5.8.2-pre.2394
- resolution: "openmrs@npm:5.8.2-pre.2394"
+ version: 5.8.2-pre.2512
+ resolution: "openmrs@npm:5.8.2-pre.2512"
dependencies:
- "@openmrs/esm-app-shell": "npm:5.8.2-pre.2394"
- "@openmrs/webpack-config": "npm:5.8.2-pre.2394"
+ "@openmrs/esm-app-shell": "npm:5.8.2-pre.2512"
+ "@openmrs/webpack-config": "npm:5.8.2-pre.2512"
"@pnpm/npm-conf": "npm:^2.1.0"
"@swc/core": "npm:^1.3.58"
autoprefixer: "npm:^10.4.20"
@@ -14518,7 +14765,7 @@ __metadata:
yargs: "npm:^17.6.2"
bin:
openmrs: ./dist/cli.js
- checksum: 10/ef5567dd6c6b13b6b4893348e7af805bbaec7b045080f790324930c1952fe96c710c37b9c9aa02899ec19b7438b6bdeeb9c8936d5100c5e9595dee0567c94db4
+ checksum: 10/6eed21a2be1721deb530e536ad3c4a8fe38b949b770ce968d60461eff47305f985d2c2b5b4ca662e61e70587feb8e6fb0db7c5a02f570d8dd4385c522383f4d6
languageName: node
linkType: hard
@@ -15823,6 +16070,15 @@ __metadata:
languageName: node
linkType: hard
+"react-hook-form@npm:^7.53.2":
+ version: 7.53.2
+ resolution: "react-hook-form@npm:7.53.2"
+ peerDependencies:
+ react: ^16.8.0 || ^17 || ^18 || ^19
+ checksum: 10/172589ac5871bd41717e8c6bc4c825d149db88eb02e356a8fef4115615a56b21bf66ad16954575b1d6302ed265b8780f1975a2730d5aa10a53e412b56d9f72ca
+ languageName: node
+ linkType: hard
+
"react-i18next@npm:^11.18.6":
version: 11.18.6
resolution: "react-i18next@npm:11.18.6"
@@ -15841,6 +16097,13 @@ __metadata:
languageName: node
linkType: hard
+"react-is@npm:18.2.0":
+ version: 18.2.0
+ resolution: "react-is@npm:18.2.0"
+ checksum: 10/200cd65bf2e0be7ba6055f647091b725a45dd2a6abef03bf2380ce701fd5edccee40b49b9d15edab7ac08a762bf83cb4081e31ec2673a5bfb549a36ba21570df
+ languageName: node
+ linkType: hard
+
"react-is@npm:^16.13.1":
version: 16.13.1
resolution: "react-is@npm:16.13.1"
@@ -19328,6 +19591,13 @@ __metadata:
languageName: node
linkType: hard
+"zod@npm:^3.23.8":
+ version: 3.23.8
+ resolution: "zod@npm:3.23.8"
+ checksum: 10/846fd73e1af0def79c19d510ea9e4a795544a67d5b34b7e1c4d0425bf6bfd1c719446d94cdfa1721c1987d891321d61f779e8236fde517dc0e524aa851a6eff1
+ languageName: node
+ linkType: hard
+
"zustand@npm:^4.5.5":
version: 4.5.5
resolution: "zustand@npm:4.5.5"