@cc-openmrs/cc-esm-active-prescriptions 1.0.65 → 1.0.68

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.
Files changed (66) hide show
  1. package/dist/116.js +1 -0
  2. package/dist/184.js +2 -1
  3. package/dist/{311.js.LICENSE.txt → 184.js.LICENSE.txt} +0 -37
  4. package/dist/197.js +1 -1
  5. package/dist/225.js +1 -1
  6. package/dist/282.js +2 -0
  7. package/dist/282.js.LICENSE.txt +32 -0
  8. package/dist/300.js +1 -1
  9. package/dist/311.js +1 -2
  10. package/dist/319.js +1 -1
  11. package/dist/335.js +1 -1
  12. package/dist/353.js +1 -0
  13. package/dist/41.js +1 -1
  14. package/dist/422.js +2 -1
  15. package/dist/464.js +2 -0
  16. package/dist/464.js.LICENSE.txt +19 -0
  17. package/dist/478.js +2 -0
  18. package/dist/499.js +1 -0
  19. package/dist/537.js +1 -1
  20. package/dist/540.js +1 -1
  21. package/dist/55.js +1 -1
  22. package/dist/606.js +2 -1
  23. package/dist/{618.js.LICENSE.txt → 606.js.LICENSE.txt} +2 -2
  24. package/dist/63.js +1 -0
  25. package/dist/652.js +1 -1
  26. package/dist/679.js +1 -1
  27. package/dist/779.js +2 -0
  28. package/dist/{31.js.LICENSE.txt → 779.js.LICENSE.txt} +11 -0
  29. package/dist/961.js +1 -1
  30. package/dist/966.js +1 -1
  31. package/dist/99.js +1 -1
  32. package/dist/993.js +1 -0
  33. package/dist/main.js +1 -2
  34. package/dist/openmrs-esm-patient-lists-app.js +1 -0
  35. package/dist/{openmrs-esm-template-app.js.buildmanifest.json → openmrs-esm-patient-lists-app.js.buildmanifest.json} +179 -248
  36. package/dist/routes.json +1 -1
  37. package/package.json +61 -51
  38. package/src/declarations.d.ts +4 -5
  39. package/src/prescriptions-actions/add-drug-prescription/add-drug-prescription.component.tsx +186 -0
  40. package/src/prescriptions-actions/add-drug-prescription/add-drug-prescription.scss +1 -0
  41. package/src/prescriptions-actions/add-drug-prescription/index.ts +2 -0
  42. package/src/prescriptions-actions/prescriptions-action-button.component.tsx +7 -13
  43. package/src/prescriptions-actions/prescriptions-action-menu-item.component.tsx +29 -0
  44. package/src/prescriptions-actions/prescriptions-action-menu-item.scss +12 -0
  45. package/src/root.component.tsx +224 -296
  46. package/src/routes.json +12 -11
  47. package/dist/177.js +0 -2
  48. package/dist/237.js +0 -2
  49. package/dist/237.js.LICENSE.txt +0 -9
  50. package/dist/31.js +0 -2
  51. package/dist/437.js +0 -1
  52. package/dist/533.js +0 -1
  53. package/dist/618.js +0 -2
  54. package/dist/635.js +0 -1
  55. package/dist/647.js +0 -2
  56. package/dist/647.js.LICENSE.txt +0 -9
  57. package/dist/692.js +0 -2
  58. package/dist/761.js +0 -2
  59. package/dist/761.js.LICENSE.txt +0 -29
  60. package/dist/875.js +0 -2
  61. package/dist/875.js.LICENSE.txt +0 -5
  62. package/dist/main.js.LICENSE.txt +0 -9
  63. package/dist/openmrs-esm-template-app.js +0 -1
  64. package/src/carbon-react.d.ts +0 -9
  65. /package/dist/{177.js.LICENSE.txt → 422.js.LICENSE.txt} +0 -0
  66. /package/dist/{692.js.LICENSE.txt → 478.js.LICENSE.txt} +0 -0
@@ -9,10 +9,10 @@
9
9
  */
10
10
 
11
11
  import React, { useMemo, useState } from 'react';
12
- import { PrescriptionService, PrescriptionPayload } from './prescriptions.service';
13
- import useSWR from 'swr';
14
- import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
12
+ import { PrescriptionService } from './prescriptions.service';
13
+ import { Workspace2 } from '@openmrs/esm-framework';
15
14
  import {
15
+ Button,
16
16
  Checkbox,
17
17
  ComboBox,
18
18
  InlineLoading,
@@ -22,176 +22,23 @@ import {
22
22
  TabPanel,
23
23
  TabPanels,
24
24
  Tabs,
25
+ TextArea,
25
26
  TextInput,
26
27
  } from '@carbon/react';
27
28
  import { useTranslation } from 'react-i18next';
28
29
  import { useLocations, type Location } from '@openmrs/esm-framework';
29
- import { usePatientOrders } from './prescriptions-orders/usePatientOrders';
30
30
  import styles from './root.scss';
31
+ import AddDrugPrescription, { type PrescriptionDrugItem } from './prescriptions-actions/add-drug-prescription/add-drug-prescription.component';
31
32
 
32
33
  interface RootProps {
33
34
  patientUuid?: string;
34
- patient?: { uuid: string }; // Alguns workspaces passam assim
35
- }
36
-
37
- // Hook para buscar ConceptDrugs do OpenMRS
38
- function useConceptDrugs(query: string) {
39
- const url = query ? `${restBaseUrl}/drug?q=${encodeURIComponent(query)}&v=custom:(uuid,name,display)` : null;
40
- const swr = useSWR<any>(url, openmrsFetch);
41
- return {
42
- drugs: swr.data?.data?.results || [],
43
- error: swr.error,
44
- isLoading: swr.isValidating,
35
+ patient?: {
36
+ name?: Array<{ given?: string[]; family?: string }>;
37
+ birthDate?: string;
38
+ gender?: string;
45
39
  };
46
40
  }
47
41
 
48
- // Tipo para dados do medicamento
49
- type MedicationFormData = {
50
- drugName: string;
51
- dosage: string;
52
- unit: string;
53
- route: string;
54
- frequency: string;
55
- patientInstructions?: string;
56
- asNeeded?: boolean;
57
- asNeededCondition?: string;
58
- startDate?: string;
59
- duration?: string;
60
- durationUnit?: string;
61
- indication?: string;
62
- };
63
-
64
- const MOCK_UNITS = [{ value: 'mg' }, { value: 'ml' }, { value: 'g' }];
65
- const MOCK_FREQUENCIES = [{ value: '1x ao dia' }, { value: '2x ao dia' }, { value: '8/8h' }, { value: '12/12h' }];
66
-
67
- type MedicationFormProps = {
68
- initialData?: MedicationFormData;
69
- onSubmit: (data: MedicationFormData) => void;
70
- onCancel: () => void;
71
- t: any;
72
- };
73
-
74
- const MedicationForm: React.FC<MedicationFormProps> = ({ initialData, onSubmit, onCancel, t }) => {
75
- const [drugName, setDrugName] = useState(initialData?.drugName || '');
76
- const [dosage, setDosage] = useState(initialData?.dosage || '');
77
- const [unit, setUnit] = useState(initialData?.unit || '');
78
- const [route, setRoute] = useState(initialData?.route || '');
79
- const [frequency, setFrequency] = useState(initialData?.frequency || '');
80
- const [patientInstructions, setPatientInstructions] = useState(initialData?.patientInstructions || '');
81
- const [asNeeded, setAsNeeded] = useState(initialData?.asNeeded || false);
82
- const [asNeededCondition, setAsNeededCondition] = useState(initialData?.asNeededCondition || '');
83
- const [indication, setIndication] = useState(initialData?.indication || '');
84
-
85
- // Busca ConceptDrugs conforme digitação
86
- const { drugs, isLoading } = useConceptDrugs(drugName);
87
- const drugOptions = drugs.map((d: any) => ({ value: d.name, uuid: d.uuid, display: d.display }));
88
-
89
- const handleDrugInput = (input: string) => {
90
- setDrugName(input);
91
- };
92
-
93
- const handleSubmit = (e: React.FormEvent) => {
94
- e.preventDefault();
95
- if (!drugName || !dosage || !unit || !frequency) return;
96
- onSubmit({
97
- drugName,
98
- dosage,
99
- unit,
100
- route,
101
- frequency,
102
- patientInstructions,
103
- asNeeded,
104
- asNeededCondition,
105
- indication,
106
- });
107
- };
108
-
109
- return (
110
- <form onSubmit={handleSubmit} style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
111
- <ComboBox
112
- id="drug-name"
113
- items={drugOptions}
114
- itemToString={(item) => item?.value || ''}
115
- selectedItem={drugOptions.find((d: any) => d.value === drugName) || null}
116
- onInputChange={handleDrugInput}
117
- onChange={({ selectedItem }) => setDrugName(selectedItem?.value || '')}
118
- placeholder={t('drugName', 'Nome do medicamento')}
119
- titleText={t('drugName', 'Nome do medicamento')}
120
- required
121
- disabled={isLoading}
122
- />
123
- <TextInput
124
- id="dosage"
125
- labelText={t('dosage', 'Dose')}
126
- value={dosage}
127
- onChange={(e) => setDosage(e.target.value)}
128
- required
129
- />
130
- <ComboBox
131
- id="unit"
132
- items={MOCK_UNITS}
133
- itemToString={(item) => item?.value || ''}
134
- selectedItem={MOCK_UNITS.find((u) => u.value === unit) || null}
135
- onChange={({ selectedItem }) => setUnit(selectedItem?.value || '')}
136
- placeholder={t('unit', 'Unidade')}
137
- titleText={t('unit', 'Unidade')}
138
- required
139
- />
140
- <TextInput
141
- id="route"
142
- labelText={t('route', 'Via de administração')}
143
- value={route}
144
- onChange={(e) => setRoute(e.target.value)}
145
- />
146
- <ComboBox
147
- id="frequency"
148
- items={MOCK_FREQUENCIES}
149
- itemToString={(item) => item?.value || ''}
150
- selectedItem={MOCK_FREQUENCIES.find((f) => f.value === frequency) || null}
151
- onChange={({ selectedItem }) => setFrequency(selectedItem?.value || '')}
152
- placeholder={t('frequency', 'Frequência')}
153
- titleText={t('frequency', 'Frequência')}
154
- required
155
- />
156
- <TextInput
157
- id="patient-instructions"
158
- labelText={t('patientInstructions', 'Instruções ao paciente')}
159
- value={patientInstructions}
160
- onChange={(e) => setPatientInstructions(e.target.value)}
161
- />
162
- <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
163
- <Checkbox
164
- id="as-needed"
165
- labelText={t('asNeeded', 'Tomar se necessário (P.R.N.)')}
166
- checked={asNeeded}
167
- onChange={() => setAsNeeded((prev) => !prev)}
168
- />
169
- <TextInput
170
- id="as-needed-condition"
171
- labelText={t('asNeededCondition', 'Motivo P.R.N.')}
172
- value={asNeededCondition}
173
- onChange={(e) => setAsNeededCondition(e.target.value)}
174
- disabled={!asNeeded}
175
- />
176
- </div>
177
- <TextInput
178
- id="indication"
179
- labelText={t('indication', 'Indicação')}
180
- value={indication}
181
- onChange={(e) => setIndication(e.target.value)}
182
- />
183
- <div style={{ display: 'flex', gap: 8, marginTop: 8 }}>
184
- <button type="submit" style={{ flex: 1 }}>
185
- {t('saveMedication', 'Salvar medicamento')}
186
- </button>
187
- <button type="button" onClick={onCancel} style={{ flex: 1 }}>
188
- {t('cancel', 'Cancelar')}
189
- </button>
190
- </div>
191
- </form>
192
- );
193
- };
194
-
195
42
  const Root: React.FC<RootProps> = ({ patientUuid, patient }) => {
196
43
  const { t } = useTranslation();
197
44
  const [activeTab, setActiveTab] = useState<'new' | 'past'>('new');
@@ -199,168 +46,249 @@ const Root: React.FC<RootProps> = ({ patientUuid, patient }) => {
199
46
  const [policyNumber, setPolicyNumber] = useState('');
200
47
  const [digitallySigned, setDigitallySigned] = useState(false);
201
48
  const [printRequested, setPrintRequested] = useState(false);
49
+ const [observations, setObservations] = useState('');
202
50
  const [isSubmitting, setIsSubmitting] = useState(false);
203
- const [medications, setMedications] = useState<MedicationFormData[]>([]);
51
+ const [submitError, setSubmitError] = useState<string | null>(null);
52
+
53
+ // Lista de medicamentos da prescrição — escopo da prescrição
54
+ const [prescriptionOrders, setPrescriptionOrders] = useState<PrescriptionDrugItem[]>([]);
204
55
  const [showAddMedication, setShowAddMedication] = useState(false);
205
56
  const [editIndex, setEditIndex] = useState<number | null>(null);
206
57
 
207
58
  const locations = useLocations();
208
59
  const locationItems = useMemo(() => locations ?? [], [locations]);
209
60
 
210
- // Adiciona novo medicamento
211
- const handleAddMedication = (med: MedicationFormData) => {
212
- setMedications((prev) => [...prev, med]);
213
- setShowAddMedication(false);
214
- };
215
-
216
- // Edita medicamento existente
217
- const handleEditMedication = (med: MedicationFormData) => {
61
+ const handleSaveMedication = (item: PrescriptionDrugItem) => {
218
62
  if (editIndex !== null) {
219
- setMedications((prev) => prev.map((m, idx) => (idx === editIndex ? med : m)));
63
+ setPrescriptionOrders((prev) => prev.map((m, idx) => (idx === editIndex ? item : m)));
220
64
  setEditIndex(null);
221
- setShowAddMedication(false);
65
+ } else {
66
+ setPrescriptionOrders((prev) => [...prev, item]);
222
67
  }
68
+ setShowAddMedication(false);
223
69
  };
224
70
 
225
- // Remove medicamento
226
71
  const handleRemoveMedication = (idx: number) => {
227
- setMedications((prev) => prev.filter((_, i) => i !== idx));
228
- };
229
-
230
- // Abre tela de adicionar
231
- const openAddMedication = () => {
232
- setEditIndex(null);
233
- setShowAddMedication(true);
72
+ setPrescriptionOrders((prev) => prev.filter((_, i) => i !== idx));
234
73
  };
235
74
 
236
- // Abre tela de editar
237
75
  const openEditMedication = (idx: number) => {
238
76
  setEditIndex(idx);
239
77
  setShowAddMedication(true);
240
78
  };
241
79
 
80
+ const handleSubmitPrescription = async () => {
81
+ if (!patientUuid || !selectedLocation || prescriptionOrders.length === 0) return;
82
+ setIsSubmitting(true);
83
+ setSubmitError(null);
84
+ try {
85
+ const payload = {
86
+ patientUuid,
87
+ patientName: patient?.name?.[0]
88
+ ? `${patient.name[0].given?.join(' ')} ${patient.name[0].family}`.trim()
89
+ : '',
90
+ patientBirthDate: patient?.birthDate ?? '',
91
+ patientGender: patient?.gender ?? '',
92
+ providerUuid: '',
93
+ providerName: '',
94
+ locationUuid: selectedLocation.uuid,
95
+ locationName: selectedLocation.display,
96
+ policyNumber,
97
+ orderUuids: prescriptionOrders.map((o) => o.drug.uuid),
98
+ digitallySigned,
99
+ printRequested,
100
+ observations,
101
+ };
102
+ await PrescriptionService.createPrescription(payload as any);
103
+ // Limpar após sucesso
104
+ setPrescriptionOrders([]);
105
+ setSelectedLocation(null);
106
+ setPolicyNumber('');
107
+ setDigitallySigned(false);
108
+ setPrintRequested(false);
109
+ setObservations('');
110
+ } catch (e: any) {
111
+ setSubmitError(e?.message ?? t('submitError', 'Erro ao salvar a prescrição.'));
112
+ } finally {
113
+ setIsSubmitting(false);
114
+ }
115
+ };
116
+
242
117
  return (
243
- <div className={styles.workspace}>
244
- <div className={styles.header}>
245
- <h2>{t('activePrescriptionsWorkspaceTitle', 'Prescrições ativas')}</h2>
246
- <p className={styles.subtitle}>
247
- {t(
248
- 'activePrescriptionsHelper',
249
- 'Revise o contexto do paciente, escolha os detalhes da visita e adicione os medicamentos à prescrição.',
250
- )}
251
- </p>
252
- </div>
118
+ <Workspace2 title={t('activePrescriptionsWorkspaceTitle', 'Prescrições ativas')} hasUnsavedChanges={false}>
119
+ <div className={styles.workspace}>
120
+ <div className={styles.header}>
121
+ <h2>{t('activePrescriptionsWorkspaceTitle', 'Prescrições ativas')}</h2>
122
+ <p className={styles.subtitle}>
123
+ {t(
124
+ 'activePrescriptionsHelper',
125
+ 'Revise o contexto do paciente, escolha os detalhes da visita e adicione os medicamentos à prescrição.',
126
+ )}
127
+ </p>
128
+ </div>
253
129
 
254
- <section className={styles.section}>
255
- <p className={styles.fieldLabel}>{t('visitTimingLabel', 'A visita é')}</p>
256
- <Tabs
257
- selectedIndex={activeTab === 'new' ? 0 : 1}
258
- onChange={({ selectedIndex }) => setActiveTab(selectedIndex === 0 ? 'new' : 'past')}
259
- >
260
- <TabList aria-label={t('visitTimingLabel', 'A visita é')}>
261
- <Tab>{t('visitTimingNew', 'Nova')}</Tab>
262
- <Tab>{t('visitTimingPast', 'No passado')}</Tab>
263
- </TabList>
264
- <TabPanels>
265
- <TabPanel className={styles.tabContent}>
266
- <div className={styles.fieldGroup}>
267
- <ComboBox
268
- id="visit-location"
269
- items={locationItems}
270
- itemToString={(item) => item?.display ?? ''}
271
- selectedItem={selectedLocation}
272
- onChange={({ selectedItem }) => setSelectedLocation((selectedItem as Location) ?? null)}
273
- placeholder={t('visitLocationPlaceholder', 'Selecione o local')}
274
- titleText={t('visitLocationHeading', 'Local da visita')}
275
- helperText={t('visitLocationHelper', 'Selecione onde a visita ocorrerá.')}
276
- />
277
- </div>
130
+ <section className={styles.section}>
131
+ <p className={styles.fieldLabel}>{t('visitTimingLabel', 'A visita é')}</p>
132
+ <Tabs
133
+ selectedIndex={activeTab === 'new' ? 0 : 1}
134
+ onChange={({ selectedIndex }) => setActiveTab(selectedIndex === 0 ? 'new' : 'past')}
135
+ >
136
+ <TabList aria-label={t('visitTimingLabel', 'A visita é')}>
137
+ <Tab>{t('visitTimingNew', 'Nova')}</Tab>
138
+ <Tab>{t('visitTimingPast', 'No passado')}</Tab>
139
+ </TabList>
140
+ <TabPanels>
141
+ <TabPanel className={styles.tabContent}>
142
+ {/* Local da visita */}
143
+ <div className={styles.fieldGroup}>
144
+ <ComboBox
145
+ id="visit-location"
146
+ items={locationItems}
147
+ itemToString={(item) => item?.display ?? ''}
148
+ selectedItem={selectedLocation}
149
+ onChange={({ selectedItem }) => setSelectedLocation((selectedItem as Location) ?? null)}
150
+ placeholder={t('visitLocationPlaceholder', 'Selecione o local')}
151
+ titleText={t('visitLocationHeading', 'Local da visita')}
152
+ helperText={t('visitLocationHelper', 'Selecione onde a visita ocorrerá.')}
153
+ />
154
+ </div>
278
155
 
279
- <div className={styles.fieldGroup}>
280
- <TextInput
281
- id="insurance-policy"
282
- labelText={t('insurancePolicyNumberLabel', 'Número da apólice (opcional)')}
283
- placeholder={t('insurancePolicyNumberPlaceholder', 'Digite o número da apólice')}
284
- value={policyNumber}
285
- onChange={(event) => setPolicyNumber(event.target.value)}
286
- />
287
- </div>
288
- <div className={styles.fieldGroup}>
289
- <Checkbox
290
- id="sign-prescription-checkbox"
291
- labelText={t('signPrescriptionLabel', 'Assinar digitalmente a prescrição')}
292
- checked={digitallySigned}
293
- onChange={() => setDigitallySigned((prev) => !prev)}
294
- />
295
- <Checkbox
296
- id="print-prescription-checkbox"
297
- labelText={t('printPrescriptionLabel', 'Imprimir a prescrição')}
298
- checked={printRequested}
299
- onChange={() => setPrintRequested((prev) => !prev)}
300
- />
301
- </div>
156
+ {/* Número da apólice */}
157
+ <div className={styles.fieldGroup}>
158
+ <TextInput
159
+ id="insurance-policy"
160
+ labelText={t('insurancePolicyNumberLabel', 'Número da apólice (opcional)')}
161
+ placeholder={t('insurancePolicyNumberPlaceholder', 'Digite o número da apólice')}
162
+ value={policyNumber}
163
+ onChange={(event) => setPolicyNumber(event.target.value)}
164
+ />
165
+ </div>
302
166
 
303
- {/* Lista de medicamentos adicionados */}
304
- <div className={styles.fieldGroup}>
305
- <h3>{t('medicationsList', 'Medicamentos adicionados')}</h3>
306
- {medications.length === 0 ? (
307
- <p>{t('noMedicationsAdded', 'Nenhum medicamento adicionado ainda.')}</p>
308
- ) : (
309
- <ul className={styles.medicationsList}>
310
- {medications.map((med, idx) => (
311
- <li key={idx} className={styles.medicationItem}>
312
- <span>
313
- <b>{med.drugName}</b> - {med.dosage} {med.unit} - {med.frequency}
314
- </span>
315
- <button type="button" onClick={() => openEditMedication(idx)}>
316
- {t('edit', 'Editar')}
317
- </button>
318
- <button type="button" onClick={() => handleRemoveMedication(idx)}>
319
- {t('remove', 'Remover')}
320
- </button>
321
- </li>
322
- ))}
323
- </ul>
324
- )}
325
- <button type="button" onClick={openAddMedication} className={styles.addMedicationBtn}>
326
- {t('addMedication', '+ Adicionar medicamento')}
327
- </button>
328
- </div>
167
+ {/* Assinatura digital e impressão */}
168
+ <div className={styles.fieldGroup}>
169
+ <Checkbox
170
+ id="sign-prescription-checkbox"
171
+ labelText={t('signPrescriptionLabel', 'Assinar digitalmente a prescrição')}
172
+ checked={digitallySigned}
173
+ onChange={(_: any, { checked }: any) => setDigitallySigned(checked)}
174
+ />
175
+ <Checkbox
176
+ id="print-prescription-checkbox"
177
+ labelText={t('printPrescriptionLabel', 'Imprimir a prescrição')}
178
+ checked={printRequested}
179
+ onChange={(_: any, { checked }: any) => setPrintRequested(checked)}
180
+ />
181
+ </div>
329
182
 
330
- {/* Tela sobreposta para adicionar/editar medicamento */}
331
- {showAddMedication && (
332
- <div className={styles.overlayForm}>
333
- <div className={styles.overlayContent}>
334
- <h3>
335
- {editIndex === null
336
- ? t('addMedication', 'Adicionar medicamento')
337
- : t('editMedication', 'Editar medicamento')}
338
- </h3>
339
- <MedicationForm
340
- initialData={editIndex !== null ? medications[editIndex] : undefined}
341
- onSubmit={editIndex === null ? handleAddMedication : handleEditMedication}
342
- onCancel={() => {
343
- setShowAddMedication(false);
344
- setEditIndex(null);
345
- }}
346
- t={t}
183
+ {/* Observações */}
184
+ <div className={styles.fieldGroup}>
185
+ <TextArea
186
+ id="observations"
187
+ labelText={t('observations', 'Observações')}
188
+ placeholder={t('observationsPlaceholder', 'Observações da prescrição...')}
189
+ value={observations}
190
+ onChange={(event: React.ChangeEvent<HTMLTextAreaElement>) => setObservations(event.target.value)}
191
+ rows={3}
192
+ />
193
+ </div>
194
+
195
+ {/* Lista de medicamentos da prescrição */}
196
+ <div className={styles.fieldGroup}>
197
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
198
+ <h3 style={{ margin: 0 }}>{t('medicationsList', 'Medicamentos')}</h3>
199
+ <Button
200
+ size="sm"
201
+ hasIconOnly
202
+ kind="ghost"
203
+ renderIcon={() => (
204
+ <svg width="20" height="20" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
205
+ <path d="M16 6V26" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
206
+ <path d="M6 16H26" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
207
+ </svg>
208
+ )}
209
+ iconDescription={t('addMedication', 'Adicionar medicamento')}
210
+ onClick={() => { setEditIndex(null); setShowAddMedication(true); }}
347
211
  />
348
212
  </div>
213
+
214
+ {prescriptionOrders.length === 0 ? (
215
+ <p>{t('noMedicationsAdded', 'Nenhum medicamento adicionado ainda.')}</p>
216
+ ) : (
217
+ <ul className={styles.medicationsList}>
218
+ {prescriptionOrders.map((order, idx) => (
219
+ <li key={idx} className={styles.medicationItem}>
220
+ <span>
221
+ <b>{order.drug.display}</b> — {order.dosage} {order.unit}
222
+ {order.route ? ` · ${order.route}` : ''} · {order.frequency}
223
+ </span>
224
+ <Button size="sm" kind="ghost" onClick={() => openEditMedication(idx)}>
225
+ {t('edit', 'Editar')}
226
+ </Button>
227
+ <Button size="sm" kind="danger--ghost" onClick={() => handleRemoveMedication(idx)}>
228
+ {t('remove', 'Remover')}
229
+ </Button>
230
+ </li>
231
+ ))}
232
+ </ul>
233
+ )}
349
234
  </div>
350
- )}
351
- </TabPanel>
352
- <TabPanel className={styles.tabContent}>
353
- <InlineNotification
354
- lowContrast
355
- kind="info"
356
- title={t('inPastTabPlaceholderTitle', 'Receitas históricas em breve')}
357
- subtitle={t('inPastTabPlaceholderSubtitle', 'Volte para Nova para gerenciar medicamentos atuais.')}
358
- />
359
- </TabPanel>
360
- </TabPanels>
361
- </Tabs>
362
- </section>
363
- </div>
235
+
236
+ {/* Formulário de adição/edição de medicamento */}
237
+ {showAddMedication && (
238
+ <div className={styles.overlayForm}>
239
+ <div className={styles.overlayContent}>
240
+ <h3 style={{ marginTop: 0 }}>
241
+ {editIndex !== null
242
+ ? t('editMedication', 'Editar medicamento')
243
+ : t('addMedication', 'Adicionar medicamento')}
244
+ </h3>
245
+ <AddDrugPrescription
246
+ initialData={editIndex !== null ? prescriptionOrders[editIndex] : undefined}
247
+ onSave={handleSaveMedication}
248
+ onCancel={() => { setShowAddMedication(false); setEditIndex(null); }}
249
+ />
250
+ </div>
251
+ </div>
252
+ )}
253
+
254
+ {/* Erro de envio */}
255
+ {submitError && (
256
+ <InlineNotification
257
+ lowContrast
258
+ kind="error"
259
+ title={t('submitError', 'Erro ao salvar a prescrição.')}
260
+ subtitle={submitError}
261
+ onCloseButtonClick={() => setSubmitError(null)}
262
+ />
263
+ )}
264
+
265
+ {/* Botão de salvar prescrição */}
266
+ <div style={{ display: 'flex', gap: 8, marginTop: 16 }}>
267
+ <Button
268
+ kind="primary"
269
+ disabled={!patientUuid || !selectedLocation || prescriptionOrders.length === 0 || isSubmitting}
270
+ onClick={handleSubmitPrescription}
271
+ >
272
+ {isSubmitting
273
+ ? <InlineLoading description={t('saving', 'Salvando...')} />
274
+ : t('createPrescriptionButtonLabel', 'Salvar Prescrição')}
275
+ </Button>
276
+ </div>
277
+ </TabPanel>
278
+
279
+ <TabPanel className={styles.tabContent}>
280
+ <InlineNotification
281
+ lowContrast
282
+ kind="info"
283
+ title={t('inPastTabPlaceholderTitle', 'Receitas históricas em breve')}
284
+ subtitle={t('inPastTabPlaceholderSubtitle', 'Volte para Nova para gerenciar medicamentos atuais.')}
285
+ />
286
+ </TabPanel>
287
+ </TabPanels>
288
+ </Tabs>
289
+ </section>
290
+ </div>
291
+ </Workspace2>
364
292
  );
365
293
  };
366
294
 
package/src/routes.json CHANGED
@@ -19,21 +19,22 @@
19
19
  "name": "Brand box",
20
20
  "component": "blueBox",
21
21
  "slot": "Boxes"
22
- },
23
- {
24
- "name": "prescriptions-action-menu",
25
- "component": "prescriptionsActionButton",
26
- "slot": "action-menu-patient-chart-items-slot"
27
22
  }
28
23
  ],
29
- "workspaces": [
24
+ "workspaces2": [
30
25
  {
31
- "name": "active-prescriptions",
32
- "title": "activePrescriptionsWorkspaceTitle",
26
+ "name": "active-prescriptions-workspace",
33
27
  "component": "root",
34
- "type": "active-prescriptions",
35
- "canHide": false,
36
- "width": "wider"
28
+ "window": "active-prescriptions-workspace"
29
+ }
30
+ ],
31
+ "workspaceWindows2": [
32
+ {
33
+ "name": "active-prescriptions-workspace",
34
+ "group": "patient-chart",
35
+ "icon": "prescriptionsActionButton",
36
+ "width": "wider",
37
+ "order": 4
37
38
  }
38
39
  ]
39
40
  }
package/dist/177.js DELETED
@@ -1,2 +0,0 @@
1
- /*! For license information please see 177.js.LICENSE.txt */
2
- "use strict";(globalThis.webpackChunk_cc_openmrs_cc_esm_active_prescriptions=globalThis.webpackChunk_cc_openmrs_cc_esm_active_prescriptions||[]).push([[177],{1496:(e,t,n)=>{n.d(t,{CC:()=>r,I0:()=>a,jB:()=>i,q2:()=>o});const r=0,i=1,o=2,a=3},2177:(e,t,n)=>{n.d(t,{Ay:()=>h,BE:()=>g});var r=n(6072),i=n(9888),o=n(6965),a=n(1496),s=n(4993);const c=()=>{};c(),new WeakMap;const u=r.use||(e=>{switch(e.status){case"pending":throw e;case"fulfilled":return e.value;case"rejected":throw e.reason;default:throw e.status="pending",e.then(t=>{e.status="fulfilled",e.value=t},t=>{e.status="rejected",e.reason=t}),e}}),l={dedupe:!0},d=Promise.resolve(o.U),f=()=>o.A,g=o.O.defineProperty(o.g,"defaultValue",{value:o.d}),h=(0,s.qm)((e,t,n)=>{const{cache:c,compare:g,suspense:h,fallbackData:v,revalidateOnMount:p,revalidateIfStale:w,refreshInterval:y,refreshWhenHidden:b,refreshWhenOffline:m,keepPreviousData:S,strictServerPrefetchWarning:O}=n,[_,E,R,k]=o.b.get(c),[T,C]=(0,o.s)(e),L=(0,r.useRef)(!1),j=(0,r.useRef)(!1),D=(0,r.useRef)(T),I=(0,r.useRef)(t),V=(0,r.useRef)(n),P=()=>V.current,x=()=>P().isVisible()&&P().isOnline(),[W,A,M,q]=(0,o.z)(c,T),F=(0,r.useRef)({}).current,U=(0,o.e)(v)?(0,o.e)(n.fallback)?o.U:n.fallback[T]:v,B=(e,t)=>{for(const n in F){const r=n;if("data"===r){if(!g(e[r],t[r])){if(!(0,o.e)(e[r]))return!1;if(!g(Q,t[r]))return!1}}else if(t[r]!==e[r])return!1}return!0},$=!L.current,z=(0,r.useMemo)(()=>{const e=W(),n=q(),r=e=>{const n=(0,o.m)(e);delete n._k;const r=(()=>{if(!T)return!1;if(!t)return!1;if(P().isPaused())return!1;if($&&!(0,o.e)(p))return p;const e=(0,o.e)(U)?n.data:U;return(0,o.e)(e)||w})();return r?{isValidating:!0,isLoading:!0,...n}:n},i=r(e),a=e===n?i:r(n);let s=i;return[()=>{const e=r(W());return B(e,s)?(s.data=e.data,s.isLoading=e.isLoading,s.isValidating=e.isValidating,s.error=e.error,s):(s=e,e)},()=>a]},[c,T]),H=(0,i.useSyncExternalStore)((0,r.useCallback)(e=>M(T,(t,n)=>{B(n,t)||e()}),[c,T]),z[0],z[1]),J=_[T]&&_[T].length>0,N=H.data,Y=(0,o.e)(N)?U&&(0,o.B)(U)?u(U):U:N,G=H.error,K=(0,r.useRef)(Y),Q=S?(0,o.e)(N)?(0,o.e)(K.current)?Y:K.current:N:Y,X=T&&(0,o.e)(Y),Z=(0,r.useRef)(null);!o.I&&(0,i.useSyncExternalStore)(f,()=>(Z.current=!1,Z),()=>(Z.current=!0,Z));const ee=Z.current;O&&ee&&!h&&X&&console.warn(`Missing pre-initiated data for serialized key "${T}" during server-side rendering. Data fetching should be initiated on the server and provided to SWR via fallback data. You can set "strictServerPrefetchWarning: false" to disable this warning.`);const te=!(!T||!t)&&!P().isPaused()&&!(J&&!(0,o.e)(G))&&($&&!(0,o.e)(p)?p:h?!(0,o.e)(Y)&&w:(0,o.e)(Y)||w),ne=$&&te,re=(0,o.e)(H.isValidating)?ne:H.isValidating,ie=(0,o.e)(H.isLoading)?ne:H.isLoading,oe=(0,r.useCallback)(async e=>{const t=I.current;if(!T||!t||j.current||P().isPaused())return!1;let r,i,s=!0;const c=e||{},u=!R[T]||!c.dedupe,l=()=>o.r?!j.current&&T===D.current&&L.current:T===D.current,d={isValidating:!1,isLoading:!1},f=()=>{A(d)},h=()=>{const e=R[T];e&&e[1]===i&&delete R[T]},v={isValidating:!0};(0,o.e)(W().data)&&(v.isLoading=!0);try{if(u&&(A(v),n.loadingTimeout&&(0,o.e)(W().data)&&setTimeout(()=>{s&&l()&&P().onLoadingSlow(T,n)},n.loadingTimeout),R[T]=[t(C),(0,o.o)()]),[r,i]=R[T],r=await r,u&&setTimeout(h,n.dedupingInterval),!R[T]||R[T][1]!==i)return u&&l()&&P().onDiscarded(T),!1;d.error=o.U;const e=E[T];if(!(0,o.e)(e)&&(i<=e[0]||i<=e[1]||0===e[1]))return f(),u&&l()&&P().onDiscarded(T),!1;const a=W().data;d.data=g(a,r)?a:r,u&&l()&&P().onSuccess(r,T,n)}catch(e){h();const t=P(),{shouldRetryOnError:n}=t;t.isPaused()||(d.error=e,u&&l()&&(t.onError(e,T,t),(!0===n||(0,o.a)(n)&&n(e))&&(P().revalidateOnFocus&&P().revalidateOnReconnect&&!x()||t.onErrorRetry(e,T,t,e=>{const t=_[T];t&&t[0]&&t[0](a.I0,e)},{retryCount:(c.retryCount||0)+1,dedupe:!0}))))}return s=!1,f(),!0},[T,c]),ae=(0,r.useCallback)((...e)=>(0,o.n)(c,D.current,...e),[]);if((0,o.u)(()=>{I.current=t,V.current=n,(0,o.e)(N)||(K.current=N)}),(0,o.u)(()=>{if(!T)return;const e=oe.bind(o.U,l);let t=0;if(P().revalidateOnFocus){const e=Date.now();t=e+P().focusThrottleInterval}const n=(0,s.aw)(T,_,(n,r={})=>{if(n==a.CC){const n=Date.now();P().revalidateOnFocus&&n>t&&x()&&(t=n+P().focusThrottleInterval,e())}else if(n==a.jB)P().revalidateOnReconnect&&x()&&e();else{if(n==a.q2)return oe();if(n==a.I0)return oe(r)}});return j.current=!1,D.current=T,L.current=!0,A({_k:C}),te&&(R[T]||((0,o.e)(Y)||o.I?e():(0,o.t)(e))),()=>{j.current=!0,n()}},[T]),(0,o.u)(()=>{let e;function t(){const t=(0,o.a)(y)?y(W().data):y;t&&-1!==e&&(e=setTimeout(n,t))}function n(){W().error||!b&&!P().isVisible()||!m&&!P().isOnline()?t():oe(l).then(t)}return t(),()=>{e&&(clearTimeout(e),e=-1)}},[y,b,m,T]),(0,r.useDebugValue)(Q),h){if(!o.r&&o.I&&X)throw new Error("Fallback data is required when using Suspense in SSR.");X&&(I.current=t,V.current=n,j.current=!1);const e=k[T],r=!(0,o.e)(e)&&X?ae(e):d;if(u(r),!(0,o.e)(G)&&X)throw G;const i=X?oe(l):d;!(0,o.e)(Q)&&X&&(i.status="fulfilled",i.value=!0),u(i)}return{mutate:ae,get data(){return F.data=!0,Q},get error(){return F.error=!0,G},get isValidating(){return F.isValidating=!0,re},get isLoading(){return F.isLoading=!0,ie}}})},4993:(e,t,n)=>{n.d(t,{Ht:()=>f,aw:()=>d,qm:()=>l});var r=n(6965),i=n(5430),o=n(6072);const a=r.i&&window.__SWR_DEVTOOLS_USE__,s=a?window.__SWR_DEVTOOLS_USE__:[],c=e=>(0,r.a)(e[1])?[e[0],e[1],e[2]||{}]:[e[0],null,(null===e[1]?e[2]:e[1])||{}],u=s.concat(e=>(t,n,o)=>e(t,n&&((...e)=>{const[o]=(0,r.s)(t),[,,,a]=r.b.get(r.c);if(o.startsWith(i.q))return n(...e);const s=a[o];return(0,r.e)(s)?n(...e):(delete a[o],s)}),o)),l=e=>function(...t){const n=(()=>{const e=(0,o.useContext)(r.S);return(0,o.useMemo)(()=>(0,r.m)(r.d,e),[e])})(),[i,a,s]=c(t),l=(0,r.f)(n,s);let d=e;const{use:f}=l,g=(f||[]).concat(u);for(let e=g.length;e--;)d=g[e](d);return d(i,a||l.fetcher||null,l)},d=(e,t,n)=>{const r=t[e]||(t[e]=[]);return r.push(n),()=>{const e=r.indexOf(n);e>=0&&(r[e]=r[r.length-1],r.pop())}},f=(e,t)=>(...n)=>{const[r,i,o]=c(n),a=(o.use||[]).concat(t);return e(r,i,{...o,use:a})};a&&(window.__SWR_DEVTOOLS_REACT__=o)},5430:(e,t,n)=>{n.d(t,{q:()=>r});const r="$inf$"},6965:(e,t,n)=>{n.d(t,{A:()=>s,B:()=>g,I:()=>T,O:()=>u,S:()=>J,U:()=>c,a:()=>d,b:()=>a,c:()=>B,d:()=>z,e:()=>l,f:()=>H,g:()=>N,i:()=>w,m:()=>f,n:()=>q,o:()=>M,r:()=>k,s:()=>W,t:()=>C,u:()=>L,z:()=>m});var r=n(6072),i=n(1496),o=Object.prototype.hasOwnProperty;const a=new WeakMap,s=()=>{},c=s(),u=Object,l=e=>e===c,d=e=>"function"==typeof e,f=(e,t)=>({...e,...t}),g=e=>d(e.then),h={},v={},p="undefined",w=typeof window!=p,y=typeof document!=p,b=w&&"Deno"in window,m=(e,t)=>{const n=a.get(e);return[()=>!l(t)&&e.get(t)||h,r=>{if(!l(t)){const i=e.get(t);t in v||(v[t]=i),n[5](t,f(i,r),i||h)}},n[6],()=>!l(t)&&t in v?v[t]:!l(t)&&e.get(t)||h]};let S=!0;const[O,_]=w&&window.addEventListener?[window.addEventListener.bind(window),window.removeEventListener.bind(window)]:[s,s],E={isOnline:()=>S,isVisible:()=>{const e=y&&document.visibilityState;return l(e)||"hidden"!==e}},R={initFocus:e=>(y&&document.addEventListener("visibilitychange",e),O("focus",e),()=>{y&&document.removeEventListener("visibilitychange",e),_("focus",e)}),initReconnect:e=>{const t=()=>{S=!0,e()},n=()=>{S=!1};return O("online",t),O("offline",n),()=>{_("online",t),_("offline",n)}}},k=!r.useId,T=!w||b,C=e=>w&&typeof window.requestAnimationFrame!=p?window.requestAnimationFrame(e):setTimeout(e,1),L=T?r.useEffect:r.useLayoutEffect,j="undefined"!=typeof navigator&&navigator.connection,D=!T&&j&&(["slow-2g","2g"].includes(j.effectiveType)||j.saveData),I=new WeakMap,V=(e,t)=>e===`[object ${t}]`;let P=0;const x=e=>{const t=typeof e,n=(r=e,u.prototype.toString.call(r));var r;const i=V(n,"Date"),o=V(n,"RegExp"),a=V(n,"Object");let s,c;if(u(e)!==e||i||o)s=i?e.toJSON():"symbol"==t?e.toString():"string"==t?JSON.stringify(e):""+e;else{if(s=I.get(e),s)return s;if(s=++P+"~",I.set(e,s),Array.isArray(e)){for(s="@",c=0;c<e.length;c++)s+=x(e[c])+",";I.set(e,s)}if(a){s="#";const t=u.keys(e).sort();for(;!l(c=t.pop());)l(e[c])||(s+=c+":"+x(e[c])+",");I.set(e,s)}}return s},W=e=>{if(d(e))try{e=e()}catch(t){e=""}const t=e;return[e="string"==typeof e?e:(Array.isArray(e)?e.length:e)?x(e):"",t]};let A=0;const M=()=>++A;async function q(...e){const[t,n,r,o]=e,s=f({populateCache:!0,throwOnError:!0},"boolean"==typeof o?{revalidate:o}:o||{});let u=s.populateCache;const h=s.rollbackOnError;let v=s.optimisticData;const p=s.throwOnError;if(d(n)){const e=n,r=[],i=t.keys();for(const n of i)!/^\$(inf|sub)\$/.test(n)&&e(t.get(n)._k)&&r.push(n);return Promise.all(r.map(w))}return w(n);async function w(n){const[o]=W(n);if(!o)return;const[f,w]=m(t,o),[y,b,S,O]=a.get(t),_=()=>{const e=y[o];return(d(s.revalidate)?s.revalidate(f().data,n):!1!==s.revalidate)&&(delete S[o],delete O[o],e&&e[0])?e[0](i.q2).then(()=>f().data):f().data};if(e.length<3)return _();let E,R=r,k=!1;const T=M();b[o]=[T,0];const C=!l(v),L=f(),j=L.data,D=L._c,I=l(D)?j:D;if(C&&(v=d(v)?v(I,j):v,w({data:v,_c:I})),d(R))try{R=R(I)}catch(e){E=e,k=!0}if(R&&g(R)){if(R=await R.catch(e=>{E=e,k=!0}),T!==b[o][0]){if(k)throw E;return R}k&&C&&(e=>"function"==typeof h?h(e):!1!==h)(E)&&(u=!0,w({data:I,_c:c}))}if(u&&!k)if(d(u)){const e=u(R,I);w({data:e,error:c,_c:c})}else w({data:R,error:c,_c:c});if(b[o][1]=M(),Promise.resolve(_()).then(()=>{w({_c:c})}),!k)return R;if(p)throw E}}const F=(e,t)=>{for(const n in e)e[n][0]&&e[n][0](t)},U=(e,t)=>{if(!a.has(e)){const n=f(R,t),r=Object.create(null),o=q.bind(c,e);let u=s;const l=Object.create(null),d=(e,t)=>{const n=l[e]||[];return l[e]=n,n.push(t),()=>n.splice(n.indexOf(t),1)},g=(t,n,r)=>{e.set(t,n);const i=l[t];if(i)for(const e of i)e(n,r)},h=()=>{if(!a.has(e)&&(a.set(e,[r,Object.create(null),Object.create(null),Object.create(null),o,g,d]),!T)){const t=n.initFocus(setTimeout.bind(c,F.bind(c,r,i.CC))),o=n.initReconnect(setTimeout.bind(c,F.bind(c,r,i.jB)));u=()=>{t&&t(),o&&o(),a.delete(e)}}};return h(),[e,o,h,u]}return[e,a.get(e)[4]]},[B,$]=U(new Map),z=f({onLoadingSlow:s,onSuccess:s,onError:s,onErrorRetry:(e,t,n,r,i)=>{const o=n.errorRetryCount,a=i.retryCount,s=~~((Math.random()+.5)*(1<<(a<8?a:8)))*n.errorRetryInterval;!l(o)&&a>o||setTimeout(r,s,i)},onDiscarded:s,revalidateOnFocus:!0,revalidateOnReconnect:!0,revalidateIfStale:!0,shouldRetryOnError:!0,errorRetryInterval:D?1e4:5e3,focusThrottleInterval:5e3,dedupingInterval:2e3,loadingTimeout:D?5e3:3e3,compare:function e(t,n){var r,i;if(t===n)return!0;if(t&&n&&(r=t.constructor)===n.constructor){if(r===Date)return t.getTime()===n.getTime();if(r===RegExp)return t.toString()===n.toString();if(r===Array){if((i=t.length)===n.length)for(;i--&&e(t[i],n[i]););return-1===i}if(!r||"object"==typeof t){for(r in i=0,t){if(o.call(t,r)&&++i&&!o.call(n,r))return!1;if(!(r in n)||!e(t[r],n[r]))return!1}return Object.keys(n).length===i}}return t!=t&&n!=n},isPaused:()=>!1,cache:B,mutate:$,fallback:{}},E),H=(e,t)=>{const n=f(e,t);if(t){const{use:r,fallback:i}=e,{use:o,fallback:a}=t;r&&o&&(n.use=r.concat(o)),i&&a&&(n.fallback=f(i,a))}return n},J=(0,r.createContext)({}),N=e=>{const{value:t}=e,n=(0,r.useContext)(J),i=d(t),o=(0,r.useMemo)(()=>i?t(n):t,[i,n,t]),a=(0,r.useMemo)(()=>i?o:H(n,o),[i,n,o]),s=o&&o.provider,u=(0,r.useRef)(c);s&&!u.current&&(u.current=U(s(a.cache||B),o));const l=u.current;return l&&(a.cache=l[0],a.mutate=l[1]),L(()=>{if(l)return l[2]&&l[2](),l[3]},[]),(0,r.createElement)(J.Provider,f(e,{value:a}))}},8493:(e,t,n)=>{var r=n(6072),i="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},o=r.useState,a=r.useEffect,s=r.useLayoutEffect,c=r.useDebugValue;function u(e){var t=e.getSnapshot;e=e.value;try{var n=t();return!i(e,n)}catch(e){return!0}}var l="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(e,t){var n=t(),r=o({inst:{value:n,getSnapshot:t}}),i=r[0].inst,l=r[1];return s(function(){i.value=n,i.getSnapshot=t,u(i)&&l({inst:i})},[e,n,t]),a(function(){return u(i)&&l({inst:i}),e(function(){u(i)&&l({inst:i})})},[e]),c(n),n};t.useSyncExternalStore=void 0!==r.useSyncExternalStore?r.useSyncExternalStore:l},9888:(e,t,n)=>{e.exports=n(8493)}}]);