Files
tracksteel_app/src/pages/Configuracoes.tsx
T

307 lines
12 KiB
TypeScript

import { useState, useEffect } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Upload, Eye } from 'lucide-react';
import { toast } from 'sonner';
import { useAuth } from '@/hooks/useAuth';
import { useUserRole } from '@/hooks/useUserRole';
import { useBrandSettings } from '@/hooks/useBrandSettings';
import { useIconStyle } from '@/hooks/useIconStyle';
import { MenuBuilder } from '@/components/menu-builder/MenuBuilder';
import { PrioridadesConfig } from '@/components/configuracoes/PrioridadesConfig';
import { NomenclaturasConfig } from '@/components/configuracoes/NomenclaturasConfig';
const FONT_OPTIONS = [{
value: 'Arial',
label: 'Arial - Padrão clássico'
}, {
value: 'Helvetica',
label: 'Helvetica - Moderna e limpa'
}, {
value: 'SF Pro Display',
label: 'SF Pro - Fonte do sistema Apple'
}, {
value: 'system-ui',
label: 'Fonte do Sistema Operacional'
}];
const Configuracoes = () => {
const {
user
} = useAuth();
const {
isAdmin
} = useUserRole();
const {
brandSettings,
setBrandSettings,
saveBrandSettings,
uploadLogo,
isLoading: brandLoading
} = useBrandSettings();
const { iconStyle, setIconStyle } = useIconStyle();
const [isLoading, setIsLoading] = useState(false);
const [logoFile, setLogoFile] = useState<File | null>(null);
const [logoPreview, setLogoPreview] = useState<string | null>(null);
useEffect(() => {
if (brandSettings.logo_url) {
setLogoPreview(brandSettings.logo_url);
}
}, [brandSettings.logo_url]);
const handleLogoUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file) {
if (file.size > 5 * 1024 * 1024) {
// 5MB limit
toast.error('Arquivo muito grande. Máximo 5MB.');
return;
}
if (!file.type.startsWith('image/')) {
toast.error('Por favor, selecione um arquivo de imagem.');
return;
}
setLogoFile(file);
const reader = new FileReader();
reader.onload = e => {
setLogoPreview(e.target?.result as string);
};
reader.readAsDataURL(file);
}
};
const handleSaveSettings = async () => {
if (!isAdmin) {
toast.error('Acesso negado. Apenas administradores podem alterar essas configurações.');
return;
}
setIsLoading(true);
try {
let logoUrl = brandSettings.logo_url;
// Upload logo if a new file was selected
if (logoFile) {
const uploadResult = await uploadLogo(logoFile);
if (uploadResult.error) {
setIsLoading(false);
return;
}
logoUrl = uploadResult.data?.publicUrl || null;
}
// Save brand settings
const settingsToSave = {
...brandSettings,
logo_url: logoUrl
};
const result = await saveBrandSettings(settingsToSave);
if (result.error) {
setIsLoading(false);
return;
}
// Apply font globally
const fontFamily = brandSettings.font_family === 'system-ui' ? 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif' : brandSettings.font_family;
document.documentElement.style.setProperty('--font-family', fontFamily);
document.body.style.fontFamily = fontFamily;
// Clear file selection
setLogoFile(null);
} catch (error) {
console.error('Error saving brand settings:', error);
toast.error('Erro ao salvar configurações');
} finally {
setIsLoading(false);
}
};
const handleFontChange = (value: string) => {
setBrandSettings(prev => ({
...prev,
font_family: value
}));
// Preview da fonte
const fontFamily = value === 'system-ui' ? 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif' : value;
document.documentElement.style.setProperty('--font-family', fontFamily);
document.body.style.fontFamily = fontFamily;
};
if (!isAdmin) {
return <div className="space-y-6 p-4 sm:p-6 bg-background min-h-screen">
<div>
<h1 className="text-3xl font-bold text-foreground mb-2">Configurações</h1>
<p className="text-muted-foreground">Configurações gerais do sistema</p>
</div>
<Card className="bg-white border-slate-300 shadow-sm dark:bg-slate-800/50 dark:border-slate-700">
<CardHeader>
<CardTitle className="text-foreground">Acesso Restrito</CardTitle>
</CardHeader>
<CardContent>
<p className="text-muted-foreground">
Apenas administradores podem acessar as configurações avançadas do sistema.
</p>
</CardContent>
</Card>
</div>;
}
return <div className="space-y-6 p-4 sm:p-6 bg-background min-h-screen">
<div>
<h1 className="text-3xl font-bold text-foreground mb-2">Configurações Gerais</h1>
<p className="text-muted-foreground">Configurações gerais do sistema</p>
</div>
{/* Personalização de Marca */}
<Card className="bg-white border-slate-300 shadow-sm dark:bg-slate-800/50 dark:border-slate-700">
<CardHeader>
<CardTitle className="text-foreground">Personalização de Marca</CardTitle>
</CardHeader>
<CardContent className="space-y-6">
{/* Nome da Empresa */}
<div className="space-y-2">
<Label htmlFor="company-name" className="text-foreground">
Nome da Empresa
</Label>
<Input
id="company-name"
value={brandSettings.company_name}
onChange={e => setBrandSettings(prev => ({
...prev,
company_name: e.target.value
}))}
placeholder="Digite o nome da empresa"
className="bg-white border-slate-300 text-slate-900 dark:bg-slate-800 dark:border-slate-700 dark:text-white"
/>
</div>
<div className="space-y-2">
<Label className="text-foreground">Logotipo da Empresa</Label>
<div className="space-y-4">
<div className="flex items-center gap-3">
<Button
type="button"
variant="outline"
className="bg-background border-border text-foreground hover:bg-accent dark:bg-slate-800 dark:border-slate-700 dark:text-white"
onClick={() => document.getElementById('logo-upload')?.click()}
>
<Upload className="w-4 h-4 mr-2" />
Selecionar Logo
</Button>
<span className="text-muted-foreground text-sm">
JPG, PNG ou SVG. Máximo 5MB.
</span>
</div>
<input id="logo-upload" type="file" accept="image/*" onChange={handleLogoUpload} className="hidden" />
{logoPreview && <div className="border border-slate-200 rounded-lg p-4 bg-slate-50 dark:bg-slate-800 dark:border-slate-700">
<p className="text-muted-foreground text-sm mb-2 flex items-center gap-2">
<Eye className="w-4 h-4" />
Pré-visualização
</p>
<img src={logoPreview} alt="Logo preview" className="max-w-32 max-h-16 object-contain" />
</div>}
</div>
</div>
<div className="space-y-2">
<Label className="text-foreground">Estilo de Fonte</Label>
<Select value={brandSettings.font_family} onValueChange={handleFontChange}>
<SelectTrigger className="bg-white border-slate-300 text-slate-900 dark:bg-slate-800 dark:border-slate-700 dark:text-white">
<SelectValue placeholder="Selecione uma fonte" />
</SelectTrigger>
<SelectContent className="bg-white border-slate-300 dark:bg-slate-800 dark:border-slate-700">
{FONT_OPTIONS.map(font => <SelectItem key={font.value} value={font.value} className="text-foreground dark:text-white">
<span style={{
fontFamily: font.value === 'system-ui' ? 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' : font.value
}}>
{font.label}
</span>
</SelectItem>)}
</SelectContent>
</Select>
<div className="mt-3 p-3 rounded border border-slate-200 bg-slate-50 dark:bg-slate-800 dark:border-slate-700">
<p className="text-muted-foreground text-sm mb-2">Pré-visualização da fonte:</p>
<p
style={{
fontFamily: brandSettings.font_family === 'system-ui'
? 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif'
: brandSettings.font_family
}}
className="text-foreground"
>
Este é um exemplo de como o texto aparecerá com a fonte selecionada.
</p>
</div>
</div>
<div className="pt-4">
<Button
onClick={handleSaveSettings}
disabled={isLoading || brandLoading}
className="bg-primary hover:bg-primary/90 text-primary-foreground"
>
{isLoading ? 'Salvando...' : 'Salvar Configurações'}
</Button>
</div>
</CardContent>
</Card>
{/* Personalização de Interface */}
<Card className="bg-white border-slate-300 shadow-sm dark:bg-slate-800/50 dark:border-slate-700">
<CardHeader>
<CardTitle className="text-foreground">Personalização de Interface</CardTitle>
</CardHeader>
<CardContent className="space-y-6">
{/* Estilo dos Ícones */}
<div className="space-y-2">
<Label className="text-foreground">Estilo dos Ícones do Menu</Label>
<Select value={iconStyle} onValueChange={setIconStyle}>
<SelectTrigger className="bg-white border-slate-300 text-slate-900 dark:bg-slate-800 dark:border-slate-700 dark:text-white">
<SelectValue placeholder="Selecione o estilo dos ícones" />
</SelectTrigger>
<SelectContent className="bg-white border-slate-300 dark:bg-slate-800 dark:border-slate-700">
<SelectItem value="colorful" className="text-foreground dark:text-white">
Coloridos (Cores Originais)
</SelectItem>
<SelectItem value="themed" className="text-foreground dark:text-white">
Temáticos (Cor Principal do Tema)
</SelectItem>
<SelectItem value="white" className="text-foreground dark:text-white">
Brancos (Monocromático)
</SelectItem>
</SelectContent>
</Select>
<div className="mt-3 p-3 rounded border border-slate-200 bg-slate-50 dark:bg-slate-800 dark:border-slate-700">
<p className="text-muted-foreground text-sm mb-2">Configuração atual:</p>
<p className="text-foreground">
{iconStyle === 'colorful' && 'Ícones coloridos (cores originais dos ícones)'}
{iconStyle === 'themed' && 'Ícones temáticos (seguem a cor principal do tema)'}
{iconStyle === 'white' && 'Ícones brancos (estilo monocromático)'}
</p>
</div>
</div>
</CardContent>
</Card>
{/* Configuração de Nomenclaturas e Identificação da Fábrica */}
<NomenclaturasConfig />
{/* Configuração de Prioridades */}
<PrioridadesConfig />
{/* Menu Builder */}
<MenuBuilder />
</div>;
};
export default Configuracoes;