210 lines
7.7 KiB
TypeScript
210 lines
7.7 KiB
TypeScript
/**
|
|
* Settings Layout
|
|
*
|
|
* Provides a sidebar navigation for settings pages with grouped sections.
|
|
* Used as a wrapper for all /settings/* routes.
|
|
*/
|
|
|
|
import React from 'react';
|
|
import { Outlet, Link, useLocation, useNavigate, useOutletContext } from 'react-router-dom';
|
|
import { useTranslation } from 'react-i18next';
|
|
import {
|
|
ArrowLeft,
|
|
Building2,
|
|
Palette,
|
|
Layers,
|
|
Globe,
|
|
Key,
|
|
Lock,
|
|
Mail,
|
|
Phone,
|
|
CreditCard,
|
|
AlertTriangle,
|
|
Calendar,
|
|
} from 'lucide-react';
|
|
import {
|
|
SettingsSidebarSection,
|
|
SettingsSidebarItem,
|
|
} from '../components/navigation/SidebarComponents';
|
|
import UnfinishedBadge from '../components/ui/UnfinishedBadge';
|
|
import { Business, User, PlanPermissions } from '../types';
|
|
import { usePlanFeatures, FeatureKey } from '../hooks/usePlanFeatures';
|
|
|
|
interface ParentContext {
|
|
user: User;
|
|
business: Business;
|
|
updateBusiness: (updates: Partial<Business>) => void;
|
|
}
|
|
|
|
// Map settings pages to their required plan features
|
|
const SETTINGS_PAGE_FEATURES: Record<string, FeatureKey> = {
|
|
'/settings/branding': 'white_label',
|
|
'/settings/custom-domains': 'custom_domain',
|
|
'/settings/api': 'api_access',
|
|
'/settings/authentication': 'custom_oauth',
|
|
'/settings/sms-calling': 'sms_reminders',
|
|
};
|
|
|
|
const SettingsLayout: React.FC = () => {
|
|
const { t } = useTranslation();
|
|
const location = useLocation();
|
|
const navigate = useNavigate();
|
|
const { canUse } = usePlanFeatures();
|
|
|
|
// Get context from parent route (BusinessLayout)
|
|
const parentContext = useOutletContext<ParentContext>();
|
|
|
|
// Check if a feature is locked (returns true if locked)
|
|
const isLocked = (feature: FeatureKey | undefined): boolean => {
|
|
if (!feature) return false;
|
|
return !canUse(feature);
|
|
};
|
|
|
|
// Get the current page's feature requirement (if any)
|
|
const currentPageFeature = SETTINGS_PAGE_FEATURES[location.pathname];
|
|
const currentPageLocked = isLocked(currentPageFeature);
|
|
|
|
return (
|
|
<div className="flex h-full bg-gray-50 dark:bg-gray-900">
|
|
{/* Settings Sidebar */}
|
|
<aside className="w-64 bg-white dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 flex flex-col shrink-0">
|
|
{/* Back Button */}
|
|
<div className="p-4 border-b border-gray-200 dark:border-gray-700">
|
|
<button
|
|
onClick={() => navigate('/')}
|
|
className="flex items-center gap-2 text-sm text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white transition-colors"
|
|
>
|
|
<ArrowLeft size={16} />
|
|
<span>{t('settings.backToApp', 'Back to App')}</span>
|
|
</button>
|
|
</div>
|
|
|
|
{/* Settings Title */}
|
|
<div className="px-4 py-4">
|
|
<h2 className="text-lg font-semibold text-gray-900 dark:text-white">
|
|
{t('settings.title', 'Settings')}
|
|
</h2>
|
|
</div>
|
|
|
|
{/* Navigation */}
|
|
<nav className="flex-1 px-2 pb-4 space-y-3 overflow-y-auto">
|
|
{/* Business Section */}
|
|
<SettingsSidebarSection title={t('settings.sections.business', 'Business')}>
|
|
<SettingsSidebarItem
|
|
to="/settings/general"
|
|
icon={Building2}
|
|
label={t('settings.general.title', 'General')}
|
|
description={t('settings.general.description', 'Name, timezone, contact')}
|
|
/>
|
|
<SettingsSidebarItem
|
|
to="/settings/resource-types"
|
|
icon={Layers}
|
|
label={t('settings.resourceTypes.title', 'Resource Types')}
|
|
description={t('settings.resourceTypes.description', 'Staff, rooms, equipment')}
|
|
badgeElement={<UnfinishedBadge />}
|
|
/>
|
|
<SettingsSidebarItem
|
|
to="/settings/booking"
|
|
icon={Calendar}
|
|
label={t('settings.booking.title', 'Booking')}
|
|
description={t('settings.booking.description', 'Booking URL, redirects')}
|
|
/>
|
|
</SettingsSidebarSection>
|
|
|
|
{/* Branding Section */}
|
|
<SettingsSidebarSection title={t('settings.sections.branding', 'Branding')}>
|
|
<SettingsSidebarItem
|
|
to="/settings/branding"
|
|
icon={Palette}
|
|
label={t('settings.appearance.title', 'Appearance')}
|
|
description={t('settings.appearance.description', 'Logo, colors, theme')}
|
|
locked={isLocked('white_label')}
|
|
/>
|
|
<SettingsSidebarItem
|
|
to="/settings/email-templates"
|
|
icon={Mail}
|
|
label={t('settings.emailTemplates.title', 'Email Templates')}
|
|
description={t('settings.emailTemplates.description', 'Customize email designs')}
|
|
/>
|
|
<SettingsSidebarItem
|
|
to="/settings/custom-domains"
|
|
icon={Globe}
|
|
label={t('settings.customDomains.title', 'Custom Domains')}
|
|
description={t('settings.customDomains.description', 'Use your own domain')}
|
|
locked={isLocked('custom_domain')}
|
|
/>
|
|
</SettingsSidebarSection>
|
|
|
|
{/* Integrations Section */}
|
|
<SettingsSidebarSection title={t('settings.sections.integrations', 'Integrations')}>
|
|
<SettingsSidebarItem
|
|
to="/settings/api"
|
|
icon={Key}
|
|
label={t('settings.api.title', 'API & Webhooks')}
|
|
description={t('settings.api.description', 'API tokens, webhooks')}
|
|
locked={isLocked('api_access')}
|
|
/>
|
|
</SettingsSidebarSection>
|
|
|
|
{/* Access Section */}
|
|
<SettingsSidebarSection title={t('settings.sections.access', 'Access')}>
|
|
<SettingsSidebarItem
|
|
to="/settings/authentication"
|
|
icon={Lock}
|
|
label={t('settings.authentication.title', 'Authentication')}
|
|
description={t('settings.authentication.description', 'OAuth, social login')}
|
|
locked={isLocked('custom_oauth')}
|
|
/>
|
|
</SettingsSidebarSection>
|
|
|
|
{/* Communication Section */}
|
|
<SettingsSidebarSection title={t('settings.sections.communication', 'Communication')}>
|
|
<SettingsSidebarItem
|
|
to="/settings/email"
|
|
icon={Mail}
|
|
label={t('settings.email.title', 'Email Setup')}
|
|
description={t('settings.email.description', 'Email addresses for tickets')}
|
|
/>
|
|
<SettingsSidebarItem
|
|
to="/settings/sms-calling"
|
|
icon={Phone}
|
|
label={t('settings.smsCalling.title', 'SMS & Calling')}
|
|
description={t('settings.smsCalling.description', 'Credits, phone numbers')}
|
|
locked={isLocked('sms_reminders')}
|
|
/>
|
|
</SettingsSidebarSection>
|
|
|
|
{/* Billing Section */}
|
|
<SettingsSidebarSection title={t('settings.sections.billing', 'Billing')}>
|
|
<SettingsSidebarItem
|
|
to="/settings/billing"
|
|
icon={CreditCard}
|
|
label={t('settings.billing.title', 'Plan & Billing')}
|
|
description={t('settings.billing.description', 'Subscription, invoices')}
|
|
/>
|
|
<SettingsSidebarItem
|
|
to="/settings/quota"
|
|
icon={AlertTriangle}
|
|
label={t('settings.quota.title', 'Quota Management')}
|
|
description={t('settings.quota.description', 'Usage limits, archiving')}
|
|
/>
|
|
</SettingsSidebarSection>
|
|
</nav>
|
|
</aside>
|
|
|
|
{/* Content Area */}
|
|
<main className="flex-1 overflow-y-auto">
|
|
<div className="max-w-4xl mx-auto p-8">
|
|
<Outlet context={{
|
|
...parentContext,
|
|
isFeatureLocked: currentPageLocked,
|
|
lockedFeature: currentPageFeature,
|
|
}} />
|
|
</div>
|
|
</main>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default SettingsLayout;
|