Move Administration's section list into the folder pane
Administration's pages are tables, and the Settings-style second column took width they need. The list of sections -- Directory > Accounts, Mail > Domains -- now sits in the folder pane where Mail keeps its folders, and the page is the open section alone, up to 1120px wide. On a phone the list is in the drawer like every other section's, so a bare /admin opens the first section rather than a page that is only a list. The back link it needed is gone.
This commit is contained in:
@@ -1,57 +1,33 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { Link, Redirect, useLocation } from "wouter";
|
||||
import { ArrowLeft, Globe, User } from "lucide-react";
|
||||
import { Redirect } from "wouter";
|
||||
import { adminSections, type AdminSection } from "@/lib/adminAccess";
|
||||
import { t } from "@/lib/i18n";
|
||||
import { AccountsAdmin } from "./AccountsAdmin";
|
||||
import { DomainsAdmin } from "./DomainsAdmin";
|
||||
import { currentAdminSection } from "./AdminNav";
|
||||
import { usePermissions } from "./usePermissions";
|
||||
|
||||
const SECTIONS: Record<AdminSection, { group: string; label: string; icon: ReactNode; render: (id?: string) => ReactNode }> = {
|
||||
accounts: { group: "Directory", label: "Accounts", icon: <User size={18} />, render: (id) => <AccountsAdmin selectedId={id} /> },
|
||||
domains: { group: "Mail", label: "Domains", icon: <Globe size={18} />, render: (id) => <DomainsAdmin selectedId={id} /> },
|
||||
const RENDER: Record<AdminSection, (id?: string) => ReactNode> = {
|
||||
accounts: (id) => <AccountsAdmin selectedId={id} />,
|
||||
domains: (id) => <DomainsAdmin selectedId={id} />,
|
||||
};
|
||||
|
||||
/**
|
||||
* Administration: what the signed-in account's Stalwart role lets it manage.
|
||||
*
|
||||
* Laid out like Settings, because it is the same kind of place -- a list of
|
||||
* sections and the one that is open -- and on a phone it behaves the same way,
|
||||
* the list first and a section on its own. Only the sections the role can read
|
||||
* are listed; a section typed into the address bar that it cannot read opens
|
||||
* the first one it can.
|
||||
* The page is only the open section. Its list of sections is in the folder
|
||||
* pane (see AdminNav), so the tables here get the width Settings spends on a
|
||||
* second column. A section the role cannot read -- typed into the address bar,
|
||||
* say -- opens the first one it can.
|
||||
*/
|
||||
export function AdminView({ section, id }: { section?: string; id?: string }) {
|
||||
const [, navigate] = useLocation();
|
||||
const allowed = adminSections(usePermissions());
|
||||
// Typed in by hand, or a role taken away since the menu was drawn. Stalwart
|
||||
// would refuse every call anyway; this spares the page of refusals.
|
||||
// A role taken away since the menu was drawn. Stalwart would refuse every
|
||||
// call anyway; this spares the page of refusals.
|
||||
if (!allowed.length) return <Redirect to="/mail" />;
|
||||
const current = allowed.find((s) => s === section) ?? allowed[0]!;
|
||||
const groups = [...new Set(allowed.map((s) => SECTIONS[s].group))];
|
||||
const current = currentAdminSection(allowed, section)!;
|
||||
return (
|
||||
<div className={`settings-layout admin-layout ${section ? "section" : "root"}`}>
|
||||
<nav className="settings-nav" aria-label={t("Administration")}>
|
||||
{groups.map((group) => (
|
||||
<div key={group}>
|
||||
<div className="nav-section" style={{ paddingLeft: 8 }}><span>{t(group)}</span></div>
|
||||
{allowed.filter((s) => SECTIONS[s].group === group).map((s) => (
|
||||
<Link key={s} href={`/admin/${s}`} className={`nav-item ${current === s ? "active" : ""}`}>
|
||||
{SECTIONS[s].icon}
|
||||
<span className="nav-label">{t(SECTIONS[s].label)}</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
<div className="settings-content admin-content">
|
||||
{section && (
|
||||
<button className="btn btn-ghost btn-sm admin-back" style={{ marginBottom: 8, marginLeft: -8 }} onClick={() => navigate("/admin")}>
|
||||
<ArrowLeft size={16} /> {t("Administration")}
|
||||
</button>
|
||||
)}
|
||||
{SECTIONS[current].render(section === current ? id : undefined)}
|
||||
</div>
|
||||
<div className="admin-layout">
|
||||
<div className="settings-content admin-content">{RENDER[current](section === current ? id : undefined)}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user