Improve staff management UI and add sorting functionality
- Remove WIP badge from staff sidebar navigation - Make action buttons consistent between Customers and Staff pages - Edit button: icon + text with gray border - Masquerade button: icon + text with indigo border - Verify email button: icon-only with colored border (green/amber) - Add sortable columns to Staff list (name and role) - Include migrations for tenant manager role removal 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -56,6 +56,16 @@ const SettingsLayout: React.FC = () => {
|
||||
|
||||
// Get context from parent route (BusinessLayout)
|
||||
const parentContext = useOutletContext<ParentContext>();
|
||||
const { user } = parentContext || {};
|
||||
const isOwner = user?.role === 'owner';
|
||||
|
||||
// Check if staff has access to a specific settings page
|
||||
const hasSettingsPermission = (permissionKey: string): boolean => {
|
||||
// Owners always have all permissions
|
||||
if (isOwner) return true;
|
||||
// Staff need the specific permission
|
||||
return user?.effective_permissions?.[permissionKey] === true;
|
||||
};
|
||||
|
||||
// Check if a feature is locked (returns true if locked)
|
||||
const isLocked = (feature: FeatureKey | undefined): boolean => {
|
||||
@@ -92,123 +102,167 @@ const SettingsLayout: React.FC = () => {
|
||||
{/* 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="/dashboard/settings/general"
|
||||
icon={Building2}
|
||||
label={t('settings.general.title', 'General')}
|
||||
description={t('settings.general.description', 'Name, timezone, contact')}
|
||||
/>
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/settings/resource-types"
|
||||
icon={Layers}
|
||||
label={t('settings.resourceTypes.title', 'Resource Types')}
|
||||
description={t('settings.resourceTypes.description', 'Staff, rooms, equipment')}
|
||||
/>
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/settings/booking"
|
||||
icon={Calendar}
|
||||
label={t('settings.booking.title', 'Booking')}
|
||||
description={t('settings.booking.description', 'Booking URL, redirects')}
|
||||
/>
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/settings/business-hours"
|
||||
icon={Clock}
|
||||
label={t('settings.businessHours.title', 'Business Hours')}
|
||||
description={t('settings.businessHours.description', 'Operating hours')}
|
||||
/>
|
||||
</SettingsSidebarSection>
|
||||
{(hasSettingsPermission('can_access_settings_general') ||
|
||||
hasSettingsPermission('can_access_settings_resource_types') ||
|
||||
hasSettingsPermission('can_access_settings_booking') ||
|
||||
hasSettingsPermission('can_access_settings_business_hours')) && (
|
||||
<SettingsSidebarSection title={t('settings.sections.business', 'Business')}>
|
||||
{hasSettingsPermission('can_access_settings_general') && (
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/settings/general"
|
||||
icon={Building2}
|
||||
label={t('settings.general.title', 'General')}
|
||||
description={t('settings.general.description', 'Name, timezone, contact')}
|
||||
/>
|
||||
)}
|
||||
{hasSettingsPermission('can_access_settings_resource_types') && (
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/settings/resource-types"
|
||||
icon={Layers}
|
||||
label={t('settings.resourceTypes.title', 'Resource Types')}
|
||||
description={t('settings.resourceTypes.description', 'Staff, rooms, equipment')}
|
||||
/>
|
||||
)}
|
||||
{hasSettingsPermission('can_access_settings_booking') && (
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/settings/booking"
|
||||
icon={Calendar}
|
||||
label={t('settings.booking.title', 'Booking')}
|
||||
description={t('settings.booking.description', 'Booking URL, redirects')}
|
||||
/>
|
||||
)}
|
||||
{hasSettingsPermission('can_access_settings_business_hours') && (
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/settings/business-hours"
|
||||
icon={Clock}
|
||||
label={t('settings.businessHours.title', 'Business Hours')}
|
||||
description={t('settings.businessHours.description', 'Operating hours')}
|
||||
/>
|
||||
)}
|
||||
</SettingsSidebarSection>
|
||||
)}
|
||||
|
||||
{/* Branding Section */}
|
||||
<SettingsSidebarSection title={t('settings.sections.branding', 'Branding')}>
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/settings/branding"
|
||||
icon={Palette}
|
||||
label={t('settings.appearance.title', 'Appearance')}
|
||||
description={t('settings.appearance.description', 'Logo, colors, theme')}
|
||||
locked={isLocked('remove_branding')}
|
||||
/>
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/settings/email-templates"
|
||||
icon={Mail}
|
||||
label={t('settings.emailTemplates.title', 'Email Templates')}
|
||||
description={t('settings.emailTemplates.description', 'Customize automated emails')}
|
||||
/>
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/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')}
|
||||
/>
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/settings/embed-widget"
|
||||
icon={Code2}
|
||||
label={t('settings.embedWidget.title', 'Embed Widget')}
|
||||
description={t('settings.embedWidget.sidebarDescription', 'Add booking to your site')}
|
||||
/>
|
||||
</SettingsSidebarSection>
|
||||
{(hasSettingsPermission('can_access_settings_branding') ||
|
||||
hasSettingsPermission('can_access_settings_email_templates') ||
|
||||
hasSettingsPermission('can_access_settings_custom_domains') ||
|
||||
hasSettingsPermission('can_access_settings_embed_widget')) && (
|
||||
<SettingsSidebarSection title={t('settings.sections.branding', 'Branding')}>
|
||||
{hasSettingsPermission('can_access_settings_branding') && (
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/settings/branding"
|
||||
icon={Palette}
|
||||
label={t('settings.appearance.title', 'Appearance')}
|
||||
description={t('settings.appearance.description', 'Logo, colors, theme')}
|
||||
locked={isLocked('remove_branding')}
|
||||
/>
|
||||
)}
|
||||
{hasSettingsPermission('can_access_settings_email_templates') && (
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/settings/email-templates"
|
||||
icon={Mail}
|
||||
label={t('settings.emailTemplates.title', 'Email Templates')}
|
||||
description={t('settings.emailTemplates.description', 'Customize automated emails')}
|
||||
/>
|
||||
)}
|
||||
{hasSettingsPermission('can_access_settings_custom_domains') && (
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/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')}
|
||||
/>
|
||||
)}
|
||||
{hasSettingsPermission('can_access_settings_embed_widget') && (
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/settings/embed-widget"
|
||||
icon={Code2}
|
||||
label={t('settings.embedWidget.title', 'Embed Widget')}
|
||||
description={t('settings.embedWidget.sidebarDescription', 'Add booking to your site')}
|
||||
/>
|
||||
)}
|
||||
</SettingsSidebarSection>
|
||||
)}
|
||||
|
||||
{/* Integrations Section */}
|
||||
<SettingsSidebarSection title={t('settings.sections.integrations', 'Integrations')}>
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/settings/api"
|
||||
icon={Key}
|
||||
label={t('settings.api.title', 'API & Webhooks')}
|
||||
description={t('settings.api.description', 'API tokens, webhooks')}
|
||||
locked={isLocked('api_access')}
|
||||
/>
|
||||
</SettingsSidebarSection>
|
||||
{hasSettingsPermission('can_access_settings_api') && (
|
||||
<SettingsSidebarSection title={t('settings.sections.integrations', 'Integrations')}>
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/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="/dashboard/settings/staff-roles"
|
||||
icon={Users}
|
||||
label={t('settings.staffRoles.title', 'Staff Roles')}
|
||||
description={t('settings.staffRoles.description', 'Role permissions')}
|
||||
/>
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/settings/authentication"
|
||||
icon={Lock}
|
||||
label={t('settings.authentication.title', 'Authentication')}
|
||||
description={t('settings.authentication.description', 'OAuth, social login')}
|
||||
locked={isLocked('custom_oauth')}
|
||||
/>
|
||||
</SettingsSidebarSection>
|
||||
{(hasSettingsPermission('can_access_settings_staff_roles') ||
|
||||
hasSettingsPermission('can_access_settings_authentication')) && (
|
||||
<SettingsSidebarSection title={t('settings.sections.access', 'Access')}>
|
||||
{hasSettingsPermission('can_access_settings_staff_roles') && (
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/settings/staff-roles"
|
||||
icon={Users}
|
||||
label={t('settings.staffRoles.title', 'Staff Roles')}
|
||||
description={t('settings.staffRoles.description', 'Role permissions')}
|
||||
/>
|
||||
)}
|
||||
{hasSettingsPermission('can_access_settings_authentication') && (
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/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="/dashboard/settings/email"
|
||||
icon={Mail}
|
||||
label={t('settings.email.title', 'Email Setup')}
|
||||
description={t('settings.email.description', 'Email addresses for tickets')}
|
||||
/>
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/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>
|
||||
{(hasSettingsPermission('can_access_settings_email') ||
|
||||
hasSettingsPermission('can_access_settings_sms_calling')) && (
|
||||
<SettingsSidebarSection title={t('settings.sections.communication', 'Communication')}>
|
||||
{hasSettingsPermission('can_access_settings_email') && (
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/settings/email"
|
||||
icon={Mail}
|
||||
label={t('settings.email.title', 'Email Setup')}
|
||||
description={t('settings.email.description', 'Email addresses for tickets')}
|
||||
/>
|
||||
)}
|
||||
{hasSettingsPermission('can_access_settings_sms_calling') && (
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/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="/dashboard/settings/billing"
|
||||
icon={CreditCard}
|
||||
label={t('settings.billing.title', 'Plan & Billing')}
|
||||
description={t('settings.billing.description', 'Subscription, invoices')}
|
||||
/>
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/settings/quota"
|
||||
icon={AlertTriangle}
|
||||
label={t('settings.quota.title', 'Quota Management')}
|
||||
description={t('settings.quota.description', 'Usage limits, archiving')}
|
||||
/>
|
||||
</SettingsSidebarSection>
|
||||
{/* Billing Section - Owner only */}
|
||||
{isOwner && (
|
||||
<SettingsSidebarSection title={t('settings.sections.billing', 'Billing')}>
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/settings/billing"
|
||||
icon={CreditCard}
|
||||
label={t('settings.billing.title', 'Plan & Billing')}
|
||||
description={t('settings.billing.description', 'Subscription, invoices')}
|
||||
/>
|
||||
<SettingsSidebarItem
|
||||
to="/dashboard/settings/quota"
|
||||
icon={AlertTriangle}
|
||||
label={t('settings.quota.title', 'Quota Management')}
|
||||
description={t('settings.quota.description', 'Usage limits, archiving')}
|
||||
/>
|
||||
</SettingsSidebarSection>
|
||||
)}
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user