feat: Email templates, bulk delete, communication credits, plan features

- Add email template presets for Browse Templates tab (12 templates)
- Add bulk selection and deletion for My Templates tab
- Add communication credits system with Twilio integration
- Add payment views for credit purchases and auto-reload
- Add SMS reminder and masked calling plan permissions
- Fix appointment status mapping (frontend/backend mismatch)
- Clear masquerade stack on login/logout for session hygiene
- Update platform settings with credit configuration
- Add new migrations for Twilio and Stripe payment fields

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-02 01:42:38 -05:00
parent 8038f67183
commit 05ebd0f2bb
77 changed files with 14185 additions and 1394 deletions

View File

@@ -0,0 +1,154 @@
/**
* 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,
Webhook,
} from 'lucide-react';
import {
SettingsSidebarSection,
SettingsSidebarItem,
} from '../components/navigation/SidebarComponents';
import { Business, User } from '../types';
interface ParentContext {
user: User;
business: Business;
updateBusiness: (updates: Partial<Business>) => void;
}
const SettingsLayout: React.FC = () => {
const { t } = useTranslation();
const location = useLocation();
const navigate = useNavigate();
// Get context from parent route (BusinessLayout)
const parentContext = useOutletContext<ParentContext>();
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/branding"
icon={Palette}
label={t('settings.branding.title', 'Branding')}
description={t('settings.branding.description', 'Logo, colors, appearance')}
/>
<SettingsSidebarItem
to="/settings/resource-types"
icon={Layers}
label={t('settings.resourceTypes.title', 'Resource Types')}
description={t('settings.resourceTypes.description', 'Staff, rooms, equipment')}
/>
</SettingsSidebarSection>
{/* Integrations Section */}
<SettingsSidebarSection title={t('settings.sections.integrations', 'Integrations')}>
<SettingsSidebarItem
to="/settings/domains"
icon={Globe}
label={t('settings.domains.title', 'Domains')}
description={t('settings.domains.description', 'Custom domain setup')}
/>
<SettingsSidebarItem
to="/settings/api"
icon={Key}
label={t('settings.api.title', 'API & Webhooks')}
description={t('settings.api.description', 'API tokens, webhooks')}
/>
</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')}
/>
</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')}
/>
</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')}
/>
</SettingsSidebarSection>
</nav>
</aside>
{/* Content Area */}
<main className="flex-1 overflow-y-auto">
<div className="max-w-4xl mx-auto p-8">
<Outlet context={parentContext} />
</div>
</main>
</div>
);
};
export default SettingsLayout;