fix: settings page redesign - tabbed interface, no sidebar dropdown

- AdminSidebar: removed dropdown menu, flat nav links, no scrolling
  - Removed SETTINGS_SUB_LINKS array
  - Removed settingsOpen state and dropdown logic
  - Settings is now a simple link to /admin/settings
  - Nav uses overflow-hidden to prevent sidebar scroll

- SettingsClient: unified tabbed interface (like CommunicationsPage)
  - New tabs: General, Add-ons, Billing, Integrations, AI, Advanced
  - Uses AdminFilterTabs for navigation
  - Each tab shows a card with link to full settings page

- Redirected pages to /admin/settings#tab:
  - /admin/settings/apps → /admin/settings#addons
  - /admin/settings/integrations → /admin/settings#integrations
  - /admin/settings/ai → /admin/settings#ai
  - /admin/advanced → /admin/settings#advanced

All TypeScript checks pass.
This commit is contained in:
2026-06-02 04:42:32 +00:00
parent 778b3fe311
commit 14ec2f9c71
6 changed files with 149 additions and 594 deletions
+2 -89
View File
@@ -21,21 +21,6 @@ const TOP_LINKS = [
{ href: "/admin/advanced", label: "Advanced", icon: "advanced" },
];
type SettingsItem = {
href: string;
label: string;
description: string;
};
const SETTINGS_SUB_LINKS: SettingsItem[] = [
{ href: "/admin/settings", label: "General", description: "Brand name, logo, timezone" },
{ href: "/admin/settings/apps", label: "Add-ons", description: "Enable and manage feature add-ons" },
{ href: "/admin/settings/billing", label: "Billing & Plans", description: "Subscription, invoices, usage" },
{ href: "/admin/settings/payments", label: "Payments", description: "Stripe, Square, payment methods" },
{ href: "/admin/communications/abandoned-carts", label: "Abandoned Cart Recovery", description: "3-email recovery sequence" },
{ href: "/admin/communications/welcome-sequence", label: "Welcome Sequence", description: "4-email onboarding for new subscribers" },
];
function GridIcon({ className }: { className?: string }) {
return (
<svg className={className ?? "w-4 h-4"} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
@@ -145,7 +130,6 @@ export default function AdminSidebar({ userRole }: SidebarProps) {
const pathname = usePathname();
const router = useRouter();
const [mobileOpen, setMobileOpen] = useState(false);
const [settingsOpen, setSettingsOpen] = useState(false);
const roleLabel = userRole === "platform_admin" ? "Platform Admin"
: userRole === "brand_admin" ? "Brand Admin"
@@ -154,23 +138,9 @@ export default function AdminSidebar({ userRole }: SidebarProps) {
const isActive = (href: string) => {
if (href === "/admin") return pathname === "/admin";
if (href === "/admin/settings") return pathname === "/admin/settings" || pathname.startsWith("/admin/settings");
return pathname.startsWith(href);
};
// Check if we're on a settings-related page
const isSettingsPage = pathname.startsWith("/admin/settings") ||
pathname.startsWith("/admin/users") ||
pathname.startsWith("/admin/communications/abandoned-carts") ||
pathname.startsWith("/admin/communications/welcome-sequence");
// Auto-expand settings menu when navigating to a settings page
useEffect(() => {
if (isSettingsPage) {
setSettingsOpen(true);
}
}, [isSettingsPage]);
async function handleLogout() {
document.cookie = "dev_session=;path=/;max-age=0";
await supabase.auth.signOut();
@@ -234,74 +204,17 @@ export default function AdminSidebar({ userRole }: SidebarProps) {
</div>
{/* Nav links */}
<nav className="flex-1 overflow-y-auto px-3 py-5 space-y-1">
<nav className="flex-1 overflow-hidden px-3 py-5">
{TOP_LINKS.map((item) => {
const active = isActive(item.href);
// Settings link with expandable sub-menu
if (item.href === "/admin/settings") {
return (
<div key={item.href}>
<button
onClick={() => setSettingsOpen(!settingsOpen)}
className={[
"w-full group flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium transition-all duration-200 border-l-2",
isSettingsPage
? "border-l-2"
: "border-l-2 border-transparent",
].join(" ")}
style={isSettingsPage ? {
backgroundColor: "rgba(202, 117, 67, 0.15)",
color: "#dea889",
borderColor: "var(--admin-accent)"
} : {
color: "var(--admin-sidebar-text)",
borderColor: "transparent"
}}
>
<span className="flex-shrink-0 transition-colors" style={{
color: isSettingsPage ? "var(--admin-accent)" : "rgba(208, 203, 180, 0.6)"
}}>
<SettingsCogIcon open={settingsOpen} />
</span>
Settings
<ChevronIcon open={settingsOpen} />
</button>
{/* Settings sub-links */}
{settingsOpen && (
<div className="ml-6 mt-1 space-y-0.5 border-l border-[var(--admin-sidebar-bg)] pl-3">
{SETTINGS_SUB_LINKS.map((subItem) => {
const subActive = pathname === subItem.href || pathname.startsWith(subItem.href + "/");
return (
<Link
key={subItem.href}
href={subItem.href}
onClick={() => setMobileOpen(false)}
className={[
"block px-3 py-2 rounded-lg text-xs font-medium transition-all duration-200",
subActive
? "bg-emerald-500/10 text-emerald-400"
: "text-[var(--admin-sidebar-text)] hover:bg-white/5 opacity-70 hover:opacity-100",
].join(" ")}
>
{subItem.label}
</Link>
);
})}
</div>
)}
</div>
);
}
return (
<Link
key={item.href}
href={item.href}
onClick={() => setMobileOpen(false)}
className={[
"group flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium transition-all duration-200",
"group flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium transition-all duration-200 mb-1",
active
? "border-l-2"
: "border-l-2 border-transparent hover:border-l-2",