@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apptimate/ui",
3
- "version": "6.0.0",
3
+ "version": "6.1.0",
4
4
  "main": "src/index.tsx",
5
5
  "types": "src/index.tsx",
6
6
  "dependencies": {
@@ -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
- <div className="w-[1px] h-5 bg-border-subtle mx-1" />
404
- <button
405
- type="button"
406
- onClick={() => inputRef.current?.focus()}
407
- title="Click to focus input for barcode scanning"
408
- className="flex items-center justify-center outline-none"
409
- >
410
- <ScanBarcode
411
- size={18}
412
- className="text-foreground-disabled shrink-0 cursor-pointer hover:text-primary transition-colors"
413
- />
414
- </button>
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