Files
brandly/src/components/TopHeader.tsx
T

217 lines
9.2 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState } from 'react';
import { LayoutTemplate, Menu, Home, Settings, Download, ZoomIn, ZoomOut, X, CalendarDays, Sparkles, Play, FolderOpen } from 'lucide-react';
import { AISettingsPanel } from './settings/AISettingsPanel';
interface TopHeaderProps {
currentStep: 'dashboard' | 'brand' | 'studio' | 'express' | 'content-grid' | 'template-builder' | 'production-form';
setCurrentStep: (step: 'dashboard' | 'brand' | 'studio' | 'express' | 'content-grid' | 'template-builder' | 'production-form') => void;
/** Open Express editor with blank canvas, no brand */
onStartExpressBlank?: () => void;
/** Open Pro editor with blank canvas, no brand */
onStartProBlank?: () => void;
outputFormat?: 'video' | 'image';
/** Zoom controls — passed up from StudioWorkspace */
zoom?: number;
onZoomIn?: () => void;
onZoomOut?: () => void;
onZoomReset?: () => void;
/** Aspect ratio controls */
aspectRatio?: string;
onAspectRatioChange?: (ratio: string) => void;
disableAspectControls?: boolean;
titleOverride?: React.ReactNode;
}
export const TopHeader: React.FC<TopHeaderProps> = ({
currentStep,
setCurrentStep,
outputFormat,
onStartExpressBlank,
onStartProBlank,
zoom = 1,
onZoomIn,
onZoomOut,
onZoomReset,
aspectRatio = '9:16',
onAspectRatioChange,
disableAspectControls,
titleOverride,
}) => {
const [menuOpen, setMenuOpen] = useState(false);
const [showAISettings, setShowAISettings] = useState(false);
const isStudio = currentStep === 'studio';
return (
<header
className="flex-none border-b border-neutral-800/60 bg-neutral-900/95 backdrop-blur-sm px-3 h-11 flex items-center justify-between z-30 relative"
style={{ WebkitAppRegion: 'drag' } as React.CSSProperties}
>
{/* Left: Hamburger + Logo */}
<div className="flex items-center gap-2 ml-[72px]" style={{ WebkitAppRegion: 'no-drag' } as React.CSSProperties}>
<div className="relative">
<button
onClick={() => setMenuOpen(!menuOpen)}
title="Menú principal"
className="p-1.5 rounded-md text-neutral-400 hover:text-white hover:bg-neutral-800 transition-colors"
>
<Menu size={16} />
</button>
{/* Dropdown menu */}
{menuOpen && (
<>
<div className="fixed inset-0 z-40" onClick={() => setMenuOpen(false)} />
<div className="absolute top-full left-0 mt-1 w-52 bg-neutral-800 border border-neutral-700 rounded-lg shadow-2xl z-50 py-1 animate-in">
<button
onClick={() => { setCurrentStep('dashboard'); setMenuOpen(false); }}
className="w-full flex items-center gap-3 px-4 py-2.5 text-sm text-neutral-300 hover:bg-neutral-700 hover:text-white transition-colors"
>
<Home size={14} /> Ir al Dashboard
</button>
{currentStep !== 'brand' && (
<button
onClick={() => { setCurrentStep('brand'); setMenuOpen(false); }}
className="w-full flex items-center gap-3 px-4 py-2.5 text-sm text-neutral-300 hover:bg-neutral-700 hover:text-white transition-colors"
>
<Settings size={14} /> Editar Marca
</button>
)}
<button
onClick={() => { setCurrentStep('content-grid'); setMenuOpen(false); }}
className="w-full flex items-center gap-3 px-4 py-2.5 text-sm text-neutral-300 hover:bg-neutral-700 hover:text-white transition-colors"
>
<CalendarDays size={14} /> Malla de Contenidos
</button>
<div className="h-px bg-neutral-700 my-1" />
<button
className="w-full flex items-center gap-3 px-4 py-2.5 text-sm text-neutral-300 hover:bg-neutral-700 hover:text-white transition-colors"
>
<FolderOpen size={14} /> Abrir
</button>
<div className="h-px bg-neutral-700 my-1" />
<button
onClick={() => { setShowAISettings(true); setMenuOpen(false); }}
className="w-full flex items-center gap-3 px-4 py-2.5 text-sm text-neutral-300 hover:bg-neutral-700 hover:text-white transition-colors"
>
<Sparkles size={14} className="text-violet-400" /> Configuración IA
</button>
</div>
</>
)}
</div>
{showAISettings && (
<AISettingsPanel onClose={() => setShowAISettings(false)} />
)}
<div className="flex items-center gap-2">
<div className="bg-violet-600/20 p-1 rounded text-violet-400">
<LayoutTemplate size={14} />
</div>
<span className="text-xs font-semibold text-white tracking-tight">
{titleOverride || 'SaaS Branding'}
</span>
</div>
</div>
{/* Center: Zoom controls (only in studio) */}
{isStudio && onZoomIn && onZoomOut && (
<div className="absolute left-1/2 -translate-x-1/2 flex items-center gap-1" style={{ WebkitAppRegion: 'no-drag' } as React.CSSProperties}>
<button
onClick={(e) => { e.stopPropagation(); onZoomOut(); }}
title="Zoom Out"
className="p-1 text-neutral-500 hover:text-white rounded transition-colors"
>
<ZoomOut size={14} />
</button>
<button
onClick={(e) => { e.stopPropagation(); onZoomReset?.(); }}
title="Restablecer zoom"
className="text-[11px] font-mono text-neutral-400 hover:text-white w-12 text-center rounded py-0.5 hover:bg-neutral-800 transition-colors"
>
{Math.round(zoom * 100)}%
</button>
<button
onClick={(e) => { e.stopPropagation(); onZoomIn(); }}
title="Zoom In"
className="p-1 text-neutral-500 hover:text-white rounded transition-colors"
>
<ZoomIn size={14} />
</button>
{/* Aspect ratio pills */}
{onAspectRatioChange && !disableAspectControls && (
<>
<div className="w-px h-4 bg-neutral-700 mx-1" />
{(['16:9', '9:16', '1:1', '4:5', '4:3'] as const).map(ratio => (
<button
key={ratio}
onClick={(e) => { e.stopPropagation(); onAspectRatioChange(ratio); }}
title={`Formato ${ratio}`}
className={`px-2 py-0.5 rounded text-[11px] font-medium transition-colors ${
aspectRatio === ratio
? 'bg-neutral-700 text-white'
: 'text-neutral-500 hover:text-neutral-300 hover:bg-neutral-800'
}`}
>
{ratio}
</button>
))}
</>
)}
</div>
)}
{/* Right: Editor buttons + Format badge */}
<div className="flex items-center gap-2 pr-2" style={{ WebkitAppRegion: 'no-drag' } as React.CSSProperties}>
{/* Express / Pro buttons — only on dashboard */}
{currentStep === 'dashboard' && onStartExpressBlank && (
<button
onClick={onStartExpressBlank}
title="Crear desde cero con el editor rápido (sin marca)"
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-gradient-to-r from-violet-600/80 to-fuchsia-600/80 hover:from-violet-500 hover:to-fuchsia-500 text-white text-[10px] font-bold transition-all shadow-sm hover:shadow-md"
>
<Sparkles size={12} />
Express
</button>
)}
{currentStep === 'dashboard' && onStartProBlank && (
<button
onClick={onStartProBlank}
title="Crear desde cero con el editor profesional (sin marca)"
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-neutral-800 hover:bg-neutral-700 border border-neutral-700 text-white text-[10px] font-semibold transition-all"
>
<Play size={12} />
Editor Pro 🎛
</button>
)}
{currentStep !== 'content-grid' && (
<button
onClick={() => setCurrentStep('content-grid')}
title="Abrir malla de contenidos"
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-neutral-800 hover:bg-neutral-700 border border-neutral-700 text-white text-[10px] font-semibold transition-all"
>
<CalendarDays size={12} />
Malla de Contenidos
</button>
)}
{isStudio && (
<span className="text-[10px] font-medium text-neutral-500 uppercase tracking-wider">
{outputFormat === 'image' ? 'Imagen' : 'Video'}
</span>
)}
{currentStep !== 'dashboard' && !isStudio && (
<button
onClick={() => setCurrentStep('dashboard')}
className="flex items-center gap-1.5 px-3 py-1 bg-neutral-800 hover:bg-neutral-700 text-white rounded-md text-xs font-medium transition-all"
>
Dashboard
</button>
)}
</div>
</header>
);
};