@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,93 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { DeletionResult } from '@deenruv/admin-types';
3
+ import { useTranslation, Button, MultipleSelector, TableCell, TableRow, apiClient, } from '@deenruv/react-ui-devkit';
4
+ import { Trash } from 'lucide-react';
5
+ import { useCallback, useState } from 'react';
6
+ import { toast } from 'sonner';
7
+ export const OptionGroup = ({ group, productId, contentLanguage, optionsUsedByVariants, onActionCompleted, }) => {
8
+ const { t } = useTranslation('products');
9
+ const [state, setState] = useState(group.options
10
+ ?.sort((a, b) => a.id.localeCompare(b.id))
11
+ .map((o) => ({ label: o.name, value: o.id, ...(optionsUsedByVariants.includes(o.id) && { fixed: true }) })));
12
+ const removeGroup = useCallback((optionGroupId) => {
13
+ if (!productId)
14
+ return;
15
+ apiClient('mutation')({
16
+ removeOptionGroupFromProduct: [
17
+ { optionGroupId, productId: productId },
18
+ { '...on Product': { id: true }, '...on ProductOptionInUseError': { message: true } },
19
+ ],
20
+ })
21
+ .then(() => {
22
+ toast(t('toasts.deletionOptionSuccessToast'));
23
+ onActionCompleted();
24
+ })
25
+ .catch(() => {
26
+ toast.error(t('toasts.deletionOptionErrorToast'));
27
+ });
28
+ }, [productId, onActionCompleted, t]);
29
+ const addOption = useCallback((option, optionGroupId) => {
30
+ if (!productId)
31
+ return;
32
+ apiClient('mutation')({
33
+ createProductOption: [
34
+ {
35
+ input: {
36
+ code: option.label.replace(/\s/g, ''),
37
+ productOptionGroupId: optionGroupId,
38
+ translations: [{ languageCode: contentLanguage, name: option.label }],
39
+ },
40
+ },
41
+ { id: true },
42
+ ],
43
+ })
44
+ .then(() => {
45
+ toast(t('toasts.createOptionSuccessToast'));
46
+ onActionCompleted();
47
+ })
48
+ .catch(() => {
49
+ toast(t('toasts.createOptionErrorToast'));
50
+ });
51
+ }, [productId, contentLanguage, t]);
52
+ const deleteOption = useCallback((optionId) => {
53
+ if (!productId)
54
+ return;
55
+ apiClient('mutation')({
56
+ deleteProductOption: [
57
+ {
58
+ id: optionId,
59
+ },
60
+ {
61
+ result: true,
62
+ },
63
+ ],
64
+ })
65
+ .then((resp) => {
66
+ if (resp.deleteProductOption.result === DeletionResult.DELETED) {
67
+ toast(t('toasts.createOptionSuccessToast'));
68
+ onActionCompleted();
69
+ }
70
+ else {
71
+ toast.error(t('toasts.createOptionInUseToast'));
72
+ }
73
+ })
74
+ .catch(() => {
75
+ toast.error(t('toasts.createOptionErrorToast'));
76
+ });
77
+ }, [productId, contentLanguage, t]);
78
+ const handleChange = useCallback((currentOptions, optionGroupId) => {
79
+ const added = group?.options.length < currentOptions.length;
80
+ if (added) {
81
+ setState(currentOptions);
82
+ const newOption = currentOptions[currentOptions.length - 1];
83
+ addOption(newOption, optionGroupId);
84
+ }
85
+ else {
86
+ const unpickedOption = state.find((o) => !currentOptions.includes(o));
87
+ if (unpickedOption) {
88
+ deleteOption(unpickedOption.value);
89
+ }
90
+ }
91
+ }, [group, addOption]);
92
+ return (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: group.name }), _jsx(TableCell, { children: _jsx(MultipleSelector, { className: "h-20", value: state, placeholder: t('optionsTab.placeholder'), onChange: (e) => handleChange(e, group.id), options: [], hideClearAllButton: true, creatable: true }) }), _jsx(TableCell, { className: "w-12", children: _jsx(Button, { size: 'icon', variant: 'outline', className: "size-8", onClick: () => removeGroup(group.id), children: _jsx(Trash, { size: 20, className: "text-red-600" }) }) })] }, group.id));
93
+ };
@@ -0,0 +1,59 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect } from 'react';
3
+ import { Button, Input, Card, CardHeader, CardTitle, CardContent, apiClient, useGFFLP, setInArrayBy, EntityCustomFields, useTranslation, } from '@deenruv/react-ui-devkit';
4
+ import { toast } from 'sonner';
5
+ export const OptionValueCard = ({ productOption, onEdited, currentTranslationLng, optionGroupId, }) => {
6
+ const { t } = useTranslation('products');
7
+ const { state, setField } = useGFFLP('UpdateProductOptionInput', 'code', 'translations', 'customFields')({});
8
+ const translations = state?.translations?.value || [];
9
+ const currentTranslationValue = translations.find((v) => v.languageCode === currentTranslationLng);
10
+ useEffect(() => {
11
+ setField('code', productOption.code);
12
+ setField('translations', productOption.translations);
13
+ if ('customFields' in productOption) {
14
+ setField('customFields', productOption.customFields);
15
+ }
16
+ }, [productOption]);
17
+ const editOption = useCallback(() => {
18
+ if (productOption.id) {
19
+ console.log('INPUT', {
20
+ id: productOption.id,
21
+ code: state.code?.validatedValue,
22
+ customFields: state.customFields?.validatedValue,
23
+ translations: state.translations?.validatedValue,
24
+ });
25
+ return apiClient('mutation')({
26
+ updateProductOption: [
27
+ {
28
+ input: {
29
+ id: productOption.id,
30
+ code: state.code?.validatedValue,
31
+ customFields: state.customFields?.validatedValue,
32
+ translations: state.translations?.validatedValue,
33
+ },
34
+ },
35
+ { id: true },
36
+ ],
37
+ })
38
+ .then(() => {
39
+ toast(t('toasts.updateOptionSuccessToast'));
40
+ onEdited();
41
+ })
42
+ .catch(() => {
43
+ toast(t('toasts.updateOptionErrorToast'));
44
+ });
45
+ }
46
+ }, [state, productOption, t, onEdited]);
47
+ return (_jsxs(Card, { className: "flex-grow basis-1/5", children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "flex flex-row justify-between text-base", children: productOption.name }) }), _jsxs(CardContent, { children: [_jsx("div", { className: "flex flex-col justify-between gap-6", children: _jsxs("div", { className: "flex flex-col gap-3", children: [_jsx(Input, { label: "name", value: currentTranslationValue?.name ?? undefined, onChange: (e) => {
48
+ setField('translations', setInArrayBy(translations, (t) => t.languageCode !== currentTranslationLng, {
49
+ name: e.target.value,
50
+ languageCode: currentTranslationLng,
51
+ }));
52
+ } }), _jsx(Input, { label: "code", value: state.code?.value ?? undefined, onChange: (e) => {
53
+ setField('code', e.target.value);
54
+ } }), _jsx(EntityCustomFields, { entityName: "productOption", withoutBorder: true, id: productOption.id, currentLanguage: currentTranslationLng, initialValues: state && 'customFields' in state
55
+ ? { customFields: state.customFields?.validatedValue }
56
+ : { customFields: {} }, onChange: (cf) => {
57
+ setField('customFields', cf);
58
+ }, additionalData: {} })] }) }), _jsx(Button, { size: 'sm', className: "mt-4", onClick: editOption, children: t('editOption') })] })] }));
59
+ };
@@ -0,0 +1,38 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import { Table, TableBody, TableCell, TableRow, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, apiClient, CustomCard, CardIcons, useTranslation, } from '@deenruv/react-ui-devkit';
4
+ import { OptionGroupSelector } from "../../../graphql/products";
5
+ import { toast } from 'sonner';
6
+ export const OptionsCard = ({ optionGroups: options, productId, onChange, optionIds, createMode, }) => {
7
+ const { t } = useTranslation('products');
8
+ const [optionGroups, setOptionGroups] = useState();
9
+ const fetchOptionGroups = useCallback(async () => {
10
+ if (productId) {
11
+ const response = await apiClient('query')({
12
+ product: [
13
+ {
14
+ id: productId,
15
+ },
16
+ {
17
+ optionGroups: OptionGroupSelector,
18
+ },
19
+ ],
20
+ });
21
+ setOptionGroups(response.product?.optionGroups);
22
+ if (!response.product) {
23
+ toast.error(t('toasts.fetchProductErrorToast'));
24
+ }
25
+ }
26
+ }, [productId]);
27
+ useEffect(() => {
28
+ fetchOptionGroups();
29
+ }, [fetchOptionGroups]);
30
+ const handleOptionChange = useCallback((optionId, groupIdx) => {
31
+ const newState = [...(optionIds || [])];
32
+ newState[groupIdx] = optionId;
33
+ onChange(newState);
34
+ }, [optionIds, onChange]);
35
+ return (_jsx(CustomCard, { title: t('options'), icon: _jsx(CardIcons.options, {}), color: "orange", children: !createMode ? (_jsx(Table, { children: _jsx(TableBody, { children: options?.map((o) => (_jsxs(TableRow, { children: [_jsxs(TableCell, { className: "font-semibold capitalize", children: [o.group.name, ":"] }), _jsx(TableCell, { className: "capitalize", children: o.name })] }, o.name))) }) })) : (optionGroups?.map((group, i) => (_jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("div", { className: "w-1/3 font-semibold", children: [group.name, ":"] }), _jsx("div", { className: "w-2/3", children: _jsxs(Select, { value: optionIds?.[i] || '', onValueChange: (e) => {
36
+ handleOptionChange(e, i);
37
+ }, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: t('addVariantDialog.selectOption') }) }), _jsx(SelectContent, { children: group.options.map((o) => (_jsx(SelectItem, { value: o.id, className: "capitalize", children: o.name }, o.id))) })] }) })] }, group.name)))) }));
38
+ };
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { OptionGroupSelector } from "../../../graphql/products";
3
+ import { useCallback, useEffect, useState } from 'react';
4
+ import { Table, TableBody, TableHead, TableHeader, TableRow, useDetailView, apiClient, useSettings, EmptyState, CustomCard, CardIcons, Separator, useTranslation, } from '@deenruv/react-ui-devkit';
5
+ import { toast } from 'sonner';
6
+ import { AddOptionGroupDialog } from "./AddOptionGroupDialog";
7
+ import { OptionValueCard } from "./OptionValueCard";
8
+ import { OptionGroup } from "./OptionGroup";
9
+ import { Info } from 'lucide-react';
10
+ export const OptionsTab = () => {
11
+ const contentLng = useSettings((p) => p.translationsLanguage);
12
+ const { id, getMarker, setLoading } = useDetailView('products-detail-view', 'CreateProductInput');
13
+ const { t } = useTranslation('products');
14
+ const [optionGroups, setOptionGroups] = useState();
15
+ const [optionsUsedByVariants, setOptionsUsedByVariants] = useState([]);
16
+ const fetchOptionGroups = useCallback(async () => {
17
+ setLoading(true);
18
+ if (id) {
19
+ const response = await apiClient('query')({
20
+ product: [{ id }, { optionGroups: OptionGroupSelector, variants: { options: { id: true } } }],
21
+ });
22
+ setOptionGroups(response.product?.optionGroups);
23
+ setOptionsUsedByVariants(response.product?.variants.flatMap((v) => v.options.map((o) => o.id)) || []);
24
+ setLoading(false);
25
+ if (!response.product) {
26
+ toast.error(t('toasts.fetchProductErrorToast'));
27
+ }
28
+ }
29
+ }, [id]);
30
+ useEffect(() => {
31
+ fetchOptionGroups();
32
+ }, [fetchOptionGroups]);
33
+ return (_jsx("div", { className: "flex flex-col items-end", children: _jsxs("div", { className: "flex w-full flex-col gap-3", children: [getMarker(), _jsx(CustomCard, { title: t('optionGroups'), color: "purple", icon: _jsx(CardIcons.group, {}), upperRight: _jsx(AddOptionGroupDialog, { currentTranslationLng: contentLng, onSuccess: fetchOptionGroups, productId: id }), children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: t('optionsTab.groupName') }), _jsx(TableHead, { children: t('optionsTab.values') }), _jsx(TableHead, {})] }) }), _jsx(TableBody, { children: id && optionGroups?.length ? (optionGroups
34
+ ?.sort((a, b) => a.id.localeCompare(b.id))
35
+ ?.map((group) => (_jsx(OptionGroup, { contentLanguage: contentLng, group: group, productId: id, onActionCompleted: fetchOptionGroups, optionsUsedByVariants: optionsUsedByVariants })))) : (_jsx(EmptyState, { columnsLength: 2, title: t('optionsTab.emptyState.title'), description: t('optionsTab.emptyState.description') })) })] }) }), _jsx(Separator, {}), optionGroups
36
+ ?.sort((a, b) => a.id.localeCompare(b.id))
37
+ ?.map((oG) => (_jsx(CustomCard, { variant: "group", title: `${t('group')}: ${oG.name}`, icon: _jsx(CardIcons.default, {}), collapsed: true, notCollapsible: !oG.options.length, upperRight: !oG.options.length && (_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(Info, { className: "size-4" }), t('optionsTab.noOptionValues')] })), children: _jsx("div", { className: "grid grid-cols-4 gap-3", children: oG.options
38
+ ?.sort((a, b) => a.id.localeCompare(b.id))
39
+ .map((o) => (_jsx(OptionValueCard, { currentTranslationLng: contentLng, productOption: o, optionGroupId: oG.id, onEdited: fetchOptionGroups }, o.id))) }) }, oG.id)))] }) }));
40
+ };
@@ -0,0 +1,27 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, apiClient, CustomCard, CardIcons, useTranslation, } from '@deenruv/react-ui-devkit';
3
+ import { useCallback, useEffect, useState } from 'react';
4
+ export const PriceCard = ({ priceValue, onPriceChange, currencyCode, taxRateValue, onTaxRateChange, }) => {
5
+ const { t } = useTranslation('products');
6
+ const [taxCategories, setTaxCategories] = useState([]);
7
+ const [currentTaxCategory, setCurrentTaxCategory] = useState();
8
+ useEffect(() => {
9
+ fetchTaxRates();
10
+ }, []);
11
+ const fetchTaxRates = useCallback(async () => {
12
+ const response = await apiClient('query')({
13
+ taxCategories: [{}, { items: { id: true, name: true } }],
14
+ taxRates: [{}, { items: { value: true, category: { id: true } } }],
15
+ });
16
+ const categoriesWithRates = response.taxCategories.items.map((c) => ({
17
+ ...c,
18
+ value: response.taxRates.items.find((r) => r.category.id === c.id)?.value,
19
+ }));
20
+ setTaxCategories(categoriesWithRates);
21
+ }, []);
22
+ const handlePriceChange = useCallback((currencyCode, value) => {
23
+ const newPrices = priceValue?.map((p) => (p.currencyCode === currencyCode ? { ...p, price: value } : p));
24
+ onPriceChange(newPrices || []);
25
+ }, [priceValue, onPriceChange]);
26
+ return (_jsx(CustomCard, { title: t('details.price'), color: "rose", icon: _jsx(CardIcons.calc, {}), children: _jsxs("div", { className: "flex flex-col gap-y-4", children: [priceValue?.map((price) => (_jsx("div", { className: "flex items-center gap-x-2", children: _jsx(Input, { type: "currency", placeholder: t('price'), value: price.price, onChange: (e) => handlePriceChange(price.currencyCode, +e.target.value), step: 0.01, startAdornment: price.currencyCode }) }, price.currencyCode))), _jsxs(Select, { value: taxRateValue, onValueChange: onTaxRateChange, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Tax rate" }) }), _jsx(SelectContent, { children: taxCategories.map((tR) => (_jsx(SelectItem, { value: tR.id.toString(), children: tR.name }, tR.id))) })] }), currentTaxCategory?.value !== undefined && `${t('details.taxRateDescription')} ${currentTaxCategory?.value}%`] }) }));
27
+ };
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { EntityChannelManager, Routes, useDetailView } from '@deenruv/react-ui-devkit';
3
+ import { useEffect } from 'react';
4
+ import { SettingsCard } from './SettingsCard';
5
+ import { CollectionsCard } from "./CollectionsCard";
6
+ import { FacetValuesCard } from "./FacetValuesCard";
7
+ import { useNavigate } from 'react-router-dom';
8
+ const PRODUCT_FORM_KEYS = [
9
+ 'CreateProductInput',
10
+ 'translations',
11
+ 'assetIds',
12
+ 'featuredAssetId',
13
+ 'facetValueIds',
14
+ 'enabled',
15
+ ];
16
+ export const ProductDetailSidebar = ({ marker }) => {
17
+ const { form, entity } = useDetailView('products-detail-view', ...PRODUCT_FORM_KEYS);
18
+ const navigate = useNavigate();
19
+ const { base: { state, setField }, } = form;
20
+ useEffect(() => {
21
+ if (!entity) {
22
+ setField('facetValueIds', []);
23
+ setField('enabled', true);
24
+ return;
25
+ }
26
+ setField('facetValueIds', entity.facetValues.map((f) => f.id));
27
+ setField('enabled', entity.enabled);
28
+ }, [entity]);
29
+ return (_jsxs("div", { className: "flex w-full flex-col gap-4", children: [_jsx(SettingsCard, { enabledValue: state.enabled?.value ?? undefined, onEnabledChange: (e) => setField('enabled', e) }), _jsx(FacetValuesCard, { facetValuesIds: state.facetValueIds?.value ?? undefined, onChange: (e) => setField('facetValueIds', e) }), !!entity?.channels?.length && (_jsx(EntityChannelManager, { entity: "product", entityChannels: entity.channels, entityId: entity.id, onRemoveSuccess: () => navigate(Routes.products.list), entitySlug: entity.slug, entityName: entity.name })), !!entity?.collections?.length && _jsx(CollectionsCard, { collections: entity.collections })] }));
30
+ };
@@ -0,0 +1,54 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useDetailView, DetailViewMarker, useSettings, setInArrayBy, EntityCustomFields, } from '@deenruv/react-ui-devkit';
3
+ import { useCallback, useEffect, useMemo } from 'react';
4
+ import { BasicFieldsCard } from './BasicFieldsCard';
5
+ import { AssetsCard } from './AssetsCard';
6
+ export const PRODUCT_FORM_KEYS = [
7
+ 'CreateProductInput',
8
+ 'translations',
9
+ 'assetIds',
10
+ 'featuredAssetId',
11
+ 'facetValueIds',
12
+ 'enabled',
13
+ 'customFields',
14
+ ];
15
+ export const ProductDetailView = () => {
16
+ const contentLng = useSettings((p) => p.translationsLanguage);
17
+ const selectedChannel = useSettings((p) => p.selectedChannel);
18
+ const { entity, id, form, loading, fetchEntity } = useDetailView('products-detail-view', ...PRODUCT_FORM_KEYS);
19
+ const { base: { setField, state }, } = form;
20
+ useEffect(() => {
21
+ (async () => {
22
+ const res = await fetchEntity();
23
+ if (!res)
24
+ return;
25
+ setField('translations', res.translations);
26
+ setField('assetIds', res.assets.map((a) => a.id));
27
+ setField('featuredAssetId', res.featuredAsset?.id);
28
+ })();
29
+ }, [selectedChannel?.id, contentLng]);
30
+ const translations = state?.translations?.value || [];
31
+ const currentTranslationValue = useMemo(() => {
32
+ return translations.find((v) => v.languageCode === contentLng);
33
+ }, [translations, contentLng]);
34
+ const setTranslationField = useCallback((field, e) => {
35
+ setField('translations', setInArrayBy(translations, (t) => t.languageCode !== contentLng, {
36
+ ...currentTranslationValue,
37
+ [field]: e,
38
+ languageCode: contentLng,
39
+ }));
40
+ }, [contentLng, translations]);
41
+ const handleAddAsset = useCallback((newId) => {
42
+ if (!newId)
43
+ return;
44
+ const currentIds = state.assetIds?.value || [];
45
+ setField('assetIds', [...currentIds, newId]);
46
+ }, [state.assetIds?.value, setField]);
47
+ return (_jsx("div", { children: _jsxs("div", { className: "flex w-full flex-col gap-4", children: [_jsx(BasicFieldsCard, { currentTranslationValue: currentTranslationValue, onChange: setTranslationField, errors: state.translations?.errors }), _jsx(DetailViewMarker, { position: 'products-detail-view' }), _jsx(EntityCustomFields, { id: id, entityName: "product", hideButton: true, onChange: (customFields, translations) => {
48
+ setField('customFields', customFields);
49
+ if (translations)
50
+ setField('translations', translations);
51
+ }, initialValues: entity && 'customFields' in entity
52
+ ? { customFields: entity.customFields, translations: entity.translations }
53
+ : { customFields: {} } }), _jsx(AssetsCard, { onAddAsset: handleAddAsset, featuredAssetId: state.featuredAssetId?.value, assetsIds: state.assetIds?.value, onFeaturedAssetChange: (id) => setField('featuredAssetId', id), onAssetsChange: (ids) => setField('assetIds', ids) })] }) }));
54
+ };
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useTranslation, Label, Switch, CustomCard, CardIcons } from '@deenruv/react-ui-devkit';
3
+ export const SettingsCard = ({ onEnabledChange, enabledValue }) => {
4
+ const { t } = useTranslation('products');
5
+ return (_jsx(CustomCard, { title: t('details.settings'), color: "gray", icon: _jsx(CardIcons.options, {}), children: _jsxs("div", { className: "flex items-center space-x-2", children: [_jsx(Switch, { id: "product-enabled", checked: enabledValue, onCheckedChange: onEnabledChange }), _jsx(Label, { htmlFor: "product-enabled", children: t('enabled') })] }) }));
6
+ };
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Switch, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuTrigger, DropdownMenuItem, Separator, apiClient, CustomCard, CardIcons, useTranslation, } from '@deenruv/react-ui-devkit';
3
+ import { GlobalFlag } from '@deenruv/admin-types';
4
+ import { MapPin } from 'lucide-react';
5
+ import { useCallback, useEffect, useMemo, useState } from 'react';
6
+ export const StockCard = ({ outOfStockThresholdValue, stockOnHandValue, useGlobalOutOfStockThresholdValue, trackInventoryValue, stockAllocated, stockLevelsValue, allStockLocations, onThresholdChange, onUseGlobalChange, onTrackInventoryChange, onStockOnHandChange, onStockLocationsChange, }) => {
7
+ const { t } = useTranslation('products');
8
+ const [stockLocations, setStockLocations] = useState([]);
9
+ const notUsedStockLocations = useMemo(() => stockLocations.filter((sL) => stockLevelsValue?.findIndex((v) => v.stockLocationId === sL.id) === -1), [stockLocations, stockLevelsValue]);
10
+ const fetchStockLocations = useCallback(async () => {
11
+ const response = await apiClient('query')({
12
+ stockLocations: [
13
+ {},
14
+ {
15
+ items: {
16
+ id: true,
17
+ name: true,
18
+ },
19
+ },
20
+ ],
21
+ });
22
+ setStockLocations(response.stockLocations.items);
23
+ }, []);
24
+ useEffect(() => {
25
+ fetchStockLocations();
26
+ }, [fetchStockLocations]);
27
+ const handleChangeStockLocation = useCallback((location) => {
28
+ const newLevels = stockLevelsValue ? [...stockLevelsValue] : [];
29
+ const elementIdx = newLevels.findIndex((l) => l.stockLocationId === location.stockLocationId);
30
+ if (elementIdx !== -1) {
31
+ newLevels[elementIdx].stockOnHand = location.stockOnHand;
32
+ }
33
+ else {
34
+ newLevels.push(location);
35
+ }
36
+ onStockLocationsChange(newLevels);
37
+ }, [onStockLocationsChange, stockLevelsValue]);
38
+ const getLocationAllocatedValue = useCallback((id) => allStockLocations?.find((e) => e.stockLocationId === id)?.stockAllocated, [allStockLocations]);
39
+ return (_jsx(CustomCard, { title: t('details.stockLevels'), color: "blue", icon: _jsx(CardIcons.stock, {}), children: _jsxs("div", { className: "flex flex-col gap-y-4", children: [_jsxs("div", { className: "flex items-start justify-start gap-x-4", children: [_jsx("div", { className: "w-1/2", children: _jsx(Input, { type: "number", placeholder: t('details.outThreshold'), label: t('details.outThreshold'), value: outOfStockThresholdValue ?? undefined, onChange: onThresholdChange }) }), _jsxs("div", { className: "flex flex-col gap-4", children: [_jsx(Label, { children: t('details.useGlobal') }), _jsx(Switch, { checked: useGlobalOutOfStockThresholdValue ?? undefined, onCheckedChange: onUseGlobalChange })] })] }), _jsxs("div", { className: "flex flex-col items-start justify-start gap-2", children: [_jsx(Label, { children: t('details.trackInventory') }), _jsxs(Select, { value: trackInventoryValue ?? undefined, onValueChange: (value) => onTrackInventoryChange(value), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: GlobalFlag.INHERIT, children: t('details.inherit') }), _jsx(SelectItem, { value: GlobalFlag.TRUE, children: t('details.track') }), _jsx(SelectItem, { value: GlobalFlag.FALSE, children: t('details.notTrack') })] })] })] }), _jsx(Separator, { orientation: "horizontal" }), _jsxs("div", { className: "flex items-start justify-start gap-4", children: [_jsx("div", { className: "w-1/2", children: _jsx(Input, { type: "number", label: t('details.defaultStock1'), value: stockOnHandValue ?? undefined, disabled: true }) }), _jsxs("div", { className: "flex w-1/2 flex-col gap-3", children: [_jsx(Label, { children: t('details.defaultStock2') }), stockAllocated] })] }), stockLevelsValue?.map((sL) => (_jsxs("div", { className: "flex items-start justify-start gap-4", children: [_jsx("div", { className: "w-1/2", children: _jsx(Input, { type: "number", label: `Stock location: ${stockLocations.find((l) => l.id === sL.stockLocationId)?.name}`, value: sL.stockOnHand, onChange: (e) => handleChangeStockLocation({ stockLocationId: sL.stockLocationId, stockOnHand: +e.target.value }) }) }), _jsxs("div", { className: "flex w-1/2 flex-col gap-3", children: [_jsx(Label, { children: "Allocated" }), getLocationAllocatedValue(sL.stockLocationId)] })] }, 'inputs' + sL.stockLocationId))), _jsx("div", { className: "flex", children: _jsxs(DropdownMenu, { modal: false, children: [_jsx(DropdownMenuTrigger, { asChild: true, disabled: !notUsedStockLocations.length, children: _jsx(Button, { size: 'sm', className: "mt-4", children: t('details.addStockLocation') }) }), _jsx(DropdownMenuContent, { className: "w-56", side: "bottom", align: "end", children: _jsx(DropdownMenuGroup, { children: notUsedStockLocations.map((sL) => (_jsxs(DropdownMenuItem, { onClick: () => handleChangeStockLocation({ stockLocationId: sL.id, stockOnHand: 0 }), children: [_jsx(MapPin, { className: "mr-2" }), sL.name] }, 'dropdown' + sL.id))) }) })] }) })] }) }));
40
+ };
@@ -0,0 +1,149 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, CardIcons, ConfirmationDialog, CustomCard, Input, apiClient, setInArrayBy, useGFFLP, EntityCustomFields, useTranslation, EntityChannelManager, } from '@deenruv/react-ui-devkit';
3
+ import { CurrencyCode } from '@deenruv/admin-types';
4
+ import { useCallback, useEffect } from 'react';
5
+ import { toast } from 'sonner';
6
+ import { AssetsCard } from "./AssetsCard";
7
+ import { PriceCard } from "./PriceCard";
8
+ import { StockCard } from "./StockCard";
9
+ import { OptionsCard } from "./OptionsCard";
10
+ import { FacetValuesCard } from "./FacetValuesCard";
11
+ export const Variant = ({ variant, currentTranslationLng, onActionCompleted, productId }) => {
12
+ const { t } = useTranslation('products');
13
+ const { state, setField } = useGFFLP('UpdateProductVariantInput', 'translations', 'price', 'prices', 'sku', 'assetIds', 'featuredAssetId', 'taxCategoryId', 'stockLevels', 'stockOnHand', 'outOfStockThreshold', 'useGlobalOutOfStockThreshold', 'trackInventory', 'facetValueIds', 'optionIds', 'customFields')({});
14
+ const translations = state?.translations?.value || [];
15
+ const currentTranslationValue = translations.find((v) => v.languageCode === currentTranslationLng);
16
+ useEffect(() => {
17
+ if (!variant)
18
+ return;
19
+ setField('sku', variant.sku);
20
+ setField('price', variant.price);
21
+ setField('prices', variant.prices);
22
+ setField('translations', variant.translations);
23
+ setField('assetIds', variant.assets.map((a) => a.id));
24
+ setField('featuredAssetId', variant.featuredAsset?.id);
25
+ setField('taxCategoryId', variant.taxCategory.id);
26
+ setField('stockLevels', variant.stockLevels.map((sL) => ({ stockLocationId: sL.stockLocationId, stockOnHand: sL.stockOnHand })));
27
+ setField('stockOnHand', variant.stockOnHand);
28
+ setField('outOfStockThreshold', variant.outOfStockThreshold);
29
+ setField('useGlobalOutOfStockThreshold', variant.useGlobalOutOfStockThreshold);
30
+ setField('trackInventory', variant.trackInventory);
31
+ setField('facetValueIds', variant.facetValues.map((f) => f.id));
32
+ }, [variant]);
33
+ const createVariant = useCallback(() => {
34
+ if (productId && state.sku?.validatedValue && state.translations?.validatedValue)
35
+ return apiClient('mutation')({
36
+ createProductVariants: [
37
+ {
38
+ input: [
39
+ {
40
+ productId,
41
+ translations: state.translations?.validatedValue,
42
+ // price: +state.price?.validatedValue,
43
+ prices: state.prices?.validatedValue,
44
+ sku: state.sku?.validatedValue,
45
+ assetIds: state.assetIds?.validatedValue,
46
+ featuredAssetId: state.featuredAssetId?.validatedValue,
47
+ outOfStockThreshold: state.outOfStockThreshold?.validatedValue,
48
+ stockOnHand: state.stockOnHand?.validatedValue,
49
+ trackInventory: state.trackInventory?.validatedValue,
50
+ taxCategoryId: state.taxCategoryId?.validatedValue,
51
+ useGlobalOutOfStockThreshold: state.useGlobalOutOfStockThreshold?.validatedValue,
52
+ stockLevels: state.stockLevels?.validatedValue,
53
+ facetValueIds: state.facetValueIds?.validatedValue,
54
+ optionIds: state.optionIds?.validatedValue,
55
+ ...(state.customFields?.validatedValue ? { customFields: state.customFields?.validatedValue } : {}),
56
+ },
57
+ ],
58
+ },
59
+ {
60
+ id: true,
61
+ },
62
+ ],
63
+ })
64
+ .then(() => {
65
+ toast(t('toasts.createProductVariantSuccessToast'));
66
+ onActionCompleted();
67
+ })
68
+ .catch(() => {
69
+ toast(t('toasts.createProductVariantErrorToast'));
70
+ });
71
+ }, [state, productId, onActionCompleted, t]);
72
+ const updateVariant = useCallback(() => {
73
+ if (!variant)
74
+ return;
75
+ apiClient('mutation')({
76
+ updateProductVariants: [
77
+ {
78
+ input: [
79
+ {
80
+ id: variant.id,
81
+ translations: state.translations?.validatedValue,
82
+ // price: +state.price?.validatedValue,
83
+ prices: state.prices?.validatedValue,
84
+ sku: state.sku?.validatedValue,
85
+ assetIds: state.assetIds?.validatedValue,
86
+ featuredAssetId: state.featuredAssetId?.validatedValue,
87
+ outOfStockThreshold: state.outOfStockThreshold?.validatedValue,
88
+ stockOnHand: state.stockOnHand?.validatedValue,
89
+ trackInventory: state.trackInventory?.validatedValue,
90
+ taxCategoryId: state.taxCategoryId?.validatedValue,
91
+ useGlobalOutOfStockThreshold: state.useGlobalOutOfStockThreshold?.validatedValue,
92
+ stockLevels: state.stockLevels?.validatedValue,
93
+ facetValueIds: state.facetValueIds?.validatedValue,
94
+ ...(state.customFields?.validatedValue ? { customFields: state.customFields?.validatedValue } : {}),
95
+ },
96
+ ],
97
+ },
98
+ { id: true },
99
+ ],
100
+ })
101
+ .then(() => {
102
+ onActionCompleted();
103
+ toast(t('toasts.updateProductSuccessToast'), {
104
+ description: new Date().toLocaleString(),
105
+ });
106
+ })
107
+ .catch(() => toast.error(t('toasts.updateProductErrorToast')));
108
+ }, [state, variant]);
109
+ const deleteVariant = useCallback(() => {
110
+ if (!variant)
111
+ return;
112
+ apiClient('mutation')({
113
+ deleteProductVariant: [{ id: variant.id }, { message: true }],
114
+ })
115
+ .then(() => {
116
+ onActionCompleted();
117
+ toast(t('toasts.deleteProductVariantSuccessToast'), {
118
+ description: new Date().toLocaleString(),
119
+ });
120
+ })
121
+ .catch(() => toast.error(t('toasts.deleteProductVariantErrorToast')));
122
+ }, [variant]);
123
+ const setTranslationField = useCallback((field, e) => {
124
+ setField('translations', setInArrayBy(translations, (t) => t.languageCode !== currentTranslationLng, {
125
+ [field]: e.target.value,
126
+ languageCode: currentTranslationLng,
127
+ }));
128
+ }, [currentTranslationLng, translations]);
129
+ const handleAddAsset = (id) => {
130
+ if (!id)
131
+ return;
132
+ const newIds = state.assetIds?.value || [];
133
+ if (newIds?.includes(id))
134
+ return;
135
+ newIds?.push(id);
136
+ setField('assetIds', newIds);
137
+ };
138
+ return (_jsxs("div", { className: "mt-4 flex flex-col gap-4", children: [_jsx("div", { className: "flex gap-3 self-end", children: variant ? (_jsxs(_Fragment, { children: [_jsx(ConfirmationDialog, { onConfirm: deleteVariant, children: _jsx(Button, { variant: 'destructive', children: t('forms.removeVariant') }) }), _jsx(Button, { onClick: updateVariant, children: t('forms.updateVariant') })] })) : (_jsx(Button, { onClick: createVariant, children: t('addVariantDialog.add') })) }), _jsxs("div", { className: "flex gap-4", children: [_jsxs("div", { className: "flex w-2/3 flex-col gap-4", children: [!!variant && (_jsx(StockCard, { priceValue: state.price?.value, taxRateValue: state.taxCategoryId?.value, outOfStockThresholdValue: state.outOfStockThreshold?.value, stockLevelsValue: state.stockLevels?.value, stockOnHandValue: state.stockOnHand?.value, useGlobalOutOfStockThresholdValue: state.useGlobalOutOfStockThreshold?.value, onThresholdChange: (e) => setField('outOfStockThreshold', +e.target.value), onUseGlobalChange: (e) => setField('useGlobalOutOfStockThreshold', e), onTrackInventoryChange: (e) => setField('trackInventory', e), onStockOnHandChange: (e) => setField('stockOnHand', +e.target.value), onStockLocationsChange: (e) => setField('stockLevels', e), allStockLocations: variant?.stockLevels, stockAllocated: variant?.stockAllocated, trackInventoryValue: state.trackInventory?.value })), _jsx(PriceCard, { currencyCode: variant?.currencyCode || CurrencyCode.PLN, priceValue: state.prices?.value, onPriceChange: (e) => setField('prices', e), taxRateValue: state.taxCategoryId?.value ?? undefined, onTaxRateChange: (id) => setField('taxCategoryId', id) }), _jsx(AssetsCard, { onAddAsset: handleAddAsset, featuredAssetId: state.featuredAssetId?.value, assetsIds: state.assetIds?.value, onFeaturedAssetChange: (id) => setField('featuredAssetId', id), onAssetsChange: (ids) => setField('assetIds', ids) }), _jsx(EntityCustomFields, { entityName: "productVariant", id: variant?.id, hideButton: true, onChange: (customFields, translations) => {
139
+ setField('customFields', customFields);
140
+ if (translations)
141
+ setField('translations', translations);
142
+ }, initialValues: variant && 'customFields' in variant
143
+ ? { customFields: variant.customFields, translations: variant.translations }
144
+ : { customFields: {} } })] }), _jsxs("div", { className: "flex w-1/3 flex-col gap-4", children: [_jsx(CustomCard, { title: t('name'), icon: _jsx(CardIcons.basic, {}), color: "purple", children: _jsxs("div", { className: "flex flex-col gap-y-4", children: [_jsx(Input, { label: t('sku'), placeholder: t('sku'), value: state?.sku?.value ?? undefined, onChange: (e) => setField('sku', e.target.value) }), _jsx(Input, { label: t('name'), placeholder: t('name'), value: currentTranslationValue?.name ?? undefined, onChange: (e) => setTranslationField('name', e) })] }) }), _jsx(EntityChannelManager, { entity: "productVariant", entityId: variant?.id, entityChannels: variant?.channels ?? [], onRemoveSuccess: onActionCompleted, entityName: variant?.name, entityVariantList: {
145
+ items: [
146
+ { price: variant?.price, priceWithTax: variant?.priceWithTax, currencyCode: variant?.currencyCode },
147
+ ],
148
+ } }), _jsx(OptionsCard, { optionGroups: variant?.options || [], productId: productId, optionIds: state.optionIds?.value ?? undefined, onChange: (e) => setField('optionIds', e), createMode: !variant }), !!variant && (_jsx(FacetValuesCard, { facetValuesIds: state.facetValueIds?.value ?? undefined, onChange: (e) => setField('facetValueIds', e) }))] })] })] }));
149
+ };
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { apiClient, EmptyState, Tabs, TabsContent, TabsList, TabsTrigger, useDetailView, useSettings, useTranslation, } from '@deenruv/react-ui-devkit';
3
+ import { ProductVariantSelector } from "../../../graphql/products";
4
+ import { Variant } from "./Variant";
5
+ import { useCallback, useEffect, useState } from 'react';
6
+ import { PlusCircle } from 'lucide-react';
7
+ import { useSearchParams } from 'react-router-dom';
8
+ const NEW_VARIANT_TAB_VALUE = 'new';
9
+ export const VariantsTab = () => {
10
+ const contentLanguage = useSettings((p) => p.translationsLanguage);
11
+ const { id, getMarker, setLoading } = useDetailView('products-detail-view', 'CreateProductInput');
12
+ const [activeTab, setActiveTab] = useState(undefined);
13
+ const { t } = useTranslation('products');
14
+ const [searchParams, setSearchParams] = useSearchParams();
15
+ const handleTabChange = (tab) => {
16
+ setActiveTab(tab);
17
+ const updatedParams = new URLSearchParams(searchParams);
18
+ updatedParams.set('variantId', tab);
19
+ setSearchParams(updatedParams);
20
+ };
21
+ useEffect(() => {
22
+ const variantId = searchParams.get('variantId');
23
+ if (variantId)
24
+ setActiveTab(variantId);
25
+ }, []);
26
+ const [variants, setVariants] = useState();
27
+ const fetchData = useCallback(async () => {
28
+ if (id) {
29
+ setLoading(true);
30
+ const response = await apiClient('query')({
31
+ productVariants: [{ productId: id }, { items: ProductVariantSelector }],
32
+ });
33
+ setLoading(false);
34
+ if (response.productVariants) {
35
+ setVariants(response.productVariants.items);
36
+ if (activeTab === undefined) {
37
+ setActiveTab(response.productVariants.items[0]?.id);
38
+ }
39
+ }
40
+ }
41
+ }, [id, activeTab]);
42
+ useEffect(() => {
43
+ fetchData();
44
+ }, [fetchData]);
45
+ return (_jsxs("div", { className: "flex flex-col", children: [getMarker(), _jsx("div", { className: "flex flex-col items-end gap-4", children: _jsxs(Tabs, { defaultValue: variants?.[0]?.id, className: "w-full", value: activeTab, onValueChange: handleTabChange, children: [_jsxs(TabsList, { className: "h-auto flex-wrap justify-start", children: [_jsxs(TabsTrigger, { value: NEW_VARIANT_TAB_VALUE, className: "text-blue-600", children: [_jsx(PlusCircle, { size: 16, className: "mr-2 translate-y-px" }), t('addVariantDialog.new')] }, 'new-variant'), variants?.map((v) => (_jsx(TabsTrigger, { value: v.id, children: v.name }, v.id + '-trigger')))] }), _jsx(TabsContent, { value: NEW_VARIANT_TAB_VALUE, children: id && _jsx(Variant, { currentTranslationLng: contentLanguage, onActionCompleted: fetchData, productId: id }) }, 'new-variant-content'), id && variants?.length ? (variants?.map((v) => (_jsx(TabsContent, { value: v.id, children: _jsx(Variant, { currentTranslationLng: contentLanguage, variant: v, onActionCompleted: fetchData, productId: id }) }, v.id + '-content')))) : (_jsx("div", { className: "flex w-full items-center justify-center", children: activeTab !== NEW_VARIANT_TAB_VALUE && (_jsx(EmptyState, { columnsLength: 1, title: t('variantsTab.emptyState.title'), description: t('variantsTab.emptyState.description') })) }))] }) })] }));
46
+ };
@@ -0,0 +1,2 @@
1
+ export * from './List.js';
2
+ export * from './Detail.js';