feat: implement theme customization and user preferences

- Added support for user-selected themes, allowing users to customize the appearance of the application.
- Introduced a new `temaPreferido` field in the user schema to store the preferred theme.
- Updated various components to apply the selected theme dynamically based on user preferences.
- Enhanced the UI to include a theme selection interface, enabling users to preview and save their theme choices.
- Implemented a polyfill for BlobBuilder to ensure compatibility across browsers, improving the functionality of the application.
This commit is contained in:
2025-11-22 22:05:52 -03:00
parent 58ac3a4f1b
commit 37d7318d5a
12 changed files with 1149 additions and 74 deletions

View File

@@ -0,0 +1,194 @@
/**
* Utilitário para gerenciamento de temas personalizados do SGSE
*/
export type TemaId =
| 'purple'
| 'blue'
| 'green'
| 'orange'
| 'red'
| 'pink'
| 'teal'
| 'dark'
| 'light'
| 'corporate';
export interface Tema {
id: TemaId;
nome: string;
descricao: string;
corPrimaria: string;
corSecundaria: string;
corGradiente: string;
}
/**
* Lista de temas disponíveis
*/
export const temasDisponiveis: Tema[] = [
{
id: 'purple',
nome: 'Roxo',
descricao: 'Tema padrão com cores roxas e azuis',
corPrimaria: '#764ba2',
corSecundaria: '#667eea',
corGradiente: 'from-purple-600 via-blue-600 to-indigo-700'
},
{
id: 'blue',
nome: 'Azul',
descricao: 'Tema azul clássico e profissional',
corPrimaria: '#2563eb',
corSecundaria: '#3b82f6',
corGradiente: 'from-blue-500 via-blue-600 to-blue-700'
},
{
id: 'green',
nome: 'Verde',
descricao: 'Tema verde natural e harmonioso',
corPrimaria: '#10b981',
corSecundaria: '#059669',
corGradiente: 'from-green-500 via-emerald-600 to-teal-700'
},
{
id: 'orange',
nome: 'Laranja',
descricao: 'Tema laranja vibrante e energético',
corPrimaria: '#f97316',
corSecundaria: '#ea580c',
corGradiente: 'from-orange-500 via-amber-600 to-orange-700'
},
{
id: 'red',
nome: 'Vermelho',
descricao: 'Tema vermelho intenso e impactante',
corPrimaria: '#ef4444',
corSecundaria: '#dc2626',
corGradiente: 'from-red-500 via-rose-600 to-red-700'
},
{
id: 'pink',
nome: 'Rosa',
descricao: 'Tema rosa suave e elegante',
corPrimaria: '#ec4899',
corSecundaria: '#db2777',
corGradiente: 'from-pink-500 via-rose-600 to-fuchsia-700'
},
{
id: 'teal',
nome: 'Verde-água',
descricao: 'Tema verde-água refrescante',
corPrimaria: '#14b8a6',
corSecundaria: '#0d9488',
corGradiente: 'from-teal-500 via-cyan-600 to-teal-700'
},
{
id: 'dark',
nome: 'Escuro',
descricao: 'Tema escuro para uso noturno',
corPrimaria: '#1e293b',
corSecundaria: '#0f172a',
corGradiente: 'from-slate-800 via-gray-900 to-slate-900'
},
{
id: 'light',
nome: 'Claro',
descricao: 'Tema claro e minimalista',
corPrimaria: '#f8fafc',
corSecundaria: '#e2e8f0',
corGradiente: 'from-gray-100 via-slate-200 to-gray-300'
},
{
id: 'corporate',
nome: 'Corporativo',
descricao: 'Tema corporativo azul escuro',
corPrimaria: '#1e40af',
corSecundaria: '#1e3a8a',
corGradiente: 'from-blue-800 via-indigo-900 to-blue-900'
}
];
/**
* Mapeamento de temas para nomes do DaisyUI
* Usamos temas nativos do DaisyUI quando disponíveis, ou temas customizados SGSE
*/
export const temaParaDaisyUI: Record<TemaId, string> = {
purple: 'aqua', // Tema padrão atual (roxo/azul) - nativo DaisyUI
blue: 'sgse-blue', // Azul - customizado
green: 'sgse-green', // Verde - customizado
orange: 'sgse-orange', // Laranja - customizado
red: 'sgse-red', // Vermelho - customizado
pink: 'sgse-pink', // Rosa - customizado
teal: 'sgse-teal', // Verde-água - customizado
dark: 'dark', // Escuro - nativo DaisyUI
light: 'light', // Claro - nativo DaisyUI
corporate: 'sgse-corporate' // Corporativo - customizado
};
/**
* Obter tema por ID
*/
export function obterTema(id: TemaId | string | null | undefined): Tema | null {
if (!id) return null;
return temasDisponiveis.find((t) => t.id === id) || null;
}
/**
* Obter nome do tema DaisyUI correspondente
*/
export function obterNomeDaisyUI(id: TemaId | string | null | undefined): string {
if (!id) return 'aqua'; // Tema padrão
const tema = obterTema(id);
if (!tema) return 'aqua';
return temaParaDaisyUI[tema.id] || 'aqua';
}
/**
* Aplicar tema ao documento HTML
* NÃO salva no localStorage - apenas no banco de dados do usuário
*/
export function aplicarTema(temaId: TemaId | string | null | undefined): void {
if (typeof document === 'undefined') return;
const nomeDaisyUI = obterNomeDaisyUI(temaId || 'purple');
const htmlElement = document.documentElement;
const bodyElement = document.body;
if (htmlElement) {
// Remover todos os atributos data-theme existentes primeiro
htmlElement.removeAttribute('data-theme');
if (bodyElement) {
bodyElement.removeAttribute('data-theme');
}
// Aplicar o novo tema
htmlElement.setAttribute('data-theme', nomeDaisyUI);
if (bodyElement) {
bodyElement.setAttribute('data-theme', nomeDaisyUI);
}
// Forçar reflow para garantir que o CSS seja aplicado
void htmlElement.offsetHeight;
// Disparar evento customizado para notificar mudança de tema
if (typeof window !== 'undefined') {
window.dispatchEvent(new CustomEvent('themechange', { detail: { theme: nomeDaisyUI } }));
}
}
}
/**
* Aplicar tema padrão (roxo)
*/
export function aplicarTemaPadrao(): void {
aplicarTema('purple');
}
/**
* Obter tema padrão
*/
export function obterTemaPadrao(): Tema {
return temasDisponiveis[0]; // Purple
}