mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 08:13:50 +00:00
The old panel kept its active tab in a store, so every page sat at /admin, the sidebar duplicated the webmail rail by hand, and each page was a stack of boxed cards with their own save buttons. Plugin settings were only reachable through a gear icon in a row of five. - one catch-all route for the whole panel; pages switch through history.pushState, so `/admin/plugins/<id>`, the back button, bookmarks and connector links all work without a route transition - sidebar laid out like the user settings: back to mail, search over pages and their sections, two group headings, change password and sign out at the bottom; the phone shows a list, then the page - pages use SettingsSection rows instead of nested cards, with one page header, one flash message and one save bar at the bottom (Discard, Save, Ctrl+S); the unsaved-changes prompt still guards leaving - plugins and themes are lists of rows with a toggle each; a row opens the extension's own page with its settings, force-on, permissions, kept versions and removal - overview shows stat tiles, server facts and recent activity - old `/admin?tab=x` links redirect; the tab store is gone and the connector registry resolves admin links to the page URL - `NEXT_TURBOPACK_ROOT` overrides the Turbopack root so `next dev` runs from a worktree with a junctioned node_modules
33 lines
1.4 KiB
TypeScript
33 lines
1.4 KiB
TypeScript
'use client';
|
|
|
|
import { useMemo } from 'react';
|
|
import { usePathname } from 'next/navigation';
|
|
import { withBasePath } from '@/lib/browser-navigation';
|
|
import { parseAdminPath, type AdminLocation } from '@/lib/admin/admin-nav';
|
|
|
|
/**
|
|
* Moves the admin panel to another of its pages without a route transition.
|
|
*
|
|
* The admin tree is one Next route, so a page switch is a history entry and
|
|
* nothing else: Next patches `history.pushState`, copies its own bookkeeping
|
|
* into the entry when the state passed is null, and updates `usePathname()`.
|
|
* Passing the current `history.state` instead would skip that sync, so the
|
|
* state here is always null. The back button lands on the previous entry
|
|
* the same way (Next's popstate handler restores the URL from the entry).
|
|
*/
|
|
export function navigateAdmin(path: string, options: { replace?: boolean } = {}): void {
|
|
if (typeof window === 'undefined') return;
|
|
const url = withBasePath(path);
|
|
if (`${window.location.pathname}${window.location.search}` === url && !options.replace) return;
|
|
if (options.replace) window.history.replaceState(null, '', url);
|
|
else window.history.pushState(null, '', url);
|
|
}
|
|
|
|
const INDEX: AdminLocation = { page: null, detail: null };
|
|
|
|
/** The page (and detail) the address bar names right now. */
|
|
export function useAdminLocation(): AdminLocation {
|
|
const pathname = usePathname();
|
|
return useMemo(() => parseAdminPath(pathname ?? '') ?? INDEX, [pathname]);
|
|
}
|