252 lines
7.0 KiB
TypeScript
252 lines
7.0 KiB
TypeScript
import React, { useState, useEffect } from "react";
|
|
import { SketchPicker, ColorResult } from "react-color";
|
|
import { BrandingConfig, defaultBrandingConfig, popularFonts } from "../src/config/branding.config";
|
|
|
|
interface CustomizationPanelProps {
|
|
active?: boolean;
|
|
}
|
|
|
|
export const CustomizationPanel: React.FC<CustomizationPanelProps> = ({ active }) => {
|
|
const [config, setConfig] = useState<BrandingConfig>(defaultBrandingConfig);
|
|
const [showPrimaryPicker, setShowPrimaryPicker] = useState(false);
|
|
const [showSecondaryPicker, setShowSecondaryPicker] = useState(false);
|
|
const [showBackgroundPicker, setShowBackgroundPicker] = useState(false);
|
|
|
|
useEffect(() => {
|
|
// Save to localStorage for persistence and cross-tab sync
|
|
if (typeof window !== "undefined") {
|
|
localStorage.setItem("email-branding-config", JSON.stringify(config));
|
|
|
|
// Send message for immediate updates
|
|
window.postMessage(
|
|
{
|
|
type: "CUSTOMIZATION_UPDATE",
|
|
config: config,
|
|
},
|
|
"*"
|
|
);
|
|
|
|
// Dispatch custom event for same-tab updates
|
|
window.dispatchEvent(
|
|
new CustomEvent("customization-update", {
|
|
detail: { config },
|
|
})
|
|
);
|
|
}
|
|
}, [config]);
|
|
|
|
const handleColorChange = (colorType: "primary" | "secondary" | "background") => (
|
|
color: ColorResult
|
|
) => {
|
|
const newConfig = {
|
|
...config,
|
|
colors: {
|
|
...config.colors,
|
|
[colorType]: color.hex,
|
|
},
|
|
};
|
|
setConfig(newConfig);
|
|
window.postMessage(
|
|
{
|
|
type: "CUSTOMIZATION_UPDATE",
|
|
config: newConfig,
|
|
},
|
|
"*"
|
|
);
|
|
};
|
|
|
|
const handleFontChange = (font: string) => {
|
|
const newConfig = {
|
|
...config,
|
|
font: {
|
|
family: font,
|
|
},
|
|
};
|
|
setConfig(newConfig);
|
|
window.postMessage(
|
|
{
|
|
type: "CUSTOMIZATION_UPDATE",
|
|
config: newConfig,
|
|
},
|
|
"*"
|
|
);
|
|
};
|
|
|
|
if (!active) return null;
|
|
|
|
return (
|
|
<div style={{ padding: "20px", fontFamily: "Arial, sans-serif" }}>
|
|
<h2 style={{ marginTop: 0, marginBottom: "20px" }}>Customize Branding</h2>
|
|
|
|
{/* Font Selector */}
|
|
<div style={{ marginBottom: "24px" }}>
|
|
<label
|
|
style={{
|
|
display: "block",
|
|
marginBottom: "8px",
|
|
fontWeight: "bold",
|
|
fontSize: "14px",
|
|
}}
|
|
>
|
|
Font Family
|
|
</label>
|
|
<select
|
|
value={config.font.family}
|
|
onChange={(e) => handleFontChange(e.target.value)}
|
|
style={{
|
|
width: "100%",
|
|
padding: "8px",
|
|
fontSize: "14px",
|
|
border: "1px solid #ddd",
|
|
borderRadius: "4px",
|
|
}}
|
|
>
|
|
{popularFonts.map((font) => (
|
|
<option key={font} value={font}>
|
|
{font.split(",")[0]}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
{/* Primary Color Picker */}
|
|
<div style={{ marginBottom: "24px" }}>
|
|
<label
|
|
style={{
|
|
display: "block",
|
|
marginBottom: "8px",
|
|
fontWeight: "bold",
|
|
fontSize: "14px",
|
|
}}
|
|
>
|
|
Primary Color
|
|
</label>
|
|
<div style={{ position: "relative" }}>
|
|
<div
|
|
onClick={() => {
|
|
setShowPrimaryPicker(!showPrimaryPicker);
|
|
setShowSecondaryPicker(false);
|
|
setShowBackgroundPicker(false);
|
|
}}
|
|
style={{
|
|
width: "100%",
|
|
height: "40px",
|
|
backgroundColor: config.colors.primary,
|
|
border: "1px solid #ddd",
|
|
borderRadius: "4px",
|
|
cursor: "pointer",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
color: "#fff",
|
|
fontWeight: "bold",
|
|
}}
|
|
>
|
|
{config.colors.primary}
|
|
</div>
|
|
{showPrimaryPicker && (
|
|
<div style={{ position: "absolute", zIndex: 1000, marginTop: "4px" }}>
|
|
<SketchPicker
|
|
color={config.colors.primary}
|
|
onChange={handleColorChange("primary")}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Secondary Color Picker */}
|
|
<div style={{ marginBottom: "24px" }}>
|
|
<label
|
|
style={{
|
|
display: "block",
|
|
marginBottom: "8px",
|
|
fontWeight: "bold",
|
|
fontSize: "14px",
|
|
}}
|
|
>
|
|
Secondary Color
|
|
</label>
|
|
<div style={{ position: "relative" }}>
|
|
<div
|
|
onClick={() => {
|
|
setShowSecondaryPicker(!showSecondaryPicker);
|
|
setShowPrimaryPicker(false);
|
|
setShowBackgroundPicker(false);
|
|
}}
|
|
style={{
|
|
width: "100%",
|
|
height: "40px",
|
|
backgroundColor: config.colors.secondary,
|
|
border: "1px solid #ddd",
|
|
borderRadius: "4px",
|
|
cursor: "pointer",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
color: "#fff",
|
|
fontWeight: "bold",
|
|
}}
|
|
>
|
|
{config.colors.secondary}
|
|
</div>
|
|
{showSecondaryPicker && (
|
|
<div style={{ position: "absolute", zIndex: 1000, marginTop: "4px" }}>
|
|
<SketchPicker
|
|
color={config.colors.secondary}
|
|
onChange={handleColorChange("secondary")}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Background Color Picker */}
|
|
<div style={{ marginBottom: "24px" }}>
|
|
<label
|
|
style={{
|
|
display: "block",
|
|
marginBottom: "8px",
|
|
fontWeight: "bold",
|
|
fontSize: "14px",
|
|
}}
|
|
>
|
|
Background Color
|
|
</label>
|
|
<div style={{ position: "relative" }}>
|
|
<div
|
|
onClick={() => {
|
|
setShowBackgroundPicker(!showBackgroundPicker);
|
|
setShowPrimaryPicker(false);
|
|
setShowSecondaryPicker(false);
|
|
}}
|
|
style={{
|
|
width: "100%",
|
|
height: "40px",
|
|
backgroundColor: config.colors.background,
|
|
border: "1px solid #ddd",
|
|
borderRadius: "4px",
|
|
cursor: "pointer",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
color: config.colors.background === "#F5F5F5" ? "#333" : "#fff",
|
|
fontWeight: "bold",
|
|
}}
|
|
>
|
|
{config.colors.background}
|
|
</div>
|
|
{showBackgroundPicker && (
|
|
<div style={{ position: "absolute", zIndex: 1000, marginTop: "4px" }}>
|
|
<SketchPicker
|
|
color={config.colors.background}
|
|
onChange={handleColorChange("background")}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|