@openmrs/esm-stock-management-app 1.0.1-pre.438 → 1.0.1-pre.444
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/574.js +1 -1
- package/dist/800.js +1 -1
- package/dist/800.js.map +1 -1
- package/dist/830.js +1 -0
- package/dist/830.js.map +1 -0
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-stock-management-app.js +1 -1
- package/dist/openmrs-esm-stock-management-app.js.buildmanifest.json +31 -31
- package/dist/openmrs-esm-stock-management-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/core/components/carbon/controlled-number-input/controlled-number-input.component.tsx +1 -1
- package/src/core/components/tabs/vertical-tabs.scss +1 -2
- package/src/stock-items/add-stock-item/drug-selector/drug-selector.component.tsx +27 -12
- package/src/stock-items/add-stock-item/drug-selector/drug-selector.resource.tsx +9 -35
- package/src/stock-items/add-stock-item/packaging-units/packaging-units.component.tsx +179 -64
- package/src/stock-items/add-stock-item/packaging-units/packaging-units.scss +33 -21
- package/src/stock-items/add-stock-item/packaging-units-concept-selector/packaging-units-concept-selector.component.tsx +4 -2
- package/src/stock-items/add-stock-item/stock-item-details/stock-item-details.component.tsx +6 -5
- package/src/stock-items/stock-items.resource.ts +10 -1
- package/translations/en.json +272 -24
- package/dist/699.js +0 -1
- package/dist/699.js.map +0 -1
@@ -1,4 +1,4 @@
|
|
1
|
-
import React, { useEffect, useMemo } from "react";
|
1
|
+
import React, { useEffect, useMemo, useState } from "react";
|
2
2
|
import { showSnackbar } from "@openmrs/esm-framework";
|
3
3
|
import { useTranslation } from "react-i18next";
|
4
4
|
import { useStockItemPackageUnitsHook } from "./packaging-units.resource";
|
@@ -21,7 +21,10 @@ import { FormProvider, useForm, useFormContext } from "react-hook-form";
|
|
21
21
|
import { zodResolver } from "@hookform/resolvers/zod";
|
22
22
|
import { PackageUnitFormData, packageUnitSchema } from "./validationSchema";
|
23
23
|
import { StockItemPackagingUOMDTO } from "../../../core/api/types/stockItem/StockItemPackagingUOM";
|
24
|
-
import {
|
24
|
+
import {
|
25
|
+
createStockItemPackagingUnit,
|
26
|
+
updateStockItemPackagingUnit,
|
27
|
+
} from "../../stock-items.resource";
|
25
28
|
import DeleteModalButton from "./packaging-units-delete-modal-button.component";
|
26
29
|
|
27
30
|
import styles from "./packaging-units.scss";
|
@@ -39,10 +42,28 @@ const PackagingUnits: React.FC<PackagingUnitsProps> = ({
|
|
39
42
|
}) => {
|
40
43
|
const { items, isLoading, setStockItemUuid, mutate } =
|
41
44
|
useStockItemPackageUnitsHook();
|
45
|
+
|
46
|
+
const [packagingUnits, setPackagingUnits] =
|
47
|
+
useState<StockItemPackagingUOMDTO[]>(items);
|
48
|
+
|
49
|
+
const [newUnit, setNewUnit] = useState<{
|
50
|
+
factor: number;
|
51
|
+
packagingUomUuid: string;
|
52
|
+
packagingUomName: string;
|
53
|
+
}>({
|
54
|
+
packagingUomUuid: undefined,
|
55
|
+
factor: 0,
|
56
|
+
packagingUomName: "",
|
57
|
+
});
|
58
|
+
|
42
59
|
useEffect(() => {
|
43
60
|
setStockItemUuid(stockItemUuid);
|
44
61
|
}, [stockItemUuid, setStockItemUuid]);
|
45
62
|
|
63
|
+
useEffect(() => {
|
64
|
+
setPackagingUnits(items);
|
65
|
+
}, [items]);
|
66
|
+
|
46
67
|
const { t } = useTranslation();
|
47
68
|
const tableHeaders = useMemo(
|
48
69
|
() => [
|
@@ -72,40 +93,118 @@ const PackagingUnits: React.FC<PackagingUnitsProps> = ({
|
|
72
93
|
});
|
73
94
|
|
74
95
|
const handleSavePackageUnits = () => {
|
75
|
-
const {
|
76
|
-
|
77
|
-
const
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
96
|
+
const { reset } = packageUnitForm;
|
97
|
+
|
98
|
+
const newPayload = newUnit
|
99
|
+
? {
|
100
|
+
factor: newUnit.factor,
|
101
|
+
packagingUomUuid: newUnit.packagingUomUuid,
|
102
|
+
stockItemUuid,
|
103
|
+
}
|
104
|
+
: null;
|
105
|
+
|
106
|
+
// Filter changed units
|
107
|
+
const updatedUnits = packagingUnits.filter((unit) => {
|
108
|
+
const originalUnit = items.find((item) => item.uuid === unit.uuid);
|
109
|
+
return originalUnit && originalUnit.factor !== unit.factor;
|
110
|
+
});
|
111
|
+
|
112
|
+
// Create new unit
|
113
|
+
const createPromises = newPayload.packagingUomUuid
|
114
|
+
? createStockItemPackagingUnit(newPayload).then(
|
115
|
+
() => {
|
116
|
+
showSnackbar({
|
117
|
+
title: t("savePackingUnitTitle", "Package Unit"),
|
118
|
+
subtitle: t(
|
119
|
+
"savePackingUnitMessage",
|
120
|
+
"Package Unit saved successfully"
|
121
|
+
),
|
122
|
+
kind: "success",
|
123
|
+
});
|
124
|
+
setNewUnit({
|
125
|
+
factor: 0,
|
126
|
+
packagingUomUuid: undefined,
|
127
|
+
packagingUomName: "",
|
128
|
+
}); // Reset new unit
|
129
|
+
},
|
130
|
+
() => {
|
131
|
+
showSnackbar({
|
132
|
+
title: t("savePackagingUnitErrorTitle", "Package Unit"),
|
133
|
+
subtitle: t(
|
134
|
+
"savePackagingUnitErrorMessage",
|
135
|
+
"Error saving package unit"
|
136
|
+
),
|
137
|
+
kind: "error",
|
138
|
+
});
|
139
|
+
}
|
140
|
+
)
|
141
|
+
: Promise.resolve({ status: "no-create" });
|
142
|
+
|
143
|
+
// Update existing units
|
144
|
+
const updatePromises = updatedUnits.map((unit) =>
|
145
|
+
updateStockItemPackagingUnit(unit, unit.uuid).then(
|
146
|
+
() => {
|
147
|
+
showSnackbar({
|
148
|
+
title: t("updatePackingUnitTitle", "Package Unit"),
|
149
|
+
subtitle: t(
|
150
|
+
"updatePackingUnitMessage",
|
151
|
+
"Package Unit {{ name }} updated successfully",
|
152
|
+
{ name: unit.packagingUomName }
|
153
|
+
),
|
154
|
+
kind: "success",
|
155
|
+
});
|
156
|
+
},
|
157
|
+
() => {
|
158
|
+
showSnackbar({
|
159
|
+
title: t("updatePackagingUnitErrorTitle", "Package Unit"),
|
160
|
+
subtitle: t(
|
161
|
+
"updatePackagingUnitErrorMessage",
|
162
|
+
"Error updating package unit {{name}}",
|
163
|
+
{ name: unit.packagingUomName }
|
164
|
+
),
|
165
|
+
kind: "error",
|
166
|
+
});
|
167
|
+
}
|
168
|
+
)
|
169
|
+
);
|
170
|
+
|
171
|
+
// Wait for all requests to complete
|
172
|
+
Promise.all([createPromises, ...updatePromises]).then(() => {
|
173
|
+
mutate();
|
174
|
+
reset();
|
175
|
+
handleTabChange(0);
|
176
|
+
});
|
177
|
+
};
|
178
|
+
|
179
|
+
const handleNewUnitFactorChange = (value: string | number) => {
|
180
|
+
setNewUnit({
|
181
|
+
...newUnit,
|
182
|
+
factor: Number(value),
|
183
|
+
});
|
184
|
+
};
|
185
|
+
|
186
|
+
const handleNewUnitPackageUnitChange = (unit: {
|
187
|
+
uuid: string;
|
188
|
+
display: string;
|
189
|
+
}) => {
|
190
|
+
setNewUnit({
|
191
|
+
...newUnit,
|
192
|
+
packagingUomUuid: unit.uuid,
|
193
|
+
packagingUomName: unit.display,
|
194
|
+
});
|
195
|
+
};
|
196
|
+
|
197
|
+
const onFactorFieldUpdate = (
|
198
|
+
row: StockItemPackagingUOMDTO,
|
199
|
+
value: string | number
|
200
|
+
) => {
|
201
|
+
const qtyValue = typeof value === "number" ? value : parseFloat(value);
|
202
|
+
|
203
|
+
setPackagingUnits((prevState) =>
|
204
|
+
prevState.map((item) =>
|
205
|
+
item.uuid === row.uuid ? { ...item, factor: qtyValue } : item
|
206
|
+
)
|
107
207
|
);
|
108
|
-
handleTabChange(0);
|
109
208
|
};
|
110
209
|
|
111
210
|
if (isLoading)
|
@@ -147,25 +246,38 @@ const PackagingUnits: React.FC<PackagingUnitsProps> = ({
|
|
147
246
|
</TableHead>
|
148
247
|
<TableBody className={styles.packingTableBody}>
|
149
248
|
{items?.map((row: StockItemPackagingUOMDTO, index) => (
|
150
|
-
<PackagingUnitRow
|
249
|
+
<PackagingUnitRow
|
250
|
+
row={row}
|
251
|
+
id={`${index}-${row?.uuid}`}
|
252
|
+
onChange={(value) => onFactorFieldUpdate(row, value)}
|
253
|
+
/>
|
151
254
|
))}
|
152
|
-
<PackagingUnitRow
|
255
|
+
<PackagingUnitRow
|
256
|
+
row={newUnit || {}}
|
257
|
+
id="new-package-unit"
|
258
|
+
isEditing
|
259
|
+
onChangePackageUnit={(value) =>
|
260
|
+
handleNewUnitPackageUnitChange(value)
|
261
|
+
}
|
262
|
+
onChange={(value) => handleNewUnitFactorChange(value)}
|
263
|
+
/>
|
153
264
|
</TableBody>
|
154
265
|
</Table>
|
155
266
|
</TableContainer>
|
156
267
|
)}
|
157
268
|
/>
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
269
|
+
<div className={styles.packageUnitsBtn}>
|
270
|
+
<Button
|
271
|
+
name="save"
|
272
|
+
type="submit"
|
273
|
+
className="submitButton"
|
274
|
+
onClick={handleSavePackageUnits}
|
275
|
+
kind="primary"
|
276
|
+
renderIcon={Save}
|
277
|
+
>
|
278
|
+
{t("save", "Save")}
|
279
|
+
</Button>
|
280
|
+
</div>
|
169
281
|
</FormProvider>
|
170
282
|
);
|
171
283
|
};
|
@@ -175,8 +287,10 @@ export default PackagingUnits;
|
|
175
287
|
const PackagingUnitRow: React.FC<{
|
176
288
|
isEditing?: boolean;
|
177
289
|
row: StockItemPackagingUOMDTO;
|
178
|
-
|
179
|
-
|
290
|
+
id?: string;
|
291
|
+
onChange?: (value: string | number) => void;
|
292
|
+
onChangePackageUnit?: (value: { uuid: string; display: string }) => void;
|
293
|
+
}> = ({ isEditing, row, id, onChange, onChangePackageUnit }) => {
|
180
294
|
const {
|
181
295
|
control,
|
182
296
|
formState: { errors },
|
@@ -194,6 +308,7 @@ const PackagingUnitRow: React.FC<{
|
|
194
308
|
name="packagingUomUuid"
|
195
309
|
placeholder="Filter"
|
196
310
|
control={control}
|
311
|
+
onPackageUnitChange={(concept) => onChangePackageUnit(concept)}
|
197
312
|
invalid={!!errors.packagingUomUuid}
|
198
313
|
/>
|
199
314
|
) : (
|
@@ -202,20 +317,20 @@ const PackagingUnitRow: React.FC<{
|
|
202
317
|
)}
|
203
318
|
</TableCell>
|
204
319
|
<TableCell>
|
205
|
-
<
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
320
|
+
<ControlledNumberInput
|
321
|
+
row={row}
|
322
|
+
controllerName="factor"
|
323
|
+
name="factor"
|
324
|
+
min={minPackagingQuantity}
|
325
|
+
control={control}
|
326
|
+
id={id}
|
327
|
+
invalid={!!errors.factor}
|
328
|
+
hideSteppers={true}
|
329
|
+
onChange={(e, state) => onChange(state.value)}
|
330
|
+
/>
|
331
|
+
</TableCell>
|
332
|
+
<TableCell>
|
333
|
+
<DeleteModalButton closeModal={() => true} row={row} />
|
219
334
|
</TableCell>
|
220
335
|
</TableRow>
|
221
336
|
</>
|
@@ -1,44 +1,56 @@
|
|
1
1
|
@use '@carbon/styles/scss/colors';
|
2
2
|
@use "@carbon/styles/scss/spacing";
|
3
3
|
@use "@carbon/styles/scss/type";
|
4
|
+
|
4
5
|
.packingTable {
|
5
|
-
|
6
|
-
|
6
|
+
min-height: 15rem;
|
7
|
+
display: block;
|
7
8
|
}
|
8
9
|
|
9
10
|
.packingTableCell {
|
10
|
-
|
11
|
-
|
11
|
+
display: flex;
|
12
|
+
align-items: center;
|
12
13
|
}
|
13
14
|
|
14
15
|
.packagingTableBody {
|
15
|
-
|
16
|
+
min-height: spacing.$spacing-13;
|
16
17
|
}
|
17
18
|
|
18
19
|
.packagingTableContainer {
|
19
|
-
|
20
|
-
|
20
|
+
padding-bottom: 1rem;
|
21
|
+
min-height: 15rem;
|
22
|
+
display: block;
|
23
|
+
|
24
|
+
:global(.cds--data-table-content) {
|
25
|
+
overflow-x: visible;
|
26
|
+
}
|
21
27
|
}
|
22
28
|
|
23
29
|
.searchAndSave {
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
+
display: flex;
|
31
|
+
flex-direction: column;
|
32
|
+
align-items: flex-start;
|
33
|
+
.submitButton {
|
34
|
+
margin-top: 1rem;
|
35
|
+
}
|
30
36
|
}
|
31
37
|
|
32
38
|
.deleteModal {
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
}
|
39
|
+
:global(.cds--modal-container.cds--modal-container--sm) {
|
40
|
+
:global(.cds--modal-content) {
|
41
|
+
:global(.cds--form-item) {
|
42
|
+
margin-top: 1rem;
|
43
|
+
}
|
39
44
|
}
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
.packageUnitsBtn {
|
49
|
+
display: flex;
|
50
|
+
justify-content: flex-end;
|
51
|
+
margin-right: 2rem;
|
40
52
|
}
|
41
53
|
|
42
54
|
:global(cds--form-item) {
|
43
|
-
|
44
|
-
}
|
55
|
+
margin-top: 1rem;
|
56
|
+
}
|
@@ -9,7 +9,7 @@ import { type ConfigObject } from "../../../config-schema";
|
|
9
9
|
|
10
10
|
interface PackagingUnitsConceptSelectorProps<T> {
|
11
11
|
row?: StockItemPackagingUOMDTO;
|
12
|
-
onPackageUnitChange?: (unit:
|
12
|
+
onPackageUnitChange?: (unit: { uuid: string; display: string }) => void;
|
13
13
|
title?: string;
|
14
14
|
placeholder?: string;
|
15
15
|
invalid?: boolean;
|
@@ -62,7 +62,9 @@ const PackagingUnitsConceptSelector = <T,>(
|
|
62
62
|
]
|
63
63
|
: dispensingUnits || []
|
64
64
|
}
|
65
|
-
onChange={(data: {
|
65
|
+
onChange={(data: {
|
66
|
+
selectedItem: { uuid: string; display: string };
|
67
|
+
}) => {
|
66
68
|
props.onPackageUnitChange?.(data?.selectedItem);
|
67
69
|
onChange(data?.selectedItem?.uuid || ""); // Provide a default value if needed
|
68
70
|
}}
|
@@ -96,6 +96,7 @@ const StockItemDetails = forwardRef<never, StockItemDetailsProps>(
|
|
96
96
|
controllerName="drugUuid"
|
97
97
|
control={control}
|
98
98
|
title={t("pleaseSpecify", "Please specify:")}
|
99
|
+
placeholder="Choose a drug"
|
99
100
|
invalid={!!errors.drugUuid}
|
100
101
|
invalidText={errors.drugUuid && errors?.drugUuid?.message}
|
101
102
|
/>
|
@@ -105,7 +106,7 @@ const StockItemDetails = forwardRef<never, StockItemDetailsProps>(
|
|
105
106
|
name="conceptUuid"
|
106
107
|
controllerName="conceptUuid"
|
107
108
|
control={control}
|
108
|
-
title={t("pleaseSpecify", "Please specify:"
|
109
|
+
title={t("pleaseSpecify", "Please specify") + ":"}
|
109
110
|
placeholder={t("chooseAnItem", "Choose an item")}
|
110
111
|
invalid={!!errors.drugUuid}
|
111
112
|
invalidText={errors.drugUuid && errors?.drugUuid?.message}
|
@@ -119,7 +120,7 @@ const StockItemDetails = forwardRef<never, StockItemDetailsProps>(
|
|
119
120
|
maxLength={255}
|
120
121
|
size={"md"}
|
121
122
|
value={`${model?.commonName ?? ""}`}
|
122
|
-
labelText={t("commonName", "Common name:"
|
123
|
+
labelText={t("commonName", "Common name") + ":"}
|
123
124
|
invalid={!!errors.commonName}
|
124
125
|
invalidText={errors.commonName && errors?.commonName?.message}
|
125
126
|
/>
|
@@ -130,7 +131,7 @@ const StockItemDetails = forwardRef<never, StockItemDetailsProps>(
|
|
130
131
|
control={control}
|
131
132
|
controllerName="acronym"
|
132
133
|
size={"md"}
|
133
|
-
labelText={t("abbreviation", "Abbreviation:"
|
134
|
+
labelText={t("abbreviation", "Abbreviation") + ":"}
|
134
135
|
invalid={!!errors.acronym}
|
135
136
|
invalidText={errors.acronym && errors?.acronym?.message}
|
136
137
|
/>
|
@@ -200,7 +201,7 @@ const StockItemDetails = forwardRef<never, StockItemDetailsProps>(
|
|
200
201
|
name="categoryUuid"
|
201
202
|
controllerName="categoryUuid"
|
202
203
|
control={control}
|
203
|
-
title={t("category:", "Category:"
|
204
|
+
title={t("category:", "Category") + ":"}
|
204
205
|
placeholder={t("chooseACategory", "Choose a category")}
|
205
206
|
invalid={!!errors.categoryUuid}
|
206
207
|
invalidText={errors.categoryUuid && errors?.categoryUuid?.message}
|
@@ -210,7 +211,7 @@ const StockItemDetails = forwardRef<never, StockItemDetailsProps>(
|
|
210
211
|
name="dispensingUnitUuid"
|
211
212
|
controllerName="dispensingUnitUuid"
|
212
213
|
control={control}
|
213
|
-
title={t("dispensingUnit", "Dispensing Unit:"
|
214
|
+
title={t("dispensingUnit", "Dispensing Unit") + ":"}
|
214
215
|
placeholder={t("dispensingUnitHolder", "Choose a dispensing unit")}
|
215
216
|
invalid={!!errors.dispensingUnitUuid}
|
216
217
|
invalidText={
|
@@ -90,6 +90,12 @@ export function useStockItems(filter: StockItemFilter) {
|
|
90
90
|
};
|
91
91
|
}
|
92
92
|
|
93
|
+
// fetch filtered stock item
|
94
|
+
export function fetchStockItem(drugUuid: string) {
|
95
|
+
const apiUrl = `${restBaseUrl}/stockmanagement/stockitem?drugUuid=${drugUuid}&limit=1`;
|
96
|
+
return openmrsFetch(apiUrl).then(({ data }) => data);
|
97
|
+
}
|
98
|
+
|
93
99
|
// getStockItemTransactions
|
94
100
|
export function useStockItemTransactions(filter: StockItemTransactionFilter) {
|
95
101
|
const apiUrl = `${restBaseUrl}/stockmanagement/stockitemtransaction${toQueryParams(
|
@@ -282,7 +288,10 @@ export function createStockItemPackagingUnit(item: StockItemPackagingUOMDTO) {
|
|
282
288
|
}
|
283
289
|
|
284
290
|
// updateStockItemPackagingUnit
|
285
|
-
export function updateStockItemPackagingUnit(
|
291
|
+
export function updateStockItemPackagingUnit(
|
292
|
+
item: StockItemPackagingUOMDTO,
|
293
|
+
uuid: string
|
294
|
+
) {
|
286
295
|
const apiUrl = `${restBaseUrl}/stockmanagement/stockitempackaginguom/${uuid}`;
|
287
296
|
const abortController = new AbortController();
|
288
297
|
return openmrsFetch(apiUrl, {
|