@lodashventure/medusa-quotation 1.4.17 → 1.4.18

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.
@@ -2444,77 +2444,160 @@ const QuoteDetails = () => {
2444
2444
  /* @__PURE__ */ jsxRuntime.jsx(ui.Toaster, {})
2445
2445
  ] });
2446
2446
  };
2447
- const columnHelper$1 = ui.createDataTableColumnHelper();
2448
- const columns$1 = [
2449
- columnHelper$1.accessor("email", {
2450
- header: "Email",
2451
- cell: (info) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "large", className: "py-2", children: info.getValue() })
2452
- }),
2453
- columnHelper$1.accessor("first_name", {
2454
- header: "First Name",
2455
- cell: (info) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "large", className: "py-2", children: info.getValue() })
2456
- }),
2457
- columnHelper$1.accessor("last_name", {
2458
- header: "Last Name",
2459
- cell: (info) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "large", className: "py-2", children: info.getValue() })
2460
- }),
2461
- columnHelper$1.accessor("company_name", {
2462
- header: "Company",
2463
- cell: (info) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "large", className: "py-2", children: info.getValue() || "-" })
2464
- })
2465
- ];
2466
- const CustomerSelector = ({
2467
- onSelectCustomer
2468
- }) => {
2469
- const [search, setSearch] = react.useState("");
2470
- const [data, setData] = react.useState(null);
2471
- const [isLoading, setIsLoading] = react.useState(false);
2472
- const debouncedSearchHandler = lodash.debounce((value) => {
2473
- setSearch(value);
2474
- }, 500);
2475
- react.useEffect(() => {
2476
- const fetchCustomers = async () => {
2477
- try {
2478
- setIsLoading(true);
2479
- const response = await sdk.admin.customer.list({
2480
- q: search,
2481
- limit: 20
2447
+ function ManageItem({
2448
+ originalItem,
2449
+ item,
2450
+ currencyCode,
2451
+ orderId
2452
+ }) {
2453
+ const { mutateAsync: updateItem } = useUpdateQuoteItem(orderId);
2454
+ const { mutateAsync: removeItem } = useRemoveQuoteItem(orderId);
2455
+ const isItemUpdated = react.useMemo(
2456
+ () => {
2457
+ var _a;
2458
+ return !!((_a = item.actions) == null ? void 0 : _a.find((a) => a.action === "ITEM_UPDATE"));
2459
+ },
2460
+ [item]
2461
+ );
2462
+ const onUpdate = async ({
2463
+ quantity,
2464
+ unit_price
2465
+ }) => {
2466
+ if (typeof quantity === "number" && quantity <= item.detail.fulfilled_quantity) {
2467
+ ui.toast.warning("Quantity should be greater than the fulfilled quantity");
2468
+ return;
2469
+ }
2470
+ try {
2471
+ await updateItem({
2472
+ quantity,
2473
+ unit_price,
2474
+ itemId: item.id
2475
+ });
2476
+ } catch (e) {
2477
+ ui.toast.error(e.message);
2478
+ }
2479
+ };
2480
+ const onRemove = async () => {
2481
+ var _a;
2482
+ try {
2483
+ const addAction = (_a = item.actions) == null ? void 0 : _a.find((a) => a.action === "ITEM_ADD");
2484
+ if (addAction == null ? void 0 : addAction.id) {
2485
+ await removeItem({
2486
+ itemId: addAction.id
2487
+ });
2488
+ } else {
2489
+ ui.toast.warning("Cannot remove item, set quantity to 0 instead");
2490
+ await updateItem({
2491
+ quantity: 0,
2492
+ itemId: item.id
2482
2493
  });
2483
- setData(response);
2484
- } catch (error) {
2485
- console.error("Error fetching customers:", error);
2486
- } finally {
2487
- setIsLoading(false);
2488
2494
  }
2489
- };
2490
- fetchCustomers();
2491
- }, [search]);
2492
- const customers = (data == null ? void 0 : data.customers) || [];
2493
- const table = ui.useDataTable({
2494
- data: customers,
2495
- columns: columns$1,
2496
- search: {
2497
- state: search,
2498
- onSearchChange: setSearch
2499
- },
2500
- getRowId: (customer) => customer.id,
2501
- onRowClick: (_, row) => {
2502
- onSelectCustomer == null ? void 0 : onSelectCustomer(row.original);
2495
+ } catch (e) {
2496
+ ui.toast.error(e.message);
2497
+ }
2498
+ };
2499
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2500
+ "div",
2501
+ {
2502
+ className: "bg-ui-bg-subtle shadow-elevation-card-rest my-2 rounded-xl ",
2503
+ children: [
2504
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-x-2 gap-y-2 p-3 text-sm md:flex-row", children: [
2505
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 items-center justify-between", children: [
2506
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-row items-center gap-x-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
2507
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2508
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "txt-small", as: "span", weight: "plus", children: [
2509
+ item.title,
2510
+ " "
2511
+ ] }),
2512
+ item.variant_sku && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2513
+ "(",
2514
+ item.variant_sku,
2515
+ ")"
2516
+ ] })
2517
+ ] }),
2518
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "div", className: "text-ui-fg-subtle txt-small", children: item.product_title })
2519
+ ] }) }),
2520
+ isItemUpdated && /* @__PURE__ */ jsxRuntime.jsx(
2521
+ ui.Badge,
2522
+ {
2523
+ size: "2xsmall",
2524
+ rounded: "full",
2525
+ color: "orange",
2526
+ className: "mr-1",
2527
+ children: "Modified"
2528
+ }
2529
+ )
2530
+ ] }),
2531
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 justify-between", children: [
2532
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-grow items-center gap-2", children: [
2533
+ /* @__PURE__ */ jsxRuntime.jsx(
2534
+ ui.Input,
2535
+ {
2536
+ className: "bg-ui-bg-base txt-small w-[67px] rounded-lg [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none",
2537
+ type: "number",
2538
+ min: item.detail.fulfilled_quantity,
2539
+ defaultValue: item.quantity,
2540
+ onBlur: (e) => {
2541
+ const val = e.target.value;
2542
+ const quantity = val === "" ? null : Number(val);
2543
+ if (quantity) {
2544
+ onUpdate({ quantity });
2545
+ }
2546
+ }
2547
+ }
2548
+ ),
2549
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "txt-small text-ui-fg-subtle", children: "Quantity" })
2550
+ ] }),
2551
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-ui-fg-subtle txt-small mr-2 flex flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
2552
+ Amount,
2553
+ {
2554
+ currencyCode,
2555
+ amount: item.total,
2556
+ originalAmount: originalItem == null ? void 0 : originalItem.total
2557
+ }
2558
+ ) })
2559
+ ] })
2560
+ ] }),
2561
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-2 p-3 md:grid-cols-2", children: [
2562
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
2563
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Price" }),
2564
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Hint, { className: "!mt-1", children: "Override the unit price of this product" })
2565
+ ] }),
2566
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-grow", children: /* @__PURE__ */ jsxRuntime.jsx(
2567
+ ui.CurrencyInput,
2568
+ {
2569
+ symbol: currencyCode,
2570
+ code: currencyCode,
2571
+ defaultValue: item.unit_price,
2572
+ type: "numeric",
2573
+ min: 0,
2574
+ onBlur: (e) => {
2575
+ onUpdate({
2576
+ unit_price: parseFloat(e.target.value),
2577
+ quantity: item.quantity
2578
+ });
2579
+ },
2580
+ className: "bg-ui-bg-field-component hover:bg-ui-bg-field-component-hover"
2581
+ }
2582
+ ) }) })
2583
+ ] }),
2584
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center px-3 pb-3", children: /* @__PURE__ */ jsxRuntime.jsx(
2585
+ ui.Button,
2586
+ {
2587
+ type: "button",
2588
+ variant: "danger",
2589
+ size: "small",
2590
+ onClick: () => {
2591
+ onRemove();
2592
+ },
2593
+ children: "Remove"
2594
+ }
2595
+ ) })
2596
+ ]
2503
2597
  },
2504
- isLoading
2505
- });
2506
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { children: [
2507
- /* @__PURE__ */ jsxRuntime.jsx(
2508
- ui.Input,
2509
- {
2510
- className: "text-lg py-2 mb-4",
2511
- placeholder: "Search customers by email, name...",
2512
- onChange: (e) => debouncedSearchHandler(e.target.value)
2513
- }
2514
- ),
2515
- /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable, { instance: table, children: /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Table, {}) })
2516
- ] });
2517
- };
2598
+ item.quantity
2599
+ );
2600
+ }
2518
2601
  const useProductSearch = (search, selectedProductIds) => {
2519
2602
  const [data, setData] = react.useState({ variants: [] });
2520
2603
  const [isLoading, setIsLoading] = react.useState(false);
@@ -2566,21 +2649,21 @@ const useProductSearch = (search, selectedProductIds) => {
2566
2649
  }, [search, selectedProductIds.join(",")]);
2567
2650
  return { data, isLoading, error };
2568
2651
  };
2569
- const columnHelper = ui.createDataTableColumnHelper();
2570
- const columns = [
2571
- columnHelper.accessor("product.title", {
2652
+ const columnHelper$1 = ui.createDataTableColumnHelper();
2653
+ const columns$1 = [
2654
+ columnHelper$1.accessor("product.title", {
2572
2655
  header: "Product",
2573
2656
  cell: (info) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "large", className: "py-2", children: info.getValue() })
2574
2657
  }),
2575
- columnHelper.accessor("title", {
2658
+ columnHelper$1.accessor("title", {
2576
2659
  header: "Variant",
2577
2660
  cell: (info) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "large", className: "py-2", children: info.getValue() })
2578
2661
  }),
2579
- columnHelper.accessor("sku", {
2662
+ columnHelper$1.accessor("sku", {
2580
2663
  header: "SKU",
2581
2664
  cell: (info) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "large", className: "py-2", children: info.getValue() })
2582
2665
  }),
2583
- columnHelper.accessor("prices", {
2666
+ columnHelper$1.accessor("prices", {
2584
2667
  header: "Price",
2585
2668
  cell: (info) => {
2586
2669
  var _a, _b;
@@ -2608,7 +2691,7 @@ const ProductSelector = ({
2608
2691
  }, [products == null ? void 0 : products.variants, selectedProductIds]);
2609
2692
  const table = ui.useDataTable({
2610
2693
  data: selectableProducts || [],
2611
- columns,
2694
+ columns: columns$1,
2612
2695
  search: {
2613
2696
  state: search,
2614
2697
  onSearchChange: setSearch
@@ -2630,29 +2713,223 @@ const ProductSelector = ({
2630
2713
  /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable, { instance: table, children: /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Table, {}) })
2631
2714
  ] });
2632
2715
  };
2633
- const CreateQuoteForm = () => {
2716
+ const ManageQuoteForm = ({ order }) => {
2717
+ const { order: preview } = useOrderPreview(order.id);
2634
2718
  const navigate = reactRouterDom.useNavigate();
2635
- const [selectedCustomer, setSelectedCustomer] = react.useState(null);
2636
- const [items, setItems] = react.useState([]);
2637
- const [openCustomerModal, setOpenCustomerModal] = react.useState(false);
2719
+ const { id: quoteId } = reactRouterDom.useParams();
2638
2720
  const [openProductModal, setOpenProductModal] = react.useState(false);
2639
- const { mutateAsync: createQuote, isPending } = useCreateQuote({
2640
- onSuccess: (data) => {
2641
- ui.toast.success("Quote created successfully");
2642
- navigate(`/quotes/${data.quote.id}`);
2643
- },
2644
- onError: (error) => {
2645
- ui.toast.error(`Failed to create quote: ${error.message}`);
2721
+ const { mutateAsync: addItem } = useAddQuoteItem(order.id);
2722
+ const { mutateAsync: confirmQuote, isPending: isRequesting } = useConfirmQuote(order.id);
2723
+ const handleSubmit = async (e) => {
2724
+ e.preventDefault();
2725
+ try {
2726
+ await confirmQuote();
2727
+ navigate(`/quotes/${quoteId}`);
2728
+ ui.toast.success("Successfully updated quote");
2729
+ } catch (e2) {
2730
+ ui.toast.error("Error", {
2731
+ description: e2.message
2732
+ });
2646
2733
  }
2647
- });
2648
- const handleAddProduct = (variant) => {
2649
- var _a, _b;
2650
- const newItem = {
2651
- variant_id: variant.id,
2652
- variant,
2653
- quantity: 1,
2654
- unit_price: ((_b = (_a = variant.prices) == null ? void 0 : _a[0]) == null ? void 0 : _b.amount) || 0
2655
- };
2734
+ };
2735
+ const originalItemsMap = react.useMemo(() => {
2736
+ return new Map(order.items.map((item) => [item.id, item]));
2737
+ }, [order]);
2738
+ if (!preview) {
2739
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
2740
+ }
2741
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2742
+ /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "flex h-full flex-col p-4 gap-2", children: [
2743
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2744
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-3 mt-8 flex items-center justify-between", children: [
2745
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Items" }),
2746
+ /* @__PURE__ */ jsxRuntime.jsx(
2747
+ ui.Button,
2748
+ {
2749
+ type: "button",
2750
+ variant: "primary",
2751
+ size: "small",
2752
+ onClick: () => setOpenProductModal(true),
2753
+ children: "Add Item"
2754
+ }
2755
+ )
2756
+ ] }),
2757
+ preview.items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
2758
+ ManageItem,
2759
+ {
2760
+ originalItem: originalItemsMap.get(item.id),
2761
+ item,
2762
+ orderId: order.id,
2763
+ currencyCode: order.currency_code
2764
+ },
2765
+ item.id
2766
+ ))
2767
+ ] }),
2768
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 border-y border-dotted py-4", children: [
2769
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
2770
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "txt-small text-ui-fg-subtle", children: "Current Total" }),
2771
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "txt-small text-ui-fg-subtle", children: formatCurrency(order.total, order.currency_code) })
2772
+ ] }),
2773
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
2774
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "txt-small text-ui-fg-subtle", children: "New Total" }),
2775
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "txt-small text-ui-fg-subtle", children: formatCurrency(preview.total, order.currency_code) })
2776
+ ] })
2777
+ ] }),
2778
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex w-full items-center justify-end gap-x-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end gap-x-2", children: /* @__PURE__ */ jsxRuntime.jsx(
2779
+ ui.Button,
2780
+ {
2781
+ type: "submit",
2782
+ variant: "primary",
2783
+ size: "small",
2784
+ disabled: isRequesting,
2785
+ children: "Confirm Edit"
2786
+ },
2787
+ "submit-button"
2788
+ ) }) })
2789
+ ] }),
2790
+ /* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal, { open: openProductModal, onOpenChange: setOpenProductModal, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.FocusModal.Content, { children: [
2791
+ /* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal.Header, { title: "Add Product" }),
2792
+ /* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal.Body, { children: /* @__PURE__ */ jsxRuntime.jsx(
2793
+ ProductSelector,
2794
+ {
2795
+ selectedProductIds: order.items.map(
2796
+ (item) => item.variant_id ?? ""
2797
+ ),
2798
+ onSelectProduct: (variant) => {
2799
+ var _a, _b;
2800
+ if (!((_a = variant.prices) == null ? void 0 : _a[0].amount)) {
2801
+ ui.toast.error("Product price not found");
2802
+ return;
2803
+ }
2804
+ addItem({
2805
+ items: [
2806
+ {
2807
+ variant_id: variant.id,
2808
+ quantity: 1,
2809
+ unit_price: (_b = variant.prices) == null ? void 0 : _b[0].amount
2810
+ }
2811
+ ]
2812
+ });
2813
+ setOpenProductModal(false);
2814
+ }
2815
+ }
2816
+ ) })
2817
+ ] }) })
2818
+ ] });
2819
+ };
2820
+ const QuoteManage = () => {
2821
+ const { id } = reactRouterDom.useParams();
2822
+ const { quote, isLoading } = useQuote(id, {
2823
+ fields: "*draft_order.customer"
2824
+ });
2825
+ if (isLoading) {
2826
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
2827
+ }
2828
+ if (!quote) {
2829
+ throw "quote not found";
2830
+ }
2831
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2832
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
2833
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { className: "flex items-center justify-between px-6 py-4", children: "Manage Quote" }),
2834
+ /* @__PURE__ */ jsxRuntime.jsx(ManageQuoteForm, { order: quote.draft_order })
2835
+ ] }),
2836
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Toaster, {})
2837
+ ] });
2838
+ };
2839
+ const columnHelper = ui.createDataTableColumnHelper();
2840
+ const columns = [
2841
+ columnHelper.accessor("email", {
2842
+ header: "Email",
2843
+ cell: (info) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "large", className: "py-2", children: info.getValue() })
2844
+ }),
2845
+ columnHelper.accessor("first_name", {
2846
+ header: "First Name",
2847
+ cell: (info) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "large", className: "py-2", children: info.getValue() })
2848
+ }),
2849
+ columnHelper.accessor("last_name", {
2850
+ header: "Last Name",
2851
+ cell: (info) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "large", className: "py-2", children: info.getValue() })
2852
+ }),
2853
+ columnHelper.accessor("company_name", {
2854
+ header: "Company",
2855
+ cell: (info) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "large", className: "py-2", children: info.getValue() || "-" })
2856
+ })
2857
+ ];
2858
+ const CustomerSelector = ({
2859
+ onSelectCustomer
2860
+ }) => {
2861
+ const [search, setSearch] = react.useState("");
2862
+ const [data, setData] = react.useState(null);
2863
+ const [isLoading, setIsLoading] = react.useState(false);
2864
+ const debouncedSearchHandler = lodash.debounce((value) => {
2865
+ setSearch(value);
2866
+ }, 500);
2867
+ react.useEffect(() => {
2868
+ const fetchCustomers = async () => {
2869
+ try {
2870
+ setIsLoading(true);
2871
+ const response = await sdk.admin.customer.list({
2872
+ q: search,
2873
+ limit: 20
2874
+ });
2875
+ setData(response);
2876
+ } catch (error) {
2877
+ console.error("Error fetching customers:", error);
2878
+ } finally {
2879
+ setIsLoading(false);
2880
+ }
2881
+ };
2882
+ fetchCustomers();
2883
+ }, [search]);
2884
+ const customers = (data == null ? void 0 : data.customers) || [];
2885
+ const table = ui.useDataTable({
2886
+ data: customers,
2887
+ columns,
2888
+ search: {
2889
+ state: search,
2890
+ onSearchChange: setSearch
2891
+ },
2892
+ getRowId: (customer) => customer.id,
2893
+ onRowClick: (_, row) => {
2894
+ onSelectCustomer == null ? void 0 : onSelectCustomer(row.original);
2895
+ },
2896
+ isLoading
2897
+ });
2898
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { children: [
2899
+ /* @__PURE__ */ jsxRuntime.jsx(
2900
+ ui.Input,
2901
+ {
2902
+ className: "text-lg py-2 mb-4",
2903
+ placeholder: "Search customers by email, name...",
2904
+ onChange: (e) => debouncedSearchHandler(e.target.value)
2905
+ }
2906
+ ),
2907
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable, { instance: table, children: /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Table, {}) })
2908
+ ] });
2909
+ };
2910
+ const CreateQuoteForm = () => {
2911
+ const navigate = reactRouterDom.useNavigate();
2912
+ const [selectedCustomer, setSelectedCustomer] = react.useState(null);
2913
+ const [items, setItems] = react.useState([]);
2914
+ const [openCustomerModal, setOpenCustomerModal] = react.useState(false);
2915
+ const [openProductModal, setOpenProductModal] = react.useState(false);
2916
+ const { mutateAsync: createQuote, isPending } = useCreateQuote({
2917
+ onSuccess: (data) => {
2918
+ ui.toast.success("Quote created successfully");
2919
+ navigate(`/quotes/${data.quote.id}`);
2920
+ },
2921
+ onError: (error) => {
2922
+ ui.toast.error(`Failed to create quote: ${error.message}`);
2923
+ }
2924
+ });
2925
+ const handleAddProduct = (variant) => {
2926
+ var _a, _b;
2927
+ const newItem = {
2928
+ variant_id: variant.id,
2929
+ variant,
2930
+ quantity: 1,
2931
+ unit_price: ((_b = (_a = variant.prices) == null ? void 0 : _a[0]) == null ? void 0 : _b.amount) || 0
2932
+ };
2656
2933
  setItems([...items, newItem]);
2657
2934
  setOpenProductModal(false);
2658
2935
  };
@@ -2875,283 +3152,6 @@ const CreateQuote = () => {
2875
3152
  /* @__PURE__ */ jsxRuntime.jsx(ui.Toaster, {})
2876
3153
  ] });
2877
3154
  };
2878
- function ManageItem({
2879
- originalItem,
2880
- item,
2881
- currencyCode,
2882
- orderId
2883
- }) {
2884
- const { mutateAsync: updateItem } = useUpdateQuoteItem(orderId);
2885
- const { mutateAsync: removeItem } = useRemoveQuoteItem(orderId);
2886
- const isItemUpdated = react.useMemo(
2887
- () => {
2888
- var _a;
2889
- return !!((_a = item.actions) == null ? void 0 : _a.find((a) => a.action === "ITEM_UPDATE"));
2890
- },
2891
- [item]
2892
- );
2893
- const onUpdate = async ({
2894
- quantity,
2895
- unit_price
2896
- }) => {
2897
- if (typeof quantity === "number" && quantity <= item.detail.fulfilled_quantity) {
2898
- ui.toast.warning("Quantity should be greater than the fulfilled quantity");
2899
- return;
2900
- }
2901
- try {
2902
- await updateItem({
2903
- quantity,
2904
- unit_price,
2905
- itemId: item.id
2906
- });
2907
- } catch (e) {
2908
- ui.toast.error(e.message);
2909
- }
2910
- };
2911
- const onRemove = async () => {
2912
- var _a;
2913
- try {
2914
- const addAction = (_a = item.actions) == null ? void 0 : _a.find((a) => a.action === "ITEM_ADD");
2915
- if (addAction == null ? void 0 : addAction.id) {
2916
- await removeItem({
2917
- itemId: addAction.id
2918
- });
2919
- } else {
2920
- ui.toast.warning("Cannot remove item, set quantity to 0 instead");
2921
- await updateItem({
2922
- quantity: 0,
2923
- itemId: item.id
2924
- });
2925
- }
2926
- } catch (e) {
2927
- ui.toast.error(e.message);
2928
- }
2929
- };
2930
- return /* @__PURE__ */ jsxRuntime.jsxs(
2931
- "div",
2932
- {
2933
- className: "bg-ui-bg-subtle shadow-elevation-card-rest my-2 rounded-xl ",
2934
- children: [
2935
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-x-2 gap-y-2 p-3 text-sm md:flex-row", children: [
2936
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 items-center justify-between", children: [
2937
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-row items-center gap-x-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
2938
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2939
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "txt-small", as: "span", weight: "plus", children: [
2940
- item.title,
2941
- " "
2942
- ] }),
2943
- item.variant_sku && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2944
- "(",
2945
- item.variant_sku,
2946
- ")"
2947
- ] })
2948
- ] }),
2949
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { as: "div", className: "text-ui-fg-subtle txt-small", children: item.product_title })
2950
- ] }) }),
2951
- isItemUpdated && /* @__PURE__ */ jsxRuntime.jsx(
2952
- ui.Badge,
2953
- {
2954
- size: "2xsmall",
2955
- rounded: "full",
2956
- color: "orange",
2957
- className: "mr-1",
2958
- children: "Modified"
2959
- }
2960
- )
2961
- ] }),
2962
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 justify-between", children: [
2963
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-grow items-center gap-2", children: [
2964
- /* @__PURE__ */ jsxRuntime.jsx(
2965
- ui.Input,
2966
- {
2967
- className: "bg-ui-bg-base txt-small w-[67px] rounded-lg [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none",
2968
- type: "number",
2969
- min: item.detail.fulfilled_quantity,
2970
- defaultValue: item.quantity,
2971
- onBlur: (e) => {
2972
- const val = e.target.value;
2973
- const quantity = val === "" ? null : Number(val);
2974
- if (quantity) {
2975
- onUpdate({ quantity });
2976
- }
2977
- }
2978
- }
2979
- ),
2980
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "txt-small text-ui-fg-subtle", children: "Quantity" })
2981
- ] }),
2982
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-ui-fg-subtle txt-small mr-2 flex flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
2983
- Amount,
2984
- {
2985
- currencyCode,
2986
- amount: item.total,
2987
- originalAmount: originalItem == null ? void 0 : originalItem.total
2988
- }
2989
- ) })
2990
- ] })
2991
- ] }),
2992
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-2 p-3 md:grid-cols-2", children: [
2993
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
2994
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Price" }),
2995
- /* @__PURE__ */ jsxRuntime.jsx(ui.Hint, { className: "!mt-1", children: "Override the unit price of this product" })
2996
- ] }),
2997
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-grow", children: /* @__PURE__ */ jsxRuntime.jsx(
2998
- ui.CurrencyInput,
2999
- {
3000
- symbol: currencyCode,
3001
- code: currencyCode,
3002
- defaultValue: item.unit_price,
3003
- type: "numeric",
3004
- min: 0,
3005
- onBlur: (e) => {
3006
- onUpdate({
3007
- unit_price: parseFloat(e.target.value),
3008
- quantity: item.quantity
3009
- });
3010
- },
3011
- className: "bg-ui-bg-field-component hover:bg-ui-bg-field-component-hover"
3012
- }
3013
- ) }) })
3014
- ] }),
3015
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center px-3 pb-3", children: /* @__PURE__ */ jsxRuntime.jsx(
3016
- ui.Button,
3017
- {
3018
- type: "button",
3019
- variant: "danger",
3020
- size: "small",
3021
- onClick: () => {
3022
- onRemove();
3023
- },
3024
- children: "Remove"
3025
- }
3026
- ) })
3027
- ]
3028
- },
3029
- item.quantity
3030
- );
3031
- }
3032
- const ManageQuoteForm = ({ order }) => {
3033
- const { order: preview } = useOrderPreview(order.id);
3034
- const navigate = reactRouterDom.useNavigate();
3035
- const { id: quoteId } = reactRouterDom.useParams();
3036
- const [openProductModal, setOpenProductModal] = react.useState(false);
3037
- const { mutateAsync: addItem } = useAddQuoteItem(order.id);
3038
- const { mutateAsync: confirmQuote, isPending: isRequesting } = useConfirmQuote(order.id);
3039
- const handleSubmit = async (e) => {
3040
- e.preventDefault();
3041
- try {
3042
- await confirmQuote();
3043
- navigate(`/quotes/${quoteId}`);
3044
- ui.toast.success("Successfully updated quote");
3045
- } catch (e2) {
3046
- ui.toast.error("Error", {
3047
- description: e2.message
3048
- });
3049
- }
3050
- };
3051
- const originalItemsMap = react.useMemo(() => {
3052
- return new Map(order.items.map((item) => [item.id, item]));
3053
- }, [order]);
3054
- if (!preview) {
3055
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
3056
- }
3057
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3058
- /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "flex h-full flex-col p-4 gap-2", children: [
3059
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3060
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-3 mt-8 flex items-center justify-between", children: [
3061
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Items" }),
3062
- /* @__PURE__ */ jsxRuntime.jsx(
3063
- ui.Button,
3064
- {
3065
- type: "button",
3066
- variant: "primary",
3067
- size: "small",
3068
- onClick: () => setOpenProductModal(true),
3069
- children: "Add Item"
3070
- }
3071
- )
3072
- ] }),
3073
- preview.items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
3074
- ManageItem,
3075
- {
3076
- originalItem: originalItemsMap.get(item.id),
3077
- item,
3078
- orderId: order.id,
3079
- currencyCode: order.currency_code
3080
- },
3081
- item.id
3082
- ))
3083
- ] }),
3084
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 border-y border-dotted py-4", children: [
3085
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
3086
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "txt-small text-ui-fg-subtle", children: "Current Total" }),
3087
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "txt-small text-ui-fg-subtle", children: formatCurrency(order.total, order.currency_code) })
3088
- ] }),
3089
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
3090
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "txt-small text-ui-fg-subtle", children: "New Total" }),
3091
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "txt-small text-ui-fg-subtle", children: formatCurrency(preview.total, order.currency_code) })
3092
- ] })
3093
- ] }),
3094
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex w-full items-center justify-end gap-x-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end gap-x-2", children: /* @__PURE__ */ jsxRuntime.jsx(
3095
- ui.Button,
3096
- {
3097
- type: "submit",
3098
- variant: "primary",
3099
- size: "small",
3100
- disabled: isRequesting,
3101
- children: "Confirm Edit"
3102
- },
3103
- "submit-button"
3104
- ) }) })
3105
- ] }),
3106
- /* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal, { open: openProductModal, onOpenChange: setOpenProductModal, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.FocusModal.Content, { children: [
3107
- /* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal.Header, { title: "Add Product" }),
3108
- /* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal.Body, { children: /* @__PURE__ */ jsxRuntime.jsx(
3109
- ProductSelector,
3110
- {
3111
- selectedProductIds: order.items.map(
3112
- (item) => item.variant_id ?? ""
3113
- ),
3114
- onSelectProduct: (variant) => {
3115
- var _a, _b;
3116
- if (!((_a = variant.prices) == null ? void 0 : _a[0].amount)) {
3117
- ui.toast.error("Product price not found");
3118
- return;
3119
- }
3120
- addItem({
3121
- items: [
3122
- {
3123
- variant_id: variant.id,
3124
- quantity: 1,
3125
- unit_price: (_b = variant.prices) == null ? void 0 : _b[0].amount
3126
- }
3127
- ]
3128
- });
3129
- setOpenProductModal(false);
3130
- }
3131
- }
3132
- ) })
3133
- ] }) })
3134
- ] });
3135
- };
3136
- const QuoteManage = () => {
3137
- const { id } = reactRouterDom.useParams();
3138
- const { quote, isLoading } = useQuote(id, {
3139
- fields: "*draft_order.customer"
3140
- });
3141
- if (isLoading) {
3142
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
3143
- }
3144
- if (!quote) {
3145
- throw "quote not found";
3146
- }
3147
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3148
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
3149
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { className: "flex items-center justify-between px-6 py-4", children: "Manage Quote" }),
3150
- /* @__PURE__ */ jsxRuntime.jsx(ManageQuoteForm, { order: quote.draft_order })
3151
- ] }),
3152
- /* @__PURE__ */ jsxRuntime.jsx(ui.Toaster, {})
3153
- ] });
3154
- };
3155
3155
  const widgetModule = { widgets: [] };
3156
3156
  const routeModule = {
3157
3157
  routes: [
@@ -3163,13 +3163,13 @@ const routeModule = {
3163
3163
  Component: QuoteDetails,
3164
3164
  path: "/quotes/:id"
3165
3165
  },
3166
- {
3167
- Component: CreateQuote,
3168
- path: "/quotes/create"
3169
- },
3170
3166
  {
3171
3167
  Component: QuoteManage,
3172
3168
  path: "/quotes/:id/manage"
3169
+ },
3170
+ {
3171
+ Component: CreateQuote,
3172
+ path: "/quotes/create"
3173
3173
  }
3174
3174
  ]
3175
3175
  };