refactor: redesign planos interface with design system patterns

- Create CreatePlanModal component with Headless UI Dialog
- Implement dark mode support throughout plans UI
- Update plans/page.tsx with professional card layout
- Update plans/[id]/page.tsx with consistent styling
- Add proper spacing, typography, and color consistency
- Implement smooth animations and transitions
- Add success/error message feedback
- Improve form UX with better input styling
This commit is contained in:
Erik Silva
2025-12-13 19:26:38 -03:00
parent 2f1cf2bb2a
commit 2a112f169d
26 changed files with 2580 additions and 119 deletions

View File

@@ -11,20 +11,10 @@ interface AgencyBrandingProps {
/**
* AgencyBranding - Aplica as cores da agência via CSS Variables
* O favicon agora é tratado via Metadata API no layout (server-side)
* O favicon é atualizado dinamicamente via DOM
*/
export function AgencyBranding({ colors }: AgencyBrandingProps) {
const [mounted, setMounted] = useState(false);
const [debugInfo, setDebugInfo] = useState<string>('Iniciando...');
useEffect(() => {
setMounted(true);
}, []);
useEffect(() => {
if (!mounted) return;
const hexToRgb = (hex: string) => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? `${parseInt(result[1], 16)} ${parseInt(result[2], 16)} ${parseInt(result[3], 16)}` : null;
@@ -67,33 +57,25 @@ export function AgencyBranding({ colors }: AgencyBrandingProps) {
if (typeof window === 'undefined' || typeof document === 'undefined') return;
try {
setDebugInfo(`Tentando atualizar favicon: ${url}`);
console.log('🎨 AgencyBranding: Atualizando favicon para:', url);
const newHref = `${url}${url.includes('?') ? '&' : '?'}v=${Date.now()}`;
// Buscar TODOS os links de ícone existentes
// Buscar TODOS os links de ícone (como estava funcionando antes)
const existingLinks = document.querySelectorAll("link[rel*='icon']");
if (existingLinks.length > 0) {
// Atualizar href de todos os links existentes (SEM REMOVER)
existingLinks.forEach(link => {
link.setAttribute('href', newHref);
});
setDebugInfo(`Favicon atualizado (${existingLinks.length} links)`);
console.log(`${existingLinks.length} favicons atualizados`);
} else {
// Criar novo link apenas se não existir nenhum
const newLink = document.createElement('link');
newLink.rel = 'icon';
newLink.type = 'image/x-icon';
newLink.href = newHref;
document.head.appendChild(newLink);
setDebugInfo('Novo favicon criado');
console.log('✅ Novo favicon criado');
console.log('✅ Favicon criado');
}
} catch (error) {
setDebugInfo(`Erro: ${error}`);
console.error('❌ Erro ao atualizar favicon:', error);
}
};
@@ -111,32 +93,23 @@ export function AgencyBranding({ colors }: AgencyBrandingProps) {
}
}
// Atualizar favicon se houver logo salvo (após montar)
// Atualizar favicon se houver logo salvo
const cachedLogo = localStorage.getItem('agency-logo-url');
if (cachedLogo) {
console.log('🔍 Logo encontrado no cache:', cachedLogo);
updateFavicon(cachedLogo);
} else {
setDebugInfo('Nenhum logo no cache');
console.log('⚠️ Nenhum logo encontrado no cache');
}
// Listener para atualizações em tempo real (ex: da página de configurações)
// Listener para atualizações em tempo real
const handleUpdate = () => {
console.log('🔔 Evento branding-update recebido!');
setDebugInfo('Evento branding-update recebido');
const cachedPrimary = localStorage.getItem('agency-primary-color');
const cachedSecondary = localStorage.getItem('agency-secondary-color');
const cachedLogo = localStorage.getItem('agency-logo-url');
if (cachedPrimary && cachedSecondary) {
console.log('🎨 Aplicando cores do cache');
applyTheme(cachedPrimary, cachedSecondary);
}
if (cachedLogo) {
console.log('🖼️ Atualizando favicon do cache:', cachedLogo);
updateFavicon(cachedLogo);
}
};
@@ -146,24 +119,8 @@ export function AgencyBranding({ colors }: AgencyBrandingProps) {
return () => {
window.removeEventListener('branding-update', handleUpdate);
};
}, [mounted, colors]);
}, [colors]);
if (!mounted) return null;
return (
<div style={{
position: 'fixed',
bottom: '10px',
left: '10px',
background: 'rgba(0,0,0,0.8)',
color: 'white',
padding: '5px 10px',
borderRadius: '4px',
fontSize: '10px',
zIndex: 9999,
pointerEvents: 'none'
}}>
DEBUG: {debugInfo}
</div>
);
// Componente não renderiza nada visualmente (apenas efeitos colaterais)
return null;
}

View File

@@ -69,18 +69,22 @@ export const SidebarRail: React.FC<SidebarRailProps> = ({
if (res.ok) {
const data = await res.json();
if (currentUser) {
// Usar localStorage como fallback se API não retornar logo
const cachedLogo = localStorage.getItem('agency-logo-url');
const finalLogoUrl = data.logo_url || cachedLogo;
const updatedUser = {
...currentUser,
company: data.name || currentUser.company,
logoUrl: data.logo_url
logoUrl: finalLogoUrl
};
setUser(updatedUser);
saveAuth(token, updatedUser); // Persistir atualização
// Atualizar localStorage do logo para uso do favicon
if (data.logo_url) {
console.log('📝 Salvando logo no localStorage:', data.logo_url);
localStorage.setItem('agency-logo-url', data.logo_url);
// Atualizar localStorage do logo (preservar se já existe)
if (finalLogoUrl) {
console.log('📝 Salvando logo no localStorage:', finalLogoUrl);
localStorage.setItem('agency-logo-url', finalLogoUrl);
window.dispatchEvent(new Event('auth-update')); // Notificar favicon
window.dispatchEvent(new Event('branding-update')); // Notificar AgencyBranding
}