@deenruv/admin-dashboard 1.0.17-dev.20 → 1.0.17-dev.23

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 (38) hide show
  1. package/dist/DeenruvAdminPanel.js +14 -8
  2. package/dist/access/admin-router.js +42 -3
  3. package/dist/access/built-in-route-permissions.js +44 -0
  4. package/dist/access/built-in-routes.js +22 -21
  5. package/dist/access/channel-selection.js +9 -0
  6. package/dist/components/Menu/ChannelSwitcher.js +8 -1
  7. package/dist/graphql/scalars.js +4 -0
  8. package/dist/locales/en/orders.json +9 -1
  9. package/dist/locales/pl/orders.json +9 -1
  10. package/dist/pages/Root.js +24 -19
  11. package/dist/pages/assets/List.js +1 -1
  12. package/dist/pages/collections/Detail.js +5 -0
  13. package/dist/pages/collections/List.js +1 -1
  14. package/dist/pages/collections/_components/ContentsCard.js +1 -1
  15. package/dist/pages/countries/Detail.js +5 -0
  16. package/dist/pages/countries/List.js +1 -1
  17. package/dist/pages/facets/Detail.js +5 -0
  18. package/dist/pages/facets/List.js +1 -1
  19. package/dist/pages/facets/_components/FacetDetailView.js +1 -1
  20. package/dist/pages/orders/_components/CancelAndRefundDialog.js +80 -19
  21. package/dist/pages/orders/_components/Payments.js +39 -23
  22. package/dist/pages/orders/_components/ProductsTable.js +13 -25
  23. package/dist/pages/orders/_components/TopActions.js +14 -8
  24. package/dist/pages/payment-methods/Detail.js +5 -0
  25. package/dist/pages/payment-methods/List.js +1 -1
  26. package/dist/pages/product-variants/List.js +4 -3
  27. package/dist/pages/products/Detail.js +3 -3
  28. package/dist/pages/products/List.js +3 -2
  29. package/dist/pages/shipping-methods/Detail.js +5 -0
  30. package/dist/pages/shipping-methods/List.js +1 -1
  31. package/dist/pages/tax-categories/Detail.js +3 -3
  32. package/dist/pages/tax-categories/List.js +1 -1
  33. package/dist/pages/tax-rates/Detail.js +5 -0
  34. package/dist/pages/tax-rates/List.js +1 -1
  35. package/dist/pages/zones/Detail.js +5 -0
  36. package/dist/pages/zones/List.js +1 -1
  37. package/dist/version.js +1 -1
  38. package/package.json +5 -5
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // eslint-disable-next-line no-restricted-imports
3
3
  import { I18nextProvider } from 'react-i18next';
4
- import { useEffect, useMemo } from 'react';
4
+ import { useCallback, useEffect, useMemo } from 'react';
5
5
  import { createBrowserRouter, RouterProvider } from 'react-router';
6
6
  import { AnimatePresence } from 'framer-motion';
7
7
  import { Toaster } from 'sonner';
@@ -17,7 +17,7 @@ import { ADMIN_DASHBOARD_VERSION } from "./version.js";
17
17
  import { ORDER_STATUS_NOTIFICATION } from "./notifications/OrderStatusNotification.js";
18
18
  import { SYSTEM_STATUS_NOTIFICATION } from "./notifications/SystemStatusNotification.js";
19
19
  import { AdminAccessProvider, builtInAdminRoutes, getDefaultAdminRoute, getPermittedAdminRoutes, } from "./access/index.js";
20
- import { createAdminRouterRoutes } from "./access/admin-router.js";
20
+ import { CommittedRouterOwner, createAdminRouterRoutes } from "./access/admin-router.js";
21
21
  const loadTranslations = () => {
22
22
  Object.entries(resources).forEach(([lang, value]) => {
23
23
  Object.entries(value).forEach(([, translations]) => {
@@ -54,7 +54,10 @@ export const DeenruvAdminPanel = ({ plugins, settings }) => {
54
54
  language: p.language,
55
55
  translationsLanguage: p.translationsLanguage,
56
56
  })));
57
- const userPermissions = useServer((p) => p.userPermissions);
57
+ const { administratorAccessState, userPermissions } = useServer(useShallow((p) => ({
58
+ administratorAccessState: p.administratorAccessState,
59
+ userPermissions: p.userPermissions,
60
+ })));
58
61
  const pluginRoutes = useMemo(() => pluginsStore.routes.map((route) => ({
59
62
  id: `plugin.${route.plugin.name}.${route.path}`,
60
63
  path: route.path,
@@ -62,14 +65,17 @@ export const DeenruvAdminPanel = ({ plugins, settings }) => {
62
65
  ...route.access,
63
66
  search: { menuKey: `${route.plugin.name} - ${route.path}`, type: 'plugin' },
64
67
  })), [plugins]);
65
- const permittedRoutes = useMemo(() => getPermittedAdminRoutes([...builtInAdminRoutes, ...pluginRoutes], userPermissions), [pluginRoutes, userPermissions]);
66
- const defaultRoute = getDefaultAdminRoute(permittedRoutes);
67
- const router = useMemo(() => createBrowserRouter(createAdminRouterRoutes({
68
+ const permittedRoutes = useMemo(() => administratorAccessState === 'ready'
69
+ ? getPermittedAdminRoutes([...builtInAdminRoutes, ...pluginRoutes], userPermissions)
70
+ : [], [administratorAccessState, pluginRoutes, userPermissions]);
71
+ const defaultRoute = administratorAccessState === 'ready' ? getDefaultAdminRoute(permittedRoutes) : undefined;
72
+ const createRouter = useCallback(() => createBrowserRouter(createAdminRouterRoutes({
68
73
  permittedRoutes,
69
74
  defaultRoute,
75
+ administratorAccessState,
70
76
  rootElement: _jsx(Root, { allPaths: permittedRoutes.map((route) => route.path).filter(Boolean) }),
71
77
  errorElement: _jsx(ErrorPage, {}),
72
- })), [defaultRoute, permittedRoutes]);
78
+ })), [administratorAccessState, defaultRoute, permittedRoutes]);
73
79
  useEffect(() => {
74
80
  const root = window.document.documentElement;
75
81
  root.classList.remove('light', 'dark');
@@ -81,7 +87,7 @@ export const DeenruvAdminPanel = ({ plugins, settings }) => {
81
87
  root.classList.add(theme);
82
88
  }
83
89
  }, [theme]);
84
- return (_jsx(GlobalStoreProvider, { ...settings, children: _jsxs(I18nextProvider, { i18n: i18n, defaultNS: 'common', children: [_jsx(AnimatePresence, { children: isLoggedIn ? (_jsx(AdminAccessProvider, { value: { routes: permittedRoutes, defaultRoute }, children: _jsx(PluginProvider, { plugins: pluginsStore, context: context, children: _jsx(NotificationProvider, { notifications: [ORDER_STATUS_NOTIFICATION, SYSTEM_STATUS_NOTIFICATION].concat(pluginsStore.notifications), children: _jsx(RouterProvider, { router: router }) }) }) })) : (_jsx(LoginScreen, {})) }), _jsx(Toaster, { theme: theme, className: "toaster group", richColors: true, expand: true, position: "bottom-right", toastOptions: {
90
+ return (_jsx(GlobalStoreProvider, { ...settings, children: _jsxs(I18nextProvider, { i18n: i18n, defaultNS: 'common', children: [_jsx(AnimatePresence, { children: isLoggedIn ? (_jsx(AdminAccessProvider, { value: { routes: permittedRoutes, defaultRoute }, children: _jsx(PluginProvider, { plugins: pluginsStore, context: context, children: _jsx(NotificationProvider, { notifications: [ORDER_STATUS_NOTIFICATION, SYSTEM_STATUS_NOTIFICATION].concat(pluginsStore.notifications), children: _jsx(CommittedRouterOwner, { createRouter: createRouter, children: (router) => _jsx(RouterProvider, { router: router }) }) }) }) })) : (_jsx(LoginScreen, {})) }), _jsx(Toaster, { theme: theme, className: "toaster group", richColors: true, expand: true, position: "bottom-right", toastOptions: {
85
91
  classNames: {
86
92
  toast: 'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg group-[.toaster]:p-4',
87
93
  title: 'group-[.toast]:font-semibold group-[.toast]:text-foreground',
@@ -1,21 +1,60 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useLayoutEffect, useRef, useState } from 'react';
2
3
  import { Routes } from '@deenruv/react-ui-devkit';
3
4
  import { Navigate } from 'react-router';
4
- export const createAdminRouterRoutes = ({ permittedRoutes, defaultRoute, rootElement, errorElement, }) => {
5
+ export const CommittedRouterOwner = ({ createRouter, children, }) => {
6
+ const [router, setRouter] = useState();
7
+ const activeRouter = useRef(undefined);
8
+ const disposedRouters = useRef(new WeakSet());
9
+ useLayoutEffect(() => {
10
+ const nextRouter = createRouter();
11
+ activeRouter.current = nextRouter;
12
+ setRouter(nextRouter);
13
+ return () => {
14
+ if (activeRouter.current === nextRouter) {
15
+ activeRouter.current = undefined;
16
+ }
17
+ globalThis.queueMicrotask(() => {
18
+ if (activeRouter.current !== nextRouter && !disposedRouters.current.has(nextRouter)) {
19
+ disposedRouters.current.add(nextRouter);
20
+ nextRouter.dispose();
21
+ }
22
+ });
23
+ };
24
+ }, [createRouter]);
25
+ return router && activeRouter.current === router ? children(router) : null;
26
+ };
27
+ export const createAdminRouterRoutes = ({ permittedRoutes, defaultRoute, administratorAccessState, rootElement, errorElement, }) => {
5
28
  const children = permittedRoutes.map(({ id, path, element }) => ({ id, path, element }));
6
- if (defaultRoute && !permittedRoutes.some((route) => route.path === Routes.dashboard)) {
29
+ if (administratorAccessState === 'ready' &&
30
+ defaultRoute &&
31
+ !permittedRoutes.some((route) => route.path === Routes.dashboard)) {
7
32
  children.push({
8
33
  id: 'admin.fallback.dashboard',
9
34
  path: Routes.dashboard,
10
35
  element: _jsx(Navigate, { to: defaultRoute.path, replace: true }),
11
36
  });
12
37
  }
13
- if (defaultRoute) {
38
+ if (administratorAccessState === 'ready' && defaultRoute) {
14
39
  children.push({
15
40
  id: 'admin.fallback.wildcard',
16
41
  path: '*',
17
42
  element: _jsx(Navigate, { to: defaultRoute.path, replace: true }),
18
43
  });
19
44
  }
45
+ else if (administratorAccessState === 'pending') {
46
+ children.push({
47
+ id: 'admin.fallback.pending-access',
48
+ path: '*',
49
+ element: null,
50
+ });
51
+ }
52
+ else {
53
+ children.push({
54
+ id: 'admin.fallback.unavailable-access',
55
+ path: '*',
56
+ element: errorElement,
57
+ });
58
+ }
20
59
  return [{ id: 'admin.root', element: rootElement, errorElement, children }];
21
60
  };
@@ -0,0 +1,44 @@
1
+ import { Permission } from '@deenruv/admin-types';
2
+ export const catalogRoutePermissions = {
3
+ products: {
4
+ read: [Permission.ReadProduct, Permission.ReadCatalog],
5
+ create: [Permission.CreateProduct, Permission.CreateCatalog],
6
+ },
7
+ collections: {
8
+ read: [Permission.ReadCollection, Permission.ReadCatalog],
9
+ create: [Permission.CreateCollection, Permission.CreateCatalog],
10
+ },
11
+ facets: {
12
+ read: [Permission.ReadFacet, Permission.ReadCatalog],
13
+ create: [Permission.CreateFacet, Permission.CreateCatalog],
14
+ },
15
+ assets: {
16
+ read: [Permission.ReadAsset, Permission.ReadCatalog],
17
+ },
18
+ };
19
+ export const settingsRoutePermissions = {
20
+ paymentMethods: {
21
+ read: [Permission.ReadPaymentMethod, Permission.ReadSettings],
22
+ create: [Permission.CreatePaymentMethod, Permission.CreateSettings],
23
+ },
24
+ shippingMethods: {
25
+ read: [Permission.ReadShippingMethod, Permission.ReadSettings],
26
+ create: [Permission.CreateShippingMethod, Permission.CreateSettings],
27
+ },
28
+ zones: {
29
+ read: [Permission.ReadZone, Permission.ReadSettings],
30
+ create: [Permission.CreateZone, Permission.CreateSettings],
31
+ },
32
+ countries: {
33
+ read: [Permission.ReadCountry, Permission.ReadSettings],
34
+ create: [Permission.CreateCountry, Permission.CreateSettings],
35
+ },
36
+ taxCategories: {
37
+ read: [Permission.ReadTaxCategory, Permission.ReadSettings],
38
+ create: [Permission.CreateTaxCategory, Permission.CreateSettings],
39
+ },
40
+ taxRates: {
41
+ read: [Permission.ReadTaxRate, Permission.ReadSettings],
42
+ create: [Permission.CreateTaxRate, Permission.CreateSettings],
43
+ },
44
+ };
@@ -3,6 +3,7 @@ import { Permission } from '@deenruv/admin-types';
3
3
  import { Routes } from '@deenruv/react-ui-devkit';
4
4
  import { BarChart, Barcode, BadgePercent, Coins, Cog, CreditCard, Flag, Folder, Globe, Globe2, Images, MapPin, Percent, ScanBarcode, Server, ShoppingCart, Store, Tag, Truck, UserCog, UserRoundSearch, Users, UsersRound, } from 'lucide-react';
5
5
  import { AdminsDetailPage, AdminsListPage, AdminsProvisionPage, AssetsListPage, ChannelsDetailPage, ChannelsListPage, CollectionsDetailPage, CollectionsListPage, CountriesDetailPage, CountriesListPage, CustomerGroupsDetailPage, CustomerGroupsListPage, CustomersDetailPage, CustomersListPage, Dashboard, Extensions, FacetsDetailPage, FacetsListPage, GlobalSettings, OrdersDetailPage, OrdersListPage, PaymentMethodsDetailPage, PaymentMethodsListPage, ProductVariantDetailPage, ProductVariantsListPage, ProductsDetailPage, ProductsListPage, PromotionsDetailPage, PromotionsListPage, RolesDetailPage, RolesListPage, SellersDetailPage, SellersListPage, ShippingMethodsDetailPage, ShippingMethodsListPage, Status, StockLocationsDetailPage, StockLocationsListPage, TaxCategoriesDetailPage, TaxCategoriesListPage, TaxRatesDetailPage, TaxRatesListPage, ZonesDetailPage, ZonesListPage, } from "../pages/index.js";
6
+ import { catalogRoutePermissions, settingsRoutePermissions } from './built-in-route-permissions.js';
6
7
  export const adminNavigationGroups = [
7
8
  { id: 'shop-group', labelKey: 'shop' },
8
9
  { id: 'assortment-group', labelKey: 'assortment' },
@@ -85,8 +86,8 @@ export const builtInAdminRoutes = [
85
86
  routes: Routes.products,
86
87
  listElement: () => _jsx(ProductsListPage, {}),
87
88
  detailElement: () => _jsx(ProductsDetailPage, {}),
88
- readPermissions: [Permission.ReadProduct, Permission.ReadCatalog],
89
- createPermissions: [Permission.CreateProduct],
89
+ readPermissions: catalogRoutePermissions.products.read,
90
+ createPermissions: catalogRoutePermissions.products.create,
90
91
  nav: { groupId: 'assortment-group', linkId: 'link-products', menuKey: 'products', icon: Barcode },
91
92
  }),
92
93
  ...createCrudRouteDefinitions({
@@ -95,8 +96,8 @@ export const builtInAdminRoutes = [
95
96
  routes: Routes.productVariants,
96
97
  listElement: () => _jsx(ProductVariantsListPage, {}),
97
98
  detailElement: () => _jsx(ProductVariantDetailPage, {}),
98
- readPermissions: [Permission.ReadProduct, Permission.ReadCatalog],
99
- createPermissions: [Permission.CreateProduct],
99
+ readPermissions: catalogRoutePermissions.products.read,
100
+ createPermissions: catalogRoutePermissions.products.create,
100
101
  nav: {
101
102
  groupId: 'assortment-group',
102
103
  linkId: 'link-product-variants',
@@ -110,8 +111,8 @@ export const builtInAdminRoutes = [
110
111
  routes: Routes.collections,
111
112
  listElement: () => _jsx(CollectionsListPage, {}),
112
113
  detailElement: () => _jsx(CollectionsDetailPage, {}),
113
- readPermissions: [Permission.ReadCollection, Permission.ReadCatalog],
114
- createPermissions: [Permission.CreateCollection],
114
+ readPermissions: catalogRoutePermissions.collections.read,
115
+ createPermissions: catalogRoutePermissions.collections.create,
115
116
  nav: { groupId: 'assortment-group', linkId: 'link-collections', menuKey: 'collections', icon: Folder },
116
117
  }),
117
118
  ...createCrudRouteDefinitions({
@@ -120,15 +121,15 @@ export const builtInAdminRoutes = [
120
121
  routes: Routes.facets,
121
122
  listElement: () => _jsx(FacetsListPage, {}),
122
123
  detailElement: () => _jsx(FacetsDetailPage, {}),
123
- readPermissions: [Permission.ReadFacet, Permission.ReadCatalog],
124
- createPermissions: [Permission.CreateFacet],
124
+ readPermissions: catalogRoutePermissions.facets.read,
125
+ createPermissions: catalogRoutePermissions.facets.create,
125
126
  nav: { groupId: 'assortment-group', linkId: 'link-facets', menuKey: 'facets', icon: Tag },
126
127
  }),
127
128
  {
128
129
  id: 'assets.list',
129
130
  path: Routes.assets.list,
130
131
  element: _jsx(AssetsListPage, {}),
131
- requiredPermissions: [Permission.ReadAsset, Permission.ReadCatalog],
132
+ requiredPermissions: catalogRoutePermissions.assets.read,
132
133
  nav: { groupId: 'assortment-group', linkId: 'link-assets', menuKey: 'assets', icon: Images },
133
134
  search: { menuKey: 'assets', type: 'list' },
134
135
  },
@@ -185,8 +186,8 @@ export const builtInAdminRoutes = [
185
186
  routes: Routes.paymentMethods,
186
187
  listElement: () => _jsx(PaymentMethodsListPage, {}),
187
188
  detailElement: () => _jsx(PaymentMethodsDetailPage, {}),
188
- readPermissions: [Permission.ReadPaymentMethod],
189
- createPermissions: [Permission.CreatePaymentMethod],
189
+ readPermissions: settingsRoutePermissions.paymentMethods.read,
190
+ createPermissions: settingsRoutePermissions.paymentMethods.create,
190
191
  nav: {
191
192
  groupId: 'shipping-group',
192
193
  linkId: 'link-payment-methods',
@@ -200,8 +201,8 @@ export const builtInAdminRoutes = [
200
201
  routes: Routes.shippingMethods,
201
202
  listElement: () => _jsx(ShippingMethodsListPage, {}),
202
203
  detailElement: () => _jsx(ShippingMethodsDetailPage, {}),
203
- readPermissions: [Permission.ReadShippingMethod],
204
- createPermissions: [Permission.CreateShippingMethod],
204
+ readPermissions: settingsRoutePermissions.shippingMethods.read,
205
+ createPermissions: settingsRoutePermissions.shippingMethods.create,
205
206
  nav: {
206
207
  groupId: 'shipping-group',
207
208
  linkId: 'link-shipping-methods',
@@ -235,8 +236,8 @@ export const builtInAdminRoutes = [
235
236
  routes: Routes.zones,
236
237
  listElement: () => _jsx(ZonesListPage, {}),
237
238
  detailElement: () => _jsx(ZonesDetailPage, {}),
238
- readPermissions: [Permission.ReadZone],
239
- createPermissions: [Permission.CreateZone],
239
+ readPermissions: settingsRoutePermissions.zones.read,
240
+ createPermissions: settingsRoutePermissions.zones.create,
240
241
  nav: { groupId: 'settings-group', linkId: 'link-zones', menuKey: 'zones', icon: Globe },
241
242
  }),
242
243
  ...createCrudRouteDefinitions({
@@ -245,8 +246,8 @@ export const builtInAdminRoutes = [
245
246
  routes: Routes.countries,
246
247
  listElement: () => _jsx(CountriesListPage, {}),
247
248
  detailElement: () => _jsx(CountriesDetailPage, {}),
248
- readPermissions: [Permission.ReadCountry],
249
- createPermissions: [Permission.CreateCountry],
249
+ readPermissions: settingsRoutePermissions.countries.read,
250
+ createPermissions: settingsRoutePermissions.countries.create,
250
251
  nav: { groupId: 'settings-group', linkId: 'link-countries', menuKey: 'countries', icon: Flag },
251
252
  }),
252
253
  ...createCrudRouteDefinitions({
@@ -255,8 +256,8 @@ export const builtInAdminRoutes = [
255
256
  routes: Routes.taxCategories,
256
257
  listElement: () => _jsx(TaxCategoriesListPage, {}),
257
258
  detailElement: () => _jsx(TaxCategoriesDetailPage, {}),
258
- readPermissions: [Permission.ReadTaxCategory],
259
- createPermissions: [Permission.CreateTaxCategory],
259
+ readPermissions: settingsRoutePermissions.taxCategories.read,
260
+ createPermissions: settingsRoutePermissions.taxCategories.create,
260
261
  nav: {
261
262
  groupId: 'settings-group',
262
263
  linkId: 'link-tax-categories',
@@ -270,8 +271,8 @@ export const builtInAdminRoutes = [
270
271
  routes: Routes.taxRates,
271
272
  listElement: () => _jsx(TaxRatesListPage, {}),
272
273
  detailElement: () => _jsx(TaxRatesDetailPage, {}),
273
- readPermissions: [Permission.ReadTaxRate],
274
- createPermissions: [Permission.CreateTaxRate],
274
+ readPermissions: settingsRoutePermissions.taxRates.read,
275
+ createPermissions: settingsRoutePermissions.taxRates.create,
275
276
  nav: { groupId: 'settings-group', linkId: 'link-tax-rates', menuKey: 'taxRates', icon: Percent },
276
277
  }),
277
278
  {
@@ -0,0 +1,9 @@
1
+ export const switchSelectedChannel = (channel, actions) => {
2
+ actions.clearPermissions();
3
+ actions.selectChannel(channel);
4
+ actions.setPermissionsForChannel(channel.id);
5
+ };
6
+ export const selectPreferredChannel = (channels, retainedChannel, configuredDefaultCode, builtInDefaultCode) => channels.find((channel) => channel.id === retainedChannel?.id) ??
7
+ channels.find((channel) => channel.code === configuredDefaultCode) ??
8
+ channels.find((channel) => channel.code === builtInDefaultCode) ??
9
+ channels[0];
@@ -1,16 +1,23 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, useServer, useSettings, DEFAULT_CHANNEL_CODE, cn, useTranslation, } from '@deenruv/react-ui-devkit';
3
3
  import { useCallback } from 'react';
4
+ import { switchSelectedChannel } from "../../access/channel-selection.js";
4
5
  export function ChannelSwitcher({ className }) {
5
6
  const channels = useServer((p) => p.channels);
6
7
  const setSelectedChannel = useSettings((p) => p.setSelectedChannel);
7
8
  const selectedChannel = useSettings((p) => p.selectedChannel);
9
+ const setUserPermissions = useServer((p) => p.setUserPermissions);
10
+ const setSelectedChannelPermissions = useServer((p) => p.setSelectedChannelPermissions);
8
11
  const { t } = useTranslation('common');
9
12
  const onChannelChange = (id) => {
10
13
  const channel = channels.find((channel) => channel.id === id);
11
14
  if (!channel)
12
15
  return;
13
- setSelectedChannel(channel);
16
+ switchSelectedChannel(channel, {
17
+ clearPermissions: () => setUserPermissions([]),
18
+ selectChannel: setSelectedChannel,
19
+ setPermissionsForChannel: setSelectedChannelPermissions,
20
+ });
14
21
  };
15
22
  const getChannelLabel = useCallback((code) => (code === DEFAULT_CHANNEL_CODE ? t('defaultChannel') : code), [t]);
16
23
  if (!channels || channels.length === 0) {
@@ -1,5 +1,9 @@
1
1
  import { ZeusScalars } from '@deenruv/admin-types';
2
2
  export const scalars = ZeusScalars({
3
+ ID: {
4
+ encode: (e) => e,
5
+ decode: (e) => e,
6
+ },
3
7
  Money: {
4
8
  decode: (e) => e,
5
9
  },
@@ -383,7 +383,15 @@
383
383
  "title": "Cancel and refund the order",
384
384
  "refund": "Refund",
385
385
  "returnToStock": "Return to warehouse",
386
- "refundShipping": "Return for shipment"
386
+ "refundShipping": "Refund shipping",
387
+ "cancelShipping": "Return shipping to stock",
388
+ "cancelShippingRequiresLines": "Select at least one item to return before returning shipping.",
389
+ "refundQuantity": "Refund quantity",
390
+ "reason": "Reason",
391
+ "suggestedAmount": "Calculated refund",
392
+ "auto": "Automatic",
393
+ "submit": "Apply cancellation and refund",
394
+ "error": "Could not apply cancellation or refund"
387
395
  },
388
396
  "create": {
389
397
  "addModal": {
@@ -383,7 +383,15 @@
383
383
  "title": "Anuluj i refunduj zamówienie",
384
384
  "refund": "Refunduj",
385
385
  "returnToStock": "Zwróć do magazynu",
386
- "refundShipping": "Zwróć za przesyłkę"
386
+ "refundShipping": "Zwróć koszt przesyłki",
387
+ "cancelShipping": "Zwróć przesyłkę do magazynu",
388
+ "cancelShippingRequiresLines": "Wybierz co najmniej jeden produkt do zwrotu przed zwrotem przesyłki.",
389
+ "refundQuantity": "Ilość do refundacji",
390
+ "reason": "Powód",
391
+ "suggestedAmount": "Wyliczony zwrot",
392
+ "auto": "Automatycznie",
393
+ "submit": "Zastosuj anulowanie i zwrot",
394
+ "error": "Nie udało się anulować lub zwrócić płatności"
387
395
  },
388
396
  "create": {
389
397
  "addModal": {
@@ -10,6 +10,8 @@ import { GlobalSearch } from "../components/GlobalSearch.js";
10
10
  import { ContentAreaSkeleton } from "../components/ContentAreaSkeleton.js";
11
11
  import { DeenruvDeveloperIndicator } from "../DeenruvDeveloperIndicator.js";
12
12
  import { Permission } from '@deenruv/admin-types';
13
+ import { selectPreferredChannel } from "../access/channel-selection.js";
14
+ import { ErrorPage } from "./Custom404.js";
13
15
  const TAKE = 100;
14
16
  const getAllPaginatedCountries = async () => {
15
17
  let countries = [];
@@ -45,14 +47,17 @@ const getAllPaymentMethods = async () => {
45
47
  export const Root = ({ allPaths }) => {
46
48
  const isLocalhost = window.location.hostname === 'localhost';
47
49
  const { t } = useTranslation('common');
48
- const clearAdministratorAccess = useServer((p) => p.clearAdministratorAccess);
50
+ const failAdministratorAccess = useServer((p) => p.failAdministratorAccess);
51
+ const beginAdministratorAccessInitialization = useServer((p) => p.beginAdministratorAccessInitialization);
49
52
  const setAdministratorAccess = useServer((p) => p.setAdministratorAccess);
53
+ const setSelectedChannelPermissions = useServer((p) => p.setSelectedChannelPermissions);
50
54
  const setServerConfig = useServer((p) => p.setServerConfig);
51
55
  const setCountries = useServer((p) => p.setCountries);
52
56
  const setFulfillmentHandlers = useServer((p) => p.setFulfillmentHandlers);
53
57
  const setPaymentMethodsType = useServer((p) => p.setPaymentMethodsType);
54
58
  const fetchPendingJobs = useServer((p) => p.fetchPendingJobs);
55
59
  const loaded = useServer((p) => p.loaded);
60
+ const administratorAccessState = useServer((p) => p.administratorAccessState);
56
61
  const setLoaded = useServer((p) => p.setLoaded);
57
62
  const setAvailableLanguages = useSettings((p) => p.setAvailableLanguages);
58
63
  const setLanguage = useSettings((p) => p.setLanguage);
@@ -69,6 +74,9 @@ export const Root = ({ allPaths }) => {
69
74
  const location = useLocation();
70
75
  const [searchParams] = useSearchParams();
71
76
  const hasInitializedAdministratorAccess = useRef(false);
77
+ useEffect(() => {
78
+ setSelectedChannelPermissions(selectedChannel?.id);
79
+ }, [selectedChannel?.id, setSelectedChannelPermissions]);
72
80
  const managePageTitle = useCallback(() => {
73
81
  const pathname = location.pathname;
74
82
  const isDeenruvPath = allPaths.some((path) => pathname.startsWith(path));
@@ -144,22 +152,23 @@ export const Root = ({ allPaths }) => {
144
152
  };
145
153
  }, [notifications.map((n) => n.id).join(','), loaded]);
146
154
  useEffect(() => {
147
- if (hasInitializedAdministratorAccess.current)
155
+ if (administratorAccessState !== 'pending' || hasInitializedAdministratorAccess.current)
148
156
  return;
149
157
  hasInitializedAdministratorAccess.current = true;
150
- clearAdministratorAccess();
158
+ beginAdministratorAccessInitialization();
151
159
  const init = async () => {
152
160
  const activeAdministratorResponse = await apiClient('query')({
153
161
  activeAdministrator: activeAdministratorSelector,
154
162
  });
155
163
  const roles = activeAdministratorResponse.activeAdministrator?.user.roles || [];
156
164
  if (!activeAdministratorResponse.activeAdministrator) {
157
- clearAdministratorAccess();
165
+ failAdministratorAccess();
166
+ setLoaded(true);
158
167
  toast.error(t('setup.failedAdmin'));
159
168
  return;
160
169
  }
161
170
  else {
162
- setAdministratorAccess(activeAdministratorResponse.activeAdministrator, Array.from(new Set(roles.flatMap((role) => role.permissions))));
171
+ let administratorChannelId = selectedChannel?.id;
163
172
  if ([Permission.ReadChannel].some((p) => roles.some((r) => r.permissions.includes(p)))) {
164
173
  const { channels: { items: allChannels = [] }, } = await apiClient('query')({
165
174
  channels: [
@@ -177,25 +186,20 @@ export const Root = ({ allPaths }) => {
177
186
  ],
178
187
  });
179
188
  setChannels(allChannels);
180
- if (!channel) {
181
- if (selectedChannel) {
182
- const foundChannel = allChannels.find((ch) => ch.code === selectedChannel.code);
183
- setSelectedChannel(foundChannel || allChannels[0]);
184
- }
185
- const existingChannel = allChannels.find((ch) => ch.code === window?.__DEENRUV_SETTINGS__?.ui?.defaultChannelCode);
186
- if (existingChannel) {
187
- setSelectedChannel(existingChannel);
188
- }
189
- const defaultChannel = allChannels.find((ch) => ch.code === DEFAULT_CHANNEL_CODE) || allChannels[0];
190
- setSelectedChannel(defaultChannel);
189
+ const preferredChannel = selectPreferredChannel(allChannels, selectedChannel, window?.__DEENRUV_SETTINGS__?.ui?.defaultChannelCode, DEFAULT_CHANNEL_CODE);
190
+ if (preferredChannel && preferredChannel.id !== channel?.id) {
191
+ setSelectedChannel(preferredChannel);
191
192
  }
193
+ administratorChannelId = preferredChannel?.id;
192
194
  }
193
195
  else {
194
196
  const possibleChannels = roles.flatMap((role) => role.channels);
195
197
  if (possibleChannels.length > 0) {
196
198
  setSelectedChannel(possibleChannels[0]);
199
+ administratorChannelId = possibleChannels[0].id;
197
200
  }
198
201
  }
202
+ setAdministratorAccess(activeAdministratorResponse.activeAdministrator, administratorChannelId);
199
203
  }
200
204
  // WE NEED TO CHECK IF LOCALSTORAGE HAS LANGUAGE SET
201
205
  if (window?.__DEENRUV_SETTINGS__?.ui?.defaultLanguageCode) {
@@ -261,8 +265,9 @@ export const Root = ({ allPaths }) => {
261
265
  await init();
262
266
  })
263
267
  .catch(() => {
264
- clearAdministratorAccess();
268
+ failAdministratorAccess();
269
+ setLoaded(true);
265
270
  });
266
- }, []);
267
- return (_jsx(_Fragment, { children: _jsxs("div", { className: "flex max-h-screen w-full max-w-full overflow-hidden bg-background text-foreground antialiased", children: [_jsx(Menu, { children: loaded ? _jsx(Outlet, {}) : _jsx(ContentAreaSkeleton, {}) }), loaded && (_jsxs(_Fragment, { children: [_jsx(GlobalSearch, {}), _jsx(CanLeaveRouteDialog, {}), isLocalhost ? _jsx(DeenruvDeveloperIndicator, {}) : null] }))] }) }));
271
+ }, [administratorAccessState]);
272
+ return (_jsx(_Fragment, { children: _jsxs("div", { className: "flex max-h-screen w-full max-w-full overflow-hidden bg-background text-foreground antialiased", children: [_jsx(Menu, { children: administratorAccessState === 'unavailable' ? (_jsx(ErrorPage, {})) : administratorAccessState === 'pending' || !loaded ? (_jsx(ContentAreaSkeleton, {})) : (_jsx(Outlet, {})) }), loaded && administratorAccessState === 'ready' && (_jsxs(_Fragment, { children: [_jsx(GlobalSearch, {}), _jsx(CanLeaveRouteDialog, {}), isLocalhost ? _jsx(DeenruvDeveloperIndicator, {}) : null] }))] }) }));
268
273
  };
@@ -78,5 +78,5 @@ export const AssetsListPage = () => {
78
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
79
  },
80
80
  },
81
- ], createPermissions: [Permission.CreateAsset], deletePermissions: [Permission.DeleteAsset] }));
81
+ ], createPermissions: [Permission.CreateAsset, Permission.CreateCatalog], deletePermissions: [Permission.DeleteAsset, Permission.DeleteCatalog] }));
82
82
  };
@@ -4,6 +4,7 @@ import { useParams } from 'react-router';
4
4
  import { useValidators, DetailView, createDeenruvForm, getMutation, useMutation, useSettings, } from '@deenruv/react-ui-devkit';
5
5
  import { CollectionsDetailView } from "./_components/CollectionDetailView.js";
6
6
  import { CollectionStorefrontAction } from "./_components/collectionStorefrontAction";
7
+ import { Permission } from '@deenruv/admin-types';
7
8
  const CreateCollectionMutation = getMutation('createCollection');
8
9
  const EditCollectionMutation = getMutation('updateCollection');
9
10
  const DeleteCollectionMutation = getMutation('deleteCollection');
@@ -76,5 +77,9 @@ export const CollectionsDetailPage = () => {
76
77
  }),
77
78
  }, topActions: {
78
79
  inline: [_jsx(CollectionStorefrontAction, {}, "collection-storefront-action")],
80
+ }, permissions: {
81
+ create: [Permission.CreateCollection, Permission.CreateCatalog],
82
+ edit: [Permission.UpdateCollection, Permission.UpdateCatalog],
83
+ delete: [Permission.DeleteCollection, Permission.DeleteCatalog],
79
84
  } }) }));
80
85
  };
@@ -197,5 +197,5 @@ export const CollectionsListPage = () => {
197
197
  return { success: '' };
198
198
  },
199
199
  },
200
- ], entityName: 'Collection', route: Routes['collections'], tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreateCollection], deletePermissions: [Permission.DeleteCollection] }));
200
+ ], entityName: 'Collection', route: Routes['collections'], tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreateCollection, Permission.CreateCatalog], deletePermissions: [Permission.DeleteCollection, Permission.DeleteCatalog] }));
201
201
  };
@@ -31,7 +31,7 @@ export const ContentsCard = ({ collectionId }) => {
31
31
  totalItems: 0,
32
32
  });
33
33
  }, [collectionId]);
34
- return (_jsx(CustomCard, { title: t('details.contents.title'), color: "green", icon: _jsx(CardIcons.group, {}), children: _jsx(DetailList, { noPaddings: true, detailLinkColumn: "id", filterFields: [{ key: 'id', operator: 'IDOperators' }], searchFields: ['code', 'name'], hideColumns: ['customFields', 'product', 'updatedAt', 'createdAt'], entityName: 'Product', route: Routes['products'], tableId: "products-list-view", fetch: fetch, noCreateButton: true, createPermissions: [Permission.CreateProduct], deletePermissions: [Permission.DeleteProduct], additionalColumns: [
34
+ return (_jsx(CustomCard, { title: t('details.contents.title'), color: "green", icon: _jsx(CardIcons.group, {}), children: _jsx(DetailList, { noPaddings: true, detailLinkColumn: "id", filterFields: [{ key: 'id', operator: 'IDOperators' }], searchFields: ['code', 'name'], hideColumns: ['customFields', 'product', 'updatedAt', 'createdAt'], entityName: 'Product', route: Routes['products'], tableId: "products-list-view", fetch: fetch, noCreateButton: true, createPermissions: [Permission.CreateProduct, Permission.CreateCatalog], deletePermissions: [Permission.DeleteProduct, Permission.DeleteCatalog], additionalColumns: [
35
35
  {
36
36
  accessorKey: 'product',
37
37
  enableSorting: false,
@@ -3,6 +3,7 @@ import { useParams } from 'react-router';
3
3
  import { useValidators, DetailView, createDeenruvForm, getMutation, useMutation, useTranslation, } from '@deenruv/react-ui-devkit';
4
4
  import { CountryDetailView } from "./_components/CountryDetailView.js";
5
5
  import { useCallback } from 'react';
6
+ import { Permission } from '@deenruv/admin-types';
6
7
  const CreateCountryMutation = getMutation('createCountry');
7
8
  const EditCountryMutation = getMutation('updateCountry');
8
9
  const DeleteCountryMutation = getMutation('deleteCountry');
@@ -53,5 +54,9 @@ export const CountriesDetailPage = () => {
53
54
  onSubmitted: onSubmitHandler,
54
55
  onDeleted: onDeleteHandler,
55
56
  }),
57
+ }, permissions: {
58
+ create: [Permission.CreateCountry, Permission.CreateSettings],
59
+ edit: [Permission.UpdateCountry, Permission.UpdateSettings],
60
+ delete: [Permission.DeleteCountry, Permission.DeleteSettings],
56
61
  } }) }));
57
62
  };
@@ -43,5 +43,5 @@ export const CountriesListPage = () => {
43
43
  { key: 'name', operator: 'StringOperators' },
44
44
  { key: 'enabled', operator: 'BooleanOperators' },
45
45
  { key: 'code', operator: 'StringOperators' },
46
- ], detailLinkColumn: "id", searchFields: ['name', 'code'], hideColumns: ['customFields', 'translations'], entityName: 'Country', route: Routes['countries'], tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreateCountry], deletePermissions: [Permission.DeleteCountry] }));
46
+ ], detailLinkColumn: "id", searchFields: ['name', 'code'], hideColumns: ['customFields', 'translations'], entityName: 'Country', route: Routes['countries'], tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreateCountry, Permission.CreateSettings], deletePermissions: [Permission.DeleteCountry, Permission.DeleteSettings] }));
47
47
  };
@@ -3,6 +3,7 @@ import { useParams } from 'react-router';
3
3
  import { useValidators, DetailView, createDeenruvForm, getMutation, useMutation, useTranslation, } from '@deenruv/react-ui-devkit';
4
4
  import { FacetsDetailView } from "./_components/FacetDetailView.js";
5
5
  import { useCallback } from 'react';
6
+ import { Permission } from '@deenruv/admin-types';
6
7
  const CreateFacetMutation = getMutation('createFacet');
7
8
  const EditFacetMutation = getMutation('updateFacet');
8
9
  const DeleteFacetMutation = getMutation('deleteFacet');
@@ -52,5 +53,9 @@ export const FacetsDetailPage = () => {
52
53
  onSubmitted: onSubmitHandler,
53
54
  onDeleted: onDeleteHandler,
54
55
  }),
56
+ }, permissions: {
57
+ create: [Permission.CreateFacet, Permission.CreateCatalog],
58
+ edit: [Permission.UpdateFacet, Permission.UpdateCatalog],
59
+ delete: [Permission.DeleteFacet, Permission.DeleteCatalog],
55
60
  } }) }));
56
61
  };
@@ -44,5 +44,5 @@ export const FacetsListPage = () => {
44
44
  header: () => _jsx(TableLabel, { children: t('table.isPrivate') }),
45
45
  cell: ({ row }) => row.original.isPrivate ? (_jsx(ListBadge, { className: "flex items-center justify-center border-orange-200 bg-orange-200 text-orange-800", children: t('visibility.private') })) : (_jsx(ListBadge, { className: "flex items-center justify-center border-green-200 bg-green-200 text-green-800", children: t('visibility.public') })),
46
46
  },
47
- ], route: Routes['facets'], tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreateFacet], deletePermissions: [Permission.DeleteFacet] }));
47
+ ], route: Routes['facets'], tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreateFacet, Permission.CreateCatalog], deletePermissions: [Permission.DeleteFacet, Permission.DeleteCatalog] }));
48
48
  };
@@ -101,5 +101,5 @@ export const FacetsDetailView = () => {
101
101
  setFacetValueIdInModal(null);
102
102
  setOpen(true);
103
103
  },
104
- }, searchFields: ['name'], tableId: tableId, noPaddings: true, createPermissions: [Permission.CreateFacet], deletePermissions: [Permission.DeleteFacet] }) }))] }) }));
104
+ }, searchFields: ['name'], tableId: tableId, noPaddings: true, createPermissions: [Permission.CreateFacet, Permission.CreateCatalog], deletePermissions: [Permission.DeleteFacet, Permission.DeleteCatalog] }) }))] }) }));
105
105
  };