- 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
127 lines
4.8 KiB
TypeScript
127 lines
4.8 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useState } from 'react';
|
|
|
|
interface AgencyBrandingProps {
|
|
colors?: {
|
|
primary: string;
|
|
secondary: string;
|
|
} | null;
|
|
}
|
|
|
|
/**
|
|
* AgencyBranding - Aplica as cores da agência via CSS Variables
|
|
* O favicon é atualizado dinamicamente via DOM
|
|
*/
|
|
export function AgencyBranding({ colors }: AgencyBrandingProps) {
|
|
useEffect(() => {
|
|
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;
|
|
};
|
|
|
|
const applyTheme = (primary: string, secondary: string) => {
|
|
if (!primary || !secondary) return;
|
|
|
|
const root = document.documentElement;
|
|
const primaryRgb = hexToRgb(primary);
|
|
const secondaryRgb = hexToRgb(secondary);
|
|
|
|
const gradient = `linear-gradient(135deg, ${primary}, ${primary})`;
|
|
const gradientText = `linear-gradient(to right, ${primary}, ${primary})`;
|
|
|
|
root.style.setProperty('--gradient', gradient);
|
|
root.style.setProperty('--gradient-text', gradientText);
|
|
root.style.setProperty('--gradient-primary', gradient);
|
|
root.style.setProperty('--color-gradient-brand', gradient);
|
|
|
|
root.style.setProperty('--brand-color', primary);
|
|
root.style.setProperty('--brand-color-strong', secondary);
|
|
|
|
if (primaryRgb) root.style.setProperty('--brand-rgb', primaryRgb);
|
|
if (secondaryRgb) root.style.setProperty('--brand-strong-rgb', secondaryRgb);
|
|
|
|
// Salvar no localStorage para cache
|
|
if (typeof window !== 'undefined') {
|
|
const hostname = window.location.hostname;
|
|
const sub = hostname.split('.')[0];
|
|
if (sub && sub !== 'www') {
|
|
localStorage.setItem(`agency-theme:${sub}`, gradient);
|
|
localStorage.setItem('agency-primary-color', primary);
|
|
localStorage.setItem('agency-secondary-color', secondary);
|
|
}
|
|
}
|
|
};
|
|
|
|
const updateFavicon = (url: string) => {
|
|
if (typeof window === 'undefined' || typeof document === 'undefined') return;
|
|
|
|
try {
|
|
const newHref = `${url}${url.includes('?') ? '&' : '?'}v=${Date.now()}`;
|
|
|
|
// Buscar TODOS os links de ícone (como estava funcionando antes)
|
|
const existingLinks = document.querySelectorAll("link[rel*='icon']");
|
|
|
|
if (existingLinks.length > 0) {
|
|
existingLinks.forEach(link => {
|
|
link.setAttribute('href', newHref);
|
|
});
|
|
console.log(`✅ ${existingLinks.length} favicons atualizados`);
|
|
} else {
|
|
const newLink = document.createElement('link');
|
|
newLink.rel = 'icon';
|
|
newLink.type = 'image/x-icon';
|
|
newLink.href = newHref;
|
|
document.head.appendChild(newLink);
|
|
console.log('✅ Favicon criado');
|
|
}
|
|
} catch (error) {
|
|
console.error('❌ Erro ao atualizar favicon:', error);
|
|
}
|
|
};
|
|
|
|
// Se temos cores do servidor, aplicar imediatamente
|
|
if (colors) {
|
|
applyTheme(colors.primary, colors.secondary);
|
|
} else {
|
|
// Fallback: tentar pegar do cache do localStorage
|
|
const cachedPrimary = localStorage.getItem('agency-primary-color');
|
|
const cachedSecondary = localStorage.getItem('agency-secondary-color');
|
|
|
|
if (cachedPrimary && cachedSecondary) {
|
|
applyTheme(cachedPrimary, cachedSecondary);
|
|
}
|
|
}
|
|
|
|
// Atualizar favicon se houver logo salvo
|
|
const cachedLogo = localStorage.getItem('agency-logo-url');
|
|
if (cachedLogo) {
|
|
updateFavicon(cachedLogo);
|
|
}
|
|
|
|
// Listener para atualizações em tempo real
|
|
const handleUpdate = () => {
|
|
const cachedPrimary = localStorage.getItem('agency-primary-color');
|
|
const cachedSecondary = localStorage.getItem('agency-secondary-color');
|
|
const cachedLogo = localStorage.getItem('agency-logo-url');
|
|
|
|
if (cachedPrimary && cachedSecondary) {
|
|
applyTheme(cachedPrimary, cachedSecondary);
|
|
}
|
|
|
|
if (cachedLogo) {
|
|
updateFavicon(cachedLogo);
|
|
}
|
|
};
|
|
|
|
window.addEventListener('branding-update', handleUpdate);
|
|
|
|
return () => {
|
|
window.removeEventListener('branding-update', handleUpdate);
|
|
};
|
|
}, [colors]);
|
|
|
|
// Componente não renderiza nada visualmente (apenas efeitos colaterais)
|
|
return null;
|
|
}
|