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:
poduck
2025-12-17 19:29:13 -05:00
parent a80b35a806
commit 92019aac7e
68 changed files with 1827 additions and 788 deletions

View File

@@ -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>