feat(admin): make an admin section linkable with ?tab=

The admin page always opened on Users, whatever the address said. A support
reply, an onboarding mail or a bookmark could point at the page but not at the
panel it was about, so every one of them ended with "then click X on the left".

Read once for the initial value and written back on each change, so the address
bar keeps saying where the reader is. Replaced rather than pushed: eleven tabs
would otherwise fill the back button with the history of a single page.
This commit is contained in:
Maurice
2026-08-15 17:54:57 +02:00
committed by Maurice
parent a6c27aac51
commit cb41d082be
+19 -3
View File
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react'
import { useNavigate } from 'react-router'
import { useCallback, useEffect, useState } from 'react'
import { useNavigate, useSearchParams } from 'react-router'
import apiClient, { adminApi, authApi } from '../../api/client'
import { useAuthStore } from '../../store/authStore'
import { useSettingsStore } from '../../store/settingsStore'
@@ -24,7 +24,23 @@ export function useAdmin() {
const devMode = useAuthStore(s => s.devMode)
const managed = useAuthStore(s => s.managed)
const [activeTab, setActiveTab] = useState<string>('users')
// ?tab= makes a section linkable: a support reply, an onboarding mail or a
// bookmark can point at the one panel it is about instead of at the top of a
// page with eleven of them. Read once for the initial value and written back
// on every change, so the address bar keeps saying where the reader is.
const [searchParams, setSearchParams] = useSearchParams()
const [activeTab, setActiveTabState] = useState<string>(() => searchParams.get('tab') || 'users')
const setActiveTab = useCallback((tab: string) => {
setActiveTabState(tab)
// replace, not push: eleven tabs would otherwise fill the back button with
// a history of a single page.
setSearchParams(prev => {
const next = new URLSearchParams(prev)
if (tab === 'users') next.delete('tab')
else next.set('tab', tab)
return next
}, { replace: true })
}, [setSearchParams])
const [users, setUsers] = useState<AdminUser[]>([])
const [stats, setStats] = useState<AdminStats | null>(null)
const [isLoading, setIsLoading] = useState<boolean>(true)