feat: Add dynamic contact info and social media settings

This commit is contained in:
Erik
2025-11-29 15:52:21 -03:00
parent c06221331e
commit a14e7749b7
4 changed files with 301 additions and 28 deletions

View File

@@ -22,6 +22,14 @@ export default function ConfiguracoesPage() {
const [customColor, setCustomColor] = useState('#FF6B35');
const [showPartnerBadge, setShowPartnerBadge] = useState(false);
const [partnerName, setPartnerName] = useState('Coca-Cola');
// Campos de contato
const [address, setAddress] = useState('');
const [phone, setPhone] = useState('');
const [email, setEmail] = useState('');
const [instagram, setInstagram] = useState('');
const [linkedin, setLinkedin] = useState('');
const [facebook, setFacebook] = useState('');
const [whatsapp, setWhatsapp] = useState('');
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
@@ -54,6 +62,13 @@ export default function ConfiguracoesPage() {
const data = await response.json();
setShowPartnerBadge(data.showPartnerBadge || false);
setPartnerName(data.partnerName || 'Coca-Cola');
setAddress(data.address || '');
setPhone(data.phone || '');
setEmail(data.email || '');
setInstagram(data.instagram || '');
setLinkedin(data.linkedin || '');
setFacebook(data.facebook || '');
setWhatsapp(data.whatsapp || '');
}
} catch (error) {
console.error('Erro ao carregar settings:', error);
@@ -65,16 +80,26 @@ export default function ConfiguracoesPage() {
const response = await fetch('/api/settings', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ showPartnerBadge, partnerName })
body: JSON.stringify({
showPartnerBadge,
partnerName,
address: address || null,
phone: phone || null,
email: email || null,
instagram: instagram || null,
linkedin: linkedin || null,
facebook: facebook || null,
whatsapp: whatsapp || null
})
});
if (!response.ok) throw new Error('Erro ao salvar');
success('Configurações do badge salvas!');
// Dispatch event para atualizar o PartnerBadge em tempo real
success('Configurações salvas!');
// Dispatch event para atualizar em tempo real
window.dispatchEvent(new Event('settings:refresh'));
} catch (error) {
showError('Erro ao salvar configurações do badge');
showError('Erro ao salvar configurações');
}
};
@@ -394,6 +419,154 @@ export default function ConfiguracoesPage() {
Salvar Configurações do Badge
</button>
</div>
{/* Contact Information Settings */}
<div className="bg-white dark:bg-secondary p-8 rounded-2xl border border-gray-200 dark:border-white/10 shadow-sm">
<div className="flex items-start gap-4 mb-6">
<div className="w-12 h-12 bg-linear-to-br from-blue-500 to-blue-600 rounded-xl flex items-center justify-center shadow-lg shadow-blue-500/30">
<i className="ri-contacts-book-2-fill text-2xl text-white"></i>
</div>
<div className="flex-1">
<h2 className="text-xl font-bold text-secondary dark:text-white mb-1">Informações de Contato</h2>
<p className="text-gray-500 dark:text-gray-400 text-sm">
Configure as informações de contato que aparecem no rodapé do site.
</p>
</div>
</div>
<div className="space-y-4">
{/* Address */}
<div>
<label className="block text-sm font-bold text-gray-700 dark:text-gray-300 mb-2">
<i className="ri-map-pin-line mr-2 text-primary"></i>
Endereço
</label>
<input
type="text"
value={address}
onChange={(e) => setAddress(e.target.value)}
placeholder="Ex: Rua das Flores, 123 - Centro, Vitória - ES"
className="w-full px-4 py-3 bg-gray-50 dark:bg-white/5 border border-gray-200 dark:border-white/10 rounded-xl text-gray-900 dark:text-white focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-all"
/>
</div>
{/* Phone */}
<div>
<label className="block text-sm font-bold text-gray-700 dark:text-gray-300 mb-2">
<i className="ri-phone-line mr-2 text-primary"></i>
Telefone
</label>
<input
type="text"
value={phone}
onChange={(e) => setPhone(e.target.value)}
placeholder="Ex: (27) 99999-9999"
className="w-full px-4 py-3 bg-gray-50 dark:bg-white/5 border border-gray-200 dark:border-white/10 rounded-xl text-gray-900 dark:text-white focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-all"
/>
</div>
{/* Email */}
<div>
<label className="block text-sm font-bold text-gray-700 dark:text-gray-300 mb-2">
<i className="ri-mail-line mr-2 text-primary"></i>
Email
</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Ex: contato@empresa.com.br"
className="w-full px-4 py-3 bg-gray-50 dark:bg-white/5 border border-gray-200 dark:border-white/10 rounded-xl text-gray-900 dark:text-white focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-all"
/>
</div>
</div>
</div>
{/* Social Media Settings */}
<div className="bg-white dark:bg-secondary p-8 rounded-2xl border border-gray-200 dark:border-white/10 shadow-sm">
<div className="flex items-start gap-4 mb-6">
<div className="w-12 h-12 bg-linear-to-br from-pink-500 to-purple-600 rounded-xl flex items-center justify-center shadow-lg shadow-pink-500/30">
<i className="ri-share-circle-fill text-2xl text-white"></i>
</div>
<div className="flex-1">
<h2 className="text-xl font-bold text-secondary dark:text-white mb-1">Redes Sociais</h2>
<p className="text-gray-500 dark:text-gray-400 text-sm">
Configure os links das suas redes sociais. Deixe em branco para ocultar.
</p>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Instagram */}
<div>
<label className="block text-sm font-bold text-gray-700 dark:text-gray-300 mb-2">
<i className="ri-instagram-line mr-2 text-pink-500"></i>
Instagram
</label>
<input
type="url"
value={instagram}
onChange={(e) => setInstagram(e.target.value)}
placeholder="https://instagram.com/suaempresa"
className="w-full px-4 py-3 bg-gray-50 dark:bg-white/5 border border-gray-200 dark:border-white/10 rounded-xl text-gray-900 dark:text-white focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-all"
/>
</div>
{/* LinkedIn */}
<div>
<label className="block text-sm font-bold text-gray-700 dark:text-gray-300 mb-2">
<i className="ri-linkedin-fill mr-2 text-blue-600"></i>
LinkedIn
</label>
<input
type="url"
value={linkedin}
onChange={(e) => setLinkedin(e.target.value)}
placeholder="https://linkedin.com/company/suaempresa"
className="w-full px-4 py-3 bg-gray-50 dark:bg-white/5 border border-gray-200 dark:border-white/10 rounded-xl text-gray-900 dark:text-white focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-all"
/>
</div>
{/* Facebook */}
<div>
<label className="block text-sm font-bold text-gray-700 dark:text-gray-300 mb-2">
<i className="ri-facebook-fill mr-2 text-blue-500"></i>
Facebook
</label>
<input
type="url"
value={facebook}
onChange={(e) => setFacebook(e.target.value)}
placeholder="https://facebook.com/suaempresa"
className="w-full px-4 py-3 bg-gray-50 dark:bg-white/5 border border-gray-200 dark:border-white/10 rounded-xl text-gray-900 dark:text-white focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-all"
/>
</div>
{/* WhatsApp */}
<div>
<label className="block text-sm font-bold text-gray-700 dark:text-gray-300 mb-2">
<i className="ri-whatsapp-line mr-2 text-green-500"></i>
WhatsApp
</label>
<input
type="text"
value={whatsapp}
onChange={(e) => setWhatsapp(e.target.value)}
placeholder="Ex: (27) 99999-9999"
className="w-full px-4 py-3 bg-gray-50 dark:bg-white/5 border border-gray-200 dark:border-white/10 rounded-xl text-gray-900 dark:text-white focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-all"
/>
</div>
</div>
{/* Save All Settings Button */}
<button
onClick={handleSaveSettings}
className="w-full mt-6 px-6 py-3 bg-primary text-white rounded-xl font-bold hover:opacity-90 transition-colors shadow-lg shadow-primary/20 flex items-center justify-center gap-2"
>
<i className="ri-save-line"></i>
Salvar Informações de Contato
</button>
</div>
</div>
)}

View File

@@ -66,7 +66,17 @@ export async function POST(request: NextRequest) {
}
const body = await request.json();
const { showPartnerBadge, partnerName } = body;
const {
showPartnerBadge,
partnerName,
address,
phone,
email,
instagram,
linkedin,
facebook,
whatsapp
} = body;
let settings = await prisma.settings.findFirst();
@@ -74,7 +84,14 @@ export async function POST(request: NextRequest) {
settings = await prisma.settings.create({
data: {
showPartnerBadge: showPartnerBadge ?? false,
partnerName: partnerName ?? 'Coca-Cola'
partnerName: partnerName ?? 'Coca-Cola',
address: address ?? null,
phone: phone ?? null,
email: email ?? null,
instagram: instagram ?? null,
linkedin: linkedin ?? null,
facebook: facebook ?? null,
whatsapp: whatsapp ?? null
}
});
} else {
@@ -82,7 +99,14 @@ export async function POST(request: NextRequest) {
where: { id: settings.id },
data: {
...(showPartnerBadge !== undefined && { showPartnerBadge }),
...(partnerName !== undefined && { partnerName })
...(partnerName !== undefined && { partnerName }),
...(address !== undefined && { address }),
...(phone !== undefined && { phone }),
...(email !== undefined && { email }),
...(instagram !== undefined && { instagram }),
...(linkedin !== undefined && { linkedin }),
...(facebook !== undefined && { facebook }),
...(whatsapp !== undefined && { whatsapp })
}
});
}

View File

@@ -1,15 +1,56 @@
"use client";
import { useEffect, useState } from 'react';
import Link from 'next/link';
import { useLocale } from '@/contexts/LocaleContext';
import { PartnerBadge } from './PartnerBadge';
type ContactSettings = {
address?: string | null;
phone?: string | null;
email?: string | null;
instagram?: string | null;
linkedin?: string | null;
facebook?: string | null;
whatsapp?: string | null;
};
export default function Footer() {
const { locale, t } = useLocale();
const [contact, setContact] = useState<ContactSettings>({});
// Prefixo para links
const prefix = locale === 'pt' ? '' : `/${locale}`;
useEffect(() => {
const fetchSettings = async () => {
try {
const response = await fetch('/api/settings');
if (response.ok) {
const data = await response.json();
setContact({
address: data.address,
phone: data.phone,
email: data.email,
instagram: data.instagram,
linkedin: data.linkedin,
facebook: data.facebook,
whatsapp: data.whatsapp
});
}
} catch (error) {
console.error('Erro ao carregar configurações:', error);
}
};
fetchSettings();
// Atualizar quando settings mudar
const handleRefresh = () => fetchSettings();
window.addEventListener('settings:refresh', handleRefresh);
return () => window.removeEventListener('settings:refresh', handleRefresh);
}, []);
return (
<footer className="bg-secondary text-white pt-16 pb-8">
<div className="container mx-auto px-4">
@@ -32,15 +73,26 @@ export default function Footer() {
</div>
<div className="flex gap-4">
<a href="#" className="w-10 h-10 rounded-full bg-white/10 flex items-center justify-center hover:bg-primary transition-colors">
<i className="ri-instagram-line"></i>
</a>
<a href="#" className="w-10 h-10 rounded-full bg-white/10 flex items-center justify-center hover:bg-primary transition-colors">
<i className="ri-linkedin-fill"></i>
</a>
<a href="#" className="w-10 h-10 rounded-full bg-white/10 flex items-center justify-center hover:bg-primary transition-colors">
<i className="ri-facebook-fill"></i>
</a>
{contact.instagram && (
<a href={contact.instagram} target="_blank" rel="noopener noreferrer" className="w-10 h-10 rounded-full bg-white/10 flex items-center justify-center hover:bg-primary transition-colors">
<i className="ri-instagram-line"></i>
</a>
)}
{contact.linkedin && (
<a href={contact.linkedin} target="_blank" rel="noopener noreferrer" className="w-10 h-10 rounded-full bg-white/10 flex items-center justify-center hover:bg-primary transition-colors">
<i className="ri-linkedin-fill"></i>
</a>
)}
{contact.facebook && (
<a href={contact.facebook} target="_blank" rel="noopener noreferrer" className="w-10 h-10 rounded-full bg-white/10 flex items-center justify-center hover:bg-primary transition-colors">
<i className="ri-facebook-fill"></i>
</a>
)}
{contact.whatsapp && (
<a href={`https://wa.me/${contact.whatsapp.replace(/\D/g, '')}`} target="_blank" rel="noopener noreferrer" className="w-10 h-10 rounded-full bg-white/10 flex items-center justify-center hover:bg-primary transition-colors">
<i className="ri-whatsapp-line"></i>
</a>
)}
</div>
</div>
@@ -71,18 +123,33 @@ export default function Footer() {
<div>
<h3 className="text-lg font-bold font-headline mb-6">{t('nav.contact')}</h3>
<ul className="space-y-4">
<li className="flex items-start gap-3 text-gray-400">
<i className="ri-map-pin-line mt-1 text-primary"></i>
<span>Endereço da Empresa, 123<br />Cidade - ES</span>
</li>
<li className="flex items-center gap-3 text-gray-400">
<i className="ri-phone-line text-primary"></i>
<span>(27) 99999-9999</span>
</li>
<li className="flex items-center gap-3 text-gray-400">
<i className="ri-mail-line text-primary"></i>
<span>contato@octto.com.br</span>
</li>
{contact.address && (
<li className="flex items-start gap-3 text-gray-400">
<i className="ri-map-pin-line mt-1 text-primary"></i>
<span>{contact.address}</span>
</li>
)}
{contact.phone && (
<li className="flex items-center gap-3 text-gray-400">
<i className="ri-phone-line text-primary"></i>
<a href={`tel:${contact.phone.replace(/\D/g, '')}`} className="hover:text-primary transition-colors">
{contact.phone}
</a>
</li>
)}
{contact.email && (
<li className="flex items-center gap-3 text-gray-400">
<i className="ri-mail-line text-primary"></i>
<a href={`mailto:${contact.email}`} className="hover:text-primary transition-colors">
{contact.email}
</a>
</li>
)}
{!contact.address && !contact.phone && !contact.email && (
<li className="text-gray-500 text-sm italic">
Informações de contato não configuradas
</li>
)}
</ul>
</div>
</div>