@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.
Files changed (292) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +30 -0
  3. package/dist/@types/resources.js +51 -0
  4. package/dist/DeenruvAdminPanel.js +118 -0
  5. package/dist/DeenruvDeveloperIndicator.js +57 -0
  6. package/dist/components/Aexol.js +4 -0
  7. package/dist/components/BrandLogo.js +22 -0
  8. package/dist/components/CanLeaveRouteDialog.js +8 -0
  9. package/dist/components/DataTable.js +13 -0
  10. package/dist/components/DeleteDialog.js +6 -0
  11. package/dist/components/DuplicateEntry.js +46 -0
  12. package/dist/components/GlobalSearch.js +134 -0
  13. package/dist/components/History/AddEntryForm.js +29 -0
  14. package/dist/components/History/DeleteEntryDialog.js +25 -0
  15. package/dist/components/History/EditEntryDialog.js +32 -0
  16. package/dist/components/History/History.js +13 -0
  17. package/dist/components/History/ModifyHistoryInfo.js +6 -0
  18. package/dist/components/History/Timeline.js +53 -0
  19. package/dist/components/History/index.js +5 -0
  20. package/dist/components/Menu/ActiveAdmins.js +24 -0
  21. package/dist/components/Menu/ChannelSwitcher.js +20 -0
  22. package/dist/components/Menu/LanguagesDropdown.js +26 -0
  23. package/dist/components/Menu/Navigation.js +270 -0
  24. package/dist/components/Menu/NavigationFooter.js +12 -0
  25. package/dist/components/Menu/Notifications.js +90 -0
  26. package/dist/components/Menu/index.js +67 -0
  27. package/dist/components/index.js +6 -0
  28. package/dist/graphql/base.js +95 -0
  29. package/dist/graphql/collections.js +94 -0
  30. package/dist/graphql/draft_order.js +307 -0
  31. package/dist/graphql/orders.js +157 -0
  32. package/dist/graphql/products.js +230 -0
  33. package/dist/graphql/scalars.js +22 -0
  34. package/dist/i18.js +13 -0
  35. package/dist/index.css +161 -0
  36. package/dist/index.d.ts +11 -0
  37. package/dist/index.js +1 -0
  38. package/dist/locales/en/admins.json +57 -0
  39. package/dist/locales/en/assets.json +69 -0
  40. package/dist/locales/en/channels.json +66 -0
  41. package/dist/locales/en/collections.json +145 -0
  42. package/dist/locales/en/common.json +1127 -0
  43. package/dist/locales/en/countries.json +50 -0
  44. package/dist/locales/en/customerGroups.json +6 -0
  45. package/dist/locales/en/customers.json +109 -0
  46. package/dist/locales/en/dashboard.json +34 -0
  47. package/dist/locales/en/facets.json +78 -0
  48. package/dist/locales/en/globalSettings.json +15 -0
  49. package/dist/locales/en/index.js +52 -0
  50. package/dist/locales/en/orders.json +932 -0
  51. package/dist/locales/en/paymentMethods.json +59 -0
  52. package/dist/locales/en/permissions.json +94 -0
  53. package/dist/locales/en/products.json +194 -0
  54. package/dist/locales/en/promotions.json +65 -0
  55. package/dist/locales/en/roles.json +59 -0
  56. package/dist/locales/en/sellers.json +41 -0
  57. package/dist/locales/en/shippingMethods.json +84 -0
  58. package/dist/locales/en/stockLocations.json +40 -0
  59. package/dist/locales/en/system.json +41 -0
  60. package/dist/locales/en/table.json +201 -0
  61. package/dist/locales/en/taxCategories.json +47 -0
  62. package/dist/locales/en/taxRates.json +56 -0
  63. package/dist/locales/en/zones.json +47 -0
  64. package/dist/locales/index.js +2 -0
  65. package/dist/locales/pl/admins.json +57 -0
  66. package/dist/locales/pl/assets.json +69 -0
  67. package/dist/locales/pl/channels.json +66 -0
  68. package/dist/locales/pl/collections.json +145 -0
  69. package/dist/locales/pl/common.json +1128 -0
  70. package/dist/locales/pl/countries.json +50 -0
  71. package/dist/locales/pl/customerGroups.json +6 -0
  72. package/dist/locales/pl/customers.json +109 -0
  73. package/dist/locales/pl/dashboard.json +34 -0
  74. package/dist/locales/pl/facets.json +78 -0
  75. package/dist/locales/pl/globalSettings.json +15 -0
  76. package/dist/locales/pl/index.js +52 -0
  77. package/dist/locales/pl/orders.json +932 -0
  78. package/dist/locales/pl/paymentMethods.json +59 -0
  79. package/dist/locales/pl/permissions.json +94 -0
  80. package/dist/locales/pl/products.json +194 -0
  81. package/dist/locales/pl/promotions.json +65 -0
  82. package/dist/locales/pl/roles.json +59 -0
  83. package/dist/locales/pl/sellers.json +41 -0
  84. package/dist/locales/pl/shippingMethods.json +84 -0
  85. package/dist/locales/pl/stockLocations.json +40 -0
  86. package/dist/locales/pl/system.json +41 -0
  87. package/dist/locales/pl/table.json +201 -0
  88. package/dist/locales/pl/taxCategories.json +47 -0
  89. package/dist/locales/pl/taxRates.json +56 -0
  90. package/dist/locales/pl/zones.json +47 -0
  91. package/dist/notifications/OrderStatusNotification.js +47 -0
  92. package/dist/notifications/SystemStatusNotification.js +19 -0
  93. package/dist/pages/Custom404.js +5 -0
  94. package/dist/pages/LoginScreen.js +37 -0
  95. package/dist/pages/Root.js +252 -0
  96. package/dist/pages/admins/Detail.js +72 -0
  97. package/dist/pages/admins/List.js +56 -0
  98. package/dist/pages/admins/_components/AdminDetailView.js +34 -0
  99. package/dist/pages/admins/_components/RolesCard.js +31 -0
  100. package/dist/pages/admins/index.js +2 -0
  101. package/dist/pages/assets/List.js +82 -0
  102. package/dist/pages/assets/_components/Asset.js +114 -0
  103. package/dist/pages/assets/_components/AssetListView.js +29 -0
  104. package/dist/pages/assets/_components/EditAssetDialog.js +85 -0
  105. package/dist/pages/assets/_components/UploadAssetDialog.js +133 -0
  106. package/dist/pages/assets/_components/UploadProgress.js +20 -0
  107. package/dist/pages/assets/index.js +1 -0
  108. package/dist/pages/channels/Detail.js +149 -0
  109. package/dist/pages/channels/List.js +46 -0
  110. package/dist/pages/channels/_components/ChannelDetailView.js +51 -0
  111. package/dist/pages/channels/_components/DefaultsCard.js +25 -0
  112. package/dist/pages/channels/index.js +2 -0
  113. package/dist/pages/collections/Detail.js +61 -0
  114. package/dist/pages/collections/List.js +135 -0
  115. package/dist/pages/collections/_components/CollectionDetailView.js +52 -0
  116. package/dist/pages/collections/_components/CollectionProductVariantsDrawer.js +28 -0
  117. package/dist/pages/collections/_components/CombinationMode.js +6 -0
  118. package/dist/pages/collections/_components/ContentsCard.js +43 -0
  119. package/dist/pages/collections/_components/ContentsTable.js +102 -0
  120. package/dist/pages/collections/_components/DeleteCollectionsFromChannel.js +36 -0
  121. package/dist/pages/collections/_components/FacetsSelector.js +30 -0
  122. package/dist/pages/collections/_components/FiltersCard.js +101 -0
  123. package/dist/pages/collections/_components/MoveCollectionsToCollections.js +107 -0
  124. package/dist/pages/collections/_components/MoveEntityToChannels.js +115 -0
  125. package/dist/pages/collections/_components/PageHeader.js +11 -0
  126. package/dist/pages/collections/_components/SelectedCollectionsModal.js +22 -0
  127. package/dist/pages/collections/_components/VariantsSelector.js +43 -0
  128. package/dist/pages/collections/consts.js +7 -0
  129. package/dist/pages/collections/index.js +2 -0
  130. package/dist/pages/countries/Detail.js +57 -0
  131. package/dist/pages/countries/List.js +47 -0
  132. package/dist/pages/countries/_components/CountryDetailView.js +28 -0
  133. package/dist/pages/countries/index.js +2 -0
  134. package/dist/pages/customer-groups/Detail.js +76 -0
  135. package/dist/pages/customer-groups/List.js +41 -0
  136. package/dist/pages/customer-groups/_components/CustomerGroupsDetailView.js +21 -0
  137. package/dist/pages/customer-groups/index.js +2 -0
  138. package/dist/pages/customers/Detail.js +75 -0
  139. package/dist/pages/customers/List.js +60 -0
  140. package/dist/pages/customers/_components/Address.js +59 -0
  141. package/dist/pages/customers/_components/AddressDialog.js +56 -0
  142. package/dist/pages/customers/_components/AddressForm.js +77 -0
  143. package/dist/pages/customers/_components/AddressesCard.js +9 -0
  144. package/dist/pages/customers/_components/CustomerDetailSidebar.js +9 -0
  145. package/dist/pages/customers/_components/CustomerDetailView.js +41 -0
  146. package/dist/pages/customers/_components/CustomerGroupsCard.js +46 -0
  147. package/dist/pages/customers/_components/HistoryTab.js +65 -0
  148. package/dist/pages/customers/_components/OrdersTab.js +46 -0
  149. package/dist/pages/customers/_components/PersonalDataCard.js +6 -0
  150. package/dist/pages/customers/_components/VerifiedCard.js +6 -0
  151. package/dist/pages/customers/index.js +2 -0
  152. package/dist/pages/dashboard/Dashboard.js +10 -0
  153. package/dist/pages/dashboard/index.js +1 -0
  154. package/dist/pages/extensions/Extensions.js +74 -0
  155. package/dist/pages/extensions/index.js +1 -0
  156. package/dist/pages/facets/Detail.js +56 -0
  157. package/dist/pages/facets/List.js +48 -0
  158. package/dist/pages/facets/_components/AddFacetValueDialog.js +105 -0
  159. package/dist/pages/facets/_components/ColorSample.js +18 -0
  160. package/dist/pages/facets/_components/FacetDetailView.js +98 -0
  161. package/dist/pages/facets/index.js +2 -0
  162. package/dist/pages/global-settings/GlobalSettingsComponent.js +30 -0
  163. package/dist/pages/global-settings/index.js +71 -0
  164. package/dist/pages/index.js +25 -0
  165. package/dist/pages/orders/Detail.js +21 -0
  166. package/dist/pages/orders/List.js +132 -0
  167. package/dist/pages/orders/_components/AddPaymentDialog.js +50 -0
  168. package/dist/pages/orders/_components/AddressCard.js +260 -0
  169. package/dist/pages/orders/_components/CancelAndRefundDialog.js +29 -0
  170. package/dist/pages/orders/_components/ChangesRegister.js +68 -0
  171. package/dist/pages/orders/_components/ChangesRegisterTable.js +18 -0
  172. package/dist/pages/orders/_components/CouponCodesCard.js +85 -0
  173. package/dist/pages/orders/_components/CustomComponent.js +21 -0
  174. package/dist/pages/orders/_components/CustomerSelectCard.js +117 -0
  175. package/dist/pages/orders/_components/EditNoteButton.js +10 -0
  176. package/dist/pages/orders/_components/FulfillmentModal.js +85 -0
  177. package/dist/pages/orders/_components/LineItem.js +7 -0
  178. package/dist/pages/orders/_components/ManualOrderChangeModal.js +47 -0
  179. package/dist/pages/orders/_components/ModifyAcceptModal.js +22 -0
  180. package/dist/pages/orders/_components/ModifyingCard.js +77 -0
  181. package/dist/pages/orders/_components/OrderHistory.js +59 -0
  182. package/dist/pages/orders/_components/OrderLineActionModal/ActionQuantityPrice.js +58 -0
  183. package/dist/pages/orders/_components/OrderLineActionModal/index.js +7 -0
  184. package/dist/pages/orders/_components/OrderLineActionModal/types.js +1 -0
  185. package/dist/pages/orders/_components/OrderLineCustomFields.js +26 -0
  186. package/dist/pages/orders/_components/OrderSummary.js +10 -0
  187. package/dist/pages/orders/_components/PaymentMetadata.js +6 -0
  188. package/dist/pages/orders/_components/Payments.js +113 -0
  189. package/dist/pages/orders/_components/PossibleOrderStates.js +54 -0
  190. package/dist/pages/orders/_components/PriceChangedInfo.js +16 -0
  191. package/dist/pages/orders/_components/ProductsCard.js +308 -0
  192. package/dist/pages/orders/_components/ProductsTable.js +29 -0
  193. package/dist/pages/orders/_components/PromotionsList.js +73 -0
  194. package/dist/pages/orders/_components/RealizationCard.js +98 -0
  195. package/dist/pages/orders/_components/RefundCard.js +11 -0
  196. package/dist/pages/orders/_components/RefundPaymentCard.js +13 -0
  197. package/dist/pages/orders/_components/ShippingMethod.js +112 -0
  198. package/dist/pages/orders/_components/SpecialLineItem.js +7 -0
  199. package/dist/pages/orders/_components/SurchargeCard.js +99 -0
  200. package/dist/pages/orders/_components/SurchargeTable.js +8 -0
  201. package/dist/pages/orders/_components/TaxSummary.js +11 -0
  202. package/dist/pages/orders/_components/ToRealizationForm.js +64 -0
  203. package/dist/pages/orders/_components/TopActions.js +219 -0
  204. package/dist/pages/orders/_components/index.js +25 -0
  205. package/dist/pages/orders/index.js +2 -0
  206. package/dist/pages/payment-methods/Detail.js +67 -0
  207. package/dist/pages/payment-methods/List.js +41 -0
  208. package/dist/pages/payment-methods/_components/OptionsCard.js +60 -0
  209. package/dist/pages/payment-methods/_components/PaymentMethodDetailView.js +53 -0
  210. package/dist/pages/payment-methods/index.js +2 -0
  211. package/dist/pages/product-variants/Detail.js +4 -0
  212. package/dist/pages/product-variants/List.js +76 -0
  213. package/dist/pages/product-variants/index.js +2 -0
  214. package/dist/pages/products/Detail.js +75 -0
  215. package/dist/pages/products/List.js +79 -0
  216. package/dist/pages/products/_components/AddOptionGroupDialog.js +65 -0
  217. package/dist/pages/products/_components/AssetsCard.js +35 -0
  218. package/dist/pages/products/_components/BasicFieldsCard.js +6 -0
  219. package/dist/pages/products/_components/ChannelsCard.js +6 -0
  220. package/dist/pages/products/_components/CollectionsCard.js +6 -0
  221. package/dist/pages/products/_components/Container.js +4 -0
  222. package/dist/pages/products/_components/DiscountRatingCard.js +6 -0
  223. package/dist/pages/products/_components/FacetValuesCard.js +6 -0
  224. package/dist/pages/products/_components/ImagesCard.js +6 -0
  225. package/dist/pages/products/_components/OptionGroup.js +93 -0
  226. package/dist/pages/products/_components/OptionValueCard.js +59 -0
  227. package/dist/pages/products/_components/OptionsCard.js +38 -0
  228. package/dist/pages/products/_components/OptionsTab.js +40 -0
  229. package/dist/pages/products/_components/PriceCard.js +27 -0
  230. package/dist/pages/products/_components/ProductDetailSidebar.js +30 -0
  231. package/dist/pages/products/_components/ProductDetailView.js +54 -0
  232. package/dist/pages/products/_components/SettingsCard.js +6 -0
  233. package/dist/pages/products/_components/StockCard.js +40 -0
  234. package/dist/pages/products/_components/Variant.js +149 -0
  235. package/dist/pages/products/_components/VariantsTab.js +46 -0
  236. package/dist/pages/products/index.js +2 -0
  237. package/dist/pages/promotions/Detail.js +80 -0
  238. package/dist/pages/promotions/List.js +38 -0
  239. package/dist/pages/promotions/_components/ActionsCard.js +67 -0
  240. package/dist/pages/promotions/_components/BasicFieldsCard.js +6 -0
  241. package/dist/pages/promotions/_components/ConditionsCard.js +70 -0
  242. package/dist/pages/promotions/_components/CustomerGroupsSelector.js +25 -0
  243. package/dist/pages/promotions/_components/EnabledCard.js +6 -0
  244. package/dist/pages/promotions/_components/OptionsCard.js +6 -0
  245. package/dist/pages/promotions/_components/PromotionDetailSidebar.js +15 -0
  246. package/dist/pages/promotions/_components/PromotionDetailView.js +66 -0
  247. package/dist/pages/promotions/_components/PromotionFieldRender.js +4 -0
  248. package/dist/pages/promotions/index.js +2 -0
  249. package/dist/pages/roles/Detail.js +72 -0
  250. package/dist/pages/roles/List.js +70 -0
  251. package/dist/pages/roles/_components/PermissionsCard.js +7 -0
  252. package/dist/pages/roles/_components/PermissionsTable.js +42 -0
  253. package/dist/pages/roles/_components/RoleDetailView.js +49 -0
  254. package/dist/pages/roles/index.js +2 -0
  255. package/dist/pages/sellers/Detail.js +48 -0
  256. package/dist/pages/sellers/List.js +37 -0
  257. package/dist/pages/sellers/_components/SellerDetailView.js +20 -0
  258. package/dist/pages/sellers/index.js +2 -0
  259. package/dist/pages/shipping-methods/Detail.js +105 -0
  260. package/dist/pages/shipping-methods/List.js +40 -0
  261. package/dist/pages/shipping-methods/_components/CalculatorCard.js +79 -0
  262. package/dist/pages/shipping-methods/_components/CheckerCard.js +76 -0
  263. package/dist/pages/shipping-methods/_components/Lines.js +20 -0
  264. package/dist/pages/shipping-methods/_components/ShippingMethodDetailView.js +67 -0
  265. package/dist/pages/shipping-methods/_components/TestCard.js +62 -0
  266. package/dist/pages/shipping-methods/index.js +2 -0
  267. package/dist/pages/status/Status.js +6 -0
  268. package/dist/pages/status/_components/FilterToolbar.js +29 -0
  269. package/dist/pages/status/_components/Health.js +21 -0
  270. package/dist/pages/status/_components/JobResultPopover.js +8 -0
  271. package/dist/pages/status/_components/Jobs.js +199 -0
  272. package/dist/pages/status/_components/JsonExplorer.js +44 -0
  273. package/dist/pages/status/_components/JsonPopup.js +8 -0
  274. package/dist/pages/status/index.js +1 -0
  275. package/dist/pages/stock-locations/Detail.js +77 -0
  276. package/dist/pages/stock-locations/List.js +38 -0
  277. package/dist/pages/stock-locations/_components/StockLocationDetailView.js +22 -0
  278. package/dist/pages/stock-locations/index.js +2 -0
  279. package/dist/pages/tax-categories/Detail.js +85 -0
  280. package/dist/pages/tax-categories/List.js +46 -0
  281. package/dist/pages/tax-categories/_components/TaxCategoryDetailView.js +24 -0
  282. package/dist/pages/tax-categories/index.js +2 -0
  283. package/dist/pages/tax-rates/Detail.js +62 -0
  284. package/dist/pages/tax-rates/List.js +64 -0
  285. package/dist/pages/tax-rates/_components/TaxRateDetailView.js +64 -0
  286. package/dist/pages/tax-rates/index.js +2 -0
  287. package/dist/pages/zones/Detail.js +86 -0
  288. package/dist/pages/zones/List.js +52 -0
  289. package/dist/pages/zones/_components/ZoneDetailView.js +49 -0
  290. package/dist/pages/zones/index.js +2 -0
  291. package/dist/version.js +1 -0
  292. package/package.json +122 -0
@@ -0,0 +1,308 @@
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, Table, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuItem, Dialog, DialogContent, TooltipProvider, TooltipTrigger, TooltipContent, Tooltip, apiClient, cn, useServer, useOrder, CustomCard, ImageWithPreview, useTranslation, priceFormatter, DialogProductPicker, } from '@deenruv/react-ui-devkit';
4
+ import { removeOrderItemsResultSelector, updateOrderItemsSelector, updatedDraftOrderSelector, } from "../../../graphql/draft_order";
5
+ import { EllipsisVertical, InfoIcon, Trash2, ShoppingCart, Package, Tag, Edit } from 'lucide-react';
6
+ import { useCallback, useMemo, useState } from 'react';
7
+ import { toast } from 'sonner';
8
+ import { OrderLineActionModal } from './OrderLineActionModal/index.js';
9
+ import { CustomComponent } from './CustomComponent.js';
10
+ import { OrderLineCustomFields } from './OrderLineCustomFields.js';
11
+ import { SpecialLineItem } from './SpecialLineItem.js';
12
+ export const ProductsCard = () => {
13
+ const { t } = useTranslation('orders');
14
+ const orderLineCustomFields = useServer((p) => p.serverConfig?.entityCustomFields?.find((el) => el.entityName === 'OrderLine')?.customFields || []);
15
+ const { mode, order, setOrder, setModifiedOrder, modifiedOrder, fetchOrder } = useOrder();
16
+ const currentOrder = useMemo(() => (mode === 'update' ? (modifiedOrder ? modifiedOrder : order) : order), [mode, order, modifiedOrder]);
17
+ const [open, setOpen] = useState(false);
18
+ const [selectedVariant, setSelectedVariant] = useState(undefined);
19
+ const [customFields, setCustomFields] = useState({});
20
+ const [quantity, setQuantity] = useState(1);
21
+ const [orderLineId, setOrderLineId] = useState();
22
+ const [orderLineAction, setOrderLineAction] = useState();
23
+ const isLineAddedInModify = (lineId) => order?.lines.findIndex((l) => l.id === lineId) === -1;
24
+ const serializeCustomFields = (customFields) => JSON.stringify(Object.fromEntries(Object.entries(customFields).sort(([a], [b]) => a.localeCompare(b))));
25
+ const addToOrder = async (productVariant, quantity, customFields) => {
26
+ if (!order)
27
+ return;
28
+ try {
29
+ if (mode === 'update' && modifiedOrder) {
30
+ const mockLine = {
31
+ id: productVariant.id,
32
+ quantity,
33
+ discountedLinePrice: productVariant.price * quantity,
34
+ unitPrice: productVariant.price,
35
+ unitPriceWithTax: Math.round(productVariant.price * (1 + order.taxSummary[0].taxRate / 100)),
36
+ linePrice: productVariant.price,
37
+ linePriceWithTax: Math.round(productVariant.price * (1 + order.taxSummary[0].taxRate / 100)),
38
+ discountedLinePriceWithTax: productVariant.priceWithTax * quantity,
39
+ discountedUnitPrice: productVariant.price,
40
+ discountedUnitPriceWithTax: productVariant.priceWithTax,
41
+ productVariant,
42
+ taxRate: order.taxSummary[0].taxRate,
43
+ ...(customFields && { customFields }),
44
+ };
45
+ const existingLineIndex = modifiedOrder.lines.findIndex((line) => {
46
+ const sameId = line.id === mockLine.id;
47
+ // if (!customFields) {
48
+ return sameId;
49
+ // }
50
+ // @ts-ignore
51
+ // return sameId && serializeCustomFields(line.customFields) === serializeCustomFields(customFields);
52
+ });
53
+ let newLines;
54
+ if (existingLineIndex !== -1) {
55
+ newLines = modifiedOrder.lines.map((line, index) => index === existingLineIndex ? { ...line, quantity: line.quantity + 1 } : line);
56
+ }
57
+ else {
58
+ newLines = [...modifiedOrder.lines, mockLine];
59
+ }
60
+ setModifiedOrder({
61
+ ...modifiedOrder,
62
+ lines: newLines,
63
+ });
64
+ toast.success(t('create.productAdded', 'Product added to order'));
65
+ return;
66
+ }
67
+ const { addItemToDraftOrder } = await apiClient('mutation')({
68
+ addItemToDraftOrder: [
69
+ {
70
+ input: {
71
+ productVariantId: productVariant.id,
72
+ quantity,
73
+ ...(customFields && { customFields }),
74
+ },
75
+ orderId: order.id,
76
+ },
77
+ updatedDraftOrderSelector,
78
+ ],
79
+ });
80
+ if (addItemToDraftOrder.__typename === 'Order' || addItemToDraftOrder.__typename === 'InsufficientStockError') {
81
+ if (addItemToDraftOrder.__typename === 'Order') {
82
+ setOrder(addItemToDraftOrder);
83
+ toast.success(t('create.productAdded', 'Product added to order'));
84
+ }
85
+ else {
86
+ setOrder(addItemToDraftOrder.order);
87
+ toast.error(t('toasts.insufficientStockError', { value: productVariant.product.name }));
88
+ }
89
+ closeAddVariantDialog();
90
+ }
91
+ }
92
+ catch (error) {
93
+ toast.error(t('create.addError', 'Failed to add product'));
94
+ }
95
+ };
96
+ const removeLineItem = async (orderLineId) => {
97
+ if (!order)
98
+ return;
99
+ try {
100
+ if (mode === 'update' && modifiedOrder) {
101
+ setModifiedOrder({
102
+ ...modifiedOrder,
103
+ lines: modifiedOrder.lines.filter((l) => l.id !== orderLineId),
104
+ });
105
+ toast.success(t('create.productRemoved', 'Product removed from order'));
106
+ return;
107
+ }
108
+ const { removeDraftOrderLine } = await apiClient('mutation')({
109
+ removeDraftOrderLine: [{ orderId: order.id, orderLineId }, removeOrderItemsResultSelector],
110
+ });
111
+ if (removeDraftOrderLine.__typename === 'Order') {
112
+ setOrder(removeDraftOrderLine);
113
+ toast.success(t('create.productRemoved', 'Product removed from order'));
114
+ }
115
+ }
116
+ catch (error) {
117
+ toast.error(t('create.removeError', 'Failed to remove product'));
118
+ }
119
+ };
120
+ const onPriceQuantityChangeApprove = async (input) => {
121
+ if (!order)
122
+ return;
123
+ const { lineID, quantityChange } = input;
124
+ try {
125
+ if (mode === 'update' && modifiedOrder) {
126
+ const editedLineIdx = modifiedOrder.lines.findIndex((l) => l.id === lineID);
127
+ const quantity = quantityChange ? quantityChange : modifiedOrder.lines[editedLineIdx].quantity;
128
+ const editedLine = {
129
+ ...modifiedOrder.lines[editedLineIdx],
130
+ quantity,
131
+ };
132
+ setModifiedOrder({
133
+ ...modifiedOrder,
134
+ lines: modifiedOrder.lines.map((l, i) => (i === editedLineIdx ? editedLine : l)),
135
+ });
136
+ toast.success(t('create.productUpdated', 'Product updated'));
137
+ }
138
+ else if (mode === 'create') {
139
+ const editedLineIdx = order.lines.findIndex((l) => l.id === lineID);
140
+ if (quantityChange) {
141
+ const { adjustDraftOrderLine } = await apiClient('mutation')({
142
+ adjustDraftOrderLine: [
143
+ {
144
+ orderId: order.id,
145
+ input: {
146
+ orderLineId: order.lines[editedLineIdx].id,
147
+ quantity: quantityChange,
148
+ ...(Object.keys(customFields || {}).length > 0 && { customFields }),
149
+ },
150
+ },
151
+ updateOrderItemsSelector,
152
+ ],
153
+ });
154
+ if (adjustDraftOrderLine.__typename === 'Order' ||
155
+ adjustDraftOrderLine.__typename === 'InsufficientStockError') {
156
+ if (adjustDraftOrderLine.__typename === 'Order') {
157
+ setOrder(adjustDraftOrderLine);
158
+ toast.success(t('create.productUpdated', 'Product updated'));
159
+ }
160
+ else {
161
+ setOrder(adjustDraftOrderLine.order);
162
+ toast.error(t('toasts.insufficientStockError', { value: order.lines[editedLineIdx].productVariant.product.name }));
163
+ }
164
+ }
165
+ }
166
+ fetchOrder(order.id);
167
+ }
168
+ }
169
+ catch (error) {
170
+ toast.error(t('create.updateError', 'Failed to update product'));
171
+ }
172
+ };
173
+ const adjustLineItem = async (orderLineId, quantity, customFields) => {
174
+ if (!order)
175
+ return;
176
+ try {
177
+ if (mode === 'update' && modifiedOrder) {
178
+ const editedLineIdx = modifiedOrder.lines.findIndex((l) => l.id === orderLineId);
179
+ const editedLine = {
180
+ ...modifiedOrder.lines[editedLineIdx],
181
+ quantity,
182
+ customFields,
183
+ };
184
+ setModifiedOrder({
185
+ ...modifiedOrder,
186
+ lines: modifiedOrder.lines.map((l, i) => (i === editedLineIdx ? editedLine : l)),
187
+ });
188
+ setOpen(false);
189
+ toast.success(t('create.productUpdated', 'Product updated'));
190
+ return;
191
+ }
192
+ const { adjustDraftOrderLine } = await apiClient('mutation')({
193
+ adjustDraftOrderLine: [
194
+ {
195
+ orderId: order.id,
196
+ input: { orderLineId, quantity, ...(Object.keys(customFields || {}).length > 0 && { customFields }) },
197
+ },
198
+ updateOrderItemsSelector,
199
+ ],
200
+ });
201
+ if (adjustDraftOrderLine.__typename === 'Order' || adjustDraftOrderLine.__typename === 'InsufficientStockError') {
202
+ if (adjustDraftOrderLine.__typename === 'Order') {
203
+ setOrder(adjustDraftOrderLine);
204
+ toast.success(t('create.productUpdated', 'Product updated'));
205
+ }
206
+ else {
207
+ setOrder(adjustDraftOrderLine.order);
208
+ toast.error(t('toasts.insufficientStockError', { value: 'product' }));
209
+ }
210
+ }
211
+ }
212
+ catch (error) {
213
+ toast.error(t('create.updateError', 'Failed to update product'));
214
+ }
215
+ };
216
+ const openAddVariantDialog = (input) => {
217
+ setOpen(true);
218
+ setOrderLineId(input.orderLineId ? input.orderLineId : undefined);
219
+ setSelectedVariant(input.variant);
220
+ setCustomFields({
221
+ attributes: input.customFields?.attributes,
222
+ discountBy: input.customFields?.discountBy,
223
+ });
224
+ setQuantity(input.quantity ? input.quantity : 1);
225
+ };
226
+ const closeAddVariantDialog = () => {
227
+ setOpen(false);
228
+ setSelectedVariant(undefined);
229
+ setOrderLineId(undefined);
230
+ };
231
+ const onOrderLineActionModalOpenChange = (isOpen) => {
232
+ if (isOpen)
233
+ return;
234
+ setOrderLineAction(undefined);
235
+ };
236
+ const handleNewVariantAdd = async (customFields) => {
237
+ if (orderLineId) {
238
+ await adjustLineItem(orderLineId, quantity, customFields);
239
+ return;
240
+ }
241
+ else if (selectedVariant) {
242
+ await addToOrder(selectedVariant, quantity, customFields);
243
+ }
244
+ closeAddVariantDialog();
245
+ };
246
+ const renderLineQuantity = useCallback((lineId, quantity) => {
247
+ const allRefundLines = order?.payments?.flatMap((payment) => payment.refunds)?.flatMap((refund) => refund.lines);
248
+ const lineInRefunds = allRefundLines?.find((line) => line.orderLineId === lineId);
249
+ if (!lineInRefunds)
250
+ return quantity;
251
+ else
252
+ return (_jsxs("div", { className: "flex gap-2", children: [_jsx("span", { className: "text-red-500 line-through", children: lineInRefunds.quantity }), quantity] }));
253
+ }, [order?.lines, order?.payments]);
254
+ if (!order)
255
+ return null;
256
+ return (_jsxs(_Fragment, { children: [_jsx(Dialog, { open: open, onOpenChange: (e) => (!e ? closeAddVariantDialog() : setOpen(true)), children: _jsx(DialogContent, { className: "bg-background max-h-[90vh] min-h-[60vh] max-w-[65vw] overflow-auto", children: selectedVariant ? (_jsxs("div", { className: "flex size-full flex-col justify-between", children: [_jsx("h3", { className: "text-primary pb-4 text-xl font-semibold", children: t('create.addVariant') }), _jsx("div", { className: "flex h-full flex-col gap-8", children: _jsx("div", { className: "flex size-full flex-col items-center gap-2", children: _jsxs("div", { className: "flex size-full justify-between gap-4", children: [_jsx(SpecialLineItem, { variant: { ...selectedVariant, quantity: 1 } }), _jsx(CustomComponent, { onVariantAdd: handleNewVariantAdd, orderLine: selectedVariant })] }) }) })] })) : (_jsx("div", { className: "text-muted-foreground flex items-center justify-center p-8", children: t('create.somethingWrong') })) }) }), _jsx(CustomCard, { color: "blue", description: t(mode === 'view' ? 'create.viewHeader' : mode === 'update' ? 'create.editHeader' : 'create.addHeader'), title: t(mode === 'view' ? 'create.viewTitle' : mode === 'update' ? 'create.editTitle' : 'create.addTitle'), icon: _jsx(ShoppingCart, {}), children: _jsxs("div", { className: "grid gap-6", children: [mode !== 'view' && (_jsx(DialogProductPicker, { initialValue: '', mode: "variant", onSubmit: async (selectedProduct) => {
257
+ const payload = {
258
+ variant: {
259
+ id: selectedProduct.productVariantId,
260
+ price: selectedProduct.price.__typename === 'SinglePrice'
261
+ ? selectedProduct.price.value
262
+ : selectedProduct.price.min,
263
+ priceWithTax: selectedProduct.priceWithTax.__typename === 'SinglePrice'
264
+ ? selectedProduct.priceWithTax.value
265
+ : selectedProduct.priceWithTax.min,
266
+ sku: selectedProduct.sku,
267
+ currencyCode: selectedProduct.currencyCode,
268
+ name: selectedProduct.productVariantName,
269
+ productId: selectedProduct.productId,
270
+ stockLevels: [{ stockOnHand: 0 }],
271
+ product: {
272
+ id: selectedProduct.productId,
273
+ name: selectedProduct.productName,
274
+ slug: selectedProduct.slug,
275
+ featuredAsset: selectedProduct.productAsset,
276
+ },
277
+ featuredAsset: selectedProduct.productVariantAsset,
278
+ },
279
+ quantity: 1,
280
+ customFields: {},
281
+ };
282
+ const variant = await apiClient('query')({
283
+ productVariant: [
284
+ { id: selectedProduct.productVariantId },
285
+ { id: true, stockLevels: { id: true, stockOnHand: true } },
286
+ ],
287
+ });
288
+ if (variant?.productVariant?.customFields) {
289
+ const customFields = variant?.productVariant?.customFields;
290
+ payload.customFields = customFields;
291
+ }
292
+ if (variant?.productVariant?.stockLevels) {
293
+ payload.variant.stockLevels = variant.productVariant.stockLevels;
294
+ }
295
+ if (orderLineCustomFields.length) {
296
+ openAddVariantDialog(payload);
297
+ }
298
+ else {
299
+ addToOrder(payload.variant, payload.quantity, payload.customFields);
300
+ }
301
+ } })), _jsx("div", { className: "border-border rounded-lg border-0 shadow-sm", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { noHover: true, className: "hover:bg-transparent", children: [_jsx(TableHead, { className: "py-3 font-semibold", children: t('create.product', 'Product') }), _jsx(TableHead, { className: "py-3 font-semibold", children: t('create.sku', 'SKU') }), _jsx(TableHead, { className: "py-3 font-semibold", children: t('create.customFields', 'Custom Fields') }), _jsx(TableHead, { className: "py-3 font-semibold", children: t('create.price', 'Price') }), _jsx(TableHead, { className: "py-3 font-semibold", children: t('create.priceWithTax', 'Price with Tax') }), _jsx(TableHead, { className: "py-3 font-semibold", children: t('create.quantity', 'Quantity') }), _jsx(TableHead, { className: "py-3 font-semibold", children: t('create.perUnit', 'Per Unit') }), (mode === 'create' || mode === 'update') && (_jsx(TableHead, { className: "py-3 text-right font-semibold", children: t('create.actions', 'Actions') }))] }) }), _jsx(TableBody, { children: currentOrder.lines.length ? (_jsxs(_Fragment, { children: [currentOrder.lines.map((line) => (_jsxs(TableRow, { className: "hover:bg-muted/20", children: [_jsx(TableCell, { className: "py-3", children: _jsxs("div", { className: "flex w-max items-center gap-3", children: [_jsx(ImageWithPreview, { imageClassName: "aspect-square w-12 h-12 rounded-md object-cover border border-border", src: line.productVariant.featuredAsset?.preview ||
302
+ line.productVariant.product?.featuredAsset?.preview ||
303
+ '/placeholder.svg' }), _jsx("div", { className: "text-primary font-medium", children: line.productVariant.product.name })] }) }), _jsx(TableCell, { className: "text-muted-foreground min-w-[200px] py-3 font-mono text-sm", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Tag, { className: "size-4 text-blue-500 dark:text-blue-400" }), line.productVariant.sku] }) }), _jsx(TableCell, { className: "py-3", children: _jsx(OrderLineCustomFields, { line: line, order: currentOrder, mode: mode }) }), _jsx(TableCell, { className: "py-3 font-medium", children: priceFormatter(line.linePrice, line.productVariant.currencyCode) }), _jsx(TableCell, { className: "py-3 font-medium", children: priceFormatter(line.linePriceWithTax, line.productVariant.currencyCode) }), _jsx(TableCell, { className: "py-3 text-center font-semibold", children: renderLineQuantity(line.id, line.quantity) }), _jsx(TableCell, { className: "py-3", children: _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { children: priceFormatter(line.unitPrice, line.productVariant.currencyCode) }), _jsxs("span", { className: "text-muted-foreground text-sm", children: ["(", priceFormatter(line.unitPriceWithTax, line.productVariant.currencyCode), ")"] })] }) }), (mode === 'create' || mode === 'update') && (_jsx(TableCell, { className: "py-3 text-right", children: _jsxs("div", { className: "flex items-center justify-end gap-3", children: [_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { className: "hover:bg-muted flex size-8 items-center justify-center rounded-md", children: _jsx(EllipsisVertical, { className: "size-5" }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsx(DropdownMenuLabel, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Edit, { className: "size-4 text-blue-500" }), t('editOptions', 'Edit Options')] }) }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { disabled: isLineAddedInModify(line.id), onClick: () => !isLineAddedInModify(line.id) && setOrderLineAction({ line }), className: cn('flex cursor-pointer justify-between', {
304
+ 'text-muted-foreground': isLineAddedInModify(line.id),
305
+ }), children: [_jsx("span", { children: t('orderLineActionModal.actionType.quantity-price', 'Adjust Quantity & Price') }), isLineAddedInModify(line.id) && (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(InfoIcon, { className: "text-muted-foreground size-4" }) }), _jsx(TooltipContent, { children: _jsx("p", { children: t('modify.disclaimer', 'Cannot modify items added in previous sessions') }) })] }) }))] })] })] }), (mode === 'create' || isLineAddedInModify(line.id)) && (_jsx("button", { className: "flex size-8 items-center justify-center rounded-md text-red-500 hover:bg-red-50 hover:text-red-600", onClick: () => removeLineItem(line.id), children: _jsx(Trash2, { className: "size-4" }) }))] }) }))] }, line.id))), currentOrder?.surcharges.map((surcharge) => (_jsxs(TableRow, { className: "bg-muted/10 hover:bg-muted/20", children: [_jsx(TableCell, { className: "text-primary py-3 font-medium", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Package, { className: "size-4 text-blue-500 dark:text-blue-400" }), surcharge.description] }) }), _jsx(TableCell, { className: "text-muted-foreground min-w-[200px] py-3 font-mono text-sm", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Tag, { className: "size-4 text-blue-500 dark:text-blue-400" }), surcharge.sku] }) }), _jsx(TableCell, { className: "py-3" }), _jsx(TableCell, { className: "text-nowrap py-3 font-medium", children: priceFormatter(surcharge.price, order.currencyCode) }), _jsx(TableCell, { className: "text-nowrap py-3 font-medium", children: priceFormatter(surcharge.priceWithTax, order.currencyCode) }), _jsx(TableCell, { className: "py-3" }), _jsx(TableCell, { className: "py-3 font-medium", children: priceFormatter(surcharge.priceWithTax, order.currencyCode) }), (mode === 'create' || mode === 'update') && _jsx(TableCell, { className: "py-3" })] }, surcharge.sku)))] })) : (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 8, children: _jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-center", children: [_jsx("div", { className: "mb-4 rounded-full bg-blue-100 p-3 dark:bg-blue-900/30", children: _jsx(ShoppingCart, { className: "size-6 text-blue-500 dark:text-blue-400" }) }), _jsx("span", { className: "text-muted-foreground text-lg font-medium", children: t('create.noItems', 'No products in this order') }), mode !== 'view' && (_jsx("span", { className: "text-muted-foreground mt-2 text-sm", children: t('create.searchPlaceholder', 'Use the search above to add products') }))] }) }) })) })] }) }), _jsx("div", { className: "mt-6 border-t pt-4", children: _jsx("div", { className: "bg-card rounded-lg border p-4 shadow-sm", children: _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex justify-between", children: [_jsx("span", { className: "text-base font-semibold", children: t('total') }), _jsx("span", { className: "text-primary text-base font-bold", children: priceFormatter(currentOrder?.totalWithTax || 0, currentOrder?.currencyCode) })] }), _jsx("div", { className: "text-muted-foreground mt-2 text-xs", children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(InfoIcon, { className: "size-3" }), _jsx("span", { children: t('totalItems', {
306
+ value: currentOrder?.lines.reduce((acc, line) => acc + line.quantity, 0) || 0,
307
+ }) })] }) })] }) }) }), _jsx(OrderLineActionModal, { onPriceQuantityChangeApprove: onPriceQuantityChangeApprove, onOpenChange: onOrderLineActionModalOpenChange, ...orderLineAction })] }) })] }));
308
+ };
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { OrderLineCustomFields } from "./OrderLineCustomFields.js";
3
+ import { Checkbox, ImageWithPreview, Input, Label, priceFormatter, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, useOrder, } from '@deenruv/react-ui-devkit';
4
+ import { Tag } from 'lucide-react';
5
+ import { useCallback } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+ export const ProductsTable = ({ setRefundLines, refundLines }) => {
8
+ const { t } = useTranslation('orders');
9
+ const { mode, currentOrder } = useOrder();
10
+ const handleLineChange = useCallback((lineId, quantity) => {
11
+ const existingLineIdx = refundLines.findIndex((l) => l.orderLineId === lineId);
12
+ console.log('EL', existingLineIdx);
13
+ setRefundLines((prev) => {
14
+ const newState = [...prev];
15
+ if (existingLineIdx !== -1) {
16
+ newState[existingLineIdx].quantity = quantity;
17
+ }
18
+ else {
19
+ newState.push({ orderLineId: lineId, quantity });
20
+ }
21
+ return newState;
22
+ });
23
+ }, [refundLines]);
24
+ return (_jsx("div", { className: "border-border rounded-lg border-0 shadow-sm", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { noHover: true, className: "hover:bg-transparent", children: [_jsx(TableHead, { className: "py-3 font-semibold", children: t('create.product', 'Product') }), _jsx(TableHead, { className: "py-3 font-semibold", children: t('create.sku', 'SKU') }), _jsx(TableHead, { className: "py-3 font-semibold", children: t('create.customFields', 'Custom Fields') }), _jsx(TableHead, { className: "py-3 font-semibold", children: t('create.price', 'Price') }), _jsx(TableHead, { className: "py-3 font-semibold", children: t('create.priceWithTax', 'Price with Tax') }), _jsx(TableHead, { className: "py-3 font-semibold", children: t('cancelAndRefund.refund') }), _jsx(TableHead, { className: "py-3 font-semibold", children: t('cancelAndRefund.returnToStock') })] }) }), _jsx(TableBody, { children: currentOrder.lines.map((line) => (_jsxs(TableRow, { className: "hover:bg-muted/20", children: [_jsx(TableCell, { className: "py-3", children: _jsxs("div", { className: "flex w-max items-center gap-3", children: [_jsx(ImageWithPreview, { imageClassName: "aspect-square w-12 h-12 rounded-md object-cover border border-border", src: line.productVariant.featuredAsset?.preview ||
25
+ line.productVariant.product?.featuredAsset?.preview ||
26
+ '/placeholder.svg' }), _jsx("div", { className: "text-primary font-medium", children: line.productVariant.product.name })] }) }), _jsx(TableCell, { className: "text-muted-foreground min-w-[200px] py-3 font-mono text-sm", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Tag, { className: "h-4 w-4 text-blue-500 dark:text-blue-400" }), line.productVariant.sku] }) }), _jsx(TableCell, { className: "py-3", children: _jsx(OrderLineCustomFields, { line: line, order: currentOrder, mode: mode }) }), _jsx(TableCell, { className: "py-3 font-medium", children: priceFormatter(line.linePrice, line.productVariant.currencyCode) }), _jsx(TableCell, { className: "py-3 font-medium", children: priceFormatter(line.linePriceWithTax, line.productVariant.currencyCode) }), _jsx(TableCell, { className: "py-3", children: _jsx(Input, { type: "number", wrapperClassName: "w-24", endAdornment: '/' + line.quantity, defaultValue: 0, max: line.quantity, onChange: (e) => {
27
+ handleLineChange(line.id, +e.target.value);
28
+ } }) }), _jsx(TableCell, { className: "py-3", children: _jsxs("div", { className: "flex gap-2", children: [_jsx(Checkbox, {}), _jsx(Label, { children: t('cancelAndRefund.returnToStock') })] }) })] }, line.id))) })] }) }));
29
+ };
@@ -0,0 +1,73 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useOrder, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Badge, ScrollArea, EmptyState, CustomCard, useTranslation, Button, useMutation, CardDescription, useLazyQuery, Input, OrderDetailSelector, priceFormatter, } from '@deenruv/react-ui-devkit';
4
+ import { BadgePercent, Gift, Tag, CreditCard, Trash, Plus } from 'lucide-react';
5
+ import { $, scalars, typedGql } from '@deenruv/admin-types';
6
+ import { toast } from 'sonner';
7
+ import { useEffect, useState } from 'react';
8
+ const ToggleExcludePromotionMutation = typedGql('mutation', { scalars })({
9
+ toggleExcludePromotionInOrder: [
10
+ { orderId: $('orderId', 'ID!'), promotionId: $('promotionId', 'ID!') },
11
+ OrderDetailSelector,
12
+ ],
13
+ });
14
+ const ExcludedPromotionsQuery = typedGql('query', { scalars })({
15
+ promotions: [{ options: { filter: { id: { in: $('ids', '[String!]') } } } }, { items: { name: true, id: true } }],
16
+ });
17
+ const ApplyCouponCodeMutation = typedGql('mutation', { scalars })({
18
+ applyCouponCodeToDraftOrder: [
19
+ { orderId: $('orderId', 'ID!'), couponCode: $('couponCode', 'String!') },
20
+ { '...on CouponCodeExpiredError': { message: true }, __typename: true, '...on Order': OrderDetailSelector },
21
+ ],
22
+ });
23
+ const RemoveCouponCodeMutation = typedGql('mutation', { scalars })({
24
+ removeCouponCodeFromDraftOrder: [
25
+ { orderId: $('orderId', 'ID!'), couponCode: $('couponCode', 'String!') },
26
+ OrderDetailSelector,
27
+ ],
28
+ });
29
+ export const PromotionsList = () => {
30
+ const { order, setOrder } = useOrder();
31
+ const { t } = useTranslation('orders');
32
+ const [togglePromotionExclusion, { loading: exclusionLoading }] = useMutation(ToggleExcludePromotionMutation);
33
+ const [removeCoupon, { loading: removeCouponLoading }] = useMutation(RemoveCouponCodeMutation);
34
+ const [applyCoupon] = useMutation(ApplyCouponCodeMutation);
35
+ const [fetchExcludedPromotions, { data: excludedData }] = useLazyQuery(ExcludedPromotionsQuery);
36
+ const excluded = excludedData?.promotions?.items ?? [];
37
+ const loading = exclusionLoading || removeCouponLoading;
38
+ useEffect(() => {
39
+ fetchExcludedPromotions({
40
+ ids: order?.excludedPromotionIds || [],
41
+ });
42
+ }, [order?.excludedPromotionIds]);
43
+ const [couponCode, setCouponCode] = useState('');
44
+ if (!order)
45
+ return null;
46
+ const removeExcludePromotionHandler = async ({ id, couponCode, }) => {
47
+ let res;
48
+ if (!couponCode) {
49
+ res = await togglePromotionExclusion({ orderId: order.id, promotionId: id });
50
+ }
51
+ else {
52
+ res = await removeCoupon({ orderId: order.id, couponCode: couponCode });
53
+ }
54
+ if ('removeCouponCodeFromDraftOrder' in res) {
55
+ setOrder(res.removeCouponCodeFromDraftOrder);
56
+ }
57
+ else if ('toggleExcludePromotionInOrder' in res) {
58
+ setOrder(res.toggleExcludePromotionInOrder);
59
+ }
60
+ };
61
+ const addCouponHandler = async () => {
62
+ const resp = await applyCoupon({ couponCode: couponCode, orderId: order.id });
63
+ if (resp.applyCouponCodeToDraftOrder.__typename === 'Order') {
64
+ setOrder(resp.applyCouponCodeToDraftOrder);
65
+ setCouponCode('');
66
+ toast.success(t('couponCodes.addToastSuccess'));
67
+ }
68
+ else {
69
+ toast.error('could not add coupon code');
70
+ }
71
+ };
72
+ return (_jsxs("div", { className: "grid w-full grid-cols-1 gap-4 md:grid-cols-6 lg:grid-cols-12", children: [_jsx(CustomCard, { notCollapsible: true, color: "blue", description: t('promotions.description', 'Active order promotions'), title: t('promotions.title', 'Promotions'), wrapperClassName: "col-span-1 h-full md:col-span-2 lg:col-span-6", icon: _jsx(Gift, {}), upperRight: _jsxs("div", { className: "flex gap-2 py-2", children: [_jsx(Input, { placeholder: t('couponCodes.placeholder'), value: couponCode, onChange: (e) => setCouponCode(e.target.value) }), _jsx(Button, { size: 'icon', className: "w-14", onClick: addCouponHandler, children: _jsx(Plus, {}) })] }), children: _jsxs(ScrollArea, { className: "h-[280px]", children: [_jsxs(Table, { className: "h-full", children: [_jsx(TableHeader, { children: _jsxs(TableRow, { noHover: true, className: "border-border border-b", children: [_jsx(TableHead, { className: "py-3", children: t('taxSummary.description') }), _jsx(TableHead, { className: "py-3", children: t('couponCodes.title') }), _jsx(TableHead, { align: "right", style: { textAlign: 'right' }, children: t('table.actions') })] }) }), _jsx(TableBody, { className: "h-[232px]", children: order.promotions.length ? (order.promotions.map(({ name, couponCode, id }) => (_jsxs(TableRow, { noHover: true, className: "group", children: [_jsx(TableCell, { className: "py-3 font-medium", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Tag, { className: "size-4 text-blue-500 dark:text-blue-400" }), _jsx("span", { className: "capitalize", children: name })] }) }), _jsx(TableCell, { className: "py-3", children: couponCode ? (_jsx(Badge, { variant: "outline", className: "font-mono text-xs", children: couponCode })) : (_jsx("span", { className: "text-muted-foreground", children: "\u2014" })) }), _jsx(TableCell, { align: "right", children: _jsx(Button, { size: "icon", variant: "destructive", disabled: loading, onClick: () => removeExcludePromotionHandler({ couponCode, id }), children: _jsx(Trash, { size: 16 }) }) })] }, name)))) : (_jsx(EmptyState, { columnsLength: 3, title: t('promotions.noPromotions', 'No promotions applied'), color: "blue", icon: _jsx(Gift, {}), small: true })) })] }), !!excluded.length && (_jsxs(_Fragment, { children: [_jsx("hr", { className: "!my-4 border-t-[3px]" }), _jsx(CardDescription, { children: t('promotion.excludedPromotions') }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { noHover: true, children: [_jsx(TableHead, { children: t('promotion.name') }), _jsx(TableHead, { align: "right", style: { textAlign: 'right' }, children: t('table.actions') })] }) }), _jsx(TableBody, { children: excluded.map(({ name, id }) => (_jsxs(TableRow, { noHover: true, children: [_jsx(TableCell, { className: "capitalize", children: name }), _jsx(TableCell, { align: "right", children: _jsx(Button, { size: "icon", variant: "destructive", onClick: () => removeExcludePromotionHandler({ id }), children: _jsx(Trash, { size: 16 }) }) })] }, name))) })] })] }))] }) }), _jsx(CustomCard, { notCollapsible: true, description: t('discounts.description', 'Applied order discounts'), title: t('discounts.title', 'Discounts'), icon: _jsx(BadgePercent, { className: "size-5 text-green-500 dark:text-green-400" }), wrapperClassName: "col-span-1 h-full md:col-span-2 lg:col-span-6", color: "green", children: _jsx(ScrollArea, { className: "h-[280px]", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { noHover: true, className: "border-border border-b", children: [_jsx(TableHead, { className: "py-3", children: t('taxSummary.description') }), _jsx(TableHead, { className: "py-3", children: t('taxSummary.taxRate', 'Type') }), _jsx(TableHead, { className: "py-3", children: t('taxSummary.taxBase', 'Amount') }), _jsx(TableHead, { className: "py-3", children: t('taxSummary.taxTotal', 'With Tax') }), _jsx(TableHead, { className: "py-3", children: t('discounts.source', 'Source') })] }) }), _jsx(TableBody, { className: "h-[232px]", children: order.discounts.length ? (order.discounts.map(({ adjustmentSource, amount, amountWithTax, description, type }, index) => (_jsxs(TableRow, { noHover: true, className: "group", children: [_jsx(TableCell, { className: "py-3 font-medium", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CreditCard, { className: "size-4 text-green-500 dark:text-green-400" }), _jsx("span", { className: "capitalize", children: description })] }) }), _jsx(TableCell, { className: "py-3", children: _jsx(Badge, { variant: "secondary", className: "font-medium", children: type }) }), _jsx(TableCell, { className: "py-3 font-mono text-sm", children: priceFormatter(amount, order.currencyCode) }), _jsx(TableCell, { className: "py-3 font-mono text-sm", children: priceFormatter(amountWithTax, order.currencyCode) }), _jsx(TableCell, { className: "py-3", children: _jsx(Badge, { variant: "outline", className: "capitalize", children: adjustmentSource }) })] }, index)))) : (_jsx(EmptyState, { columnsLength: 5, title: t('discounts.emptyState'), color: "green", icon: _jsx(BadgePercent, {}), small: true })) })] }) }) })] }));
73
+ };
@@ -0,0 +1,98 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { TableHeader, TableRow, TableHead, TableBody, TableCell, Table, apiClient, useOrder, OrderDetailSelector, Badge, ScrollArea, CustomCard, ContextMenu, DropdownMenuItem, ConfirmationDialog, Button, useTranslation, } from '@deenruv/react-ui-devkit';
4
+ import { AnimatePresence, motion } from 'framer-motion';
5
+ import { useCallback, useState } from 'react';
6
+ import { toast } from 'sonner';
7
+ import { ORDER_STATE } from "../../../graphql/base";
8
+ import { Package, Truck, CheckCircle, XCircle, AlertCircle, ClipboardCheck } from 'lucide-react';
9
+ export const RealizationCard = () => {
10
+ const { order, setOrder, fetchOrderHistory, cancelFulfillment } = useOrder();
11
+ const { t } = useTranslation('orders');
12
+ const [processingFulfillments, setProcessingFulfillments] = useState({});
13
+ const markAsDelivered = async (fulfillmentId) => {
14
+ if (!order)
15
+ return;
16
+ setProcessingFulfillments((prev) => ({ ...prev, [fulfillmentId]: true }));
17
+ try {
18
+ const { transitionFulfillmentToState } = await apiClient('mutation')({
19
+ transitionFulfillmentToState: [
20
+ { id: fulfillmentId, state: 'Delivered' },
21
+ {
22
+ __typename: true,
23
+ '...on Fulfillment': {
24
+ id: true,
25
+ },
26
+ '...on FulfillmentStateTransitionError': {
27
+ errorCode: true,
28
+ fromState: true,
29
+ message: true,
30
+ toState: true,
31
+ transitionError: true,
32
+ },
33
+ },
34
+ ],
35
+ });
36
+ if (transitionFulfillmentToState.__typename === 'Fulfillment') {
37
+ const resp = await apiClient('query')({ order: [{ id: order.id }, OrderDetailSelector] });
38
+ setOrder(resp.order);
39
+ fetchOrderHistory();
40
+ toast.success(t('fulfillments.deliveredSuccess', 'Fulfillment marked as delivered'));
41
+ }
42
+ else {
43
+ const errorMessage = transitionFulfillmentToState?.message || t('fulfillments.error', 'Something went wrong');
44
+ toast.error(errorMessage);
45
+ }
46
+ }
47
+ catch (error) {
48
+ toast.error(t('fulfillments.error', 'Something went wrong'));
49
+ }
50
+ finally {
51
+ setProcessingFulfillments((prev) => ({ ...prev, [fulfillmentId]: false }));
52
+ }
53
+ };
54
+ const handleCancelFulfillment = async (fulfillmentId) => {
55
+ if (!order)
56
+ return;
57
+ setProcessingFulfillments((prev) => ({ ...prev, [fulfillmentId]: true }));
58
+ try {
59
+ cancelFulfillment(fulfillmentId);
60
+ }
61
+ finally {
62
+ setProcessingFulfillments((prev) => ({ ...prev, [fulfillmentId]: false }));
63
+ }
64
+ };
65
+ const markAllAsDelivered = useCallback(async () => {
66
+ const shippedIds = order?.fulfillments?.filter((f) => f.state === ORDER_STATE.SHIPPED).map((f) => f.id);
67
+ shippedIds?.forEach((id) => {
68
+ markAsDelivered(id);
69
+ });
70
+ }, []);
71
+ const getFulfillmentStateBadge = (state) => {
72
+ switch (state) {
73
+ case ORDER_STATE.CREATED:
74
+ return { variant: 'secondary', icon: _jsx(Package, { className: "mr-1 size-3.5" }) };
75
+ case ORDER_STATE.SHIPPED:
76
+ return { variant: 'outline', icon: _jsx(Truck, { className: "mr-1 size-3.5 text-blue-500" }) };
77
+ case ORDER_STATE.DELIVERED:
78
+ return { variant: 'success', icon: _jsx(CheckCircle, { className: "mr-1 size-3.5" }) };
79
+ case ORDER_STATE.CANCELLED:
80
+ return { variant: 'destructive', icon: _jsx(XCircle, { className: "mr-1 size-3.5" }) };
81
+ default:
82
+ return { variant: 'outline', icon: _jsx(AlertCircle, { className: "mr-1 size-3.5" }) };
83
+ }
84
+ };
85
+ if (!order)
86
+ return null;
87
+ if (!order.fulfillments?.length)
88
+ return null;
89
+ return (_jsx(AnimatePresence, { children: _jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.4, ease: 'easeInOut' }, children: _jsx(CustomCard, { title: t('fulfillments.title', 'Order Fulfillments'), description: t('fulfillments.description', 'Track and manage the delivery status of this order'), icon: _jsx(ClipboardCheck, {}), color: "rose", upperRight: _jsxs(Button, { variant: order.state === ORDER_STATE.SHIPPED ? 'action' : 'ghost', size: "sm", disabled: order.state !== ORDER_STATE.SHIPPED, className: "gap-2", onClick: markAllAsDelivered, children: [_jsx(CheckCircle, { className: "size-4" }), order?.fulfillments?.filter((f) => f.state === ORDER_STATE.SHIPPED).length > 0
90
+ ? t('fulfillments.markAllAsDelivered')
91
+ : t('fulfillments.allDelivered')] }), children: _jsx(ScrollArea, { className: "max-h-[350px]", children: _jsxs(Table, { children: [_jsx(TableHeader, { className: "bg-muted/30", children: _jsxs(TableRow, { noHover: true, className: "hover:bg-transparent", children: [_jsx(TableHead, { className: "py-3 font-semibold", children: t('fulfillments.method', 'Method') }), _jsx(TableHead, { className: "py-3 font-semibold", children: t('fulfillments.state', 'Status') }), _jsx(TableHead, { className: "py-3 font-semibold", children: t('fulfillments.trackingCode', 'Tracking Code') }), _jsx(TableHead, { className: "py-3 text-right font-semibold", children: t('fulfillments.actions', 'Actions') })] }) }), _jsx(TableBody, { children: order.fulfillments.map((fulfillment) => {
92
+ const stateBadge = getFulfillmentStateBadge(fulfillment.state);
93
+ const isProcessing = processingFulfillments[fulfillment.id];
94
+ console.log('FLF', fulfillment);
95
+ return (_jsxs(TableRow, { className: "hover:bg-muted/20", children: [_jsx(TableCell, { className: "py-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Truck, { className: "size-4 text-green-500 dark:text-green-400" }), _jsx("span", { className: "font-medium", children: fulfillment.method })] }) }), _jsx(TableCell, { className: "py-3", children: _jsxs(Badge, { variant: stateBadge.variant, className: "flex w-fit items-center gap-1", children: [stateBadge.icon, t('fulfillments.states.' + fulfillment.state.toLowerCase())] }) }), _jsx(TableCell, { className: "py-3", children: fulfillment.trackingCode ? (_jsx("div", { className: "flex items-center gap-2", children: _jsx("code", { className: "bg-muted rounded px-2 py-1 font-mono text-xs", children: fulfillment.trackingCode }) })) : (_jsx("span", { className: "text-muted-foreground", children: "\u2014" })) }), _jsx(TableCell, { className: "py-3 text-right", children: _jsxs(ContextMenu, { disabled: fulfillment.state === ORDER_STATE.CANCELLED, children: [fulfillment.state === ORDER_STATE.SHIPPED && (_jsxs(DropdownMenuItem, { onClick: () => markAsDelivered(fulfillment.id), disabled: isProcessing, className: "flex cursor-pointer items-center gap-2", children: [_jsx(CheckCircle, { className: "size-3.5 text-green-500" }), t('fulfillments.markAsDelivered', 'Mark as Delivered')] }, 'set')), (fulfillment.state === ORDER_STATE.DELIVERED ||
96
+ fulfillment.state === ORDER_STATE.SHIPPED) && (_jsx(ConfirmationDialog, { onConfirm: () => handleCancelFulfillment(fulfillment.id), children: _jsxs(DropdownMenuItem, { disabled: isProcessing, className: "flex cursor-pointer items-center gap-2 text-red-500", onSelect: (e) => e.preventDefault(), children: [_jsx(XCircle, { size: 16 }), t('fulfillments.cancel', 'Cancel')] }, 'cancel') }))] }) })] }, fulfillment.id));
97
+ }) })] }) }) }) }) }));
98
+ };
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useTranslation, cn, priceFormatter, Textarea, useOrder } from '@deenruv/react-ui-devkit';
3
+ import { CreditCard, FileText } from 'lucide-react';
4
+ import { useState } from 'react';
5
+ export const RefundCard = ({ priceDifference, refundReason, setRefundReason }) => {
6
+ const { t } = useTranslation('orders');
7
+ const { modifiedOrder } = useOrder();
8
+ const currentPayment = modifiedOrder?.payments?.[0];
9
+ const [activeTab, setActiveTab] = useState('details');
10
+ return (_jsxs("div", { className: "border-border bg-muted/20 space-y-4 rounded-md border p-4", children: [_jsxs("div", { className: "flex items-center gap-6", children: [_jsxs("div", { className: cn('flex cursor-pointer items-center gap-2', activeTab === 'details' ? 'opacity-100' : 'opacity-60 hover:opacity-80'), onClick: () => setActiveTab('details'), children: [_jsx("div", { className: "flex size-8 items-center justify-center rounded-full bg-blue-100 dark:bg-blue-900/30", children: _jsx(CreditCard, { className: "size-4 text-blue-500 dark:text-blue-400" }) }), _jsx("h4", { className: "font-medium", children: t('refund.details', 'Refund Details') })] }), _jsxs("div", { className: cn('flex cursor-pointer items-center gap-2', activeTab === 'note' ? 'opacity-100' : 'opacity-60 hover:opacity-80'), onClick: () => setActiveTab('note'), children: [_jsx("div", { className: "flex size-8 items-center justify-center rounded-full bg-blue-100 dark:bg-blue-900/30", children: _jsx(FileText, { className: "size-4 text-blue-500 dark:text-blue-400" }) }), _jsx("h4", { className: "font-medium", children: t('refund.note', 'Refund Note') })] })] }), activeTab === 'details' && (_jsxs("div", { className: "grid gap-6", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { children: [_jsx("p", { className: "text-muted-foreground text-sm", children: t('refund.method') }), _jsx("p", { className: "font-medium", children: currentPayment?.method })] }), _jsxs("div", { children: [_jsx("p", { className: "text-muted-foreground text-sm", children: t('refund.id') }), _jsx("p", { className: "font-medium", children: currentPayment?.transactionId })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { children: [_jsx("p", { className: "text-muted-foreground text-sm", children: t('refund.amount') }), _jsx("p", { className: "font-medium", children: priceFormatter(currentPayment?.amount ?? 0, modifiedOrder?.currencyCode) })] }), _jsxs("div", { children: [_jsx("p", { className: "text-muted-foreground text-sm", children: t('refund.refund') }), _jsx("p", { className: "font-medium", children: priceFormatter(priceDifference * -1, modifiedOrder?.currencyCode) })] })] })] })), activeTab === 'note' && (_jsx("div", { className: "space-y-2", children: _jsx(Textarea, { id: "note", placeholder: t('notePlaceholder', 'Enter optional info of the reason'), value: refundReason, onChange: (e) => setRefundReason(e.target.value), className: "min-h-[60px] resize-y" }) }))] }));
11
+ };
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Checkbox, CustomCard, Input, Label, priceFormatter, Textarea, useOrder } from '@deenruv/react-ui-devkit';
3
+ import { useTranslation } from 'react-i18next';
4
+ export const RefundPaymentCard = ({ refundReason, setRefundReason, cancelShipping, setCancelShipping, refundAmount, setRefundAmount, }) => {
5
+ const { t } = useTranslation('orders');
6
+ const { modifiedOrder } = useOrder();
7
+ const currentPayment = modifiedOrder?.payments?.[0];
8
+ return (_jsxs("div", { className: "mt-8 grid grid-cols-2 gap-4", children: [_jsx(CustomCard, { title: t('refund.details', 'Refund Details'), notCollapsible: true, children: _jsxs("div", { className: "grid gap-6", children: [_jsx("div", { className: "grid grid-cols-2 gap-4", children: _jsxs("div", { className: "flex gap-2", children: [_jsx(Checkbox, { checked: cancelShipping, onCheckedChange: (e) => setCancelShipping(!!e) }), _jsx(Label, { children: t('cancelAndRefund.refundShipping') })] }) }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { children: [_jsx("p", { className: "text-muted-foreground text-sm", children: t('refund.method') }), _jsx("p", { className: "font-medium", children: currentPayment?.method })] }), _jsxs("div", { children: [_jsx("p", { className: "text-muted-foreground text-sm", children: t('refund.id') }), _jsx("p", { className: "font-medium", children: currentPayment?.transactionId })] })] }), _jsx("div", { className: "grid grid-cols-2 gap-4", children: _jsxs("div", { children: [_jsx("p", { className: "text-muted-foreground text-sm", children: t('refund.amount') }), _jsx("p", { className: "font-medium", children: priceFormatter(currentPayment?.amount ?? 0, modifiedOrder?.currencyCode) })] }) }), _jsx("div", { className: "grid grid-cols-2 gap-4", children: _jsxs("div", { children: [_jsx("p", { className: "text-muted-foreground text-sm", children: t('refund.refund') }), _jsx(Input
9
+ // type="currency"
10
+ , {
11
+ // type="currency"
12
+ startAdornment: modifiedOrder?.currencyCode, adornmentPlain: true, value: refundAmount, onChange: (e) => setRefundAmount(+e.target.value) })] }) })] }) }), _jsx(CustomCard, { title: t('refund.note', 'Refund Note'), notCollapsible: true, children: _jsx(Textarea, { id: "note", placeholder: t('notePlaceholder', 'Enter optional info of the reason'), value: refundReason, onChange: (e) => setRefundReason(e.target.value), className: "min-h-[60px] resize-y", rows: 8 }) })] }));
13
+ };