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
;
}
return (
);
};
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.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 */}
{/* NAVIGATION */}
{activeTab === 'home' && (
{settings?.hero_title}
{settings?.hero_subtitle}
{loading ? (
) : (
{vehicles.slice(0, 6).map(v => (
))}
)}
)}
{activeTab === 'estoque' && (
{vehicles.filter(v => v.model.toLowerCase().includes(searchTerm.toLowerCase())).map(v => )}
)}
{activeTab === 'admin' && (
{!user ? (
Acesso Painel
) : (
{isDiego && (
)}
{adminSubTab === 'vehicles' ? (
Meus Anúncios
| Foto | Veículo | Preço | Ações |
{vehicles.map(v => (
 |
{v.make} {v.model} |
R$ {v.price.toLocaleString('pt-BR')} |
|
))}
) : (
)}
)}
)}
{showModal && (
Gerenciar Anúncio
)}
{showLGPD && (
Privacidade e Cookies
Nosso site utiliza tecnologias para melhorar sua experiência em conformidade com a LGPD.
)}
);
};
const root = createRoot(document.getElementById('root')!);
root.render();