feat: Reorganize settings sidebar and add plan-based feature locking
- Add locked state to Plugins sidebar item with plan feature check - Create Branding section in settings with Appearance, Email Templates, Custom Domains - Split Domains page into Booking (URLs, redirects) and Custom Domains (BYOD, purchase) - Add booking_return_url field to Tenant model for customer redirects - Update SidebarItem component to support locked prop with lock icon - Move Email Templates from main sidebar to Settings > Branding - Add communication credits hooks and payment form updates - Add timezone fields migration and various UI improvements 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -2,13 +2,17 @@
|
||||
* Branding Settings Page
|
||||
*
|
||||
* Logo uploads, colors, and display preferences.
|
||||
* Features live preview of color changes that revert on navigation/reload if not saved.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useOutletContext } from 'react-router-dom';
|
||||
import { Palette, Save, Check, Upload, X, Image as ImageIcon } from 'lucide-react';
|
||||
import { Business, User } from '../../types';
|
||||
import { applyBrandColors } from '../../utils/colorUtils';
|
||||
import { UpgradePrompt } from '../../components/UpgradePrompt';
|
||||
import { FeatureKey } from '../../hooks/usePlanFeatures';
|
||||
|
||||
// Color palette options
|
||||
const colorPalettes = [
|
||||
@@ -26,10 +30,12 @@ const colorPalettes = [
|
||||
|
||||
const BrandingSettings: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const { business, updateBusiness, user } = useOutletContext<{
|
||||
const { business, updateBusiness, user, isFeatureLocked, lockedFeature } = useOutletContext<{
|
||||
business: Business;
|
||||
updateBusiness: (updates: Partial<Business>) => void;
|
||||
user: User;
|
||||
isFeatureLocked?: boolean;
|
||||
lockedFeature?: FeatureKey;
|
||||
}>();
|
||||
|
||||
const [formState, setFormState] = useState({
|
||||
@@ -41,8 +47,37 @@ const BrandingSettings: React.FC = () => {
|
||||
});
|
||||
const [showToast, setShowToast] = useState(false);
|
||||
|
||||
// Store the original saved colors to restore on unmount/navigation
|
||||
const savedColorsRef = useRef({
|
||||
primary: business.primaryColor,
|
||||
secondary: business.secondaryColor || business.primaryColor,
|
||||
});
|
||||
|
||||
// Live preview: Update CSS variables as user cycles through palettes
|
||||
useEffect(() => {
|
||||
applyBrandColors(formState.primaryColor, formState.secondaryColor);
|
||||
|
||||
// Cleanup: Restore saved colors when component unmounts (navigation away)
|
||||
return () => {
|
||||
applyBrandColors(savedColorsRef.current.primary, savedColorsRef.current.secondary);
|
||||
};
|
||||
}, [formState.primaryColor, formState.secondaryColor]);
|
||||
|
||||
// Update savedColorsRef when business data changes (after successful save)
|
||||
useEffect(() => {
|
||||
savedColorsRef.current = {
|
||||
primary: business.primaryColor,
|
||||
secondary: business.secondaryColor || business.primaryColor,
|
||||
};
|
||||
}, [business.primaryColor, business.secondaryColor]);
|
||||
|
||||
const handleSave = async () => {
|
||||
await updateBusiness(formState);
|
||||
// Update the saved reference so cleanup doesn't revert
|
||||
savedColorsRef.current = {
|
||||
primary: formState.primaryColor,
|
||||
secondary: formState.secondaryColor,
|
||||
};
|
||||
setShowToast(true);
|
||||
setTimeout(() => setShowToast(false), 3000);
|
||||
};
|
||||
@@ -63,6 +98,11 @@ const BrandingSettings: React.FC = () => {
|
||||
);
|
||||
}
|
||||
|
||||
// Show upgrade prompt if feature is locked
|
||||
if (isFeatureLocked && lockedFeature) {
|
||||
return <UpgradePrompt feature={lockedFeature} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Header */}
|
||||
|
||||
Reference in New Issue
Block a user