Restructure navigation: move setup items to Settings with accordion menu

Move rarely-used setup items from main sidebar to Settings to keep
daily-use features prominent:

- Services → Settings > Business section
- Locations → Settings > Business section
- Site Builder → Settings > Branding section

Settings sidebar changes:
- Convert static sections to accordion (one open at a time)
- Auto-expand section based on current URL
- Preserve all permission checks for moved items

Add redirects from old URLs to new locations for backwards compatibility.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-23 22:18:02 -05:00
parent 3eb1c303e5
commit e4668f81c5
5 changed files with 341 additions and 207 deletions

View File

@@ -10,15 +10,12 @@ import {
MessageSquare,
LogOut,
ClipboardList,
Briefcase,
Ticket,
HelpCircle,
Clock,
Plug,
FileSignature,
CalendarOff,
LayoutTemplate,
MapPin,
Image,
} from 'lucide-react';
import { Business, User } from '../types';
@@ -159,25 +156,14 @@ const Sidebar: React.FC<SidebarProps> = ({ business, user, isCollapsed, toggleCo
{/* Manage Section - Show if user has any manage-related permission */}
{(canViewManagementPages ||
hasPermission('can_access_site_builder') ||
hasPermission('can_access_gallery') ||
hasPermission('can_access_customers') ||
hasPermission('can_access_services') ||
hasPermission('can_access_resources') ||
hasPermission('can_access_staff') ||
hasPermission('can_access_contracts') ||
hasPermission('can_access_time_blocks') ||
hasPermission('can_access_locations')
hasPermission('can_access_time_blocks')
) && (
<SidebarSection title={t('nav.sections.manage', 'Manage')} isCollapsed={isCollapsed}>
{hasPermission('can_access_site_builder') && (
<SidebarItem
to="/dashboard/site-editor"
icon={LayoutTemplate}
label={t('nav.siteBuilder', 'Site Builder')}
isCollapsed={isCollapsed}
/>
)}
{hasPermission('can_access_gallery') && (
<SidebarItem
to="/dashboard/gallery"
@@ -194,14 +180,6 @@ const Sidebar: React.FC<SidebarProps> = ({ business, user, isCollapsed, toggleCo
isCollapsed={isCollapsed}
/>
)}
{hasPermission('can_access_services') && (
<SidebarItem
to="/dashboard/services"
icon={Briefcase}
label={t('nav.services', 'Services')}
isCollapsed={isCollapsed}
/>
)}
{hasPermission('can_access_resources') && (
<SidebarItem
to="/dashboard/resources"
@@ -235,15 +213,6 @@ const Sidebar: React.FC<SidebarProps> = ({ business, user, isCollapsed, toggleCo
isCollapsed={isCollapsed}
/>
)}
{hasPermission('can_access_locations') && (
<SidebarItem
to="/dashboard/locations"
icon={MapPin}
label={t('nav.locations', 'Locations')}
isCollapsed={isCollapsed}
locked={!canUse('multi_location')}
/>
)}
</SidebarSection>
)}

View File

@@ -256,6 +256,55 @@ export const SettingsSidebarSection: React.FC<SettingsSidebarSectionProps> = ({
);
};
interface SettingsAccordionSectionProps {
title: string;
sectionKey: string;
isOpen: boolean;
onToggle: (sectionKey: string) => void;
children: React.ReactNode;
hasVisibleItems?: boolean;
}
/**
* Collapsible accordion section for settings sidebar
* Only one section can be open at a time (controlled by parent)
*/
export const SettingsAccordionSection: React.FC<SettingsAccordionSectionProps> = ({
title,
sectionKey,
isOpen,
onToggle,
children,
hasVisibleItems = true,
}) => {
// Don't render if no visible items
if (!hasVisibleItems) return null;
return (
<div className="space-y-0.5">
<button
onClick={() => onToggle(sectionKey)}
className="flex items-center justify-between w-full px-4 py-2 text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 rounded-lg transition-colors"
>
<span>{title}</span>
<ChevronDown
size={14}
className={`transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`}
/>
</button>
<div
className={`overflow-hidden transition-all duration-200 ease-in-out ${
isOpen ? 'max-h-[500px] opacity-100' : 'max-h-0 opacity-0'
}`}
>
<div className="space-y-0.5 pt-1">
{children}
</div>
</div>
</div>
);
};
interface SettingsSidebarItemProps {
to: string;
icon: LucideIcon;