v1.4: Segurança multi-tenant, file serving via API e UX humanizada
- Validação cross-tenant no login e rotas protegidas
- File serving via /api/files/{bucket}/{path} (eliminação DNS)
- Mensagens de erro humanizadas inline (sem pop-ups)
- Middleware tenant detection via headers customizados
- Upload de logos retorna URLs via API
- README atualizado com changelog v1.4 completo
This commit is contained in:
@@ -1,25 +1,53 @@
|
||||
'use client';
|
||||
|
||||
import { ReactNode } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
const DEFAULT_GRADIENT = 'linear-gradient(135deg, #ff3a05, #ff0080)';
|
||||
// Helper to lighten color
|
||||
const lightenColor = (color: string, percent: number) => {
|
||||
const num = parseInt(color.replace("#", ""), 16),
|
||||
amt = Math.round(2.55 * percent),
|
||||
R = (num >> 16) + amt,
|
||||
B = ((num >> 8) & 0x00ff) + amt,
|
||||
G = (num & 0x0000ff) + amt;
|
||||
return (
|
||||
"#" +
|
||||
(
|
||||
0x1000000 +
|
||||
(R < 255 ? (R < 1 ? 0 : R) : 255) * 0x10000 +
|
||||
(B < 255 ? (B < 1 ? 0 : B) : 255) * 0x100 +
|
||||
(G < 255 ? (G < 1 ? 0 : G) : 255)
|
||||
)
|
||||
.toString(16)
|
||||
.slice(1)
|
||||
);
|
||||
};
|
||||
|
||||
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'));
|
||||
const setBrandColors = (primary: string, secondary: string) => {
|
||||
document.documentElement.style.setProperty('--brand-color', primary);
|
||||
document.documentElement.style.setProperty('--brand-color-strong', secondary);
|
||||
|
||||
// Create a lighter version of primary for hover
|
||||
const primaryLight = lightenColor(primary, 20); // Lighten by 20%
|
||||
document.documentElement.style.setProperty('--brand-color-hover', primaryLight);
|
||||
|
||||
// Set RGB variables if needed by other components
|
||||
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 primaryRgb = hexToRgb(primary);
|
||||
const secondaryRgb = hexToRgb(secondary);
|
||||
const primaryLightRgb = hexToRgb(primaryLight);
|
||||
|
||||
if (primaryRgb) document.documentElement.style.setProperty('--brand-rgb', primaryRgb);
|
||||
if (secondaryRgb) document.documentElement.style.setProperty('--brand-strong-rgb', secondaryRgb);
|
||||
if (primaryLightRgb) document.documentElement.style.setProperty('--brand-hover-rgb', primaryLightRgb);
|
||||
};
|
||||
|
||||
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]);
|
||||
|
||||
// Temporariamente desativado o carregamento dinâmico de cores/tema para eliminar possíveis
|
||||
// efeitos colaterais de hidratação e 429 no middleware/backend. Se precisar reativar, mover
|
||||
// para nível de servidor (next/head ou metadata) para evitar mutações de DOM no cliente.
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user