From cb41d082be62031410ce18d7efe50204fcd106de Mon Sep 17 00:00:00 2001 From: Maurice Date: Sat, 15 Aug 2026 17:54:57 +0200 Subject: [PATCH] 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. --- client/src/pages/admin/useAdmin.ts | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/client/src/pages/admin/useAdmin.ts b/client/src/pages/admin/useAdmin.ts index 2f7cda5af..4b2d6d8dc 100644 --- a/client/src/pages/admin/useAdmin.ts +++ b/client/src/pages/admin/useAdmin.ts @@ -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('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(() => 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([]) const [stats, setStats] = useState(null) const [isLoading, setIsLoading] = useState(true)