@deenruv/admin-dashboard 1.0.5 → 1.0.9

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.
Files changed (76) hide show
  1. package/dist/DeenruvAdminPanel.js +5 -4
  2. package/dist/DeenruvDeveloperIndicator.js +1 -1
  3. package/dist/components/ContentAreaSkeleton.js +5 -0
  4. package/dist/components/Menu/LanguagesDropdown.js +4 -3
  5. package/dist/components/Menu/Navigation.js +10 -9
  6. package/dist/components/Menu/Notifications.js +2 -1
  7. package/dist/components/Menu/flag-icons.js +16 -0
  8. package/dist/components/Menu/index.js +3 -2
  9. package/dist/pages/Custom404.js +16 -2
  10. package/dist/pages/Root.js +2 -2
  11. package/dist/pages/admins/Detail.js +7 -7
  12. package/dist/pages/admins/_components/AdminDetailView.js +17 -9
  13. package/dist/pages/channels/Detail.js +12 -12
  14. package/dist/pages/channels/_components/ChannelDetailView.js +25 -17
  15. package/dist/pages/collections/Detail.js +8 -7
  16. package/dist/pages/collections/List.js +1 -2
  17. package/dist/pages/collections/_components/CollectionDetailView.js +65 -22
  18. package/dist/pages/collections/_components/CollectionProductVariantsDrawer.js +1 -1
  19. package/dist/pages/countries/Detail.js +5 -5
  20. package/dist/pages/countries/_components/CountryDetailView.js +15 -8
  21. package/dist/pages/customer-groups/Detail.js +3 -3
  22. package/dist/pages/customer-groups/_components/CustomerGroupsDetailView.js +4 -4
  23. package/dist/pages/customers/Detail.js +6 -8
  24. package/dist/pages/customers/_components/AddressForm.js +48 -60
  25. package/dist/pages/customers/_components/AddressesCard.js +1 -1
  26. package/dist/pages/customers/_components/CustomerDetailView.js +9 -9
  27. package/dist/pages/dashboard/Dashboard.js +3 -2
  28. package/dist/pages/extensions/Extensions.js +12 -13
  29. package/dist/pages/facets/Detail.js +6 -6
  30. package/dist/pages/facets/_components/AddFacetValueDialog.js +39 -39
  31. package/dist/pages/facets/_components/FacetDetailView.js +19 -12
  32. package/dist/pages/global-settings/GlobalSettingsComponent.js +9 -8
  33. package/dist/pages/global-settings/index.js +4 -4
  34. package/dist/pages/orders/List.js +2 -1
  35. package/dist/pages/orders/_components/AddressCard.js +123 -78
  36. package/dist/pages/orders/_components/CustomerSelectCard.js +38 -43
  37. package/dist/pages/orders/_components/FulfillmentModal.js +47 -28
  38. package/dist/pages/orders/_components/ModifyingCard.js +30 -13
  39. package/dist/pages/orders/_components/OrderLineCustomFields.js +2 -1
  40. package/dist/pages/orders/_components/PossibleOrderStates.js +2 -1
  41. package/dist/pages/orders/_components/ProductsCard.js +2 -1
  42. package/dist/pages/orders/_components/SurchargeCard.js +58 -60
  43. package/dist/pages/orders/_components/TopActions.js +2 -1
  44. package/dist/pages/payment-methods/Detail.js +8 -7
  45. package/dist/pages/payment-methods/_components/PaymentMethodDetailView.js +22 -14
  46. package/dist/pages/products/Detail.js +9 -7
  47. package/dist/pages/products/_components/AddOptionGroupDialog.js +36 -14
  48. package/dist/pages/products/_components/BasicFieldsCard.js +5 -2
  49. package/dist/pages/products/_components/OptionValueCard.js +25 -23
  50. package/dist/pages/products/_components/ProductDetailSidebar.js +15 -7
  51. package/dist/pages/products/_components/ProductDetailView.js +51 -17
  52. package/dist/pages/products/_components/Variant.js +105 -57
  53. package/dist/pages/promotions/Detail.js +13 -11
  54. package/dist/pages/promotions/_components/OptionsCard.js +1 -1
  55. package/dist/pages/promotions/_components/PromotionDetailSidebar.js +9 -6
  56. package/dist/pages/promotions/_components/PromotionDetailView.js +26 -20
  57. package/dist/pages/roles/Detail.js +5 -5
  58. package/dist/pages/roles/_components/RoleDetailView.js +13 -8
  59. package/dist/pages/sellers/Detail.js +4 -4
  60. package/dist/pages/sellers/_components/SellerDetailView.js +4 -4
  61. package/dist/pages/shipping-methods/Detail.js +14 -12
  62. package/dist/pages/shipping-methods/_components/Lines.js +1 -1
  63. package/dist/pages/shipping-methods/_components/ShippingMethodDetailView.js +26 -13
  64. package/dist/pages/shipping-methods/_components/TestCard.js +67 -21
  65. package/dist/pages/status/_components/Health.js +2 -1
  66. package/dist/pages/status/_components/Jobs.js +2 -6
  67. package/dist/pages/stock-locations/Detail.js +4 -4
  68. package/dist/pages/stock-locations/_components/StockLocationDetailView.js +5 -5
  69. package/dist/pages/tax-categories/Detail.js +4 -4
  70. package/dist/pages/tax-categories/_components/TaxCategoryDetailView.js +7 -7
  71. package/dist/pages/tax-rates/Detail.js +8 -8
  72. package/dist/pages/tax-rates/_components/TaxRateDetailView.js +13 -9
  73. package/dist/pages/zones/Detail.js +4 -4
  74. package/dist/pages/zones/_components/ZoneDetailView.js +8 -7
  75. package/dist/version.js +1 -1
  76. package/package.json +79 -78
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { Button, Tabs, TabsList, TabsTrigger, TabsContent, Input, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, apiClient, useOrder, cn, OrderDetailSelector, Label, useGFFLP, CustomCard, EntityCustomFields, useTranslation, CustomerSearch, } from '@deenruv/react-ui-devkit';
3
+ import { Button, Tabs, TabsList, TabsTrigger, TabsContent, Input, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, apiClient, useOrder, cn, OrderDetailSelector, Label, useDeenruvForm, z, CustomCard, EntityCustomFields, useTranslation, CustomerSearch, } from '@deenruv/react-ui-devkit';
4
4
  import { useEffect, useState } from 'react';
5
5
  import { toast } from 'sonner';
6
6
  import { Edit, User, Check, Mail, Phone, Loader2, UserPlus, Search, AlertCircle } from 'lucide-react';
@@ -11,49 +11,35 @@ export const CustomerSelectCard = () => {
11
11
  const [selected, setSelected] = useState(order?.customer);
12
12
  const [open, setOpen] = useState(false);
13
13
  const [isSubmitting, setIsSubmitting] = useState(false);
14
- const { state, checkIfAllFieldsAreValid, setField, clearAllForm } = useGFFLP('CreateCustomerInput', 'firstName', 'lastName', 'title', 'phoneNumber', 'emailAddress', 'customFields')({
15
- firstName: {
16
- initialValue: '',
17
- validate: (v) => {
18
- if (!v || v === '')
19
- return [t('form.requiredError')];
20
- },
21
- },
22
- lastName: {
23
- initialValue: '',
24
- validate: (v) => {
25
- if (!v || v === '')
26
- return [t('form.requiredError')];
27
- },
28
- },
29
- phoneNumber: {
30
- initialValue: '',
31
- validate: (v) => {
32
- if (!v || v === '')
33
- return [t('form.requiredError')];
34
- // if (!phoneNumberRegExp.test(v)) return [t('form.phoneError')];
35
- },
36
- },
37
- emailAddress: {
38
- initialValue: '',
39
- validate: (v) => {
40
- if (!v || v === '')
41
- return [t('form.requiredError')];
42
- },
43
- },
44
- customFields: {
45
- initialValue: {},
14
+ const customerSchema = z.object({
15
+ firstName: z.string().min(1, t('form.requiredError')),
16
+ lastName: z.string().min(1, t('form.requiredError')),
17
+ title: z.string().default(''),
18
+ phoneNumber: z.string().min(1, t('form.requiredError')),
19
+ emailAddress: z.string().min(1, t('form.requiredError')),
20
+ customFields: z.record(z.string(), z.unknown()).default({}),
21
+ });
22
+ const form = useDeenruvForm({
23
+ schema: customerSchema,
24
+ defaultValues: {
25
+ firstName: '',
26
+ lastName: '',
27
+ title: '',
28
+ phoneNumber: '',
29
+ emailAddress: '',
30
+ customFields: {},
46
31
  },
47
32
  });
33
+ const formValues = form.watch();
48
34
  useEffect(() => setSelected(order?.customer), [order]);
49
35
  useEffect(() => {
50
36
  if (tab === 'create')
51
- clearAllForm();
37
+ form.reset({ firstName: '', lastName: '', title: '', phoneNumber: '', emailAddress: '', customFields: {} });
52
38
  }, [tab]);
53
39
  const validateAndSubmitIfCorrect = async () => {
54
40
  if (!order?.id)
55
41
  return;
56
- if (tab === 'create' && !checkIfAllFieldsAreValid()) {
42
+ if (tab === 'create' && !(await form.trigger())) {
57
43
  return;
58
44
  }
59
45
  setIsSubmitting(true);
@@ -66,6 +52,7 @@ export const CustomerSelectCard = () => {
66
52
  });
67
53
  }
68
54
  else {
55
+ const values = form.getValues();
69
56
  const { setCustomerForDraftOrder } = await apiClient('mutation')({
70
57
  setCustomerForDraftOrder: [
71
58
  {
@@ -74,11 +61,11 @@ export const CustomerSelectCard = () => {
74
61
  ? { customerId: selected?.id }
75
62
  : {
76
63
  input: {
77
- title: state.title?.validatedValue,
78
- firstName: state.firstName?.validatedValue || '',
79
- lastName: state.lastName?.validatedValue || '',
80
- emailAddress: state.emailAddress?.validatedValue || '',
81
- phoneNumber: state.phoneNumber?.validatedValue,
64
+ title: values.title,
65
+ firstName: values.firstName || '',
66
+ lastName: values.lastName || '',
67
+ emailAddress: values.emailAddress || '',
68
+ phoneNumber: values.phoneNumber,
82
69
  },
83
70
  }),
84
71
  },
@@ -109,9 +96,17 @@ export const CustomerSelectCard = () => {
109
96
  if (!order)
110
97
  return null;
111
98
  return (_jsx(CustomCard, { notCollapsible: true, color: order?.customer ? 'indigo' : 'gray', description: t('create.selectCustomer.description', 'Choose an existing customer or create a new one for this order'), title: t('create.selectCustomer.select', 'Customer Information'), icon: _jsx(User, {}), upperRight: mode !== 'view' &&
112
- mode !== 'update' && (_jsxs(Dialog, { open: open, onOpenChange: setOpen, children: [_jsx(DialogTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", className: "size-8", children: _jsx(Edit, { size: 16, className: "text-indigo-500 dark:text-indigo-400" }) }) }), _jsxs(DialogContent, { className: "min-w-max", children: [_jsxs(DialogHeader, { children: [_jsxs(DialogTitle, { className: "flex items-center gap-2 text-xl", children: [_jsx(User, { className: "size-5 text-indigo-500 dark:text-indigo-400" }), t('create.selectCustomer.label', 'Select Customer')] }), _jsx(DialogDescription, { className: "mt-2 text-muted-foreground", children: t('create.selectCustomer.description', 'Choose an existing customer or create a new one for this order') })] }), _jsxs(Tabs, { value: tab, onValueChange: (e) => setTab(e), children: [_jsxs(TabsList, { className: "my-4 grid w-full grid-cols-2", children: [_jsx(TabsTrigger, { className: "w-full", value: "select", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Search, { className: "size-4" }), t('create.selectCustomer.selectTab', 'Find Customer')] }) }), _jsx(TabsTrigger, { className: "w-full", value: "create", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(UserPlus, { className: "size-4" }), t('create.selectCustomer.createTab', 'Create New')] }) })] }), _jsx(TabsContent, { className: "focus-visible:ring-transparent", value: "select", children: _jsx(CustomerSearch, { selectedCustomer: selected, onSelect: (selected) => setSelected(selected) }) }), _jsx(TabsContent, { value: "create", className: "h-fit max-h-[calc(80vh-230px)] overflow-y-auto pt-2", children: _jsxs("div", { className: "space-y-4 px-1", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "title", className: "text-sm font-medium", children: t('create.selectCustomer.titleLabel', 'Title') }), _jsx(Input, { id: "title", name: "title", placeholder: t('create.selectCustomer.titlePlaceholder', 'Mr., Mrs., Dr., etc.'), value: state.title?.value ?? undefined, onChange: (e) => setField('title', e.target.value), errors: state.title?.errors })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs(Label, { htmlFor: "firstName", className: "text-sm font-medium", children: [t('create.selectCustomer.firstNameLabel', 'First Name'), ' ', _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "firstName", name: "firstName", placeholder: t('create.selectCustomer.firstNamePlaceholder', 'Enter first name'), value: state.firstName?.value, onChange: (e) => setField('firstName', e.target.value), className: cn(state.firstName?.errors?.length && 'border-red-300'), required: true, errors: state.firstName?.errors })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs(Label, { htmlFor: "lastName", className: "text-sm font-medium", children: [t('create.selectCustomer.lastNameLabel', 'Last Name'), ' ', _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "lastName", name: "lastName", placeholder: t('create.selectCustomer.lastNamePlaceholder', 'Enter last name'), value: state.lastName?.value, onChange: (e) => setField('lastName', e.target.value), className: cn(state.lastName?.errors?.length && 'border-red-300'), required: true, errors: state.lastName?.errors })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs(Label, { htmlFor: "email", className: "text-sm font-medium", children: [t('create.selectCustomer.emailLabel', 'Email Address'), " ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "email", name: "emailAddress", type: "email", placeholder: t('create.selectCustomer.emailPlaceholder', 'Enter email address'), value: state.emailAddress?.value, onChange: (e) => setField('emailAddress', e.target.value), className: cn(state.emailAddress?.errors?.length && 'border-red-300'), required: true, errors: state.emailAddress?.errors })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs(Label, { htmlFor: "phone", className: "text-sm font-medium", children: [t('create.selectCustomer.phoneNumberLabel', 'Phone Number'), ' ', _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "phone", name: "phoneNumber", placeholder: t('create.selectCustomer.phonePlaceholder', 'Enter phone number'), value: state.phoneNumber?.value ?? undefined, onChange: (e) => setField('phoneNumber', e.target.value), className: cn(state.phoneNumber?.errors?.length && 'border-red-300'), required: true, errors: state.phoneNumber?.errors })] }), _jsx(EntityCustomFields, { id: selected?.id, entityName: "customer", hideButton: true, initialValues: selected && 'customFields' in selected
99
+ mode !== 'update' && (_jsxs(Dialog, { open: open, onOpenChange: setOpen, children: [_jsx(DialogTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", className: "size-8", children: _jsx(Edit, { size: 16, className: "text-indigo-500 dark:text-indigo-400" }) }) }), _jsxs(DialogContent, { className: "min-w-max", children: [_jsxs(DialogHeader, { children: [_jsxs(DialogTitle, { className: "flex items-center gap-2 text-xl", children: [_jsx(User, { className: "size-5 text-indigo-500 dark:text-indigo-400" }), t('create.selectCustomer.label', 'Select Customer')] }), _jsx(DialogDescription, { className: "mt-2 text-muted-foreground", children: t('create.selectCustomer.description', 'Choose an existing customer or create a new one for this order') })] }), _jsxs(Tabs, { value: tab, onValueChange: (e) => setTab(e), children: [_jsxs(TabsList, { className: "my-4 grid w-full grid-cols-2", children: [_jsx(TabsTrigger, { className: "w-full", value: "select", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Search, { className: "size-4" }), t('create.selectCustomer.selectTab', 'Find Customer')] }) }), _jsx(TabsTrigger, { className: "w-full", value: "create", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(UserPlus, { className: "size-4" }), t('create.selectCustomer.createTab', 'Create New')] }) })] }), _jsx(TabsContent, { className: "focus-visible:ring-transparent", value: "select", children: _jsx(CustomerSearch, { selectedCustomer: selected, onSelect: (selected) => setSelected(selected) }) }), _jsx(TabsContent, { value: "create", className: "h-fit max-h-[calc(80vh-230px)] overflow-y-auto pt-2", children: _jsxs("div", { className: "space-y-4 px-1", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "title", className: "text-sm font-medium", children: t('create.selectCustomer.titleLabel', 'Title') }), _jsx(Input, { id: "title", name: "title", placeholder: t('create.selectCustomer.titlePlaceholder', 'Mr., Mrs., Dr., etc.'), value: formValues.title ?? undefined, onChange: (e) => form.setField('title', e.target.value), errors: form.formState.errors.title?.message ? [form.formState.errors.title.message] : undefined })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs(Label, { htmlFor: "firstName", className: "text-sm font-medium", children: [t('create.selectCustomer.firstNameLabel', 'First Name'), ' ', _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "firstName", name: "firstName", placeholder: t('create.selectCustomer.firstNamePlaceholder', 'Enter first name'), value: formValues.firstName, onChange: (e) => form.setField('firstName', e.target.value), className: cn(form.formState.errors.firstName && 'border-red-300'), required: true, errors: form.formState.errors.firstName?.message
100
+ ? [form.formState.errors.firstName.message]
101
+ : undefined })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs(Label, { htmlFor: "lastName", className: "text-sm font-medium", children: [t('create.selectCustomer.lastNameLabel', 'Last Name'), ' ', _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "lastName", name: "lastName", placeholder: t('create.selectCustomer.lastNamePlaceholder', 'Enter last name'), value: formValues.lastName, onChange: (e) => form.setField('lastName', e.target.value), className: cn(form.formState.errors.lastName && 'border-red-300'), required: true, errors: form.formState.errors.lastName?.message
102
+ ? [form.formState.errors.lastName.message]
103
+ : undefined })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs(Label, { htmlFor: "email", className: "text-sm font-medium", children: [t('create.selectCustomer.emailLabel', 'Email Address'), " ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "email", name: "emailAddress", type: "email", placeholder: t('create.selectCustomer.emailPlaceholder', 'Enter email address'), value: formValues.emailAddress, onChange: (e) => form.setField('emailAddress', e.target.value), className: cn(form.formState.errors.emailAddress && 'border-red-300'), required: true, errors: form.formState.errors.emailAddress?.message
104
+ ? [form.formState.errors.emailAddress.message]
105
+ : undefined })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs(Label, { htmlFor: "phone", className: "text-sm font-medium", children: [t('create.selectCustomer.phoneNumberLabel', 'Phone Number'), ' ', _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "phone", name: "phoneNumber", placeholder: t('create.selectCustomer.phonePlaceholder', 'Enter phone number'), value: formValues.phoneNumber ?? undefined, onChange: (e) => form.setField('phoneNumber', e.target.value), className: cn(form.formState.errors.phoneNumber && 'border-red-300'), required: true, errors: form.formState.errors.phoneNumber?.message
106
+ ? [form.formState.errors.phoneNumber.message]
107
+ : undefined })] }), _jsx(EntityCustomFields, { id: selected?.id, entityName: "customer", hideButton: true, initialValues: selected && 'customFields' in selected
113
108
  ? { customFields: selected.customFields }
114
- : { customFields: {} }, onChange: (customFields, translations) => {
115
- setField('customFields', customFields);
109
+ : { customFields: {} }, onChange: (customFields) => {
110
+ form.setField('customFields', customFields);
116
111
  }, additionalData: {} })] }) })] }), _jsxs(DialogFooter, { className: "gap-2", children: [_jsx(Button, { variant: "outline", onClick: () => setOpen(false), disabled: isSubmitting, children: t('common.cancel', 'Cancel') }), _jsx(Button, { disabled: (tab === 'select' && !selected) || isSubmitting, className: "gap-2", onClick: validateAndSubmitIfCorrect, children: isSubmitting ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "size-4 animate-spin" }), t('common.processing', 'Processing...')] })) : (_jsxs(_Fragment, { children: [_jsx(Check, { className: "size-4" }), t(tab === 'create' ? 'create.selectCustomer.create' : 'create.selectCustomer.selectButton')] })) })] })] })] })), children: _jsx("div", { className: "rounded-lg border border-border bg-muted/50 p-3", children: _jsx("div", { className: "flex items-start gap-3", children: !order?.customer ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "mt-0.5 flex size-8 items-center justify-center rounded-full bg-secondary", children: _jsx(AlertCircle, { className: "size-4 text-green-500 dark:text-green-600" }) }), _jsxs("div", { className: "flex-1", children: [_jsx("p", { className: "text-sm text-muted-foreground italic", children: t('create.selectCustomer.noCustomer', 'No customer assigned to this order') }), mode !== 'view' && (_jsxs(Button, { variant: "outline", size: "sm", className: "mt-2 gap-2", onClick: () => setOpen(true), children: [_jsx(UserPlus, { className: "size-3.5" }), t('create.selectCustomer.addCustomer', 'Add Customer')] }))] })] })) : (_jsx(_Fragment, { children: _jsx("div", { className: "flex-1", children: _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("p", { className: "text-sm font-medium", children: [order.customer.title && `${order.customer.title} `, order.customer.firstName, " ", order.customer.lastName] }), _jsx("span", { className: "rounded-full bg-indigo-100 px-2 py-0.5 text-xs text-indigo-800 dark:bg-indigo-900/50 dark:text-indigo-300", children: t('create.selectCustomer.customer', 'Customer') })] }), _jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(Mail, { className: "size-3.5 text-indigo-500 dark:text-indigo-400" }), _jsx("span", { children: order.customer.emailAddress })] }), order.customer.phoneNumber && (_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(Phone, { className: "size-3.5 text-indigo-500 dark:text-indigo-400" }), _jsx("span", { children: order.customer.phoneNumber })] }))] }) }) })) }) }) }));
117
112
  };
@@ -1,27 +1,45 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { TableHeader, TableRow, TableHead, TableBody, TableCell, Button, ScrollArea, Table, Input, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, useServer, Badge, priceFormatter, ArgumentFieldsComponent, CustomCard, useGFFLP, useTranslation, } from '@deenruv/react-ui-devkit';
3
+ import { TableHeader, TableRow, TableHead, TableBody, TableCell, Button, ScrollArea, Table, Input, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, useServer, Badge, priceFormatter, ArgumentFieldsComponent, CustomCard, useDeenruvForm, z, useTranslation, } from '@deenruv/react-ui-devkit';
4
4
  import { LineItem } from './LineItem.js';
5
- import { useState } from 'react';
5
+ import { useState, useMemo } from 'react';
6
6
  import { Package, Truck, MapPin, User, Building, Phone, Box, CheckCircle2, AlertCircle, Loader2 } from 'lucide-react';
7
+ const fulfillOrderSchema = z.object({
8
+ lines: z
9
+ .array(z.object({
10
+ orderLineId: z.string(),
11
+ quantity: z.number(),
12
+ customFields: z.record(z.string(), z.unknown()).optional(),
13
+ }))
14
+ .default([]),
15
+ handler: z
16
+ .object({
17
+ code: z.string().optional(),
18
+ arguments: z
19
+ .array(z.object({
20
+ name: z.string(),
21
+ value: z.string(),
22
+ }))
23
+ .optional(),
24
+ })
25
+ .default({ code: undefined, arguments: [] }),
26
+ });
7
27
  export const FulfillmentModal = ({ order, onSubmitted, disabled }) => {
8
28
  const { t } = useTranslation('orders');
9
29
  const [open, setOpen] = useState(false);
10
30
  const [isSubmitting, setIsSubmitting] = useState(false);
11
31
  const neededFulfillmentHandlers = order?.shippingLines?.map((line) => line.shippingMethod.fulfillmentHandlerCode);
12
- const filteredFulfillmentHandlers = useServer((p) => {
13
- return p.fulfillmentHandlers.filter((handler) => neededFulfillmentHandlers.includes(handler?.code));
14
- });
15
- const { state, setField } = useGFFLP('FulfillOrderInput')({
16
- lines: {
17
- initialValue: order.lines.map((line) => ({
32
+ const fulfillmentHandlers = useServer((p) => p.fulfillmentHandlers);
33
+ const filteredFulfillmentHandlers = useMemo(() => fulfillmentHandlers.filter((handler) => neededFulfillmentHandlers.includes(handler?.code)), [fulfillmentHandlers, neededFulfillmentHandlers]);
34
+ const form = useDeenruvForm({
35
+ schema: fulfillOrderSchema,
36
+ defaultValues: {
37
+ lines: order.lines.map((line) => ({
18
38
  orderLineId: line.id,
19
39
  quantity: line.quantity || 1,
20
40
  customFields: {},
21
41
  })),
22
- },
23
- handler: {
24
- initialValue: {
42
+ handler: {
25
43
  code: filteredFulfillmentHandlers[0]?.code,
26
44
  arguments: filteredFulfillmentHandlers[0]?.args.map((arg) => ({
27
45
  name: arg.name,
@@ -30,17 +48,19 @@ export const FulfillmentModal = ({ order, onSubmitted, disabled }) => {
30
48
  },
31
49
  },
32
50
  });
51
+ const linesValue = form.watch('lines');
52
+ const handlerValue = form.watch('handler');
33
53
  const handleSubmit = async (e) => {
34
54
  e.preventDefault();
35
- if (!state.lines?.value || !state.handler?.value)
55
+ if (!linesValue || !handlerValue)
36
56
  return;
37
- const lines = state.lines?.value.map((line) => ({
57
+ const lines = linesValue.map((line) => ({
38
58
  orderLineId: line.orderLineId,
39
59
  quantity: line.quantity,
40
60
  }));
41
61
  setIsSubmitting(true);
42
62
  try {
43
- await onSubmitted({ lines, handler: state.handler?.value });
63
+ await onSubmitted({ lines, handler: handlerValue });
44
64
  setOpen(false);
45
65
  }
46
66
  catch (error) {
@@ -52,34 +72,33 @@ export const FulfillmentModal = ({ order, onSubmitted, disabled }) => {
52
72
  };
53
73
  return (_jsxs(Dialog, { open: open, onOpenChange: setOpen, children: [_jsx(DialogTrigger, { asChild: true, children: _jsxs(Button, { disabled: disabled, size: "sm", className: "gap-2", children: [_jsx(Package, { className: "size-4" }), t('fulfillment.completeOrderButton', 'Fulfill Order')] }) }), _jsxs(DialogContent, { className: "flex h-[80vh] max-w-[80vw] flex-col gap-0 overflow-hidden p-0", children: [_jsxs(DialogHeader, { className: "border-b px-6 py-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Truck, { className: "size-5 text-primary" }), _jsx(DialogTitle, { children: t('fulfillment.completeDialogTitle', 'Complete Order Fulfillment') })] }), _jsx(DialogDescription, { children: t('fulfillment.completeDialogDescription', 'Prepare items for shipping and complete the fulfillment process') })] }), _jsxs("div", { className: "flex size-full flex-col overflow-hidden md:flex-row", children: [_jsxs("div", { className: "flex size-full flex-col border-r p-6 md:w-1/2", children: [_jsxs("div", { className: "mb-4 flex items-center gap-2", children: [_jsx(Box, { className: "size-5 text-blue-500 dark:text-blue-400" }), _jsx("h3", { className: "text-lg font-medium", children: t('fulfillment.itemsToFulfill', 'Items to Fulfill') })] }), _jsx(ScrollArea, { className: "h-[calc(100%-3rem)] w-full rounded-md border", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { noHover: true, className: "bg-muted/50", children: [_jsx(TableHead, { className: "py-3", children: t('fulfillment.product', 'Product') }), _jsx(TableHead, { className: "py-3", children: t('fulfillment.sku', 'SKU') }), _jsx(TableHead, { className: "py-3", children: t('fulfillment.fulfilled', 'Quantity') })] }) }), _jsx(TableBody, { className: "w-full", children: order?.lines.length ? (order.lines.map((line) => {
54
74
  const onStock = line.productVariant.stockLevels.reduce((acc, stock) => acc + stock.stockOnHand, 0);
55
- const stateLine = state.lines?.value?.find((l) => l.orderLineId === line.id);
75
+ const stateLine = linesValue?.find((l) => l.orderLineId === line.id);
56
76
  const isLowStock = onStock < line.quantity;
57
77
  return (_jsx(LineItem, { variant: line.productVariant, children: _jsx(TableCell, { className: "py-3", children: _jsx("div", { className: "flex items-center", children: _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("div", { className: "flex items-center gap-2", children: _jsx(Input, { type: "number", className: "w-20", value: stateLine?.quantity, onChange: (e) => {
58
- const value = state.lines?.value;
59
- if (!value)
60
- return;
78
+ const value = linesValue ? [...linesValue] : [];
61
79
  const index = value.findIndex((v) => v.orderLineId === line.id);
62
80
  if (index === -1)
63
81
  return;
64
82
  if (Number.parseInt(e.target.value) < 1)
65
83
  return;
66
84
  if (Number.parseInt(e.target.value) <= line.quantity) {
67
- value[index].quantity = Number.parseInt(e.target.value);
68
- setField('lines', value);
85
+ value[index] = { ...value[index], quantity: Number.parseInt(e.target.value) };
86
+ form.setField('lines', value);
69
87
  }
70
88
  }, endAdornment: _jsxs("span", { className: "text-sm", children: ["/ ", line.quantity] }) }) }), _jsx("div", { className: "flex items-center gap-1", children: isLowStock ? (_jsxs("div", { className: "flex items-center gap-1 text-amber-500", children: [_jsx(AlertCircle, { className: "size-4" }), _jsx("p", { className: "text-xs font-medium", children: t('fulfillment.lowStock', 'Low stock') })] })) : (_jsxs("div", { className: "flex items-center gap-1 text-green-500", children: [_jsx(CheckCircle2, { className: "size-4" }), _jsx("p", { className: "text-xs font-medium", children: t('fulfillment.inStock', 'In stock') })] })) }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["(", t('fulfillment.onStockValue', { value: onStock }), ")"] })] }) }) }) }, line.id));
71
- })) : (_jsx(TableRow, { noHover: true, children: _jsx(TableCell, { colSpan: 4, className: "py-6 text-center text-muted-foreground", children: _jsxs("div", { className: "flex flex-col items-center justify-center gap-2", children: [_jsx("div", { className: "rounded-full bg-blue-100 p-3 dark:bg-blue-900/30", children: _jsx(Box, { className: "size-6 text-blue-500 dark:text-blue-400" }) }), _jsx("p", { children: t('fulfillment.emptyState', 'No items to fulfill') }), _jsx("p", { className: "text-xs text-muted-foreground", children: t('fulfillment.emptyStateHint', 'Add products to the order before fulfillment') })] }) }) })) })] }) })] }), _jsx(ScrollArea, { className: "flex h-full md:w-1/2", children: _jsx("div", { className: "flex size-full flex-col p-6", children: _jsxs("form", { onSubmit: handleSubmit, className: "flex h-full flex-col gap-6", children: [_jsxs(CustomCard, { title: t('fulfillment.shippingAddress', 'Shipping Address'), color: "blue", icon: _jsx(MapPin, {}), children: [_jsxs("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(User, { className: "size-4 text-muted-foreground" }), _jsx("p", { className: "text-xs font-medium text-muted-foreground", children: t('fullName', 'Full Name') })] }), _jsx("p", { className: "text-sm font-medium", children: order.shippingAddress?.fullName || '—' })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Building, { className: "size-4 text-muted-foreground" }), _jsx("p", { className: "text-xs font-medium text-muted-foreground", children: t('company', 'Company') })] }), _jsx("p", { className: "text-sm font-medium", children: order.shippingAddress?.company || '—' })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(MapPin, { className: "size-4 text-muted-foreground" }), _jsx("p", { className: "text-xs font-medium text-muted-foreground", children: t('street1', 'Street Address') })] }), _jsx("p", { className: "text-sm font-medium", children: order.shippingAddress?.streetLine1 || '—' })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-xs font-medium text-muted-foreground", children: t('street2', 'Street Address 2') }), _jsx("p", { className: "text-sm font-medium", children: order.shippingAddress?.streetLine2 || '—' })] })] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-xs font-medium text-muted-foreground", children: t('city', 'City') }), _jsx("p", { className: "text-sm font-medium", children: order.shippingAddress?.city || '—' })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-xs font-medium text-muted-foreground", children: t('postalCode', 'Postal Code') }), _jsx("p", { className: "text-sm font-medium", children: order.shippingAddress?.postalCode || '—' })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-xs font-medium text-muted-foreground", children: t('country', 'Country') }), _jsx("p", { className: "text-sm font-medium", children: order.shippingAddress?.country || '—' })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Phone, { className: "size-4 text-muted-foreground" }), _jsx("p", { className: "text-xs font-medium text-muted-foreground", children: t('phoneNumber', 'Phone Number') })] }), _jsx("p", { className: "text-sm font-medium", children: order.shippingAddress?.phoneNumber || '—' })] })] })] }), _jsxs("div", { className: "mt-4 border-t pt-4", children: [_jsx("p", { className: "mb-2 text-xs font-medium text-muted-foreground", children: t('fulfillment.shippingMethod', 'Shipping Method') }), order.shippingLines.map((line) => (_jsxs("div", { className: "flex items-center justify-between rounded-md bg-muted/50 p-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Truck, { className: "size-4 text-blue-500 dark:text-blue-400" }), _jsx("span", { className: "font-medium", children: line.shippingMethod.name })] }), _jsx(Badge, { variant: "outline", className: "font-mono", children: priceFormatter(line.priceWithTax, order.currencyCode) })] }, line.id)))] })] }), _jsx(CustomCard, { color: "green", title: t('fulfillment.fulfillmentOptions', 'Fulfillment Options'), icon: _jsx(Package, {}), bottomRight: _jsx(Button, { type: "submit", className: "w-full gap-2", disabled: isSubmitting, children: isSubmitting ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "size-4 animate-spin" }), t('fulfillment.processing', 'Processing...')] })) : (_jsxs(_Fragment, { children: [_jsx(CheckCircle2, { className: "size-4" }), t('fulfillment.fulfill', 'Complete Fulfillment')] })) }), children: _jsx(ArgumentFieldsComponent, { actions: filteredFulfillmentHandlers, args: state.handler?.value.arguments?.length
72
- ? state.handler.value.arguments
73
- : state.handler?.initialValue?.arguments?.length
74
- ? state.handler.initialValue.arguments
75
- : [], setArg: (argument, data) => {
76
- const newArgs = state.handler?.value.arguments.map((arg) => {
89
+ })) : (_jsx(TableRow, { noHover: true, children: _jsx(TableCell, { colSpan: 4, className: "py-6 text-center text-muted-foreground", children: _jsxs("div", { className: "flex flex-col items-center justify-center gap-2", children: [_jsx("div", { className: "rounded-full bg-blue-100 p-3 dark:bg-blue-900/30", children: _jsx(Box, { className: "size-6 text-blue-500 dark:text-blue-400" }) }), _jsx("p", { children: t('fulfillment.emptyState', 'No items to fulfill') }), _jsx("p", { className: "text-xs text-muted-foreground", children: t('fulfillment.emptyStateHint', 'Add products to the order before fulfillment') })] }) }) })) })] }) })] }), _jsx(ScrollArea, { className: "flex h-full md:w-1/2", children: _jsx("div", { className: "flex size-full flex-col p-6", children: _jsxs("form", { onSubmit: handleSubmit, className: "flex h-full flex-col gap-6", children: [_jsxs(CustomCard, { title: t('fulfillment.shippingAddress', 'Shipping Address'), color: "blue", icon: _jsx(MapPin, {}), children: [_jsxs("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(User, { className: "size-4 text-muted-foreground" }), _jsx("p", { className: "text-xs font-medium text-muted-foreground", children: t('fullName', 'Full Name') })] }), _jsx("p", { className: "text-sm font-medium", children: order.shippingAddress?.fullName || '—' })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Building, { className: "size-4 text-muted-foreground" }), _jsx("p", { className: "text-xs font-medium text-muted-foreground", children: t('company', 'Company') })] }), _jsx("p", { className: "text-sm font-medium", children: order.shippingAddress?.company || '—' })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(MapPin, { className: "size-4 text-muted-foreground" }), _jsx("p", { className: "text-xs font-medium text-muted-foreground", children: t('street1', 'Street Address') })] }), _jsx("p", { className: "text-sm font-medium", children: order.shippingAddress?.streetLine1 || '—' })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-xs font-medium text-muted-foreground", children: t('street2', 'Street Address 2') }), _jsx("p", { className: "text-sm font-medium", children: order.shippingAddress?.streetLine2 || '—' })] })] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-xs font-medium text-muted-foreground", children: t('city', 'City') }), _jsx("p", { className: "text-sm font-medium", children: order.shippingAddress?.city || '—' })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-xs font-medium text-muted-foreground", children: t('postalCode', 'Postal Code') }), _jsx("p", { className: "text-sm font-medium", children: order.shippingAddress?.postalCode || '—' })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-xs font-medium text-muted-foreground", children: t('country', 'Country') }), _jsx("p", { className: "text-sm font-medium", children: order.shippingAddress?.country || '—' })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Phone, { className: "size-4 text-muted-foreground" }), _jsx("p", { className: "text-xs font-medium text-muted-foreground", children: t('phoneNumber', 'Phone Number') })] }), _jsx("p", { className: "text-sm font-medium", children: order.shippingAddress?.phoneNumber || '—' })] })] })] }), _jsxs("div", { className: "mt-4 border-t pt-4", children: [_jsx("p", { className: "mb-2 text-xs font-medium text-muted-foreground", children: t('fulfillment.shippingMethod', 'Shipping Method') }), order.shippingLines.map((line) => (_jsxs("div", { className: "flex items-center justify-between rounded-md bg-muted/50 p-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Truck, { className: "size-4 text-blue-500 dark:text-blue-400" }), _jsx("span", { className: "font-medium", children: line.shippingMethod.name })] }), _jsx(Badge, { variant: "outline", className: "font-mono", children: priceFormatter(line.priceWithTax, order.currencyCode) })] }, line.id)))] })] }), _jsx(CustomCard, { color: "green", title: t('fulfillment.fulfillmentOptions', 'Fulfillment Options'), icon: _jsx(Package, {}), bottomRight: _jsx(Button, { type: "submit", className: "w-full gap-2", disabled: isSubmitting, children: isSubmitting ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "size-4 animate-spin" }), t('fulfillment.processing', 'Processing...')] })) : (_jsxs(_Fragment, { children: [_jsx(CheckCircle2, { className: "size-4" }), t('fulfillment.fulfill', 'Complete Fulfillment')] })) }), children: _jsx(ArgumentFieldsComponent, { actions: filteredFulfillmentHandlers, args: handlerValue?.arguments?.length
90
+ ? handlerValue.arguments
91
+ : filteredFulfillmentHandlers[0]?.args.map((arg) => ({
92
+ name: arg.name,
93
+ value: JSON.stringify(arg.defaultValue ?? ''),
94
+ })) || [], setArg: (argument, data) => {
95
+ const newArgs = handlerValue?.arguments?.map((arg) => {
77
96
  if (arg.name === argument.name)
78
97
  return { ...arg, value: data.value };
79
98
  return arg;
80
99
  });
81
- if (!state.handler?.value.code || !newArgs)
100
+ if (!handlerValue?.code || !newArgs)
82
101
  return;
83
- setField('handler', { code: state.handler?.value.code, arguments: newArgs });
102
+ form.setField('handler', { code: handlerValue.code, arguments: newArgs });
84
103
  } }) })] }) }) })] })] })] }));
85
104
  };
@@ -1,16 +1,33 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useEffect, useMemo, useState } from 'react';
4
- import { useOrder, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Checkbox, Label, Textarea, Tooltip, TooltipContent, TooltipTrigger, Separator, useGFFLP, useTranslation, } from '@deenruv/react-ui-devkit';
4
+ import { useOrder, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Checkbox, Label, Textarea, Tooltip, TooltipContent, TooltipTrigger, Separator, useDeenruvForm, z, useTranslation, } from '@deenruv/react-ui-devkit';
5
5
  import { toast } from 'sonner';
6
6
  import { PriceChangedInfo } from "./PriceChangedInfo.js";
7
7
  import { FileEdit, Truck, Tag, CreditCard, AlertCircle, Save, Loader2, RefreshCw } from 'lucide-react';
8
8
  import { RefundCard } from "./RefundCard.js";
9
+ const modifyOrderSchema = z.object({
10
+ note: z.string().default(''),
11
+ options: z
12
+ .object({
13
+ recalculateShipping: z.boolean().default(false),
14
+ freezePromotions: z.boolean().default(false),
15
+ })
16
+ .default({ recalculateShipping: false, freezePromotions: false }),
17
+ });
9
18
  export const ModifyingCard = ({ onNoteModified, onOptionsChange, changes }) => {
10
19
  const { t } = useTranslation('orders');
11
20
  const { modifiedOrder, setModifyOrderInput, modifyOrder, modifyOrderInput } = useOrder();
12
21
  const [isSubmitting, setIsSubmitting] = useState(false);
13
- const { state, setField } = useGFFLP('ModifyOrderInput')({});
22
+ const form = useDeenruvForm({
23
+ schema: modifyOrderSchema,
24
+ defaultValues: {
25
+ note: '',
26
+ options: { recalculateShipping: false, freezePromotions: false },
27
+ },
28
+ });
29
+ const note = form.watch('note');
30
+ const options = form.watch('options');
14
31
  const [noteAdded, setNoteAdded] = useState(false);
15
32
  const [sendRefund, setSendRefund] = useState(false);
16
33
  const [refundReason, setRefundReason] = useState('');
@@ -25,13 +42,13 @@ export const ModifyingCard = ({ onNoteModified, onOptionsChange, changes }) => {
25
42
  if (priceDifference < 0)
26
43
  setSendRefund(true);
27
44
  }, [priceDifference]);
28
- useEffect(() => setNoteAdded(!!state.note?.value), [state.note?.value]);
45
+ useEffect(() => setNoteAdded(!!note), [note]);
29
46
  useEffect(() => {
30
- if (modifiedOrder && state.note?.value) {
47
+ if (modifiedOrder && note) {
31
48
  setModifyOrderInput({
32
49
  ...modifyOrderInput,
33
- note: state.note?.value,
34
- options: state.options?.value,
50
+ note,
51
+ options,
35
52
  refund: sendRefund && currentPayment?.transactionId
36
53
  ? {
37
54
  paymentId: currentPayment.transactionId,
@@ -41,7 +58,7 @@ export const ModifyingCard = ({ onNoteModified, onOptionsChange, changes }) => {
41
58
  : undefined,
42
59
  });
43
60
  }
44
- }, [state, modifiedOrder]);
61
+ }, [note, options, modifiedOrder]);
45
62
  const acceptModifiedChanges = async () => {
46
63
  if (!noteAdded)
47
64
  return;
@@ -59,19 +76,19 @@ export const ModifyingCard = ({ onNoteModified, onOptionsChange, changes }) => {
59
76
  setIsSubmitting(false);
60
77
  }
61
78
  };
62
- return (_jsx("form", { className: "h-full max-h-[calc(100vh-200px)]", children: _jsxs(Card, { className: "flex h-full flex-col border-l-4 border-l-blue-500 shadow-sm transition-shadow duration-200 hover:shadow dark:border-l-blue-400", children: [_jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(FileEdit, { className: "size-5 text-blue-500 dark:text-blue-400" }), _jsxs("div", { children: [_jsx(CardTitle, { children: t('orderModification', 'Order Modification') }), _jsx(CardDescription, { className: "mt-1", children: t('orderModificationDescription', 'Modify order details and apply changes') })] })] }) }), _jsxs(CardContent, { className: "flex-1 space-y-6 overflow-y-auto", children: [_jsxs("div", { className: "space-y-3", children: [_jsxs(Label, { htmlFor: "note", className: "text-sm font-medium", children: [t('note', 'Modification Note'), " ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsxs("div", { className: "relative", children: [_jsx(Textarea, { id: "note", placeholder: t('notePlaceholder', 'Enter a note explaining the reason for these modifications...'), value: state.note?.value ?? '', onChange: (e) => setField('note', e.target.value), className: "min-h-[60px] resize-y" }), !state.note?.value && (_jsx("div", { className: "absolute top-3 right-3 text-amber-500", children: _jsx(AlertCircle, { className: "size-4" }) }))] }), _jsx("p", { className: "text-xs text-muted-foreground", children: t('noteRequired', 'A note is required to explain why these changes are being made') })] }), _jsx(Separator, {}), _jsxs("div", { className: "space-y-3", children: [_jsx("h3", { className: "text-sm font-medium text-muted-foreground", children: t('modificationOptions', 'Modification Options') }), _jsxs("div", { className: "flex flex-col space-y-4", children: [_jsxs("div", { className: "space-y-4 rounded-md border border-border bg-muted/20 p-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex size-8 items-center justify-center rounded-full bg-blue-100 dark:bg-blue-900/30", children: _jsx(RefreshCw, { className: "size-4 text-blue-500 dark:text-blue-400" }) }), _jsx("h4", { className: "font-medium", children: t('processingOptions', 'Processing Options') })] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center space-x-2", children: [_jsx(Checkbox, { id: "recalculateShipping", checked: state.options?.value?.recalculateShipping ?? false, onCheckedChange: (e) => {
79
+ return (_jsx("form", { className: "h-full max-h-[calc(100vh-200px)]", children: _jsxs(Card, { className: "flex h-full flex-col border-l-4 border-l-blue-500 shadow-sm transition-shadow duration-200 hover:shadow dark:border-l-blue-400", children: [_jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(FileEdit, { className: "size-5 text-blue-500 dark:text-blue-400" }), _jsxs("div", { children: [_jsx(CardTitle, { children: t('orderModification', 'Order Modification') }), _jsx(CardDescription, { className: "mt-1", children: t('orderModificationDescription', 'Modify order details and apply changes') })] })] }) }), _jsxs(CardContent, { className: "flex-1 space-y-6 overflow-y-auto", children: [_jsxs("div", { className: "space-y-3", children: [_jsxs(Label, { htmlFor: "note", className: "text-sm font-medium", children: [t('note', 'Modification Note'), " ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsxs("div", { className: "relative", children: [_jsx(Textarea, { id: "note", placeholder: t('notePlaceholder', 'Enter a note explaining the reason for these modifications...'), value: note ?? '', onChange: (e) => form.setField('note', e.target.value), className: "min-h-[60px] resize-y" }), !note && (_jsx("div", { className: "absolute top-3 right-3 text-amber-500", children: _jsx(AlertCircle, { className: "size-4" }) }))] }), _jsx("p", { className: "text-xs text-muted-foreground", children: t('noteRequired', 'A note is required to explain why these changes are being made') })] }), _jsx(Separator, {}), _jsxs("div", { className: "space-y-3", children: [_jsx("h3", { className: "text-sm font-medium text-muted-foreground", children: t('modificationOptions', 'Modification Options') }), _jsxs("div", { className: "flex flex-col space-y-4", children: [_jsxs("div", { className: "space-y-4 rounded-md border border-border bg-muted/20 p-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex size-8 items-center justify-center rounded-full bg-blue-100 dark:bg-blue-900/30", children: _jsx(RefreshCw, { className: "size-4 text-blue-500 dark:text-blue-400" }) }), _jsx("h4", { className: "font-medium", children: t('processingOptions', 'Processing Options') })] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center space-x-2", children: [_jsx(Checkbox, { id: "recalculateShipping", checked: options?.recalculateShipping ?? false, onCheckedChange: (e) => {
63
80
  const optionsObj = {
64
- freezePromotions: state?.options?.value?.freezePromotions || false,
81
+ freezePromotions: options?.freezePromotions || false,
65
82
  recalculateShipping: !!e,
66
83
  };
67
- setField('options', optionsObj);
84
+ form.setField('options', optionsObj);
68
85
  onOptionsChange(optionsObj);
69
- } }), _jsxs(Label, { htmlFor: "recalculateShipping", className: "flex cursor-pointer items-center gap-2 text-sm font-medium", children: [_jsx(Truck, { className: "size-4 text-blue-500 dark:text-blue-400" }), t('recalculateShipping', 'Recalculate Shipping')] })] }), _jsxs("div", { className: "flex items-center space-x-2", children: [_jsx(Checkbox, { id: "freezePromotions", checked: state.options?.value?.freezePromotions ?? false, onCheckedChange: (e) => {
86
+ } }), _jsxs(Label, { htmlFor: "recalculateShipping", className: "flex cursor-pointer items-center gap-2 text-sm font-medium", children: [_jsx(Truck, { className: "size-4 text-blue-500 dark:text-blue-400" }), t('recalculateShipping', 'Recalculate Shipping')] })] }), _jsxs("div", { className: "flex items-center space-x-2", children: [_jsx(Checkbox, { id: "freezePromotions", checked: options?.freezePromotions ?? false, onCheckedChange: (e) => {
70
87
  const optionsObj = {
71
88
  freezePromotions: !!e,
72
- recalculateShipping: state?.options?.value?.recalculateShipping || false,
89
+ recalculateShipping: options?.recalculateShipping || false,
73
90
  };
74
- setField('options', optionsObj);
91
+ form.setField('options', optionsObj);
75
92
  onOptionsChange(optionsObj);
76
93
  } }), _jsxs(Label, { htmlFor: "freezePromotions", className: "flex cursor-pointer items-center gap-2 text-sm font-medium", children: [_jsx(Tag, { className: "size-4 text-blue-500 dark:text-blue-400" }), t('freezePromotions', 'Freeze Promotions')] })] }), !!priceDifference && (_jsxs("div", { className: "flex items-center space-x-2", children: [_jsx(Checkbox, { id: "refund", checked: sendRefund, onCheckedChange: (e) => setSendRefund(e) }), _jsxs(Label, { htmlFor: "refund", className: "flex cursor-pointer items-center gap-2 text-sm font-medium", children: [_jsx(CreditCard, { className: "size-4 text-blue-500 dark:text-blue-400" }), t('refund', 'Process Refund')] })] }))] })] }), sendRefund && (_jsx(RefundCard, { priceDifference: priceDifference, refundReason: refundReason, setRefundReason: setRefundReason }))] })] }), _jsx("div", { className: "flex justify-end", children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { tabIndex: 0, children: _jsx(Button, { onClick: acceptModifiedChanges, disabled: !noteAdded || isSubmitting, type: "button", className: "gap-2", children: isSubmitting ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "size-4 animate-spin" }), t('processing', 'Processing...')] })) : (_jsxs(_Fragment, { children: [_jsx(Save, { className: "size-4" }), t('applyChanges', 'Apply Changes')] })) }) }) }), !noteAdded && (_jsx(TooltipContent, { className: "border border-amber-200 bg-amber-50 text-amber-800 dark:border-amber-800 dark:bg-amber-900/50 dark:text-amber-200", children: _jsxs("div", { className: "flex items-center gap-2 px-1", children: [_jsx(AlertCircle, { className: "size-4" }), t('disabledBtnTooltip', 'A note is required to apply changes')] }) }))] }) })] }), _jsx(PriceChangedInfo, { changes })] }) }));
77
94
  };
@@ -1,9 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Button, Dialog, DialogContent, DialogTitle, ScrollArea, useOrder, useServer, EntityCustomFields, useTranslation, } from '@deenruv/react-ui-devkit';
3
3
  import { useState } from 'react';
4
+ import { useShallow } from 'zustand/react/shallow';
4
5
  export const OrderLineCustomFields = ({ line, order, mode }) => {
5
6
  const { setModifiedOrder } = useOrder(({ setModifiedOrder }) => ({ setModifiedOrder }));
6
- const orderLineCustomFields = useServer((p) => p.serverConfig?.entityCustomFields?.find((el) => el.entityName === 'OrderLine')?.customFields || []);
7
+ const orderLineCustomFields = useServer(useShallow((p) => p.serverConfig?.entityCustomFields?.find((el) => el.entityName === 'OrderLine')?.customFields || []));
7
8
  const { t } = useTranslation('common');
8
9
  const [open, setOpen] = useState(false);
9
10
  if (!order)
@@ -1,11 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useMemo } from 'react';
3
+ import { useShallow } from 'zustand/react/shallow';
3
4
  import { ScrollArea, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, Timeline, TimelineItem, TimelineLine, TimelineDot, TimelineContent, TimelineHeading, useServer, Button, Badge, useTranslation, } from '@deenruv/react-ui-devkit';
4
5
  import { ArrowRight, Check, CircleDot } from 'lucide-react';
5
6
  export const PossibleOrderStates = ({ orderState }) => {
6
7
  const { t } = useTranslation('orders');
7
8
  const { t: tCommon } = useTranslation('common');
8
- const orderProcess = useServer((p) => p.serverConfig?.orderProcess || []);
9
+ const orderProcess = useServer(useShallow((p) => p.serverConfig?.orderProcess || []));
9
10
  const statesAfterCancelled = useMemo(() => {
10
11
  const cancelledIndex = orderProcess.findIndex((s) => s.name === 'Cancelled');
11
12
  if (cancelledIndex === -1)
@@ -4,6 +4,7 @@ import { TableHeader, TableRow, TableHead, TableBody, TableCell, Table, Dropdown
4
4
  import { removeOrderItemsResultSelector, updateOrderItemsSelector, updatedDraftOrderSelector, } from "../../../graphql/draft_order";
5
5
  import { EllipsisVertical, InfoIcon, Trash2, ShoppingCart, Package, Tag, Edit } from 'lucide-react';
6
6
  import { useCallback, useMemo, useState } from 'react';
7
+ import { useShallow } from 'zustand/react/shallow';
7
8
  import { toast } from 'sonner';
8
9
  import { OrderLineActionModal } from './OrderLineActionModal/index.js';
9
10
  import { CustomComponent } from './CustomComponent.js';
@@ -11,7 +12,7 @@ import { OrderLineCustomFields } from './OrderLineCustomFields.js';
11
12
  import { SpecialLineItem } from './SpecialLineItem.js';
12
13
  export const ProductsCard = () => {
13
14
  const { t } = useTranslation('orders');
14
- const orderLineCustomFields = useServer((p) => p.serverConfig?.entityCustomFields?.find((el) => el.entityName === 'OrderLine')?.customFields || []);
15
+ const orderLineCustomFields = useServer(useShallow((p) => p.serverConfig?.entityCustomFields?.find((el) => el.entityName === 'OrderLine')?.customFields || []));
15
16
  const { mode, order, setOrder, setModifiedOrder, modifiedOrder, fetchOrder } = useOrder();
16
17
  const currentOrder = useMemo(() => (mode === 'update' ? (modifiedOrder ? modifiedOrder : order) : order), [mode, order, modifiedOrder]);
17
18
  const [open, setOpen] = useState(false);