mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 08:21:39 -06:00
fix(settings): restore magic links admin tab access
Expose magic links as its own admin tab and route it directly in Admin settings. Keep it superuser-only and cover tab visibility and routing behavior in tests.
This commit is contained in:
@@ -4,6 +4,7 @@ import { useRoute, useRouter } from 'vue-router'
|
||||
import SettingsPageHeader from './SettingsPageHeader.vue'
|
||||
import UsersPage from '@/features/admin/UsersPage.vue'
|
||||
import OidcSettings from './OidcSettings.vue'
|
||||
import MagicLinksSettings from './MagicLinksSettings.vue'
|
||||
import { usePermissions } from '@/features/auth/composables/usePermissions'
|
||||
import { ADMIN_TAB_INFO, ADMIN_TABS, normalizeAdminTab, type AdminTab as Tab } from './lib/admin-tabs'
|
||||
|
||||
@@ -47,6 +48,7 @@ watch(availableTabs, (tabs) => {
|
||||
|
||||
const tabWidths: Record<Tab, string> = {
|
||||
users: 'max-w-6xl',
|
||||
'magic-links': 'max-w-6xl',
|
||||
oidc: 'max-w-3xl',
|
||||
}
|
||||
|
||||
@@ -80,6 +82,7 @@ function selectTab(tab: Tab) {
|
||||
|
||||
<div :class="tabWidths[activeTab]">
|
||||
<UsersPage v-if="activeTab === 'users'" embedded />
|
||||
<MagicLinksSettings v-else-if="activeTab === 'magic-links'" :with-header="false" />
|
||||
<OidcSettings v-else-if="activeTab === 'oidc'" embedded />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -31,6 +31,7 @@ vi.mock('@/features/auth/composables/usePermissions', () => ({
|
||||
}))
|
||||
|
||||
vi.mock('@/features/admin/UsersPage.vue', () => ({ default: { template: '<div data-testid="users-page" />' } }))
|
||||
vi.mock('../MagicLinksSettings.vue', () => ({ default: { template: '<div data-testid="magic-links-settings" />' } }))
|
||||
vi.mock('../OidcSettings.vue', () => ({ default: { template: '<div data-testid="oidc-settings" />' } }))
|
||||
vi.mock('../SettingsPageHeader.vue', () => ({ default: { template: '<div />' } }))
|
||||
|
||||
@@ -48,10 +49,11 @@ describe('AdminAllSettings', () => {
|
||||
})
|
||||
|
||||
describe('superuser', () => {
|
||||
it('sees both Users and OIDC tabs', () => {
|
||||
it('sees Users, Magic Links, and OIDC tabs', () => {
|
||||
const wrapper = mountComponent(undefined, { su: true })
|
||||
const labels = wrapper.findAll('button').map((b) => b.text())
|
||||
expect(labels).toContain('Users')
|
||||
expect(labels).toContain('Magic Links')
|
||||
expect(labels).toContain('OIDC / SSO')
|
||||
})
|
||||
|
||||
@@ -70,6 +72,13 @@ describe('AdminAllSettings', () => {
|
||||
const wrapper = mountComponent('users', { su: true })
|
||||
expect(wrapper.find('[data-testid="users-page"]').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('renders MagicLinksSettings when tab=magic-links', () => {
|
||||
const wrapper = mountComponent('magic-links', { su: true })
|
||||
expect(wrapper.find('[data-testid="magic-links-settings"]').exists()).toBe(true)
|
||||
expect(wrapper.find('[data-testid="users-page"]').exists()).toBe(false)
|
||||
expect(wrapper.find('[data-testid="oidc-settings"]').exists()).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('user with manage_users only', () => {
|
||||
@@ -77,6 +86,7 @@ describe('AdminAllSettings', () => {
|
||||
const wrapper = mountComponent(undefined, { perms: ['manage_users'] })
|
||||
const labels = wrapper.findAll('button').map((b) => b.text())
|
||||
expect(labels).toContain('Users')
|
||||
expect(labels).not.toContain('Magic Links')
|
||||
expect(labels).not.toContain('OIDC / SSO')
|
||||
})
|
||||
|
||||
@@ -111,6 +121,7 @@ describe('AdminAllSettings', () => {
|
||||
const wrapper = mountComponent(undefined, { perms: ['manage_users', 'manage_app_settings'] })
|
||||
const labels = wrapper.findAll('button').map((b) => b.text())
|
||||
expect(labels).toContain('Users')
|
||||
expect(labels).not.toContain('Magic Links')
|
||||
expect(labels).toContain('OIDC / SSO')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,12 +3,12 @@ import { ADMIN_TABS, ADMIN_TAB_INFO, normalizeAdminTab } from '../lib/admin-tabs
|
||||
|
||||
describe('admin-tabs', () => {
|
||||
describe('ADMIN_TABS', () => {
|
||||
it('contains exactly users and oidc', () => {
|
||||
expect(ADMIN_TABS).toEqual(['users', 'oidc'])
|
||||
it('contains exactly users, magic-links, and oidc', () => {
|
||||
expect(ADMIN_TABS).toEqual(['users', 'magic-links', 'oidc'])
|
||||
})
|
||||
|
||||
it('has length 2', () => {
|
||||
expect(ADMIN_TABS.length).toBe(2)
|
||||
it('has length 3', () => {
|
||||
expect(ADMIN_TABS.length).toBe(3)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -40,11 +40,20 @@ describe('admin-tabs', () => {
|
||||
expect(ADMIN_TAB_INFO.oidc.permission).toBe('manage_app_settings')
|
||||
})
|
||||
|
||||
it('magic-links entry is superuser-only', () => {
|
||||
expect(ADMIN_TAB_INFO['magic-links'].permission).toBeNull()
|
||||
})
|
||||
|
||||
it('users entry has correct labels', () => {
|
||||
expect(ADMIN_TAB_INFO.users.navLabel).toBe('Users')
|
||||
expect(ADMIN_TAB_INFO.users.titleLabel).toBe('Users')
|
||||
})
|
||||
|
||||
it('magic-links entry has correct labels', () => {
|
||||
expect(ADMIN_TAB_INFO['magic-links'].navLabel).toBe('Magic Links')
|
||||
expect(ADMIN_TAB_INFO['magic-links'].titleLabel).toBe('Magic Links')
|
||||
})
|
||||
|
||||
it('oidc entry has correct labels', () => {
|
||||
expect(ADMIN_TAB_INFO.oidc.navLabel).toBe('OIDC / SSO')
|
||||
expect(ADMIN_TAB_INFO.oidc.titleLabel).toBe('OIDC / SSO')
|
||||
@@ -80,6 +89,10 @@ describe('admin-tabs', () => {
|
||||
expect(normalizeAdminTab('oidc')).toBe('oidc')
|
||||
})
|
||||
|
||||
it('returns magic-links when given "magic-links"', () => {
|
||||
expect(normalizeAdminTab('magic-links')).toBe('magic-links')
|
||||
})
|
||||
|
||||
it('is case-sensitive (Users is not valid)', () => {
|
||||
expect(normalizeAdminTab('Users')).toBe('users')
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export const ADMIN_TABS = ['users', 'oidc'] as const
|
||||
export const ADMIN_TABS = ['users', 'magic-links', 'oidc'] as const
|
||||
|
||||
export type AdminTab = (typeof ADMIN_TABS)[number]
|
||||
|
||||
@@ -16,6 +16,12 @@ export const ADMIN_TAB_INFO: Record<AdminTab, AdminTabInfo> = {
|
||||
subtitle: 'Manage user accounts and permission assignments.',
|
||||
permission: 'manage_users',
|
||||
},
|
||||
'magic-links': {
|
||||
navLabel: 'Magic Links',
|
||||
titleLabel: 'Magic Links',
|
||||
subtitle: 'Create shareable login links for shared accounts.',
|
||||
permission: null,
|
||||
},
|
||||
oidc: {
|
||||
navLabel: 'OIDC / SSO',
|
||||
titleLabel: 'OIDC / SSO',
|
||||
|
||||
@@ -215,7 +215,7 @@ export const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: 'admin/magic-links',
|
||||
name: 'settings-admin-magic-links',
|
||||
redirect: () => ({ name: 'settings-admin', query: { tab: 'users' } }),
|
||||
redirect: () => ({ name: 'settings-admin', query: { tab: 'magic-links' } }),
|
||||
},
|
||||
{ path: ':pathMatch(.*)*', redirect: { name: 'settings-libraries' } },
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user