import React, { useState, useEffect, useMemo, useRef } from 'react'; import { createRoot } from 'react-dom/client'; import { createClient } from '@supabase/supabase-js'; import { Search, MapPin, Phone, Instagram, Facebook, Car, Fuel, Gauge, Calendar, Plus, Trash2, Edit3, LogOut, User, ChevronRight, ShieldCheck, X, Menu, Lock, Mail, CheckCircle, AlertCircle, Loader2, Upload, Camera, Images, Trash, Settings, Save, Palette, Globe, ImagePlus, MessageCircle } from 'lucide-react'; /** * INSTRUÇÕES PARA O PATRÃO (SUPABASE): * 1. Acesse o SQL Editor no painel do seu Supabase. * 2. Copie, cole e execute o código abaixo para criar/atualizar as tabelas do site: * * CREATE TABLE IF NOT EXISTS public.vehicles ( * id uuid DEFAULT gen_random_uuid() PRIMARY KEY, * make text NOT NULL, * model text NOT NULL, * year_model integer NOT NULL, * year_fab integer NOT NULL, * price numeric NOT NULL, * mileage integer NOT NULL, * fuel_type text NOT NULL, * description text, * images text[] DEFAULT '{}', * status text DEFAULT 'available', * created_at timestamp with time zone DEFAULT timezone('utc'::text, now()) NOT NULL * ); * * CREATE TABLE IF NOT EXISTS public.site_settings ( * id text PRIMARY KEY, * site_name text DEFAULT 'Lameira Multimarcas', * address text DEFAULT 'Estrada da posse 1200, Rio de Janeiro - RJ, 23088-000', * phone text DEFAULT '21 96449-2682', * whatsapp text DEFAULT '5521964492682', * instagram_url text DEFAULT 'https://www.instagram.com/lameiras.multimarcas', * facebook_url text DEFAULT 'https://www.facebook.com/lameirasmultimarcas', * logo_url text, * primary_color text DEFAULT '#fcc201', * hero_title text DEFAULT 'LAMEIRAS MULTIMARCAS', * hero_subtitle text DEFAULT 'Qualidade, procedência e as melhores condições da Estrada da Posse.', * footer_description text DEFAULT 'A melhor experiência na compra e venda de seminovos no Rio de Janeiro.' * ); * * -- Criar a configuração inicial do site com os links corretos * INSERT INTO public.site_settings (id, instagram_url, facebook_url, whatsapp) * VALUES ('global', 'https://www.instagram.com/lameiras.multimarcas', 'https://www.facebook.com/lameirasmultimarcas', '5521964492682') * ON CONFLICT (id) DO UPDATE SET * instagram_url = EXCLUDED.instagram_url, * facebook_url = EXCLUDED.facebook_url, * whatsapp = EXCLUDED.whatsapp; * * 3. Vá em 'Storage' no menu lateral, crie um novo bucket chamado 'vehicles' e mude o acesso para 'Public'. */ const supabaseUrl = 'https://lehlswirraoqiioazdkg.supabase.co'; const supabaseKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImxlaGxzd2lycmFvcWlpb2F6ZGtnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Njk4OTE4NTUsImV4cCI6MjA4NTQ2Nzg1NX0.0CbI9nfut-mW9NtcmNwXFmpfTABPVG9unfVzAmAAm-4'; const supabase = createClient(supabaseUrl, supabaseKey); type FuelType = 'Gasolina' | 'Etanol' | 'Flex' | 'Diesel' | 'GNV' | 'Híbrido' | 'Elétrico'; interface Vehicle { id: string; make: string; model: string; year_model: number; year_fab: number; price: number; mileage: number; fuel_type: FuelType; description: string; images: string[]; status: 'available' | 'sold'; created_at?: string; } interface SiteSettings { id: string; site_name: string; address: string; phone: string; whatsapp: string; instagram_url: string; facebook_url: string; logo_url: string | null; primary_color: string; hero_title: string; hero_subtitle: string; footer_description: string; } const Logo = ({ className = "h-10", settings }: { className?: string, settings?: SiteSettings }) => { if (settings?.logo_url) { return {settings.site_name}; } return ( {settings?.site_name.split(' ')[0] || 'Lameira'} ); }; const VehicleCard: React.FC<{ vehicle: Vehicle, settings?: SiteSettings }> = ({ vehicle, settings }) => { const adLink = `${window.location.origin}/?veiculo=${vehicle.id}`; const messageText = `Olá! Vi o anúncio no site e tenho interesse neste veículo: 📌 *${vehicle.make} ${vehicle.model}* 📅 Ano: ${vehicle.year_fab}/${vehicle.year_model} ⛽ Combustível: ${vehicle.fuel_type} 🛣️ KM: ${vehicle.mileage.toLocaleString('pt-BR')} 💰 Valor: *R$ ${vehicle.price.toLocaleString('pt-BR')}* 🔗 Link do anúncio: ${adLink}`; const message = encodeURIComponent(messageText); const waNumber = settings?.whatsapp || '5521964492682'; const url = `https://wa.me/${waNumber}?text=${message}`; return (
{vehicle.model}
{vehicle.year_model}
{vehicle.images && vehicle.images.length > 1 && (
{vehicle.images.length}
)} {vehicle.status === 'sold' && (
Vendido
)}

{vehicle.make} {vehicle.model}

{vehicle.fuel_type}

R$ {vehicle.price.toLocaleString('pt-BR')}

{vehicle.mileage.toLocaleString('pt-BR')} km
Tenho Interesse
); }; const App = () => { const [activeTab, setActiveTab] = useState<'home' | 'estoque' | 'admin' | 'contato' | 'politicas'>('home'); const [adminSubTab, setAdminSubTab] = useState<'vehicles' | 'settings'>('vehicles'); const [settings, setSettings] = useState(null); const [vehicles, setVehicles] = useState([]); const [loading, setLoading] = useState(true); const [user, setUser] = useState(null); const [showLGPD, setShowLGPD] = useState(true); const [searchTerm, setSearchTerm] = useState(''); const [isMenuOpen, setIsMenuOpen] = useState(false); const [loginEmail, setLoginEmail] = useState(''); const [loginPassword, setLoginPassword] = useState(''); const [loginError, setLoginError] = useState(''); const [showModal, setShowModal] = useState(false); const [saving, setSaving] = useState(false); const [editingId, setEditingId] = useState(null); const [selectedFiles, setSelectedFiles] = useState([]); const fileInputRef = useRef(null); const logoInputRef = useRef(null); const directLogoInputRef = useRef(null); const [formData, setFormData] = useState>({ make: '', model: '', year_model: 2024, year_fab: 2024, price: 0, mileage: 0, fuel_type: 'Flex', status: 'available', images: [] }); const [settingsForm, setSettingsForm] = useState>({}); useEffect(() => { fetchInitialData(); supabase.auth.getSession().then(({ data: { session } }) => setUser(session?.user ?? null)); const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => setUser(session?.user ?? null)); return () => subscription.unsubscribe(); }, []); const fetchInitialData = async () => { setLoading(true); const [vehiclesRes, settingsRes] = await Promise.all([ supabase.from('vehicles').select('*').order('created_at', { ascending: false }), supabase.from('site_settings').select('*').eq('id', 'global').single() ]); if (vehiclesRes.data) setVehicles(vehiclesRes.data); if (settingsRes.data) { setSettings(settingsRes.data); setSettingsForm(settingsRes.data); } setLoading(false); }; const isDiego = user?.email === 'diegocarreiroonline@gmail.com'; const handleLogin = async (e: React.FormEvent) => { e.preventDefault(); setLoginError(''); const { data, error } = await supabase.auth.signInWithPassword({ email: loginEmail, password: loginPassword }); if (error) setLoginError('Erro de acesso. Verifique seu email e senha.'); else setUser(data.user); }; const handleLogout = async () => { await supabase.auth.signOut(); setUser(null); toggleTab('home'); }; const handleSaveSettings = async (e?: React.FormEvent, newSettings?: Partial) => { if (e) e.preventDefault(); setSaving(true); const updatedData = newSettings || settingsForm; const { error } = await supabase.from('site_settings').update(updatedData).eq('id', 'global'); if (!error) { setSettings(prev => ({ ...prev, ...updatedData } as SiteSettings)); setSettingsForm(prev => ({ ...prev, ...updatedData })); if (!newSettings) alert('Configurações atualizadas com sucesso!'); } else { alert('Erro ao salvar: ' + error.message); } setSaving(false); }; const handleLogoUpload = async (e: React.ChangeEvent) => { if (!e.target.files?.[0]) return; const file = e.target.files[0]; const fileExt = file.name.split('.').pop(); const filePath = `site-assets/logo-${Math.random()}.${fileExt}`; setSaving(true); const { error: uploadError } = await supabase.storage.from('vehicles').upload(filePath, file); if (uploadError) { setSaving(false); return alert('Erro upload logo'); } const { data } = supabase.storage.from('vehicles').getPublicUrl(filePath); await handleSaveSettings(undefined, { logo_url: data.publicUrl }); setSaving(false); }; const handleRemoveLogo = async () => { if (confirm('Deseja remover a imagem da logo e voltar para o padrão?')) { await handleSaveSettings(undefined, { logo_url: null }); } }; const uploadImages = async (files: File[]): Promise => { const uploadPromises = files.map(async (file) => { const fileExt = file.name.split('.').pop(); const fileName = `${Math.random()}.${fileExt}`; const filePath = `vehicle-photos/${fileName}`; const { error } = await supabase.storage.from('vehicles').upload(filePath, file); if (error) return null; const { data } = supabase.storage.from('vehicles').getPublicUrl(filePath); return data.publicUrl; }); const results = await Promise.all(uploadPromises); return results.filter((url): url is string => url !== null); }; const closeModal = () => { setShowModal(false); setEditingId(null); setFormData({ make: '', model: '', year_model: 2024, year_fab: 2024, price: 0, mileage: 0, fuel_type: 'Flex', status: 'available', images: [] }); setSelectedFiles([]); }; const handleSaveVehicle = async (e: React.FormEvent) => { e.preventDefault(); setSaving(true); try { let currentImages = formData.images || []; if (selectedFiles.length > 0) { const newUrls = await uploadImages(selectedFiles); currentImages = [...currentImages, ...newUrls]; } if (currentImages.length === 0) throw new Error('Mínimo de 1 foto.'); const vehicleData = { ...formData, images: currentImages }; const { error } = editingId ? await supabase.from('vehicles').update(vehicleData).eq('id', editingId) : await supabase.from('vehicles').insert([vehicleData]); if (error) throw error; fetchInitialData(); closeModal(); } catch (err: any) { alert(err.message); } finally { setSaving(false); } }; const toggleTab = (tab: any) => { setActiveTab(tab); window.scrollTo(0, 0); setIsMenuOpen(false); }; const primaryColor = settings?.primary_color || '#fcc201'; return (
{/* HEADER TOP BAR */}
{settings?.address}
{/* NAVIGATION */}
{activeTab === 'home' && (

{settings?.hero_title}

{settings?.hero_subtitle}

Últimos Anúncios

{loading ? (
) : (
{vehicles.slice(0, 6).map(v => ( ))}
)}
)} {activeTab === 'estoque' && (

Estoque Completo

setSearchTerm(e.target.value)} />
{vehicles.filter(v => v.model.toLowerCase().includes(searchTerm.toLowerCase())).map(v => )}
)} {activeTab === 'admin' && (
{!user ? (

Acesso Painel

setLoginEmail(e.target.value)} className="w-full p-4 bg-gray-50 rounded-2xl border" placeholder="Email" required /> setLoginPassword(e.target.value)} className="w-full p-4 bg-gray-50 rounded-2xl border" placeholder="Senha" required />
) : (
{isDiego && ( )}
{adminSubTab === 'vehicles' ? (

Meus Anúncios

{vehicles.map(v => ( ))}
FotoVeículoPreçoAções
{v.make} {v.model} R$ {v.price.toLocaleString('pt-BR')}
) : (

Customizar o Site (CMS)

Identidade Visual

{settingsForm.logo_url ? : }
setSettingsForm({...settingsForm, primary_color: e.target.value})} className="w-12 h-12 rounded-xl border-none cursor-pointer" /> {settingsForm.primary_color}

Informações de Contato

setSettingsForm({...settingsForm, site_name: e.target.value})} className="w-full p-4 bg-gray-50 rounded-xl border font-bold" />
setSettingsForm({...settingsForm, address: e.target.value})} className="w-full p-4 bg-gray-50 rounded-xl border font-bold" />
setSettingsForm({...settingsForm, phone: e.target.value})} className="w-full p-4 bg-gray-50 rounded-xl border font-bold" />
setSettingsForm({...settingsForm, whatsapp: e.target.value})} className="w-full p-4 bg-gray-50 rounded-xl border font-bold" />

Conteúdo do Site (Hero & Footer)

setSettingsForm({...settingsForm, hero_title: e.target.value})} className="w-full p-4 bg-gray-50 rounded-xl border font-bold" />
setSettingsForm({...settingsForm, hero_subtitle: e.target.value})} className="w-full p-4 bg-gray-50 rounded-xl border font-bold" />