/** * useTabRouter — centralized tab routing and state management. * * Encapsulates tab navigation logic, URL state synchronization, and history * management for the config page. Supports deep linking to specific tabs and * optional item IDs (e.g., jail names). */ import { useEffect, useState, useCallback } from "react"; import { useLocation, useNavigate } from "react-router-dom"; /** Available tab IDs in the config page. */ export type ConfigTabId = "jails" | "filters" | "actions" | "server" | "regex"; /** Tab navigation state with optional active item/jail name. */ export interface TabRouterState { activeTab: ConfigTabId; activeItem: string | null; } interface UseTabRouterReturn extends TabRouterState { selectTab: (tab: ConfigTabId) => void; selectTabWithItem: (tab: ConfigTabId, itemId: string | null) => void; } /** * Hook for managing config page tab routing. * * Syncs tab selection to URL state via `location.state` so it persists across * navigation. The activeItem (jail name, filter name, etc.) is also tracked * to support deep linking. * * @returns Tab state and navigation functions. */ export function useTabRouter(): UseTabRouterReturn { const location = useLocation(); const navigate = useNavigate(); const [activeTab, setActiveTab] = useState("jails"); const [activeItem, setActiveItem] = useState(null); // Sync from location.state on mount or when location changes. useEffect(() => { const state = location.state as { tab?: string; item?: string } | null; if (state?.tab === "jails" || state?.tab === "filters" || state?.tab === "actions" || state?.tab === "server" || state?.tab === "regex") { setActiveTab(state.tab); } if (state?.item) { setActiveItem(state.item); } }, [location.state]); const selectTab = useCallback( (tab: ConfigTabId): void => { setActiveTab(tab); setActiveItem(null); navigate(location.pathname, { state: { tab }, replace: false, }); }, [navigate, location.pathname], ); const selectTabWithItem = useCallback( (tab: ConfigTabId, itemId: string | null): void => { setActiveTab(tab); setActiveItem(itemId); navigate(location.pathname, { state: { tab, item: itemId }, replace: false, }); }, [navigate, location.pathname], ); return { activeTab, activeItem, selectTab, selectTabWithItem, }; }