@apptimate/ui 3.5.0 → 3.6.0
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/package.json +3 -2
- package/src/base-components/Input.tsx +6 -3
- package/src/base-components/TagsInput.tsx +106 -0
- package/src/base-components/WizardModal.tsx +162 -0
- package/src/common-components/item-wizard/ItemFormWizard.tsx +705 -0
- package/src/common-components/item-wizard/SkuConfigModal.tsx +314 -0
- package/src/common-components/pickers/BrandPicker.tsx +194 -0
- package/src/common-components/pickers/CategoryPicker.tsx +342 -0
- package/src/common-components/pickers/EntityPickerModal.tsx +221 -0
- package/src/common-components/pickers/PartyPicker.tsx +231 -0
- package/src/common-components/pickers/TransactionItemPicker.tsx +166 -0
- package/src/common-components/pickers/UomGroupPicker.tsx +196 -0
- package/src/common-components/pickers/UomPicker.tsx +306 -0
- package/src/common-components/pickers/WarehousePicker.tsx +129 -0
- package/src/common-components/pickers/index.ts +11 -0
- package/src/common-components/pickers/modals/BatchSelectionModal.tsx +359 -0
- package/src/common-components/pickers/modals/SerialSelectionModal.tsx +247 -0
- package/src/common-components/pickers/modals/types.ts +30 -0
- package/src/common-components/transaction/MetadataPanel.tsx +417 -0
- package/src/common-components/transaction/ProductSelectionPanel.tsx +587 -0
- package/src/common-components/transaction/ProductTransactionScreen.tsx +250 -0
- package/src/common-components/transaction/index.ts +17 -0
- package/src/common-components/transaction/types.ts +300 -0
- package/src/components/shared/ImageUploadComponent.tsx +1 -2
- package/src/index.tsx +20 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React, { useState, useCallback } from "react";
|
|
4
|
+
import { Button, Input, Modal, ModalFooter, Select } from "@apptimate/ui";
|
|
5
|
+
import { Plus, Ruler } from "lucide-react";
|
|
6
|
+
import {
|
|
7
|
+
EntityPickerModal,
|
|
8
|
+
PickerItem,
|
|
9
|
+
PickerTrigger,
|
|
10
|
+
} from "./EntityPickerModal";
|
|
11
|
+
import { getUomGroups, createUom } from "@apptimate/core-lib";
|
|
12
|
+
import { UomGroupPicker } from "./UomGroupPicker";
|
|
13
|
+
import toast from "react-hot-toast";
|
|
14
|
+
|
|
15
|
+
interface UomPickerProps {
|
|
16
|
+
value?: number | string | null;
|
|
17
|
+
displayValue?: string | null;
|
|
18
|
+
onChange: (uom: { id: number; name: string; abbreviation: string; uom_group_id?: number } | null) => void;
|
|
19
|
+
label?: string;
|
|
20
|
+
placeholder?: string;
|
|
21
|
+
isRequired?: boolean;
|
|
22
|
+
excludeId?: number | string | null;
|
|
23
|
+
/** If provided, restricts the picker to only show UOMs from this specific group ID */
|
|
24
|
+
uomGroupId?: number | null;
|
|
25
|
+
pickerZIndex?: number;
|
|
26
|
+
baseUnitsOnly?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function UomPicker({
|
|
30
|
+
value,
|
|
31
|
+
displayValue,
|
|
32
|
+
onChange,
|
|
33
|
+
label = "Unit of Measure",
|
|
34
|
+
placeholder = "Select UOM…",
|
|
35
|
+
isRequired = false,
|
|
36
|
+
excludeId,
|
|
37
|
+
uomGroupId,
|
|
38
|
+
pickerZIndex,
|
|
39
|
+
baseUnitsOnly = false,
|
|
40
|
+
}: UomPickerProps) {
|
|
41
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
42
|
+
const [search, setSearch] = useState("");
|
|
43
|
+
const [groups, setGroups] = useState<any[]>([]);
|
|
44
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
45
|
+
|
|
46
|
+
// Quick add
|
|
47
|
+
const [isQuickAddOpen, setIsQuickAddOpen] = useState(false);
|
|
48
|
+
const [quickAddData, setQuickAddData] = useState({
|
|
49
|
+
name: "",
|
|
50
|
+
abbreviation: "",
|
|
51
|
+
conversion_factor: "1",
|
|
52
|
+
uom_group_id: null as number | null,
|
|
53
|
+
uom_group_name: "",
|
|
54
|
+
base_uom_id: null as number | null,
|
|
55
|
+
base_uom_name: "",
|
|
56
|
+
status: "active"
|
|
57
|
+
});
|
|
58
|
+
const [isCreating, setIsCreating] = useState(false);
|
|
59
|
+
|
|
60
|
+
const fetchData = useCallback(async () => {
|
|
61
|
+
setIsLoading(true);
|
|
62
|
+
try {
|
|
63
|
+
const res = await getUomGroups({ include_uoms: true });
|
|
64
|
+
if (res.is_success) {
|
|
65
|
+
setGroups(res.result || []);
|
|
66
|
+
}
|
|
67
|
+
} catch {}
|
|
68
|
+
setIsLoading(false);
|
|
69
|
+
}, []);
|
|
70
|
+
|
|
71
|
+
const handleOpen = () => {
|
|
72
|
+
setSearch("");
|
|
73
|
+
setIsOpen(true);
|
|
74
|
+
fetchData();
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const handleSelect = (uom: any) => {
|
|
78
|
+
onChange({ id: uom.id, name: uom.name, abbreviation: uom.abbreviation, uom_group_id: uom.uom_group_id });
|
|
79
|
+
setIsOpen(false);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const handleClear = () => {
|
|
83
|
+
onChange(null);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const handleQuickAdd = async () => {
|
|
87
|
+
if (!quickAddData.name.trim() || !quickAddData.abbreviation.trim() || !quickAddData.uom_group_id) {
|
|
88
|
+
toast.error("Please fill all required fields");
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
setIsCreating(true);
|
|
92
|
+
try {
|
|
93
|
+
const res = await createUom({
|
|
94
|
+
name: quickAddData.name.trim(),
|
|
95
|
+
abbreviation: quickAddData.abbreviation.trim(),
|
|
96
|
+
conversion_factor: parseFloat(quickAddData.conversion_factor) || 1,
|
|
97
|
+
uom_group_id: quickAddData.uom_group_id,
|
|
98
|
+
base_uom_id: quickAddData.base_uom_id || null,
|
|
99
|
+
status: quickAddData.status,
|
|
100
|
+
});
|
|
101
|
+
if (res.is_success && res.result) {
|
|
102
|
+
toast.success("UOM created");
|
|
103
|
+
onChange({ id: res.result.id, name: res.result.name, abbreviation: res.result.abbreviation, uom_group_id: res.result.uom_group_id });
|
|
104
|
+
setIsQuickAddOpen(false);
|
|
105
|
+
setQuickAddData({ name: "", abbreviation: "", conversion_factor: "1", uom_group_id: null, uom_group_name: "", base_uom_id: null, base_uom_name: "", status: "active" });
|
|
106
|
+
setIsOpen(false);
|
|
107
|
+
} else {
|
|
108
|
+
toast.error(res.message || "Failed to create");
|
|
109
|
+
}
|
|
110
|
+
} catch (e: any) {
|
|
111
|
+
toast.error(e.message || "Error creating UOM");
|
|
112
|
+
}
|
|
113
|
+
setIsCreating(false);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const searchLower = search.toLowerCase();
|
|
117
|
+
|
|
118
|
+
const filteredGroups = groups
|
|
119
|
+
.filter(g => !uomGroupId || String(g.id) === String(uomGroupId))
|
|
120
|
+
.map(g => {
|
|
121
|
+
const matchedUoms = (g.uoms || [])
|
|
122
|
+
.filter((u: any) => !excludeId || String(u.id) !== String(excludeId))
|
|
123
|
+
.filter((u: any) => !baseUnitsOnly || !u.base_uom_id)
|
|
124
|
+
.filter((u: any) =>
|
|
125
|
+
u.name.toLowerCase().includes(searchLower) || u.abbreviation.toLowerCase().includes(searchLower)
|
|
126
|
+
);
|
|
127
|
+
return { ...g, uoms: matchedUoms };
|
|
128
|
+
})
|
|
129
|
+
.filter(g => g.uoms.length > 0 || g.name.toLowerCase().includes(searchLower));
|
|
130
|
+
|
|
131
|
+
return (
|
|
132
|
+
<>
|
|
133
|
+
<PickerTrigger
|
|
134
|
+
label={label}
|
|
135
|
+
value={displayValue || null}
|
|
136
|
+
placeholder={placeholder}
|
|
137
|
+
isRequired={isRequired}
|
|
138
|
+
onClick={handleOpen}
|
|
139
|
+
onClear={value ? handleClear : undefined}
|
|
140
|
+
/>
|
|
141
|
+
|
|
142
|
+
<EntityPickerModal
|
|
143
|
+
isOpen={isOpen}
|
|
144
|
+
onClose={() => setIsOpen(false)}
|
|
145
|
+
onSelect={handleSelect}
|
|
146
|
+
title="Select Unit of Measure"
|
|
147
|
+
searchPlaceholder="Search UOMs…"
|
|
148
|
+
selectedId={value}
|
|
149
|
+
search={search}
|
|
150
|
+
onSearchChange={setSearch}
|
|
151
|
+
size="sm"
|
|
152
|
+
zIndex={pickerZIndex}
|
|
153
|
+
headerActions={
|
|
154
|
+
<button
|
|
155
|
+
type="button"
|
|
156
|
+
onClick={() => { setIsQuickAddOpen(true); setQuickAddData({ name: "", abbreviation: "", conversion_factor: "1", uom_group_id: null, uom_group_name: "", base_uom_id: null, base_uom_name: "", status: "active" }); }}
|
|
157
|
+
className="p-1.5 rounded-lg bg-primary-50 text-primary-600 hover:bg-primary-100 transition-colors flex-shrink-0"
|
|
158
|
+
title="Quick Add UOM"
|
|
159
|
+
>
|
|
160
|
+
<Plus size={16} strokeWidth={2.5} />
|
|
161
|
+
</button>
|
|
162
|
+
}
|
|
163
|
+
>
|
|
164
|
+
{isLoading ? (
|
|
165
|
+
<div className="py-12 text-center">
|
|
166
|
+
<div className="inline-block h-6 w-6 rounded-full border-2 border-gray-200 border-t-primary-500 animate-spin" />
|
|
167
|
+
<p className="mt-3 text-sm text-gray-400">Loading UOMs…</p>
|
|
168
|
+
</div>
|
|
169
|
+
) : filteredGroups.length === 0 ? (
|
|
170
|
+
<div className="py-12 text-center">
|
|
171
|
+
<Ruler size={32} className="mx-auto text-gray-300 mb-2" />
|
|
172
|
+
<p className="text-sm text-gray-400">No units of measure found</p>
|
|
173
|
+
</div>
|
|
174
|
+
) : (
|
|
175
|
+
<div className="space-y-4 py-1">
|
|
176
|
+
{filteredGroups.map(group => (
|
|
177
|
+
<div key={group.id} className="space-y-1">
|
|
178
|
+
<div className="px-3 py-1 text-xs font-semibold text-gray-500 uppercase tracking-wider bg-gray-50/50">
|
|
179
|
+
{group.name}
|
|
180
|
+
</div>
|
|
181
|
+
{group.uoms.map((uom: any) => (
|
|
182
|
+
<PickerItem
|
|
183
|
+
key={uom.id}
|
|
184
|
+
label={uom.name}
|
|
185
|
+
sublabel={uom.abbreviation}
|
|
186
|
+
isSelected={String(value) === String(uom.id)}
|
|
187
|
+
onClick={() => handleSelect(uom)}
|
|
188
|
+
/>
|
|
189
|
+
))}
|
|
190
|
+
</div>
|
|
191
|
+
))}
|
|
192
|
+
</div>
|
|
193
|
+
)}
|
|
194
|
+
</EntityPickerModal>
|
|
195
|
+
|
|
196
|
+
{/* Quick Add Modal */}
|
|
197
|
+
<Modal
|
|
198
|
+
isOpen={isQuickAddOpen}
|
|
199
|
+
onClose={() => setIsQuickAddOpen(false)}
|
|
200
|
+
title="New UoM"
|
|
201
|
+
size="md"
|
|
202
|
+
zIndex={(pickerZIndex || 200) + 100}
|
|
203
|
+
>
|
|
204
|
+
<div className="space-y-4">
|
|
205
|
+
<UomGroupPicker
|
|
206
|
+
label="UOM Group"
|
|
207
|
+
isRequired
|
|
208
|
+
value={quickAddData.uom_group_id}
|
|
209
|
+
displayValue={quickAddData.uom_group_name || null}
|
|
210
|
+
pickerZIndex={400}
|
|
211
|
+
onChange={(g) => {
|
|
212
|
+
setQuickAddData({
|
|
213
|
+
...quickAddData,
|
|
214
|
+
uom_group_id: g?.id ?? null,
|
|
215
|
+
uom_group_name: g?.name ?? "",
|
|
216
|
+
base_uom_id: null,
|
|
217
|
+
base_uom_name: "",
|
|
218
|
+
conversion_factor: "1"
|
|
219
|
+
});
|
|
220
|
+
}}
|
|
221
|
+
/>
|
|
222
|
+
<div className="grid grid-cols-2 gap-4">
|
|
223
|
+
<Input
|
|
224
|
+
label="Code Name"
|
|
225
|
+
isRequired
|
|
226
|
+
placeholder="e.g. Kilogram"
|
|
227
|
+
value={quickAddData.name}
|
|
228
|
+
onChange={(e) => setQuickAddData({ ...quickAddData, name: e.target.value })}
|
|
229
|
+
/>
|
|
230
|
+
<Input
|
|
231
|
+
label="Code"
|
|
232
|
+
isRequired
|
|
233
|
+
placeholder="e.g. KG"
|
|
234
|
+
value={quickAddData.abbreviation}
|
|
235
|
+
onChange={(e) => setQuickAddData({ ...quickAddData, abbreviation: e.target.value })}
|
|
236
|
+
/>
|
|
237
|
+
</div>
|
|
238
|
+
{!baseUnitsOnly && (
|
|
239
|
+
<UomPicker
|
|
240
|
+
label="Base UOM"
|
|
241
|
+
placeholder="Select base unit…"
|
|
242
|
+
value={quickAddData.base_uom_id}
|
|
243
|
+
displayValue={quickAddData.base_uom_name || null}
|
|
244
|
+
pickerZIndex={400}
|
|
245
|
+
onChange={(uom) => {
|
|
246
|
+
let newGroupId = quickAddData.uom_group_id;
|
|
247
|
+
let newGroupName = quickAddData.uom_group_name;
|
|
248
|
+
|
|
249
|
+
if (uom?.uom_group_id && uom.uom_group_id !== quickAddData.uom_group_id) {
|
|
250
|
+
newGroupId = uom.uom_group_id;
|
|
251
|
+
const foundGroup = groups.find(g => g.id === uom.uom_group_id);
|
|
252
|
+
if (foundGroup) newGroupName = foundGroup.name;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
setQuickAddData({
|
|
256
|
+
...quickAddData,
|
|
257
|
+
base_uom_id: uom?.id ?? null,
|
|
258
|
+
base_uom_name: uom?.name ?? "",
|
|
259
|
+
uom_group_id: newGroupId,
|
|
260
|
+
uom_group_name: newGroupName,
|
|
261
|
+
});
|
|
262
|
+
}}
|
|
263
|
+
uomGroupId={quickAddData.uom_group_id}
|
|
264
|
+
/>
|
|
265
|
+
)}
|
|
266
|
+
<div className="grid grid-cols-2 gap-4">
|
|
267
|
+
{!baseUnitsOnly && (
|
|
268
|
+
<Input
|
|
269
|
+
label="Conversion Factor"
|
|
270
|
+
isRequired
|
|
271
|
+
type="number"
|
|
272
|
+
placeholder="1"
|
|
273
|
+
value={quickAddData.conversion_factor}
|
|
274
|
+
onChange={(e) => setQuickAddData({ ...quickAddData, conversion_factor: e.target.value })}
|
|
275
|
+
/>
|
|
276
|
+
)}
|
|
277
|
+
<Select
|
|
278
|
+
label="Status"
|
|
279
|
+
value={quickAddData.status}
|
|
280
|
+
onChange={(e) => setQuickAddData({ ...quickAddData, status: e.target.value })}
|
|
281
|
+
options={[
|
|
282
|
+
{ value: "active", label: "Active" },
|
|
283
|
+
{ value: "inactive", label: "Inactive" },
|
|
284
|
+
]}
|
|
285
|
+
/>
|
|
286
|
+
</div>
|
|
287
|
+
<ModalFooter>
|
|
288
|
+
<Button
|
|
289
|
+
variant="flat"
|
|
290
|
+
color="default"
|
|
291
|
+
onClick={() => setIsQuickAddOpen(false)}
|
|
292
|
+
>
|
|
293
|
+
Cancel
|
|
294
|
+
</Button>
|
|
295
|
+
<Button
|
|
296
|
+
onClick={handleQuickAdd}
|
|
297
|
+
isDisabled={isCreating || !quickAddData.name.trim() || !quickAddData.abbreviation.trim() || !quickAddData.uom_group_id}
|
|
298
|
+
>
|
|
299
|
+
{isCreating ? "Creating…" : "Create & Select"}
|
|
300
|
+
</Button>
|
|
301
|
+
</ModalFooter>
|
|
302
|
+
</div>
|
|
303
|
+
</Modal>
|
|
304
|
+
</>
|
|
305
|
+
);
|
|
306
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React, { useState, useCallback } from "react";
|
|
4
|
+
import { Button, Input, Modal, ModalFooter } from "@apptimate/ui";
|
|
5
|
+
import { Plus, Warehouse } from "lucide-react";
|
|
6
|
+
import {
|
|
7
|
+
EntityPickerModal,
|
|
8
|
+
PickerItem,
|
|
9
|
+
PickerTrigger,
|
|
10
|
+
} from "./EntityPickerModal";
|
|
11
|
+
import { getWarehousesLookup } from "@apptimate/core-lib";
|
|
12
|
+
import toast from "react-hot-toast";
|
|
13
|
+
|
|
14
|
+
interface WarehousePickerProps {
|
|
15
|
+
value?: number | string | null;
|
|
16
|
+
displayValue?: string | null;
|
|
17
|
+
onChange: (warehouse: { id: number; name: string; code?: string; organization_id?: number } | null) => void;
|
|
18
|
+
label?: string;
|
|
19
|
+
placeholder?: string;
|
|
20
|
+
isRequired?: boolean;
|
|
21
|
+
fetchFromAllUserOrgs?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function WarehousePicker({
|
|
25
|
+
value,
|
|
26
|
+
displayValue,
|
|
27
|
+
onChange,
|
|
28
|
+
label = "Warehouse",
|
|
29
|
+
placeholder = "Select warehouse…",
|
|
30
|
+
isRequired = false,
|
|
31
|
+
fetchFromAllUserOrgs = false,
|
|
32
|
+
}: WarehousePickerProps) {
|
|
33
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
34
|
+
const [search, setSearch] = useState("");
|
|
35
|
+
const [warehouses, setWarehouses] = useState<any[]>([]);
|
|
36
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
37
|
+
|
|
38
|
+
const fetchData = useCallback(async () => {
|
|
39
|
+
setIsLoading(true);
|
|
40
|
+
try {
|
|
41
|
+
const res = await getWarehousesLookup(fetchFromAllUserOrgs);
|
|
42
|
+
if (res.is_success) {
|
|
43
|
+
setWarehouses(res.result || []);
|
|
44
|
+
// Auto-select if there is exactly 1 warehouse and no value is currently selected
|
|
45
|
+
if (res.result?.length === 1 && !value) {
|
|
46
|
+
onChange({ id: res.result[0].id, name: res.result[0].name, code: res.result[0].code, organization_id: res.result[0].organization_id });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
} catch {}
|
|
50
|
+
setIsLoading(false);
|
|
51
|
+
}, [value, onChange, fetchFromAllUserOrgs]);
|
|
52
|
+
|
|
53
|
+
// Fetch on mount to pre-populate and auto-select if applicable
|
|
54
|
+
React.useEffect(() => {
|
|
55
|
+
fetchData();
|
|
56
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
57
|
+
}, []);
|
|
58
|
+
|
|
59
|
+
const handleOpen = () => {
|
|
60
|
+
setSearch("");
|
|
61
|
+
setIsOpen(true);
|
|
62
|
+
fetchData();
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const handleSelect = (wh: any) => {
|
|
66
|
+
onChange({ id: wh.id, name: wh.name, code: wh.code, organization_id: wh.organization_id });
|
|
67
|
+
setIsOpen(false);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const handleClear = () => {
|
|
71
|
+
onChange(null);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const searchLower = (search || "").toLowerCase();
|
|
75
|
+
const filtered = warehouses.filter(
|
|
76
|
+
(w) =>
|
|
77
|
+
(w.name || "").toLowerCase().includes(searchLower) ||
|
|
78
|
+
(w.code || "").toLowerCase().includes(searchLower)
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<>
|
|
83
|
+
<PickerTrigger
|
|
84
|
+
label={label}
|
|
85
|
+
value={displayValue || null}
|
|
86
|
+
placeholder={placeholder}
|
|
87
|
+
isRequired={isRequired}
|
|
88
|
+
onClick={handleOpen}
|
|
89
|
+
onClear={value ? handleClear : undefined}
|
|
90
|
+
/>
|
|
91
|
+
|
|
92
|
+
<EntityPickerModal
|
|
93
|
+
isOpen={isOpen}
|
|
94
|
+
onClose={() => setIsOpen(false)}
|
|
95
|
+
onSelect={handleSelect}
|
|
96
|
+
title={`Select ${label}`}
|
|
97
|
+
searchPlaceholder={`Search warehouses…`}
|
|
98
|
+
selectedId={value}
|
|
99
|
+
search={search}
|
|
100
|
+
onSearchChange={setSearch}
|
|
101
|
+
size="sm"
|
|
102
|
+
>
|
|
103
|
+
{isLoading ? (
|
|
104
|
+
<div className="py-12 text-center">
|
|
105
|
+
<div className="inline-block h-6 w-6 rounded-full border-2 border-gray-200 border-t-primary-500 animate-spin" />
|
|
106
|
+
<p className="mt-3 text-sm text-gray-400">Loading warehouses…</p>
|
|
107
|
+
</div>
|
|
108
|
+
) : filtered.length === 0 ? (
|
|
109
|
+
<div className="py-12 text-center">
|
|
110
|
+
<Warehouse size={32} className="mx-auto text-gray-300 mb-2" />
|
|
111
|
+
<p className="text-sm text-gray-400">No warehouses found</p>
|
|
112
|
+
</div>
|
|
113
|
+
) : (
|
|
114
|
+
<div className="space-y-0.5 py-1">
|
|
115
|
+
{filtered.map((wh) => (
|
|
116
|
+
<PickerItem
|
|
117
|
+
key={wh.id}
|
|
118
|
+
label={wh.name}
|
|
119
|
+
sublabel={wh.code ? `Code: ${wh.code}` : undefined}
|
|
120
|
+
isSelected={String(value) === String(wh.id)}
|
|
121
|
+
onClick={() => handleSelect(wh)}
|
|
122
|
+
/>
|
|
123
|
+
))}
|
|
124
|
+
</div>
|
|
125
|
+
)}
|
|
126
|
+
</EntityPickerModal>
|
|
127
|
+
</>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './BrandPicker';
|
|
2
|
+
export * from './CategoryPicker';
|
|
3
|
+
export * from './EntityPickerModal';
|
|
4
|
+
export * from './UomGroupPicker';
|
|
5
|
+
export * from './UomPicker';
|
|
6
|
+
export * from './PartyPicker';
|
|
7
|
+
export * from './WarehousePicker';
|
|
8
|
+
export * from './TransactionItemPicker';
|
|
9
|
+
export * from './modals/BatchSelectionModal';
|
|
10
|
+
export * from './modals/SerialSelectionModal';
|
|
11
|
+
export * from './modals/types';
|