| import { useState } from 'react' |
| import { X, Upload } from 'lucide-react' |
| import clsx from 'clsx' |
|
|
| |
| |
| |
| |
| function BrandKitEditor({ isOpen, onClose, clipId, jobId }) { |
| const [primaryColor, setPrimaryColor] = useState('#E8A020') |
| const [secondaryColor, setSecondaryColor] = useState('#FFB800') |
| const [watermarkPos, setWatermarkPos] = useState('bottom-right') |
| const [watermarkOpacity, setWatermarkOpacity] = useState(0.8) |
| const [font, setFont] = useState('Inter') |
| const [logoFile, setLogoFile] = useState(null) |
|
|
| if (!isOpen) return null |
|
|
| const positions = [ |
| { id: 'top-left', label: 'TL', x: 'left', y: 'top' }, |
| { id: 'top-center', label: 'T', x: 'center', y: 'top' }, |
| { id: 'top-right', label: 'TR', x: 'right', y: 'top' }, |
| { id: 'middle-left', label: 'L', x: 'left', y: 'center' }, |
| { id: 'center', label: 'C', x: 'center', y: 'center' }, |
| { id: 'middle-right', label: 'R', x: 'right', y: 'center' }, |
| { id: 'bottom-left', label: 'BL', x: 'left', y: 'bottom' }, |
| { id: 'bottom-center', label: 'B', x: 'center', y: 'bottom' }, |
| { id: 'bottom-right', label: 'BR', x: 'right', y: 'bottom' }, |
| ] |
|
|
| const fonts = ['Inter', 'Poppins', 'Oswald', 'Montserrat', 'Roboto', 'Playfair Display'] |
|
|
| const handleSave = () => { |
| console.log({ |
| primaryColor, |
| secondaryColor, |
| watermarkPos, |
| watermarkOpacity, |
| font, |
| logoFile, |
| }) |
| onClose() |
| } |
|
|
| return ( |
| <> |
| {/* Overlay */} |
| <div |
| className="fixed inset-0 bg-black/60 backdrop-blur-sm z-40 animate-fade-in" |
| onClick={onClose} |
| /> |
| |
| {/* Modal */} |
| <div className="fixed inset-0 z-50 flex items-center justify-center p-4 animate-scale-in"> |
| <div className="glass-lg max-w-3xl w-full max-h-[90vh] overflow-y-auto rounded-3xl"> |
| {/* Header */} |
| <div className="flex items-center justify-between p-8 border-b border-white/10 sticky top-0 bg-dark-900/50 backdrop-blur"> |
| <h2 className="text-2xl font-bold text-white">Brand Kit Editor</h2> |
| <button |
| onClick={onClose} |
| className="p-2 hover:bg-white/10 rounded-lg transition-colors" |
| > |
| <X className="w-5 h-5 text-white/70" /> |
| </button> |
| </div> |
| |
| <div className="grid grid-cols-1 lg:grid-cols-2 gap-8 p-8"> |
| {/* Left Panel - Settings */} |
| <div className="space-y-8"> |
| {/* Logo Upload */} |
| <div> |
| <label className="block text-sm font-semibold text-white mb-4">Logo</label> |
| <label className="upload-zone cursor-pointer"> |
| <div className="flex flex-col items-center gap-3"> |
| <Upload className="w-6 h-6 text-primary-500" /> |
| <div> |
| <p className="text-sm font-semibold text-white"> |
| {logoFile ? 'Change logo' : 'Upload logo'} |
| </p> |
| <p className="text-xs text-white/60">PNG or SVG, max 2MB</p> |
| </div> |
| </div> |
| <input |
| type="file" |
| accept="image/png,image/svg+xml" |
| onChange={(e) => setLogoFile(e.target.files?.[0] || null)} |
| className="hidden" |
| /> |
| </label> |
| {logoFile && ( |
| <p className="text-xs text-white/60 mt-2">Selected: {logoFile.name}</p> |
| )} |
| </div> |
| |
| {/* Primary Color */} |
| <div> |
| <label className="block text-sm font-semibold text-white mb-3">Primary Color</label> |
| <div className="flex gap-3 items-center"> |
| <input |
| type="color" |
| value={primaryColor} |
| onChange={(e) => setPrimaryColor(e.target.value)} |
| className="w-16 h-12 rounded-lg cursor-pointer border border-white/10" |
| /> |
| <input |
| type="text" |
| value={primaryColor} |
| onChange={(e) => setPrimaryColor(e.target.value)} |
| className="input-field flex-1 text-sm font-mono" |
| placeholder="#E8A020" |
| /> |
| </div> |
| </div> |
| |
| {/* Secondary Color */} |
| <div> |
| <label className="block text-sm font-semibold text-white mb-3">Secondary Color</label> |
| <div className="flex gap-3 items-center"> |
| <input |
| type="color" |
| value={secondaryColor} |
| onChange={(e) => setSecondaryColor(e.target.value)} |
| className="w-16 h-12 rounded-lg cursor-pointer border border-white/10" |
| /> |
| <input |
| type="text" |
| value={secondaryColor} |
| onChange={(e) => setSecondaryColor(e.target.value)} |
| className="input-field flex-1 text-sm font-mono" |
| placeholder="#FFB800" |
| /> |
| </div> |
| </div> |
| |
| {/* Font */} |
| <div> |
| <label className="block text-sm font-semibold text-white mb-3">Font Family</label> |
| <select |
| value={font} |
| onChange={(e) => setFont(e.target.value)} |
| className="input-field-lg w-full bg-white/5" |
| > |
| {fonts.map((f) => ( |
| <option key={f} value={f}> |
| {f} |
| </option> |
| ))} |
| </select> |
| </div> |
| |
| {/* Watermark Opacity */} |
| <div> |
| <div className="flex justify-between items-center mb-3"> |
| <label className="text-sm font-semibold text-white">Watermark Opacity</label> |
| <span className="text-primary-500 font-bold">{Math.round(watermarkOpacity * 100)}%</span> |
| </div> |
| <input |
| type="range" |
| min="0" |
| max="1" |
| step="0.1" |
| value={watermarkOpacity} |
| onChange={(e) => setWatermarkOpacity(parseFloat(e.target.value))} |
| className="w-full h-2 bg-white/10 rounded-full accent-primary-500" |
| /> |
| </div> |
| </div> |
| |
| {/* Right Panel - Preview */} |
| <div> |
| <p className="text-sm font-semibold text-white mb-4">Live Preview</p> |
| <div className="glass-lg p-6 rounded-2xl aspect-9-16 flex flex-col relative overflow-hidden bg-dark-850"> |
| {/* Gradient background based on colors */} |
| <div |
| className="absolute inset-0 opacity-20" |
| style={{ |
| background: `linear-gradient(135deg, ${primaryColor}, ${secondaryColor})`, |
| }} |
| /> |
| |
| {/* Content */} |
| <div className="relative z-10 flex flex-col h-full"> |
| {/* Logo at top-left if watermarkPos involves top */} |
| {(watermarkPos.includes('top') || watermarkPos.includes('center')) && ( |
| <div |
| className="p-3" |
| style={{ opacity: watermarkOpacity }} |
| > |
| <div |
| className="w-12 h-12 rounded-lg" |
| style={{ backgroundColor: primaryColor }} |
| /> |
| </div> |
| )} |
| |
| {/* Center content */} |
| <div className="flex-1 flex flex-col items-center justify-center px-4"> |
| <h3 |
| className="text-xl font-bold text-center mb-2" |
| style={{ color: primaryColor, fontFamily: font }} |
| > |
| Your Brand |
| </h3> |
| <p className="text-xs text-white/60 text-center"> |
| This is how your clips will look with your brand kit applied |
| </p> |
| </div> |
| |
| {/* Watermark at bottom if positioned there */} |
| {(watermarkPos.includes('bottom') || watermarkPos.includes('center')) && ( |
| <div |
| className="p-3 flex items-center justify-center" |
| style={{ opacity: watermarkOpacity }} |
| > |
| <div |
| className="text-xs font-bold" |
| style={{ color: primaryColor }} |
| > |
| WATERMARK |
| </div> |
| </div> |
| )} |
| </div> |
| </div> |
| |
| {/* Watermark Position */} |
| <div className="mt-6"> |
| <p className="text-sm font-semibold text-white mb-4">Watermark Position</p> |
| <div className="grid grid-cols-3 gap-2"> |
| {positions.map((pos) => ( |
| <button |
| key={pos.id} |
| onClick={() => setWatermarkPos(pos.id)} |
| className={clsx( |
| 'p-3 rounded-lg font-bold text-sm transition-all', |
| watermarkPos === pos.id |
| ? 'glass-lg bg-primary-500/20 border-primary-500/50 text-primary-400' |
| : 'glass hover:bg-white/10 text-white/70' |
| )} |
| > |
| {pos.label} |
| </button> |
| ))} |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| {/* Actions */} |
| <div className="flex gap-3 p-8 border-t border-white/10"> |
| <button onClick={onClose} className="btn-secondary-lg flex-1"> |
| Cancel |
| </button> |
| <button onClick={handleSave} className="btn-primary-lg flex-1"> |
| Save Brand Kit |
| </button> |
| </div> |
| </div> |
| </div> |
| </> |
| ) |
| } |
|
|
| export default BrandKitEditor |
|
|