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

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.
@@ -6,17 +6,17 @@ export const BrandLogo = ({ isCollapsed = false }) => {
6
6
  let Logo = null;
7
7
  if (isCollapsed) {
8
8
  if (typeof collapsed === 'string')
9
- Logo = _jsx("img", { src: collapsed, alt: "Logo", className: "size-full object-fill" });
9
+ Logo = _jsx("img", { src: collapsed, alt: "", className: "size-full object-contain" });
10
10
  if (typeof collapsed === 'object')
11
- Logo = React.cloneElement(collapsed, { className: 'object-contain' });
11
+ Logo = React.cloneElement(collapsed, { className: 'size-full object-contain' });
12
12
  }
13
13
  if (!isCollapsed) {
14
14
  if (typeof full === 'string')
15
- Logo = _jsx("img", { src: full, alt: "Logo", className: "size-full object-fill" });
15
+ Logo = _jsx("img", { src: full, alt: "", className: "size-full object-contain" });
16
16
  if (typeof full === 'object')
17
- Logo = React.cloneElement(full, { className: 'object-contain' });
17
+ Logo = React.cloneElement(full, { className: 'size-full object-contain' });
18
18
  }
19
19
  if (!Logo)
20
- return (_jsxs("svg", { width: "260px", height: "100%", viewBox: "0 0 200 60", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("defs", { children: _jsxs("filter", { id: "shadow", x: "-50%", y: "-50%", width: "200%", height: "200%", children: [_jsx("feOffset", { result: "offOut", in: "SourceAlpha", dx: "2", dy: "2" }), _jsx("feGaussianBlur", { result: "blurOut", in: "offOut", stdDeviation: "2" }), _jsx("feBlend", { in: "SourceGraphic", in2: "blurOut", mode: "normal" })] }) }), _jsx("text", { x: "50%", y: "50%", textAnchor: "middle", fill: "black", fontSize: "24", fontWeight: "bold", dy: ".3em", children: isCollapsed ? name?.charAt?.(0) : name }), _jsx("text", { x: "50%", y: "50%", textAnchor: "middle", fill: "#ffffff", fontSize: "24", fontWeight: "bold", dy: ".3em", filter: "url(#shadow)", opacity: "0.9", children: isCollapsed ? name?.charAt?.(0) : name })] }));
20
+ return (_jsxs("svg", { className: "size-full", viewBox: "0 0 200 60", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [_jsx("defs", { children: _jsxs("filter", { id: "shadow", x: "-50%", y: "-50%", width: "200%", height: "200%", children: [_jsx("feOffset", { result: "offOut", in: "SourceAlpha", dx: "2", dy: "2" }), _jsx("feGaussianBlur", { result: "blurOut", in: "offOut", stdDeviation: "2" }), _jsx("feBlend", { in: "SourceGraphic", in2: "blurOut", mode: "normal" })] }) }), _jsx("text", { x: "50%", y: "50%", textAnchor: "middle", fill: "black", fontSize: "24", fontWeight: "bold", dy: ".3em", children: isCollapsed ? name?.charAt?.(0) : name }), _jsx("text", { x: "50%", y: "50%", textAnchor: "middle", fill: "#ffffff", fontSize: "24", fontWeight: "bold", dy: ".3em", filter: "url(#shadow)", opacity: "0.9", children: isCollapsed ? name?.charAt?.(0) : name })] }));
21
21
  return _jsx(_Fragment, { children: Logo });
22
22
  };
@@ -0,0 +1,58 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { apiClient, Button, cn, createDialog, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Tooltip, TooltipContent, TooltipTrigger, usePluginStore, useServer, useSettings, useTranslation, } from '@deenruv/react-ui-devkit';
3
+ import { Permission } from '@deenruv/admin-types';
4
+ import { LogOutIcon, RotateCwSquare } from 'lucide-react';
5
+ import { useNavigate } from 'react-router';
6
+ import { canAccessAdminItem, useAdminAccess } from "../../access/index.js";
7
+ const accountMenuItemClass = 'cursor-pointer gap-2 focus:bg-[var(--sidebar-hover)] focus:text-[var(--sidebar-ink)]';
8
+ const accountMenuSurfaceClass = 'border-[var(--sidebar-hairline)] bg-[var(--sidebar-surface)] text-[var(--sidebar-ink)] shadow-sm dark:shadow-none';
9
+ export const AccountMenu = ({ isCollapsed, mode, onNavigate, }) => {
10
+ const { t } = useTranslation('common');
11
+ const navigate = useNavigate();
12
+ const { routes } = useAdminAccess();
13
+ const { topNavigationActionsMenu } = usePluginStore();
14
+ const { logOut } = useSettings();
15
+ const { activeAdministrator, clearAdministratorAccess, setJobQueue, userPermissions } = useServer();
16
+ const administratorName = activeAdministrator
17
+ ? `${activeAdministrator.firstName} ${activeAdministrator.lastName}`.trim()
18
+ : t('account');
19
+ const initials = activeAdministrator
20
+ ? `${activeAdministrator.firstName.charAt(0)}${activeAdministrator.lastName.charAt(0)}`.toUpperCase()
21
+ : '?';
22
+ const reindexEnabled = canAccessAdminItem({
23
+ item: { requiredPermissions: [Permission.UpdateCatalog, Permission.UpdateProduct] },
24
+ userPermissions,
25
+ });
26
+ const allowedActions = topNavigationActionsMenu?.filter((entry) => canAccessAdminItem({ item: entry.access, userPermissions }));
27
+ const fastLinks = [
28
+ { key: 'systemStatus', route: routes.find((route) => route.id === 'system.status') },
29
+ { key: 'globalSettings', route: routes.find((route) => route.id === 'settings.global') },
30
+ ].filter(({ route }) => route && canAccessAdminItem({ item: route, userPermissions }));
31
+ const rebuildSearchIndex = async () => {
32
+ const { reindex } = await apiClient('mutation')({ reindex: { id: true, queueName: true, state: true } });
33
+ setJobQueue(reindex.queueName, reindex.state === 'RUNNING');
34
+ };
35
+ const trigger = (_jsxs(Button, { variant: "ghost", className: isCollapsed
36
+ ? 'size-8 rounded-[4px] p-0 text-[var(--sidebar-secondary)] hover:bg-[var(--sidebar-hover)] hover:text-[var(--sidebar-ink)] focus-visible:ring-[var(--sidebar-focus)]'
37
+ : 'sidebar-row mx-2 w-[calc(100%-1rem)] justify-start bg-transparent focus-visible:ring-[var(--sidebar-focus)]', "aria-label": t('openAccountMenu'), children: [_jsx("span", { className: "sidebar-icon-slot rounded-[4px] bg-[var(--sidebar-active)] text-[10px] font-medium text-[var(--sidebar-active-ink)]", children: initials }), !isCollapsed && (_jsxs("span", { className: "min-w-0 flex-1 text-left", children: [_jsx("span", { className: "block truncate text-sm font-medium text-[var(--sidebar-ink)]", children: administratorName }), activeAdministrator?.emailAddress && (_jsx("span", { className: "block truncate text-xs font-normal text-[var(--sidebar-tertiary)]", children: activeAdministrator.emailAddress }))] }))] }));
38
+ return (_jsxs(DropdownMenu, { children: [isCollapsed ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(DropdownMenuTrigger, { asChild: true, children: trigger }) }), _jsx(TooltipContent, { side: "right", children: administratorName })] })) : (_jsx(DropdownMenuTrigger, { asChild: true, children: trigger })), _jsxs(DropdownMenuContent, { side: mode === 'mobile' ? 'top' : 'right', align: mode === 'mobile' ? 'start' : 'end', sideOffset: 6, collisionPadding: 8, className: cn('z-[150] min-w-56', accountMenuSurfaceClass), children: [activeAdministrator?.emailAddress && (_jsxs(_Fragment, { children: [_jsxs(DropdownMenuLabel, { children: [_jsx("span", { className: "block truncate", children: administratorName }), _jsx("span", { className: "block truncate text-xs font-normal text-[var(--sidebar-tertiary)]", children: activeAdministrator.emailAddress })] }), _jsx(DropdownMenuSeparator, { className: "bg-[var(--sidebar-hairline)]" })] })), reindexEnabled && (_jsxs(DropdownMenuItem, { className: accountMenuItemClass, onSelect: rebuildSearchIndex, children: [_jsx(RotateCwSquare, { className: "size-4" }), t('rebuildSerachIndex')] })), fastLinks.length > 0 && (_jsx(DropdownMenuGroup, { children: _jsxs(DropdownMenuSub, { children: [_jsx(DropdownMenuSubTrigger, { className: "focus:bg-[var(--sidebar-hover)] focus:text-[var(--sidebar-ink)]", children: t('fastLinks') }), _jsx(DropdownMenuPortal, { children: _jsx(DropdownMenuSubContent, { className: accountMenuSurfaceClass, children: fastLinks.map(({ key, route }) => (_jsxs(DropdownMenuItem, { className: accountMenuItemClass, onSelect: () => {
39
+ navigate(route.path, { viewTransition: true });
40
+ onNavigate?.();
41
+ }, children: [_jsx(RotateCwSquare, { className: "size-4" }), t(key)] }, key))) }) })] }) })), allowedActions?.length ? (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, { className: "bg-[var(--sidebar-hairline)]" }), allowedActions.map((action) => (_jsxs(DropdownMenuItem, { className: cn(accountMenuItemClass, action.className), onSelect: () => {
42
+ action.onClick();
43
+ onNavigate?.();
44
+ }, children: [action.icon && _jsx(action.icon, { className: "size-4" }), action.label] }, action.label)))] })) : null, _jsx(DropdownMenuSeparator, { className: "bg-[var(--sidebar-hairline)]" }), _jsxs(DropdownMenuItem, { className: cn(accountMenuItemClass, 'text-red-500 focus:text-red-500'), onSelect: async () => {
45
+ const confirmed = await createDialog({
46
+ title: t('logOutConfirmation'),
47
+ description: t('logOutConfirmationDescription'),
48
+ buttons: [
49
+ { label: t('cancel'), variant: 'secondary', returnValue: false },
50
+ { label: t('logOut'), variant: 'destructive', returnValue: true },
51
+ ],
52
+ });
53
+ if (confirmed) {
54
+ clearAdministratorAccess();
55
+ logOut();
56
+ }
57
+ }, children: [_jsx(LogOutIcon, { className: "size-4" }), t('logOut')] })] })] }));
58
+ };
@@ -1,38 +1,35 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
2
3
  import { NavLink, useLocation } from 'react-router';
3
- import React, { useEffect, useMemo, useRef, useState } from 'react';
4
- import { cn, buttonVariants, usePluginStore, Tooltip, TooltipContent, TooltipTrigger, useServer, Accordion, AccordionItem, AccordionTrigger, AccordionContent, useNotifications, useTranslation, capitalizeFirstLetter, Skeleton, } from '@deenruv/react-ui-devkit';
5
- import { adminNavigationGroups, canAccessAdminItem, getActiveNavigationLinkIds, getActiveNavigationGroupIds, getNavigationLinkActivePaths, insertNavigationLink, useAdminAccess, } from "../../access/index.js";
6
- export function Navigation({ isCollapsed }) {
4
+ import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Button, cn, Skeleton, Tooltip, TooltipContent, TooltipTrigger, useNotifications, usePluginStore, useServer, useTranslation, } from '@deenruv/react-ui-devkit';
5
+ import { adminNavigationGroups, canAccessAdminItem, getActiveNavigationGroupIds, getActiveNavigationLinkIds, getNavigationLinkActivePaths, insertNavigationLink, useAdminAccess, } from "../../access/index.js";
6
+ import { mergeOpenGroupIds } from './SidebarState.js';
7
+ export function Navigation({ isCollapsed, manuallyOpenGroupIds, onExpand, onOpenGroupIdsChange, onNavigate, }) {
7
8
  const { t } = useTranslation('common');
8
- const { t: _pluginT } = useTranslation();
9
+ const { t: pluginTranslation } = useTranslation();
9
10
  const location = useLocation();
10
11
  const { navMenuData, viewMarkers } = usePluginStore();
11
12
  const { routes } = useAdminAccess();
12
- const userPermissions = useServer((p) => p.userPermissions);
13
- const loaded = useServer((p) => p.loaded);
13
+ const userPermissions = useServer((state) => state.userPermissions);
14
+ const loaded = useServer((state) => state.loaded);
14
15
  const getNavigationNotification = useNotifications(({ getNavigationNotification }) => getNavigationNotification);
15
- const pluginT = (trans) => {
16
- const split = trans.split('.');
17
- const key = split.slice(1).join('.') || '';
18
- const ns = split[0] || '';
19
- return _pluginT(key, { ns });
16
+ const pluginT = (translation) => {
17
+ const [namespace = '', ...keyParts] = translation.split('.');
18
+ return pluginTranslation(keyParts.join('.'), { ns: namespace });
20
19
  };
21
20
  const navigationGroups = useMemo(() => {
22
- const navData = adminNavigationGroups.map((group) => ({
21
+ const groups = adminNavigationGroups.map((group) => ({
23
22
  label: t(`menuGroups.${group.labelKey}`),
24
23
  id: group.id,
25
24
  links: [],
26
25
  }));
27
26
  routes.forEach((route) => {
28
- if (!route.nav)
27
+ if (!route.nav || !canAccessAdminItem({ item: route, userPermissions }))
29
28
  return;
30
- if (!canAccessAdminItem({ item: route, userPermissions }))
29
+ const group = groups.find((candidate) => candidate.id === route.nav?.groupId);
30
+ if (!group)
31
31
  return;
32
- const foundGroupIdx = navData.findIndex((group) => group.id === route.nav?.groupId);
33
- if (foundGroupIdx === -1)
34
- return;
35
- navData[foundGroupIdx].links.push({
32
+ group.links.push({
36
33
  title: t(`menu.${route.nav.menuKey}`),
37
34
  href: route.path,
38
35
  id: route.nav.linkId,
@@ -43,68 +40,54 @@ export function Navigation({ isCollapsed }) {
43
40
  activePaths: getNavigationLinkActivePaths(route, routes),
44
41
  });
45
42
  });
46
- const { groups, links } = navMenuData;
47
- groups.forEach(({ id, labelId, placement, access }) => {
43
+ navMenuData.groups.forEach(({ id, labelId, placement, access }) => {
48
44
  if (!canAccessAdminItem({ item: access, userPermissions }))
49
45
  return;
50
- let foundGroupIdx = -1;
51
- const newGroup = { id, label: pluginT(labelId), links: [] };
52
- if (placement?.groupId) {
53
- foundGroupIdx = navData.findIndex((group) => group.id === placement.groupId);
54
- }
55
- if (foundGroupIdx == -1) {
56
- navData.push(newGroup);
57
- }
58
- else {
59
- navData.splice(foundGroupIdx + 1, 0, newGroup);
60
- }
46
+ const nextGroup = { id, label: pluginT(labelId), links: [] };
47
+ const anchorIndex = placement?.groupId ? groups.findIndex((group) => group.id === placement.groupId) : -1;
48
+ if (anchorIndex === -1)
49
+ groups.push(nextGroup);
50
+ else
51
+ groups.splice(anchorIndex + 1, 0, nextGroup);
61
52
  });
62
- links.forEach(({ groupId, href, labelId, id, icon, placement, access }) => {
53
+ navMenuData.links.forEach(({ groupId, href, labelId, id, icon, placement, access }) => {
63
54
  if (!canAccessAdminItem({ item: access, userPermissions }))
64
55
  return;
65
- const foundGroupIdx = navData.findIndex((group) => group.id === groupId);
66
- if (foundGroupIdx == -1)
67
- return;
68
- const newElement = {
69
- title: pluginT(labelId),
70
- label: pluginT(labelId),
71
- href: `/${href}`,
72
- id,
73
- icon,
74
- access,
75
- groupId,
76
- };
77
- if (!placement) {
78
- navData[foundGroupIdx].links.push(newElement);
56
+ const group = groups.find((candidate) => candidate.id === groupId);
57
+ if (!group)
79
58
  return;
80
- }
81
- navData[foundGroupIdx].links = insertNavigationLink(navData[foundGroupIdx].links, newElement, placement);
59
+ const link = { title: pluginT(labelId), href: `/${href}`, id, icon, access, groupId };
60
+ group.links = insertNavigationLink(group.links, link, placement);
82
61
  });
83
- return navData.filter((group) => group.links.length > 0);
84
- }, [navMenuData.groups, navMenuData.links, pluginT, routes, t, userPermissions]);
85
- const permittedNavigationGroups = navigationGroups;
86
- const activeGroupIds = useMemo(() => getActiveNavigationGroupIds(permittedNavigationGroups.flatMap((group) => group.links), location.pathname), [location.pathname, permittedNavigationGroups]);
87
- const activeLinkIds = useMemo(() => new Set(getActiveNavigationLinkIds(permittedNavigationGroups.flatMap((group) => group.links), location.pathname).map((link) => link.id)), [location.pathname, permittedNavigationGroups]);
88
- const previousPathname = useRef(location.pathname);
89
- const [openGroupIds, setOpenGroupIds] = useState(activeGroupIds);
90
- useEffect(() => {
91
- const hasPathnameChanged = previousPathname.current !== location.pathname;
92
- previousPathname.current = location.pathname;
93
- if (!hasPathnameChanged)
94
- return;
95
- setOpenGroupIds((currentGroupIds) => {
96
- const nextGroupIds = [...new Set([...currentGroupIds, ...activeGroupIds])];
97
- return nextGroupIds.length === currentGroupIds.length ? currentGroupIds : nextGroupIds;
98
- });
99
- }, [activeGroupIds, location.pathname]);
62
+ return groups.filter((group) => group.links.length > 0);
63
+ }, [navMenuData.groups, navMenuData.links, pluginTranslation, routes, t, userPermissions]);
64
+ const allLinks = useMemo(() => navigationGroups.flatMap((group) => group.links), [navigationGroups]);
65
+ const activeGroupIds = useMemo(() => getActiveNavigationGroupIds(allLinks, location.pathname), [allLinks, location.pathname]);
66
+ const activeLinkIds = useMemo(() => new Set(getActiveNavigationLinkIds(allLinks, location.pathname).map((link) => link.id)), [allLinks, location.pathname]);
67
+ const openGroupIds = useMemo(() => mergeOpenGroupIds(manuallyOpenGroupIds, activeGroupIds), [activeGroupIds, manuallyOpenGroupIds]);
68
+ const updateOpenGroups = (groupIds) => {
69
+ const nextGroupIds = mergeOpenGroupIds(groupIds, activeGroupIds);
70
+ onOpenGroupIdsChange(nextGroupIds);
71
+ };
100
72
  if (!loaded) {
101
- return (_jsx("div", { className: "relative overflow-y-auto", children: _jsx("div", { className: "flex h-[calc(100%-64px)] flex-col gap-3 pb-2 lg:h-[calc(100%-72px)]", children: Array.from({ length: 3 }).map((_, groupIdx) => (_jsxs("div", { className: "flex flex-col gap-1 px-2.5", children: [!isCollapsed && (_jsx("div", { className: "px-3 py-2", children: _jsx(Skeleton, { className: "h-3 w-20" }) })), Array.from({ length: groupIdx === 0 ? 4 : groupIdx === 1 ? 5 : 3 }).map((_, linkIdx) => (_jsxs("div", { className: cn('flex items-center px-3 py-2', isCollapsed && 'justify-center px-0'), children: [_jsx(Skeleton, { className: cn('size-4 shrink-0', isCollapsed && 'size-6') }), !isCollapsed && _jsx(Skeleton, { className: "ml-2 h-4 w-24" })] }, linkIdx)))] }, groupIdx))) }) }));
73
+ return (_jsx("div", { className: "flex min-h-0 flex-1 flex-col gap-3 overflow-y-auto px-2 py-3", children: Array.from({ length: 10 }).map((_, index) => (_jsxs("div", { className: cn('flex h-8 items-center gap-2 px-2', isCollapsed && 'justify-center px-0'), children: [_jsx(Skeleton, { className: "size-5 shrink-0 bg-[var(--sidebar-hairline)]" }), !isCollapsed && _jsx(Skeleton, { className: "h-3 w-24 bg-[var(--sidebar-hairline)]" })] }, index))) }));
102
74
  }
103
- return (_jsx("div", { className: "relative overflow-y-auto", children: _jsx("div", { "data-collapsed": isCollapsed, className: "group flex h-[calc(100%-64px)] flex-col gap-2 py-2 data-[collapsed=true]:py-2 lg:h-[calc(100%-72px)]", children: _jsx(Accordion, { type: "multiple", className: "w-full", value: isCollapsed ? permittedNavigationGroups.map((g) => g.id) : openGroupIds, onValueChange: setOpenGroupIds, children: permittedNavigationGroups.map((group) => (_jsxs(AccordionItem, { value: group.id, className: "border-none", children: [!isCollapsed && (_jsx(AccordionTrigger, { className: cn('flex items-center justify-between px-3 py-2 hover:no-underline'), children: _jsxs("div", { className: "flex items-center gap-2 px-1", children: [_jsx("h4", { className: "text-[11px] font-semibold tracking-[0.08em] text-muted-foreground uppercase", children: group.label }), getNavigationNotification(group.id), viewMarkers ? (_jsx("p", { className: "text-xs font-semibold text-muted-foreground lowercase dark:text-muted-foreground", children: group.id })) : null] }) })), _jsx(AccordionContent, { className: cn(isCollapsed ? 'py-1' : 'pb-2'), children: _jsx("nav", { id: group.id, className: "grid gap-1 px-2.5 text-muted-foreground group-[[data-collapsed=true]]:justify-center group-[[data-collapsed=true]]:px-2", children: group.links.map((link, index) => {
104
- const notifications = getNavigationNotification(link.id);
75
+ return (_jsx("div", { className: "min-h-0 flex-1 overflow-y-auto py-2", children: _jsx(Accordion, { type: "multiple", value: openGroupIds, onValueChange: updateOpenGroups, className: "w-full", children: navigationGroups.map((group) => {
76
+ const firstLink = group.links[0];
77
+ const isGroupActive = activeGroupIds.includes(group.id);
78
+ if (isCollapsed) {
79
+ const isSingleLink = group.links.length === 1;
80
+ const collapsedControl = (_jsx("div", { className: "flex size-8 items-center justify-center", children: isSingleLink ? (_jsx(NavLink, { to: firstLink.href, viewTransition: true, onClick: onNavigate, "aria-current": activeLinkIds.has(firstLink.id) ? 'page' : undefined, className: "sidebar-link group/sidebar-link", children: _jsxs("span", { className: cn('flex size-8 items-center justify-center rounded-[4px] text-[var(--sidebar-secondary)] transition-colors group-focus-visible/sidebar-link:ring-2 group-focus-visible/sidebar-link:ring-[var(--sidebar-focus)] group-focus-visible/sidebar-link:outline-none hover:bg-[var(--sidebar-hover)] hover:text-[var(--sidebar-ink)]', activeLinkIds.has(firstLink.id) &&
81
+ 'bg-[var(--sidebar-active)] text-[var(--sidebar-active-ink)] shadow-[inset_2px_0_0_var(--sidebar-active-indicator)]'), children: [_jsx(firstLink.icon, { className: "size-4" }), _jsx("span", { className: "sr-only", children: firstLink.title })] }) })) : (_jsx(Button, { variant: "ghost", size: "icon", className: cn('size-8 rounded-[4px] text-[var(--sidebar-secondary)] hover:bg-[var(--sidebar-hover)] hover:text-[var(--sidebar-ink)] focus-visible:ring-[var(--sidebar-focus)]', isGroupActive &&
82
+ 'bg-[var(--sidebar-active)] text-[var(--sidebar-active-ink)] shadow-[inset_2px_0_0_var(--sidebar-active-indicator)]'), onClick: () => {
83
+ updateOpenGroups([...openGroupIds, group.id]);
84
+ onExpand();
85
+ }, "aria-label": t('expandNavigationGroup', { group: group.label }), children: _jsx(firstLink.icon, { className: "size-4" }) })) }));
86
+ return (_jsx("div", { className: "flex justify-center py-0.5", children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: collapsedControl }), _jsxs(TooltipContent, { side: "right", className: "flex items-center gap-2", children: [_jsx("span", { children: isSingleLink ? firstLink.title : group.label }), getNavigationNotification(isSingleLink ? firstLink.id : group.id), viewMarkers && _jsx("span", { className: "text-xs text-[var(--sidebar-tertiary)]", children: group.id })] })] }) }, group.id));
87
+ }
88
+ return (_jsxs(AccordionItem, { value: group.id, className: "border-none px-2", children: [_jsx(AccordionTrigger, { className: "h-8 px-2 py-0 text-xs font-medium tracking-normal text-[var(--sidebar-secondary)] hover:text-[var(--sidebar-ink)] hover:no-underline", children: _jsxs("span", { className: "flex min-w-0 flex-1 items-center gap-2 text-left", children: [_jsx("span", { className: "truncate", children: group.label }), _jsxs("span", { className: "ml-auto flex shrink-0 items-center gap-1", children: [getNavigationNotification(group.id), viewMarkers && _jsx("span", { className: "text-[10px] font-normal", children: group.id })] })] }) }), _jsx(AccordionContent, { className: "pb-1", children: _jsx("nav", { id: group.id, "aria-label": group.label, className: "grid gap-0.5", children: group.links.map((link) => {
105
89
  const isActive = activeLinkIds.has(link.id);
106
- return (_jsx(React.Fragment, { children: isCollapsed ? (_jsxs(Tooltip, { delayDuration: 0, children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("div", { children: _jsx(NavLink, { to: link.href, viewTransition: true, "aria-current": isActive ? 'page' : undefined, children: _jsxs("div", { className: cn(buttonVariants({ variant: 'navigation-link', size: 'icon' }), 'h-9 w-9 rounded-lg ring-offset-background transition-all hover:scale-[1.03] focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2', isActive &&
107
- 'bg-primary text-primary-foreground opacity-100 shadow-sm hover:scale-100 hover:bg-primary hover:text-primary-foreground'), children: [_jsx(link.icon, { className: "size-5" }), _jsx("span", { className: "sr-only", children: link.title })] }) }) }) }), _jsxs(TooltipContent, { side: "right", className: "relative flex items-center gap-4", children: [viewMarkers ? (_jsx("div", { className: "text-xs font-semibold text-muted-foreground lowercase dark:text-muted-foreground", children: link.id })) : null, capitalizeFirstLetter(link.title), notifications] })] }, index)) : (_jsx(NavLink, { to: link.href, viewTransition: true, "aria-current": isActive ? 'page' : undefined, children: _jsxs("div", { id: link.id, className: cn('relative flex h-10 items-center justify-start rounded-lg px-3 text-sm font-medium capitalize transition-all hover:bg-muted hover:text-foreground', isActive &&
108
- 'bg-primary text-primary-foreground opacity-100 shadow-sm hover:bg-primary hover:text-primary-foreground'), children: [viewMarkers ? (_jsx("div", { className: "absolute top-1/2 right-2 -translate-y-1/2 text-xs font-semibold text-muted-foreground lowercase dark:text-muted-foreground", children: link.id })) : null, _jsx("span", { className: cn('mr-2.5 flex size-6 shrink-0 items-center justify-center rounded-md bg-muted/80 text-muted-foreground transition-colors', isActive && 'bg-primary-foreground/15 text-primary-foreground'), children: _jsx(link.icon, { className: "size-4" }) }), _jsx("span", { className: "truncate", children: capitalizeFirstLetter(link.title) }), notifications] }) })) }, link.id));
109
- }) }) })] }, group.id))) }) }) }));
90
+ return (_jsx(NavLink, { to: link.href, viewTransition: true, onClick: onNavigate, "aria-current": isActive ? 'page' : undefined, className: "sidebar-link group/sidebar-link", children: _jsxs("span", { className: "sidebar-row group-focus-visible/sidebar-link:ring-2 group-focus-visible/sidebar-link:ring-[var(--sidebar-focus)] group-focus-visible/sidebar-link:outline-none", "data-active": isActive, children: [_jsx("span", { className: "sidebar-icon-slot", children: _jsx(link.icon, { className: "size-4" }) }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: link.title }), _jsxs("span", { className: "ml-auto flex shrink-0 items-center gap-1", children: [getNavigationNotification(link.id), viewMarkers && (_jsx("span", { className: "text-[10px] font-normal text-[var(--sidebar-tertiary)]", children: link.id }))] })] }) }, link.id));
91
+ }) }) })] }, group.id));
92
+ }) }) }));
110
93
  }
@@ -1,15 +1,21 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { capitalizeFirstLetter, cn, Routes, Separator, useServer, useTranslation } from '@deenruv/react-ui-devkit';
2
+ import { cn, Routes, Separator, Tooltip, TooltipContent, TooltipTrigger, useServer, useTranslation, } from '@deenruv/react-ui-devkit';
3
3
  import { Puzzle } from 'lucide-react';
4
4
  import { NavLink } from 'react-router';
5
5
  import { canAccessAdminItem, useAdminAccess } from "../../access/index.js";
6
- export const NavigationFooter = ({ isCollapsed }) => {
6
+ import { AccountMenu } from './AccountMenu.js';
7
+ export const NavigationFooter = ({ isCollapsed, mode, onNavigate }) => {
7
8
  const { t } = useTranslation('common');
8
- const userPermissions = useServer((p) => p.userPermissions);
9
+ const userPermissions = useServer((state) => state.userPermissions);
9
10
  const { routes } = useAdminAccess();
10
11
  const extensionsRoute = routes.find((route) => route.id === 'extensions');
11
- const isPermittedToExtensions = !!extensionsRoute && canAccessAdminItem({ item: extensionsRoute, userPermissions });
12
+ const canAccessExtensions = !!extensionsRoute && canAccessAdminItem({ item: extensionsRoute, userPermissions });
12
13
  const extensionsPath = extensionsRoute?.path || Routes.extensions;
13
- return (_jsxs("div", { className: "flex w-full flex-col gap-2 border-t bg-card/80 py-2 text-xs text-muted-foreground select-none", children: [isPermittedToExtensions && !isCollapsed && (_jsxs(_Fragment, { children: [_jsx("div", { children: _jsx(NavLink, { to: extensionsPath, viewTransition: true, children: _jsxs("div", { className: cn('relative mx-2 flex h-8 items-center justify-center px-3 text-sm font-medium capitalize transition-colors hover:bg-muted/70 hover:text-foreground', location.pathname === extensionsPath &&
14
- 'bg-primary/10 text-primary opacity-100 hover:bg-primary/10 hover:text-primary'), children: [_jsx(Puzzle, { className: "mr-2 size-4" }), capitalizeFirstLetter(t('menu.extensions'))] }) }) }), _jsx(Separator, {})] })), _jsxs("div", { className: "flex items-center justify-center gap-1 px-2", children: [!isCollapsed && _jsx("p", { className: "uppercase", children: "Deenruv" }), _jsxs("span", { children: [!isCollapsed ? 'ver. ' : 'v. ', window.__DEENRUV_SETTINGS__.appVersion] })] })] }));
14
+ const version = window.__DEENRUV_SETTINGS__.appVersion;
15
+ const extensionsLink = (_jsx(NavLink, { to: extensionsPath, viewTransition: true, onClick: onNavigate, "aria-label": t('menu.extensions'), className: "sidebar-link group/sidebar-link", children: ({ isActive }) => (_jsxs("span", { className: cn(isCollapsed
16
+ ? 'mx-auto flex size-8 items-center justify-center rounded-[4px] text-[var(--sidebar-secondary)] transition-colors hover:bg-[var(--sidebar-hover)] hover:text-[var(--sidebar-ink)]'
17
+ : 'sidebar-row mx-2', 'group-focus-visible/sidebar-link:ring-2 group-focus-visible/sidebar-link:ring-[var(--sidebar-focus)] group-focus-visible/sidebar-link:outline-none', isCollapsed &&
18
+ isActive &&
19
+ 'bg-[var(--sidebar-active)] text-[var(--sidebar-active-ink)] shadow-[inset_2px_0_0_var(--sidebar-active-indicator)]'), "data-active": !isCollapsed && isActive, children: [isCollapsed ? (_jsx(Puzzle, { className: "size-4" })) : (_jsx("span", { className: "sidebar-icon-slot", children: _jsx(Puzzle, { className: "size-4" }) })), !isCollapsed && _jsx("span", { className: "truncate", children: t('menu.extensions') })] })) }));
20
+ return (_jsxs("div", { className: "shrink-0 border-t border-[var(--sidebar-hairline)] bg-[var(--sidebar-surface)] py-2 text-xs text-[var(--sidebar-secondary)]", children: [canAccessExtensions && (_jsxs(_Fragment, { children: [isCollapsed ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("div", { className: "flex h-8 items-center justify-center", children: extensionsLink }) }), _jsx(TooltipContent, { side: "right", children: t('menu.extensions') })] })) : (extensionsLink), _jsx(Separator, { className: "my-2 bg-[var(--sidebar-hairline)]" })] })), isCollapsed ? (_jsx("div", { className: "flex justify-center", children: _jsx(AccountMenu, { isCollapsed: true, mode: mode, onNavigate: onNavigate }) })) : (_jsx(AccountMenu, { isCollapsed: false, mode: mode, onNavigate: onNavigate })), isCollapsed ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("div", { className: "mx-auto mt-2 max-w-10 truncate px-1 text-center text-[10px] text-[var(--sidebar-tertiary)]", children: ["v ", version] }) }), _jsxs(TooltipContent, { side: "right", children: [t('versionAbbreviation'), " ", version] })] })) : (_jsxs("div", { className: "mt-2 flex h-8 items-center gap-1 px-4 text-xs text-[var(--sidebar-tertiary)]", children: [_jsx("span", { children: "Deenruv" }), _jsxs("span", { children: [t('versionAbbreviation'), " ", version] })] }))] }));
15
21
  };
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, cn, useTranslation } from '@deenruv/react-ui-devkit';
3
+ import { BrandLogo } from "../BrandLogo.js";
4
+ import { Navigation } from './Navigation.js';
5
+ import { NavigationFooter } from './NavigationFooter.js';
6
+ export const SidebarContent = ({ isCollapsed, manuallyOpenGroupIds, mode, onExpand, onOpenGroupIdsChange, onNavigate, onNavigateHome, }) => {
7
+ const { t } = useTranslation('common');
8
+ return (_jsxs("div", { className: "deenruv-sidebar flex h-full min-h-0 flex-col bg-[var(--sidebar-canvas)] text-[var(--sidebar-ink)]", children: [_jsx("div", { className: cn('flex h-16 shrink-0 items-center border-b border-[var(--sidebar-hairline)] px-2 lg:h-[72px]', isCollapsed ? 'justify-center' : 'pr-10'), children: _jsx(Button, { variant: "ghost", className: cn('h-12 rounded-[4px] px-2 text-[var(--sidebar-ink)] hover:bg-[var(--sidebar-hover)] hover:text-[var(--sidebar-ink)] focus-visible:ring-[var(--sidebar-focus)]', isCollapsed ? 'size-8 p-1' : 'w-full justify-start'), onClick: onNavigateHome, "aria-label": t('goToDashboard'), title: t('goToDashboard'), children: _jsx("span", { className: cn('flex shrink-0 items-center justify-center', isCollapsed ? 'size-6' : 'h-10 w-full min-w-0'), children: _jsx(BrandLogo, { isCollapsed: isCollapsed }) }) }) }), _jsx(Navigation, { isCollapsed: isCollapsed, manuallyOpenGroupIds: manuallyOpenGroupIds, onExpand: onExpand, onOpenGroupIdsChange: onOpenGroupIdsChange, onNavigate: onNavigate }), _jsx(NavigationFooter, { isCollapsed: isCollapsed, mode: mode, onNavigate: onNavigate })] }));
9
+ };
@@ -0,0 +1,93 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+ export const SIDEBAR_COLLAPSED_STORAGE_KEY = 'deenruv:admin-sidebar:v1:collapsed';
3
+ export const SIDEBAR_GROUPS_STORAGE_KEY = 'deenruv:admin-sidebar:v1:open-groups';
4
+ export const parseStoredCollapsed = (value, fallback = false) => {
5
+ if (value === 'true')
6
+ return true;
7
+ if (value === 'false')
8
+ return false;
9
+ return fallback;
10
+ };
11
+ export const parseStoredGroupIds = (value) => {
12
+ if (!value)
13
+ return [];
14
+ try {
15
+ const parsed = JSON.parse(value);
16
+ return Array.isArray(parsed) ? [...new Set(parsed.filter((id) => typeof id === 'string'))] : [];
17
+ }
18
+ catch {
19
+ return [];
20
+ }
21
+ };
22
+ export const mergeOpenGroupIds = (storedGroupIds, activeGroupIds) => [
23
+ ...new Set([...storedGroupIds, ...activeGroupIds]),
24
+ ];
25
+ export const readSidebarStorage = (key) => {
26
+ try {
27
+ return window.localStorage.getItem(key);
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ };
33
+ export const writeSidebarStorage = (key, value) => {
34
+ try {
35
+ window.localStorage.setItem(key, value);
36
+ }
37
+ catch {
38
+ // Navigation remains usable when browser storage is unavailable.
39
+ }
40
+ };
41
+ const getInitialCollapsed = () => typeof window === 'undefined' ? false : parseStoredCollapsed(readSidebarStorage(SIDEBAR_COLLAPSED_STORAGE_KEY));
42
+ const getInitialOpenGroupIds = () => typeof window === 'undefined' ? [] : parseStoredGroupIds(readSidebarStorage(SIDEBAR_GROUPS_STORAGE_KEY));
43
+ export const useSidebarState = () => {
44
+ const [isCollapsed, setIsCollapsedState] = useState(getInitialCollapsed);
45
+ const [isMobileOpen, setIsMobileOpen] = useState(false);
46
+ const [manuallyOpenGroupIds, setManuallyOpenGroupIdsState] = useState(getInitialOpenGroupIds);
47
+ const setIsCollapsed = useCallback((collapsed) => {
48
+ setIsCollapsedState(collapsed);
49
+ writeSidebarStorage(SIDEBAR_COLLAPSED_STORAGE_KEY, String(collapsed));
50
+ }, []);
51
+ const toggleDesktop = useCallback(() => {
52
+ setIsCollapsedState((collapsed) => {
53
+ const nextCollapsed = !collapsed;
54
+ writeSidebarStorage(SIDEBAR_COLLAPSED_STORAGE_KEY, String(nextCollapsed));
55
+ return nextCollapsed;
56
+ });
57
+ }, []);
58
+ const setManuallyOpenGroupIds = useCallback((groupIds) => {
59
+ const nextGroupIds = [...new Set(groupIds)];
60
+ setManuallyOpenGroupIdsState(nextGroupIds);
61
+ writeSidebarStorage(SIDEBAR_GROUPS_STORAGE_KEY, JSON.stringify(nextGroupIds));
62
+ }, []);
63
+ useEffect(() => {
64
+ const handleShortcut = (event) => {
65
+ const target = event.target;
66
+ const isEditable = target instanceof HTMLElement &&
67
+ (target.isContentEditable || ['INPUT', 'TEXTAREA', 'SELECT'].includes(target.tagName));
68
+ if (event.key.toLowerCase() !== 'b' ||
69
+ (!event.metaKey && !event.ctrlKey) ||
70
+ event.altKey ||
71
+ event.shiftKey ||
72
+ isEditable) {
73
+ return;
74
+ }
75
+ event.preventDefault();
76
+ if (window.matchMedia('(min-width: 768px)').matches)
77
+ toggleDesktop();
78
+ else
79
+ setIsMobileOpen((open) => !open);
80
+ };
81
+ window.addEventListener('keydown', handleShortcut);
82
+ return () => window.removeEventListener('keydown', handleShortcut);
83
+ }, [toggleDesktop]);
84
+ return {
85
+ isCollapsed,
86
+ isMobileOpen,
87
+ manuallyOpenGroupIds,
88
+ setIsCollapsed,
89
+ setIsMobileOpen,
90
+ setManuallyOpenGroupIds,
91
+ toggleDesktop,
92
+ };
93
+ };
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, Popover, PopoverContent, PopoverTrigger, useTranslation, } from '@deenruv/react-ui-devkit';
3
+ import { SlidersHorizontal } from 'lucide-react';
4
+ import { ChannelSwitcher } from './ChannelSwitcher.js';
5
+ import { LanguagesDropdown } from './LanguagesDropdown.js';
6
+ export const TopbarOverflow = ({ components }) => {
7
+ const { t } = useTranslation('common');
8
+ return (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { variant: "outline", size: "icon", className: "size-9 shrink-0 lg:hidden", "aria-label": t('openTopbarControls'), title: t('openTopbarControls'), children: _jsx(SlidersHorizontal, { className: "size-4" }) }) }), _jsx(PopoverContent, { align: "end", className: "z-[2138] w-[min(20rem,calc(100vw-2rem))] p-3 lg:hidden", children: _jsxs("div", { className: "flex flex-col gap-3", children: [components.length > 0 && (_jsx("div", { className: "flex flex-wrap items-center gap-2 border-b pb-3", children: components.map(({ component: Component }, index) => (_jsx(Component, {}, index))) })), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(LanguagesDropdown, {}), _jsx(ChannelSwitcher, { className: "min-w-0 flex-1" })] })] }) })] }));
9
+ };
@@ -1,102 +1,43 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import React, { useMemo, useState } from 'react';
3
- import { Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbSeparator, Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, ScrollArea, TooltipProvider, Routes, useSettings, useServer, usePluginStore, cn, dashToCamelCase, apiClient, useGlobalSearch, useTranslation, DropdownMenuGroup, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuPortal, DropdownMenuSubContent, createDialog, buildURL, } from '@deenruv/react-ui-devkit';
4
- import { Permission } from '@deenruv/admin-types';
5
- import { useShallow } from 'zustand/react/shallow';
6
- import { GripVertical, LogOutIcon, MenuIcon, Moon, Slash, Sun, SunMoon, RotateCwSquare, SearchIcon, } from 'lucide-react';
7
- import * as ResizablePrimitive from 'react-resizable-panels';
8
- import { Navigation } from './Navigation.js';
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useMemo } from 'react';
9
3
  import { NavLink, useMatches, useNavigate } from 'react-router';
4
+ import { Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbSeparator, buildURL, Button, cn, dashToCamelCase, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, ScrollArea, Sheet, SheetContent, SheetDescription, SheetTitle, TooltipProvider, Routes, useGlobalSearch, usePluginStore, useServer, useSettings, useTranslation, } from '@deenruv/react-ui-devkit';
5
+ import { MenuIcon, Moon, PanelLeftClose, PanelLeftOpen, SearchIcon, Slash, Sun, SunMoon } from 'lucide-react';
6
+ import { canAccessAdminItem, useAdminAccess } from "../../access/index.js";
10
7
  import { ChannelSwitcher } from './ChannelSwitcher.js';
11
- import { BrandLogo } from "../BrandLogo.js";
12
8
  import { LanguagesDropdown } from './LanguagesDropdown.js';
13
9
  import { Notifications } from './Notifications.js';
14
- import { NavigationFooter } from "./NavigationFooter.js";
15
- import { canAccessAdminItem, useAdminAccess } from "../../access/index.js";
16
- const ResizablePanelGroup = ({ className, ...props }) => (_jsx(ResizablePrimitive.Group, { className: cn('flex h-full w-full data-[orientation=vertical]:flex-col', className), ...props }));
17
- const ResizablePanel = ResizablePrimitive.Panel;
18
- const ResizableHandle = ({ withHandle, className, ...props }) => (_jsx(ResizablePrimitive.Separator, { className: cn('relative flex w-px items-center justify-center bg-border/70 after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none data-[orientation=vertical]:h-px data-[orientation=vertical]:w-full data-[orientation=vertical]:after:left-0 data-[orientation=vertical]:after:h-1 data-[orientation=vertical]:after:w-full data-[orientation=vertical]:after:translate-x-0 data-[orientation=vertical]:after:-translate-y-1/2 [&[data-orientation=vertical]>div]:rotate-90', className), ...props, children: withHandle && (_jsx("div", { className: "z-10 flex h-4 w-3 items-center justify-center border bg-background text-muted-foreground", children: _jsx(GripVertical, { className: "size-2.5" }) })) }));
10
+ import { SidebarContent } from './SidebarContent.js';
11
+ import { useSidebarState } from './SidebarState.js';
12
+ import { TopbarOverflow } from './TopbarOverflow.js';
19
13
  const removableCrumbs = ['draft', 'admin-ui'];
20
14
  export const Menu = ({ children }) => {
21
- const openGlobalSearch = useGlobalSearch((state) => state.open);
22
- const linkPath = [];
23
15
  const { t } = useTranslation('common');
24
- const { topNavigationActionsMenu, topNavigationComponents } = usePluginStore();
25
- const { routes, defaultRoute } = useAdminAccess();
26
- const { logOut, theme, setTheme } = useSettings(useShallow((p) => ({
27
- logOut: p.logOut,
28
- theme: p.theme,
29
- setTheme: p.setTheme,
30
- language: p.language,
31
- setLanguage: p.setLanguage,
32
- })));
33
16
  const navigate = useNavigate();
34
- const [isCollapsed, setIsCollapsed] = useState(false);
35
- const { activeAdministrator, clearAdministratorAccess, setJobQueue, userPermissions } = useServer();
17
+ const matches = useMatches();
18
+ const openGlobalSearch = useGlobalSearch((state) => state.open);
19
+ const { topNavigationComponents } = usePluginStore();
20
+ const { defaultRoute } = useAdminAccess();
21
+ const userPermissions = useServer((state) => state.userPermissions);
22
+ const { theme, setTheme } = useSettings();
23
+ const { isCollapsed, isMobileOpen, manuallyOpenGroupIds, setIsCollapsed, setIsMobileOpen, setManuallyOpenGroupIds, toggleDesktop, } = useSidebarState();
36
24
  const defaultRoutePath = defaultRoute?.path || Routes.dashboard;
37
25
  const defaultRouteMenuKey = defaultRoute?.nav?.menuKey || defaultRoute?.search?.menuKey || 'dashboard';
38
- const globalSearchEnabled = true;
39
- const languageSwitcherEnabled = true;
40
- const channelSwitcherEnabled = true;
41
- const notificationsEnabled = true;
42
- const reindexEnabled = canAccessAdminItem({
43
- item: { requiredPermissions: [Permission.UpdateCatalog, Permission.UpdateProduct] },
44
- userPermissions,
45
- });
46
- const canAccessPluginSurface = (entry) => canAccessAdminItem({ item: entry.access, userPermissions });
47
- const allowedTopNavigationComponents = topNavigationComponents?.filter(canAccessPluginSurface);
48
- const allowedTopNavigationActions = topNavigationActionsMenu?.filter(canAccessPluginSurface);
49
- const systemStatusRoute = routes.find((route) => route.id === 'system.status');
50
- const globalSettingsRoute = routes.find((route) => route.id === 'settings.global');
51
- const fastLinks = [
52
- systemStatusRoute &&
53
- canAccessAdminItem({ item: systemStatusRoute, userPermissions }) && {
54
- key: 'systemStatus',
55
- label: t('systemStatus'),
56
- path: systemStatusRoute.path,
57
- },
58
- globalSettingsRoute &&
59
- canAccessAdminItem({ item: globalSettingsRoute, userPermissions }) && {
60
- key: 'globalSettings',
61
- label: t('globalSettings'),
62
- path: globalSettingsRoute.path,
63
- },
64
- ].filter(Boolean);
65
- const rebuildSearchIndex = async () => {
66
- await apiClient('mutation')({ reindex: { id: true, queueName: true, state: true } }).then(({ reindex: { queueName, state } }) => {
67
- setJobQueue(queueName, state === 'RUNNING');
68
- });
69
- };
70
- const matches = useMatches();
26
+ const allowedTopNavigationComponents = topNavigationComponents?.filter((entry) => canAccessAdminItem({ item: entry.access, userPermissions }));
71
27
  const crumbs = useMemo(() => matches
72
28
  .filter((match) => !!match.pathname)
73
29
  .map((match) => match.pathname)
74
- .flatMap((p) => p.split('/'))
30
+ .flatMap((path) => path.split('/'))
75
31
  .filter(Boolean)
76
32
  .filter((crumb) => !removableCrumbs.includes(crumb)), [matches]);
77
- return (_jsx("div", { className: "w-full bg-background", children: _jsx("div", { className: "flex h-full max-h-screen flex-col", children: _jsx("div", { className: "flex-1", children: _jsx(TooltipProvider, { delayDuration: 100, children: _jsxs(ResizablePanelGroup, { onLayoutChanged: (layout) => {
78
- document.cookie = `react-resizable-panels:layout=${JSON.stringify(Object.values(layout))}`;
79
- }, orientation: "horizontal", className: "size-full", children: [_jsxs(ResizablePanel, { id: "navigation", defaultSize: "16%", collapsedSize: "4%", collapsible: true, minSize: "11%", maxSize: "18%", onResize: (size) => {
80
- const collapsed = size.asPercentage <= 4;
81
- setIsCollapsed(collapsed);
82
- document.cookie = `react-resizable-panels:collapsed=${JSON.stringify(collapsed)}`;
83
- }, className: cn(isCollapsed && 'min-w-[50px] transition-all duration-300 ease-in-out'), children: [_jsx("div", { className: cn('flex h-[72px] flex-col items-center justify-center gap-4 border-b bg-card/80'), children: _jsx("div", { className: `flex h-full items-center justify-center ${!isCollapsed && 'w-full'} cursor-pointer px-4 py-3`, onClick: () => navigate(defaultRoutePath, { viewTransition: true }), children: _jsx(BrandLogo, { isCollapsed: isCollapsed }) }) }), _jsxs("div", { className: "flex h-[calc(100vh-72px)] flex-col justify-between bg-card/70", children: [_jsx(Navigation, { isCollapsed: isCollapsed }), _jsx(NavigationFooter, { isCollapsed: isCollapsed })] })] }), _jsx(ResizableHandle, { withHandle: true }), _jsxs(ResizablePanel, { id: "content", children: [_jsxs("div", { className: "flex h-[64px] items-center border-b bg-background/95 px-3 backdrop-blur lg:h-[72px] lg:px-5", children: [_jsxs("div", { className: "flex flex-col items-start justify-center", children: [_jsx(Breadcrumb, { children: _jsx(BreadcrumbList, { children: crumbs.length ? (crumbs.map((c, i) => {
84
- linkPath.push(c);
85
- return (_jsxs(React.Fragment, { children: [_jsx(BreadcrumbItem, { children: _jsx(NavLink, { to: buildURL(linkPath), viewTransition: true, children: _jsx("p", { className: cn('text-sm font-semibold tracking-tight text-foreground capitalize'), children: i === 0 ? t('menu.' + dashToCamelCase(c)) : c }) }) }), i !== crumbs.length - 1 && (_jsx(BreadcrumbSeparator, { children: _jsx(Slash, { className: "text-muted-foreground" }) }))] }, c));
86
- })) : (_jsx(BreadcrumbItem, { children: _jsx(NavLink, { to: defaultRoutePath, viewTransition: true, children: _jsx("p", { className: "text-xl font-semibold tracking-tight text-foreground", children: t(defaultRouteMenuKey === 'dashboard'
87
- ? 'dashboard'
88
- : `menu.${dashToCamelCase(defaultRouteMenuKey)}`) }) }) })) }) }), _jsx("div", { className: "flex items-center gap-2" })] }), _jsxs("div", { className: "flex flex-1 items-center justify-end gap-1.5", children: [allowedTopNavigationComponents && allowedTopNavigationComponents.length > 0 ? (_jsx("div", { className: "flex items-center gap-2", children: allowedTopNavigationComponents.map(({ component: Component }, index) => (_jsx(Component, {}, index))) })) : null, languageSwitcherEnabled && _jsx(LanguagesDropdown, {}), channelSwitcherEnabled && _jsx(ChannelSwitcher, { className: "min-w-44" }), globalSearchEnabled && (_jsx(Button, { onClick: openGlobalSearch, variant: "outline", size: "icon", className: "relative size-9", children: _jsx(SearchIcon, { className: "size-4" }) })), notificationsEnabled && _jsx(Notifications, {}), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "icon", className: "size-9", children: [theme === 'light' ? (_jsx(Sun, { className: "size-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" })) : theme === 'dark' ? (_jsx(Moon, { className: "absolute size-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" })) : (_jsx(SunMoon, { className: "absolute size-[1.2rem] rotate-90 transition-all" })), _jsx("span", { className: "sr-only", children: t('toggleTheme') })] }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsx(DropdownMenuItem, { onClick: () => setTheme('light'), children: t('themeLight') }), _jsx(DropdownMenuItem, { onClick: () => setTheme('dark'), children: t('themeDark') }), _jsx(DropdownMenuItem, { onClick: () => setTheme('system'), children: t('themeSystem') })] })] }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "outline", size: "icon", className: "size-9", children: _jsx(MenuIcon, { className: "size-4" }) }) }), _jsxs(DropdownMenuContent, { className: "z-[150] mr-6 min-w-40", children: [activeAdministrator?.emailAddress && (_jsxs(_Fragment, { children: [_jsxs(DropdownMenuLabel, { className: "flex items-center gap-2 px-3 py-2 font-medium", children: [_jsx("div", { className: "flex size-6 items-center justify-center bg-primary/10 text-xs font-semibold text-primary", children: activeAdministrator.firstName.charAt(0).toUpperCase() }), _jsxs("div", { className: "truncate text-sm", children: [activeAdministrator.firstName, " ", activeAdministrator.lastName] })] }), _jsx(DropdownMenuSeparator, { className: "my-1" })] })), reindexEnabled && (_jsxs(DropdownMenuItem, { className: "flex cursor-pointer items-center gap-2 text-nowrap", onSelect: rebuildSearchIndex, children: [_jsx(RotateCwSquare, { className: "size-4" }), t('rebuildSerachIndex')] })), reindexEnabled && fastLinks.length > 0 && _jsx(DropdownMenuSeparator, {}), fastLinks.length > 0 && (_jsx(DropdownMenuGroup, { children: _jsxs(DropdownMenuSub, { children: [_jsx(DropdownMenuSubTrigger, { children: t('fastLinks') }), _jsx(DropdownMenuPortal, { children: _jsx(DropdownMenuSubContent, { children: fastLinks.map((link, index) => (_jsxs(React.Fragment, { children: [index > 0 && _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { className: "flex cursor-pointer items-center gap-2 text-nowrap", onSelect: () => navigate(link.path, { viewTransition: true }), children: [_jsx(RotateCwSquare, { className: "size-4" }), link.label] })] }, link.key))) }) })] }) })), allowedTopNavigationActions?.length && allowedTopNavigationActions.length > 0 ? (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), allowedTopNavigationActions.map((action) => (_jsxs(DropdownMenuItem, { className: "flex cursor-pointer items-center gap-2", onSelect: action.onClick, children: [action.icon && _jsx(action.icon, { className: "size-4" }), action.label] }, action.label)))] })) : null, _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { className: "flex cursor-pointer items-center gap-2 text-red-500", onSelect: async () => {
89
- const result = await createDialog({
90
- title: t('logOutConfirmation'),
91
- description: t('logOutConfirmationDescription'),
92
- buttons: [
93
- { label: t('cancel'), variant: 'secondary', returnValue: false },
94
- { label: t('logOut'), variant: 'destructive', returnValue: true },
95
- ],
96
- });
97
- if (result) {
98
- clearAdministratorAccess();
99
- logOut();
100
- }
101
- }, children: [_jsx(LogOutIcon, { className: "size-4" }), t('logOut')] })] })] })] })] }), _jsx(ScrollArea, { className: "relative h-[calc(100vh-64px)] overflow-y-hidden lg:h-[calc(100vh-72px)]", children: children })] })] }) }) }) }) }));
33
+ const navigateHome = () => navigate(defaultRoutePath, { viewTransition: true });
34
+ return (_jsx(TooltipProvider, { delayDuration: 100, children: _jsxs("div", { className: "flex h-screen w-full overflow-hidden bg-[var(--sidebar-canvas)]", children: [_jsx("aside", { "data-collapsed": isCollapsed, className: cn('hidden h-full shrink-0 border-r border-[var(--sidebar-hairline)] bg-[var(--sidebar-canvas)] transition-[width] duration-300 ease-out motion-reduce:transition-none md:block', isCollapsed ? 'w-12' : 'w-64'), "aria-label": t('mainNavigation'), children: _jsx(SidebarContent, { isCollapsed: isCollapsed, manuallyOpenGroupIds: manuallyOpenGroupIds, mode: "desktop", onExpand: () => setIsCollapsed(false), onOpenGroupIdsChange: setManuallyOpenGroupIds, onNavigate: () => undefined, onNavigateHome: navigateHome }) }), _jsx(Sheet, { open: isMobileOpen, onOpenChange: setIsMobileOpen, children: _jsxs(SheetContent, { side: "left", className: "w-72 max-w-[calc(100vw-1rem)] border-[var(--sidebar-hairline)] bg-[var(--sidebar-canvas)] p-0 sm:max-w-72 [&>button]:rounded-[4px] [&>button]:text-[var(--sidebar-secondary)] [&>button]:focus:ring-[var(--sidebar-focus)] [&>button]:data-[state=open]:bg-[var(--sidebar-hover)]", children: [_jsx(SheetTitle, { className: "sr-only", children: t('mainNavigation') }), _jsx(SheetDescription, { className: "sr-only", children: t('mobileNavigationDescription') }), _jsx(SidebarContent, { isCollapsed: false, manuallyOpenGroupIds: manuallyOpenGroupIds, mode: "mobile", onExpand: () => undefined, onOpenGroupIdsChange: setManuallyOpenGroupIds, onNavigate: () => setIsMobileOpen(false), onNavigateHome: () => {
35
+ navigateHome();
36
+ setIsMobileOpen(false);
37
+ } })] }) }), _jsxs("main", { className: "flex min-w-0 flex-1 flex-col bg-[var(--sidebar-canvas)]", children: [_jsxs("header", { className: "flex h-16 shrink-0 items-center gap-2 border-b border-[var(--sidebar-hairline)] bg-[var(--sidebar-canvas)] px-3 lg:h-[72px] lg:px-5", children: [_jsx(Button, { variant: "outline", size: "icon", className: "size-9 shrink-0 md:hidden", onClick: () => setIsMobileOpen(true), "aria-label": t('openSidebar'), title: t('openSidebar'), children: _jsx(MenuIcon, { className: "size-4" }) }), _jsx(Button, { variant: "outline", size: "icon", className: "hidden size-9 shrink-0 md:inline-flex", onClick: toggleDesktop, "aria-label": isCollapsed ? t('expandSidebar') : t('collapseSidebar'), title: `${isCollapsed ? t('expandSidebar') : t('collapseSidebar')} (${t('sidebarShortcut')})`, children: isCollapsed ? _jsx(PanelLeftOpen, { className: "size-4" }) : _jsx(PanelLeftClose, { className: "size-4" }) }), _jsx("div", { className: "hidden min-w-0 flex-col items-start justify-center sm:flex", children: _jsx(Breadcrumb, { children: _jsx(BreadcrumbList, { className: "flex-nowrap overflow-hidden", children: crumbs.length ? (crumbs.map((crumb, index) => {
38
+ const path = crumbs.slice(0, index + 1);
39
+ return (_jsxs(React.Fragment, { children: [_jsx(BreadcrumbItem, { className: "min-w-0", children: _jsx(NavLink, { to: buildURL(path), viewTransition: true, className: "truncate", children: _jsx("span", { className: "text-sm font-semibold tracking-tight text-foreground", children: index === 0 ? t(`menu.${dashToCamelCase(crumb)}`) : crumb }) }) }), index !== crumbs.length - 1 && (_jsx(BreadcrumbSeparator, { children: _jsx(Slash, { className: "text-muted-foreground" }) }))] }, `${crumb}-${index}`));
40
+ })) : (_jsx(BreadcrumbItem, { children: _jsx(NavLink, { to: defaultRoutePath, viewTransition: true, children: _jsx("span", { className: "text-lg font-semibold tracking-tight text-foreground", children: t(defaultRouteMenuKey === 'dashboard'
41
+ ? 'dashboard'
42
+ : `menu.${dashToCamelCase(defaultRouteMenuKey)}`) }) }) })) }) }) }), _jsxs("div", { className: "ml-auto flex min-w-0 items-center justify-end gap-1.5", children: [_jsx(TopbarOverflow, { components: allowedTopNavigationComponents || [] }), allowedTopNavigationComponents?.length ? (_jsx("div", { className: "hidden items-center gap-2 lg:flex", children: allowedTopNavigationComponents.map(({ component: Component }, index) => (_jsx(Component, {}, index))) })) : null, _jsxs("div", { className: "hidden items-center gap-1.5 lg:flex", children: [_jsx(LanguagesDropdown, {}), _jsx(ChannelSwitcher, { className: "min-w-44" })] }), _jsx(Button, { onClick: openGlobalSearch, variant: "outline", size: "icon", className: "relative size-9 shrink-0", "aria-label": t('openGlobalSearch'), title: t('openGlobalSearch'), children: _jsx(SearchIcon, { className: "size-4" }) }), _jsx(Notifications, {}), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "icon", className: "relative size-9 shrink-0", children: [theme === 'light' ? (_jsx(Sun, { className: "size-[1.2rem]" })) : theme === 'dark' ? (_jsx(Moon, { className: "size-[1.2rem]" })) : (_jsx(SunMoon, { className: "size-[1.2rem]" })), _jsx("span", { className: "sr-only", children: t('toggleTheme') })] }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsx(DropdownMenuItem, { onClick: () => setTheme('light'), children: t('themeLight') }), _jsx(DropdownMenuItem, { onClick: () => setTheme('dark'), children: t('themeDark') }), _jsx(DropdownMenuItem, { onClick: () => setTheme('system'), children: t('themeSystem') })] })] })] })] }), _jsx(ScrollArea, { className: "relative min-h-0 flex-1 overflow-y-hidden bg-[var(--sidebar-canvas)]", children: children })] })] }) }));
102
43
  };
package/dist/index.css CHANGED
@@ -59,6 +59,18 @@
59
59
 
60
60
  --navigation-link: hsl(228 14% 28%);
61
61
 
62
+ --sidebar-canvas: #fbfbf5;
63
+ --sidebar-surface: #ffffff;
64
+ --sidebar-ink: #000000;
65
+ --sidebar-secondary: #71717a;
66
+ --sidebar-tertiary: #52525b;
67
+ --sidebar-hairline: #e4e4e7;
68
+ --sidebar-hover: #ffffff;
69
+ --sidebar-active: #d4f9e0;
70
+ --sidebar-active-ink: #000000;
71
+ --sidebar-active-indicator: #000000;
72
+ --sidebar-focus: #000000;
73
+
62
74
  --topbar-height: 64px;
63
75
  --topbar-height-lg: 72px;
64
76
 
@@ -104,6 +116,18 @@
104
116
  --warning-foreground: hsl(38 92% 50%);
105
117
 
106
118
  --navigation-link: hsl(220 18% 86%);
119
+
120
+ --sidebar-canvas: #000000;
121
+ --sidebar-surface: #0a0a0a;
122
+ --sidebar-ink: #ffffff;
123
+ --sidebar-secondary: #a1a1aa;
124
+ --sidebar-tertiary: #9dabad;
125
+ --sidebar-hairline: #1e2c31;
126
+ --sidebar-hover: #0a0a0a;
127
+ --sidebar-active: #1e2c31;
128
+ --sidebar-active-ink: #ffffff;
129
+ --sidebar-active-indicator: #ffffff;
130
+ --sidebar-focus: #bdbdca;
107
131
  }
108
132
 
109
133
  @theme inline {
@@ -236,6 +260,47 @@ a {
236
260
  text-decoration: none;
237
261
  }
238
262
 
263
+ .deenruv-sidebar a.sidebar-link {
264
+ display: block;
265
+ }
266
+
267
+ .deenruv-sidebar .sidebar-row {
268
+ display: flex;
269
+ height: 36px;
270
+ min-height: 36px;
271
+ align-items: center;
272
+ gap: 8px;
273
+ padding: 0 8px;
274
+ border-radius: 4px;
275
+ color: var(--sidebar-secondary);
276
+ font-size: 14px;
277
+ font-weight: 500;
278
+ line-height: 20px;
279
+ transition:
280
+ color 150ms ease,
281
+ background-color 150ms ease;
282
+ }
283
+
284
+ .deenruv-sidebar .sidebar-row:hover {
285
+ background-color: var(--sidebar-hover);
286
+ color: var(--sidebar-ink);
287
+ }
288
+
289
+ .deenruv-sidebar .sidebar-row[data-active="true"] {
290
+ background-color: var(--sidebar-active);
291
+ color: var(--sidebar-active-ink);
292
+ box-shadow: inset 2px 0 0 var(--sidebar-active-indicator);
293
+ }
294
+
295
+ .deenruv-sidebar .sidebar-icon-slot {
296
+ display: flex;
297
+ width: 20px;
298
+ height: 20px;
299
+ flex: 0 0 20px;
300
+ align-items: center;
301
+ justify-content: center;
302
+ }
303
+
239
304
  .Puck > div:nth-child(1) {
240
305
  position: relative !important;
241
306
  }
@@ -476,6 +476,19 @@
476
476
  "edit": "Edit",
477
477
  "rebuildSerachIndex": "Rebuild search index",
478
478
  "fastLinks": "Quick links",
479
+ "account": "Account",
480
+ "openAccountMenu": "Open account menu",
481
+ "mainNavigation": "Main navigation",
482
+ "mobileNavigationDescription": "Navigate to an administration area.",
483
+ "openSidebar": "Open navigation",
484
+ "expandSidebar": "Expand navigation",
485
+ "collapseSidebar": "Collapse navigation",
486
+ "sidebarShortcut": "Ctrl or Command + B",
487
+ "expandNavigationGroup": "Expand navigation and open {{group}}",
488
+ "goToDashboard": "Go to dashboard",
489
+ "openGlobalSearch": "Open global search",
490
+ "openTopbarControls": "Open language, channel and plugin controls",
491
+ "versionAbbreviation": "version",
479
492
  "systemStatus": "System status",
480
493
  "globalSettings": "Global settings",
481
494
  "delete": "Delete",
@@ -568,34 +581,34 @@
568
581
  "options": "Options",
569
582
  "variants": "Variants",
570
583
  "adminUiV2": "Admin UI",
571
- "extensions": "extensions",
572
- "dashboard": "desktop",
573
- "status": "status",
574
- "products": "products",
575
- "customers": "customers",
576
- "customerGroups": "customer groups",
577
- "productVariants": "product variants",
578
- "collections": "collections",
579
- "orders": "orders",
580
- "facets": "aspects of",
581
- "channels": "channels",
582
- "globalSettings": "global settings",
583
- "roles": "roles",
584
- "admins": "administrators",
585
- "adminProvision": "administrator provisioning",
586
- "stock": "storage locations",
587
- "sellers": "retailers",
588
- "assets": "assets",
589
- "zones": "zones",
590
- "promotions": "promotions",
591
- "countries": "countries",
592
- "paymentMethods": "payment methods",
593
- "shippingMethods": "shipping methods",
594
- "stockLocations": "warehouse locations",
595
- "taxCategories": "tax categories",
596
- "taxRates": "tax rates",
597
- "logOut": "logout",
598
- "systemStatus": "system status"
584
+ "extensions": "Extensions",
585
+ "dashboard": "Dashboard",
586
+ "status": "Status",
587
+ "products": "Products",
588
+ "customers": "Customers",
589
+ "customerGroups": "Customer groups",
590
+ "productVariants": "Product variants",
591
+ "collections": "Collections",
592
+ "orders": "Orders",
593
+ "facets": "Facets",
594
+ "channels": "Channels",
595
+ "globalSettings": "Global settings",
596
+ "roles": "Roles",
597
+ "admins": "Administrators",
598
+ "adminProvision": "Administrator provisioning",
599
+ "stock": "Storage locations",
600
+ "sellers": "Sellers",
601
+ "assets": "Assets",
602
+ "zones": "Zones",
603
+ "promotions": "Promotions",
604
+ "countries": "Countries",
605
+ "paymentMethods": "Payment methods",
606
+ "shippingMethods": "Shipping methods",
607
+ "stockLocations": "Warehouse locations",
608
+ "taxCategories": "Tax categories",
609
+ "taxRates": "Tax rates",
610
+ "logOut": "Log out",
611
+ "systemStatus": "System status"
599
612
  },
600
613
  "noFilterField": "None",
601
614
  "draft": "Project",
@@ -475,6 +475,19 @@
475
475
  "edit": "Edytuj",
476
476
  "rebuildSerachIndex": "Przebuduj search index",
477
477
  "fastLinks": "Szybkie linki",
478
+ "account": "Konto",
479
+ "openAccountMenu": "Otwórz menu konta",
480
+ "mainNavigation": "Główna nawigacja",
481
+ "mobileNavigationDescription": "Przejdź do wybranego obszaru administracyjnego.",
482
+ "openSidebar": "Otwórz nawigację",
483
+ "expandSidebar": "Rozwiń nawigację",
484
+ "collapseSidebar": "Zwiń nawigację",
485
+ "sidebarShortcut": "Ctrl lub Command + B",
486
+ "expandNavigationGroup": "Rozwiń nawigację i otwórz grupę {{group}}",
487
+ "goToDashboard": "Przejdź do pulpitu",
488
+ "openGlobalSearch": "Otwórz wyszukiwanie globalne",
489
+ "openTopbarControls": "Otwórz ustawienia języka, kanału i wtyczek",
490
+ "versionAbbreviation": "wersja",
478
491
  "systemStatus": "Status systemu",
479
492
  "globalSettings": "Ustawienia globalne",
480
493
  "delete": "Usuń",
@@ -567,34 +580,34 @@
567
580
  "options": "Opcje produktu",
568
581
  "variants": "Warianty",
569
582
  "adminUiV2": "Admin UI",
570
- "extensions": "rozszerzenia",
571
- "dashboard": "pulpit",
572
- "status": "status",
573
- "products": "produkty",
574
- "customers": "klienci",
575
- "customerGroups": "grupy klientów",
576
- "productVariants": "warianty produktów",
577
- "collections": "kolekcje",
578
- "orders": "zamówienia",
579
- "facets": "aspekty",
580
- "channels": "kanały",
581
- "globalSettings": "ustawienia globalne",
582
- "roles": "role",
583
- "admins": "administratorzy",
584
- "adminProvision": "zakładanie administratora",
585
- "stock": "lokalizacje magazynowe",
586
- "sellers": "sprzedawcy",
587
- "assets": "aktywa",
588
- "zones": "strefy",
589
- "promotions": "promocje",
590
- "countries": "kraje",
591
- "paymentMethods": "metody płatności",
592
- "shippingMethods": "metody wysyłki",
593
- "stockLocations": "lokacje magazynów",
594
- "taxCategories": "kategorie podatkowe",
595
- "taxRates": "stawki podatkowe",
596
- "logOut": "wylogowanie",
597
- "systemStatus": "status systemu"
583
+ "extensions": "Rozszerzenia",
584
+ "dashboard": "Pulpit",
585
+ "status": "Status",
586
+ "products": "Produkty",
587
+ "customers": "Klienci",
588
+ "customerGroups": "Grupy klientów",
589
+ "productVariants": "Warianty produktów",
590
+ "collections": "Kolekcje",
591
+ "orders": "Zamówienia",
592
+ "facets": "Aspekty",
593
+ "channels": "Kanały",
594
+ "globalSettings": "Ustawienia globalne",
595
+ "roles": "Role",
596
+ "admins": "Administratorzy",
597
+ "adminProvision": "Zakładanie administratora",
598
+ "stock": "Lokalizacje magazynowe",
599
+ "sellers": "Sprzedawcy",
600
+ "assets": "Aktywa",
601
+ "zones": "Strefy",
602
+ "promotions": "Promocje",
603
+ "countries": "Kraje",
604
+ "paymentMethods": "Metody płatności",
605
+ "shippingMethods": "Metody wysyłki",
606
+ "stockLocations": "Lokacje magazynów",
607
+ "taxCategories": "Kategorie podatkowe",
608
+ "taxRates": "Stawki podatkowe",
609
+ "logOut": "Wylogowanie",
610
+ "systemStatus": "Status systemu"
598
611
  },
599
612
  "noFilterField": "Brak",
600
613
  "draft": "Projekt",
@@ -652,7 +665,7 @@
652
665
  "notifications": "Powiadomienia",
653
666
  "noNewNotifications": "Brak nowych powiadomień",
654
667
  "toggleNotifications": "Przełącz powiadomienia",
655
- "dashboard": "Pulpit nawigacyjny",
668
+ "dashboard": "Pulpit",
656
669
  "search": {
657
670
  "advanceToggle": "Wyszukiwanie zaawansowane",
658
671
  "basicToggle": "Wyszukiwanie podstawowe",
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const ADMIN_DASHBOARD_VERSION = '1.0.17-dev.17';
1
+ export const ADMIN_DASHBOARD_VERSION = '1.0.17-dev.20';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deenruv/admin-dashboard",
3
- "version": "1.0.17-dev.17",
3
+ "version": "1.0.17-dev.20",
4
4
  "main": "dist/index.js",
5
5
  "style": "dist/index.css",
6
6
  "types": "dist/index.d.ts",
@@ -80,10 +80,10 @@
80
80
  "yup": "^1.7.1",
81
81
  "zod": "^4.4.3",
82
82
  "zustand": "^5.0.14",
83
- "@deenruv/admin-dashboard": "1.0.17-dev.17",
84
- "@deenruv/admin-types": "1.0.17-dev.17",
85
- "@deenruv/deenruv-examples-plugin": "1.0.17-dev.17",
86
- "@deenruv/react-ui-devkit": "1.0.17-dev.17"
83
+ "@deenruv/admin-dashboard": "1.0.17-dev.20",
84
+ "@deenruv/admin-types": "1.0.17-dev.20",
85
+ "@deenruv/deenruv-examples-plugin": "1.0.17-dev.20",
86
+ "@deenruv/react-ui-devkit": "1.0.17-dev.20"
87
87
  },
88
88
  "devDependencies": {
89
89
  "@tailwindcss/typography": "^0.5.20",