@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,252 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { CanLeaveRouteDialog } from "../components";
3
+ import { serverConfigSelector, configurableOperationDefinitionSelector, countrySelector, apiClient, useOrder, useNotifications, DEFAULT_CHANNEL_CODE, useTranslation, capitalizeFirstLetter, } from '@deenruv/react-ui-devkit';
4
+ import { useCallback, useEffect } from 'react';
5
+ import { Outlet, useLocation, useSearchParams } from 'react-router-dom';
6
+ import { toast } from 'sonner';
7
+ import { activeAdministratorSelector, paymentMethodsSelector, useServer, useSettings, } from '@deenruv/react-ui-devkit';
8
+ import { Menu } from "../components";
9
+ import { GlobalSearch } from "../components/GlobalSearch.js";
10
+ import { DeenruvDeveloperIndicator } from "../DeenruvDeveloperIndicator.js";
11
+ import { Permission } from '@deenruv/admin-types';
12
+ const TAKE = 100;
13
+ const getAllPaginatedCountries = async () => {
14
+ let countries = [];
15
+ let totalItems = 0;
16
+ let skip = 0;
17
+ do {
18
+ const { countries: { items, totalItems: total }, } = await apiClient('query')({
19
+ countries: [{ options: { skip, take: TAKE } }, { items: countrySelector, totalItems: true }],
20
+ });
21
+ countries = [...countries, ...items];
22
+ totalItems = total;
23
+ skip += TAKE;
24
+ } while (countries.length < totalItems);
25
+ return { countries };
26
+ };
27
+ const getAllPaymentMethods = async () => {
28
+ let paymentMethods = [];
29
+ let totalItems = 0;
30
+ let skip = 0;
31
+ do {
32
+ const { paymentMethods: { items, totalItems: total }, } = await apiClient('query')({
33
+ paymentMethods: [
34
+ { options: { skip, take: TAKE, filter: { enabled: { eq: true } } } },
35
+ { items: paymentMethodsSelector, totalItems: true },
36
+ ],
37
+ });
38
+ paymentMethods = [...paymentMethods, ...items];
39
+ totalItems = total;
40
+ skip += TAKE;
41
+ } while (paymentMethods.length < totalItems);
42
+ return { paymentMethods };
43
+ };
44
+ export const Root = ({ allPaths }) => {
45
+ const isLocalhost = window.location.hostname === 'localhost';
46
+ const { t } = useTranslation('common');
47
+ const setActiveAdministrator = useServer((p) => p.setActiveAdministrator);
48
+ const setUserPermissions = useServer((p) => p.setUserPermissions);
49
+ const setServerConfig = useServer((p) => p.setServerConfig);
50
+ const setCountries = useServer((p) => p.setCountries);
51
+ const setFulfillmentHandlers = useServer((p) => p.setFulfillmentHandlers);
52
+ const setPaymentMethodsType = useServer((p) => p.setPaymentMethodsType);
53
+ const fetchPendingJobs = useServer((p) => p.fetchPendingJobs);
54
+ const loaded = useServer((p) => p.loaded);
55
+ const setLoaded = useServer((p) => p.setLoaded);
56
+ const setAvailableLanguages = useSettings((p) => p.setAvailableLanguages);
57
+ const setLanguage = useSettings((p) => p.setLanguage);
58
+ const setTranslationLanguage = useSettings((p) => p.setTranslationsLanguage);
59
+ const selectedChannel = useSettings((p) => p.selectedChannel);
60
+ const setSelectedChannel = useSettings((p) => p.setSelectedChannel);
61
+ const setChannels = useServer((p) => p.setChannels);
62
+ const fetchGraphQLSchema = useServer((p) => p.fetchGraphQLSchema);
63
+ const fetchStatus = useServer((p) => p.fetchStatus);
64
+ const { initializeOrderCustomFields } = useOrder();
65
+ const setData = useNotifications(({ setData }) => setData);
66
+ const notifications = useNotifications(({ notifications }) => notifications);
67
+ const channel = useSettings((p) => p.selectedChannel);
68
+ const location = useLocation();
69
+ const [searchParams] = useSearchParams();
70
+ const managePageTitle = useCallback(() => {
71
+ const pathname = location.pathname;
72
+ const isDeenruvPath = allPaths.some((path) => pathname.startsWith(path));
73
+ if (isDeenruvPath) {
74
+ let [, , route, id] = pathname.split('/');
75
+ if (!route) {
76
+ document.title = [
77
+ window.__DEENRUV_SETTINGS__.branding.name,
78
+ capitalizeFirstLetter(t(`menu.dashboard`)),
79
+ 'Admin UI',
80
+ ].join(' - ');
81
+ }
82
+ else {
83
+ if (route?.split('-').length > 1) {
84
+ const [first, second] = route.split('-');
85
+ route = `${first}${second.charAt(0).toUpperCase()}${second.slice(1)}`;
86
+ }
87
+ if (isNaN(parseInt(id, 10))) {
88
+ document.title = [
89
+ window.__DEENRUV_SETTINGS__.branding.name,
90
+ capitalizeFirstLetter(t(`menu.${route}`)),
91
+ 'Admin UI',
92
+ ].join(' - ');
93
+ }
94
+ else {
95
+ const tab = searchParams.get('tab');
96
+ if (tab) {
97
+ const variantID = searchParams.get('variantId');
98
+ if (variantID)
99
+ id = variantID;
100
+ document.title = [
101
+ window.__DEENRUV_SETTINGS__.branding.name,
102
+ capitalizeFirstLetter(t(`menu.${tab}`)),
103
+ id,
104
+ 'Admin UI',
105
+ ].join(' - ');
106
+ }
107
+ else {
108
+ document.title = [
109
+ window.__DEENRUV_SETTINGS__.branding.name,
110
+ capitalizeFirstLetter(t(`menu.${route}`)),
111
+ id,
112
+ 'Admin UI',
113
+ ].join(' - ');
114
+ }
115
+ }
116
+ }
117
+ }
118
+ }, [location, searchParams]);
119
+ useEffect(() => {
120
+ managePageTitle();
121
+ }, [location, searchParams]);
122
+ useEffect(() => {
123
+ const intervalId = setInterval(() => {
124
+ fetchStatus();
125
+ }, 10000);
126
+ return () => clearInterval(intervalId);
127
+ }, [fetchStatus]);
128
+ useEffect(() => {
129
+ if (!loaded)
130
+ return;
131
+ const intervalIds = notifications.map(({ id, fetch, interval }) => {
132
+ const fetchData = async () => {
133
+ const data = await fetch();
134
+ setData(id, data);
135
+ };
136
+ fetchData();
137
+ const intervalId = setInterval(fetchData, interval);
138
+ return intervalId;
139
+ });
140
+ return () => {
141
+ intervalIds.forEach(clearInterval);
142
+ };
143
+ }, [notifications.map((n) => n.id).join(','), loaded]);
144
+ useEffect(() => {
145
+ const init = async () => {
146
+ const activeAdministratorResponse = await apiClient('query')({
147
+ activeAdministrator: activeAdministratorSelector,
148
+ });
149
+ const roles = activeAdministratorResponse.activeAdministrator?.user.roles || [];
150
+ if (!activeAdministratorResponse.activeAdministrator) {
151
+ toast.error(t('setup.failedAdmin'));
152
+ }
153
+ else {
154
+ setActiveAdministrator(activeAdministratorResponse.activeAdministrator);
155
+ setUserPermissions(Array.from(new Set(roles.flatMap((role) => role.permissions))));
156
+ if ([Permission.ReadChannel].some((p) => roles.some((r) => r.permissions.includes(p)))) {
157
+ const { channels: { items: allChannels = [] }, } = await apiClient('query')({
158
+ channels: [
159
+ {},
160
+ {
161
+ items: {
162
+ id: true,
163
+ code: true,
164
+ token: true,
165
+ currencyCode: true,
166
+ defaultLanguageCode: true,
167
+ availableLanguageCodes: true,
168
+ },
169
+ },
170
+ ],
171
+ });
172
+ setChannels(allChannels);
173
+ if (!channel) {
174
+ if (selectedChannel) {
175
+ const foundChannel = allChannels.find((ch) => ch.code === selectedChannel.code);
176
+ setSelectedChannel(foundChannel || allChannels[0]);
177
+ }
178
+ const existingChannel = allChannels.find((ch) => ch.code === window?.__DEENRUV_SETTINGS__?.ui?.defaultChannelCode);
179
+ if (existingChannel) {
180
+ setSelectedChannel(existingChannel);
181
+ }
182
+ const defaultChannel = allChannels.find((ch) => ch.code === DEFAULT_CHANNEL_CODE) || allChannels[0];
183
+ setSelectedChannel(defaultChannel);
184
+ }
185
+ }
186
+ else {
187
+ const possibleChannels = roles.flatMap((role) => role.channels);
188
+ if (possibleChannels.length > 0) {
189
+ setSelectedChannel(possibleChannels[0]);
190
+ }
191
+ }
192
+ }
193
+ // WE NEED TO CHECK IF LOCALSTORAGE HAS LANGUAGE SET
194
+ if (window?.__DEENRUV_SETTINGS__?.ui?.defaultLanguageCode) {
195
+ // window?.__DEENRUV_SETTINGS__.i18n.changeLanguage(window?.__DEENRUV_SETTINGS__?.ui?.defaultLanguageCode);
196
+ // setLanguage(window?.__DEENRUV_SETTINGS__?.ui?.defaultLanguageCode);
197
+ }
198
+ // WE NEED TO CHECK IF LOCALSTORAGE HAS LANGUAGE SET
199
+ if (window?.__DEENRUV_SETTINGS__?.ui?.defaultTranslationLanguageCode) {
200
+ // setTranslationLanguage(window?.__DEENRUV_SETTINGS__?.ui?.defaultTranslationLanguageCode);
201
+ }
202
+ const { globalSettings } = await apiClient('query')({
203
+ globalSettings: { serverConfig: serverConfigSelector, availableLanguages: true },
204
+ });
205
+ fetchPendingJobs();
206
+ initializeOrderCustomFields(globalSettings.serverConfig);
207
+ setLoaded(true);
208
+ if ([Permission.ReadCountry, Permission.ReadPaymentMethod, Permission.ReadOrder].some((p) => roles.some((r) => r.permissions.includes(p)))) {
209
+ const [countriesResponse, paymentsResponse, fulfillmentsResponse] = await Promise.allSettled([
210
+ getAllPaginatedCountries(),
211
+ getAllPaymentMethods(),
212
+ apiClient('query')({ fulfillmentHandlers: configurableOperationDefinitionSelector }),
213
+ ]);
214
+ if (countriesResponse.status === 'rejected') {
215
+ toast.error(t('setup.failedServer'));
216
+ }
217
+ else {
218
+ setServerConfig(globalSettings.serverConfig);
219
+ setAvailableLanguages(globalSettings.availableLanguages);
220
+ // const socket = serverConfigResponse.value.globalSettings.serverConfig.plugins?.find(
221
+ // (plugin) => plugin.name === 'AexolAdminsPlugin',
222
+ // );
223
+ // if (socket && socket.active) setNeedSocket(true);
224
+ }
225
+ if (countriesResponse.status === 'rejected') {
226
+ toast.error(t('setup.failedCountries'));
227
+ }
228
+ else {
229
+ setCountries(countriesResponse.value.countries);
230
+ }
231
+ if (paymentsResponse.status === 'rejected') {
232
+ toast.error(t('setup.failedPayments'));
233
+ }
234
+ else {
235
+ setPaymentMethodsType(paymentsResponse.value.paymentMethods);
236
+ }
237
+ if (fulfillmentsResponse.status === 'rejected') {
238
+ toast.error(t('setup.failedFulfillments'));
239
+ }
240
+ else {
241
+ setFulfillmentHandlers(fulfillmentsResponse.value?.fulfillmentHandlers);
242
+ }
243
+ }
244
+ };
245
+ fetchGraphQLSchema().then(async (schema) => {
246
+ window.__DEENRUV_SCHEMA__ = schema;
247
+ await new Promise((resolve) => setTimeout(resolve, 1000));
248
+ await init();
249
+ });
250
+ }, []);
251
+ return (_jsx(_Fragment, { children: _jsx("div", { className: "bg-background text-foreground flex max-h-screen w-full max-w-full overflow-hidden", children: loaded ? (_jsxs(_Fragment, { children: [_jsx(Menu, { children: _jsx(Outlet, {}) }), _jsx(GlobalSearch, {}), _jsx(CanLeaveRouteDialog, {}), isLocalhost ? _jsx(DeenruvDeveloperIndicator, {}) : null] })) : (_jsx("div", { className: "flex h-screen w-full items-center justify-center", children: _jsx("span", { className: "animate-spin", children: "\u23F3" }) })) }) }));
252
+ };
@@ -0,0 +1,72 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback, useMemo } from 'react';
3
+ import { useParams } from 'react-router-dom';
4
+ import { DetailView, createDeenruvForm, getMutation, useMutation, useValidators, useTranslation, } from '@deenruv/react-ui-devkit';
5
+ import { AdminDetailView } from "./_components/AdminDetailView.js";
6
+ import { Permission } from '@deenruv/admin-types';
7
+ const CreateAdminMutation = getMutation('createAdministrator');
8
+ const EditAdminMutation = getMutation('updateAdministrator');
9
+ const DeleteAdminMutation = getMutation('deleteAdministrator');
10
+ export const AdminsDetailPage = () => {
11
+ const { id } = useParams();
12
+ const [update] = useMutation(EditAdminMutation);
13
+ const [create] = useMutation(CreateAdminMutation);
14
+ const [remove] = useMutation(DeleteAdminMutation);
15
+ const editMode = useMemo(() => !!id, [id]);
16
+ const { t } = useTranslation('admins');
17
+ const { emailValidator, stringValidator, arrayValidator } = useValidators();
18
+ const onSubmitHandler = useCallback((data) => {
19
+ if (!data.emailAddress?.validatedValue) {
20
+ throw new Error('Name is required.');
21
+ }
22
+ const inputData = {
23
+ emailAddress: data.emailAddress?.validatedValue,
24
+ firstName: data.firstName?.validatedValue,
25
+ lastName: data.lastName?.validatedValue,
26
+ password: data.password?.validatedValue ? data.password?.validatedValue : undefined,
27
+ roleIds: data.roleIds?.validatedValue,
28
+ ...(data.customFields?.validatedValue ? { customFields: data.customFields?.validatedValue } : {}),
29
+ };
30
+ if (id) {
31
+ return update({
32
+ input: {
33
+ id,
34
+ ...inputData,
35
+ },
36
+ });
37
+ }
38
+ else {
39
+ return create({
40
+ input: inputData,
41
+ });
42
+ }
43
+ }, [id, update, create]);
44
+ const onDeleteHandler = useCallback(() => {
45
+ if (!id) {
46
+ throw new Error('Could not find the id.');
47
+ }
48
+ return remove({ input: { id } });
49
+ }, [remove, id]);
50
+ return (_jsx("div", { className: "relative flex flex-col gap-y-4", children: _jsx(DetailView, { id: id, locationId: "admins-detail-view", main: {
51
+ name: 'admin',
52
+ label: 'Admin',
53
+ component: _jsx(AdminDetailView, {}),
54
+ form: createDeenruvForm({
55
+ key: 'CreateAdministratorInput',
56
+ keys: ['firstName', 'lastName', 'emailAddress', 'password', 'roleIds', 'customFields'],
57
+ config: {
58
+ emailAddress: emailValidator,
59
+ firstName: stringValidator(t('validation.firstNameRequired')),
60
+ lastName: stringValidator(t('validation.lastNameRequired')),
61
+ password: !editMode ? stringValidator(t('validation.passwordRequired')) : undefined,
62
+ roleIds: arrayValidator(t('validation.rolesRequired')),
63
+ },
64
+ onSubmitted: onSubmitHandler,
65
+ onDeleted: onDeleteHandler,
66
+ }),
67
+ }, permissions: {
68
+ create: Permission.CreateAdministrator,
69
+ delete: Permission.DeleteAdministrator,
70
+ edit: Permission.UpdateAdministrator,
71
+ } }) }));
72
+ };
@@ -0,0 +1,56 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Routes, apiClient, DetailList, deepMerge, ListBadge, ListLocations, useTranslation, TableLabel, } from '@deenruv/react-ui-devkit';
3
+ import { Permission, SortOrder } from '@deenruv/admin-types';
4
+ const tableId = 'admins-list-view';
5
+ const { selector } = ListLocations[tableId];
6
+ const fetch = async ({ page, perPage, filter, filterOperator, sort }, additionalSelector) => {
7
+ const response = await apiClient('query')({
8
+ administrators: [
9
+ {
10
+ options: {
11
+ take: perPage,
12
+ skip: (page - 1) * perPage,
13
+ filterOperator: filterOperator,
14
+ sort: sort ? { [sort.key]: sort.sortDir } : { createdAt: SortOrder.DESC },
15
+ ...(filter && { filter }),
16
+ },
17
+ },
18
+ { items: deepMerge(selector, additionalSelector ?? {}), totalItems: true },
19
+ ],
20
+ });
21
+ return response.administrators;
22
+ };
23
+ const onRemove = async (items) => {
24
+ try {
25
+ const ids = items.map((item) => item.id);
26
+ const { deleteAdministrators } = await apiClient('mutation')({
27
+ deleteAdministrators: [
28
+ { ids },
29
+ {
30
+ message: true,
31
+ result: true,
32
+ },
33
+ ],
34
+ });
35
+ return !!deleteAdministrators.length;
36
+ }
37
+ catch (error) {
38
+ return error;
39
+ }
40
+ };
41
+ export const AdminsListPage = () => {
42
+ const { t } = useTranslation('admins');
43
+ return (_jsx(DetailList, { filterFields: [
44
+ { key: 'firstName', operator: 'StringOperators' },
45
+ { key: 'emailAddress', operator: 'StringOperators' },
46
+ { key: 'lastName', operator: 'StringOperators' },
47
+ ], detailLinkColumn: "id", searchFields: ['firstName', 'lastName', 'emailAddress'], hideColumns: ['customFields', 'translations', 'user'], additionalColumns: [
48
+ {
49
+ accessorKey: 'role',
50
+ enableSorting: false,
51
+ enableColumnFilter: false,
52
+ header: () => _jsx(TableLabel, { children: t('table.role') }),
53
+ cell: ({ row }) => (_jsx("div", { className: "flex gap-1", children: row.original.user.roles.map((r) => (_jsx(ListBadge, { children: r.description }, r.description))) })),
54
+ },
55
+ ], entityName: 'Administrator', route: Routes['admins'], tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreateAdministrator], deletePermissions: [Permission.DeleteAdministrator] }));
56
+ };
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo } from 'react';
3
+ import { EntityCustomFields, CardIcons, CustomCard, DetailViewMarker, Input, useDetailView, useTranslation, } from '@deenruv/react-ui-devkit';
4
+ import { RolesCard } from "./RolesCard";
5
+ const ADMIN_FORM_KEYS = [
6
+ 'CreateAdministratorInput',
7
+ 'firstName',
8
+ 'lastName',
9
+ 'emailAddress',
10
+ 'password',
11
+ 'roleIds',
12
+ 'customFields',
13
+ ];
14
+ export const AdminDetailView = () => {
15
+ const { form, entity, fetchEntity, id } = useDetailView('admins-detail-view', ...ADMIN_FORM_KEYS);
16
+ const { base: { setField, state }, } = form;
17
+ const editMode = useMemo(() => !!id, [id]);
18
+ const { t } = useTranslation('admins');
19
+ useEffect(() => {
20
+ (async () => {
21
+ const res = await fetchEntity();
22
+ if (!res)
23
+ return;
24
+ setField('firstName', res.firstName);
25
+ setField('lastName', res.lastName);
26
+ setField('emailAddress', res.emailAddress);
27
+ setField('password', '');
28
+ setField('roleIds', res.user.roles.map((r) => r.id));
29
+ })();
30
+ }, []);
31
+ return (_jsx("main", { className: "my-4", children: _jsxs("div", { className: "flex flex-col gap-3", children: [_jsx(CustomCard, { title: t('details.basic.title'), icon: _jsx(CardIcons.basic, {}), children: _jsxs("div", { className: "flex items-start gap-4", children: [_jsx(Input, { wrapperClassName: "basis-full md:basis-1/2 xl:basis-1/4", label: t('details.basic.firstName'), value: state.firstName?.value ?? undefined, onChange: (e) => setField('firstName', e.target.value), errors: state.firstName?.errors, required: true }), _jsx(Input, { wrapperClassName: "basis-full md:basis-1/2 xl:basis-1/4", label: t('details.basic.lastName'), value: state.lastName?.value ?? undefined, onChange: (e) => setField('lastName', e.target.value), errors: state.lastName?.errors, required: true }), _jsx(Input, { wrapperClassName: "basis-full md:basis-1/2 xl:basis-1/4", label: t('details.basic.emailAddress'), value: state.emailAddress?.value ?? undefined, onChange: (e) => setField('emailAddress', e.target.value), errors: state.emailAddress?.errors, required: true }), _jsx(Input, { wrapperClassName: "basis-full md:basis-1/2 xl:basis-1/4", label: t('details.basic.password'), value: state.password?.value ?? undefined, onChange: (e) => setField('password', e.target.value), errors: state.password?.errors, required: !editMode })] }) }), _jsx(DetailViewMarker, { position: 'admins-detail-view' }), _jsx(EntityCustomFields, { entityName: "administrator", id: id, hideButton: true, onChange: (customFields) => {
32
+ setField('customFields', customFields);
33
+ }, initialValues: entity && 'customFields' in entity ? { customFields: entity.customFields } : { customFields: {} } }), _jsx(RolesCard, { adminRoleIds: state.roleIds?.value ?? undefined, onRolesChange: (e) => setField('roleIds', e), errors: state.roleIds?.errors })] }) }));
34
+ };
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo, useState } from 'react';
3
+ import { useTranslation, MultipleSelector, apiClient, ErrorMessage, CardIcons, CustomCard, RoleSelector, } from '@deenruv/react-ui-devkit';
4
+ import { PermissionsTable } from "../../roles/_components/PermissionsTable";
5
+ export const RolesCard = ({ adminRoleIds, onRolesChange, errors }) => {
6
+ const { t } = useTranslation('admins');
7
+ const [allRoles, setAllRoles] = useState([]);
8
+ const currentPermissions = useMemo(() => {
9
+ if (!allRoles.length)
10
+ return;
11
+ return adminRoleIds?.flatMap((id) => allRoles.find((r) => r.id === id)?.permissions ?? []);
12
+ }, [adminRoleIds, allRoles]);
13
+ const fetchAllRoles = useCallback(() => {
14
+ apiClient('query')({
15
+ roles: [{}, { items: RoleSelector }],
16
+ }).then((resp) => {
17
+ setAllRoles(resp.roles.items);
18
+ });
19
+ }, []);
20
+ useEffect(() => {
21
+ fetchAllRoles();
22
+ }, [fetchAllRoles]);
23
+ const rolesToOptions = useCallback((roles) => roles?.map((r) => ({ label: r.description, value: r.id })), []);
24
+ const currentRolesOptions = useMemo(() => {
25
+ if (!allRoles.length)
26
+ return undefined;
27
+ else
28
+ return adminRoleIds?.map((id) => ({ value: id, label: allRoles.find((r) => r.id === id).description }));
29
+ }, [allRoles, adminRoleIds]);
30
+ return (_jsxs(CustomCard, { title: t('details.basic.title'), icon: _jsx(CardIcons.permissions, {}), upperRight: _jsx(ErrorMessage, { errors: errors }), children: [_jsx(MultipleSelector, { options: rolesToOptions(allRoles), value: currentRolesOptions, placeholder: t('details.roles.placeholder'), onChange: (options) => onRolesChange(options.map((o) => o.value)), hideClearAllButton: true }), _jsx("div", { className: "mt-4", children: _jsx(PermissionsTable, { currentPermissions: currentPermissions }) })] }));
31
+ };
@@ -0,0 +1,2 @@
1
+ export * from './Detail.js';
2
+ export * from './List.js';
@@ -0,0 +1,82 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { apiClient, DetailList, deepMerge, ListLocations, createDialogFromComponent, Badge, ImageWithPreview, useTranslation, TableLabel, } from '@deenruv/react-ui-devkit';
3
+ import { Permission, SortOrder } from '@deenruv/admin-types';
4
+ import { UploadAssetDialog } from "./_components/UploadAssetDialog.js";
5
+ import { EditAssetDialog } from "./_components/EditAssetDialog.js";
6
+ const tableId = 'assets-list-view';
7
+ const { selector } = ListLocations[tableId];
8
+ const fetch = async ({ page, perPage, filter, filterOperator, sort }, additionalSelector) => {
9
+ const response = await apiClient('query')({
10
+ assets: [
11
+ {
12
+ options: {
13
+ take: perPage,
14
+ skip: (page - 1) * perPage,
15
+ filterOperator: filterOperator,
16
+ sort: sort ? { [sort.key]: sort.sortDir } : { createdAt: SortOrder.DESC },
17
+ ...(filter && { filter }),
18
+ },
19
+ },
20
+ { items: deepMerge(selector, additionalSelector ?? {}), totalItems: true },
21
+ ],
22
+ });
23
+ return response.assets;
24
+ };
25
+ const onRemove = async (items) => {
26
+ try {
27
+ const ids = items.map((item) => item.id);
28
+ const { deleteAssets } = await apiClient('mutation')({
29
+ deleteAssets: [{ input: { assetIds: ids } }, { message: true, result: true }],
30
+ });
31
+ return !!deleteAssets.result.length;
32
+ }
33
+ catch (error) {
34
+ return error;
35
+ }
36
+ };
37
+ export const AssetsListPage = () => {
38
+ const { t } = useTranslation('table');
39
+ return (_jsx(DetailList, { tableId: tableId, detailLinkColumn: "id", searchFields: ['id'], suggestedOrderColumns: { id: 1, preview: 2, tags: 3 }, hideColumns: [
40
+ 'fileSize',
41
+ 'width',
42
+ 'height',
43
+ 'focalPoint',
44
+ 'mimeType',
45
+ 'source',
46
+ 'type',
47
+ 'customFields',
48
+ 'translations',
49
+ ], entityName: 'Asset', route: {
50
+ create: async (refetch) => {
51
+ const response = await createDialogFromComponent(UploadAssetDialog, {}, { className: 'max-w-[1200px] h-[700px]' });
52
+ if (response.success) {
53
+ refetch();
54
+ }
55
+ },
56
+ edit: async (id, row, refetch) => {
57
+ const response = await createDialogFromComponent(EditAssetDialog, row.original, {
58
+ className: 'max-w-[1200px] h-[700px]',
59
+ });
60
+ if (response.success) {
61
+ refetch();
62
+ }
63
+ },
64
+ }, fetch: fetch, onRemove: onRemove, additionalColumns: [
65
+ {
66
+ accessorKey: 'preview',
67
+ header: () => _jsx(TableLabel, { children: t('columns.image') }),
68
+ cell: ({ row }) => {
69
+ const { name, preview } = row.original;
70
+ return (_jsx("div", { className: "relative", children: _jsx(ImageWithPreview, { src: preview, alt: name, previewClassName: "p-2" }) }));
71
+ },
72
+ },
73
+ {
74
+ accessorKey: 'tags',
75
+ header: () => _jsx(TableLabel, { children: t('columns.tags') }),
76
+ cell: ({ row }) => {
77
+ const { tags } = row.original;
78
+ return (_jsx("div", { className: "flex flex-wrap gap-1", children: tags.length ? (tags.map((tag, index) => (_jsx(Badge, { variant: "secondary", className: "text-xs", children: tag.value }, index)))) : (_jsx(Badge, { variant: "secondary", className: "text-xs", children: "No Tags" })) }));
79
+ },
80
+ },
81
+ ], createPermissions: [Permission.CreateChannel], deletePermissions: [Permission.DeleteChannel] }));
82
+ };
@@ -0,0 +1,114 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Button, Dialog, DialogContent, DialogHeader, DialogTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, Input, Skeleton, Table, TableBody, TableCell, TableRow, Tooltip, apiClient, Tabs, TabsContent, TabsList, TabsTrigger, useServer, EntityCustomFields, useTranslation, TableLabel, } from '@deenruv/react-ui-devkit';
4
+ import { assetsSelector } from "../../../graphql/base";
5
+ import { DeletionResult } from '@deenruv/admin-types';
6
+ import { DialogTrigger } from '@radix-ui/react-dialog';
7
+ import { TooltipContent, TooltipTrigger } from '@radix-ui/react-tooltip';
8
+ import { format } from 'date-fns';
9
+ import { useCallback, useState } from 'react';
10
+ import { toast } from 'sonner';
11
+ import { Copy, ExternalLink, MoreHorizontal, Pencil, Trash } from 'lucide-react';
12
+ export const Asset = ({ asset, onAssetChange }) => {
13
+ const { t } = useTranslation(['common', 'assets']);
14
+ const [open, setOpen] = useState(false);
15
+ const [assetDetails, setAssetDetails] = useState();
16
+ const [assetName, setAssetName] = useState(asset.name);
17
+ const [isLoading, setIsLoading] = useState(true);
18
+ const [activeTab, setActiveTab] = useState('preview');
19
+ const hasEntityCustomFields = !!useServer((p) => p.serverConfig?.entityCustomFields?.find((el) => el.entityName.charAt(0).toLowerCase() + el.entityName.slice(1) === 'asset'))?.customFields.length;
20
+ const tableData = [
21
+ {
22
+ header: _jsx(TableLabel, { children: t('assets:detailsTable.id', 'ID') }),
23
+ render: assetDetails?.id,
24
+ },
25
+ {
26
+ header: _jsx(TableLabel, { children: t('assets:detailsTable.name', 'Name') }),
27
+ render: _jsx(Input, { value: assetName, onChange: (e) => setAssetName(e.target.value) }),
28
+ },
29
+ {
30
+ header: _jsx(TableLabel, { children: t('assets:detailsTable.fileSize', 'File Size') }),
31
+ render: assetDetails?.fileSize && assetDetails.fileSize / 1000 + 'kB',
32
+ },
33
+ {
34
+ header: _jsx(TableLabel, { children: t('assets:detailsTable.createdAt', 'Created At') }),
35
+ render: assetDetails?.createdAt && format(new Date(assetDetails.createdAt), 'yyyy-MM-dd, HH:ss'),
36
+ },
37
+ {
38
+ header: _jsx(TableLabel, { children: t('assets:detailsTable.size', 'Dimensions') }),
39
+ render: assetDetails?.width && assetDetails?.height ? `${assetDetails.width}px x ${assetDetails.height}px` : '-',
40
+ },
41
+ {
42
+ header: _jsx(TableLabel, { children: t('assets:detailsTable.source', 'Source') }),
43
+ render: assetDetails?.source,
44
+ },
45
+ ];
46
+ const onClose = () => {
47
+ setAssetDetails(undefined);
48
+ setOpen(false);
49
+ };
50
+ const onEditName = useCallback(async () => {
51
+ const { updateAsset } = await apiClient('mutation')({
52
+ updateAsset: [{ input: { id: asset.id, name: assetName } }, { name: true }],
53
+ });
54
+ if (updateAsset.name) {
55
+ toast.success(t('assets:editSuccess', 'Asset updated successfully'));
56
+ onAssetChange();
57
+ onClose();
58
+ }
59
+ else
60
+ toast.error(t('assets:editFail', 'Failed to update asset'));
61
+ }, [assetName, asset.id, onAssetChange, onClose, t]);
62
+ const onDelete = useCallback(async () => {
63
+ const { deleteAsset } = await apiClient('mutation')({
64
+ deleteAsset: [{ input: { assetId: asset.id } }, { message: true, result: true }],
65
+ });
66
+ if (deleteAsset.result === DeletionResult.DELETED) {
67
+ toast.success(t('assets:deleteSuccess', 'Asset deleted successfully'));
68
+ onAssetChange();
69
+ onClose();
70
+ }
71
+ else
72
+ toast.error(t('assets:deleteFail', 'Failed to delete asset') + ': ' + deleteAsset.message);
73
+ }, [asset.id, onAssetChange, onClose, t]);
74
+ const getAsset = async () => {
75
+ const response = await apiClient('query')({
76
+ asset: [
77
+ {
78
+ id: asset.id,
79
+ },
80
+ assetsSelector,
81
+ ],
82
+ });
83
+ if (response.asset)
84
+ setAssetDetails(response.asset);
85
+ };
86
+ const copyAssetUrl = () => {
87
+ navigator.clipboard.writeText(asset.preview);
88
+ toast.success(t('assets:urlCopied', 'Asset URL copied to clipboard'));
89
+ };
90
+ return (_jsxs(Dialog, { open: open, onOpenChange: setOpen, children: [_jsx(DialogTrigger, { asChild: true, children: _jsxs("div", { className: "bg-background group relative h-52 w-40 overflow-hidden rounded-lg border transition-all hover:shadow-md", onClick: () => getAsset(), children: [_jsx("div", { className: "absolute inset-0 bg-black/0 transition-colors duration-200 group-hover:bg-black/5" }), _jsx("div", { className: "absolute right-2 top-2 z-10", children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, onClick: (e) => e.stopPropagation(), children: _jsxs(Button, { size: "sm", variant: "ghost", className: "bg-background/80 size-8 rounded-full p-0 opacity-0 shadow-sm backdrop-blur-sm group-hover:opacity-100", children: [_jsx(MoreHorizontal, { size: 16 }), _jsx("span", { className: "sr-only", children: "Open menu" })] }) }), _jsxs(DropdownMenuContent, { align: "end", className: "w-[160px]", children: [_jsxs(DropdownMenuItem, { onClick: (e) => {
91
+ e.stopPropagation();
92
+ copyAssetUrl();
93
+ }, className: "gap-2", children: [_jsx(Copy, { size: 14 }), _jsx("span", { children: t('common:copyURL', 'Copy URL') })] }), _jsxs(DropdownMenuItem, { onClick: (e) => {
94
+ e.stopPropagation();
95
+ window.open(asset.preview, '_blank');
96
+ }, className: "gap-2", children: [_jsx(ExternalLink, { size: 14 }), _jsx("span", { children: t('common:open', 'Open') })] }), _jsxs(DropdownMenuItem, { onClick: (e) => {
97
+ e.stopPropagation();
98
+ getAsset();
99
+ setOpen(true);
100
+ }, className: "gap-2", children: [_jsx(Pencil, { size: 14 }), _jsx("span", { children: t('common:edit', 'Edit') })] }), _jsxs(DropdownMenuItem, { onClick: (e) => {
101
+ e.stopPropagation();
102
+ getAsset();
103
+ onDelete();
104
+ }, className: "text-destructive gap-2", children: [_jsx(Trash, { size: 14 }), _jsx("span", { children: t('common:delete', 'Delete') })] })] })] }) }), _jsxs("div", { className: "bg-muted/30 aspect-square", children: [isLoading && _jsx(Skeleton, { className: "size-full" }), _jsx("img", { src: `${asset.preview}?preset=tile`, alt: asset.name, className: "size-full object-cover transition-all duration-300", style: { opacity: isLoading ? 0 : 1 }, onLoad: () => setIsLoading(false), onError: () => setIsLoading(false) })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { className: "block w-full truncate p-3 text-left text-sm font-medium", title: asset.name, children: asset.name }), _jsx(TooltipContent, { className: "bg-popover text-popover-foreground z-50 rounded-md border px-3 py-1.5 text-sm shadow-md", children: asset.name })] })] }) }), _jsxs(DialogContent, { className: "max-w-5xl", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { className: "text-xl", children: asset.name }) }), _jsxs(Tabs, { value: activeTab, onValueChange: setActiveTab, className: "mt-2", children: [_jsxs(TabsList, { className: "grid w-full max-w-md grid-cols-2", children: [_jsx(TabsTrigger, { value: "preview", children: t('common:preview') }), _jsx(TabsTrigger, { value: "details", children: t('common:details') })] }), _jsx(TabsContent, { value: "preview", className: "space-y-4", children: _jsxs("div", { className: "relative flex h-[500px] items-center justify-center overflow-hidden rounded-lg p-4", children: [_jsx("div", { className: "absolute inset-0 bg-[#f0f0f0]", style: {
105
+ backgroundImage: `
106
+ linear-gradient(45deg, #e0e0e0 25%, transparent 25%),
107
+ linear-gradient(-45deg, #e0e0e0 25%, transparent 25%),
108
+ linear-gradient(45deg, transparent 75%, #e0e0e0 75%),
109
+ linear-gradient(-45deg, transparent 75%, #e0e0e0 75%)
110
+ `,
111
+ backgroundSize: '20px 20px',
112
+ backgroundPosition: '0 0, 0 10px, 10px -10px, -10px 0px',
113
+ } }), _jsx("div", { className: "border-border pointer-events-none absolute inset-0 rounded-lg border" }), _jsx("img", { src: assetDetails?.source ? `${assetDetails.source}?preset=medium` : asset.preview, alt: asset.name, className: "relative z-10 max-h-full max-w-full rounded object-contain shadow-md" })] }) }), _jsx(TabsContent, { value: "details", className: "mt-4", children: _jsxs("div", { className: "grid h-[492px] gap-6 md:grid-cols-2", children: [_jsx("div", { className: "overflow-hidden rounded-lg border", children: _jsx(Table, { children: _jsx(TableBody, { children: tableData.map((d, i) => (_jsxs(TableRow, { className: i % 2 === 0 ? 'bg-muted/30' : '', children: [_jsx(TableCell, { className: "w-1/3 font-medium", children: d.header }), _jsx(TableCell, { className: "break-all", children: d.render })] }, i))) }) }) }), hasEntityCustomFields && (_jsxs("div", { children: [_jsxs("h3", { className: "mb-3 flex items-center gap-2 text-lg font-medium", children: [_jsx(Pencil, { size: 16 }), t('common:customFields')] }), _jsx("div", { className: "rounded-lg border p-4", children: _jsx(EntityCustomFields, { entityName: "asset", id: asset?.id }) })] }))] }) })] }), _jsxs("div", { className: "mt-4 flex justify-between", children: [_jsx(Button, { variant: "destructive", onClick: () => onDelete(), children: t('assets:delete', 'Delete') }), _jsxs("div", { className: "flex gap-3", children: [_jsx(Button, { variant: "outline", onClick: () => onClose(), children: t('common:cancel', 'Cancel') }), _jsx(Button, { onClick: () => onEditName(), children: t('common:save', 'Save') })] })] })] })] }));
114
+ };