@cc-openmrs/cc-esm-active-prescriptions 1.0.67 → 1.0.69

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.
@@ -0,0 +1,186 @@
1
+ import React, { useState } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { Button, Checkbox, ComboBox, TextInput } from '@carbon/react';
4
+ import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
5
+ import useSWR from 'swr';
6
+
7
+ export interface PrescriptionDrugItem {
8
+ drug: { uuid: string; name: string; display: string };
9
+ dosage: string;
10
+ unit: string;
11
+ route: string;
12
+ frequency: string;
13
+ patientInstructions?: string;
14
+ asNeeded?: boolean;
15
+ asNeededCondition?: string;
16
+ indication?: string;
17
+ }
18
+
19
+ export interface AddDrugPrescriptionProps {
20
+ initialData?: PrescriptionDrugItem;
21
+ onSave: (item: PrescriptionDrugItem) => void;
22
+ onCancel: () => void;
23
+ }
24
+
25
+ const UNITS = [{ value: 'mg' }, { value: 'ml' }, { value: 'g' }, { value: 'mcg' }, { value: 'comprimido(s)' }];
26
+
27
+ const ROUTES = [
28
+ { value: 'Oral' },
29
+ { value: 'Intravenoso (IV)' },
30
+ { value: 'Intramuscular (IM)' },
31
+ { value: 'Subcutâneo (SC)' },
32
+ { value: 'Tópico' },
33
+ { value: 'Inalatório' },
34
+ { value: 'Sublingual' },
35
+ ];
36
+
37
+ const FREQUENCIES = [
38
+ { value: '1x ao dia' },
39
+ { value: '2x ao dia' },
40
+ { value: '3x ao dia' },
41
+ { value: '4x ao dia' },
42
+ { value: '6/6h' },
43
+ { value: '8/8h' },
44
+ { value: '12/12h' },
45
+ { value: 'Se necessário' },
46
+ ];
47
+
48
+ function useDrugSearch(query: string) {
49
+ const url =
50
+ query?.length >= 3
51
+ ? `${restBaseUrl}/drug?q=${encodeURIComponent(query)}&v=custom:(uuid,name,display)&limit=10`
52
+ : null;
53
+ const { data, isValidating } = useSWR<any>(url, openmrsFetch);
54
+ return {
55
+ drugs: (data?.data?.results ?? []) as Array<{ uuid: string; name: string; display: string }>,
56
+ isLoading: isValidating,
57
+ };
58
+ }
59
+
60
+ const AddDrugPrescription: React.FC<AddDrugPrescriptionProps> = ({ initialData, onSave, onCancel }) => {
61
+ const { t } = useTranslation();
62
+
63
+ const [drugQuery, setDrugQuery] = useState(initialData?.drug?.name ?? '');
64
+ const [selectedDrug, setSelectedDrug] = useState<PrescriptionDrugItem['drug'] | null>(initialData?.drug ?? null);
65
+ const [dosage, setDosage] = useState(initialData?.dosage ?? '');
66
+ const [unit, setUnit] = useState(initialData?.unit ?? '');
67
+ const [route, setRoute] = useState(initialData?.route ?? '');
68
+ const [frequency, setFrequency] = useState(initialData?.frequency ?? '');
69
+ const [patientInstructions, setPatientInstructions] = useState(initialData?.patientInstructions ?? '');
70
+ const [asNeeded, setAsNeeded] = useState(initialData?.asNeeded ?? false);
71
+ const [asNeededCondition, setAsNeededCondition] = useState(initialData?.asNeededCondition ?? '');
72
+ const [indication, setIndication] = useState(initialData?.indication ?? '');
73
+
74
+ const { drugs, isLoading } = useDrugSearch(drugQuery);
75
+
76
+ const handleSubmit = (e: React.FormEvent) => {
77
+ e.preventDefault();
78
+ if (!selectedDrug || !dosage || !unit || !frequency) return;
79
+ onSave({
80
+ drug: selectedDrug,
81
+ dosage,
82
+ unit,
83
+ route,
84
+ frequency,
85
+ patientInstructions: patientInstructions || undefined,
86
+ asNeeded,
87
+ asNeededCondition: asNeeded ? asNeededCondition || undefined : undefined,
88
+ indication: indication || undefined,
89
+ });
90
+ };
91
+
92
+ return (
93
+ <form onSubmit={handleSubmit} style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
94
+ <ComboBox
95
+ id="drug-search"
96
+ titleText={t('drugName', 'Medicamento')}
97
+ placeholder={t('drugSearchPlaceholder', 'Digite para buscar (mín. 3 letras)...')}
98
+ items={drugs.map((d) => ({ id: d.uuid, label: d.display || d.name, ...d }))}
99
+ itemToString={(item: any) => item?.label ?? ''}
100
+ selectedItem={selectedDrug ? { id: selectedDrug.uuid, label: selectedDrug.display } : null}
101
+ onInputChange={(v: string) => {
102
+ setDrugQuery(v);
103
+ if (!v) setSelectedDrug(null);
104
+ }}
105
+ onChange={({ selectedItem }: any) => {
106
+ if (selectedItem) {
107
+ setSelectedDrug({
108
+ uuid: selectedItem.uuid,
109
+ name: selectedItem.name,
110
+ display: selectedItem.display ?? selectedItem.name,
111
+ });
112
+ }
113
+ }}
114
+ disabled={isLoading}
115
+ helperText={isLoading ? t('searching', 'Buscando...') : undefined}
116
+ />
117
+ <TextInput
118
+ id="dosage"
119
+ labelText={t('dosage', 'Dose')}
120
+ value={dosage}
121
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => setDosage(e.target.value)}
122
+ required
123
+ />
124
+ <ComboBox
125
+ id="unit"
126
+ titleText={t('unit', 'Unidade')}
127
+ items={UNITS}
128
+ itemToString={(item: any) => item?.value ?? ''}
129
+ selectedItem={UNITS.find((u) => u.value === unit) ?? null}
130
+ onChange={({ selectedItem }: any) => setUnit(selectedItem?.value ?? '')}
131
+ />
132
+ <ComboBox
133
+ id="route"
134
+ titleText={t('route', 'Via de administração')}
135
+ items={ROUTES}
136
+ itemToString={(item: any) => item?.value ?? ''}
137
+ selectedItem={ROUTES.find((r) => r.value === route) ?? null}
138
+ onChange={({ selectedItem }: any) => setRoute(selectedItem?.value ?? '')}
139
+ />
140
+ <ComboBox
141
+ id="frequency"
142
+ titleText={t('frequency', 'Frequência')}
143
+ items={FREQUENCIES}
144
+ itemToString={(item: any) => item?.value ?? ''}
145
+ selectedItem={FREQUENCIES.find((f) => f.value === frequency) ?? null}
146
+ onChange={({ selectedItem }: any) => setFrequency(selectedItem?.value ?? '')}
147
+ />
148
+ <TextInput
149
+ id="patient-instructions"
150
+ labelText={t('patientInstructions', 'Instruções ao paciente')}
151
+ value={patientInstructions}
152
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => setPatientInstructions(e.target.value)}
153
+ />
154
+ <Checkbox
155
+ id="as-needed"
156
+ labelText={t('asNeeded', 'Tomar se necessário (P.R.N.)')}
157
+ checked={asNeeded}
158
+ onChange={(_: any, { checked }: any) => setAsNeeded(checked)}
159
+ />
160
+ {asNeeded && (
161
+ <TextInput
162
+ id="as-needed-condition"
163
+ labelText={t('asNeededCondition', 'Motivo P.R.N.')}
164
+ value={asNeededCondition}
165
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => setAsNeededCondition(e.target.value)}
166
+ />
167
+ )}
168
+ <TextInput
169
+ id="indication"
170
+ labelText={t('indication', 'Indicação')}
171
+ value={indication}
172
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => setIndication(e.target.value)}
173
+ />
174
+ <div style={{ display: 'flex', gap: '0.5rem', marginTop: '0.5rem' }}>
175
+ <Button type="submit" kind="primary" disabled={!selectedDrug || !dosage || !unit || !frequency}>
176
+ {t('saveMedication', 'Salvar medicamento')}
177
+ </Button>
178
+ <Button type="button" kind="secondary" onClick={onCancel}>
179
+ {t('cancel', 'Cancelar')}
180
+ </Button>
181
+ </div>
182
+ </form>
183
+ );
184
+ };
185
+
186
+ export default AddDrugPrescription;
@@ -0,0 +1 @@
1
+ @import "../add-drug-order/add-drug-order.scss";
@@ -0,0 +1,2 @@
1
+ export { default } from './add-drug-prescription.component';
2
+ export type { PrescriptionDrugItem, AddDrugPrescriptionProps } from './add-drug-prescription.component';
@@ -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, Workspace2 } 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,46 +46,74 @@ 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
118
  <Workspace2 title={t('activePrescriptionsWorkspaceTitle', 'Prescrições ativas')} hasUnsavedChanges={false}>
244
119
  <div className={styles.workspace}>
@@ -264,6 +139,7 @@ const Root: React.FC<RootProps> = ({ patientUuid, patient }) => {
264
139
  </TabList>
265
140
  <TabPanels>
266
141
  <TabPanel className={styles.tabContent}>
142
+ {/* Local da visita */}
267
143
  <div className={styles.fieldGroup}>
268
144
  <ComboBox
269
145
  id="visit-location"
@@ -277,6 +153,7 @@ const Root: React.FC<RootProps> = ({ patientUuid, patient }) => {
277
153
  />
278
154
  </div>
279
155
 
156
+ {/* Número da apólice */}
280
157
  <div className={styles.fieldGroup}>
281
158
  <TextInput
282
159
  id="insurance-policy"
@@ -286,68 +163,119 @@ const Root: React.FC<RootProps> = ({ patientUuid, patient }) => {
286
163
  onChange={(event) => setPolicyNumber(event.target.value)}
287
164
  />
288
165
  </div>
166
+
167
+ {/* Assinatura digital e impressão */}
289
168
  <div className={styles.fieldGroup}>
290
169
  <Checkbox
291
170
  id="sign-prescription-checkbox"
292
171
  labelText={t('signPrescriptionLabel', 'Assinar digitalmente a prescrição')}
293
172
  checked={digitallySigned}
294
- onChange={() => setDigitallySigned((prev) => !prev)}
173
+ onChange={(_: any, { checked }: any) => setDigitallySigned(checked)}
295
174
  />
296
175
  <Checkbox
297
176
  id="print-prescription-checkbox"
298
177
  labelText={t('printPrescriptionLabel', 'Imprimir a prescrição')}
299
178
  checked={printRequested}
300
- onChange={() => setPrintRequested((prev) => !prev)}
179
+ onChange={(_: any, { checked }: any) => setPrintRequested(checked)}
301
180
  />
302
181
  </div>
303
182
 
183
+ {/* Observações */}
304
184
  <div className={styles.fieldGroup}>
305
- <h3>{t('medicationsList', 'Medicamentos adicionados')}</h3>
306
- {medications.length === 0 ? (
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); }}
211
+ />
212
+ </div>
213
+
214
+ {prescriptionOrders.length === 0 ? (
307
215
  <p>{t('noMedicationsAdded', 'Nenhum medicamento adicionado ainda.')}</p>
308
216
  ) : (
309
217
  <ul className={styles.medicationsList}>
310
- {medications.map((med, idx) => (
218
+ {prescriptionOrders.map((order, idx) => (
311
219
  <li key={idx} className={styles.medicationItem}>
312
220
  <span>
313
- <b>{med.drugName}</b> - {med.dosage} {med.unit} - {med.frequency}
221
+ <b>{order.drug.display}</b> {order.dosage} {order.unit}
222
+ {order.route ? ` · ${order.route}` : ''} · {order.frequency}
314
223
  </span>
315
- <button type="button" onClick={() => openEditMedication(idx)}>
224
+ <Button size="sm" kind="ghost" onClick={() => openEditMedication(idx)}>
316
225
  {t('edit', 'Editar')}
317
- </button>
318
- <button type="button" onClick={() => handleRemoveMedication(idx)}>
226
+ </Button>
227
+ <Button size="sm" kind="danger--ghost" onClick={() => handleRemoveMedication(idx)}>
319
228
  {t('remove', 'Remover')}
320
- </button>
229
+ </Button>
321
230
  </li>
322
231
  ))}
323
232
  </ul>
324
233
  )}
325
- <button type="button" onClick={openAddMedication} className={styles.addMedicationBtn}>
326
- {t('addMedication', '+ Adicionar medicamento')}
327
- </button>
328
234
  </div>
329
235
 
236
+ {/* Formulário de adição/edição de medicamento */}
330
237
  {showAddMedication && (
331
238
  <div className={styles.overlayForm}>
332
239
  <div className={styles.overlayContent}>
333
- <h3>
334
- {editIndex === null
335
- ? t('addMedication', 'Adicionar medicamento')
336
- : t('editMedication', 'Editar medicamento')}
240
+ <h3 style={{ marginTop: 0 }}>
241
+ {editIndex !== null
242
+ ? t('editMedication', 'Editar medicamento')
243
+ : t('addMedication', 'Adicionar medicamento')}
337
244
  </h3>
338
- <MedicationForm
339
- initialData={editIndex !== null ? medications[editIndex] : undefined}
340
- onSubmit={editIndex === null ? handleAddMedication : handleEditMedication}
341
- onCancel={() => {
342
- setShowAddMedication(false);
343
- setEditIndex(null);
344
- }}
345
- t={t}
245
+ <AddDrugPrescription
246
+ initialData={editIndex !== null ? prescriptionOrders[editIndex] : undefined}
247
+ onSave={handleSaveMedication}
248
+ onCancel={() => { setShowAddMedication(false); setEditIndex(null); }}
346
249
  />
347
250
  </div>
348
251
  </div>
349
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>
350
277
  </TabPanel>
278
+
351
279
  <TabPanel className={styles.tabContent}>
352
280
  <InlineNotification
353
281
  lowContrast