@cc-openmrs/cc-esm-active-prescriptions 1.0.21 → 1.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/296.js CHANGED
@@ -1 +1 @@
1
- "use strict";(globalThis.webpackChunk_cc_openmrs_cc_esm_active_prescriptions=globalThis.webpackChunk_cc_openmrs_cc_esm_active_prescriptions||[]).push([[296],{4296(e,t,r){r.r(t),r.d(t,{default:()=>s});var n=r(6072),c=r(3335),o=r(5987),i=r(2076),a=r(8150);function p(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}const s=function(e){var t=e.patientUuid,r=(0,i.useTranslation)().t,s=(0,n.useCallback)(function(){var e=function(e){var t;return null!=e?e:null===(t=window.location.pathname.match(/\/patient\/([^/]+)\/chart/))||void 0===t?void 0:t[1]}(t);e&&(0,o.navigateAndLaunchWorkspace)({targetUrl:"".concat(window.getOpenmrsSpaBase(),"patient/").concat(e,"/chart"),workspaceName:"active-prescriptions-workspace",contextKey:"patient/".concat(e),additionalProps:{patientUuid:e}})},[t]),u=r("prescriptionsActionMenuItem","Active prescriptions");return n.createElement(a.ED,{getIcon:function(e){return n.createElement(c.KM,(t=function(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{},n=Object.keys(r);"function"==typeof Object.getOwnPropertySymbols&&(n=n.concat(Object.getOwnPropertySymbols(r).filter(function(e){return Object.getOwnPropertyDescriptor(r,e).enumerable}))),n.forEach(function(t){p(e,t,r[t])})}return e}({},e),r=null!=(r={"aria-hidden":"true"})?r:{},Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):function(e){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t.push.apply(t,r)}return t}(Object(r)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))}),t));var t,r},handler:s,iconDescription:u,label:u,type:"active-prescriptions"})}}}]);
1
+ "use strict";(globalThis.webpackChunk_cc_openmrs_cc_esm_active_prescriptions=globalThis.webpackChunk_cc_openmrs_cc_esm_active_prescriptions||[]).push([[296],{4296(e,t,r){r.r(t),r.d(t,{default:()=>s});var n=r(6072),c=r(3335),i=r(5987),o=r(2076),a=r(8150);function p(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}const s=function(e){var t=e.patientUuid,r=(0,o.useTranslation)().t,s=(0,n.useCallback)(function(){var e=function(e){var t;return null!=e?e:null===(t=window.location.pathname.match(/\/patient\/([^/]+)\/chart/))||void 0===t?void 0:t[1]}(t);e&&(0,i.launchWorkspace)("active-prescriptions-workspace",{patientUuid:e})},[t]),u=r("prescriptionsActionMenuItem","Active prescriptions");return n.createElement(a.ED,{getIcon:function(e){return n.createElement(c.KM,(t=function(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{},n=Object.keys(r);"function"==typeof Object.getOwnPropertySymbols&&(n=n.concat(Object.getOwnPropertySymbols(r).filter(function(e){return Object.getOwnPropertyDescriptor(r,e).enumerable}))),n.forEach(function(t){p(e,t,r[t])})}return e}({},e),r=null!=(r={"aria-hidden":"true"})?r:{},Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):function(e){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t.push.apply(t,r)}return t}(Object(r)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))}),t));var t,r},handler:s,iconDescription:u,label:u,type:"active-prescriptions"})}}}]);
@@ -265,9 +265,9 @@
265
265
  "initial": false,
266
266
  "entry": false,
267
267
  "recorded": false,
268
- "size": 3601,
268
+ "size": 3357,
269
269
  "sizes": {
270
- "javascript": 3601
270
+ "javascript": 3357
271
271
  },
272
272
  "names": [],
273
273
  "idHints": [],
@@ -281,7 +281,7 @@
281
281
  "auxiliaryFiles": [
282
282
  "296.js.map"
283
283
  ],
284
- "hash": "b9d359d185f9a39b",
284
+ "hash": "09d2e4b9da296d99",
285
285
  "childrenByOrder": {}
286
286
  },
287
287
  {
@@ -480,7 +480,7 @@
480
480
  "auxiliaryFiles": [
481
481
  "477.js.map"
482
482
  ],
483
- "hash": "4183d200638a6f42",
483
+ "hash": "75c8b897ebf3de67",
484
484
  "childrenByOrder": {}
485
485
  },
486
486
  {
package/dist/routes.json CHANGED
@@ -1 +1 @@
1
- {"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"fhir2":">=1.2","webservices.rest":">=2.2.0"},"extensions":[{"name":"Red box","component":"redBox","slot":"Boxes"},{"name":"Blue box","component":"blueBox","slot":"Boxes"},{"name":"Brand box","component":"brandBox","slot":"Boxes"},{"name":"prescriptions-action-menu","component":"prescriptionsActionMenuItem","slot":"action-menu-patient-chart-items-slot","order":4,"meta":{"label":"Prescriptions 2","icon":"pill"}},{"name":"Prescriptions action","component":"prescriptionsActionButton","slot":"patient-actions-slot","order":50}],"pages":[{"component":"root","route":"root"}],"workspaces":[{"name":"active-prescriptions-workspace","title":"activePrescriptionsWorkspaceTitle","component":"root","type":"active-prescriptions","canHide":true,"width":"wider"}],"workspaces2":[{"name":"active-prescriptions-workspace","component":"root","window":"patient-chart-active-prescriptions"}],"workspaceWindows2":[{"name":"patient-chart-active-prescriptions","group":"patient-chart","icon":"prescriptionsActionMenuItem","order":4,"width":"wider","canMaximize":true}],"version":"1.0.21"}
1
+ {"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"fhir2":">=1.2","webservices.rest":">=2.2.0"},"extensions":[{"name":"Red box","component":"redBox","slot":"Boxes"},{"name":"Blue box","component":"blueBox","slot":"Boxes"},{"name":"Brand box","component":"brandBox","slot":"Boxes"},{"name":"prescriptions-siderail","component":"prescriptionsActionMenuItem","slot":"patient-chart-siderail-slot","order":4,"meta":{"label":"Prescriptions","icon":"pill"}},{"name":"prescriptions-action","component":"prescriptionsActionButton","slot":"action-menu-patient-chart-items-slot","order":50}],"pages":[{"component":"root","route":"root"}],"workspaces":[{"name":"active-prescriptions-workspace","title":"Active prescriptions","component":"root","group":"patient-chart","canHide":true,"width":"wider"}],"version":"1.0.23"}
package/fonta_ia.txt ADDED
@@ -0,0 +1,639 @@
1
+ # src/root.component.tsx
2
+ /**
3
+ * From here, the application is pretty typical React, but with lots of
4
+ * support from `@openmrs/esm-framework`. Check out `Greeter` to see
5
+ * usage of the configuration system, and check out `PatientGetter` to
6
+ * see data fetching using the OpenMRS FHIR API.
7
+ *
8
+ * Check out the Config docs:
9
+ * https://openmrs.github.io/openmrs-esm-core/#/main/config
10
+ */
11
+
12
+ import React, { useMemo, useState } from 'react';
13
+ import { PrescriptionService, PrescriptionPayload } from './prescriptions.service';
14
+ import {
15
+ Checkbox,
16
+ ComboBox,
17
+ InlineLoading,
18
+ InlineNotification,
19
+ Tab,
20
+ TabList,
21
+ TabPanel,
22
+ TabPanels,
23
+ Tabs,
24
+ TextInput,
25
+ } from '@carbon/react';
26
+ import { useTranslation } from 'react-i18next';
27
+ import { useLocations, type Location } from '@openmrs/esm-framework';
28
+ import { usePatientOrders } from './prescriptions-orders/usePatientOrders';
29
+ import styles from './root.scss';
30
+
31
+ interface RootProps {
32
+ patientUuid?: string;
33
+ }
34
+
35
+ const Root: React.FC<RootProps> = ({ patientUuid }) => {
36
+ const { t } = useTranslation();
37
+ const [activeTab, setActiveTab] = useState<'new' | 'past'>('new');
38
+ const [selectedLocation, setSelectedLocation] = useState<Location | null>(null);
39
+ const [policyNumber, setPolicyNumber] = useState('');
40
+ const [selectedOrders, setSelectedOrders] = useState<Set<string>>(new Set());
41
+ const [digitallySigned, setDigitallySigned] = useState(false);
42
+ const [printRequested, setPrintRequested] = useState(false);
43
+ const [isSubmitting, setIsSubmitting] = useState(false);
44
+ const locations = useLocations();
45
+ const { orders, isLoading, error } = usePatientOrders(patientUuid);
46
+
47
+ const locationItems = useMemo(() => locations ?? [], [locations]);
48
+ const selectedOrdersLabel =
49
+ selectedOrders.size > 0
50
+ ? t('selectedOrdersCount', {
51
+ count: selectedOrders.size,
52
+ })
53
+ : null;
54
+
55
+ const toggleOrderSelection = (orderUuid: string) => {
56
+ setSelectedOrders((prev) => {
57
+ const updated = new Set(prev);
58
+ if (updated.has(orderUuid)) {
59
+ updated.delete(orderUuid);
60
+ } else {
61
+ updated.add(orderUuid);
62
+ }
63
+ return updated;
64
+ });
65
+ };
66
+
67
+ const renderOrdersSection = () => {
68
+ if (!patientUuid) {
69
+ return (
70
+ <InlineNotification
71
+ lowContrast
72
+ kind="warning"
73
+ title={t('ordersMissingPatientTitle', 'Select a patient to continue')}
74
+ subtitle={t(
75
+ 'ordersMissingPatientSubtitle',
76
+ 'Launch this workspace from a patient chart to review their orders.',
77
+ )}
78
+ />
79
+ );
80
+ }
81
+
82
+ if (error) {
83
+ return (
84
+ <InlineNotification
85
+ lowContrast
86
+ kind="error"
87
+ title={t('ordersErrorTitle', 'Unable to load orders')}
88
+ subtitle={t('ordersErrorSubtitle', 'Try again or refresh the patient chart.')}
89
+ />
90
+ );
91
+ }
92
+
93
+ if (isLoading) {
94
+ return <InlineLoading description={`${t('loading', 'Loading')}...`} />;
95
+ }
96
+
97
+ if (!orders.length) {
98
+ return (
99
+ <InlineNotification
100
+ lowContrast
101
+ kind="info"
102
+ title={t('ordersEmptyState', 'There are no orders for this patient yet.')}
103
+ />
104
+ );
105
+ }
106
+
107
+ return (
108
+ <>
109
+ <div className={styles.ordersList} role="group" aria-label={t('visitTypeHeading', 'Visit type')}>
110
+ {orders.map((order) => (
111
+ <Checkbox
112
+ key={order.uuid}
113
+ id={`order-${order.uuid}`}
114
+ labelText={
115
+ <span className={styles.orderLabel}>
116
+ <span className={styles.orderName}>{order.conceptName}</span>
117
+ {order.display && <span className={styles.orderMeta}>{order.display}</span>}
118
+ {!order.display && order.orderNumber && <span className={styles.orderMeta}>{order.orderNumber}</span>}
119
+ </span>
120
+ }
121
+ checked={selectedOrders.has(order.uuid)}
122
+ onChange={() => toggleOrderSelection(order.uuid)}
123
+ />
124
+ ))}
125
+ </div>
126
+ {selectedOrdersLabel ? <p className={styles.selectionSummary}>{selectedOrdersLabel}</p> : null}
127
+ </>
128
+ );
129
+ };
130
+
131
+ return (
132
+ <div className={styles.workspace}>
133
+ <div className={styles.header}>
134
+ <h2>{t('activePrescriptionsWorkspaceTitle', 'Active prescriptions')}</h2>
135
+ <p className={styles.subtitle}>
136
+ {t(
137
+ 'activePrescriptionsHelper',
138
+ 'Review the patient context, choose the visit details, and add the matching orders to the prescription.',
139
+ )}
140
+ </p>
141
+ </div>
142
+
143
+ <section className={styles.section}>
144
+ <p className={styles.fieldLabel}>{t('visitTimingLabel', 'The visit is')}</p>
145
+ <Tabs
146
+ selectedIndex={activeTab === 'new' ? 0 : 1}
147
+ onChange={({ selectedIndex }) => setActiveTab(selectedIndex === 0 ? 'new' : 'past')}
148
+ >
149
+ <TabList aria-label={t('visitTimingLabel', 'The visit is')}>
150
+ <Tab>{t('visitTimingNew', 'New')}</Tab>
151
+ <Tab>{t('visitTimingPast', 'In the past')}</Tab>
152
+ </TabList>
153
+ <TabPanels>
154
+ <TabPanel className={styles.tabContent}>
155
+ <div className={styles.fieldGroup}>
156
+ <ComboBox
157
+ id="visit-location"
158
+ items={locationItems}
159
+ itemToString={(item) => item?.display ?? ''}
160
+ selectedItem={selectedLocation}
161
+ onChange={({ selectedItem }) => setSelectedLocation((selectedItem as Location) ?? null)}
162
+ placeholder={t('visitLocationPlaceholder', 'Select a location')}
163
+ titleText={t('visitLocationHeading', 'Visit location')}
164
+ helperText={t('visitLocationHelper', 'Select where the visit will take place.')}
165
+ />
166
+ </div>
167
+
168
+ <div className={styles.fieldGroup}>
169
+ <p className={styles.fieldLabel}>{t('visitTypeHeading', 'Visit type')}</p>
170
+ <p className={styles.helperText}>
171
+ {t('visitTypeHelper', 'Select the orders that should be included in this prescription.')}
172
+ </p>
173
+ {renderOrdersSection()}
174
+ </div>
175
+
176
+ <div className={styles.fieldGroup}>
177
+ <TextInput
178
+ id="insurance-policy"
179
+ labelText={t('insurancePolicyNumberLabel', 'Insurance Policy Number (optional)')}
180
+ placeholder={t('insurancePolicyNumberPlaceholder', 'Enter the policy number')}
181
+ value={policyNumber}
182
+ onChange={(event) => setPolicyNumber(event.target.value)}
183
+ />
184
+ </div>
185
+ <div className={styles.fieldGroup}>
186
+ <Checkbox
187
+ id="sign-prescription-checkbox"
188
+ labelText={t('signPrescriptionLabel', 'Digitally sign the prescription')}
189
+ checked={digitallySigned}
190
+ onChange={() => setDigitallySigned((prev) => !prev)}
191
+ />
192
+ <Checkbox
193
+ id="print-prescription-checkbox"
194
+ labelText={t('printPrescriptionLabel', 'Print the prescription')}
195
+ checked={printRequested}
196
+ onChange={() => setPrintRequested((prev) => !prev)}
197
+ />
198
+ </div>
199
+ <div className={styles.fieldGroup} style={{ flexDirection: 'row', gap: '1rem' }}>
200
+ <button
201
+ type="button"
202
+ id="discard-prescription-btn"
203
+ onClick={() => {
204
+ setSelectedLocation(null);
205
+ setPolicyNumber('');
206
+ setSelectedOrders(new Set());
207
+ setDigitallySigned(false);
208
+ setPrintRequested(false);
209
+ }}
210
+ disabled={isSubmitting}
211
+ >
212
+ {t('discardButtonLabel', 'Discard')}
213
+ </button>
214
+ <button
215
+ type="button"
216
+ id="create-prescription-btn"
217
+ onClick={async () => {
218
+ if (!patientUuid || !selectedLocation || selectedOrders.size === 0) return;
219
+ setIsSubmitting(true);
220
+ // TODO: obter dados reais do paciente e do médico
221
+ const payload: PrescriptionPayload = {
222
+ patient: {
223
+ uuid: patientUuid,
224
+ },
225
+ provider: {
226
+ uuid: 'provider-uuid', // Substituir pelo uuid real do médico
227
+ },
228
+ location: {
229
+ uuid: selectedLocation.uuid,
230
+ name: selectedLocation.display,
231
+ },
232
+ policyNumber,
233
+ orderUuids: Array.from(selectedOrders),
234
+ digitallySigned,
235
+ printRequested,
236
+ };
237
+ try {
238
+ await PrescriptionService.createPrescription(payload);
239
+ // Limpar formulário após sucesso
240
+ setSelectedLocation(null);
241
+ setPolicyNumber('');
242
+ setSelectedOrders(new Set());
243
+ setDigitallySigned(false);
244
+ setPrintRequested(false);
245
+ } catch (e) {
246
+ // TODO: tratar erro
247
+ } finally {
248
+ setIsSubmitting(false);
249
+ }
250
+ }}
251
+ disabled={isSubmitting}
252
+ >
253
+ {isSubmitting ? t('loading', 'Loading') : t('createPrescriptionButtonLabel', 'Create Prescription')}
254
+ </button>
255
+ </div>
256
+ </TabPanel>
257
+ <TabPanel className={styles.tabContent}>
258
+ <InlineNotification
259
+ lowContrast
260
+ kind="info"
261
+ title={t('inPastTabPlaceholderTitle', 'Historic prescriptions will be available soon')}
262
+ subtitle={t('inPastTabPlaceholderSubtitle', 'Switch back to the New tab to manage current orders.')}
263
+ />
264
+ </TabPanel>
265
+ </TabPanels>
266
+ </Tabs>
267
+ </section>
268
+ </div>
269
+ );
270
+ };
271
+
272
+ export default Root;
273
+
274
+ # src/prescriptions-actions/prescriptions-action-button.component.tsx
275
+ import React, { useCallback } from 'react';
276
+ import { OverflowMenuItem } from '@carbon/react';
277
+ import { launchWorkspace, navigate } from '@openmrs/esm-framework';
278
+ import { useTranslation } from 'react-i18next';
279
+
280
+ interface PrescriptionsActionButtonProps {
281
+ patientUuid?: string;
282
+ }
283
+
284
+ const resolvePatientUuid = (patientUuid?: string) =>
285
+ patientUuid ?? window.location.pathname.match(/\/patient\/([^/]+)\/chart/)?.[1];
286
+
287
+ const PrescriptionsActionButton: React.FC<PrescriptionsActionButtonProps> = ({ patientUuid }) => {
288
+ const { t } = useTranslation();
289
+ const label = t('prescriptionsActionButton', 'Active prescriptions');
290
+
291
+ const handleClick = useCallback(() => {
292
+ const uuid = resolvePatientUuid(patientUuid);
293
+ if (!uuid) return;
294
+
295
+ launchWorkspace('active-prescriptions-workspace', { patientUuid: uuid });
296
+ }, [patientUuid]);
297
+
298
+ return <OverflowMenuItem itemText={label} onClick={handleClick} />;
299
+ };
300
+
301
+ export default PrescriptionsActionButton;
302
+
303
+ # src/prescriptions-actions/prescriptions-action-menu-item.component.tsx
304
+ import React, { useCallback } from 'react';
305
+ import { CertificateCheck } from '@carbon/react/icons';
306
+ import { launchWorkspace, navigate } from '@openmrs/esm-framework';
307
+ import { navigateAndLaunchWorkspace } from '@openmrs/esm-framework';
308
+ import { useTranslation } from 'react-i18next';
309
+ import { ActionMenuButton } from '@openmrs/esm-styleguide';
310
+
311
+ interface PrescriptionsActionMenuItemProps {
312
+ patientUuid?: string;
313
+ }
314
+
315
+ const resolvePatientUuid = (patientUuid?: string) =>
316
+ patientUuid ?? window.location.pathname.match(/\/patient\/([^/]+)\/chart/)?.[1];
317
+
318
+ const PrescriptionsActionMenuItem: React.FC<PrescriptionsActionMenuItemProps> = ({ patientUuid }) => {
319
+ const { t } = useTranslation();
320
+
321
+ const handleClick = useCallback(() => {
322
+ const uuid = resolvePatientUuid(patientUuid);
323
+ if (!uuid) return;
324
+
325
+ navigateAndLaunchWorkspace({
326
+ targetUrl: `${window.getOpenmrsSpaBase()}patient/${uuid}/chart`,
327
+ workspaceName: 'active-prescriptions-workspace',
328
+ contextKey: `patient/${uuid}`,
329
+ additionalProps: { patientUuid: uuid },
330
+ });
331
+ }, [patientUuid]);
332
+
333
+ const label = t('prescriptionsActionMenuItem', 'Active prescriptions');
334
+
335
+ return (
336
+ <ActionMenuButton
337
+ getIcon={(props) => <CertificateCheck {...props} aria-hidden="true" />}
338
+ handler={handleClick}
339
+ iconDescription={label}
340
+ label={label}
341
+ type="active-prescriptions"
342
+ />
343
+ );
344
+ };
345
+
346
+ export default PrescriptionsActionMenuItem;
347
+
348
+
349
+ # src/prescriptions.service.ts
350
+ // prescription.service.ts
351
+ // Serviço para manipulação de prescrições e integração com backend
352
+
353
+ export interface PrescriptionPayload {
354
+ patient: {
355
+ uuid: string;
356
+ name?: string;
357
+ birthDate?: string;
358
+ gender?: string;
359
+ };
360
+ provider: {
361
+ uuid: string;
362
+ name?: string;
363
+ };
364
+ location: {
365
+ uuid: string;
366
+ name?: string;
367
+ };
368
+ policyNumber?: string;
369
+ orderUuids: string[];
370
+ digitallySigned: boolean;
371
+ printRequested: boolean;
372
+ }
373
+
374
+ export class PrescriptionService {
375
+ static async createPrescription(payload: PrescriptionPayload): Promise<Response> {
376
+ // Troque a URL abaixo pelo endpoint real do backend
377
+ const url = '/api/prescriptions';
378
+ return fetch(url, {
379
+ method: 'POST',
380
+ headers: {
381
+ 'Content-Type': 'application/json',
382
+ },
383
+ body: JSON.stringify(payload),
384
+ });
385
+ }
386
+ }
387
+
388
+
389
+ # src/index.ts
390
+ /**
391
+ * This is the entrypoint file of the application. It communicates the
392
+ * important features of this microfrontend to the app shell. It
393
+ * connects the app shell to the React application(s) that make up this
394
+ * microfrontend.
395
+ */
396
+ import { getAsyncLifecycle, defineConfigSchema } from '@openmrs/esm-framework';
397
+ import { configSchema } from './config-schema';
398
+
399
+ const moduleName = '@cc-openmrs/cc-esm-active-prescriptions';
400
+
401
+ const options = {
402
+ featureName: 'root-world',
403
+ moduleName,
404
+ };
405
+
406
+ /**
407
+ * This tells the app shell how to obtain translation files: that they
408
+ * are JSON files in the directory `../translations` (which you should
409
+ * see in the directory structure).
410
+ */
411
+ export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
412
+
413
+ /**
414
+ * This function performs any setup that should happen at microfrontend
415
+ * load-time (such as defining the config schema) and then returns an
416
+ * object which describes how the React application(s) should be
417
+ * rendered.
418
+ */
419
+ export function startupApp() {
420
+ defineConfigSchema(moduleName, configSchema);
421
+ }
422
+
423
+ /**
424
+ * This named export tells the app shell that the default export of `root.component.tsx`
425
+ * should be rendered when the route matches `root`. The full route
426
+ * will be `openmrsSpaBase() + 'root'`, which is usually
427
+ * `/openmrs/spa/root`.
428
+ */
429
+ export const root = getAsyncLifecycle(() => import('./root.component'), options);
430
+
431
+ /**
432
+ * The following are named exports for the extensions defined in this frontend modules. See the `routes.json` file to see how these are used.
433
+ */
434
+ export const redBox = getAsyncLifecycle(() => import('./boxes/extensions/red-box.component'), options);
435
+
436
+ export const blueBox = getAsyncLifecycle(() => import('./boxes/extensions/blue-box.component'), options);
437
+
438
+ export const brandBox = getAsyncLifecycle(() => import('./boxes/extensions/brand-box.component'), options);
439
+
440
+ export const prescriptionsActionButton = getAsyncLifecycle(
441
+ () => import('./prescriptions-actions/prescriptions-action-button.component'),
442
+ options,
443
+ );
444
+
445
+ export const prescriptionsActionMenuItem = getAsyncLifecycle(
446
+ () => import('./prescriptions-actions/prescriptions-action-menu-item.component'),
447
+ options,
448
+ );
449
+
450
+
451
+ # package.json
452
+ {
453
+ "name": "@cc-openmrs/cc-esm-active-prescriptions",
454
+ "version": "1.0.21",
455
+ "license": "MPL-2.0",
456
+ "description": "An OpenMRS seed application for building microfrontends",
457
+ "browser": "dist/openmrs-esm-template-app.js",
458
+ "main": "src/index.ts",
459
+ "source": true,
460
+ "scripts": {
461
+ "start": "openmrs develop",
462
+ "serve": "webpack serve --mode=development",
463
+ "build": "webpack --mode production",
464
+ "analyze": "webpack --mode=production --env analyze=true",
465
+ "lint": "eslint src --ext js,jsx,ts,tsx --max-warnings 0",
466
+ "prettier": "prettier --write \"src/**/*.{ts,tsx}\" --list-different",
467
+ "typescript": "tsc",
468
+ "test": "jest --config jest.config.js --passWithNoTests",
469
+ "verify": "turbo lint typescript coverage",
470
+ "coverage": "yarn test --coverage",
471
+ "prepare": "husky install",
472
+ "extract-translations": "i18next 'src/**/*.component.tsx' --config ./tools/i18next-parser.config.js",
473
+ "test-e2e": "playwright test"
474
+ },
475
+ "browserslist": [
476
+ "extends browserslist-config-openmrs"
477
+ ],
478
+ "keywords": [
479
+ "openmrs",
480
+ "microfrontends"
481
+ ],
482
+ "repository": {
483
+ "type": "git",
484
+ "url": "git+https://github.com/openmrs/openmrs-esm-template-app.git"
485
+ },
486
+ "homepage": "https://github.com/openmrs/openmrs-esm-template-app#readme",
487
+ "publishConfig": {
488
+ "access": "public"
489
+ },
490
+ "bugs": {
491
+ "url": "https://github.com/openmrs/openmrs-esm-template-app/issues"
492
+ },
493
+ "openmrs": {
494
+ "frontendModuleName": "@cc-openmrs/cc-esm-active-prescriptions",
495
+ "routes": "src/routes.json"
496
+ },
497
+ "dependencies": {
498
+ "@carbon/react": "^1.83.0",
499
+ "lodash-es": "^4.17.21"
500
+ },
501
+ "peerDependencies": {
502
+ "@openmrs/esm-framework": "*",
503
+ "dayjs": "1.x",
504
+ "react": "18.x",
505
+ "react-i18next": "16.x",
506
+ "react-router-dom": "6.x",
507
+ "rxjs": "6.x"
508
+ },
509
+ "devDependencies": {
510
+ "@openmrs/esm-framework": "8.0.0",
511
+ "@openmrs/esm-styleguide": "8.0.0",
512
+ "@playwright/test": "^1.52.0",
513
+ "@swc/cli": "^0.3.12",
514
+ "@swc/core": "^1.3.68",
515
+ "@swc/jest": "^0.2.36",
516
+ "@testing-library/dom": "^10.1.0",
517
+ "@testing-library/jest-dom": "^6.4.5",
518
+ "@testing-library/react": "^15.0.6",
519
+ "@testing-library/user-event": "^14.5.2",
520
+ "@types/jest": "^29.5.12",
521
+ "@types/react": "^18.3.21",
522
+ "@types/react-dom": "^18.3.0",
523
+ "@types/react-router": "^5.1.20",
524
+ "@types/react-router-dom": "^5.3.3",
525
+ "@types/webpack-env": "^1.18.1",
526
+ "@typescript-eslint/eslint-plugin": "^7.8.0",
527
+ "@typescript-eslint/parser": "^7.8.0",
528
+ "css-loader": "^6.8.1",
529
+ "dayjs": "^1.11.13",
530
+ "dotenv": "^16.0.3",
531
+ "eslint": "^8.50.0",
532
+ "eslint-config-prettier": "^8.8.0",
533
+ "eslint-plugin-jest-dom": "^5.4.0",
534
+ "eslint-plugin-prettier": "^5.1.3",
535
+ "eslint-plugin-react-hooks": "^4.6.2",
536
+ "husky": "^8.0.3",
537
+ "i18next": "^25.0.0",
538
+ "i18next-parser": "^9.3.0",
539
+ "identity-obj-proxy": "^3.0.0",
540
+ "jest": "^29.7.0",
541
+ "jest-cli": "^29.7.0",
542
+ "jest-environment-jsdom": "^29.7.0",
543
+ "lint-staged": "^15.2.2",
544
+ "openmrs": "8.0.0",
545
+ "prettier": "^3.3.3",
546
+ "react": "^18.3.1",
547
+ "react-dom": "^18.3.1",
548
+ "react-i18next": "^16.0.0",
549
+ "react-router-dom": "^6.14.1",
550
+ "rxjs": "^6.6.7",
551
+ "swc-loader": "^0.2.3",
552
+ "turbo": "^2.5.2",
553
+ "typescript": "^5.0.0",
554
+ "webpack": "^5.99.9",
555
+ "webpack-cli": "^6.0.1"
556
+ },
557
+ "lint-staged": {
558
+ "packages/**/src/**/*.{ts,tsx}": "eslint --cache --fix --max-warnings 0",
559
+ "*.{css,scss,ts,tsx}": "prettier --write --list-different"
560
+ },
561
+ "packageManager": "yarn@4.10.3"
562
+ }
563
+
564
+
565
+ # src/routes.json
566
+ {
567
+ "$schema": "https://json.openmrs.org/routes.schema.json",
568
+ "backendDependencies": {
569
+ "fhir2": ">=1.2",
570
+ "webservices.rest": ">=2.2.0"
571
+ },
572
+ "extensions": [
573
+ {
574
+ "name": "Red box",
575
+ "component": "redBox",
576
+ "slot": "Boxes"
577
+ },
578
+ {
579
+ "name": "Blue box",
580
+ "component": "blueBox",
581
+ "slot": "Boxes"
582
+ },
583
+ {
584
+ "name": "Brand box",
585
+ "component": "brandBox",
586
+ "slot": "Boxes"
587
+ },
588
+ {
589
+ "name": "prescriptions-action-menu",
590
+ "component": "prescriptionsActionMenuItem",
591
+ "slot": "action-menu-patient-chart-items-slot",
592
+ "order": 4,
593
+ "meta": {
594
+ "label": "Prescriptions 2",
595
+ "icon": "pill"
596
+ }
597
+ },
598
+ {
599
+ "name": "Prescriptions action",
600
+ "component": "prescriptionsActionButton",
601
+ "slot": "patient-actions-slot",
602
+ "order": 50
603
+ }
604
+ ],
605
+ "pages": [
606
+ {
607
+ "component": "root",
608
+ "route": "root"
609
+ }
610
+ ],
611
+ "workspaces": [
612
+ {
613
+ "name": "active-prescriptions-workspace",
614
+ "title": "activePrescriptionsWorkspaceTitle",
615
+ "component": "root",
616
+ "type": "active-prescriptions",
617
+ "canHide": true,
618
+ "width": "wider"
619
+ }
620
+ ],
621
+ "workspaces2": [
622
+ {
623
+ "name": "active-prescriptions-workspace",
624
+ "component": "root",
625
+ "window": "patient-chart-active-prescriptions"
626
+ }
627
+ ],
628
+ "workspaceWindows2": [
629
+ {
630
+ "name": "patient-chart-active-prescriptions",
631
+ "group": "patient-chart",
632
+ "icon": "prescriptionsActionMenuItem",
633
+ "order": 4,
634
+ "width": "wider",
635
+ "canMaximize": true
636
+ }
637
+ ]
638
+ }
639
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cc-openmrs/cc-esm-active-prescriptions",
3
- "version": "1.0.21",
3
+ "version": "1.0.23",
4
4
  "license": "MPL-2.0",
5
5
  "description": "An OpenMRS seed application for building microfrontends",
6
6
  "browser": "dist/openmrs-esm-template-app.js",
@@ -1,6 +1,6 @@
1
1
  import React, { useCallback } from 'react';
2
2
  import { OverflowMenuItem } from '@carbon/react';
3
- import { launchWorkspace, navigate } from '@openmrs/esm-framework';
3
+ import { launchWorkspace } from '@openmrs/esm-framework';
4
4
  import { useTranslation } from 'react-i18next';
5
5
 
6
6
  interface PrescriptionsActionButtonProps {
@@ -1,7 +1,6 @@
1
1
  import React, { useCallback } from 'react';
2
2
  import { CertificateCheck } from '@carbon/react/icons';
3
- import { launchWorkspace, navigate } from '@openmrs/esm-framework';
4
- import { navigateAndLaunchWorkspace } from '@openmrs/esm-framework';
3
+ import { launchWorkspace } from '@openmrs/esm-framework';
5
4
  import { useTranslation } from 'react-i18next';
6
5
  import { ActionMenuButton } from '@openmrs/esm-styleguide';
7
6
 
@@ -19,12 +18,7 @@ const PrescriptionsActionMenuItem: React.FC<PrescriptionsActionMenuItemProps> =
19
18
  const uuid = resolvePatientUuid(patientUuid);
20
19
  if (!uuid) return;
21
20
 
22
- navigateAndLaunchWorkspace({
23
- targetUrl: `${window.getOpenmrsSpaBase()}patient/${uuid}/chart`,
24
- workspaceName: 'active-prescriptions-workspace',
25
- contextKey: `patient/${uuid}`,
26
- additionalProps: { patientUuid: uuid },
27
- });
21
+ launchWorkspace('active-prescriptions-workspace', { patientUuid: uuid });
28
22
  }, [patientUuid]);
29
23
 
30
24
  const label = t('prescriptionsActionMenuItem', 'Active prescriptions');
package/src/routes.json CHANGED
@@ -21,19 +21,19 @@
21
21
  "slot": "Boxes"
22
22
  },
23
23
  {
24
- "name": "prescriptions-action-menu",
24
+ "name": "prescriptions-siderail",
25
25
  "component": "prescriptionsActionMenuItem",
26
- "slot": "action-menu-patient-chart-items-slot",
26
+ "slot": "patient-chart-siderail-slot",
27
27
  "order": 4,
28
28
  "meta": {
29
- "label": "Prescriptions 2",
29
+ "label": "Prescriptions",
30
30
  "icon": "pill"
31
31
  }
32
32
  },
33
33
  {
34
- "name": "Prescriptions action",
34
+ "name": "prescriptions-action",
35
35
  "component": "prescriptionsActionButton",
36
- "slot": "patient-actions-slot",
36
+ "slot": "action-menu-patient-chart-items-slot",
37
37
  "order": 50
38
38
  }
39
39
  ],
@@ -46,28 +46,11 @@
46
46
  "workspaces": [
47
47
  {
48
48
  "name": "active-prescriptions-workspace",
49
- "title": "activePrescriptionsWorkspaceTitle",
49
+ "title": "Active prescriptions",
50
50
  "component": "root",
51
- "type": "active-prescriptions",
51
+ "group": "patient-chart",
52
52
  "canHide": true,
53
53
  "width": "wider"
54
54
  }
55
- ],
56
- "workspaces2": [
57
- {
58
- "name": "active-prescriptions-workspace",
59
- "component": "root",
60
- "window": "patient-chart-active-prescriptions"
61
- }
62
- ],
63
- "workspaceWindows2": [
64
- {
65
- "name": "patient-chart-active-prescriptions",
66
- "group": "patient-chart",
67
- "icon": "prescriptionsActionMenuItem",
68
- "order": 4,
69
- "width": "wider",
70
- "canMaximize": true
71
- }
72
55
  ]
73
56
  }