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
24 lines
856 B
TypeScript
24 lines
856 B
TypeScript
import type { ReactNode } from 'react';
|
|
import type { AppIcon } from '@/components/icons';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
interface EmptyStateProps {
|
|
icon?: AppIcon;
|
|
title: ReactNode;
|
|
hint?: ReactNode;
|
|
action?: ReactNode;
|
|
className?: string;
|
|
}
|
|
|
|
/** A list with nothing in it, with what to do about that. */
|
|
export function EmptyState({ icon: Icon, title, hint, action, className }: EmptyStateProps) {
|
|
return (
|
|
<div className={cn('rounded-lg border border-dashed border-border px-6 py-10 text-center', className)}>
|
|
{Icon && <Icon className="mx-auto mb-3 h-8 w-8 text-muted-foreground/50" />}
|
|
<p className="text-sm font-medium text-foreground">{title}</p>
|
|
{hint && <p className="mt-1 text-xs text-muted-foreground">{hint}</p>}
|
|
{action && <div className="mt-4 flex justify-center">{action}</div>}
|
|
</div>
|
|
);
|
|
}
|