chore: snapshot before agency split

This commit is contained in:
Erik Silva
2025-12-09 17:21:25 -03:00
parent 6ec29c7eef
commit 053e180321
27 changed files with 428 additions and 234 deletions

1
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1 @@
{}

View File

@@ -6,10 +6,6 @@ services:
restart: unless-stopped
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.endpoint=tcp://host.docker.internal:2375"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.network=aggios-network"
- "--providers.file.directory=/etc/traefik/dynamic"
- "--providers.file.watch=true"
- "--entrypoints.web.address=:80"

View File

@@ -22,19 +22,31 @@ const tabs = [
];
const themePresets = [
{ name: 'Laranja/Rosa', gradient: 'linear-gradient(90deg, #FF3A05, #FF0080)', colors: ['#FF3A05', '#FF0080'] },
{ name: 'Azul/Roxo', gradient: 'linear-gradient(90deg, #0066FF, #9333EA)', colors: ['#0066FF', '#9333EA'] },
{ name: 'Verde/Esmeralda', gradient: 'linear-gradient(90deg, #10B981, #059669)', colors: ['#10B981', '#059669'] },
{ name: 'Ciano/Azul', gradient: 'linear-gradient(90deg, #06B6D4, #3B82F6)', colors: ['#06B6D4', '#3B82F6'] },
{ name: 'Rosa/Roxo', gradient: 'linear-gradient(90deg, #EC4899, #A855F7)', colors: ['#EC4899', '#A855F7'] },
{ name: 'Vermelho/Laranja', gradient: 'linear-gradient(90deg, #EF4444, #F97316)', colors: ['#EF4444', '#F97316'] },
{ name: 'Marca', gradient: 'linear-gradient(135deg, #ff3a05, #ff0080)', colors: ['#ff3a05', '#ff0080'] },
{ name: 'Azul/Roxo', gradient: 'linear-gradient(135deg, #0066FF, #9333EA)', colors: ['#0066FF', '#9333EA'] },
{ name: 'Verde/Esmeralda', gradient: 'linear-gradient(135deg, #10B981, #059669)', colors: ['#10B981', '#059669'] },
{ name: 'Ciano/Azul', gradient: 'linear-gradient(135deg, #06B6D4, #3B82F6)', colors: ['#06B6D4', '#3B82F6'] },
{ name: 'Rosa/Roxo', gradient: 'linear-gradient(135deg, #EC4899, #A855F7)', colors: ['#EC4899', '#A855F7'] },
{ name: 'Vermelho/Laranja', gradient: 'linear-gradient(135deg, #EF4444, #F97316)', colors: ['#EF4444', '#F97316'] },
];
const DEFAULT_GRADIENT = 'linear-gradient(135deg, #ff3a05, #ff0080)';
const THEME_STORAGE_PREFIX = 'agency-theme:';
const setThemeVariables = (gradient: string) => {
document.documentElement.style.setProperty('--gradient-primary', gradient);
document.documentElement.style.setProperty('--gradient', gradient);
document.documentElement.style.setProperty('--gradient-text', gradient.replace('90deg', 'to right'));
document.documentElement.style.setProperty('--color-gradient-brand', gradient.replace('90deg', 'to right'));
};
export default function ConfiguracoesPage() {
const [selectedTab, setSelectedTab] = useState(0);
const [selectedTheme, setSelectedTheme] = useState(0);
const [customColor1, setCustomColor1] = useState('#FF3A05');
const [customColor2, setCustomColor2] = useState('#FF0080');
const [activeGradient, setActiveGradient] = useState(DEFAULT_GRADIENT);
const [themeKey, setThemeKey] = useState('default');
const [customColor1, setCustomColor1] = useState('#ff3a05');
const [customColor2, setCustomColor2] = useState('#ff0080');
const [showSuccessDialog, setShowSuccessDialog] = useState(false);
const [successMessage, setSuccessMessage] = useState('');
const [showSupportDialog, setShowSupportDialog] = useState(false);
@@ -64,7 +76,7 @@ export default function ConfiguracoesPage() {
confirmPassword: '',
});
// Buscar dados da agência da API
// Buscar dados da agência da API e inicializar tema salvo
useEffect(() => {
const fetchAgencyData = async () => {
try {
@@ -78,6 +90,24 @@ export default function ConfiguracoesPage() {
return;
}
const parsedUser = JSON.parse(userData);
const hostname = window.location.hostname;
const hostSubdomain = hostname.split('.')[0] || 'default';
const key = parsedUser?.subdomain || parsedUser?.tenantId || hostSubdomain;
setThemeKey(key);
const savedGradient = localStorage.getItem(`${THEME_STORAGE_PREFIX}${key}`) || DEFAULT_GRADIENT;
setActiveGradient(savedGradient);
setThemeVariables(savedGradient);
const presetIndex = themePresets.findIndex((theme) => theme.gradient === savedGradient);
if (presetIndex >= 0) {
setSelectedTheme(presetIndex);
setCustomColor1(themePresets[presetIndex].colors[0]);
setCustomColor2(themePresets[presetIndex].colors[1]);
}
// Buscar dados da API
const response = await fetch('/api/agency/profile', {
headers: {
@@ -138,14 +168,13 @@ export default function ConfiguracoesPage() {
}, []);
const applyTheme = (gradient: string) => {
document.documentElement.style.setProperty('--gradient-primary', gradient);
document.documentElement.style.setProperty('--gradient', gradient);
document.documentElement.style.setProperty('--gradient-text', gradient.replace('90deg', 'to right'));
document.documentElement.style.setProperty('--color-gradient-brand', gradient.replace('90deg', 'to right'));
setActiveGradient(gradient);
setThemeVariables(gradient);
};
const applyCustomTheme = () => {
const gradient = `linear-gradient(90deg, ${customColor1}, ${customColor2})`;
setSelectedTheme(-1);
applyTheme(gradient);
};
@@ -193,9 +222,15 @@ export default function ConfiguracoesPage() {
};
const handleSaveTheme = () => {
// TODO: Integrar com API para salvar no banco
const selectedGradient = themePresets[selectedTheme].gradient;
console.log('Salvando tema:', selectedGradient);
const gradientToSave = selectedTheme >= 0
? themePresets[selectedTheme].gradient
: activeGradient;
applyTheme(gradientToSave);
if (themeKey) {
localStorage.setItem(`${THEME_STORAGE_PREFIX}${themeKey}`, gradientToSave);
}
setSuccessMessage('Tema salvo com sucesso!');
setShowSuccessDialog(true);
};
@@ -283,7 +318,7 @@ export default function ConfiguracoesPage() {
`w-full flex items-center justify-center space-x-2 rounded-lg py-2.5 text-sm font-medium leading-5 transition-all
${selected
? 'bg-white dark:bg-gray-900 text-gray-900 dark:text-white shadow'
: 'text-gray-600 dark:text-gray-400 hover:bg-white/[0.5] dark:hover:bg-gray-700/[0.5] hover:text-gray-900 dark:hover:text-white'
: 'text-gray-600 dark:text-gray-400 hover:bg-white/50 dark:hover:bg-gray-700/50 hover:text-gray-900 dark:hover:text-white'
}`
}
>
@@ -315,7 +350,7 @@ export default function ConfiguracoesPage() {
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 flex items-center justify-between">
<label className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 flex items-center justify-between">
<span>CNPJ</span>
<span className="text-xs text-gray-500">Alteração via suporte</span>
</label>
@@ -332,7 +367,7 @@ export default function ConfiguracoesPage() {
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 flex items-center justify-between">
<label className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 flex items-center justify-between">
<span>E-mail (acesso)</span>
<span className="text-xs text-gray-500">Alteração via suporte</span>
</label>

View File

@@ -48,6 +48,15 @@ import {
const ThemeToggle = dynamic(() => import('@/components/ThemeToggle'), { ssr: false });
const ThemeTester = dynamic(() => import('@/components/ThemeTester'), { ssr: false });
const DEFAULT_GRADIENT = 'linear-gradient(135deg, #ff3a05, #ff0080)';
const setGradientVariables = (gradient: string) => {
document.documentElement.style.setProperty('--gradient-primary', gradient);
document.documentElement.style.setProperty('--gradient', gradient);
document.documentElement.style.setProperty('--gradient-text', gradient.replace('90deg', 'to right'));
document.documentElement.style.setProperty('--color-gradient-brand', gradient.replace('90deg', 'to right'));
};
export default function AgencyLayout({
children,
}: {
@@ -88,8 +97,13 @@ export default function AgencyLayout({
}
const hostname = window.location.hostname;
const subdomain = hostname.split('.')[0];
setAgencyName(subdomain);
const hostSubdomain = hostname.split('.')[0] || 'default';
const themeKey = parsedUser?.subdomain || parsedUser?.tenantId || hostSubdomain;
setAgencyName(parsedUser?.subdomain || hostSubdomain);
const storedGradient = localStorage.getItem(`agency-theme:${themeKey}`);
setGradientVariables(storedGradient || DEFAULT_GRADIENT);
// Inicializar com "Todos os Clientes"
setSelectedClient(clients[0]);
@@ -106,7 +120,10 @@ export default function AgencyLayout({
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('keydown', handleKeyDown);
setGradientVariables(DEFAULT_GRADIENT);
};
}, [router]);
if (!user) {

View File

@@ -31,8 +31,8 @@ export default function CadastroPage() {
const [subdomain, setSubdomain] = useState("");
const [domainAvailable, setDomainAvailable] = useState<boolean | null>(null);
const [checkingDomain, setCheckingDomain] = useState(false);
const [primaryColor, setPrimaryColor] = useState("#FF3A05");
const [secondaryColor, setSecondaryColor] = useState("#FF0080");
const [primaryColor, setPrimaryColor] = useState("#ff3a05");
const [secondaryColor, setSecondaryColor] = useState("#ff0080");
const [logoUrl, setLogoUrl] = useState<string>("");
const [showPreviewMobile, setShowPreviewMobile] = useState(false);
@@ -52,8 +52,8 @@ export default function CadastroPage() {
setCepData(data.cepData || { state: "", city: "", neighborhood: "", street: "" });
setSubdomain(data.subdomain || "");
setDomainAvailable(data.domainAvailable ?? null);
setPrimaryColor(data.primaryColor || "#FF3A05");
setSecondaryColor(data.secondaryColor || "#FF0080");
setPrimaryColor(data.primaryColor || "#ff3a05");
setSecondaryColor(data.secondaryColor || "#ff0080");
setLogoUrl(data.logoUrl || "");
} catch (error) {
console.error('Erro ao carregar dados:', error);
@@ -323,7 +323,7 @@ export default function CadastroPage() {
console.log('📤 Enviando cadastro completo:', payload);
toast.loading('Criando sua conta...', { id: 'register' });
const response = await fetch('/api/admin/agencies', {
const response = await fetch(API_ENDPOINTS.adminAgencyRegister, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -332,8 +332,15 @@ export default function CadastroPage() {
});
if (!response.ok) {
let errorMessage = 'Erro ao criar conta';
try {
const error = await response.json();
throw new Error(error.message || 'Erro ao criar conta');
errorMessage = error.message || error.error || errorMessage;
} catch (e) {
const text = await response.text();
if (text) errorMessage = text;
}
throw new Error(errorMessage);
}
const data = await response.json();
@@ -365,9 +372,10 @@ export default function CadastroPage() {
},
});
// Redirecionar para o painel da agência no subdomínio
// Redirecionar para o painel da agência no subdomínio, enviando o gradiente escolhido
setTimeout(() => {
const agencyUrl = `http://${data.subdomain}.localhost/login`;
const gradient = `linear-gradient(135deg, ${primaryColor}, ${secondaryColor})`;
const agencyUrl = `http://${data.subdomain}.localhost/login?theme=${encodeURIComponent(gradient)}`;
window.location.href = agencyUrl;
}, 2000);
@@ -406,8 +414,8 @@ export default function CadastroPage() {
setContacts([{ id: 1, whatsapp: "(11) 98765-4321" }]);
setSubdomain("idealpages");
setDomainAvailable(true);
setPrimaryColor("#FF3A05");
setSecondaryColor("#FF0080");
setPrimaryColor("#ff3a05");
setSecondaryColor("#ff0080");
// Marcar todos os steps como completos e ir pro step 5
setCompletedSteps([1, 2, 3, 4]);
@@ -529,9 +537,9 @@ export default function CadastroPage() {
const getPasswordStrengthColor = () => {
if (passwordStrength <= 1) return "#EF4444";
if (passwordStrength === 2) return "#F59E0B";
if (passwordStrength === 3) return "#3B82F6";
if (passwordStrength === 4) return "#10B981";
return "#059669";
if (passwordStrength === 3) return "#ff3a05";
if (passwordStrength === 4) return "#ff3a05";
return "#ff3a05";
};
const fetchCnpjData = async (cnpj: string) => {
@@ -607,7 +615,7 @@ export default function CadastroPage() {
error: {
icon: '⚠️',
style: {
background: '#ff3a05',
background: '#ef4444',
color: '#FFFFFF',
border: 'none',
},
@@ -660,8 +668,8 @@ export default function CadastroPage() {
/>
<defs>
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stopColor="#FF3A05" />
<stop offset="100%" stopColor="#FF0080" />
<stop offset="0%" stopColor="#ff3a05" />
<stop offset="100%" stopColor="#ff0080" />
</linearGradient>
</defs>
</svg>
@@ -763,7 +771,11 @@ export default function CadastroPage() {
label={
<span>
Concordo com os{" "}
<Link href="/termos" className="bg-linear-to-r from-[#FF3A05] to-[#FF0080] bg-clip-text text-transparent hover:underline cursor-pointer font-medium">
<Link
href="/termos"
className="font-medium hover:underline cursor-pointer"
style={{ color: 'var(--brand-color)' }}
>
Termos de Uso
</Link>
</span>
@@ -779,7 +791,11 @@ export default function CadastroPage() {
{/* Link para login */}
<p className="text-center mt-6 text-[14px] text-[#7D7D7D]">
possui uma conta?{" "}
<Link href="/login" className="bg-linear-to-r from-[#FF3A05] to-[#FF0080] bg-clip-text text-transparent font-medium hover:underline cursor-pointer">
<Link
href="/login"
className="font-medium hover:underline cursor-pointer"
style={{ color: 'var(--brand-color)' }}
>
Fazer login
</Link>
</p>
@@ -829,13 +845,13 @@ export default function CadastroPage() {
disabled
/>
<div>
<label className="block text-[13px] font-semibold text-[#000000] mb-2">
Descrição Breve<span className="text-[#FF3A05] ml-1">*</span>
<label className="block text-[13px] font-semibold text-zinc-900 mb-2">
Descrição Breve<span className="ml-1" style={{ color: 'var(--brand-color)' }}>*</span>
</label>
<textarea
name="description"
placeholder="Apresente sua empresa em poucas palavras (máx 300 caracteres)"
className="w-full px-3.5 py-3 text-[14px] font-normal border rounded-md bg-white placeholder:text-[#7D7D7D] border-[#E5E5E5] focus:border-[#FF3A05] outline-none ring-0 focus:ring-0 shadow-none focus:shadow-none resize-none"
className="w-full px-3.5 py-3 text-[14px] font-normal border rounded-md bg-white placeholder:text-zinc-500 border-zinc-200 outline-none ring-0 shadow-none focus:shadow-none resize-none focus:border-[var(--brand-color)]"
rows={4}
maxLength={300}
value={formData.description || ''}
@@ -989,19 +1005,19 @@ export default function CadastroPage() {
</div>
{/* Contatos da Empresa */}
<div className="pt-4 border-t border-[#E5E5E5]">
<div className="pt-4 border-t border-zinc-200">
<div className="space-y-4">
<div className="flex items-center justify-between">
<h3 className="text-sm font-semibold text-[#000000]">Contatos da Empresa</h3>
<h3 className="text-sm font-semibold text-zinc-900">Contatos da Empresa</h3>
</div>
{contacts.map((contact, index) => (
<div key={contact.id} className="space-y-4 p-4 border border-[#E5E5E5] rounded-md bg-white">
<div key={contact.id} className="space-y-4 p-4 border border-zinc-200 rounded-md bg-white">
{contacts.length > 1 && (
<div className="flex items-center justify-end -mt-2 -mr-2">
<button
type="button"
onClick={() => removeContact(contact.id)}
className="text-[#7D7D7D] hover:text-[#FF3A05] transition-colors"
className="text-zinc-500 transition-colors hover:text-[var(--brand-color)]"
>
<i className="ri-close-line text-[18px]" />
</button>
@@ -1044,8 +1060,8 @@ export default function CadastroPage() {
<div className="space-y-6">
{/* Subdomínio Aggios */}
<div className="space-y-2">
<label className="block text-sm font-medium text-[#000000]">
Subdomínio Aggios <span className="text-[#FF3A05]">*</span>
<label className="block text-sm font-medium text-zinc-900">
Subdomínio Aggios <span style={{ color: 'var(--brand-color)' }}>*</span>
</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
@@ -1061,12 +1077,12 @@ export default function CadastroPage() {
}}
onBlur={() => subdomain && checkDomainAvailability(subdomain)}
placeholder="minhaempresa"
className="w-full pl-10 pr-4 py-2 text-sm border border-[#E5E5E5] rounded-md focus:border-[#FF3A05] transition-colors"
className="w-full pl-10 pr-4 py-2 text-sm border border-zinc-200 rounded-md transition-colors focus:border-[var(--brand-color)]"
required
/>
{checkingDomain && (
<div className="absolute inset-y-0 right-0 flex items-center pr-3">
<div className="w-4 h-4 border-2 border-[#FF3A05] border-t-transparent rounded-full animate-spin" />
<div className="w-4 h-4 border-2 border-[var(--brand-color)] border-t-transparent rounded-full animate-spin" />
</div>
)}
{!checkingDomain && domainAvailable === true && (
@@ -1076,13 +1092,13 @@ export default function CadastroPage() {
)}
{!checkingDomain && domainAvailable === false && (
<div className="absolute inset-y-0 right-0 flex items-center pr-3">
<i className="ri-close-circle-fill text-[#FF3A05] text-[20px]" />
<i className="ri-close-circle-fill text-red-500 text-[20px]" />
</div>
)}
</div>
<p className="text-xs text-[#7D7D7D] flex items-center gap-1">
<p className="text-xs text-zinc-600 flex items-center gap-1">
<i className="ri-information-line" />
Seu painel ficará em: <span className="font-medium text-[#000000]">{subdomain || 'seu-dominio'}.aggios.app</span>
Seu painel ficará em: <span className="font-medium text-zinc-900">{subdomain || 'seu-dominio'}.aggios.app</span>
</p>
{domainAvailable === true && (
<p className="text-xs text-[#10B981] flex items-center gap-1">
@@ -1091,7 +1107,7 @@ export default function CadastroPage() {
</p>
)}
{domainAvailable === false && (
<p className="text-xs text-[#FF3A05] flex items-center gap-1">
<p className="text-xs text-red-500 flex items-center gap-1">
<i className="ri-error-warning-line" />
Indisponível. Este subdomínio está em uso.
</p>
@@ -1100,11 +1116,11 @@ export default function CadastroPage() {
{/* Informações Adicionais */}
<div className="p-6 bg-[#F5F5F5] rounded-md space-y-3">
<h4 className="text-sm font-semibold text-[#000000] flex items-center gap-2">
<i className="ri-lightbulb-line text-[#FF3A05]" />
<h4 className="text-sm font-semibold text-zinc-900 flex items-center gap-2">
<i className="ri-lightbulb-line" style={{ color: 'var(--brand-color)' }} />
Dicas para escolher seu domínio
</h4>
<ul className="text-xs text-[#7D7D7D] space-y-1 ml-6">
<ul className="text-xs text-zinc-600 space-y-1 ml-6">
<li className="list-disc">Use o nome da sua empresa</li>
<li className="list-disc">Evite números e hífens quando possível</li>
<li className="list-disc">Escolha algo fácil de lembrar e digitar</li>
@@ -1121,7 +1137,14 @@ export default function CadastroPage() {
<button
type="button"
onClick={() => setShowPreviewMobile(!showPreviewMobile)}
className="w-full flex items-center justify-center gap-2 px-4 py-3 rounded-lg border-2 border-[#FF3A05] text-[#FF3A05] font-medium hover:bg-[#FF3A05]/5 transition-colors"
className="w-full flex items-center justify-center gap-2 px-4 py-3 rounded-lg border-2 font-medium transition-colors"
style={{
borderColor: 'var(--brand-color)',
color: 'var(--brand-color)',
backgroundColor: showPreviewMobile ? 'transparent' : undefined
}}
onMouseEnter={(e) => (e.currentTarget.style.backgroundColor = 'color-mix(in srgb, var(--brand-color) 10%, transparent)')}
onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = 'transparent')}
>
<i className={`${showPreviewMobile ? 'ri-edit-line' : 'ri-eye-line'} text-xl`} />
{showPreviewMobile ? 'Voltar ao Formulário' : 'Ver Preview do Painel'}
@@ -1177,7 +1200,7 @@ export default function CadastroPage() {
/>
<label
htmlFor="logo-upload"
className="inline-flex items-center gap-2 px-4 py-2 border border-[#E5E5E5] rounded-md text-sm font-medium text-[#000000] hover:bg-[#F5F5F5] transition-colors cursor-pointer"
className="inline-flex items-center gap-2 px-4 py-2 border border-zinc-200 rounded-md text-sm font-medium text-zinc-900 hover:bg-zinc-50 transition-colors cursor-pointer"
>
<i className="ri-upload-2-line" />
Escolher arquivo
@@ -1186,12 +1209,13 @@ export default function CadastroPage() {
<button
type="button"
onClick={() => setLogoUrl('')}
className="ml-2 text-sm bg-linear-to-r from-[#FF3A05] to-[#FF0080] bg-clip-text text-transparent hover:underline font-medium"
className="ml-2 text-sm hover:underline font-medium"
style={{ color: 'var(--brand-color)' }}
>
Remover
</button>
)}
<p className="text-xs text-[#7D7D7D] mt-2">
<p className="text-xs text-zinc-600 mt-2">
PNG, JPG ou SVG. Tamanho recomendado: 200x200px
</p>
</div>
@@ -1202,13 +1226,13 @@ export default function CadastroPage() {
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Cor Primária */}
<div>
<label className="block text-sm font-medium text-[#000000] mb-3">
Cor Primária <span className="text-[#FF3A05]">*</span>
<label className="block text-sm font-medium text-zinc-900 mb-3">
Cor Primária <span style={{ color: 'var(--brand-color)' }}>*</span>
</label>
<div className="flex gap-3">
<div className="relative flex-1">
<div className="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
<i className="ri-palette-line text-[#7D7D7D] text-[18px]" />
<i className="ri-palette-line text-zinc-500 text-[18px]" />
</div>
<input
type="text"
@@ -1219,18 +1243,18 @@ export default function CadastroPage() {
setPrimaryColor(value);
}
}}
placeholder="#FF3A05"
className="w-full pl-10 pr-4 py-2 text-sm border border-[#E5E5E5] rounded-md focus:border-[#FF3A05] transition-colors font-mono"
placeholder="#ff3a05"
className="w-full pl-10 pr-4 py-2 text-sm border border-zinc-200 rounded-md transition-colors font-mono focus:border-[var(--brand-color)]"
/>
</div>
<input
type="color"
value={primaryColor}
onChange={(e) => setPrimaryColor(e.target.value)}
className="w-14 h-10 border-2 border-[#E5E5E5] rounded-md cursor-pointer"
className="w-14 h-10 border-2 border-zinc-200 rounded-md cursor-pointer"
/>
</div>
<p className="text-xs text-[#7D7D7D] mt-1 flex items-center gap-1">
<p className="text-xs text-zinc-600 mt-1 flex items-center gap-1">
<i className="ri-information-line" />
Usada em menus, botões e destaques
</p>
@@ -1238,13 +1262,13 @@ export default function CadastroPage() {
{/* Cor Secundária */}
<div>
<label className="block text-sm font-medium text-[#000000] mb-3">
Cor Secundária <span className="text-[#7D7D7D]">(opcional)</span>
<label className="block text-sm font-medium text-zinc-900 mb-3">
Cor Secundária <span className="text-zinc-500">(opcional)</span>
</label>
<div className="flex gap-3">
<div className="relative flex-1">
<div className="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
<i className="ri-brush-line text-[#7D7D7D] text-[18px]" />
<i className="ri-brush-line text-zinc-500 text-[18px]" />
</div>
<input
type="text"
@@ -1255,18 +1279,18 @@ export default function CadastroPage() {
setSecondaryColor(value);
}
}}
placeholder="#FF0080"
className="w-full pl-10 pr-4 py-2 text-sm border border-[#E5E5E5] rounded-md focus:border-[#FF3A05] transition-colors font-mono"
placeholder="#ff0080"
className="w-full pl-10 pr-4 py-2 text-sm border border-zinc-200 rounded-md transition-colors font-mono focus:border-[var(--brand-color)]"
/>
</div>
<input
type="color"
value={secondaryColor}
onChange={(e) => setSecondaryColor(e.target.value)}
className="w-14 h-10 border-2 border-[#E5E5E5] rounded-md cursor-pointer"
className="w-14 h-10 border-2 border-zinc-200 rounded-md cursor-pointer"
/>
</div>
<p className="text-xs text-[#7D7D7D] mt-1 flex items-center gap-1">
<p className="text-xs text-zinc-600 mt-1 flex items-center gap-1">
<i className="ri-information-line" />
Usada em cards e elementos secundários
</p>
@@ -1275,10 +1299,10 @@ export default function CadastroPage() {
{/* Paletas Sugeridas */}
<div>
<h4 className="text-sm font-semibold text-[#000000] mb-4">Paletas Sugeridas</h4>
<h4 className="text-sm font-semibold text-zinc-900 mb-4">Paletas Sugeridas</h4>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{[
{ name: 'Fogo', primary: '#FF3A05', secondary: '#FF0080' },
{ name: 'Marca', primary: '#FF3A05', secondary: '#FF0080' },
{ name: 'Oceano', primary: '#0EA5E9', secondary: '#3B82F6' },
{ name: 'Natureza', primary: '#10B981', secondary: '#059669' },
{ name: 'Elegante', primary: '#8B5CF6', secondary: '#A78BFA' },
@@ -1294,7 +1318,8 @@ export default function CadastroPage() {
setPrimaryColor(palette.primary);
setSecondaryColor(palette.secondary);
}}
className="flex items-center gap-2 p-2 rounded-md border border-[#E5E5E5] hover:border-[#FF3A05] transition-colors group cursor-pointer"
className="flex items-center gap-2 p-2 rounded-md border border-zinc-200 transition-colors group cursor-pointer"
style={{ borderColor: palette.name === 'Marca' ? 'var(--brand-color)' : undefined }}
>
<div className="flex gap-1">
<div
@@ -1306,7 +1331,7 @@ export default function CadastroPage() {
style={{ backgroundColor: palette.secondary }}
/>
</div>
<span className="text-xs font-medium text-[#7D7D7D] group-hover:text-[#000000]">
<span className="text-xs font-medium text-zinc-600 group-hover:text-zinc-900">
{palette.name}
</span>
</button>
@@ -1317,7 +1342,7 @@ export default function CadastroPage() {
{/* Informações */}
<div className="p-6 bg-[#F0F9FF] border border-[#BAE6FD] rounded-md">
<div className="flex gap-4">
<i className="ri-information-line text-[#0EA5E9] text-xl mt-0.5" />
<i className="ri-information-line text-[#ff3a05] text-xl mt-0.5" />
<div>
<h4 className="text-sm font-semibold text-[#000000] mb-1">
Você pode alterar depois
@@ -1337,7 +1362,7 @@ export default function CadastroPage() {
</div>
{/* Rodapé - botão voltar à esquerda, etapas e botão ação à direita */}
<div className="border-t border-[#E5E5E5] bg-white px-4 sm:px-12 py-4">
<div className="border-t border-zinc-200 bg-white px-4 sm:px-12 py-4">
{/* Desktop: Linha única com tudo */}
<div className="hidden md:flex items-center justify-between">
{/* Botão voltar à esquerda */}
@@ -1363,21 +1388,21 @@ export default function CadastroPage() {
? "bg-[#10B981] text-white"
: currentStep === step.number
? "text-white"
: "bg-[#E5E5E5] text-[#7D7D7D] group-hover:bg-[#D5D5D5]"
: "bg-zinc-200 text-zinc-500 group-hover:bg-zinc-300"
}`}
style={currentStep === step.number ? { background: 'linear-gradient(90deg, #FF3A05, #FF0080)' } : undefined}
style={currentStep === step.number ? { background: 'var(--gradient-primary)' } : undefined}
>
{step.number}
</div>
<span className={`text-xs transition-colors ${currentStep === step.number
? "text-[#000000] font-semibold"
: "text-[#7D7D7D] group-hover:text-[#000000]"
? "text-zinc-900 font-semibold"
: "text-zinc-500 group-hover:text-zinc-900"
}`}>
{step.title}
</span>
</button>
{index < steps.length - 1 && (
<div className="w-12 h-0.5 bg-[#E5E5E5] mb-5" />
<div className="w-12 h-0.5 bg-zinc-200 mb-5" />
)}
</div>
))}
@@ -1407,9 +1432,9 @@ export default function CadastroPage() {
? "w-2 bg-[#10B981]"
: currentStep === step.number
? "w-8"
: "w-2 bg-[#E5E5E5] hover:bg-[#D5D5D5]"
: "w-2 bg-zinc-200 hover:bg-zinc-300"
}`}
style={currentStep === step.number ? { background: 'linear-gradient(90deg, #FF3A05, #FF0080)' } : undefined}
style={currentStep === step.number ? { background: 'var(--gradient-primary)' } : undefined}
aria-label={`Ir para ${step.title}`}
/>
))}
@@ -1442,7 +1467,7 @@ export default function CadastroPage() {
</div>
{/* Lado Direito - Branding Dinâmico */}
<div className="hidden lg:flex lg:w-[50%] relative overflow-hidden" style={{ background: 'linear-gradient(90deg, #FF3A05, #FF0080)' }}>
<div className="hidden lg:flex lg:w-[50%] relative overflow-hidden" style={{ background: 'var(--gradient-primary)' }}>
<DynamicBranding
currentStep={currentStep}
companyName={formData.companyName}

View File

@@ -56,7 +56,7 @@ export default function RecuperarSenhaPage() {
error: {
icon: '⚠️',
style: {
background: '#ff3a05',
background: '#ef4444',
color: '#FFFFFF',
border: 'none',
},
@@ -150,7 +150,7 @@ export default function RecuperarSenhaPage() {
<div className="p-6 bg-[#F0F9FF] border border-[#BAE6FD] rounded-md text-left mb-6">
<div className="flex gap-4">
<i className="ri-information-line text-[#0EA5E9] text-xl mt-0.5" />
<i className="ri-information-line text-[#ff3a05] text-xl mt-0.5" />
<div>
<h4 className="text-sm font-semibold text-zinc-900 dark:text-white mb-1">
Verifique sua caixa de entrada

View File

@@ -1,7 +1,25 @@
'use client';
import { ReactNode } from 'react';
import { useEffect } from 'react';
import { usePathname } from 'next/navigation';
const DEFAULT_GRADIENT = 'linear-gradient(135deg, #ff3a05, #ff0080)';
const setGradientVariables = (gradient: string) => {
document.documentElement.style.setProperty('--gradient-primary', gradient);
document.documentElement.style.setProperty('--gradient', gradient);
document.documentElement.style.setProperty('--gradient-text', gradient.replace('90deg', 'to right'));
document.documentElement.style.setProperty('--color-gradient-brand', gradient.replace('90deg', 'to right'));
};
export default function LayoutWrapper({ children }: { children: ReactNode }) {
const pathname = usePathname();
useEffect(() => {
// Em toda troca de rota, volta para o tema padrão; layouts específicos (ex.: agência) aplicam o próprio na sequência
setGradientVariables(DEFAULT_GRADIENT);
}, [pathname]);
return <>{children}</>;
}

View File

@@ -61,6 +61,14 @@ html.dark {
color: var(--color-text-inverse);
}
/* Seleção em campos de formulário usa o gradiente padrão da marca */
input::selection,
textarea::selection,
select::selection {
background: var(--color-gradient-brand);
color: var(--color-text-inverse);
}
.surface-card {
background-color: var(--color-surface-card);
border: 1px solid var(--color-border-strong);

View File

@@ -9,6 +9,15 @@ import dynamic from 'next/dynamic';
const ThemeToggle = dynamic(() => import('@/components/ThemeToggle'), { ssr: false });
const DEFAULT_GRADIENT = 'linear-gradient(135deg, #ff3a05, #ff0080)';
const setGradientVariables = (gradient: string) => {
document.documentElement.style.setProperty('--gradient-primary', gradient);
document.documentElement.style.setProperty('--gradient', gradient);
document.documentElement.style.setProperty('--gradient-text', gradient.replace('90deg', 'to right'));
document.documentElement.style.setProperty('--color-gradient-brand', gradient.replace('90deg', 'to right'));
};
export default function LoginPage() {
const [isLoading, setIsLoading] = useState(false);
const [isSuperAdmin, setIsSuperAdmin] = useState(false);
@@ -27,6 +36,22 @@ export default function LoginPage() {
setSubdomain(sub);
setIsSuperAdmin(superAdmin);
// Aplicar tema: dash sempre padrão; tenants aplicam o salvo ou vindo via query param
const searchParams = new URLSearchParams(window.location.search);
const themeParam = searchParams.get('theme');
if (superAdmin) {
setGradientVariables(DEFAULT_GRADIENT);
} else {
const stored = localStorage.getItem(`agency-theme:${sub}`);
const gradient = themeParam || stored || DEFAULT_GRADIENT;
setGradientVariables(gradient);
if (themeParam) {
localStorage.setItem(`agency-theme:${sub}`, gradient);
}
}
if (isAuthenticated()) {
const target = superAdmin ? '/superadmin' : '/dashboard';
window.location.href = target;
@@ -106,7 +131,7 @@ export default function LoginPage() {
error: {
icon: '⚠️',
style: {
background: '#ff3a05',
background: '#ef4444',
color: '#FFFFFF',
border: 'none',
},

View File

@@ -11,7 +11,7 @@ export default function NotFound() {
<div className="w-full max-w-md text-center">
{/* Logo mobile */}
<div className="lg:hidden mb-8">
<div className="inline-block px-6 py-3 rounded-2xl bg-linear-to-r from-[#FF3A05] to-[#FF0080]">
<div className="inline-block px-6 py-3 rounded-2xl bg-linear-to-r from-brand-500 to-brand-700">
<h1 className="text-3xl font-bold text-white">aggios</h1>
</div>
</div>
@@ -82,7 +82,7 @@ export default function NotFound() {
</div>
{/* Lado Direito - Branding */}
<div className="hidden lg:flex lg:w-1/2 relative overflow-hidden" style={{ background: 'linear-gradient(90deg, #FF3A05, #FF0080)' }}>
<div className="hidden lg:flex lg:w-1/2 relative overflow-hidden" style={{ background: 'var(--gradient-primary)' }}>
<div className="relative z-10 flex flex-col justify-center items-center w-full p-12 text-white">
{/* Logo */}
<div className="mb-8">

View File

@@ -165,11 +165,11 @@ export default function PainelPage() {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-[#FF3A05] mx-auto mb-4"></div>
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-brand-500 mx-auto mb-4"></div>
<p className="text-gray-600 dark:text-gray-400">Carregando...</p>
</div>
{detailsLoadingId && (
<div className="mt-8 bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-dashed border-[#FF3A05] p-6 text-sm text-gray-600 dark:text-gray-300">
<div className="mt-8 bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-dashed border-brand-500 p-6 text-sm text-gray-600 dark:text-gray-300">
Carregando detalhes da agência selecionada...
</div>
)}
@@ -192,7 +192,7 @@ export default function PainelPage() {
href={selectedDetails.access_url}
target="_blank"
rel="noopener noreferrer"
className="text-sm text-[#FF3A05] hover:text-[#FF0080]"
className="text-sm text-brand-600 hover:text-brand-700"
>
Abrir painel da agência
</a>
@@ -260,7 +260,7 @@ export default function PainelPage() {
<div>
<p className="text-gray-500 dark:text-gray-400">Website</p>
{selectedDetails.tenant.website ? (
<a href={selectedDetails.tenant.website} target="_blank" rel="noopener noreferrer" className="text-[#FF3A05] hover:text-[#FF0080]">
<a href={selectedDetails.tenant.website} target="_blank" rel="noopener noreferrer" className="text-brand-600 hover:text-brand-700">
{selectedDetails.tenant.website}
</a>
) : (
@@ -321,7 +321,7 @@ export default function PainelPage() {
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<div className="flex items-center justify-center w-10 h-10 bg-gradient-to-r from-[#FF3A05] to-[#FF0080] rounded-lg">
<div className="flex items-center justify-center w-10 h-10 bg-gradient-to-r from-brand-500 to-brand-700 rounded-lg">
<span className="text-white font-bold text-lg">A</span>
</div>
<div>
@@ -403,7 +403,7 @@ export default function PainelPage() {
{loadingAgencies ? (
<div className="p-8 text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-[#FF3A05] mx-auto mb-4"></div>
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-brand-500 mx-auto mb-4"></div>
<p className="text-gray-600 dark:text-gray-400">Carregando agências...</p>
</div>
) : agencies.length === 0 ? (
@@ -427,11 +427,11 @@ export default function PainelPage() {
{agencies.map((agency) => (
<tr
key={agency.id}
className={`hover:bg-gray-50 dark:hover:bg-gray-700 ${selectedAgencyId === agency.id ? 'bg-orange-50/60 dark:bg-gray-700/60' : ''}`}
className={`hover:bg-gray-50 dark:hover:bg-gray-700 ${selectedAgencyId === agency.id ? 'bg-brand-50/70 dark:bg-gray-700/60' : ''}`}
>
<td className="px-6 py-4 whitespace-nowrap">
<div className="flex items-center">
<div className="flex-shrink-0 h-10 w-10 bg-gradient-to-br from-[#FF3A05] to-[#FF0080] rounded-lg flex items-center justify-center">
<div className="flex-shrink-0 h-10 w-10 bg-gradient-to-br from-brand-500 to-brand-700 rounded-lg flex items-center justify-center">
<span className="text-white font-bold">{agency.name.charAt(0).toUpperCase()}</span>
</div>
<div className="ml-4">
@@ -459,7 +459,7 @@ export default function PainelPage() {
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium space-x-2">
<button
onClick={() => handleViewDetails(agency.id)}
className="inline-flex items-center px-3 py-1.5 rounded-md bg-[#FF3A05] text-white hover:bg-[#FF0080] transition"
className="inline-flex items-center px-3 py-1.5 rounded-md bg-gradient-to-r from-brand-500 to-brand-700 text-white hover:opacity-90 transition"
disabled={detailsLoadingId === agency.id || deletingId === agency.id}
>
{detailsLoadingId === agency.id ? 'Carregando...' : 'Visualizar'}

View File

@@ -1,10 +1,14 @@
@layer theme {
:root {
/* Gradientes */
--gradient: linear-gradient(90deg, #FF3A05, #FF0080);
--gradient-text: linear-gradient(to right, #FF3A05, #FF0080);
--gradient-primary: linear-gradient(90deg, #FF3A05, #FF0080);
--color-gradient-brand: linear-gradient(to right, #FF3A05, #FF0080);
--gradient: linear-gradient(135deg, #ff3a05, #ff0080);
--gradient-text: linear-gradient(to right, #ff3a05, #ff0080);
--gradient-primary: linear-gradient(135deg, #ff3a05, #ff0080);
--color-gradient-brand: linear-gradient(135deg, #ff3a05, #ff0080);
/* Cores sólidas de marca (usadas em textos/bordas) */
--brand-color: #ff3a05;
--brand-color-strong: #ff0080;
/* Superfícies e tipografia */
--color-surface-light: #ffffff;

View File

@@ -5,36 +5,36 @@ import { SwatchIcon } from '@heroicons/react/24/outline';
const themePresets = [
{
name: 'Laranja/Rosa (Padrão)',
gradient: 'linear-gradient(90deg, #FF3A05, #FF0080)',
name: 'Azul (Marca)',
gradient: 'linear-gradient(135deg, #0ea5e9, #0284c7)',
},
{
name: 'Azul/Roxo',
gradient: 'linear-gradient(90deg, #0066FF, #9333EA)',
gradient: 'linear-gradient(135deg, #0066FF, #9333EA)',
},
{
name: 'Verde/Esmeralda',
gradient: 'linear-gradient(90deg, #10B981, #059669)',
gradient: 'linear-gradient(135deg, #10B981, #059669)',
},
{
name: 'Ciano/Azul',
gradient: 'linear-gradient(90deg, #06B6D4, #3B82F6)',
gradient: 'linear-gradient(135deg, #06B6D4, #3B82F6)',
},
{
name: 'Rosa/Roxo',
gradient: 'linear-gradient(90deg, #EC4899, #A855F7)',
gradient: 'linear-gradient(135deg, #EC4899, #A855F7)',
},
{
name: 'Vermelho/Laranja',
gradient: 'linear-gradient(90deg, #EF4444, #F97316)',
gradient: 'linear-gradient(135deg, #EF4444, #F97316)',
},
{
name: 'Índigo/Violeta',
gradient: 'linear-gradient(90deg, #6366F1, #8B5CF6)',
gradient: 'linear-gradient(135deg, #6366F1, #8B5CF6)',
},
{
name: 'Âmbar/Amarelo',
gradient: 'linear-gradient(90deg, #F59E0B, #EAB308)',
gradient: 'linear-gradient(135deg, #F59E0B, #EAB308)',
},
];
@@ -44,8 +44,8 @@ export default function ThemeTester() {
const applyTheme = (gradient: string) => {
document.documentElement.style.setProperty('--gradient-primary', gradient);
document.documentElement.style.setProperty('--gradient', gradient);
document.documentElement.style.setProperty('--gradient-text', gradient.replace('90deg', 'to right'));
document.documentElement.style.setProperty('--color-gradient-brand', gradient.replace('90deg', 'to right'));
document.documentElement.style.setProperty('--gradient-text', gradient);
document.documentElement.style.setProperty('--color-gradient-brand', gradient);
};
return (

View File

@@ -16,8 +16,8 @@ export default function DynamicBranding({
currentStep,
companyName = '',
subdomain = '',
primaryColor = '#FF3A05',
secondaryColor = '#FF0080',
primaryColor = '#0ea5e9',
secondaryColor = '#0284c7',
logoUrl = ''
}: DynamicBrandingProps) {
const [activeTestimonial, setActiveTestimonial] = useState(0);

View File

@@ -26,7 +26,7 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>(
ref
) => {
const baseStyles =
"inline-flex items-center justify-center font-medium rounded-[6px] transition-opacity focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#FF3A05] disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer";
"inline-flex items-center justify-center font-medium rounded-[6px] transition-opacity focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-500 disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer";
const variants = {
primary: "text-white hover:opacity-90 active:opacity-80",

View File

@@ -29,9 +29,9 @@ const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
type="checkbox"
className={`
appearance-none w-[18px] h-[18px] border rounded-sm
border-[#E5E5E5] dark:border-gray-600 bg-white dark:bg-gray-700
checked:border-[#FF3A05]
focus:outline-none focus:border-[#FF3A05]
border-zinc-200 dark:border-gray-600 bg-white dark:bg-gray-700
checked:border-brand-500
focus:outline-none focus:border-brand-500
transition-colors cursor-pointer
${className}
`}
@@ -48,13 +48,13 @@ const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
/>
</div>
{label && (
<span className="text-[14px] text-[#000000] dark:text-white select-none">
<span className="text-[14px] text-zinc-900 dark:text-white select-none">
{label}
</span>
)}
</label>
{error && (
<p className="mt-1 text-[13px] text-[#FF3A05] flex items-center gap-1">
<p className="mt-1 text-[13px] text-red-500 flex items-center gap-1">
<i className="ri-error-warning-line" />
{error}
</p>

View File

@@ -34,9 +34,9 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
return (
<div className="w-full">
{label && (
<label className="block text-[13px] font-semibold text-[#000000] dark:text-white mb-2">
<label className="block text-[13px] font-semibold text-zinc-900 dark:text-white mb-2">
{label}
{props.required && <span className="text-[#FF3A05] ml-1">*</span>}
{props.required && <span className="text-brand-500 ml-1">*</span>}
</label>
)}
<div className="relative">
@@ -51,16 +51,16 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
className={`
w-full px-3.5 py-3 text-[14px] font-normal
border rounded-md bg-white dark:bg-gray-700 dark:text-white
placeholder:text-[#7D7D7D] dark:placeholder:text-gray-400
placeholder:text-zinc-500 dark:placeholder:text-gray-400
transition-all
${leftIcon ? "pl-11" : ""}
${isPassword || rightIcon ? "pr-11" : ""}
${error
? "border-[#FF3A05]"
: "border-[#E5E5E5] dark:border-gray-600 focus:border-[#FF3A05]"
? "border-red-500 focus:border-red-500"
: "border-zinc-200 dark:border-gray-600 focus:border-brand-500"
}
outline-none ring-0 focus:ring-0 shadow-none focus:shadow-none
disabled:bg-[#E5E5E5]/30 disabled:cursor-not-allowed
disabled:bg-zinc-100 disabled:cursor-not-allowed
${className}
`}
{...props}
@@ -69,7 +69,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3.5 top-1/2 -translate-y-1/2 text-[#7D7D7D] hover:text-[#000000] transition-colors cursor-pointer"
className="absolute right-3.5 top-1/2 -translate-y-1/2 text-zinc-500 hover:text-zinc-900 transition-colors cursor-pointer"
>
<i
className={`${showPassword ? "ri-eye-off-line" : "ri-eye-line"} text-[20px]`}
@@ -80,20 +80,20 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
<button
type="button"
onClick={onRightIconClick}
className="absolute right-3.5 top-1/2 -translate-y-1/2 text-[#7D7D7D] hover:text-[#000000] transition-colors cursor-pointer"
className="absolute right-3.5 top-1/2 -translate-y-1/2 text-zinc-500 hover:text-zinc-900 transition-colors cursor-pointer"
>
<i className={`${rightIcon} text-[20px]`} />
</button>
)}
</div>
{error && (
<p className="mt-1 text-[13px] text-[#FF3A05] flex items-center gap-1">
<p className="mt-1 text-[13px] text-red-500 flex items-center gap-1">
<i className="ri-error-warning-line" />
{error}
</p>
)}
{helperText && !error && (
<p className="mt-1 text-[13px] text-[#7D7D7D]">{helperText}</p>
<p className="mt-1 text-[13px] text-zinc-500">{helperText}</p>
)}
</div>
);

View File

@@ -109,7 +109,7 @@ const SearchableSelect = forwardRef<HTMLSelectElement, SearchableSelectProps>(
{label && (
<label className="block text-[13px] font-semibold text-zinc-900 dark:text-white mb-2">
{label}
{required && <span className="text-[#FF3A05] ml-1">*</span>}
{required && <span className="text-brand-500 ml-1">*</span>}
</label>
)}
@@ -133,8 +133,8 @@ const SearchableSelect = forwardRef<HTMLSelectElement, SearchableSelectProps>(
${leftIcon ? "pl-11" : ""}
pr-11
${error
? "border-[#FF3A05]"
: "border-zinc-200 dark:border-zinc-700 focus:border-[#FF3A05]"
? "border-red-500 focus:border-red-500"
: "border-zinc-200 dark:border-zinc-700 focus:border-brand-500"
}
outline-none ring-0 focus:ring-0 shadow-none focus:shadow-none
${className}
@@ -160,7 +160,7 @@ const SearchableSelect = forwardRef<HTMLSelectElement, SearchableSelectProps>(
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Buscar..."
className="w-full pl-9 pr-3 py-2 text-[14px] border border-zinc-200 dark:border-zinc-700 rounded-md outline-none focus:border-[#FF3A05] shadow-none bg-white dark:bg-zinc-900 text-zinc-900 dark:text-white placeholder:text-zinc-500 dark:placeholder:text-zinc-400"
className="w-full pl-9 pr-3 py-2 text-[14px] border border-zinc-200 dark:border-zinc-700 rounded-md outline-none focus:border-brand-500 shadow-none bg-white dark:bg-zinc-900 text-zinc-900 dark:text-white placeholder:text-zinc-500 dark:placeholder:text-zinc-400"
/>
</div>
</div>
@@ -176,7 +176,7 @@ const SearchableSelect = forwardRef<HTMLSelectElement, SearchableSelectProps>(
className={`
w-full px-4 py-2.5 text-left text-[14px] transition-colors
hover:bg-zinc-100 dark:hover:bg-zinc-700 cursor-pointer
${selectedOption?.value === option.value ? 'bg-[#FF3A05]/10 text-[#FF3A05] font-medium' : 'text-zinc-900 dark:text-white'}
${selectedOption?.value === option.value ? 'bg-brand-500/10 text-brand-600 font-medium' : 'text-zinc-900 dark:text-white'}
`}
>
{option.label}
@@ -196,7 +196,7 @@ const SearchableSelect = forwardRef<HTMLSelectElement, SearchableSelectProps>(
<p className="mt-1.5 text-[12px] text-zinc-600 dark:text-zinc-400">{helperText}</p>
)}
{error && (
<p className="mt-1 text-[13px] text-[#FF3A05] flex items-center gap-1">
<p className="mt-1 text-[13px] text-red-500 flex items-center gap-1">
<i className="ri-error-warning-line" />
{error}
</p>

View File

@@ -33,9 +33,9 @@ const Select = forwardRef<HTMLSelectElement, SelectProps>(
return (
<div className="w-full">
{label && (
<label className="block text-[13px] font-semibold text-[#000000] mb-2">
<label className="block text-[13px] font-semibold text-zinc-900 mb-2">
{label}
{props.required && <span className="text-[#FF3A05] ml-1">*</span>}
{props.required && <span className="text-brand-500 ml-1">*</span>}
</label>
)}
<div className="relative">
@@ -49,17 +49,17 @@ const Select = forwardRef<HTMLSelectElement, SelectProps>(
className={`
w-full px-3.5 py-3 text-[14px] font-normal
border rounded-md bg-white
text-[#000000]
text-zinc-900
transition-all appearance-none
cursor-pointer
${leftIcon ? "pl-11" : ""}
pr-11
${error
? "border-[#FF3A05]"
: "border-[#E5E5E5] focus:border-[#FF3A05]"
? "border-red-500 focus:border-red-500"
: "border-zinc-200 focus:border-brand-500"
}
outline-none ring-0 focus:ring-0 shadow-none focus:shadow-none
disabled:bg-[#F5F5F5] disabled:cursor-not-allowed
disabled:bg-zinc-100 disabled:cursor-not-allowed
${className}
`}
{...props}
@@ -76,9 +76,9 @@ const Select = forwardRef<HTMLSelectElement, SelectProps>(
<i className="ri-arrow-down-s-line absolute right-3.5 top-1/2 -translate-y-1/2 text-[#7D7D7D] text-[20px] pointer-events-none" />
</div>
{helperText && !error && (
<p className="mt-1.5 text-[12px] text-[#7D7D7D]">{helperText}</p>
<p className="mt-1.5 text-[12px] text-zinc-500">{helperText}</p>
)}
{error && <p className="mt-1.5 text-[12px] text-[#FF3A05]">{error}</p>}
{error && <p className="mt-1.5 text-[12px] text-red-500">{error}</p>}
</div>
);
}

View File

@@ -16,6 +16,9 @@ export const API_ENDPOINTS = {
refresh: `${API_BASE_URL}/api/auth/refresh`,
me: `${API_BASE_URL}/api/me`,
// Admin / Agencies
adminAgencyRegister: `${API_BASE_URL}/api/admin/agencies/register`,
// Health
health: `${API_BASE_URL}/health`,
apiHealth: `${API_BASE_URL}/api/health`,

View File

@@ -1,10 +1,12 @@
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
export async function middleware(request: NextRequest) {
const hostname = request.headers.get('host') || '';
const url = request.nextUrl;
const apiBase = process.env.API_INTERNAL_URL || 'http://backend:8080';
// Extrair subdomínio
const subdomain = hostname.split('.')[0];
@@ -17,9 +19,8 @@ export function middleware(request: NextRequest) {
// Se for agência ({subdomain}.localhost) - validar se existe
if (hostname.includes('.')) {
try {
const res = await fetch(`http://backend:8080/api/tenant/check?subdomain=${subdomain}`);
if (res.status === 404) {
// Redireciona para o host base (sem subdomínio)
const res = await fetch(`${apiBase}/api/tenant/check?subdomain=${subdomain}`);
if (!res.ok) {
const baseHost = hostname.split('.').slice(1).join('.') || hostname;
const redirectUrl = new URL(url.toString());
redirectUrl.hostname = baseHost;
@@ -27,7 +28,11 @@ export function middleware(request: NextRequest) {
return NextResponse.redirect(redirectUrl);
}
} catch (err) {
// Em caso de erro de rede, não bloquear
const baseHost = hostname.split('.').slice(1).join('.') || hostname;
const redirectUrl = new URL(url.toString());
redirectUrl.hostname = baseHost;
redirectUrl.pathname = '/';
return NextResponse.redirect(redirectUrl);
}
}

View File

@@ -10,17 +10,17 @@ module.exports = {
},
colors: {
brand: {
50: '#f0f9ff',
100: '#e0f2fe',
200: '#bae6fd',
300: '#7dd3fc',
400: '#38bdf8',
500: '#0ea5e9',
600: '#0284c7',
700: '#0369a1',
800: '#075985',
900: '#0c4a6e',
950: '#082f49',
50: '#fff4ef',
100: '#ffe8df',
200: '#ffd0c0',
300: '#ffb093',
400: '#ff8a66',
500: '#ff3a05',
600: '#ff1f45',
700: '#ff0080',
800: '#d10069',
900: '#9e0050',
950: '#4b0028',
},
surface: {
light: '#ffffff',
@@ -28,7 +28,7 @@ module.exports = {
},
},
boxShadow: {
glow: '0 0 20px rgba(14, 165, 233, 0.3)',
glow: '0 0 20px rgba(255, 58, 5, 0.25)',
},
},
},

View File

@@ -35,3 +35,37 @@ html.dark {
backdrop-filter: blur(20px);
}
}
@layer utilities {
.bg-brand {
background: var(--color-gradient-brand);
}
.bg-brand-soft {
background: linear-gradient(135deg, var(--color-brand-from-20), var(--color-brand-to-20));
}
.bg-brand-horizontal-soft {
background: linear-gradient(90deg, var(--color-brand-from-20), var(--color-brand-to-20));
}
.hover\:bg-brand:hover {
background: var(--color-gradient-brand);
}
.text-brand-gradient {
background: var(--color-gradient-brand);
-webkit-background-clip: text;
color: transparent;
}
.hover\:text-brand-gradient:hover {
background: var(--color-gradient-brand);
-webkit-background-clip: text;
color: transparent;
}
.shadow-brand-20 {
box-shadow: 0 10px 15px -3px var(--color-brand-shadow-20), 0 4px 6px -4px var(--color-brand-shadow-20);
}
}

View File

@@ -12,7 +12,7 @@ export default function Home() {
<section className="py-28 bg-white dark:bg-zinc-900 transition-colors">
<div className="max-w-7xl mx-auto px-6 lg:px-8 grid lg:grid-cols-2 gap-14 items-center">
<div>
<div className="inline-flex items-center gap-2 px-4 py-2 bg-linear-to-r from-[#FF3A05] to-[#FF0080] rounded-full text-sm font-semibold text-white shadow-lg shadow-[#FF3A05]/25 mb-8">
<div className="inline-flex items-center gap-2 px-4 py-2 bg-brand rounded-full text-sm font-semibold text-white shadow-lg shadow-brand-20 mb-8">
<i className="ri-rocket-line text-base"></i>
<span>Plataforma de Gestão Financeira</span>
</div>
@@ -26,11 +26,11 @@ export default function Home() {
</p>
<div className="flex flex-col sm:flex-row items-center gap-4">
<Link href="http://dash.localhost/cadastro" className="px-8 py-3 bg-linear-to-r from-[#FF3A05] to-[#FF0080] text-white font-semibold rounded-xl hover:opacity-90 transition-opacity shadow-lg shadow-[#FF3A05]/30">
<Link href="http://dash.localhost/cadastro" className="px-8 py-3 bg-brand text-white font-semibold rounded-xl hover:opacity-90 transition-opacity shadow-lg shadow-brand-20">
<i className="ri-arrow-right-line mr-2"></i>
Começar Grátis
</Link>
<Link href="#demo" className="px-8 py-3 rounded-xl border border-zinc-300 text-zinc-700 dark:text-white font-semibold hover:border-transparent hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] hover:text-white transition-all">
<Link href="#demo" className="px-8 py-3 rounded-xl border border-zinc-300 text-zinc-700 dark:text-white font-semibold hover:border-transparent hover:bg-brand hover:text-white transition-all">
<i className="ri-play-circle-line mr-2"></i>
Ver Demo
</Link>
@@ -49,7 +49,7 @@ export default function Home() {
</div>
<div className="relative">
<div className="absolute inset-0 blur-3xl bg-linear-to-r from-[#FF3A05]/20 to-[#FF0080]/20 rounded-[40px]"></div>
<div className="absolute inset-0 blur-3xl bg-brand-horizontal-soft rounded-[40px]"></div>
<div className="relative rounded-4xl border border-white/30 bg-white/80 dark:bg-zinc-950/80 backdrop-blur-xl shadow-2xl overflow-hidden">
<div className="flex items-center justify-between px-8 py-6 border-b border-zinc-100 dark:border-zinc-800">
<div>
@@ -76,11 +76,11 @@ export default function Home() {
<p className="font-semibold text-zinc-900 dark:text-white">Fluxo de caixa</p>
<span className="text-sm text-emerald-500">+18% este mês</span>
</div>
<div className="h-40 bg-linear-to-r from-[#FF3A05]/20 via-transparent to-[#FF0080]/20 rounded-xl relative">
<div className="h-40 bg-brand-horizontal-soft rounded-xl relative">
<div className="absolute inset-4 rounded-xl border border-dashed border-zinc-200 dark:border-zinc-800"></div>
<div className="absolute inset-0 flex items-end gap-2 p-6">
{[40, 60, 80, 50, 90, 70].map((height, index) => (
<span key={index} className="w-4 rounded-full bg-linear-to-r from-[#FF3A05] to-[#FF0080]" style={{ height: `${height}%` }}></span>
<span key={index} className="w-4 rounded-full bg-brand" style={{ height: `${height}%` }}></span>
))}
</div>
</div>
@@ -116,11 +116,11 @@ export default function Home() {
{ id: "integra", icon: "ri-share-forward-line", title: "Integrações API", desc: "Conecte a Aggios com BI, contabilidade e ferramentas internas via API segura." },
].map((item) => (
<div id={item.id} key={item.id} className="relative rounded-4xl border border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-950/80 backdrop-blur-xl p-8 shadow-lg overflow-hidden">
<div className="absolute -top-20 -right-10 h-40 w-40 rounded-full bg-linear-to-r from-[#FF3A05]/10 to-[#FF0080]/10 blur-3xl" aria-hidden="true"></div>
<div className="absolute -top-20 -right-10 h-40 w-40 rounded-full bg-brand-soft blur-3xl" aria-hidden="true"></div>
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-6 relative z-10">
<div>
<div className="inline-flex items-center gap-3 mb-4 px-4 py-2 rounded-full border border-zinc-200 dark:border-zinc-800 text-xs uppercase tracking-[0.2em] text-zinc-500">
<span className="flex h-8 w-8 items-center justify-center rounded-2xl bg-linear-to-r from-[#FF3A05] to-[#FF0080] text-white text-lg">
<span className="flex h-8 w-8 items-center justify-center rounded-2xl bg-brand text-white text-lg">
<i className={item.icon}></i>
</span>
{item.title}
@@ -132,8 +132,8 @@ export default function Home() {
<div className="w-full sm:w-48 h-36 rounded-2xl border border-dashed border-zinc-200 dark:border-zinc-800 p-4 text-sm text-zinc-500 dark:text-zinc-400 bg-white/60 dark:bg-zinc-900/60">
<p className="font-semibold text-zinc-900 dark:text-white mb-2">Módulo em ação</p>
<div className="space-y-2">
<div className="h-2 rounded-full bg-linear-to-r from-[#FF3A05] to-[#FF0080]/70"></div>
<div className="h-2 rounded-full bg-linear-to-r from-[#FF0080] to-[#FF3A05]/70 w-3/4"></div>
<div className="h-2 rounded-full bg-brand"></div>
<div className="h-2 rounded-full bg-brand w-3/4"></div>
<div className="h-2 rounded-full bg-zinc-200 dark:bg-zinc-800 w-2/3"></div>
</div>
</div>
@@ -157,48 +157,48 @@ export default function Home() {
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<div className="bg-white dark:bg-zinc-900 p-8 rounded-2xl border border-zinc-200 dark:border-zinc-700 hover:border-transparent hover:shadow-lg hover:shadow-[#FF3A05]/20 dark:hover:shadow-[#FF3A05]/10 transition-all">
<div className="w-12 h-12 bg-linear-to-r from-[#FF3A05] to-[#FF0080] rounded-xl flex items-center justify-center mb-6">
<div className="bg-white dark:bg-zinc-900 p-8 rounded-2xl border border-zinc-200 dark:border-zinc-700 hover:border-transparent hover:shadow-lg hover:shadow-brand-20 transition-all">
<div className="w-12 h-12 bg-brand rounded-xl flex items-center justify-center mb-6">
<i className="ri-dashboard-3-line text-2xl text-white"></i>
</div>
<h3 className="font-heading font-bold text-xl text-zinc-900 dark:text-white mb-4 transition-colors">Dashboard Inteligente</h3>
<p className="text-zinc-600 dark:text-zinc-400 leading-relaxed transition-colors">Visualize todos os seus dados financeiros em tempo real com gráficos e métricas intuitivas.</p>
</div>
<div className="bg-white dark:bg-zinc-900 p-8 rounded-2xl border border-zinc-200 dark:border-zinc-700 hover:border-transparent hover:shadow-lg hover:shadow-[#FF3A05]/20 dark:hover:shadow-[#FF3A05]/10 transition-all">
<div className="w-12 h-12 bg-linear-to-r from-[#FF3A05] to-[#FF0080] rounded-xl flex items-center justify-center mb-6">
<div className="bg-white dark:bg-zinc-900 p-8 rounded-2xl border border-zinc-200 dark:border-zinc-700 hover:border-transparent hover:shadow-lg hover:shadow-brand-20 transition-all">
<div className="w-12 h-12 bg-brand rounded-xl flex items-center justify-center mb-6">
<i className="ri-team-line text-2xl text-white"></i>
</div>
<h3 className="font-heading font-bold text-xl text-zinc-900 dark:text-white mb-4 transition-colors">Gestão de Clientes</h3>
<p className="text-zinc-600 dark:text-zinc-400 leading-relaxed transition-colors">Organize e acompanhe todos os seus clientes com informações detalhadas e histórico completo.</p>
</div>
<div className="bg-white dark:bg-zinc-900 p-8 rounded-2xl border border-zinc-200 dark:border-zinc-700 hover:border-transparent hover:shadow-lg hover:shadow-[#FF3A05]/20 dark:hover:shadow-[#FF3A05]/10 transition-all">
<div className="w-12 h-12 bg-linear-to-r from-[#FF3A05] to-[#FF0080] rounded-xl flex items-center justify-center mb-6">
<div className="bg-white dark:bg-zinc-900 p-8 rounded-2xl border border-zinc-200 dark:border-zinc-700 hover:border-transparent hover:shadow-lg hover:shadow-brand-20 transition-all">
<div className="w-12 h-12 bg-brand rounded-xl flex items-center justify-center mb-6">
<i className="ri-file-chart-line text-2xl text-white"></i>
</div>
<h3 className="font-heading font-bold text-xl text-zinc-900 dark:text-white mb-4 transition-colors">Relatórios Avançados</h3>
<p className="text-zinc-600 dark:text-zinc-400 leading-relaxed transition-colors">Gere relatórios detalhados e personalizados para tomar decisões mais assertivas.</p>
</div>
<div className="bg-white dark:bg-zinc-900 p-8 rounded-2xl border border-zinc-200 dark:border-zinc-700 hover:border-transparent hover:shadow-lg hover:shadow-[#FF3A05]/20 dark:hover:shadow-[#FF3A05]/10 transition-all">
<div className="w-12 h-12 bg-linear-to-r from-[#FF3A05] to-[#FF0080] rounded-xl flex items-center justify-center mb-6">
<div className="bg-white dark:bg-zinc-900 p-8 rounded-2xl border border-zinc-200 dark:border-zinc-700 hover:border-transparent hover:shadow-lg hover:shadow-brand-20 transition-all">
<div className="w-12 h-12 bg-brand rounded-xl flex items-center justify-center mb-6">
<i className="ri-secure-payment-line text-2xl text-white"></i>
</div>
<h3 className="font-heading font-bold text-xl text-zinc-900 dark:text-white mb-4 transition-colors">Segurança Total</h3>
<p className="text-zinc-600 dark:text-zinc-400 leading-relaxed transition-colors">Seus dados protegidos com criptografia de ponta e backup automático na nuvem.</p>
</div>
<div className="bg-white dark:bg-zinc-900 p-8 rounded-2xl border border-zinc-200 dark:border-zinc-700 hover:border-transparent hover:shadow-lg hover:shadow-[#FF3A05]/20 dark:hover:shadow-[#FF3A05]/10 transition-all">
<div className="w-12 h-12 bg-linear-to-r from-[#FF3A05] to-[#FF0080] rounded-xl flex items-center justify-center mb-6">
<div className="bg-white dark:bg-zinc-900 p-8 rounded-2xl border border-zinc-200 dark:border-zinc-700 hover:border-transparent hover:shadow-lg hover:shadow-brand-20 transition-all">
<div className="w-12 h-12 bg-brand rounded-xl flex items-center justify-center mb-6">
<i className="ri-smartphone-line text-2xl text-white"></i>
</div>
<h3 className="font-heading font-bold text-xl text-zinc-900 dark:text-white mb-4 transition-colors">Acesso Mobile</h3>
<p className="text-zinc-600 dark:text-zinc-400 leading-relaxed transition-colors">Gerencie seu negócio de qualquer lugar com nossa plataforma responsiva e intuitiva.</p>
</div>
<div className="bg-white dark:bg-zinc-900 p-8 rounded-2xl border border-zinc-200 dark:border-zinc-700 hover:border-transparent hover:shadow-lg hover:shadow-[#FF3A05]/20 dark:hover:shadow-[#FF3A05]/10 transition-all">
<div className="w-12 h-12 bg-linear-to-r from-[#FF3A05] to-[#FF0080] rounded-xl flex items-center justify-center mb-6">
<div className="bg-white dark:bg-zinc-900 p-8 rounded-2xl border border-zinc-200 dark:border-zinc-700 hover:border-transparent hover:shadow-lg hover:shadow-brand-20 transition-all">
<div className="w-12 h-12 bg-brand rounded-xl flex items-center justify-center mb-6">
<i className="ri-customer-service-2-line text-2xl text-white"></i>
</div>
<h3 className="font-heading font-bold text-xl text-zinc-900 dark:text-white mb-4 transition-colors">Suporte 24/7</h3>
@@ -222,7 +222,7 @@ export default function Home() {
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 max-w-6xl mx-auto">
{/* Plano Básico */}
<div className="bg-white dark:bg-zinc-800 p-8 rounded-2xl border border-zinc-200 dark:border-zinc-700 hover:border-transparent hover:shadow-lg hover:shadow-[#FF3A05]/20 dark:hover:shadow-[#FF3A05]/10 transition-all">
<div className="bg-white dark:bg-zinc-800 p-8 rounded-2xl border border-zinc-200 dark:border-zinc-700 hover:border-transparent hover:shadow-lg hover:shadow-brand-20 transition-all">
<div className="text-center mb-8">
<h3 className="font-heading font-bold text-xl text-zinc-900 dark:text-white mb-2 transition-colors">Básico</h3>
<div className="flex items-baseline justify-center gap-1 mb-2">
@@ -234,40 +234,40 @@ export default function Home() {
<ul className="space-y-4 mb-8">
<li className="flex items-center gap-3">
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-linear-to-r from-[#FF3A05] to-[#FF0080] text-white shadow-sm">
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-brand text-white shadow-sm">
<i className="ri-check-line text-[13px]"></i>
</span>
<span className="text-zinc-600 dark:text-zinc-300 transition-colors">Até 10 clientes</span>
</li>
<li className="flex items-center gap-3">
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-linear-to-r from-[#FF3A05] to-[#FF0080] text-white shadow-sm">
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-brand text-white shadow-sm">
<i className="ri-check-line text-[13px]"></i>
</span>
<span className="text-zinc-600 dark:text-zinc-300 transition-colors">Dashboard básico</span>
</li>
<li className="flex items-center gap-3">
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-linear-to-r from-[#FF3A05] to-[#FF0080] text-white shadow-sm">
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-brand text-white shadow-sm">
<i className="ri-check-line text-[13px]"></i>
</span>
<span className="text-zinc-600 dark:text-zinc-300 transition-colors">Relatórios mensais</span>
</li>
<li className="flex items-center gap-3">
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-linear-to-r from-[#FF3A05] to-[#FF0080] text-white shadow-sm">
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-brand text-white shadow-sm">
<i className="ri-check-line text-[13px]"></i>
</span>
<span className="text-zinc-600 dark:text-zinc-300 transition-colors">Suporte por email</span>
</li>
</ul>
<Link href="http://dash.localhost/cadastro" className="w-full px-6 py-3 border-2 border-zinc-200 dark:border-zinc-600 text-zinc-700 dark:text-zinc-300 font-semibold rounded-lg hover:border-transparent hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] hover:text-white transition-all block text-center">
<Link href="http://dash.localhost/cadastro" className="w-full px-6 py-3 border-2 border-zinc-200 dark:border-zinc-600 text-zinc-700 dark:text-zinc-300 font-semibold rounded-lg hover:border-transparent hover:bg-brand hover:text-white transition-all block text-center">
Começar Grátis
</Link>
</div>
{/* Plano Pro */}
<div className="bg-linear-to-br from-[#FF3A05] to-[#FF0080] p-8 rounded-2xl text-white shadow-2xl transform scale-105">
<div className="bg-brand p-8 rounded-2xl text-white shadow-2xl transform scale-105">
<div className="text-center mb-8">
<div className="inline-flex items-center gap-2 px-3 py-1 mb-4 rounded-full bg-linear-to-r from-[#FF3A05] to-[#FF0080] text-sm font-semibold tracking-tight text-white shadow-lg shadow-black/20">
<div className="inline-flex items-center gap-2 px-3 py-1 mb-4 rounded-full bg-brand text-sm font-semibold tracking-tight text-white shadow-lg shadow-black/20">
<i className="ri-star-line text-xs"></i>
<span>Mais Popular</span>
</div>
@@ -308,7 +308,7 @@ export default function Home() {
</div>
{/* Plano Enterprise */}
<div className="bg-white dark:bg-zinc-800 p-8 rounded-2xl border border-zinc-200 dark:border-zinc-700 hover:border-transparent hover:shadow-lg hover:shadow-[#FF3A05]/20 dark:hover:shadow-[#FF3A05]/10 transition-all">
<div className="bg-white dark:bg-zinc-800 p-8 rounded-2xl border border-zinc-200 dark:border-zinc-700 hover:border-transparent hover:shadow-lg hover:shadow-brand-20 transition-all">
<div className="text-center mb-8">
<h3 className="font-heading font-bold text-xl text-zinc-900 dark:text-white mb-2 transition-colors">Enterprise</h3>
<div className="flex items-baseline justify-center gap-1 mb-2">
@@ -320,38 +320,38 @@ export default function Home() {
<ul className="space-y-4 mb-8">
<li className="flex items-center gap-3">
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-linear-to-r from-[#FF3A05] to-[#FF0080] text-white shadow-sm">
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-brand text-white shadow-sm">
<i className="ri-check-line text-[13px]"></i>
</span>
<span className="text-zinc-600 dark:text-zinc-300 transition-colors">Clientes ilimitados</span>
</li>
<li className="flex items-center gap-3">
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-linear-to-r from-[#FF3A05] to-[#FF0080] text-white shadow-sm">
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-brand text-white shadow-sm">
<i className="ri-check-line text-[13px]"></i>
</span>
<span className="text-zinc-600 dark:text-zinc-300 transition-colors">Dashboard personalizado</span>
</li>
<li className="flex items-center gap-3">
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-linear-to-r from-[#FF3A05] to-[#FF0080] text-white shadow-sm">
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-brand text-white shadow-sm">
<i className="ri-check-line text-[13px]"></i>
</span>
<span className="text-zinc-600 dark:text-zinc-300 transition-colors">Relatórios avançados</span>
</li>
<li className="flex items-center gap-3">
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-linear-to-r from-[#FF3A05] to-[#FF0080] text-white shadow-sm">
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-brand text-white shadow-sm">
<i className="ri-check-line text-[13px]"></i>
</span>
<span className="text-zinc-600 dark:text-zinc-300 transition-colors">Suporte dedicado</span>
</li>
<li className="flex items-center gap-3">
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-linear-to-r from-[#FF3A05] to-[#FF0080] text-white shadow-sm">
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-brand text-white shadow-sm">
<i className="ri-check-line text-[13px]"></i>
</span>
<span className="text-zinc-600 dark:text-zinc-300 transition-colors">White label</span>
</li>
</ul>
<Link href="#contact" className="w-full px-6 py-3 border-2 border-zinc-200 dark:border-zinc-600 text-zinc-700 dark:text-zinc-300 font-semibold rounded-lg hover:border-transparent hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] hover:text-white transition-all block text-center">
<Link href="#contact" className="w-full px-6 py-3 border-2 border-zinc-200 dark:border-zinc-600 text-zinc-700 dark:text-zinc-300 font-semibold rounded-lg hover:border-transparent hover:bg-brand hover:text-white transition-all block text-center">
Falar com Vendas
</Link>
</div>
@@ -371,11 +371,11 @@ export default function Home() {
</p>
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
<Link href="http://dash.localhost/cadastro" className="px-6 py-3 bg-linear-to-r from-[#FF3A05] to-[#FF0080] text-white font-semibold rounded-lg hover:opacity-90 transition-opacity shadow-lg">
<Link href="http://dash.localhost/cadastro" className="px-6 py-3 bg-brand text-white font-semibold rounded-lg hover:opacity-90 transition-opacity shadow-lg">
<i className="ri-rocket-line mr-2"></i>
Começar Grátis Agora
</Link>
<Link href="#contact" className="px-6 py-3 border-2 border-zinc-300 dark:border-zinc-600 text-zinc-700 dark:text-zinc-300 font-semibold rounded-lg hover:border-transparent hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] hover:text-white transition-all">
<Link href="#contact" className="px-6 py-3 border-2 border-zinc-300 dark:border-zinc-600 text-zinc-700 dark:text-zinc-300 font-semibold rounded-lg hover:border-transparent hover:bg-brand hover:text-white transition-all">
<i className="ri-phone-line mr-2"></i>
Falar com Especialista
</Link>
@@ -391,7 +391,7 @@ export default function Home() {
<div className="grid grid-cols-1 md:grid-cols-4 gap-8">
<div className="md:col-span-2">
<div className="flex items-center gap-2 mb-6">
<div className="w-8 h-8 bg-linear-to-r from-[#FF3A05] to-[#FF0080] rounded-lg flex items-center justify-center">
<div className="w-8 h-8 bg-brand rounded-lg flex items-center justify-center">
<span className="text-white font-bold text-sm">A</span>
</div>
<span className="font-heading font-bold text-xl">aggios</span>
@@ -401,13 +401,13 @@ export default function Home() {
Transforme sua gestão e impulsione seus resultados.
</p>
<div className="flex items-center gap-4">
<a href="#" className="w-10 h-10 bg-zinc-800 dark:bg-zinc-700 rounded-lg flex items-center justify-center hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] transition-all group">
<a href="#" className="w-10 h-10 bg-zinc-800 dark:bg-zinc-700 rounded-lg flex items-center justify-center hover:bg-brand transition-all group">
<i className="ri-linkedin-line text-lg group-hover:text-white"></i>
</a>
<a href="#" className="w-10 h-10 bg-zinc-800 dark:bg-zinc-700 rounded-lg flex items-center justify-center hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] transition-all group">
<a href="#" className="w-10 h-10 bg-zinc-800 dark:bg-zinc-700 rounded-lg flex items-center justify-center hover:bg-brand transition-all group">
<i className="ri-twitter-line text-lg group-hover:text-white"></i>
</a>
<a href="#" className="w-10 h-10 bg-zinc-800 dark:bg-zinc-700 rounded-lg flex items-center justify-center hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] transition-all group">
<a href="#" className="w-10 h-10 bg-zinc-800 dark:bg-zinc-700 rounded-lg flex items-center justify-center hover:bg-brand transition-all group">
<i className="ri-instagram-line text-lg group-hover:text-white"></i>
</a>
</div>
@@ -416,20 +416,20 @@ export default function Home() {
<div>
<h3 className="font-heading font-bold text-lg mb-4">Produto</h3>
<ul className="space-y-3">
<li><a href="#features" className="text-zinc-400 hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] hover:bg-clip-text hover:text-transparent transition-all">Recursos</a></li>
<li><a href="#pricing" className="text-zinc-400 hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] hover:bg-clip-text hover:text-transparent transition-all">Preços</a></li>
<li><a href="#" className="text-zinc-400 hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] hover:bg-clip-text hover:text-transparent transition-all">API</a></li>
<li><a href="#" className="text-zinc-400 hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] hover:bg-clip-text hover:text-transparent transition-all">Integrações</a></li>
<li><a href="#features" className="text-zinc-400 hover:text-brand-gradient transition-all">Recursos</a></li>
<li><a href="#pricing" className="text-zinc-400 hover:text-brand-gradient transition-all">Preços</a></li>
<li><a href="#" className="text-zinc-400 hover:text-brand-gradient transition-all">API</a></li>
<li><a href="#" className="text-zinc-400 hover:text-brand-gradient transition-all">Integrações</a></li>
</ul>
</div>
<div>
<h3 className="font-heading font-bold text-lg mb-4">Suporte</h3>
<ul className="space-y-3">
<li><a href="#" className="text-zinc-400 hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] hover:bg-clip-text hover:text-transparent transition-all">Central de Ajuda</a></li>
<li><a href="#" className="text-zinc-400 hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] hover:bg-clip-text hover:text-transparent transition-all">Documentação</a></li>
<li><a href="mailto:suporte@aggios.app" className="text-zinc-400 hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] hover:bg-clip-text hover:text-transparent transition-all">suporte@aggios.app</a></li>
<li><a href="tel:+5511999999999" className="text-zinc-400 hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] hover:bg-clip-text hover:text-transparent transition-all">(11) 99999-9999</a></li>
<li><a href="#" className="text-zinc-400 hover:text-brand-gradient transition-all">Central de Ajuda</a></li>
<li><a href="#" className="text-zinc-400 hover:text-brand-gradient transition-all">Documentação</a></li>
<li><a href="mailto:suporte@aggios.app" className="text-zinc-400 hover:text-brand-gradient transition-all">suporte@aggios.app</a></li>
<li><a href="tel:+5511999999999" className="text-zinc-400 hover:text-brand-gradient transition-all">(11) 99999-9999</a></li>
</ul>
</div>
</div>
@@ -439,9 +439,9 @@ export default function Home() {
© 2025 Aggios. Todos os direitos reservados.
</p>
<div className="flex items-center gap-6 mt-4 md:mt-0">
<a href="#" className="text-zinc-400 hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] hover:bg-clip-text hover:text-transparent transition-all text-sm">Privacidade</a>
<a href="#" className="text-zinc-400 hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] hover:bg-clip-text hover:text-transparent transition-all text-sm">Termos</a>
<a href="#" className="text-zinc-400 hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] hover:bg-clip-text hover:text-transparent transition-all text-sm">Cookies</a>
<a href="#" className="text-zinc-400 hover:text-brand-gradient transition-all text-sm">Privacidade</a>
<a href="#" className="text-zinc-400 hover:text-brand-gradient transition-all text-sm">Termos</a>
<a href="#" className="text-zinc-400 hover:text-brand-gradient transition-all text-sm">Cookies</a>
</div>
</div>
</div>

View File

@@ -45,6 +45,14 @@
/* Gradientes */
--gradient-primary: linear-gradient(135deg, var(--color-brand-500) 0%, var(--color-brand-700) 100%);
--gradient-accent: linear-gradient(135deg, #6366f1 0%, #a855f7 100%);
--color-brand-from: #ff3a05;
--color-brand-to: #ff0080;
--color-gradient-brand: linear-gradient(135deg, var(--color-brand-from) 0%, var(--color-brand-to) 100%);
--color-brand-from-10: color-mix(in srgb, var(--color-brand-from) 10%, transparent);
--color-brand-from-20: color-mix(in srgb, var(--color-brand-from) 20%, transparent);
--color-brand-to-10: color-mix(in srgb, var(--color-brand-to) 10%, transparent);
--color-brand-to-20: color-mix(in srgb, var(--color-brand-to) 20%, transparent);
--color-brand-shadow-20: color-mix(in srgb, var(--color-brand-from) 20%, transparent);
/* Focus ring */
--focus-ring: 0 0 0 3px rgba(14, 165, 233, 0.3);

View File

@@ -21,7 +21,7 @@ export default function Header() {
<div className="max-w-7xl mx-auto px-6 lg:px-8">
<div className="flex items-center justify-between py-4">
<Link href="#" className="flex items-center gap-2" onClick={closeMobileMenu}>
<div className="w-8 h-8 bg-linear-to-r from-[#FF3A05] to-[#FF0080] rounded-lg flex items-center justify-center">
<div className="w-8 h-8 bg-brand rounded-lg flex items-center justify-center">
<span className="text-white font-bold text-sm">A</span>
</div>
<span className="font-heading font-bold text-xl text-zinc-900 dark:text-white transition-colors">aggios</span>
@@ -29,12 +29,12 @@ export default function Header() {
<nav className="hidden md:flex items-center gap-6 lg:gap-8 text-sm font-medium relative">
{navItems.map((item) => (
<a key={item.href} href={item.href} className="text-zinc-600 dark:text-zinc-400 hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] hover:bg-clip-text hover:text-transparent transition-all">
<a key={item.href} href={item.href} className="text-zinc-600 dark:text-zinc-400 hover:text-brand-gradient transition-all">
{item.label}
</a>
))}
<div className="relative group">
<button className="flex items-center gap-1 text-zinc-600 dark:text-zinc-400 hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] hover:bg-clip-text hover:text-transparent transition-all">
<button className="flex items-center gap-1 text-zinc-600 dark:text-zinc-400 hover:text-brand-gradient transition-all">
Soluções
<i className="ri-arrow-down-s-line text-sm" />
</button>
@@ -49,7 +49,7 @@ export default function Header() {
{ href: '#integra', icon: 'ri-share-forward-line', title: 'Integrações API', desc: 'Conexões com BI, contabilidade e apps internos.' },
].map((item) => (
<a key={item.href} href={item.href} className="flex items-start gap-3 rounded-2xl px-4 py-3 text-left text-zinc-700 dark:text-zinc-200 hover:bg-zinc-100/70 dark:hover:bg-zinc-800/70 transition-colors">
<span className="mt-1 flex h-10 w-10 items-center justify-center rounded-2xl bg-linear-to-r from-[#FF3A05] to-[#FF0080] text-white text-xl">
<span className="mt-1 flex h-10 w-10 items-center justify-center rounded-2xl bg-brand text-white text-xl">
<i className={item.icon}></i>
</span>
<span>
@@ -65,10 +65,10 @@ export default function Header() {
<div className="hidden md:flex items-center gap-4">
<ThemeToggle />
<Link href="http://dash.localhost/login" className="text-zinc-600 dark:text-zinc-400 hover:bg-linear-to-r hover:from-[#FF3A05] hover:to-[#FF0080] hover:bg-clip-text hover:text-transparent transition-all font-medium">
<Link href="http://dash.localhost/login" className="text-zinc-600 dark:text-zinc-400 hover:text-brand-gradient transition-all font-medium">
Entrar
</Link>
<Link href="http://dash.localhost/cadastro" className="px-6 py-2 bg-linear-to-r from-[#FF3A05] to-[#FF0080] text-white font-semibold rounded-lg hover:opacity-90 transition-opacity shadow-lg">
<Link href="http://dash.localhost/cadastro" className="px-6 py-2 bg-brand text-white font-semibold rounded-lg hover:opacity-90 transition-opacity shadow-lg">
Começar Grátis
</Link>
</div>
@@ -136,7 +136,7 @@ export default function Header() {
<Link
href="http://dash.localhost/cadastro"
onClick={closeMobileMenu}
className="w-full px-5 py-3 rounded-2xl bg-linear-to-r from-[#FF3A05] to-[#FF0080] text-white font-semibold shadow-lg"
className="w-full px-5 py-3 rounded-2xl bg-brand text-white font-semibold shadow-lg"
>
Começar Grátis
</Link>

View File

@@ -38,6 +38,16 @@ http:
- security-headers
service: dashboard-service
# Institucional site (aggios.local and localhost)
institucional-router:
entryPoints:
- web
rule: "Host(`aggios.local`) || Host(`localhost`)"
priority: 3
middlewares:
- security-headers
service: institucional-service
services:
api-service:
loadBalancer:
@@ -48,3 +58,8 @@ http:
loadBalancer:
servers:
- url: http://dashboard:3000
institucional-service:
loadBalancer:
servers:
- url: http://institucional:3000