@apptimate/ui 6.0.0 → 6.1.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 +1 -1
- package/src/common-components/item-wizard/QuickItemWizard.tsx +86 -0
- package/src/common-components/transaction/ProductSelectionPanel.tsx +19 -13
- package/src/common-components/transaction/ProductTransactionScreen.tsx +2 -0
- package/src/common-components/transaction/types.ts +2 -0
- package/src/index.tsx +1 -0
package/package.json
CHANGED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect } from "react";
|
|
4
|
+
import ItemFormWizard from "./ItemFormWizard";
|
|
5
|
+
import { emptyFormData } from "./ItemFormWizard";
|
|
6
|
+
import { createItem } from "@apptimate/core-lib";
|
|
7
|
+
import toast from "react-hot-toast";
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
isOpen: boolean;
|
|
11
|
+
onClose: () => void;
|
|
12
|
+
onSuccess?: (newItem: any) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function QuickItemWizard({ isOpen, onClose, onSuccess }: Props) {
|
|
16
|
+
const [formData, setFormData] = useState<any>(emptyFormData);
|
|
17
|
+
const [uoms, setUoms] = useState<any[]>([]);
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (isOpen) {
|
|
21
|
+
setFormData(emptyFormData);
|
|
22
|
+
fetchLookups();
|
|
23
|
+
}
|
|
24
|
+
}, [isOpen]);
|
|
25
|
+
|
|
26
|
+
const fetchLookups = async () => {
|
|
27
|
+
try {
|
|
28
|
+
const { lookupUoms } = await import("@apptimate/core-lib");
|
|
29
|
+
const uomRes = await lookupUoms();
|
|
30
|
+
if (uomRes.is_success) setUoms(uomRes.result || []);
|
|
31
|
+
} catch { }
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const handleSubmit = async () => {
|
|
35
|
+
try {
|
|
36
|
+
if (!formData.name || !formData.sku || !formData.category_id || !formData.uom_id) {
|
|
37
|
+
toast.error("Please fill required fields (Name, SKU, Category, UOM)");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const payload: any = {
|
|
42
|
+
...formData,
|
|
43
|
+
sale_price: parseFloat(formData.sale_price) || 0,
|
|
44
|
+
cost_price: parseFloat(formData.cost_price) || 0,
|
|
45
|
+
min_stock_level: formData.min_stock_level ? parseFloat(formData.min_stock_level) : null,
|
|
46
|
+
max_stock_level: formData.max_stock_level ? parseFloat(formData.max_stock_level) : null,
|
|
47
|
+
reorder_point: formData.reorder_point ? parseFloat(formData.reorder_point) : null,
|
|
48
|
+
reorder_qty: formData.reorder_qty ? parseFloat(formData.reorder_qty) : null,
|
|
49
|
+
lead_time_days: formData.lead_time_days ? parseInt(formData.lead_time_days) : null,
|
|
50
|
+
weight: formData.weight ? parseFloat(formData.weight) : null,
|
|
51
|
+
variants: formData.has_variants ? formData.variants.map((v: any) => ({
|
|
52
|
+
...v, sale_price: v.sale_price ? parseFloat(v.sale_price) : null,
|
|
53
|
+
cost_price: v.cost_price ? parseFloat(v.cost_price) : null,
|
|
54
|
+
})) : undefined,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
delete payload.category_name;
|
|
58
|
+
delete payload.brand_name;
|
|
59
|
+
|
|
60
|
+
const res = await createItem(payload);
|
|
61
|
+
if (res.is_success) {
|
|
62
|
+
toast.success(res.message || "Item created successfully");
|
|
63
|
+
onClose();
|
|
64
|
+
if (onSuccess && res.result) {
|
|
65
|
+
onSuccess(res.result);
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
toast.error(res.message || "Failed to create item");
|
|
69
|
+
}
|
|
70
|
+
} catch (e: any) {
|
|
71
|
+
toast.error(e.message || "An error occurred while creating item");
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<ItemFormWizard
|
|
77
|
+
isOpen={isOpen}
|
|
78
|
+
onClose={onClose}
|
|
79
|
+
formData={formData}
|
|
80
|
+
setFormData={setFormData}
|
|
81
|
+
uoms={uoms}
|
|
82
|
+
onSubmit={handleSubmit}
|
|
83
|
+
isEditing={false}
|
|
84
|
+
/>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
|
4
4
|
import { cn } from '@apptimate/core-lib';
|
|
5
5
|
import {
|
|
6
|
-
Search, ScanBarcode, X, ShoppingCart, Package, Loader2,
|
|
6
|
+
Search, ScanBarcode, X, ShoppingCart, Package, Loader2, Plus,
|
|
7
7
|
Trash2, ChevronDown, ChevronRight, AlertTriangle, Tag
|
|
8
8
|
} from 'lucide-react';
|
|
9
9
|
import { Badge } from '../../base-components/Badge';
|
|
@@ -24,6 +24,7 @@ interface ProductSelectionPanelProps {
|
|
|
24
24
|
onUpdateLine: (uid: string, updates: Partial<TransactionLineItem>) => void;
|
|
25
25
|
onRemoveLine: (uid: string) => void;
|
|
26
26
|
onToggleExpand: (uid: string) => void;
|
|
27
|
+
onAddNewItem?: (callbacks: { setItemSearch: (value: string) => void }) => void;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
export function ProductSelectionPanel({
|
|
@@ -35,6 +36,7 @@ export function ProductSelectionPanel({
|
|
|
35
36
|
onUpdateLine,
|
|
36
37
|
onRemoveLine,
|
|
37
38
|
onToggleExpand,
|
|
39
|
+
onAddNewItem,
|
|
38
40
|
}: ProductSelectionPanelProps) {
|
|
39
41
|
const [searchValue, setSearchValue] = useState('');
|
|
40
42
|
const [searchResults, setSearchResults] = useState<TransactionItem[]>([]);
|
|
@@ -400,18 +402,22 @@ export function ProductSelectionPanel({
|
|
|
400
402
|
</button>
|
|
401
403
|
}
|
|
402
404
|
/>
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
405
|
+
{onAddNewItem && (
|
|
406
|
+
<>
|
|
407
|
+
<div className="w-[1px] h-5 bg-border-subtle mx-1" />
|
|
408
|
+
<button
|
|
409
|
+
type="button"
|
|
410
|
+
onClick={() => onAddNewItem({ setItemSearch: handleSearch })}
|
|
411
|
+
title="Create new item"
|
|
412
|
+
className="flex items-center justify-center outline-none mr-1"
|
|
413
|
+
>
|
|
414
|
+
<Plus
|
|
415
|
+
size={18}
|
|
416
|
+
className="text-foreground-disabled shrink-0 cursor-pointer hover:text-primary transition-colors"
|
|
417
|
+
/>
|
|
418
|
+
</button>
|
|
419
|
+
</>
|
|
420
|
+
)}
|
|
415
421
|
</div>
|
|
416
422
|
</div>
|
|
417
423
|
|
|
@@ -30,6 +30,7 @@ export function ProductTransactionScreen({
|
|
|
30
30
|
renderExtraMeta,
|
|
31
31
|
fetchChequeLeaves,
|
|
32
32
|
initialData,
|
|
33
|
+
onAddNewItem,
|
|
33
34
|
}: ProductTransactionScreenProps) {
|
|
34
35
|
// ── State ──
|
|
35
36
|
const [lineItems, setLineItems] = useState<TransactionLineItem[]>([]);
|
|
@@ -469,6 +470,7 @@ export function ProductTransactionScreen({
|
|
|
469
470
|
onUpdateLine={handleUpdateLine}
|
|
470
471
|
onRemoveLine={handleRemoveLine}
|
|
471
472
|
onToggleExpand={handleToggleExpand}
|
|
473
|
+
onAddNewItem={onAddNewItem}
|
|
472
474
|
/>
|
|
473
475
|
</div>
|
|
474
476
|
|
|
@@ -316,6 +316,8 @@ export interface ProductTransactionScreenProps {
|
|
|
316
316
|
fetchChequeLeaves?: (query: string, page: number) => Promise<{ is_success: boolean; result?: any[] }>;
|
|
317
317
|
/** Initial data for populating the transaction screen (e.g., from a PO) */
|
|
318
318
|
initialData?: any;
|
|
319
|
+
/** Optional callback to create a new item directly from the search bar */
|
|
320
|
+
onAddNewItem?: (callbacks: { setItemSearch: (value: string) => void }) => void;
|
|
319
321
|
}
|
|
320
322
|
|
|
321
323
|
export interface TransactionPayload {
|
package/src/index.tsx
CHANGED
|
@@ -54,6 +54,7 @@ export * from './common-components/pickers/WarehousePicker';
|
|
|
54
54
|
// Item Wizard
|
|
55
55
|
export { default as ItemFormWizard } from './common-components/item-wizard/ItemFormWizard';
|
|
56
56
|
export * from './common-components/item-wizard/ItemFormWizard';
|
|
57
|
+
export * from './common-components/item-wizard/QuickItemWizard';
|
|
57
58
|
export { default as SkuConfigModal } from './common-components/item-wizard/SkuConfigModal';
|
|
58
59
|
export * from './common-components/item-wizard/SkuConfigModal';
|
|
59
60
|
|