fix(sidebar): hide outbox/audit/webhooks для PUBLIC users

This commit is contained in:
Александр Зимин
2026-05-15 17:05:51 +00:00
parent 836bc6a4e0
commit df704c28a6
@@ -63,6 +63,13 @@ type NavItem = {
* (или {@code ordinis:admin} super-role). Hide если нет. * (или {@code ordinis:admin} super-role). Hide если нет.
*/ */
reviewerOnly?: boolean reviewerOnly?: boolean
/**
* Admin-only nav entries (outbox/audit/webhooks). Backend возвращает 403
* audit_internal_required / outbox_internal_required для PUBLIC-scope users.
* Frontend hide'ит вместо показа links → 403 toast (confusing UX).
* Hide когда {@link useHasInternalScope} returns false.
*/
internalOnly?: boolean
} }
export function Sidebar() { export function Sidebar() {
@@ -138,6 +145,7 @@ export function Sidebar() {
icon: Inbox, icon: Inbox,
badge: outboxPending > 0 ? outboxPending : undefined, badge: outboxPending > 0 ? outboxPending : undefined,
authRequired: true, authRequired: true,
internalOnly: true,
}, },
{ {
to: '/webhooks', to: '/webhooks',
@@ -145,8 +153,13 @@ export function Sidebar() {
icon: Webhook, icon: Webhook,
badge: webhooksActive > 0 ? webhooksActive : undefined, badge: webhooksActive > 0 ? webhooksActive : undefined,
authRequired: true, authRequired: true,
internalOnly: true,
}, },
{ to: '/audit', label: t('nav.audit'), icon: ScrollText, authRequired: true }, // /audit, /outbox, /webhooks — admin observability tools. Backend
// возвращает 403 (audit_internal_required / outbox_internal_required)
// для PUBLIC-scope users. UI больше не показывает links — раньше
// PUBLIC users видели nav entries и кликали → 403 toast (confusing).
{ to: '/audit', label: t('nav.audit'), icon: ScrollText, authRequired: true, internalOnly: true },
{ to: '/search', label: t('nav.search'), icon: Search }, { to: '/search', label: t('nav.search'), icon: Search },
], ],
}, },
@@ -158,6 +171,7 @@ export function Sidebar() {
items: s.items.filter((i) => { items: s.items.filter((i) => {
if (i.authRequired && !canMutate) return false if (i.authRequired && !canMutate) return false
if (i.reviewerOnly && !isReviewer) return false if (i.reviewerOnly && !isReviewer) return false
if (i.internalOnly && !hasInternal) return false
return true return true
}), }),
})) }))
@@ -502,6 +516,7 @@ export function MobileSidebar({ open, onClose }: { open: boolean; onClose: () =>
icon: Inbox, icon: Inbox,
badge: outboxPending > 0 ? outboxPending : undefined, badge: outboxPending > 0 ? outboxPending : undefined,
authRequired: true, authRequired: true,
internalOnly: true,
}, },
{ {
to: '/webhooks', to: '/webhooks',
@@ -509,8 +524,9 @@ export function MobileSidebar({ open, onClose }: { open: boolean; onClose: () =>
icon: Webhook, icon: Webhook,
badge: webhooksActive > 0 ? webhooksActive : undefined, badge: webhooksActive > 0 ? webhooksActive : undefined,
authRequired: true, authRequired: true,
internalOnly: true,
}, },
{ to: '/audit', label: t('nav.audit'), icon: ScrollText, authRequired: true }, { to: '/audit', label: t('nav.audit'), icon: ScrollText, authRequired: true, internalOnly: true },
{ to: '/search', label: t('nav.search'), icon: Search }, { to: '/search', label: t('nav.search'), icon: Search },
], ],
}, },
@@ -521,6 +537,7 @@ export function MobileSidebar({ open, onClose }: { open: boolean; onClose: () =>
items: s.items.filter((i) => { items: s.items.filter((i) => {
if (i.authRequired && !canMutate) return false if (i.authRequired && !canMutate) return false
if (i.reviewerOnly && !isReviewer) return false if (i.reviewerOnly && !isReviewer) return false
if (i.internalOnly && !hasInternal) return false
return true return true
}), }),
})) }))