@deenruv/admin-dashboard 1.0.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/LICENSE +23 -0
- package/README.md +30 -0
- package/dist/@types/resources.js +51 -0
- package/dist/DeenruvAdminPanel.js +118 -0
- package/dist/DeenruvDeveloperIndicator.js +57 -0
- package/dist/components/Aexol.js +4 -0
- package/dist/components/BrandLogo.js +22 -0
- package/dist/components/CanLeaveRouteDialog.js +8 -0
- package/dist/components/DataTable.js +13 -0
- package/dist/components/DeleteDialog.js +6 -0
- package/dist/components/DuplicateEntry.js +46 -0
- package/dist/components/GlobalSearch.js +134 -0
- package/dist/components/History/AddEntryForm.js +29 -0
- package/dist/components/History/DeleteEntryDialog.js +25 -0
- package/dist/components/History/EditEntryDialog.js +32 -0
- package/dist/components/History/History.js +13 -0
- package/dist/components/History/ModifyHistoryInfo.js +6 -0
- package/dist/components/History/Timeline.js +53 -0
- package/dist/components/History/index.js +5 -0
- package/dist/components/Menu/ActiveAdmins.js +24 -0
- package/dist/components/Menu/ChannelSwitcher.js +20 -0
- package/dist/components/Menu/LanguagesDropdown.js +26 -0
- package/dist/components/Menu/Navigation.js +270 -0
- package/dist/components/Menu/NavigationFooter.js +12 -0
- package/dist/components/Menu/Notifications.js +90 -0
- package/dist/components/Menu/index.js +67 -0
- package/dist/components/index.js +6 -0
- package/dist/graphql/base.js +95 -0
- package/dist/graphql/collections.js +94 -0
- package/dist/graphql/draft_order.js +307 -0
- package/dist/graphql/orders.js +157 -0
- package/dist/graphql/products.js +230 -0
- package/dist/graphql/scalars.js +22 -0
- package/dist/i18.js +13 -0
- package/dist/index.css +161 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +1 -0
- package/dist/locales/en/admins.json +57 -0
- package/dist/locales/en/assets.json +69 -0
- package/dist/locales/en/channels.json +66 -0
- package/dist/locales/en/collections.json +145 -0
- package/dist/locales/en/common.json +1127 -0
- package/dist/locales/en/countries.json +50 -0
- package/dist/locales/en/customerGroups.json +6 -0
- package/dist/locales/en/customers.json +109 -0
- package/dist/locales/en/dashboard.json +34 -0
- package/dist/locales/en/facets.json +78 -0
- package/dist/locales/en/globalSettings.json +15 -0
- package/dist/locales/en/index.js +52 -0
- package/dist/locales/en/orders.json +932 -0
- package/dist/locales/en/paymentMethods.json +59 -0
- package/dist/locales/en/permissions.json +94 -0
- package/dist/locales/en/products.json +194 -0
- package/dist/locales/en/promotions.json +65 -0
- package/dist/locales/en/roles.json +59 -0
- package/dist/locales/en/sellers.json +41 -0
- package/dist/locales/en/shippingMethods.json +84 -0
- package/dist/locales/en/stockLocations.json +40 -0
- package/dist/locales/en/system.json +41 -0
- package/dist/locales/en/table.json +201 -0
- package/dist/locales/en/taxCategories.json +47 -0
- package/dist/locales/en/taxRates.json +56 -0
- package/dist/locales/en/zones.json +47 -0
- package/dist/locales/index.js +2 -0
- package/dist/locales/pl/admins.json +57 -0
- package/dist/locales/pl/assets.json +69 -0
- package/dist/locales/pl/channels.json +66 -0
- package/dist/locales/pl/collections.json +145 -0
- package/dist/locales/pl/common.json +1128 -0
- package/dist/locales/pl/countries.json +50 -0
- package/dist/locales/pl/customerGroups.json +6 -0
- package/dist/locales/pl/customers.json +109 -0
- package/dist/locales/pl/dashboard.json +34 -0
- package/dist/locales/pl/facets.json +78 -0
- package/dist/locales/pl/globalSettings.json +15 -0
- package/dist/locales/pl/index.js +52 -0
- package/dist/locales/pl/orders.json +932 -0
- package/dist/locales/pl/paymentMethods.json +59 -0
- package/dist/locales/pl/permissions.json +94 -0
- package/dist/locales/pl/products.json +194 -0
- package/dist/locales/pl/promotions.json +65 -0
- package/dist/locales/pl/roles.json +59 -0
- package/dist/locales/pl/sellers.json +41 -0
- package/dist/locales/pl/shippingMethods.json +84 -0
- package/dist/locales/pl/stockLocations.json +40 -0
- package/dist/locales/pl/system.json +41 -0
- package/dist/locales/pl/table.json +201 -0
- package/dist/locales/pl/taxCategories.json +47 -0
- package/dist/locales/pl/taxRates.json +56 -0
- package/dist/locales/pl/zones.json +47 -0
- package/dist/notifications/OrderStatusNotification.js +47 -0
- package/dist/notifications/SystemStatusNotification.js +19 -0
- package/dist/pages/Custom404.js +5 -0
- package/dist/pages/LoginScreen.js +37 -0
- package/dist/pages/Root.js +252 -0
- package/dist/pages/admins/Detail.js +72 -0
- package/dist/pages/admins/List.js +56 -0
- package/dist/pages/admins/_components/AdminDetailView.js +34 -0
- package/dist/pages/admins/_components/RolesCard.js +31 -0
- package/dist/pages/admins/index.js +2 -0
- package/dist/pages/assets/List.js +82 -0
- package/dist/pages/assets/_components/Asset.js +114 -0
- package/dist/pages/assets/_components/AssetListView.js +29 -0
- package/dist/pages/assets/_components/EditAssetDialog.js +85 -0
- package/dist/pages/assets/_components/UploadAssetDialog.js +133 -0
- package/dist/pages/assets/_components/UploadProgress.js +20 -0
- package/dist/pages/assets/index.js +1 -0
- package/dist/pages/channels/Detail.js +149 -0
- package/dist/pages/channels/List.js +46 -0
- package/dist/pages/channels/_components/ChannelDetailView.js +51 -0
- package/dist/pages/channels/_components/DefaultsCard.js +25 -0
- package/dist/pages/channels/index.js +2 -0
- package/dist/pages/collections/Detail.js +61 -0
- package/dist/pages/collections/List.js +135 -0
- package/dist/pages/collections/_components/CollectionDetailView.js +52 -0
- package/dist/pages/collections/_components/CollectionProductVariantsDrawer.js +28 -0
- package/dist/pages/collections/_components/CombinationMode.js +6 -0
- package/dist/pages/collections/_components/ContentsCard.js +43 -0
- package/dist/pages/collections/_components/ContentsTable.js +102 -0
- package/dist/pages/collections/_components/DeleteCollectionsFromChannel.js +36 -0
- package/dist/pages/collections/_components/FacetsSelector.js +30 -0
- package/dist/pages/collections/_components/FiltersCard.js +101 -0
- package/dist/pages/collections/_components/MoveCollectionsToCollections.js +107 -0
- package/dist/pages/collections/_components/MoveEntityToChannels.js +115 -0
- package/dist/pages/collections/_components/PageHeader.js +11 -0
- package/dist/pages/collections/_components/SelectedCollectionsModal.js +22 -0
- package/dist/pages/collections/_components/VariantsSelector.js +43 -0
- package/dist/pages/collections/consts.js +7 -0
- package/dist/pages/collections/index.js +2 -0
- package/dist/pages/countries/Detail.js +57 -0
- package/dist/pages/countries/List.js +47 -0
- package/dist/pages/countries/_components/CountryDetailView.js +28 -0
- package/dist/pages/countries/index.js +2 -0
- package/dist/pages/customer-groups/Detail.js +76 -0
- package/dist/pages/customer-groups/List.js +41 -0
- package/dist/pages/customer-groups/_components/CustomerGroupsDetailView.js +21 -0
- package/dist/pages/customer-groups/index.js +2 -0
- package/dist/pages/customers/Detail.js +75 -0
- package/dist/pages/customers/List.js +60 -0
- package/dist/pages/customers/_components/Address.js +59 -0
- package/dist/pages/customers/_components/AddressDialog.js +56 -0
- package/dist/pages/customers/_components/AddressForm.js +77 -0
- package/dist/pages/customers/_components/AddressesCard.js +9 -0
- package/dist/pages/customers/_components/CustomerDetailSidebar.js +9 -0
- package/dist/pages/customers/_components/CustomerDetailView.js +41 -0
- package/dist/pages/customers/_components/CustomerGroupsCard.js +46 -0
- package/dist/pages/customers/_components/HistoryTab.js +65 -0
- package/dist/pages/customers/_components/OrdersTab.js +46 -0
- package/dist/pages/customers/_components/PersonalDataCard.js +6 -0
- package/dist/pages/customers/_components/VerifiedCard.js +6 -0
- package/dist/pages/customers/index.js +2 -0
- package/dist/pages/dashboard/Dashboard.js +10 -0
- package/dist/pages/dashboard/index.js +1 -0
- package/dist/pages/extensions/Extensions.js +74 -0
- package/dist/pages/extensions/index.js +1 -0
- package/dist/pages/facets/Detail.js +56 -0
- package/dist/pages/facets/List.js +48 -0
- package/dist/pages/facets/_components/AddFacetValueDialog.js +105 -0
- package/dist/pages/facets/_components/ColorSample.js +18 -0
- package/dist/pages/facets/_components/FacetDetailView.js +98 -0
- package/dist/pages/facets/index.js +2 -0
- package/dist/pages/global-settings/GlobalSettingsComponent.js +30 -0
- package/dist/pages/global-settings/index.js +71 -0
- package/dist/pages/index.js +25 -0
- package/dist/pages/orders/Detail.js +21 -0
- package/dist/pages/orders/List.js +132 -0
- package/dist/pages/orders/_components/AddPaymentDialog.js +50 -0
- package/dist/pages/orders/_components/AddressCard.js +260 -0
- package/dist/pages/orders/_components/CancelAndRefundDialog.js +29 -0
- package/dist/pages/orders/_components/ChangesRegister.js +68 -0
- package/dist/pages/orders/_components/ChangesRegisterTable.js +18 -0
- package/dist/pages/orders/_components/CouponCodesCard.js +85 -0
- package/dist/pages/orders/_components/CustomComponent.js +21 -0
- package/dist/pages/orders/_components/CustomerSelectCard.js +117 -0
- package/dist/pages/orders/_components/EditNoteButton.js +10 -0
- package/dist/pages/orders/_components/FulfillmentModal.js +85 -0
- package/dist/pages/orders/_components/LineItem.js +7 -0
- package/dist/pages/orders/_components/ManualOrderChangeModal.js +47 -0
- package/dist/pages/orders/_components/ModifyAcceptModal.js +22 -0
- package/dist/pages/orders/_components/ModifyingCard.js +77 -0
- package/dist/pages/orders/_components/OrderHistory.js +59 -0
- package/dist/pages/orders/_components/OrderLineActionModal/ActionQuantityPrice.js +58 -0
- package/dist/pages/orders/_components/OrderLineActionModal/index.js +7 -0
- package/dist/pages/orders/_components/OrderLineActionModal/types.js +1 -0
- package/dist/pages/orders/_components/OrderLineCustomFields.js +26 -0
- package/dist/pages/orders/_components/OrderSummary.js +10 -0
- package/dist/pages/orders/_components/PaymentMetadata.js +6 -0
- package/dist/pages/orders/_components/Payments.js +113 -0
- package/dist/pages/orders/_components/PossibleOrderStates.js +54 -0
- package/dist/pages/orders/_components/PriceChangedInfo.js +16 -0
- package/dist/pages/orders/_components/ProductsCard.js +308 -0
- package/dist/pages/orders/_components/ProductsTable.js +29 -0
- package/dist/pages/orders/_components/PromotionsList.js +73 -0
- package/dist/pages/orders/_components/RealizationCard.js +98 -0
- package/dist/pages/orders/_components/RefundCard.js +11 -0
- package/dist/pages/orders/_components/RefundPaymentCard.js +13 -0
- package/dist/pages/orders/_components/ShippingMethod.js +112 -0
- package/dist/pages/orders/_components/SpecialLineItem.js +7 -0
- package/dist/pages/orders/_components/SurchargeCard.js +99 -0
- package/dist/pages/orders/_components/SurchargeTable.js +8 -0
- package/dist/pages/orders/_components/TaxSummary.js +11 -0
- package/dist/pages/orders/_components/ToRealizationForm.js +64 -0
- package/dist/pages/orders/_components/TopActions.js +219 -0
- package/dist/pages/orders/_components/index.js +25 -0
- package/dist/pages/orders/index.js +2 -0
- package/dist/pages/payment-methods/Detail.js +67 -0
- package/dist/pages/payment-methods/List.js +41 -0
- package/dist/pages/payment-methods/_components/OptionsCard.js +60 -0
- package/dist/pages/payment-methods/_components/PaymentMethodDetailView.js +53 -0
- package/dist/pages/payment-methods/index.js +2 -0
- package/dist/pages/product-variants/Detail.js +4 -0
- package/dist/pages/product-variants/List.js +76 -0
- package/dist/pages/product-variants/index.js +2 -0
- package/dist/pages/products/Detail.js +75 -0
- package/dist/pages/products/List.js +79 -0
- package/dist/pages/products/_components/AddOptionGroupDialog.js +65 -0
- package/dist/pages/products/_components/AssetsCard.js +35 -0
- package/dist/pages/products/_components/BasicFieldsCard.js +6 -0
- package/dist/pages/products/_components/ChannelsCard.js +6 -0
- package/dist/pages/products/_components/CollectionsCard.js +6 -0
- package/dist/pages/products/_components/Container.js +4 -0
- package/dist/pages/products/_components/DiscountRatingCard.js +6 -0
- package/dist/pages/products/_components/FacetValuesCard.js +6 -0
- package/dist/pages/products/_components/ImagesCard.js +6 -0
- package/dist/pages/products/_components/OptionGroup.js +93 -0
- package/dist/pages/products/_components/OptionValueCard.js +59 -0
- package/dist/pages/products/_components/OptionsCard.js +38 -0
- package/dist/pages/products/_components/OptionsTab.js +40 -0
- package/dist/pages/products/_components/PriceCard.js +27 -0
- package/dist/pages/products/_components/ProductDetailSidebar.js +30 -0
- package/dist/pages/products/_components/ProductDetailView.js +54 -0
- package/dist/pages/products/_components/SettingsCard.js +6 -0
- package/dist/pages/products/_components/StockCard.js +40 -0
- package/dist/pages/products/_components/Variant.js +149 -0
- package/dist/pages/products/_components/VariantsTab.js +46 -0
- package/dist/pages/products/index.js +2 -0
- package/dist/pages/promotions/Detail.js +80 -0
- package/dist/pages/promotions/List.js +38 -0
- package/dist/pages/promotions/_components/ActionsCard.js +67 -0
- package/dist/pages/promotions/_components/BasicFieldsCard.js +6 -0
- package/dist/pages/promotions/_components/ConditionsCard.js +70 -0
- package/dist/pages/promotions/_components/CustomerGroupsSelector.js +25 -0
- package/dist/pages/promotions/_components/EnabledCard.js +6 -0
- package/dist/pages/promotions/_components/OptionsCard.js +6 -0
- package/dist/pages/promotions/_components/PromotionDetailSidebar.js +15 -0
- package/dist/pages/promotions/_components/PromotionDetailView.js +66 -0
- package/dist/pages/promotions/_components/PromotionFieldRender.js +4 -0
- package/dist/pages/promotions/index.js +2 -0
- package/dist/pages/roles/Detail.js +72 -0
- package/dist/pages/roles/List.js +70 -0
- package/dist/pages/roles/_components/PermissionsCard.js +7 -0
- package/dist/pages/roles/_components/PermissionsTable.js +42 -0
- package/dist/pages/roles/_components/RoleDetailView.js +49 -0
- package/dist/pages/roles/index.js +2 -0
- package/dist/pages/sellers/Detail.js +48 -0
- package/dist/pages/sellers/List.js +37 -0
- package/dist/pages/sellers/_components/SellerDetailView.js +20 -0
- package/dist/pages/sellers/index.js +2 -0
- package/dist/pages/shipping-methods/Detail.js +105 -0
- package/dist/pages/shipping-methods/List.js +40 -0
- package/dist/pages/shipping-methods/_components/CalculatorCard.js +79 -0
- package/dist/pages/shipping-methods/_components/CheckerCard.js +76 -0
- package/dist/pages/shipping-methods/_components/Lines.js +20 -0
- package/dist/pages/shipping-methods/_components/ShippingMethodDetailView.js +67 -0
- package/dist/pages/shipping-methods/_components/TestCard.js +62 -0
- package/dist/pages/shipping-methods/index.js +2 -0
- package/dist/pages/status/Status.js +6 -0
- package/dist/pages/status/_components/FilterToolbar.js +29 -0
- package/dist/pages/status/_components/Health.js +21 -0
- package/dist/pages/status/_components/JobResultPopover.js +8 -0
- package/dist/pages/status/_components/Jobs.js +199 -0
- package/dist/pages/status/_components/JsonExplorer.js +44 -0
- package/dist/pages/status/_components/JsonPopup.js +8 -0
- package/dist/pages/status/index.js +1 -0
- package/dist/pages/stock-locations/Detail.js +77 -0
- package/dist/pages/stock-locations/List.js +38 -0
- package/dist/pages/stock-locations/_components/StockLocationDetailView.js +22 -0
- package/dist/pages/stock-locations/index.js +2 -0
- package/dist/pages/tax-categories/Detail.js +85 -0
- package/dist/pages/tax-categories/List.js +46 -0
- package/dist/pages/tax-categories/_components/TaxCategoryDetailView.js +24 -0
- package/dist/pages/tax-categories/index.js +2 -0
- package/dist/pages/tax-rates/Detail.js +62 -0
- package/dist/pages/tax-rates/List.js +64 -0
- package/dist/pages/tax-rates/_components/TaxRateDetailView.js +64 -0
- package/dist/pages/tax-rates/index.js +2 -0
- package/dist/pages/zones/Detail.js +86 -0
- package/dist/pages/zones/List.js +52 -0
- package/dist/pages/zones/_components/ZoneDetailView.js +49 -0
- package/dist/pages/zones/index.js +2 -0
- package/dist/version.js +1 -0
- package/package.json +122 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useState } from 'react';
|
|
4
|
+
import { MultipleSelector, useLazyQuery, useOrder, useMutation, OrderDetailSelector, EmptyState, CustomCard, ORDER_STATE, useTranslation, } from '@deenruv/react-ui-devkit';
|
|
5
|
+
import { typedGql, scalars, $ } from '@deenruv/admin-types';
|
|
6
|
+
import { toast } from 'sonner';
|
|
7
|
+
import { Ticket, Search } from 'lucide-react';
|
|
8
|
+
const PromotionCodesQuery = typedGql('query', { scalars })({
|
|
9
|
+
promotions: [
|
|
10
|
+
{ options: { filter: { couponCode: { contains: $('code', 'String!') } } } },
|
|
11
|
+
{ items: { couponCode: true, name: true } },
|
|
12
|
+
],
|
|
13
|
+
});
|
|
14
|
+
const ApplyCouponCodeMutation = typedGql('mutation', { scalars })({
|
|
15
|
+
applyCouponCodeToDraftOrder: [
|
|
16
|
+
{ orderId: $('orderId', 'ID!'), couponCode: $('couponCode', 'String!') },
|
|
17
|
+
{ '...on CouponCodeExpiredError': { message: true }, __typename: true, '...on Order': OrderDetailSelector },
|
|
18
|
+
],
|
|
19
|
+
});
|
|
20
|
+
const RemoveCouponCodeMutation = typedGql('mutation', { scalars })({
|
|
21
|
+
removeCouponCodeFromDraftOrder: [
|
|
22
|
+
{ orderId: $('orderId', 'ID!'), couponCode: $('couponCode', 'String!') },
|
|
23
|
+
OrderDetailSelector,
|
|
24
|
+
],
|
|
25
|
+
});
|
|
26
|
+
export const CouponCodesCard = () => {
|
|
27
|
+
const { t } = useTranslation('orders');
|
|
28
|
+
const { order, setOrder } = useOrder();
|
|
29
|
+
const [fetchPromotionCodes] = useLazyQuery(PromotionCodesQuery);
|
|
30
|
+
const [applyCoupon] = useMutation(ApplyCouponCodeMutation);
|
|
31
|
+
const [removeCoupon] = useMutation(RemoveCouponCodeMutation);
|
|
32
|
+
const [currentValue, setCurrentValue] = useState(order?.couponCodes.map((c) => ({ label: c, value: c })) || []);
|
|
33
|
+
const [isSearching, setIsSearching] = useState(false);
|
|
34
|
+
const handleChange = useCallback((e) => {
|
|
35
|
+
if (!order)
|
|
36
|
+
return;
|
|
37
|
+
const currentState = currentValue.map((v) => v.value) || [];
|
|
38
|
+
if (e.length > currentState?.length) {
|
|
39
|
+
const newCoupon = e[e.length - 1];
|
|
40
|
+
toast.promise(applyCoupon({ couponCode: newCoupon.value, orderId: order.id }).then((resp) => {
|
|
41
|
+
if (resp.applyCouponCodeToDraftOrder.__typename === 'Order') {
|
|
42
|
+
setOrder(resp.applyCouponCodeToDraftOrder);
|
|
43
|
+
setCurrentValue((prev) => [...prev, newCoupon]);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
throw new Error('Failed to apply coupon');
|
|
48
|
+
}
|
|
49
|
+
}), {
|
|
50
|
+
loading: t('couponCodes.addingCoupon', 'Applying coupon code...'),
|
|
51
|
+
success: t('couponCodes.addToastSuccess', 'Coupon code applied successfully'),
|
|
52
|
+
error: (err) => err.message,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
const couponToRemove = currentState.find((c) => !e.map((i) => i.value).includes(c));
|
|
57
|
+
if (couponToRemove) {
|
|
58
|
+
toast.promise(removeCoupon({ couponCode: couponToRemove, orderId: order.id }).then((resp) => {
|
|
59
|
+
if (resp.removeCouponCodeFromDraftOrder?.id) {
|
|
60
|
+
setOrder(resp.removeCouponCodeFromDraftOrder);
|
|
61
|
+
setCurrentValue((prev) => prev.filter((i) => i.value !== couponToRemove));
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}), {
|
|
66
|
+
loading: t('couponCodes.removingCoupon', 'Removing coupon code...'),
|
|
67
|
+
success: t('couponCodes.removeToastSuccess', 'Coupon code removed successfully'),
|
|
68
|
+
error: t('couponCodes.removeToastError', 'Failed to remove coupon code'),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}, [order, currentValue, applyCoupon, removeCoupon, setOrder, t]);
|
|
73
|
+
return (_jsxs(CustomCard, { notCollapsible: true, wrapperClassName: "h-full", color: "purple", description: t('couponCodes.description', 'Apply coupon codes to this order'), title: t('couponCodes.title', 'Coupon Codes'), icon: _jsx(Ticket, {}), children: [_jsxs("div", { className: "relative", children: [_jsx(MultipleSelector, { hideClearAllButton: true, placeholder: t('couponCodes.placeholder', 'Search for coupon codes...'), value: currentValue, onChange: handleChange, disabled: order?.state === ORDER_STATE.DELIVERED || order?.state === ORDER_STATE.CANCELLED, onSearch: (searchString) => {
|
|
74
|
+
setIsSearching(true);
|
|
75
|
+
return fetchPromotionCodes({ code: searchString })
|
|
76
|
+
.then((resp) => resp.promotions.items
|
|
77
|
+
.filter((i) => i.couponCode)
|
|
78
|
+
.map((i) => ({
|
|
79
|
+
value: i.couponCode,
|
|
80
|
+
label: i.couponCode,
|
|
81
|
+
description: i.name,
|
|
82
|
+
})))
|
|
83
|
+
.finally(() => setIsSearching(false));
|
|
84
|
+
}, delay: 500 }), _jsx("div", { className: "text-muted-foreground absolute right-3 top-2.5", children: isSearching ? (_jsx("div", { className: "size-4 animate-spin rounded-full border-2 border-current border-t-transparent" })) : (_jsx(Search, { className: "size-4" })) })] }), currentValue.length === 0 && (_jsx("div", { className: "flex h-[240px] pt-12", children: _jsx(EmptyState, { columnsLength: 2, title: t('couponCodes.noCoupons', 'No coupon codes applied'), color: "purple", icon: _jsx(Ticket, {}), small: true }) }))] }));
|
|
85
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation, EntityCustomFields, Button } from '@deenruv/react-ui-devkit';
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
export const CustomComponent = ({ onVariantAdd, orderLine, }) => {
|
|
5
|
+
const { t } = useTranslation('orders');
|
|
6
|
+
const [customFields, setCustomFields] = useState({});
|
|
7
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
console.log('KUSTOM', customFields);
|
|
10
|
+
}, []);
|
|
11
|
+
const onSubmit = async () => {
|
|
12
|
+
try {
|
|
13
|
+
setIsSubmitting(true);
|
|
14
|
+
await onVariantAdd(customFields);
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
17
|
+
setIsSubmitting(false);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
return (_jsxs("div", { className: "flex h-full flex-col rounded-md border", children: [_jsx("div", { className: "h-full grow space-y-4 overflow-y-auto p-4", children: _jsx(EntityCustomFields, { entityName: "orderLine", hideButton: true, initialValues: { customFields: {} }, onChange: setCustomFields, additionalData: { product: orderLine.product, variant: orderLine } }) }), _jsx("div", { className: "bg-muted/30 border-t p-4", children: _jsx("div", { className: "flex flex-col items-start justify-end gap-4 sm:flex-row sm:items-center", children: _jsx(Button, { onClick: onSubmit, disabled: isSubmitting, className: "min-w-[120px] self-end sm:self-auto", children: isSubmitting ? (_jsxs("span", { className: "flex items-center gap-2", children: [_jsx("span", { className: "size-4 animate-spin rounded-full border-2 border-current border-r-transparent" }), t('common:processing')] })) : (t('create.add')) }) }) })] }));
|
|
21
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
'use client';
|
|
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';
|
|
4
|
+
import { useEffect, useState } from 'react';
|
|
5
|
+
import { toast } from 'sonner';
|
|
6
|
+
import { Edit, User, Check, Mail, Phone, Loader2, UserPlus, Search, AlertCircle } from 'lucide-react';
|
|
7
|
+
export const CustomerSelectCard = () => {
|
|
8
|
+
const { t } = useTranslation('orders');
|
|
9
|
+
const { setOrder, mode, currentOrder: order, setCustomerAndAddressesForDraftOrder } = useOrder();
|
|
10
|
+
const [tab, setTab] = useState('select');
|
|
11
|
+
const [selected, setSelected] = useState(order?.customer);
|
|
12
|
+
const [open, setOpen] = useState(false);
|
|
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: {},
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
useEffect(() => setSelected(order?.customer), [order]);
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (tab === 'create')
|
|
51
|
+
clearAllForm();
|
|
52
|
+
}, [tab]);
|
|
53
|
+
const validateAndSubmitIfCorrect = async () => {
|
|
54
|
+
if (!order?.id)
|
|
55
|
+
return;
|
|
56
|
+
if (tab === 'create' && !checkIfAllFieldsAreValid()) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
setIsSubmitting(true);
|
|
60
|
+
try {
|
|
61
|
+
if (tab === 'select' && selected) {
|
|
62
|
+
setCustomerAndAddressesForDraftOrder(selected.id).then(() => {
|
|
63
|
+
setOpen(false);
|
|
64
|
+
toast.success(t('create.selectCustomer.success', 'Customer successfully assigned to order'));
|
|
65
|
+
setIsSubmitting(false);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
const { setCustomerForDraftOrder } = await apiClient('mutation')({
|
|
70
|
+
setCustomerForDraftOrder: [
|
|
71
|
+
{
|
|
72
|
+
orderId: order.id,
|
|
73
|
+
...(tab === 'select'
|
|
74
|
+
? { customerId: selected?.id }
|
|
75
|
+
: {
|
|
76
|
+
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,
|
|
82
|
+
},
|
|
83
|
+
}),
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
__typename: true,
|
|
87
|
+
'...on Order': OrderDetailSelector,
|
|
88
|
+
'...on EmailAddressConflictError': { errorCode: true, message: true },
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
});
|
|
92
|
+
if (setCustomerForDraftOrder.__typename === 'Order') {
|
|
93
|
+
setOpen(false);
|
|
94
|
+
setOrder(setCustomerForDraftOrder);
|
|
95
|
+
toast.success(t('create.selectCustomer.success', 'Customer successfully assigned to order'));
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
toast.error(t('create.selectCustomer.error', 'Failed to assign customer to order'));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
toast.error(t('create.selectCustomer.error', 'Failed to assign customer to order'));
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
setIsSubmitting(false);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
if (!order)
|
|
110
|
+
return null;
|
|
111
|
+
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: "text-muted-foreground mt-2", 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
|
|
113
|
+
? { customFields: selected.customFields }
|
|
114
|
+
: { customFields: {} }, onChange: (customFields, translations) => {
|
|
115
|
+
setField('customFields', customFields);
|
|
116
|
+
}, 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: "border-border bg-muted/50 rounded-lg border p-3", children: _jsx("div", { className: "flex items-start gap-3", children: !order?.customer ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "bg-secondary mt-0.5 flex size-8 items-center justify-center rounded-full", children: _jsx(AlertCircle, { className: "size-4 text-green-500 dark:text-green-600" }) }), _jsxs("div", { className: "flex-1", children: [_jsx("p", { className: "text-muted-foreground text-sm 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
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { AlertDialogHeader, Textarea, AlertDialogFooter, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogTitle, AlertDialogTrigger, Checkbox, Label, Button, cn, useTranslation, } from '@deenruv/react-ui-devkit';
|
|
3
|
+
import { Pencil } from 'lucide-react';
|
|
4
|
+
import { useState } from 'react';
|
|
5
|
+
export const EditNoteButton = ({ note, updateConfirmed }) => {
|
|
6
|
+
const { t } = useTranslation('orders');
|
|
7
|
+
const [isPrivate, setIsPrivate] = useState(!note.isPublic);
|
|
8
|
+
const [text, setText] = useState(note.data?.note || '');
|
|
9
|
+
return (_jsxs(AlertDialog, { children: [_jsx(AlertDialogTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", className: "flex w-full justify-start gap-2", children: [_jsx(Pencil, { className: "size-4" }), " ", t('history.edit')] }) }), _jsxs(AlertDialogContent, { className: "min-w-min", children: [_jsx(AlertDialogHeader, { children: _jsx(AlertDialogTitle, { children: t('history.editNoteHeader') }) }), _jsx(Textarea, { onChange: (e) => setText(e.currentTarget.value), value: text, className: "h-[60vh] w-auto min-w-[50vh] resize-none overflow-auto rounded-md p-2" }), _jsxs("div", { className: "flex items-center gap-2 pb-4", children: [_jsx(Checkbox, { id: "isPublic", name: "isPublic", checked: isPrivate, onClick: () => setIsPrivate((p) => !p) }), _jsxs(Label, { htmlFor: "isPublic", className: "cursor-pointer", children: [t('history.isPrivate'), _jsx("span", { className: "ml-2 text-gray-500", children: t('history.isPrivateDescription') })] }), _jsx(Label, { className: cn('ml-auto', isPrivate ? 'text-green-600' : 'text-yellow-600'), children: t(isPrivate ? 'history.toAdmins' : 'history.toAdminsAndCustomer') })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: t('history.cancel') }), _jsx(AlertDialogAction, { disabled: text === '', onClick: () => updateConfirmed({ noteId: note.id, isPublic: !isPrivate, note: text }), children: t('history.edit') })] })] })] }));
|
|
10
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use client';
|
|
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';
|
|
4
|
+
import { LineItem } from './LineItem.js';
|
|
5
|
+
import { useState } from 'react';
|
|
6
|
+
import { Package, Truck, MapPin, User, Building, Phone, Box, CheckCircle2, AlertCircle, Loader2 } from 'lucide-react';
|
|
7
|
+
export const FulfillmentModal = ({ order, onSubmitted, disabled }) => {
|
|
8
|
+
const { t } = useTranslation('orders');
|
|
9
|
+
const [open, setOpen] = useState(false);
|
|
10
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
11
|
+
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) => ({
|
|
18
|
+
orderLineId: line.id,
|
|
19
|
+
quantity: line.quantity || 1,
|
|
20
|
+
customFields: {},
|
|
21
|
+
})),
|
|
22
|
+
},
|
|
23
|
+
handler: {
|
|
24
|
+
initialValue: {
|
|
25
|
+
code: filteredFulfillmentHandlers[0]?.code,
|
|
26
|
+
arguments: filteredFulfillmentHandlers[0]?.args.map((arg) => ({
|
|
27
|
+
name: arg.name,
|
|
28
|
+
value: JSON.stringify(arg.defaultValue),
|
|
29
|
+
})),
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
const handleSubmit = async (e) => {
|
|
34
|
+
e.preventDefault();
|
|
35
|
+
if (!state.lines?.value || !state.handler?.value)
|
|
36
|
+
return;
|
|
37
|
+
const lines = state.lines?.value.map((line) => ({
|
|
38
|
+
orderLineId: line.orderLineId,
|
|
39
|
+
quantity: line.quantity,
|
|
40
|
+
}));
|
|
41
|
+
setIsSubmitting(true);
|
|
42
|
+
try {
|
|
43
|
+
await onSubmitted({ lines, handler: state.handler?.value });
|
|
44
|
+
setOpen(false);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
console.error(error);
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
setIsSubmitting(false);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
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: "text-primary size-5" }), _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
|
+
const onStock = line.productVariant.stockLevels.reduce((acc, stock) => acc + stock.stockOnHand, 0);
|
|
55
|
+
const stateLine = state.lines?.value?.find((l) => l.orderLineId === line.id);
|
|
56
|
+
const isLowStock = onStock < line.quantity;
|
|
57
|
+
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;
|
|
61
|
+
const index = value.findIndex((v) => v.orderLineId === line.id);
|
|
62
|
+
if (index === -1)
|
|
63
|
+
return;
|
|
64
|
+
if (Number.parseInt(e.target.value) < 1)
|
|
65
|
+
return;
|
|
66
|
+
if (Number.parseInt(e.target.value) <= line.quantity) {
|
|
67
|
+
value[index].quantity = Number.parseInt(e.target.value);
|
|
68
|
+
setField('lines', value);
|
|
69
|
+
}
|
|
70
|
+
}, 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-muted-foreground text-xs", children: ["(", t('fulfillment.onStockValue', { value: onStock }), ")"] })] }) }) }) }, line.id));
|
|
71
|
+
})) : (_jsx(TableRow, { noHover: true, children: _jsx(TableCell, { colSpan: 4, className: "text-muted-foreground py-6 text-center", 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-muted-foreground text-xs", 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: "text-muted-foreground size-4" }), _jsx("p", { className: "text-muted-foreground text-xs font-medium", 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: "text-muted-foreground size-4" }), _jsx("p", { className: "text-muted-foreground text-xs font-medium", 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: "text-muted-foreground size-4" }), _jsx("p", { className: "text-muted-foreground text-xs font-medium", 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-muted-foreground text-xs font-medium", 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-muted-foreground text-xs font-medium", 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-muted-foreground text-xs font-medium", 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-muted-foreground text-xs font-medium", 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: "text-muted-foreground size-4" }), _jsx("p", { className: "text-muted-foreground text-xs font-medium", 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: "text-muted-foreground mb-2 text-xs font-medium", children: t('fulfillment.shippingMethod', 'Shipping Method') }), order.shippingLines.map((line) => (_jsxs("div", { className: "bg-muted/50 flex items-center justify-between rounded-md 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) => {
|
|
77
|
+
if (arg.name === argument.name)
|
|
78
|
+
return { ...arg, value: data.value };
|
|
79
|
+
return arg;
|
|
80
|
+
});
|
|
81
|
+
if (!state.handler?.value.code || !newArgs)
|
|
82
|
+
return;
|
|
83
|
+
setField('handler', { code: state.handler?.value.code, arguments: newArgs });
|
|
84
|
+
} }) })] }) }) })] })] })] }));
|
|
85
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { TableRow, TableCell, Button } from '@deenruv/react-ui-devkit';
|
|
3
|
+
import { Minus, Plus, Package } from 'lucide-react';
|
|
4
|
+
export const LineItem = ({ noBorder, noHover, children, variant, adjustLineItem }) => {
|
|
5
|
+
const imageUrl = variant?.featuredAsset?.preview || variant?.product?.featuredAsset?.preview;
|
|
6
|
+
return (_jsxs(TableRow, { noHover: noHover, noBorder: noBorder, className: "group transition-colors", children: [_jsx(TableCell, { className: "py-3", children: _jsxs("div", { className: "flex items-center gap-3", children: [imageUrl ? (_jsx("img", { alt: `${variant?.product.name} image`, className: "aspect-square size-12 rounded-md border bg-white object-cover shadow-sm", height: "48", width: "48", src: imageUrl || '/placeholder.svg' })) : (_jsx("div", { className: "bg-muted/30 flex size-12 items-center justify-center rounded-md border", children: _jsx(Package, { className: "text-muted-foreground size-6" }) })), _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { className: "line-clamp-1 text-sm font-medium md:text-base", children: variant?.product.name }), variant?.name && variant.name !== variant?.product.name && (_jsx("span", { className: "text-muted-foreground line-clamp-1 text-xs", children: variant.name }))] })] }) }), _jsx(TableCell, { className: "text-muted-foreground font-mono text-sm", children: variant?.sku }), variant?.quantity ? (_jsx(TableCell, { children: adjustLineItem ? (_jsx("div", { className: "flex items-center", children: _jsxs("div", { className: "bg-background inline-flex items-center rounded-md border shadow-sm", children: [_jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => adjustLineItem(Math.max(1, (variant.quantity || 1) - 1)), className: "hover:bg-muted h-8 rounded-r-none border-r px-2", "aria-label": "Decrease quantity", children: _jsx(Minus, { className: "size-3.5" }) }), _jsx("span", { className: "px-3 py-1 text-sm font-medium tabular-nums", children: variant?.quantity }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => adjustLineItem((variant.quantity || 0) + 1), className: "hover:bg-muted h-8 rounded-l-none border-l px-2", "aria-label": "Increase quantity", children: _jsx(Plus, { className: "size-3.5" }) })] }) })) : (_jsx("span", { className: "bg-primary/10 text-primary inline-flex min-w-8 items-center justify-center rounded-full px-2 py-0.5 text-xs font-medium", children: variant?.quantity })) })) : null, children] }));
|
|
7
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { Button, Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, OrderStateBadge, usePluginStore, useTranslation, } from '@deenruv/react-ui-devkit';
|
|
4
|
+
import { toast } from 'sonner';
|
|
5
|
+
export const ManualOrderChangeModal = ({ currentPossibilities, wantedState, order, open, setOpen, onConfirm }) => {
|
|
6
|
+
const [components, setComponents] = useState([]);
|
|
7
|
+
const beforeSubmit = useRef();
|
|
8
|
+
const { getModalComponents } = usePluginStore();
|
|
9
|
+
const { t } = useTranslation('orders');
|
|
10
|
+
const [value, setValue] = useState(() => {
|
|
11
|
+
if (wantedState) {
|
|
12
|
+
const stateIndex = currentPossibilities.to.indexOf(wantedState);
|
|
13
|
+
return stateIndex === -1 ? currentPossibilities.to[0] : currentPossibilities.to[stateIndex];
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
const stateIndex = currentPossibilities.to.indexOf(order.state);
|
|
17
|
+
return stateIndex === -1 ? currentPossibilities.to[0] : currentPossibilities.to[stateIndex + 1];
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
const submit = async () => {
|
|
21
|
+
try {
|
|
22
|
+
if (beforeSubmit.current) {
|
|
23
|
+
await beforeSubmit.current?.();
|
|
24
|
+
}
|
|
25
|
+
await onConfirm(value);
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
toast.error(t('changeStatus.error'));
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const stored = getModalComponents('manual-order-state');
|
|
33
|
+
setComponents(stored.map((component, index) => {
|
|
34
|
+
const data = {
|
|
35
|
+
state: value,
|
|
36
|
+
setState: setValue,
|
|
37
|
+
order,
|
|
38
|
+
beforeSubmit,
|
|
39
|
+
};
|
|
40
|
+
return _jsx(React.Fragment, { children: React.createElement(component, { data }) }, index);
|
|
41
|
+
}));
|
|
42
|
+
return () => {
|
|
43
|
+
setComponents([]);
|
|
44
|
+
};
|
|
45
|
+
}, [value]);
|
|
46
|
+
return (_jsx(Dialog, { open: open, modal: open, onOpenChange: (state) => setOpen({ state }), children: _jsxs(DialogContent, { className: "min-w-fit max-w-[50vw]", children: [_jsx(DialogHeader, { children: _jsxs(DialogTitle, { className: "flex items-center gap-2", children: [t('changeStatus.header'), _jsx(OrderStateBadge, { state: order.state })] }) }), _jsx(DialogDescription, { children: t('changeStatus.description') }), _jsxs(Select, { name: "orderState", value: value, onValueChange: (e) => setValue(e), children: [_jsx(SelectTrigger, { className: "w-[180px]", children: _jsx(SelectValue, { placeholder: "Status.." }) }), _jsx(SelectContent, { children: _jsx(SelectGroup, { children: [currentPossibilities.name, ...currentPossibilities.to].map((state) => (_jsx(SelectItem, { value: state, disabled: state === order.state, children: state }, state))) }) })] }), components, _jsx(Button, { className: "ml-auto w-min", onClick: submit, children: t('changeStatus.button') })] }) }));
|
|
47
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useOrder, Button, Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, useTranslation, } from '@deenruv/react-ui-devkit';
|
|
3
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
4
|
+
import { ModifyingCard } from './ModifyingCard.js';
|
|
5
|
+
import { ChangesRegister } from './ChangesRegister';
|
|
6
|
+
export const ModifyAcceptModal = () => {
|
|
7
|
+
const { t } = useTranslation('orders');
|
|
8
|
+
const [open, setOpen] = useState(false);
|
|
9
|
+
const { isOrderModified, getChangesRegistry } = useOrder();
|
|
10
|
+
const [changes, setChanges] = useState();
|
|
11
|
+
const getAndSetChanges = useCallback((options) => {
|
|
12
|
+
setChanges(undefined);
|
|
13
|
+
getChangesRegistry(options).then(setChanges);
|
|
14
|
+
}, [getChangesRegistry]);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (open)
|
|
17
|
+
getAndSetChanges();
|
|
18
|
+
else
|
|
19
|
+
setChanges(undefined);
|
|
20
|
+
}, [open]);
|
|
21
|
+
return (_jsxs(Dialog, { open: open, onOpenChange: setOpen, children: [_jsx(DialogTrigger, { asChild: true, children: _jsx(Button, { className: "w-full justify-start", disabled: !isOrderModified(), children: t('applyChanges') }) }), _jsxs(DialogContent, { className: "h-[90dvh] max-w-[90vw] grid-rows-[auto_1fr_auto]", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { className: "text-muted-foreground", children: t('reviewChanges') }) }), _jsxs("div", { className: "grid grow grid-cols-3 gap-4", children: [_jsx("div", { className: "col-span-2 flex h-full flex-col", children: _jsx("div", { className: "h-0 grow overflow-y-auto pr-2", children: _jsx(ChangesRegister, { changes }) }) }), _jsx(ModifyingCard, { onNoteModified: (bool) => setOpen(bool), onOptionsChange: getAndSetChanges, changes })] })] })] }));
|
|
22
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
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';
|
|
5
|
+
import { toast } from 'sonner';
|
|
6
|
+
import { PriceChangedInfo } from "./PriceChangedInfo.js";
|
|
7
|
+
import { FileEdit, Truck, Tag, CreditCard, AlertCircle, Save, Loader2, RefreshCw } from 'lucide-react';
|
|
8
|
+
import { RefundCard } from "./RefundCard.js";
|
|
9
|
+
export const ModifyingCard = ({ onNoteModified, onOptionsChange, changes }) => {
|
|
10
|
+
const { t } = useTranslation('orders');
|
|
11
|
+
const { modifiedOrder, setModifyOrderInput, modifyOrder, modifyOrderInput } = useOrder();
|
|
12
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
13
|
+
const { state, setField } = useGFFLP('ModifyOrderInput')({});
|
|
14
|
+
const [noteAdded, setNoteAdded] = useState(false);
|
|
15
|
+
const [sendRefund, setSendRefund] = useState(false);
|
|
16
|
+
const [refundReason, setRefundReason] = useState('');
|
|
17
|
+
const priceDifference = useMemo(() => {
|
|
18
|
+
const totalWithTaxChange = changes?.rest.find((ch) => ch.path === 'totalWithTax');
|
|
19
|
+
if (!totalWithTaxChange)
|
|
20
|
+
return 0;
|
|
21
|
+
return +totalWithTaxChange.added - +totalWithTaxChange.removed;
|
|
22
|
+
}, [changes]);
|
|
23
|
+
const currentPayment = modifiedOrder?.payments?.[0];
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (priceDifference < 0)
|
|
26
|
+
setSendRefund(true);
|
|
27
|
+
}, [priceDifference]);
|
|
28
|
+
useEffect(() => setNoteAdded(!!state.note?.value), [state.note?.value]);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (modifiedOrder && state.note?.value) {
|
|
31
|
+
setModifyOrderInput({
|
|
32
|
+
...modifyOrderInput,
|
|
33
|
+
note: state.note?.value,
|
|
34
|
+
options: state.options?.value,
|
|
35
|
+
refund: sendRefund && currentPayment?.transactionId
|
|
36
|
+
? {
|
|
37
|
+
paymentId: currentPayment.transactionId,
|
|
38
|
+
amount: priceDifference,
|
|
39
|
+
reason: refundReason,
|
|
40
|
+
}
|
|
41
|
+
: undefined,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}, [state, modifiedOrder]);
|
|
45
|
+
const acceptModifiedChanges = async () => {
|
|
46
|
+
if (!noteAdded)
|
|
47
|
+
return;
|
|
48
|
+
setIsSubmitting(true);
|
|
49
|
+
try {
|
|
50
|
+
await modifyOrder();
|
|
51
|
+
toast.success(t('modifySuccess', 'Order modifications applied successfully'));
|
|
52
|
+
onNoteModified && onNoteModified(false);
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
const message = e instanceof Error ? e.message : t('modifyError', 'Failed to apply order modifications');
|
|
56
|
+
toast.error(message);
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
setIsSubmitting(false);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
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 right-3 top-3 text-amber-500", children: _jsx(AlertCircle, { className: "size-4" }) }))] }), _jsx("p", { className: "text-muted-foreground text-xs", 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-muted-foreground text-sm font-medium", children: t('modificationOptions', 'Modification Options') }), _jsxs("div", { className: "flex flex-col space-y-4", children: [_jsxs("div", { className: "border-border bg-muted/20 space-y-4 rounded-md border 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) => {
|
|
63
|
+
const optionsObj = {
|
|
64
|
+
freezePromotions: state?.options?.value?.freezePromotions || false,
|
|
65
|
+
recalculateShipping: !!e,
|
|
66
|
+
};
|
|
67
|
+
setField('options', optionsObj);
|
|
68
|
+
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) => {
|
|
70
|
+
const optionsObj = {
|
|
71
|
+
freezePromotions: !!e,
|
|
72
|
+
recalculateShipping: state?.options?.value?.recalculateShipping || false,
|
|
73
|
+
};
|
|
74
|
+
setField('options', optionsObj);
|
|
75
|
+
onOptionsChange(optionsObj);
|
|
76
|
+
} }), _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
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation, useOrder, apiClient, CustomCard } from '@deenruv/react-ui-devkit';
|
|
3
|
+
import { DeletionResult } from '@deenruv/admin-types';
|
|
4
|
+
import { toast } from 'sonner';
|
|
5
|
+
import { History } from "../../../components";
|
|
6
|
+
import { ClipboardList, AlertCircle } from 'lucide-react';
|
|
7
|
+
export const OrderHistory = () => {
|
|
8
|
+
const { order, orderHistory: { data, error, loading }, fetchOrderHistory, } = useOrder();
|
|
9
|
+
const { t } = useTranslation('orders');
|
|
10
|
+
const addMessageToOrder = async (input) => {
|
|
11
|
+
if (!order)
|
|
12
|
+
return;
|
|
13
|
+
try {
|
|
14
|
+
const { addNoteToOrder } = await apiClient('mutation')({
|
|
15
|
+
addNoteToOrder: [{ input: input }, { id: true }],
|
|
16
|
+
});
|
|
17
|
+
if (addNoteToOrder?.id) {
|
|
18
|
+
await fetchOrderHistory();
|
|
19
|
+
toast.success(t('history.addSuccess', 'Note added successfully'));
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
toast.error(t('history.addError'), { position: 'top-center' });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
toast.error(t('history.addError'), { position: 'top-center' });
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const deleteMessageFromOrder = async (id) => {
|
|
30
|
+
try {
|
|
31
|
+
const { deleteOrderNote } = await apiClient('mutation')({
|
|
32
|
+
deleteOrderNote: [{ id }, { message: true, result: true }],
|
|
33
|
+
});
|
|
34
|
+
if (deleteOrderNote.result === DeletionResult.DELETED) {
|
|
35
|
+
await fetchOrderHistory();
|
|
36
|
+
toast.success(t('history.deleteSuccess', 'Note deleted successfully'));
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
toast.error(t('history.deleteError', { value: deleteOrderNote.message }), { position: 'top-center' });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
toast.error(t('history.deleteError', { value: error instanceof Error ? error.message : 'Unknown error' }), {
|
|
44
|
+
position: 'top-center',
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const editMessageInOrder = async (input) => {
|
|
49
|
+
try {
|
|
50
|
+
await apiClient('mutation')({ updateOrderNote: [{ input }, { id: true }] });
|
|
51
|
+
await fetchOrderHistory();
|
|
52
|
+
toast.success(t('history.editSuccess', 'Note updated successfully'));
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
toast.error(t('history.editError'), { position: 'top-center' });
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
return (_jsxs(CustomCard, { color: "amber", description: t('history.description', 'Timeline of order events and notes'), title: t('history.title', 'Order History'), icon: _jsx(ClipboardList, {}), collapsed: true, children: [loading && (_jsxs("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: [_jsx("div", { className: "size-8 animate-spin rounded-full border-4 border-amber-200 border-t-amber-500" }), _jsx("p", { className: "text-muted-foreground mt-4 text-sm", children: t('history.loading', 'Loading order history...') })] })), error && (_jsxs("div", { className: "flex flex-col items-center justify-center gap-3 py-16 text-center", children: [_jsx("div", { className: "rounded-full bg-red-100 p-3 dark:bg-red-900/30", children: _jsx(AlertCircle, { className: "size-6 text-red-500 dark:text-red-400" }) }), _jsxs("div", { children: [_jsx("p", { className: "font-medium text-red-600", children: t('toasts.orderHistoryLoadingError', { value: order?.id }) }), _jsx("p", { className: "text-muted-foreground mt-2 text-sm", children: t('history.tryAgain', 'Please try refreshing the page') })] })] })), !loading && !error && (_jsx(History, { data: data, onNoteAdd: addMessageToOrder, onNoteDelete: deleteMessageFromOrder, onNoteEdit: editMessageInOrder }))] }));
|
|
59
|
+
};
|