mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 16:13:15 +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.3 KiB
TypeScript
33 lines
1.3 KiB
TypeScript
'use client';
|
|
|
|
import type { AnchorHTMLAttributes } from 'react';
|
|
import { cn } from '@/lib/utils';
|
|
import { AdminLink } from './admin-link';
|
|
|
|
const VARIANTS = {
|
|
default: 'bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm hover:shadow',
|
|
outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
|
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
|
} as const;
|
|
|
|
interface AdminButtonLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
href: string;
|
|
variant?: keyof typeof VARIANTS;
|
|
/** A link out of the panel (a hard navigation) rather than to another admin page. */
|
|
external?: boolean;
|
|
}
|
|
|
|
/** A link that looks like a small Button, for actions that open a page. */
|
|
export function AdminButtonLink({ href, variant = 'outline', external, className, children, ...rest }: AdminButtonLinkProps) {
|
|
const classes = cn(
|
|
'inline-flex h-9 items-center justify-center gap-2 rounded-md px-3 text-sm font-medium transition-all duration-200',
|
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',
|
|
VARIANTS[variant],
|
|
className,
|
|
);
|
|
if (external) {
|
|
return <a href={href} className={classes} {...rest}>{children}</a>;
|
|
}
|
|
return <AdminLink href={href} className={classes} {...rest}>{children}</AdminLink>;
|
|
}
|