Code cleanup and deduplication
This commit is contained in:
62
src/layout/ModuleSubnav.tsx
Normal file
62
src/layout/ModuleSubnav.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
export type ModuleSubnavItem<T extends string> = {
|
||||
id: T;
|
||||
label: string;
|
||||
subtle?: boolean;
|
||||
primary?: boolean;
|
||||
};
|
||||
|
||||
export type ModuleSubnavAction = {
|
||||
actionId: string;
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
subtle?: boolean;
|
||||
primary?: boolean;
|
||||
};
|
||||
|
||||
export type ModuleSubnavEntry<T extends string> = ModuleSubnavItem<T> | ModuleSubnavAction;
|
||||
|
||||
export type ModuleSubnavGroup<T extends string> = {
|
||||
title?: string;
|
||||
items: ModuleSubnavEntry<T>[];
|
||||
};
|
||||
|
||||
function isAction<T extends string>(entry: ModuleSubnavEntry<T>): entry is ModuleSubnavAction {
|
||||
return "actionId" in entry;
|
||||
}
|
||||
|
||||
export default function ModuleSubnav<T extends string>({
|
||||
active,
|
||||
groups,
|
||||
onSelect,
|
||||
className = ""
|
||||
}: {
|
||||
active: T;
|
||||
groups: ModuleSubnavGroup<T>[];
|
||||
onSelect: (section: T) => void;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<aside className={`section-sidebar ${className}`.trim()}>
|
||||
{groups.map((group, groupIndex) => (
|
||||
<div className="section-group" key={`${group.title ?? "group"}-${groupIndex}`}>
|
||||
{group.title && <div className={`section-title ${groupIndex > 0 ? "section-title-lower" : ""}`.trim()}>{group.title}</div>}
|
||||
{group.items.map((entry) => {
|
||||
const key = isAction(entry) ? entry.actionId : entry.id;
|
||||
const activeClass = !isAction(entry) && active === entry.id ? " active" : "";
|
||||
const primaryClass = entry.primary ? " section-link-primary" : "";
|
||||
const subtleClass = entry.subtle ? " subtle" : "";
|
||||
return (
|
||||
<button
|
||||
key={key}
|
||||
className={`section-link${primaryClass}${subtleClass}${activeClass}`}
|
||||
onClick={() => (isAction(entry) ? entry.onClick() : onSelect(entry.id))}
|
||||
>
|
||||
{entry.label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
))}
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
@@ -1,21 +1,36 @@
|
||||
import type { CampaignWorkspaceSection } from "../types";
|
||||
import ModuleSubnav, { type ModuleSubnavGroup } from "./ModuleSubnav";
|
||||
|
||||
const campaignItems: { id: CampaignWorkspaceSection; label: string }[] = [
|
||||
{ id: "campaign", label: "General" },
|
||||
{ id: "fields", label: "Fields" },
|
||||
{ id: "files", label: "Attachments" },
|
||||
{ id: "recipients", label: "Recipients" },
|
||||
{ id: "recipient-data", label: "Recipient data" },
|
||||
{ id: "template", label: "Template" },
|
||||
];
|
||||
|
||||
const sendItems: { id: CampaignWorkspaceSection; label: string }[] = [
|
||||
{ id: "mail-settings", label: "Server settings" },
|
||||
{ id: "global-settings", label: "Global settings" },
|
||||
{ id: "review", label: "Review" },
|
||||
{ id: "send", label: "Send" },
|
||||
{ id: "report", label: "Report" },
|
||||
{ id: "audit", label: "Audit log" },
|
||||
const campaignSubnav: ModuleSubnavGroup<CampaignWorkspaceSection>[] = [
|
||||
{
|
||||
items: [{ id: "overview", label: "Overview", primary: true }]
|
||||
},
|
||||
{
|
||||
title: "CAMPAIGN",
|
||||
items: [
|
||||
{ id: "campaign", label: "General" },
|
||||
{ id: "fields", label: "Fields" },
|
||||
{ id: "files", label: "Attachments" },
|
||||
{ id: "recipients", label: "Recipients" },
|
||||
{ id: "recipient-data", label: "Recipient data" },
|
||||
{ id: "template", label: "Template" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "SEND CAMPAIGN",
|
||||
items: [
|
||||
{ id: "mail-settings", label: "Server settings" },
|
||||
{ id: "global-settings", label: "Global settings" },
|
||||
{ id: "review", label: "Review" },
|
||||
{ id: "send", label: "Send" },
|
||||
{ id: "report", label: "Report" },
|
||||
{ id: "audit", label: "Audit log" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "ADVANCED",
|
||||
items: [{ id: "json", label: "JSON", subtle: true }]
|
||||
}
|
||||
];
|
||||
|
||||
export default function SectionSidebar({
|
||||
@@ -25,26 +40,5 @@ export default function SectionSidebar({
|
||||
active: CampaignWorkspaceSection;
|
||||
onSelect: (section: CampaignWorkspaceSection) => void;
|
||||
}) {
|
||||
return (
|
||||
<aside className="section-sidebar">
|
||||
<button className={`section-link section-link-primary ${active === "overview" ? "active" : ""}`} onClick={() => onSelect("overview")}>Overview</button>
|
||||
|
||||
<div className="section-title section-title-lower">CAMPAIGN</div>
|
||||
{campaignItems.map((item) => (
|
||||
<button key={item.id} className={`section-link ${active === item.id ? "active" : ""}`} onClick={() => onSelect(item.id)}>
|
||||
{item.label}
|
||||
</button>
|
||||
))}
|
||||
|
||||
<div className="section-title section-title-lower">SEND CAMPAIGN</div>
|
||||
{sendItems.map((item) => (
|
||||
<button key={item.id} className={`section-link ${active === item.id ? "active" : ""}`} onClick={() => onSelect(item.id)}>
|
||||
{item.label}
|
||||
</button>
|
||||
))}
|
||||
|
||||
<div className="section-title section-title-lower">ADVANCED</div>
|
||||
<button className={`section-link subtle ${active === "json" ? "active" : ""}`} onClick={() => onSelect("json")}>JSON</button>
|
||||
</aside>
|
||||
);
|
||||
return <ModuleSubnav active={active} groups={campaignSubnav} onSelect={onSelect} />;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ type Props = {
|
||||
onAuthChange: (auth: AuthInfo | null, accessToken?: string) => void;
|
||||
};
|
||||
|
||||
export default function Titlebar({ settings, auth, onSettingsChange, onAuthChange }: Props) {
|
||||
export default function Titlebar({ settings, auth, onAuthChange }: Props) {
|
||||
const [accountOpen, setAccountOpen] = useState(false);
|
||||
const [tenantOpen, setTenantOpen] = useState(false);
|
||||
const [loginOpen, setLoginOpen] = useState(false);
|
||||
|
||||
Reference in New Issue
Block a user