generated from autoblog/Advogados
Initial commit
This commit is contained in:
commit
8e32874bf5
51 changed files with 13146 additions and 0 deletions
BIN
.DS_Store
vendored
Normal file
BIN
.DS_Store
vendored
Normal file
Binary file not shown.
BIN
Template-01/.DS_Store
vendored
Normal file
BIN
Template-01/.DS_Store
vendored
Normal file
Binary file not shown.
20
Template-01/README.md
Normal file
20
Template-01/README.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<div align="center">
|
||||
<img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
|
||||
</div>
|
||||
|
||||
# Run and deploy your AI Studio app
|
||||
|
||||
This contains everything you need to run your app locally.
|
||||
|
||||
View your app in AI Studio: https://ai.studio/apps/780e049e-cbd9-47bc-85b1-ae1175ffdb47
|
||||
|
||||
## Run Locally
|
||||
|
||||
**Prerequisites:** Node.js
|
||||
|
||||
|
||||
1. Install dependencies:
|
||||
`npm install`
|
||||
2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
|
||||
3. Run the app:
|
||||
`npm run dev`
|
||||
111
Template-01/index.html
Normal file
111
Template-01/index.html
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
<!doctype html>
|
||||
<html lang="pt-BR">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<!-- Primary Meta Tags -->
|
||||
<title>João Silva | Advogado Especialista - Trabalhista, Civil e Família</title>
|
||||
<meta name="title" content="João Silva | Advogado Especialista - Trabalhista, Civil e Família">
|
||||
<meta name="description" content="Atendimento jurídico humanizado e estratégico em São Paulo. Referência em Direito Trabalhista, Previdenciário, Civil, Família, Consumidor e Imobiliário.">
|
||||
<meta name="keywords" content="advogado, advocacia, advogado especialista, direito trabalhista, direito previdenciário, direito civil, direito de família, direito do consumidor, direito imobiliário, advogado são paulo, consultoria jurídica, advogado online">
|
||||
|
||||
<!-- Open Graph / Facebook / WhatsApp (Card Compartilhado) -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://joaosilva.adv.br/">
|
||||
<meta property="og:title" content="João Silva | Advocacia Premium e Especializada">
|
||||
<meta property="og:description" content="Advocacia estratégica e humanizada. Atendimento direto e especializado em Direito Trabalhista, Previdenciário, Civil, Família, Consumidor e Imobiliário. Proteja seus direitos.">
|
||||
<meta property="og:image" content="https://images.unsplash.com/photo-1589829085413-56de8ae18c73?q=80&w=1200&auto=format&fit=crop">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:url" content="https://joaosilva.adv.br/">
|
||||
<meta property="twitter:title" content="João Silva | Advocacia Premium e Especializada">
|
||||
<meta property="twitter:description" content="Advocacia estratégica e humanizada. Atendimento direto e especializado em Direito Trabalhista, Previdenciário, Civil, Família, Consumidor e Imobiliário. Proteja seus direitos.">
|
||||
<meta property="twitter:image" content="https://images.unsplash.com/photo-1589829085413-56de8ae18c73?q=80&w=1200&auto=format&fit=crop">
|
||||
|
||||
<!-- Favicon (SVG) -->
|
||||
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23D4AF37' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='m16 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z'/><path d='m2 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z'/><path d='M7 21h10'/><path d='M12 3v18'/><path d='M3 7h2c2 0 5-1 7-2 2 1 5 2 7 2h2'/></svg>" />
|
||||
|
||||
<!-- Schema.org LegalService (Google Rich Content) -->
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "LegalService",
|
||||
"name": "João Silva | Advogado Especialista",
|
||||
"image": "https://images.unsplash.com/photo-1589829085413-56de8ae18c73?q=80&w=1200&auto=format&fit=crop",
|
||||
"@id": "https://joaosilva.adv.br/",
|
||||
"url": "https://joaosilva.adv.br/",
|
||||
"telephone": "+5511999999999",
|
||||
"address": {
|
||||
"@type": "PostalAddress",
|
||||
"streetAddress": "Av. Paulista, 1000 - Bela Vista",
|
||||
"addressLocality": "São Paulo",
|
||||
"addressRegion": "SP",
|
||||
"postalCode": "01310-100",
|
||||
"addressCountry": "BR"
|
||||
},
|
||||
"geo": {
|
||||
"@type": "GeoCoordinates",
|
||||
"latitude": -23.561491,
|
||||
"longitude": -46.655881
|
||||
},
|
||||
"priceRange": "$$",
|
||||
"openingHoursSpecification": {
|
||||
"@type": "OpeningHoursSpecification",
|
||||
"dayOfWeek": [
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday"
|
||||
],
|
||||
"opens": "09:00",
|
||||
"closes": "18:00"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
|
||||
<!-- Theme Auto-Updater injected by autoblogia -->
|
||||
<script>
|
||||
window.addEventListener('message', (event) => {
|
||||
if (event.data && event.data.type === 'UPDATE_APPEARANCE') {
|
||||
const { primaryColor, backgroundColor, fontFamily } = event.data.settings;
|
||||
const root = document.documentElement;
|
||||
const style = root.style;
|
||||
|
||||
if (primaryColor) {
|
||||
style.setProperty('--primary', primaryColor);
|
||||
style.setProperty('--color-primary', primaryColor);
|
||||
style.setProperty('--color-tech-primary', primaryColor);
|
||||
style.setProperty('--color-seo-primary', primaryColor);
|
||||
style.setProperty('--color-finance-primary', primaryColor);
|
||||
style.setProperty('--color-recipe-primary', primaryColor);
|
||||
style.setProperty('--color-health-primary', primaryColor);
|
||||
style.setProperty('--color-corporate-primary', primaryColor);
|
||||
}
|
||||
if (backgroundColor) {
|
||||
style.setProperty('--background', backgroundColor);
|
||||
style.setProperty('--color-bg', backgroundColor);
|
||||
style.setProperty('--color-tech-surface', backgroundColor);
|
||||
style.setProperty('--color-seo-surface', backgroundColor);
|
||||
style.backgroundColor = backgroundColor;
|
||||
}
|
||||
if (fontFamily) {
|
||||
const fontString = '"' + fontFamily + '", sans-serif';
|
||||
style.setProperty('--font-family', fontString);
|
||||
style.setProperty('--font-sans', fontString);
|
||||
style.fontFamily = fontString;
|
||||
}
|
||||
}
|
||||
});
|
||||
// Ping parent window that we are ready
|
||||
window.parent.postMessage({ type: 'IFRAME_READY' }, '*');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
6
Template-01/metadata.json
Normal file
6
Template-01/metadata.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "Advocacia Premium",
|
||||
"description": "Website premium para advogado autônomo com foco em autoridade e captação.",
|
||||
"requestFramePermissions": [],
|
||||
"majorCapabilities": []
|
||||
}
|
||||
4443
Template-01/package-lock.json
generated
Normal file
4443
Template-01/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
38
Template-01/package.json
Normal file
38
Template-01/package.json
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"name": "react-example",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --port=3000 --host=0.0.0.0",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"clean": "rm -rf dist",
|
||||
"lint": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@google/genai": "^1.29.0",
|
||||
"@tailwindcss/typography": "^0.5.19",
|
||||
"@tailwindcss/vite": "^4.1.14",
|
||||
"@vitejs/plugin-react": "^5.0.4",
|
||||
"clsx": "^2.1.1",
|
||||
"dotenv": "^17.2.3",
|
||||
"express": "^4.21.2",
|
||||
"lucide-react": "^0.546.0",
|
||||
"motion": "^12.23.24",
|
||||
"react": "^19.0.1",
|
||||
"react-dom": "^19.0.1",
|
||||
"react-router-dom": "^7.15.0",
|
||||
"tailwind-merge": "^3.5.0",
|
||||
"vite": "^6.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/node": "^22.14.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"tailwindcss": "^4.1.14",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "~5.8.2",
|
||||
"vite": "^6.2.3"
|
||||
}
|
||||
}
|
||||
40
Template-01/src/App.tsx
Normal file
40
Template-01/src/App.tsx
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import React from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
||||
import { Navbar } from './components/layout/Navbar';
|
||||
import { Footer } from './components/layout/Footer';
|
||||
import { WhatsAppButton } from './components/layout/WhatsAppButton';
|
||||
import { CookieBanner } from './components/layout/CookieBanner';
|
||||
import { Analytics } from './components/layout/Analytics';
|
||||
import { Home } from './pages/Home';
|
||||
import { Sobre } from './pages/Sobre';
|
||||
import { Areas } from './pages/Areas';
|
||||
import { ComoFunciona } from './pages/ComoFunciona';
|
||||
import { Blog } from './pages/Blog';
|
||||
import { BlogPost } from './pages/BlogPost';
|
||||
import { Contact } from './pages/Contact';
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Router>
|
||||
<Analytics />
|
||||
<div className="flex min-h-screen flex-col font-sans text-gray-800 antialiased selection:bg-brand-gold selection:text-white">
|
||||
<Navbar />
|
||||
<div className="flex-1">
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/sobre" element={<Sobre />} />
|
||||
<Route path="/areas" element={<Areas />} />
|
||||
<Route path="/funcionamento" element={<ComoFunciona />} />
|
||||
<Route path="/blog" element={<Blog />} />
|
||||
<Route path="/blog/:id" element={<BlogPost />} />
|
||||
<Route path="/contato" element={<Contact />} />
|
||||
</Routes>
|
||||
</div>
|
||||
<Footer />
|
||||
<WhatsAppButton />
|
||||
<CookieBanner />
|
||||
</div>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
|
||||
85
Template-01/src/components/layout/Analytics.tsx
Normal file
85
Template-01/src/components/layout/Analytics.tsx
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import { useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
gtag?: (...args: any[]) => void;
|
||||
dataLayer?: any[];
|
||||
fbq?: (...args: any[]) => void;
|
||||
_fbq?: any;
|
||||
}
|
||||
}
|
||||
|
||||
export function Analytics() {
|
||||
const location = useLocation();
|
||||
|
||||
// Inicialização dos Scripts (Executado apenas uma vez)
|
||||
useEffect(() => {
|
||||
// 1. Google Analytics (GA4)
|
||||
const gaId = import.meta.env.VITE_GA_MEASUREMENT_ID;
|
||||
if (gaId && !document.getElementById('ga-script')) {
|
||||
const script1 = document.createElement('script');
|
||||
script1.async = true;
|
||||
script1.src = `https://www.googletagmanager.com/gtag/js?id=${gaId}`;
|
||||
script1.id = 'ga-script';
|
||||
document.head.appendChild(script1);
|
||||
|
||||
const script2 = document.createElement('script');
|
||||
script2.innerHTML = `
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', '${gaId}');
|
||||
`;
|
||||
document.head.appendChild(script2);
|
||||
}
|
||||
|
||||
// 2. Facebook Pixel
|
||||
const fbPixelId = import.meta.env.VITE_FB_PIXEL_ID;
|
||||
if (fbPixelId && !document.getElementById('fb-pixel-script')) {
|
||||
const script = document.createElement('script');
|
||||
script.id = 'fb-pixel-script';
|
||||
script.innerHTML = `
|
||||
!function(f,b,e,v,n,t,s)
|
||||
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
|
||||
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
|
||||
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
|
||||
n.queue=[];t=b.createElement(e);t.async=!0;
|
||||
t.src=v;s=b.getElementsByTagName(e)[0];
|
||||
s.parentNode.insertBefore(t,s)}(window, document,'script',
|
||||
'https://connect.facebook.net/en_US/fbevents.js');
|
||||
fbq('init', '${fbPixelId}');
|
||||
fbq('track', 'PageView');
|
||||
`;
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
|
||||
// 3. Google AdSense
|
||||
const adsenseId = import.meta.env.VITE_ADSENSE_CLIENT_ID;
|
||||
if (adsenseId && !document.getElementById('adsense-script')) {
|
||||
const script = document.createElement('script');
|
||||
script.id = 'adsense-script';
|
||||
script.async = true;
|
||||
script.src = `https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${adsenseId}`;
|
||||
script.crossOrigin = 'anonymous';
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Rastreamento de Mudança de Rota (Page Views)
|
||||
useEffect(() => {
|
||||
const gaId = import.meta.env.VITE_GA_MEASUREMENT_ID;
|
||||
if (gaId && window.gtag) {
|
||||
window.gtag('config', gaId, {
|
||||
page_path: location.pathname + location.search,
|
||||
});
|
||||
}
|
||||
|
||||
const fbPixelId = import.meta.env.VITE_FB_PIXEL_ID;
|
||||
if (fbPixelId && typeof window.fbq === 'function') {
|
||||
window.fbq('track', 'PageView');
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
return null; // Este componente não renderiza nada na tela
|
||||
}
|
||||
69
Template-01/src/components/layout/CookieBanner.tsx
Normal file
69
Template-01/src/components/layout/CookieBanner.tsx
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { motion, AnimatePresence } from 'motion/react';
|
||||
import { X } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export function CookieBanner() {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Check if user has already accepted/declined
|
||||
const consent = localStorage.getItem('lgpd_consent');
|
||||
if (!consent) {
|
||||
// Small delay so it doesn't pop up instantly on page load
|
||||
const timer = setTimeout(() => setIsVisible(true), 1500);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleConsent = (accepted: boolean) => {
|
||||
localStorage.setItem('lgpd_consent', accepted ? 'accepted' : 'declined');
|
||||
setIsVisible(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{isVisible && (
|
||||
<motion.div
|
||||
initial={{ y: 100, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
exit={{ y: 100, opacity: 0 }}
|
||||
className="fixed bottom-0 left-0 right-0 z-[60] bg-brand-blue p-4 shadow-2xl md:p-6"
|
||||
>
|
||||
<div className="mx-auto flex max-w-7xl flex-col items-center justify-between gap-4 md:flex-row">
|
||||
<div className="text-sm text-gray-300 md:max-w-3xl">
|
||||
<strong className="block text-white mb-1">Privacidade e LGPD</strong>
|
||||
Utilizamos cookies para melhorar sua experiência em nosso site, personalizar conteúdo e analisar nosso tráfego.
|
||||
Ao continuar navegando, você concorda com a nossa{' '}
|
||||
<Link to="/contato" className="text-brand-gold hover:underline">
|
||||
Política de Privacidade
|
||||
</Link>.
|
||||
</div>
|
||||
|
||||
<div className="flex w-full flex-shrink-0 flex-row gap-3 md:w-auto">
|
||||
<button
|
||||
onClick={() => handleConsent(false)}
|
||||
className="flex-1 rounded-sm border border-white/30 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-white/10 md:flex-none"
|
||||
>
|
||||
Recusar
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleConsent(true)}
|
||||
className="flex-1 rounded-sm bg-brand-gold px-6 py-2 text-sm font-bold text-white transition-colors hover:bg-brand-gold-hover md:flex-none"
|
||||
>
|
||||
Aceitar
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleConsent(false)}
|
||||
className="hidden items-center justify-center text-gray-400 hover:text-white md:flex ml-2"
|
||||
aria-label="Fechar"
|
||||
>
|
||||
<X className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
75
Template-01/src/components/layout/Footer.tsx
Normal file
75
Template-01/src/components/layout/Footer.tsx
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import React from 'react';
|
||||
import { Scale, Mail, MapPin, Phone, Instagram, Linkedin } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<footer className="bg-brand-blue py-16 text-white text-opacity-80">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 gap-12 md:grid-cols-4 lg:grid-cols-5">
|
||||
{/* Brand */}
|
||||
<div className="lg:col-span-2">
|
||||
<Link to="/" className="mb-6 flex items-center gap-2">
|
||||
<Scale className="h-8 w-8 text-brand-gold" />
|
||||
<div className="flex flex-col">
|
||||
<span className="font-serif text-2xl font-bold leading-none tracking-tight text-white">
|
||||
João Silva
|
||||
</span>
|
||||
<span className="text-xs font-medium tracking-widest text-brand-gold">
|
||||
ADVOCACIA E CONSULTORIA
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
<p className="mb-6 max-w-xs text-sm leading-relaxed">
|
||||
Atendimento direto, estratégico e sigiloso focado na defesa incansável dos seus direitos e do seu patrimônio.
|
||||
</p>
|
||||
<div className="flex items-center gap-4">
|
||||
<a href="#" className="flex h-10 w-10 items-center justify-center rounded-full border border-white/20 transition-colors hover:border-brand-gold hover:text-brand-gold">
|
||||
<Instagram className="h-4 w-4" />
|
||||
</a>
|
||||
<a href="#" className="flex h-10 w-10 items-center justify-center rounded-full border border-white/20 transition-colors hover:border-brand-gold hover:text-brand-gold">
|
||||
<Linkedin className="h-4 w-4" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Links */}
|
||||
<div>
|
||||
<h3 className="mb-6 font-serif text-lg font-semibold text-white">Navegação</h3>
|
||||
<ul className="flex flex-col gap-3 text-sm">
|
||||
<li><Link to="/sobre" className="hover:text-brand-gold transition-colors">Sobre o Advogado</Link></li>
|
||||
<li><Link to="/areas" className="hover:text-brand-gold transition-colors">Áreas de Atuação</Link></li>
|
||||
<li><Link to="/funcionamento" className="hover:text-brand-gold transition-colors">Como Funciona</Link></li>
|
||||
<li><Link to="/blog" className="hover:text-brand-gold transition-colors">Blog Jurídico</Link></li>
|
||||
<li><Link to="/contato" className="hover:text-brand-gold transition-colors">Contato</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Contact */}
|
||||
<div className="lg:col-span-2">
|
||||
<h3 className="mb-6 font-serif text-lg font-semibold text-white">Contato Direto</h3>
|
||||
<ul className="flex flex-col gap-4 text-sm">
|
||||
<li className="flex items-start gap-3">
|
||||
<Phone className="h-5 w-5 shrink-0 text-brand-gold" />
|
||||
<span>+55 (11) 99999-9999</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-3">
|
||||
<Mail className="h-5 w-5 shrink-0 text-brand-gold" />
|
||||
<span>contato@joaosilva.adv.br</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-3">
|
||||
<MapPin className="h-5 w-5 shrink-0 text-brand-gold" />
|
||||
<span>Av. Paulista, 1000 - Bela Vista<br />São Paulo - SP, 01310-100</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-16 flex flex-col items-center justify-between border-t border-white/10 pt-8 text-xs sm:flex-row">
|
||||
<p>© {new Date().getFullYear()} João Silva Advocacia. Todos os direitos reservados.</p>
|
||||
<p className="mt-2 sm:mt-0">OAB/SP 123.456</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
161
Template-01/src/components/layout/Navbar.tsx
Normal file
161
Template-01/src/components/layout/Navbar.tsx
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { Menu, X, Scale, Search } from 'lucide-react';
|
||||
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
||||
import { cn } from '../../lib/utils';
|
||||
|
||||
export function Navbar() {
|
||||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setIsScrolled(window.scrollY > 10);
|
||||
};
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
return () => window.removeEventListener('scroll', handleScroll);
|
||||
}, []);
|
||||
|
||||
// Handle hash scrolling when navigating across pages
|
||||
useEffect(() => {
|
||||
if (location.hash) {
|
||||
const element = document.querySelector(location.hash);
|
||||
if (element) {
|
||||
setTimeout(() => {
|
||||
element.scrollIntoView({ behavior: 'smooth' });
|
||||
}, 100);
|
||||
}
|
||||
} else {
|
||||
window.scrollTo(0,0);
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
const handleNavClick = (e: React.MouseEvent<HTMLAnchorElement>, href: string) => {
|
||||
setIsMobileMenuOpen(false);
|
||||
if (href.startsWith('/#')) {
|
||||
const hash = href.substring(1);
|
||||
if (location.pathname === '/') {
|
||||
e.preventDefault();
|
||||
const element = document.querySelector(hash);
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: 'smooth' });
|
||||
// Update URL hash without jumping
|
||||
window.history.pushState(null, '', hash);
|
||||
}
|
||||
} else {
|
||||
// Let it navigate to /#hash normally
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const navLinks = [
|
||||
{ name: 'Sobre', href: '/sobre' },
|
||||
{ name: 'Áreas de Atuação', href: '/areas' },
|
||||
{ name: 'Como Funciona', href: '/funcionamento' },
|
||||
{ name: 'Blog', href: '/blog' },
|
||||
{ name: 'Contato', href: '/contato' },
|
||||
];
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={cn(
|
||||
'fixed left-0 right-0 top-0 z-50 transition-all duration-300',
|
||||
isScrolled
|
||||
? 'bg-white shadow-md py-3'
|
||||
: 'bg-brand-blue py-5 text-white'
|
||||
)}
|
||||
>
|
||||
<div className="mx-auto flex max-w-7xl items-center justify-between px-6 lg:px-8">
|
||||
<Link to="/" className="flex items-center gap-2">
|
||||
<Scale className={cn("h-8 w-8", isScrolled ? "text-brand-gold" : "text-brand-gold")} />
|
||||
<div className="flex flex-col">
|
||||
<span className={cn("font-serif text-xl font-bold leading-none tracking-tight", isScrolled ? "text-brand-blue" : "text-white")}>
|
||||
João Silva
|
||||
</span>
|
||||
<span className={cn("text-xs font-medium tracking-widest", isScrolled ? "text-gray-500" : "text-gray-300")}>
|
||||
ADVOGADO
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
{/* Desktop Nav */}
|
||||
<div className="hidden items-center gap-8 md:flex">
|
||||
{navLinks.map((link) => (
|
||||
<Link
|
||||
key={link.name}
|
||||
to={link.href}
|
||||
onClick={(e) => handleNavClick(e, link.href)}
|
||||
className={cn(
|
||||
'text-sm font-medium transition-colors hover:text-brand-gold',
|
||||
isScrolled ? 'text-gray-700' : 'text-gray-200'
|
||||
)}
|
||||
>
|
||||
{link.name}
|
||||
</Link>
|
||||
))}
|
||||
<div className="flex items-center gap-6 border-l border-white/20 pl-6">
|
||||
<Link
|
||||
to="/blog?focus=true"
|
||||
title="Buscar Artigos"
|
||||
className={cn(
|
||||
'transition-colors hover:text-brand-gold',
|
||||
isScrolled ? 'text-brand-blue' : 'text-white'
|
||||
)}
|
||||
>
|
||||
<Search className="h-5 w-5" />
|
||||
</Link>
|
||||
<Link
|
||||
to="/contato"
|
||||
className="rounded-none border-2 border-brand-gold bg-brand-gold px-6 py-2 text-sm font-semibold text-white transition-colors hover:bg-transparent hover:text-brand-gold"
|
||||
>
|
||||
Consulta Online
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Nav Toggle */}
|
||||
<button
|
||||
className="md:hidden"
|
||||
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
||||
>
|
||||
{isMobileMenuOpen ? (
|
||||
<X className={cn("h-6 w-6", isScrolled ? "text-brand-blue" : "text-white")} />
|
||||
) : (
|
||||
<Menu className={cn("h-6 w-6", isScrolled ? "text-brand-blue" : "text-white")} />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu */}
|
||||
{isMobileMenuOpen && (
|
||||
<div className="absolute left-0 right-0 top-full flex flex-col items-center gap-6 bg-white py-8 shadow-xl md:hidden">
|
||||
{navLinks.map((link) => (
|
||||
<Link
|
||||
key={link.name}
|
||||
to={link.href}
|
||||
onClick={(e) => handleNavClick(e, link.href)}
|
||||
className="text-base font-medium text-gray-800 transition-colors hover:text-brand-gold"
|
||||
>
|
||||
{link.name}
|
||||
</Link>
|
||||
))}
|
||||
<Link
|
||||
to="/blog?focus=true"
|
||||
onClick={() => setIsMobileMenuOpen(false)}
|
||||
className="flex items-center gap-2 text-base font-medium text-gray-800 transition-colors hover:text-brand-gold"
|
||||
>
|
||||
<Search className="h-5 w-5" /> Buscar Artigo
|
||||
</Link>
|
||||
<Link
|
||||
to="/contato"
|
||||
onClick={() => setIsMobileMenuOpen(false)}
|
||||
className="mt-4 rounded-none border-2 border-brand-gold bg-brand-gold px-8 py-3 text-base font-semibold text-white transition-colors hover:bg-white hover:text-brand-gold"
|
||||
>
|
||||
Consulta Online
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
18
Template-01/src/components/layout/WhatsAppButton.tsx
Normal file
18
Template-01/src/components/layout/WhatsAppButton.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { Phone } from 'lucide-react';
|
||||
|
||||
export function WhatsAppButton() {
|
||||
return (
|
||||
<div className="fixed bottom-6 right-6 z-50">
|
||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-400 opacity-75"></span>
|
||||
<a
|
||||
href="https://wa.me/5511999999999?text=Ol%C3%A1%2C%20gostaria%20de%20agendar%20uma%20consulta%20jur%C3%ADdica."
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="relative flex h-14 w-14 items-center justify-center rounded-full bg-green-500 text-white shadow-lg transition-transform hover:scale-110 hover:shadow-xl hover:bg-green-600 focus:outline-none"
|
||||
aria-label="Falar pelo WhatsApp"
|
||||
>
|
||||
<Phone className="h-7 w-7" fill="currentColor" />
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
113
Template-01/src/data/posts.ts
Normal file
113
Template-01/src/data/posts.ts
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
export interface BlogPostData {
|
||||
id: number;
|
||||
title: string;
|
||||
excerpt: string;
|
||||
category: string;
|
||||
date: string;
|
||||
imageUrl: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
const generateMockPosts = (): BlogPostData[] => {
|
||||
const categories = [
|
||||
"Direito Trabalhista", "Direito Previdenciário", "Direito do Consumidor",
|
||||
"Direito de Família", "Direito Imobiliário", "Direito Civil Empresarial"
|
||||
];
|
||||
|
||||
const titles = [
|
||||
"Direitos do Trabalhador: O que você precisa saber antes de pedir demissão",
|
||||
"Revisão da Vida Toda: Entenda se você tem direito a receber os atrasados",
|
||||
"Voo Cancelado: Quais são os seus direitos como consumidor?",
|
||||
"Guarda Compartilhada vs Guarda Unilateral: Entendendo as diferenças",
|
||||
"Contratos de Aluguel: Direitos e Deveres do Inquilino",
|
||||
"Responsabilidade Civil de Sócios: Até onde vai o risco empresarial?",
|
||||
"Atraso na Entrega de Imóveis: Direitos e Indenizações",
|
||||
"Pensão Alimentícia: Como é feito o cálculo?",
|
||||
"Assédio Moral no Trabalho: Como identificar e reunir provas",
|
||||
"Golpes no Pix e a Responsabilidade dos Bancos",
|
||||
"União Estável vs Casamento: Regimes de Bens",
|
||||
"LGPD nas Pequenas Empresas: O que é preciso adequar?",
|
||||
"Demissão por Justa Causa: Quando se aplica?",
|
||||
"Auxílio-Doença Negado pelo INSS: O que fazer?",
|
||||
"Cobranças Indevidas: Como agir e pedir reparação",
|
||||
"Inventário Extrajudicial: Requisitos e Prazos",
|
||||
"Horas Extras: Direitos, Limites e Reflexos",
|
||||
"Aposentadoria Especial: Exigências e Comprovação",
|
||||
"Direito de Arrependimento nas Compras Online",
|
||||
"Alienação Parental: Consequências Jurídicas",
|
||||
"Direitos Autorais na Era Digital: Protegendo suas Criações"
|
||||
];
|
||||
|
||||
const images = [
|
||||
"https://images.unsplash.com/photo-1589829085413-56de8ae18c73",
|
||||
"https://images.unsplash.com/photo-1454165804606-c3d57bc86b40",
|
||||
"https://images.unsplash.com/photo-1507679799987-c73779587ccf",
|
||||
"https://images.unsplash.com/photo-1503694978374-8a2fa686963a",
|
||||
"https://images.unsplash.com/photo-1554224155-6726b3ff858f",
|
||||
"https://images.unsplash.com/photo-1600880292203-757bb62b4baf",
|
||||
"https://images.unsplash.com/photo-1524813686514-a57563d77965",
|
||||
"https://images.unsplash.com/photo-1589391886645-d51941baf7fb",
|
||||
"https://images.unsplash.com/photo-1450101499163-c8848c66cb85",
|
||||
"https://images.unsplash.com/photo-1629904853716-f0bc54eea481",
|
||||
"https://images.unsplash.com/photo-1517486808906-6ca8b3f04846",
|
||||
"https://images.unsplash.com/photo-1505373877841-8d25f7d46678",
|
||||
"https://images.unsplash.com/photo-1593642532400-2682810df593",
|
||||
"https://images.unsplash.com/photo-1436491865332-7a61a109cc05",
|
||||
"https://images.unsplash.com/photo-1502086223501-7ea6ecd79368",
|
||||
"https://images.unsplash.com/photo-1521791136064-7986c2920216",
|
||||
"https://images.unsplash.com/photo-1573164713988-8665fc963095",
|
||||
"https://images.unsplash.com/photo-1536640712-4d4c36ef0e2c",
|
||||
"https://images.unsplash.com/photo-1523240795612-9a054b0db644",
|
||||
"https://images.unsplash.com/photo-1494526585095-c41746248156",
|
||||
"https://images.unsplash.com/photo-1486406146926-c627a92ad1ab",
|
||||
];
|
||||
|
||||
return titles.map((title, index) => {
|
||||
const category = categories[index % categories.length];
|
||||
return {
|
||||
id: index + 1,
|
||||
title: title,
|
||||
excerpt: `Entenda os principais aspectos legais, a jurisprudência atualizada e como garantir seus direitos em relação a ${title.split(':')[0].toLowerCase()}.`,
|
||||
category: category,
|
||||
date: `0${(21 - index) % 9 + 1} Mar 2024`,
|
||||
imageUrl: `${images[index]}?auto=format&fit=crop&q=80&w=800`,
|
||||
content: `
|
||||
<p>A área de <strong>${category}</strong> apresenta constantes desafios judiciais e extrajudiciais. O tema <em>"${title}"</em> tem sido objeto de grande debate nos tribunais superiores do Brasil.</p>
|
||||
|
||||
<h2>Contexto e Aplicação</h2>
|
||||
<p>Diante das recentes mudanças legislativas, é fundamental entender seus impactos práticos. O Superior Tribunal de Justiça (STJ) tem firmado entendimentos importantes que afetam diretamente as relações cotidianas.</p>
|
||||
|
||||
<blockquote>
|
||||
<p>"A proteção dos direitos fundamentais exige do operador do direito uma visão sistêmica e atualizada da jurisprudência." - Doutrina Jurídica Contemporânea</p>
|
||||
</blockquote>
|
||||
|
||||
<p>Entre os principais pontos a serem observados na prática, destacamos:</p>
|
||||
<ul>
|
||||
<li>Análise preventiva de riscos e <em>compliance</em>.</li>
|
||||
<li>Atuação extrajudicial para resolução pacífica de conflitos.</li>
|
||||
<li>Utilização das novas teses firmadas em recursos repetitivos (Temas do STJ e STF).</li>
|
||||
</ul>
|
||||
|
||||
<h2>Exemplo Prático e Decisões Recentes</h2>
|
||||
<p>Imagine o seguinte cenário: um cliente procura auxílio relatando um problema complexo envolvendo direitos não reconhecidos na esfera pertinente ao caso. A abordagem ideal combina a análise documental detalhada com a jurisprudência mais recente.</p>
|
||||
|
||||
<figure>
|
||||
<img src="${images[index]}?auto=format&fit=crop&q=80&w=800" alt="${title}" style="border-radius: 8px; margin: 20px 0; width: 100%; aspect-ratio: 16/9; object-fit: cover;" />
|
||||
<figcaption style="text-align: center; font-size: 0.9em; color: gray;">Reflexões e desdobramentos práticos na defesa dos direitos.</figcaption>
|
||||
</figure>
|
||||
|
||||
<h2>Como agir diante desta situação?</h2>
|
||||
<p>O primeiro passo é reunir todas as provas materiais possíveis: documentos, trocas de e-mails, conversas de WhatsApp, fotografias ou qualquer registro formal da relação firmada entre as partes.</p>
|
||||
<p>Após reunir a documentação, é vital buscar orientação técnica <strong>antes de assinar qualquer acordo ou recibo de quitação</strong>. Em muitos casos, a aceitação de termos genéricos pode inviabilizar a busca por uma reparação total no futuro.</p>
|
||||
|
||||
<p>Portanto, para mais informações e para análise pormenorizada do seu caso concreto, certifique-se de contar com assessoria jurídica especializada e combativa. <a href="/contato">Agende uma consulta conosco</a> para entender as particularidades da sua situação.</p>
|
||||
`
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const MOCK_POSTSList = generateMockPosts();
|
||||
export const MOCK_POSTS: Record<string, BlogPostData> = MOCK_POSTSList.reduce((acc, curr) => ({
|
||||
...acc,
|
||||
[curr.id.toString()]: curr
|
||||
}), {});
|
||||
37
Template-01/src/index.css
Normal file
37
Template-01/src/index.css
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Playfair+Display:ital,wght@0,400;0,500;0,600;0,700;1,400;1,600&display=swap');
|
||||
@import "tailwindcss";
|
||||
@plugin "@tailwindcss/typography";
|
||||
|
||||
@theme {
|
||||
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
|
||||
--font-serif: "Playfair Display", ui-serif, Georgia, serif;
|
||||
|
||||
--color-brand-blue: #0A192F;
|
||||
--color-brand-blue-light: #112240;
|
||||
--color-brand-gold: #D4AF37;
|
||||
--color-brand-gold-hover: #C5A030;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-sans);
|
||||
background-color: #FAFAFA;
|
||||
}
|
||||
|
||||
/* Modern clean scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #0A192F;
|
||||
border-radius: 4px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #D4AF37;
|
||||
}
|
||||
6
Template-01/src/lib/utils.ts
Normal file
6
Template-01/src/lib/utils.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import { type ClassValue, clsx } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
10
Template-01/src/main.tsx
Normal file
10
Template-01/src/main.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import {StrictMode} from 'react';
|
||||
import {createRoot} from 'react-dom/client';
|
||||
import App from './App.tsx';
|
||||
import './index.css';
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
);
|
||||
92
Template-01/src/pages/Areas.tsx
Normal file
92
Template-01/src/pages/Areas.tsx
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
import React from 'react';
|
||||
import { Users, ShieldCheck, Scale, FileText, Home as HomeIcon, ArrowRight } from 'lucide-react';
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
const FadeIn = ({ children, delay = 0 }: { children: React.ReactNode, delay?: number }) => (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
transition={{ duration: 0.6, delay }}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
export function Areas() {
|
||||
const areas = [
|
||||
{
|
||||
title: "Direito Trabalhista",
|
||||
icon: Users,
|
||||
desc: "Defesa dos seus direitos no ambiente de trabalho. Atuamos com rescisões indiretas, cobrança de horas extras não pagas, assédio moral no trabalho, reconhecimento de vínculo empregatício e doenças ocupacionais. Orientação tanto para empregados quanto para empresas em compliance preventivo."
|
||||
},
|
||||
{
|
||||
title: "Direito Previdenciário",
|
||||
icon: ShieldCheck,
|
||||
desc: "Garantia do seu benefício justo. Realizamos planejamento previdenciário, pedidos de aposentadoria por tempo de contribuição ou idade, aposentadorias especiais, benefícios assistenciais (BPC/LOAS), pensão por morte e reversão de auxílio-doença negado pelo INSS."
|
||||
},
|
||||
{
|
||||
title: "Direito Civil",
|
||||
icon: Scale,
|
||||
desc: "Proteção do seu patrimônio e interesses civis. Elaboração e revisão de contratos, ações de indenizações por danos morais e materiais, responsabilidade civil, regularização de propriedades e processos de cobranças diversas para recuperação de crédito."
|
||||
},
|
||||
{
|
||||
title: "Direito de Família",
|
||||
icon: Users,
|
||||
desc: "Soluções ágeis com a sensibilidade que o caso exige. Orientação em divórcios consensuais ou litigiosos, fixação e revisão de pensão alimentícia, regulamentação de guarda e convivência (visitas), além de atuação em inventários judiciais e extrajudiciais."
|
||||
},
|
||||
{
|
||||
title: "Direito do Consumidor",
|
||||
icon: FileText,
|
||||
desc: "Defesa intransigente contra abusos de empresas e prestadoras de serviço. Atuamos em casos de negativações indevidas (SPC/Serasa), falhas em prestação de serviços (telefonia, planos de saúde), atrasos e cancelamentos de voos, e defeitos não solucionados em produtos novos."
|
||||
},
|
||||
{
|
||||
title: "Direito Imobiliário",
|
||||
icon: HomeIcon,
|
||||
desc: "Segurança total em transações de imóveis. Assessoramos em usucapião para legalização de titularidade, distratos imobiliários com construtoras por atraso de obras, confecção de contratos de aluguel e venda protegidos, e ações de despejo."
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<main className="w-full bg-[#f9fafb] pb-24 pt-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div className="mb-16 text-center">
|
||||
<FadeIn>
|
||||
<h1 className="mb-4 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl sm:text-5xl">
|
||||
Áreas de Atuação
|
||||
</h1>
|
||||
<p className="mx-auto max-w-2xl text-lg text-gray-600">
|
||||
Atuação jurídica ampla e estratégica focada nos ramos mais críticos para a defesa dos seus direitos processuais e patrimoniais.
|
||||
</p>
|
||||
</FadeIn>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{areas.map((area, index) => (
|
||||
<FadeIn key={index} delay={index * 0.1}>
|
||||
<div className="group relative flex h-full flex-col justify-between border border-gray-200 bg-white p-8 transition-all hover:border-brand-gold hover:shadow-xl">
|
||||
<div>
|
||||
<div className="mb-6 inline-flex h-12 w-12 items-center justify-center bg-blue-50 text-brand-blue transition-colors group-hover:bg-brand-gold group-hover:text-white">
|
||||
<area.icon className="h-6 w-6" />
|
||||
</div>
|
||||
<h3 className="mb-3 font-serif text-xl font-bold text-brand-blue">
|
||||
{area.title}
|
||||
</h3>
|
||||
<p className="mb-8 text-gray-600 leading-relaxed text-sm">
|
||||
{area.desc}
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="https://wa.me/5511999999999"
|
||||
className="mt-auto inline-flex items-center text-sm font-semibold text-brand-gold hover:underline"
|
||||
>
|
||||
Falar sobre o caso <ArrowRight className="ml-1 h-4 w-4" />
|
||||
</a>
|
||||
</div>
|
||||
</FadeIn>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
120
Template-01/src/pages/Blog.tsx
Normal file
120
Template-01/src/pages/Blog.tsx
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { ArrowLeft, Search, Calendar, User } from 'lucide-react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { motion } from 'motion/react';
|
||||
import { MOCK_POSTSList } from '../data/posts';
|
||||
|
||||
export function Blog() {
|
||||
const [activeCategory, setActiveCategory] = useState("Todos");
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const location = useLocation();
|
||||
const searchInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (location.state?.focusSearch || location.search.includes('focus=true')) {
|
||||
setTimeout(() => {
|
||||
searchInputRef.current?.focus();
|
||||
}, 100);
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
const filteredPosts = MOCK_POSTSList.filter(post => {
|
||||
const matchesCategory = activeCategory === "Todos" || post.category === activeCategory;
|
||||
const matchesSearch = post.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
post.excerpt.toLowerCase().includes(searchQuery.toLowerCase());
|
||||
return matchesCategory && matchesSearch;
|
||||
});
|
||||
|
||||
const categories = ["Todos", ...Array.from(new Set(MOCK_POSTSList.map(p => p.category)))];
|
||||
|
||||
return (
|
||||
<main className="w-full bg-[#fcfcfc] pb-24 pt-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
|
||||
{/* Header */}
|
||||
<div className="mb-16">
|
||||
<Link to="/" className="mb-6 inline-flex items-center gap-2 text-sm text-brand-gold hover:underline">
|
||||
<ArrowLeft className="h-4 w-4" /> Voltar para o início
|
||||
</Link>
|
||||
<div className="flex flex-col gap-6 md:flex-row md:items-end md:justify-between">
|
||||
<div>
|
||||
<h1 className="mb-4 font-serif text-4xl font-bold tracking-tight text-brand-blue md:text-5xl">
|
||||
Blog Jurídico
|
||||
</h1>
|
||||
<p className="max-w-xl text-lg text-gray-600">
|
||||
Artigos desmistificando o direito de forma clara. Saiba como se proteger nas mais diversas situações do dia a dia.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Search */}
|
||||
<div className="relative w-full max-w-md">
|
||||
<input
|
||||
ref={searchInputRef}
|
||||
type="text"
|
||||
placeholder="Buscar palavra-chave..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="w-full border-b-2 border-gray-200 bg-transparent py-2 pl-2 pr-10 text-brand-blue outline-none transition-colors focus:border-brand-gold"
|
||||
/>
|
||||
<Search className="absolute right-2 top-2 h-5 w-5 text-gray-400" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Categories (Mock) */}
|
||||
<div className="mb-12 flex flex-wrap gap-3">
|
||||
{categories.map(cat => (
|
||||
<button
|
||||
key={cat}
|
||||
onClick={() => setActiveCategory(cat)}
|
||||
className={`rounded-full px-5 py-2 text-sm font-medium transition-colors ${cat === activeCategory ? 'bg-brand-blue text-white' : 'bg-white border border-gray-200 text-gray-700 hover:border-brand-gold hover:text-brand-gold'}`}
|
||||
>
|
||||
{cat}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Posts Grid */}
|
||||
<div className="grid grid-cols-1 gap-10 md:grid-cols-2 lg:grid-cols-3">
|
||||
{filteredPosts.map((post, index) => (
|
||||
<motion.article
|
||||
key={post.id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: index * 0.1 }}
|
||||
className="group flex cursor-pointer flex-col overflow-hidden bg-white shadow-sm border border-gray-100 transition-all hover:-translate-y-1 hover:shadow-xl hover:border-brand-gold/30"
|
||||
>
|
||||
<div className="aspect-[16/10] w-full overflow-hidden">
|
||||
<img
|
||||
src={post.imageUrl}
|
||||
alt={post.title}
|
||||
className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-105"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col p-6">
|
||||
<span className="mb-3 text-xs font-bold uppercase tracking-wider text-brand-gold">
|
||||
{post.category}
|
||||
</span>
|
||||
<Link to={`/blog/${post.id}`} className="mb-3 font-serif text-xl font-bold leading-snug text-brand-blue group-hover:text-brand-gold-hover">
|
||||
{post.title}
|
||||
</Link>
|
||||
<p className="mb-6 flex-1 text-sm text-gray-600 line-clamp-3">
|
||||
{post.excerpt}
|
||||
</p>
|
||||
<div className="mt-auto flex items-center justify-between border-t border-gray-100 pt-4 text-xs text-gray-500">
|
||||
<div className="flex items-center gap-2">
|
||||
<User className="h-3 w-3" /> João Silva
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Calendar className="h-3 w-3" /> {post.date}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
212
Template-01/src/pages/BlogPost.tsx
Normal file
212
Template-01/src/pages/BlogPost.tsx
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { ArrowLeft, User, Calendar, Share2, Copy, Check, MessageCircle, Linkedin, Facebook, Twitter, X } from 'lucide-react';
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import { motion, AnimatePresence } from 'motion/react';
|
||||
import { MOCK_POSTS, MOCK_POSTSList } from '../data/posts';
|
||||
|
||||
export function BlogPost() {
|
||||
const { id } = useParams();
|
||||
const post = id ? MOCK_POSTS[id] : null;
|
||||
const [showShareMenu, setShowShareMenu] = useState(false);
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo({ top: 0, behavior: 'instant' });
|
||||
}, [id]);
|
||||
|
||||
if (!post) {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-gray-50 pt-20">
|
||||
<div className="text-center">
|
||||
<h2 className="mb-4 text-2xl font-bold text-brand-blue">Artigo não encontrado</h2>
|
||||
<Link to="/blog" className="text-brand-gold hover:underline">
|
||||
Voltar para o Blog
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const url = window.location.href;
|
||||
const encodedUrl = encodeURIComponent(url);
|
||||
const encodedTitle = encodeURIComponent(post.title);
|
||||
|
||||
const copyToClipboard = () => {
|
||||
navigator.clipboard.writeText(url);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
};
|
||||
|
||||
const shareLinks = [
|
||||
{
|
||||
name: 'WhatsApp',
|
||||
icon: MessageCircle,
|
||||
href: `https://api.whatsapp.com/send?text=${encodedTitle}%20${encodedUrl}`,
|
||||
color: 'hover:text-green-500 hover:bg-green-50'
|
||||
},
|
||||
{
|
||||
name: 'LinkedIn',
|
||||
icon: Linkedin,
|
||||
href: `https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`,
|
||||
color: 'hover:text-blue-600 hover:bg-blue-50'
|
||||
},
|
||||
{
|
||||
name: 'Facebook',
|
||||
icon: Facebook,
|
||||
href: `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`,
|
||||
color: 'hover:text-blue-500 hover:bg-blue-50'
|
||||
},
|
||||
{
|
||||
name: 'X (Twitter)',
|
||||
icon: Twitter,
|
||||
href: `https://twitter.com/intent/tweet?url=${encodedUrl}&text=${encodedTitle}`,
|
||||
color: 'hover:text-gray-900 hover:bg-gray-100'
|
||||
}
|
||||
];
|
||||
|
||||
const relatedPosts = MOCK_POSTSList.filter(p => p.category === post.category && p.id !== post.id).slice(0, 3);
|
||||
if (relatedPosts.length < 3) {
|
||||
const morePosts = MOCK_POSTSList.filter(p => !relatedPosts.includes(p) && p.id !== post.id);
|
||||
relatedPosts.push(...morePosts.slice(0, 3 - relatedPosts.length));
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="w-full bg-[#fcfcfc] pb-24 pt-32">
|
||||
<article className="mx-auto max-w-4xl px-6 lg:px-8">
|
||||
|
||||
{/* ... (rest of the content is unchanged) */}
|
||||
<Link to="/blog" className="mb-10 inline-flex items-center gap-2 text-sm text-gray-500 transition-colors hover:text-brand-gold">
|
||||
<ArrowLeft className="h-4 w-4" /> Todos os artigos
|
||||
</Link>
|
||||
|
||||
{/* Header */}
|
||||
<header className="mb-12 text-center">
|
||||
<span className="mb-4 inline-block bg-brand-blue px-3 py-1 text-xs font-bold uppercase tracking-widest text-brand-gold">
|
||||
{post.category}
|
||||
</span>
|
||||
<h1 className="mb-6 font-serif text-3xl font-bold leading-tight text-brand-blue md:text-5xl">
|
||||
{post.title}
|
||||
</h1>
|
||||
<div className="flex flex-wrap items-center justify-center gap-6 border-b border-gray-200 pb-8 text-sm text-gray-500">
|
||||
<div className="flex items-center gap-2">
|
||||
<User className="h-4 w-4" /> Dr. João Silva
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Calendar className="h-4 w-4" /> {post.date}
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={copyToClipboard}
|
||||
className={`flex items-center gap-2 transition-colors ${copied ? 'text-green-600' : 'hover:text-brand-blue'}`}
|
||||
>
|
||||
{copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
|
||||
{copied ? 'Copiado!' : 'Copiar Link'}
|
||||
</button>
|
||||
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={() => setShowShareMenu(!showShareMenu)}
|
||||
className="flex items-center gap-2 hover:text-brand-blue transition-colors"
|
||||
>
|
||||
{showShareMenu ? <X className="h-4 w-4" /> : <Share2 className="h-4 w-4" />}
|
||||
Compartilhar
|
||||
</button>
|
||||
|
||||
<AnimatePresence>
|
||||
{showShareMenu && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10, scale: 0.95 }}
|
||||
animate={{ opacity: 1, y: 0, scale: 1 }}
|
||||
exit={{ opacity: 0, y: 10, scale: 0.95 }}
|
||||
transition={{ duration: 0.15 }}
|
||||
className="absolute right-1/2 top-full mt-2 w-48 translate-x-1/2 sm:translate-x-0 sm:right-0 sm:left-auto rounded-lg border border-gray-100 bg-white p-2 shadow-xl z-10"
|
||||
>
|
||||
<div className="flex flex-col text-left">
|
||||
{shareLinks.map((link) => (
|
||||
<a
|
||||
key={link.name}
|
||||
href={link.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={`flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium text-gray-700 transition-colors ${link.color}`}
|
||||
onClick={() => setShowShareMenu(false)}
|
||||
>
|
||||
<link.icon className="h-4 w-4" /> {link.name}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Featured Image */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.98 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
className="mb-14 overflow-hidden rounded-sm"
|
||||
>
|
||||
<img
|
||||
src={post.imageUrl}
|
||||
alt={post.title}
|
||||
className="aspect-[2/1] w-full object-cover"
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
{/* Article Body */}
|
||||
<div className="mx-auto max-w-3xl prose prose-blue prose-lg prose-headings:font-serif prose-headings:text-brand-blue prose-a:text-brand-gold prose-a:no-underline hover:prose-a:underline">
|
||||
{/* Render HTML content safely since it's hardcoded mock, but natively prefer Markdown if CMS */}
|
||||
<div dangerouslySetInnerHTML={{ __html: post.content }} />
|
||||
</div>
|
||||
|
||||
{/* CTA Section */}
|
||||
<div className="mx-auto mt-20 max-w-3xl bg-brand-blue p-8 text-center sm:p-12 mb-20">
|
||||
<h3 className="mb-4 font-serif text-2xl font-bold text-white">Este conteúdo ajudou em sua situação?</h3>
|
||||
<p className="mb-8 text-gray-300">Cada caso demanda uma análise específica. Caso esteja passando por esta situação, avaliarei o seu cenário de forma técnica e sigilosa.</p>
|
||||
<Link to="/contato" className="inline-block bg-brand-gold px-8 py-3 font-semibold text-white transition-colors hover:bg-white hover:text-brand-blue">
|
||||
Solicitar Análise de Caso
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Artigos Relacionados */}
|
||||
{relatedPosts.length > 0 && (
|
||||
<div className="mx-auto max-w-5xl border-t border-gray-100 pt-16">
|
||||
<h3 className="mb-8 font-serif text-2xl font-bold text-brand-blue text-center">
|
||||
Você também pode se interessar por:
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 gap-8 md:grid-cols-3">
|
||||
{relatedPosts.map((relatedPost, index) => (
|
||||
<motion.article
|
||||
key={relatedPost.id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: index * 0.1 }}
|
||||
className="group flex flex-col overflow-hidden bg-white shadow-sm border border-gray-100 transition-all hover:-translate-y-1 hover:shadow-xl hover:border-brand-gold/30"
|
||||
>
|
||||
<Link to={`/blog/${relatedPost.id}`} className="aspect-[16/10] w-full overflow-hidden block">
|
||||
<img
|
||||
src={relatedPost.imageUrl}
|
||||
alt={relatedPost.title}
|
||||
className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-105"
|
||||
/>
|
||||
</Link>
|
||||
<div className="flex flex-1 flex-col p-6">
|
||||
<span className="mb-3 text-xs font-bold uppercase tracking-wider text-brand-gold">
|
||||
{relatedPost.category}
|
||||
</span>
|
||||
<Link to={`/blog/${relatedPost.id}`} className="font-serif text-lg font-bold leading-snug text-brand-blue group-hover:text-brand-gold-hover">
|
||||
{relatedPost.title}
|
||||
</Link>
|
||||
</div>
|
||||
</motion.article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</article>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
96
Template-01/src/pages/ComoFunciona.tsx
Normal file
96
Template-01/src/pages/ComoFunciona.tsx
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
import React from 'react';
|
||||
import { MessageCircle } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
const FadeIn = ({ children, delay = 0 }: { children: React.ReactNode, delay?: number }) => (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
transition={{ duration: 0.6, delay }}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
export function ComoFunciona() {
|
||||
const steps = [
|
||||
{ num: "01", title: "Envie sua Dúvida", desc: "Entre em contato via WhatsApp e explique brevemente o que aconteceu. Não há barreiras burocráticas no primeiro contato." },
|
||||
{ num: "02", title: "Avaliação Inicial", desc: "Faço uma análise rápida e honesta da viabilidade jurídica do seu caso, informando as chances de êxito." },
|
||||
{ num: "03", title: "Orientação e Estratégia", desc: "Apresento a melhor estratégia, os custos de forma totalmente transparente e enviamos o termo de prestação de serviços." },
|
||||
{ num: "04", title: "Acompanhamento", desc: "Inicio as medidas necessárias, mantendo você atualizado ativamente em cada etapa e movimentação chave do processo." }
|
||||
];
|
||||
|
||||
const points = [
|
||||
{ title: "Atendimento Direto", desc: "Você fala diretamente com o advogado responsável pelo caso, sem secretárias intermediando." },
|
||||
{ title: "Linguagem Clara", desc: "Sem 'juridiquês' complicado. Explico sua situação de forma simples que qualquer um entende." },
|
||||
{ title: "Transparência Total", desc: "Honorários acordados no início. Sem surpresas ou cobranças ocultas no meio do processo." },
|
||||
{ title: "Agilidade Online", desc: "Tenha todo o suporte por WhatsApp ou videoconferência, sem precisar sair de casa." }
|
||||
];
|
||||
|
||||
return (
|
||||
<main className="w-full bg-white pb-24 pt-32">
|
||||
<section className="bg-brand-blue py-24 sm:py-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<FadeIn>
|
||||
<h1 className="mb-6 text-center font-serif text-3xl font-bold tracking-tight text-white sm:text-4xl md:text-5xl">
|
||||
Como funciona o acompanhamento
|
||||
</h1>
|
||||
<p className="mx-auto mb-16 max-w-2xl text-center text-lg text-gray-300">
|
||||
Minha prioridade é oferecer um método de atendimento moderno, simplificado e altamente eficiente.
|
||||
</p>
|
||||
</FadeIn>
|
||||
|
||||
<div className="grid grid-cols-1 gap-12 md:grid-cols-2 lg:grid-cols-4 relative">
|
||||
<div className="absolute top-1/2 left-0 right-0 h-[1px] bg-white/20 hidden lg:block -translate-y-[15px] z-0"></div>
|
||||
|
||||
{steps.map((step, index) => (
|
||||
<FadeIn key={index} delay={index * 0.1}>
|
||||
<div className="relative z-10 flex flex-col items-center text-center">
|
||||
<div className="mb-6 flex h-20 w-20 items-center justify-center rounded-full border-4 border-brand-blue bg-brand-gold text-2xl font-serif font-bold text-white shadow-xl">
|
||||
{step.num}
|
||||
</div>
|
||||
<h3 className="mb-3 font-serif text-xl font-bold text-white">{step.title}</h3>
|
||||
<p className="text-gray-300 text-sm leading-relaxed">{step.desc}</p>
|
||||
</div>
|
||||
</FadeIn>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="py-24 sm:py-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 gap-16 lg:grid-cols-2 lg:items-center">
|
||||
<FadeIn>
|
||||
<h2 className="mb-6 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
|
||||
Por que me escolher para defender você?
|
||||
</h2>
|
||||
<p className="mb-10 text-lg text-gray-600 text-balance">
|
||||
Acredito que a advocacia moderna não precisa ser lenta, cheia de protocolos e distante do cliente. Meu modelo de atuação é focado no resultado e na tranquilidade do cliente.
|
||||
</p>
|
||||
<Link
|
||||
to="/contato"
|
||||
className="inline-flex items-center justify-center gap-2 bg-brand-blue px-6 py-3 font-semibold text-white transition hover:bg-brand-blue-light"
|
||||
>
|
||||
<MessageCircle className="h-5 w-5" /> Agendar Consulta
|
||||
</Link>
|
||||
</FadeIn>
|
||||
|
||||
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2">
|
||||
{points.map((point, i) => (
|
||||
<FadeIn key={i} delay={i * 0.1}>
|
||||
<div className="border-l-4 border-brand-gold pl-6">
|
||||
<h3 className="mb-2 font-serif text-lg font-bold text-brand-blue">{point.title}</h3>
|
||||
<p className="text-gray-600 leading-relaxed text-sm">{point.desc}</p>
|
||||
</div>
|
||||
</FadeIn>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
216
Template-01/src/pages/Contact.tsx
Normal file
216
Template-01/src/pages/Contact.tsx
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { motion } from 'motion/react';
|
||||
import { MapPin, Phone, Mail, Clock, Send, CheckCircle2 } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export function Contact() {
|
||||
const [formState, setFormState] = useState<'idle' | 'submitting' | 'success'>('idle');
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setFormState('submitting');
|
||||
// Simulate form submission
|
||||
setTimeout(() => {
|
||||
setFormState('success');
|
||||
}, 1500);
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="w-full bg-[#fcfcfc] pb-24 pt-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
|
||||
{/* Header */}
|
||||
<div className="mb-16 text-center">
|
||||
<h1 className="mb-4 font-serif text-4xl font-bold tracking-tight text-brand-blue md:text-5xl">
|
||||
Entre em Contato
|
||||
</h1>
|
||||
<p className="mx-auto max-w-2xl text-lg text-gray-600">
|
||||
Estamos prontos para ouvir você e encontrar a melhor solução jurídica para o seu caso. Escolha a forma de contato de sua preferência.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-16 lg:grid-cols-2">
|
||||
{/* Informações de Contato */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
className="flex flex-col justify-between"
|
||||
>
|
||||
<div>
|
||||
<h2 className="mb-8 font-serif text-2xl font-bold text-brand-blue">Informações de Atendimento</h2>
|
||||
|
||||
<div className="mb-12 space-y-8">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex h-12 w-12 shrink-0 items-center justify-center bg-brand-blue text-brand-gold">
|
||||
<Phone className="h-6 w-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="mb-1 font-serif text-lg font-bold text-brand-blue">WhatsApp & Telefone</h3>
|
||||
<p className="text-gray-600">+55 (11) 99999-9999</p>
|
||||
<p className="text-gray-600">Atendimento prioritário via WhatsApp</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex h-12 w-12 shrink-0 items-center justify-center bg-brand-blue text-brand-gold">
|
||||
<Mail className="h-6 w-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="mb-1 font-serif text-lg font-bold text-brand-blue">E-mail</h3>
|
||||
<p className="text-gray-600">contato@joaosilva.adv.br</p>
|
||||
<p className="text-gray-600">Para envio de documentos e propostas</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex h-12 w-12 shrink-0 items-center justify-center bg-brand-blue text-brand-gold">
|
||||
<MapPin className="h-6 w-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="mb-1 font-serif text-lg font-bold text-brand-blue">Escritório Físico</h3>
|
||||
<p className="text-gray-600">Av. Paulista, 1000 - Bela Vista</p>
|
||||
<p className="text-gray-600">São Paulo - SP, 01310-100</p>
|
||||
<p className="mt-1 text-sm text-brand-gold">Atendimento presencial apenas com agendamento.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex h-12 w-12 shrink-0 items-center justify-center bg-brand-blue text-brand-gold">
|
||||
<Clock className="h-6 w-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="mb-1 font-serif text-lg font-bold text-brand-blue">Horário de Funcionamento</h3>
|
||||
<p className="text-gray-600">Segunda a Sexta: 09h às 18h</p>
|
||||
<p className="text-gray-600">Plantão para urgências criminais 24h</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Map (Placeholder using generic image or iframe) */}
|
||||
<div className="aspect-[16/9] w-full overflow-hidden bg-gray-100 filter grayscale">
|
||||
<iframe
|
||||
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3657.1973618684784!2d-46.658097023769186!3d-23.56134956158784!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x94ce59c8da0aa315%3A0xd59f9431f2c9776a!2sAv.%20Paulista%2C%201000%20-%20Bela%20Vista%2C%20S%C3%A3o%20Paulo%20-%20SP%2C%2001310-100!5e0!3m2!1spt-BR!2sbr!4v1714571987515!5m2!1spt-BR!2sbr"
|
||||
width="100%"
|
||||
height="100%"
|
||||
style={{ border: 0 }}
|
||||
allowFullScreen={false}
|
||||
loading="lazy"
|
||||
referrerPolicy="no-referrer-when-downgrade">
|
||||
</iframe>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Formulário de Contato */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
className="flex flex-col justify-center bg-white p-8 shadow-xl md:p-12 border border-gray-100"
|
||||
>
|
||||
<h2 className="mb-8 font-serif text-2xl font-bold text-brand-blue">Envie sua Mensagem</h2>
|
||||
|
||||
{formState === 'success' ? (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
className="flex flex-col items-center justify-center p-8 text-center"
|
||||
>
|
||||
<CheckCircle2 className="mb-4 h-16 w-16 text-green-500" />
|
||||
<h3 className="mb-2 font-serif text-xl font-bold text-brand-blue">Mensagem Enviada!</h3>
|
||||
<p className="mb-8 text-gray-600">Recebemos seu contato. Retornaremos o mais breve possível para o e-mail ou telefone informado.</p>
|
||||
<button
|
||||
onClick={() => setFormState('idle')}
|
||||
className="bg-brand-blue px-6 py-3 font-semibold text-white transition-colors hover:bg-brand-gold"
|
||||
>
|
||||
Enviar Nova Mensagem
|
||||
</button>
|
||||
</motion.div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} className="flex flex-col gap-6">
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="name" className="text-sm font-semibold text-brand-blue uppercase tracking-wider">Seu Nome Completo</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
required
|
||||
className="border-b-2 border-gray-200 bg-transparent py-2 outline-none transition-colors focus:border-brand-gold"
|
||||
placeholder="Ex: Carlos Silva"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-6 md:flex-row">
|
||||
<div className="flex w-full flex-col gap-2">
|
||||
<label htmlFor="email" className="text-sm font-semibold text-brand-blue uppercase tracking-wider">E-mail</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
required
|
||||
className="border-b-2 border-gray-200 bg-transparent py-2 outline-none transition-colors focus:border-brand-gold"
|
||||
placeholder="seu@email.com"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex w-full flex-col gap-2">
|
||||
<label htmlFor="phone" className="text-sm font-semibold text-brand-blue uppercase tracking-wider">Telefone / WhatsApp</label>
|
||||
<input
|
||||
type="tel"
|
||||
id="phone"
|
||||
required
|
||||
className="border-b-2 border-gray-200 bg-transparent py-2 outline-none transition-colors focus:border-brand-gold"
|
||||
placeholder="(11) 99999-9999"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="area" className="text-sm font-semibold text-brand-blue uppercase tracking-wider">Área de Interesse (Opcional)</label>
|
||||
<select
|
||||
id="area"
|
||||
className="border-b-2 border-gray-200 bg-transparent py-2 outline-none transition-colors focus:border-brand-gold text-gray-600"
|
||||
>
|
||||
<option value="">Selecione a área...</option>
|
||||
<option value="trabalhista">Direito Trabalhista</option>
|
||||
<option value="previdenciario">Direito Previdenciário</option>
|
||||
<option value="civil">Direito Civil</option>
|
||||
<option value="familia">Direito de Família</option>
|
||||
<option value="consumidor">Direito do Consumidor</option>
|
||||
<option value="outros">Outros</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="message" className="text-sm font-semibold text-brand-blue uppercase tracking-wider">Resumo do Caso</label>
|
||||
<textarea
|
||||
id="message"
|
||||
rows={4}
|
||||
required
|
||||
className="resize-none border-b-2 border-gray-200 bg-transparent py-2 outline-none transition-colors focus:border-brand-gold"
|
||||
placeholder="Descreva brevemente o que aconteceu..."
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={formState === 'submitting'}
|
||||
className="mt-4 flex items-center justify-center gap-2 bg-brand-gold px-8 py-4 font-semibold text-white transition-all hover:bg-brand-blue disabled:opacity-70 disabled:hover:bg-brand-gold"
|
||||
>
|
||||
{formState === 'submitting' ? 'Enviando...' : (
|
||||
<>
|
||||
Enviar Mensagem <Send className="h-4 w-4" />
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
<p className="text-xs text-gray-500 text-center">
|
||||
Garantimos sigilo absoluto sobre todas as informações prestadas neste formulário.
|
||||
</p>
|
||||
</form>
|
||||
)}
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
471
Template-01/src/pages/Home.tsx
Normal file
471
Template-01/src/pages/Home.tsx
Normal file
|
|
@ -0,0 +1,471 @@
|
|||
import React, { useState } from 'react';
|
||||
import { motion, AnimatePresence } from 'motion/react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import {
|
||||
ArrowRight, ShieldCheck, Scale, FileText,
|
||||
Users, Building, Home as HomeIcon, CheckCircle2,
|
||||
MessageCircle, Plus, Minus
|
||||
} from 'lucide-react';
|
||||
import { cn } from '../lib/utils';
|
||||
|
||||
// Reusable animated container
|
||||
const FadeIn = ({ children, delay = 0 }: { children: React.ReactNode, delay?: number }) => (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
transition={{ duration: 0.6, delay }}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
export function Home() {
|
||||
return (
|
||||
<main className="w-full">
|
||||
<HeroSection />
|
||||
<AboutSection />
|
||||
<PracticeAreasSection />
|
||||
<HowItWorksSection />
|
||||
<DifferentiatorsSection />
|
||||
<TestimonialsSection />
|
||||
<FAQSection />
|
||||
<FinalCTASection />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
function HeroSection() {
|
||||
return (
|
||||
<section className="relative flex min-h-[90vh] items-center bg-brand-blue pt-20">
|
||||
{/* Background Pattern / Overlay */}
|
||||
<div className="absolute inset-0 z-0 opacity-10 bg-[url('https://images.unsplash.com/photo-1589829085413-56de8ae18c73?q=80&w=2000&auto=format&fit=crop')] bg-cover bg-center mix-blend-overlay"></div>
|
||||
|
||||
<div className="relative z-10 mx-auto grid w-full max-w-7xl grid-cols-1 gap-12 px-6 lg:grid-cols-2 lg:px-8">
|
||||
<motion.div
|
||||
className="flex flex-col justify-center pt-10 pb-16 lg:py-24"
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
>
|
||||
<div className="mb-6 flex items-center gap-4">
|
||||
<span className="h-[1px] w-12 bg-brand-gold"></span>
|
||||
<span className="text-sm font-semibold tracking-widest text-brand-gold uppercase">
|
||||
OAB/SP 123.456
|
||||
</span>
|
||||
</div>
|
||||
<h1 className="mb-6 font-serif text-4xl leading-tight font-bold text-white md:text-5xl lg:text-6xl">
|
||||
Advocacia estratégica e <span className="text-brand-gold">atendimento direto</span> para proteger seus direitos.
|
||||
</h1>
|
||||
<p className="mb-10 max-w-lg text-lg text-gray-300 leading-relaxed">
|
||||
Atendimento humanizado, sem intermediários. Foco em soluções ágeis e máxima segurança jurídica para você e seu patrimônio.
|
||||
</p>
|
||||
<div className="flex flex-col gap-4 sm:flex-row">
|
||||
<a
|
||||
href="https://wa.me/5511999999999"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="flex items-center justify-center gap-2 bg-brand-gold px-8 py-4 text-center font-semibold text-white transition-all hover:bg-brand-gold-hover hover:-translate-y-1"
|
||||
>
|
||||
Falar com o Advogado <ArrowRight className="h-5 w-5" />
|
||||
</a>
|
||||
<Link
|
||||
to="/areas"
|
||||
className="flex items-center justify-center gap-2 border border-white/30 px-8 py-4 text-center font-semibold text-white backdrop-blur-sm transition-all hover:bg-white hover:text-brand-blue"
|
||||
>
|
||||
Conheça as Áreas
|
||||
</Link>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
className="relative hidden items-end justify-center lg:flex"
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.8, delay: 0.2 }}
|
||||
>
|
||||
{/* Lawyer Photo - High quality professional portrait */}
|
||||
<div className="relative h-[85%] w-full max-w-md">
|
||||
<div className="absolute -inset-4 border border-brand-gold/30 translate-x-4 -translate-y-4"></div>
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1556157382-97eda2d62296?auto=format&fit=crop&q=80&w=800"
|
||||
alt="Dr. João Silva"
|
||||
className="relative h-full w-full object-cover object-top shadow-2xl grayscale-[20%] contrast-125"
|
||||
/>
|
||||
{/* Experience Badge */}
|
||||
<div className="absolute -left-12 bottom-12 flex items-center gap-4 bg-white p-4 shadow-xl">
|
||||
<div className="flex h-12 w-12 items-center justify-center bg-brand-blue text-brand-gold font-serif font-bold text-xl">
|
||||
15
|
||||
</div>
|
||||
<p className="font-serif text-sm font-bold leading-tight text-brand-blue uppercase">
|
||||
Anos de<br/>Experiência
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function AboutSection() {
|
||||
return (
|
||||
<section id="sobre" className="overflow-hidden bg-white py-24 sm:py-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 items-center gap-y-16 gap-x-16 lg:grid-cols-2">
|
||||
<FadeIn>
|
||||
<div className="relative">
|
||||
<div className="aspect-[3/4] w-full overflow-hidden bg-gray-100 lg:max-w-md">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1560250097-0b93528c311a?auto=format&fit=crop&q=80&w=800"
|
||||
alt="Advogado João Silva em seu escritório"
|
||||
className="h-full w-full object-cover grayscale-[10%]"
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute -right-4 -bottom-4 h-32 w-32 bg-brand-gold opacity-20 -z-10"></div>
|
||||
</div>
|
||||
</FadeIn>
|
||||
|
||||
<FadeIn delay={0.2}>
|
||||
<div className="max-w-xl">
|
||||
<h2 className="mb-4 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
|
||||
Dr. João Silva
|
||||
</h2>
|
||||
<p className="mb-6 text-sm font-medium tracking-widest text-brand-gold uppercase">
|
||||
Especialista & Fundador
|
||||
</p>
|
||||
<div className="mb-8 space-y-4 text-lg text-gray-600">
|
||||
<p>
|
||||
Com mais de uma década de dedicação exclusiva à advocacia, meu objetivo sempre foi o mesmo: entregar resultados concretos com um atendimento pautado pela transparência e proximidade.
|
||||
</p>
|
||||
<p>
|
||||
Não sou apenas um intermediário em processos. Sou um parceiro estratégico focado em defender seus interesses. Diferente dos grandes escritórios onde você é apenas um número, aqui o seu caso é tratado diretamente por mim, do início ao fim.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ul className="mb-10 space-y-4">
|
||||
{[
|
||||
"Formação em Direito pela Universidade de São Paulo (USP)",
|
||||
"Pós-graduado em Direito Civil e Processo Civil",
|
||||
"Atuação ética focada na resolução rápida de conflitos",
|
||||
"Atendimento 100% humanizado e sem burocracias"
|
||||
].map((item, i) => (
|
||||
<li key={i} className="flex items-start gap-3 text-gray-700">
|
||||
<CheckCircle2 className="mt-1 h-5 w-5 shrink-0 text-brand-gold" />
|
||||
<span>{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</FadeIn>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function PracticeAreasSection() {
|
||||
const areas = [
|
||||
{
|
||||
title: "Direito Trabalhista",
|
||||
icon: Users,
|
||||
desc: "Defesa dos seus direitos no ambiente de trabalho. Rescisões, horas extras, assédio moral e doenças ocupacionais."
|
||||
},
|
||||
{
|
||||
title: "Direito Previdenciário",
|
||||
icon: ShieldCheck,
|
||||
desc: "Garantia do seu benefício justo. Aposentadorias, BPC/LOAS, pensão por morte e auxílio-doença."
|
||||
},
|
||||
{
|
||||
title: "Direito Civil",
|
||||
icon: Scale,
|
||||
desc: "Proteção do seu patrimônio e interesses. Contratos, indenizações, responsabilidade civil e cobranças."
|
||||
},
|
||||
{
|
||||
title: "Direito de Família",
|
||||
icon: Users,
|
||||
desc: "Soluções com sensibilidade. Divórcios, pensão alimentícia, guarda, inventários e partilha de bens."
|
||||
},
|
||||
{
|
||||
title: "Direito do Consumidor",
|
||||
icon: FileText,
|
||||
desc: "Contra abusos de empresas. Negativações indevidas, problemas com planos de saúde e voos, produtos com defeito."
|
||||
},
|
||||
{
|
||||
title: "Direito Imobiliário",
|
||||
icon: HomeIcon,
|
||||
desc: "Segurança em transações. Regularização de imóveis, usucapião, contratos de aluguel e distratos."
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section id="areas" className="bg-[#f9fafb] py-24 sm:py-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div className="mb-16 text-center">
|
||||
<FadeIn>
|
||||
<h2 className="mb-4 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
|
||||
Áreas de Especialidade
|
||||
</h2>
|
||||
<p className="mx-auto max-w-2xl text-lg text-gray-600">
|
||||
Atuação jurídica ampla e estratégica focada nos ramos mais críticos para a defesa dos seus direitos processuais e patrimoniais.
|
||||
</p>
|
||||
</FadeIn>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{areas.map((area, index) => (
|
||||
<FadeIn key={index} delay={index * 0.1}>
|
||||
<div className="group relative flex h-full flex-col justify-between border border-gray-200 bg-white p-8 transition-all hover:border-brand-gold hover:shadow-xl">
|
||||
<div>
|
||||
<div className="mb-6 inline-flex h-12 w-12 items-center justify-center bg-blue-50 text-brand-blue transition-colors group-hover:bg-brand-gold group-hover:text-white">
|
||||
<area.icon className="h-6 w-6" />
|
||||
</div>
|
||||
<h3 className="mb-3 font-serif text-xl font-bold text-brand-blue">
|
||||
{area.title}
|
||||
</h3>
|
||||
<p className="mb-8 text-gray-600">
|
||||
{area.desc}
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="https://wa.me/5511999999999"
|
||||
className="mt-auto inline-flex items-center text-sm font-semibold text-brand-gold hover:underline"
|
||||
>
|
||||
Falar sobre o caso <ArrowRight className="ml-1 h-4 w-4" />
|
||||
</a>
|
||||
</div>
|
||||
</FadeIn>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function HowItWorksSection() {
|
||||
const steps = [
|
||||
{ num: "01", title: "Envie sua Dúvida", desc: "Entre em contato via WhatsApp e explique brevemente o que aconteceu." },
|
||||
{ num: "02", title: "Avaliação Inicial", desc: "Faço uma análise rápida e honesta da viabilidade jurídica do seu caso." },
|
||||
{ num: "03", title: "Orientação e Estratégia", desc: "Apresento a melhor estratégia, custos transparentes e fechamos o acordo." },
|
||||
{ num: "04", title: "Acompanhamento", desc: "Inicio as medidas necessárias, mantendo você atualizado em cada etapa." }
|
||||
];
|
||||
|
||||
return (
|
||||
<section id="funcionamento" className="bg-brand-blue py-24 sm:py-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<FadeIn>
|
||||
<h2 className="mb-16 text-center font-serif text-3xl font-bold tracking-tight text-white sm:text-4xl">
|
||||
Como funciona meu atendimento
|
||||
</h2>
|
||||
</FadeIn>
|
||||
|
||||
<div className="grid grid-cols-1 gap-12 md:grid-cols-2 lg:grid-cols-4 relative">
|
||||
<div className="absolute top-1/2 left-0 right-0 h-[1px] bg-white/20 hidden lg:block -translate-y-[45px] z-0"></div>
|
||||
|
||||
{steps.map((step, index) => (
|
||||
<FadeIn key={index} delay={index * 0.1}>
|
||||
<div className="relative z-10 flex flex-col items-center text-center">
|
||||
<div className="mb-6 flex h-20 w-20 items-center justify-center rounded-full border-4 border-brand-blue bg-brand-gold text-2xl font-serif font-bold text-white shadow-xl">
|
||||
{step.num}
|
||||
</div>
|
||||
<h3 className="mb-3 font-serif text-xl font-bold text-white">{step.title}</h3>
|
||||
<p className="text-gray-300">{step.desc}</p>
|
||||
</div>
|
||||
</FadeIn>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function DifferentiatorsSection() {
|
||||
const points = [
|
||||
{ title: "Atendimento Direto", desc: "Você fala diretamente com o advogado responsável pelo caso, sem secretárias intermediando." },
|
||||
{ title: "Linguagem Clara", desc: "Sem 'juridiquês' complicado. Explico sua situação de forma simples que qualquer um entende." },
|
||||
{ title: "Transparência Total", desc: "Honorários acordados no início. Sem surpresas ou cobranças ocultas no meio do processo." },
|
||||
{ title: "Agilidade Online", desc: "Tenha todo o suporte por WhatsApp ou videoconferência, sem precisar sair de casa." }
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="bg-white py-24 sm:py-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 gap-16 lg:grid-cols-2 lg:items-center">
|
||||
<FadeIn>
|
||||
<h2 className="mb-6 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
|
||||
Por que me escolher para defender você?
|
||||
</h2>
|
||||
<p className="mb-10 text-lg text-gray-600 text-balance">
|
||||
Advocacia não precisa ser lenta e distante. Meu modelo de atuação é focado no resultado e no cliente.
|
||||
</p>
|
||||
<Link
|
||||
to="/contato"
|
||||
className="inline-flex items-center justify-center gap-2 bg-brand-blue px-6 py-3 font-semibold text-white transition hover:bg-brand-blue-light"
|
||||
>
|
||||
<MessageCircle className="h-5 w-5" /> Agendar Consulta
|
||||
</Link>
|
||||
</FadeIn>
|
||||
|
||||
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2">
|
||||
{points.map((point, i) => (
|
||||
<FadeIn key={i} delay={i * 0.1}>
|
||||
<div className="border-l-4 border-brand-gold pl-6">
|
||||
<h3 className="mb-2 font-serif text-lg font-bold text-brand-blue">{point.title}</h3>
|
||||
<p className="text-gray-600 leading-relaxed text-sm">{point.desc}</p>
|
||||
</div>
|
||||
</FadeIn>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function TestimonialsSection() {
|
||||
const testimonials = [
|
||||
{ quote: "O Dr. João foi excepcional. Resolveu meu problema trabalhista muito mais rápido do que eu esperava. Ele me manteve informada pelo WhatsApp durante todo o processo.", author: "Maria C.", role: "Cliente" },
|
||||
{ quote: "Senti muita confiança desde a primeira reunião. Ele explicou tudo sem usar aquele monte de palavras difíceis e foi muito transparente sobre os custos. Recomendo muito.", author: "Carlos R.", role: "Cliente" },
|
||||
{ quote: "Meu processo de aposentadoria estava travado, e em pouco tempo após ele assumir conseguimos destravar. Agradeço pela paciência e excelente trabalho.", author: "Antônio P.", role: "Cliente" }
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="bg-[#fdfbf6] py-24 sm:py-32 border-y border-amber-900/5">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<FadeIn>
|
||||
<h2 className="mb-16 text-center font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
|
||||
A opinião de quem já confia
|
||||
</h2>
|
||||
</FadeIn>
|
||||
|
||||
<div className="grid grid-cols-1 gap-8 md:grid-cols-3">
|
||||
{testimonials.map((item, i) => (
|
||||
<FadeIn key={i} delay={i * 0.1}>
|
||||
<div className="flex h-full flex-col justify-between bg-white p-8 shadow-sm border border-gray-100">
|
||||
<div>
|
||||
<div className="mb-6 text-brand-gold">
|
||||
{"★".repeat(5)}
|
||||
</div>
|
||||
<p className="mb-8 text-gray-700 font-medium italic">"{item.quote}"</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 border-t border-gray-100 pt-4">
|
||||
<div className="h-10 w-10 shrink-0 bg-gray-200 rounded-full"></div>
|
||||
<div>
|
||||
<h4 className="font-serif font-bold text-brand-blue">{item.author}</h4>
|
||||
<p className="text-xs text-gray-500 uppercase tracking-widest">{item.role}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FadeIn>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function FAQSection() {
|
||||
const faqs = [
|
||||
{
|
||||
q: "Preciso ir até o escritório para ser atendido?",
|
||||
a: "Não é necessário. Todo o atendimento pode ser realizado de forma 100% online via WhatsApp ou videoconferência. Você assina e envia os documentos pelo celular com validade jurídica."
|
||||
},
|
||||
{
|
||||
q: "A primeira consulta é cobrada?",
|
||||
a: "Avaliamos rapidamente o seu caso sem compromisso. Caso seja necessária uma análise documental profunda ou parecer jurídico detalhado, a consulta formal poderá ser cobrada, mas tudo é informado previamente."
|
||||
},
|
||||
{
|
||||
q: "Quanto custa um processo?",
|
||||
a: "Cada caso tem uma complexidade diferente. Meus honorários seguem a tabela ética da OAB e são acordados de forma clara antes de qualquer assinatura de contrato. Em muitos casos (trabalhista/previdenciário), o pagamento ocorre apenas no sucesso da ação."
|
||||
},
|
||||
{
|
||||
q: "Como saberei o andamento do meu processo?",
|
||||
a: "Você receberá atualizações periódicas por WhatsApp. Além disso, sempre que o juiz der uma movimentação importante, entrarei em contato para avisar."
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="bg-white py-24 sm:py-32">
|
||||
<div className="mx-auto max-w-3xl px-6 lg:px-8">
|
||||
<FadeIn>
|
||||
<h2 className="mb-12 text-center font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
|
||||
Perguntas Frequentes
|
||||
</h2>
|
||||
</FadeIn>
|
||||
|
||||
<div className="space-y-4">
|
||||
{faqs.map((faq, i) => (
|
||||
<FAQItem key={i} question={faq.q} answer={faq.a} delay={i * 0.1}/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function FAQItem({ question, answer, delay }: { question: string, answer: string, delay: number }) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<FadeIn delay={delay}>
|
||||
<div className="border border-gray-200 bg-white">
|
||||
<button
|
||||
className="flex w-full items-center justify-between px-6 py-4 text-left font-serif text-lg font-semibold text-brand-blue transition-colors hover:bg-gray-50"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
{question}
|
||||
<span className="ml-6 flex shrink-0 text-brand-gold">
|
||||
{isOpen ? <Minus className="h-5 w-5" /> : <Plus className="h-5 w-5" />}
|
||||
</span>
|
||||
</button>
|
||||
<AnimatePresence>
|
||||
{isOpen && (
|
||||
<motion.div
|
||||
initial={{ height: 0, opacity: 0 }}
|
||||
animate={{ height: 'auto', opacity: 1 }}
|
||||
exit={{ height: 0, opacity: 0 }}
|
||||
className="overflow-hidden"
|
||||
>
|
||||
<div className="px-6 pb-4 pt-2 text-gray-600">
|
||||
{answer}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</FadeIn>
|
||||
);
|
||||
}
|
||||
|
||||
function FinalCTASection() {
|
||||
return (
|
||||
<section className="relative overflow-hidden bg-brand-blue py-24 sm:py-32">
|
||||
<div className="absolute inset-0 z-0 opacity-10 bg-[url('https://images.unsplash.com/photo-1589829085413-56de8ae18c73?q=80&w=2000&auto=format&fit=crop')] bg-cover bg-center mix-blend-overlay"></div>
|
||||
<div className="relative z-10 mx-auto max-w-4xl px-6 text-center lg:px-8">
|
||||
<FadeIn>
|
||||
<Scale className="mx-auto mb-6 h-12 w-12 text-brand-gold" />
|
||||
<h2 className="mb-6 font-serif text-3xl font-bold tracking-tight text-white sm:text-5xl">
|
||||
Precisa de orientação jurídica?
|
||||
</h2>
|
||||
<p className="mx-auto mb-10 max-w-2xl text-lg text-gray-300">
|
||||
Não deixe para depois a proteção dos seus direitos e do seu patrimônio. Entre em contato agora e vamos encontrar a melhor solução para o seu caso.
|
||||
</p>
|
||||
<div className="flex flex-col gap-4 justify-center sm:flex-row">
|
||||
<a
|
||||
href="https://wa.me/5511999999999"
|
||||
className="inline-flex items-center justify-center gap-2 bg-brand-gold px-8 py-4 font-semibold text-white transition-colors hover:bg-white hover:text-brand-blue"
|
||||
>
|
||||
Falar pelo WhatsApp
|
||||
</a>
|
||||
<Link
|
||||
to="/contato"
|
||||
className="inline-flex items-center justify-center gap-2 border border-white/50 px-8 py-4 font-semibold text-white transition-colors hover:bg-white/10"
|
||||
>
|
||||
Enviar e-mail
|
||||
</Link>
|
||||
</div>
|
||||
</FadeIn>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
75
Template-01/src/pages/Sobre.tsx
Normal file
75
Template-01/src/pages/Sobre.tsx
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import React from 'react';
|
||||
import { CheckCircle2 } from 'lucide-react';
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
const FadeIn = ({ children, delay = 0 }: { children: React.ReactNode, delay?: number }) => (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
transition={{ duration: 0.6, delay }}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
export function Sobre() {
|
||||
return (
|
||||
<main className="w-full bg-white pb-24 pt-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 items-center gap-y-16 gap-x-16 lg:grid-cols-2">
|
||||
<FadeIn>
|
||||
<div className="relative">
|
||||
<div className="aspect-[3/4] w-full overflow-hidden bg-gray-100 lg:max-w-md">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1560250097-0b93528c311a?auto=format&fit=crop&q=80&w=800"
|
||||
alt="Advogado João Silva em seu escritório"
|
||||
className="h-full w-full object-cover grayscale-[10%]"
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute -right-4 -bottom-4 h-32 w-32 bg-brand-gold opacity-20 -z-10"></div>
|
||||
</div>
|
||||
</FadeIn>
|
||||
|
||||
<FadeIn delay={0.2}>
|
||||
<div className="max-w-xl">
|
||||
<h1 className="mb-4 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
|
||||
Dr. João Silva
|
||||
</h1>
|
||||
<p className="mb-6 text-sm font-medium tracking-widest text-brand-gold uppercase">
|
||||
Especialista & Fundador
|
||||
</p>
|
||||
<div className="mb-8 space-y-4 text-lg text-gray-600">
|
||||
<p>
|
||||
Com mais de uma década de dedicação exclusiva à advocacia, meu objetivo sempre foi o mesmo: entregar resultados concretos com um atendimento pautado pela transparência e proximidade.
|
||||
</p>
|
||||
<p>
|
||||
Acredito que a advocacia de excelência é baseada na confiança mútua. Por isso, não sou apenas um intermediário em processos. Sou um parceiro estratégico focado em defender seus interesses. Diferente dos grandes escritórios onde você é apenas um número, aqui o seu caso é tratado diretamente por mim, do início ao fim.
|
||||
</p>
|
||||
<p>
|
||||
Minha abordagem é pragmática e voltada para a solução. Seja na esfera trabalhista, civil ou familiar, meu compromisso é garantir que os direitos dos meus clientes sejam integralmente respeitados, com ética e determinação.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ul className="mb-10 space-y-4">
|
||||
{[
|
||||
"Formação em Direito pela Universidade de São Paulo (USP)",
|
||||
"Pós-graduado em Direito Civil e Processo Civil",
|
||||
"Atuação ética focada na resolução rápida de conflitos",
|
||||
"Atendimento 100% humanizado e sem burocracias",
|
||||
"Membro da Ordem dos Advogados do Brasil – Seção São Paulo",
|
||||
"Autor de diversos artigos jurídicos e palestras sobre direitos fundamentais"
|
||||
].map((item, i) => (
|
||||
<li key={i} className="flex items-start gap-3 text-gray-700">
|
||||
<CheckCircle2 className="mt-1 h-5 w-5 shrink-0 text-brand-gold" />
|
||||
<span>{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</FadeIn>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
26
Template-01/tsconfig.json
Normal file
26
Template-01/tsconfig.json
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"experimentalDecorators": true,
|
||||
"useDefineForClassFields": false,
|
||||
"module": "ESNext",
|
||||
"lib": [
|
||||
"ES2022",
|
||||
"DOM",
|
||||
"DOM.Iterable"
|
||||
],
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"allowJs": true,
|
||||
"jsx": "react-jsx",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./*"
|
||||
]
|
||||
},
|
||||
"allowImportingTsExtensions": true,
|
||||
"noEmit": true
|
||||
}
|
||||
}
|
||||
24
Template-01/vite.config.ts
Normal file
24
Template-01/vite.config.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import tailwindcss from '@tailwindcss/vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import path from 'path';
|
||||
import {defineConfig, loadEnv} from 'vite';
|
||||
|
||||
export default defineConfig(({mode}) => {
|
||||
const env = loadEnv(mode, '.', '');
|
||||
return {
|
||||
plugins: [react(), tailwindcss()],
|
||||
define: {
|
||||
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY),
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, '.'),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
// HMR is disabled in AI Studio via DISABLE_HMR env var.
|
||||
// Do not modifyâfile watching is disabled to prevent flickering during agent edits.
|
||||
hmr: process.env.DISABLE_HMR !== 'true',
|
||||
},
|
||||
};
|
||||
});
|
||||
20
Template-02/README.md
Normal file
20
Template-02/README.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<div align="center">
|
||||
<img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
|
||||
</div>
|
||||
|
||||
# Run and deploy your AI Studio app
|
||||
|
||||
This contains everything you need to run your app locally.
|
||||
|
||||
View your app in AI Studio: https://ai.studio/apps/780e049e-cbd9-47bc-85b1-ae1175ffdb47
|
||||
|
||||
## Run Locally
|
||||
|
||||
**Prerequisites:** Node.js
|
||||
|
||||
|
||||
1. Install dependencies:
|
||||
`npm install`
|
||||
2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
|
||||
3. Run the app:
|
||||
`npm run dev`
|
||||
111
Template-02/index.html
Normal file
111
Template-02/index.html
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
<!doctype html>
|
||||
<html lang="pt-BR">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<!-- Primary Meta Tags -->
|
||||
<title>Dra. Laura Silva | Advocacia Humanizada e Estratégica</title>
|
||||
<meta name="title" content="Dra. Laura Silva | Advocacia Humanizada e Estratégica">
|
||||
<meta name="description" content="Atendimento jurídico humanizado e estratégico para mulheres e famílias em São Paulo. Referência em Direito de Família, Civil e Trabalhista.">
|
||||
<meta name="keywords" content="advogada, advocacia para mulheres, advogada especialista, direito de família, direito civil, direito do consumidor, divórcio, advogada são paulo, consultoria jurídica para mulheres">
|
||||
|
||||
<!-- Open Graph / Facebook / WhatsApp (Card Compartilhado) -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://laurasilva.adv.br/">
|
||||
<meta property="og:title" content="Dra. Laura Silva | Advocacia Humanizada">
|
||||
<meta property="og:description" content="Advocacia estratégica e humanizada. Atendimento direto e especializado em Direito de Família, Civil e Trabalhista. Proteja seus direitos com quem entende.">
|
||||
<meta property="og:image" content="https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?q=80&w=1200&auto=format&fit=crop">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:url" content="https://laurasilva.adv.br/">
|
||||
<meta property="twitter:title" content="Dra. Laura Silva | Advocacia Humanizada">
|
||||
<meta property="twitter:description" content="Advocacia estratégica e humanizada. Atendimento direto e especializado em Direito de Família, Civil e Trabalhista. Proteja seus direitos com quem entende.">
|
||||
<meta property="twitter:image" content="https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?q=80&w=1200&auto=format&fit=crop">
|
||||
|
||||
<!-- Favicon (SVG) using pastel pink theme -->
|
||||
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23DDA7A5' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='m16 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z'/><path d='m2 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z'/><path d='M7 21h10'/><path d='M12 3v18'/><path d='M3 7h2c2 0 5-1 7-2 2 1 5 2 7 2h2'/></svg>" />
|
||||
|
||||
<!-- Schema.org LegalService (Google Rich Content) -->
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "LegalService",
|
||||
"name": "Dra. Laura Silva | Advogada Especialista",
|
||||
"image": "https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?q=80&w=1200&auto=format&fit=crop",
|
||||
"@id": "https://laurasilva.adv.br/",
|
||||
"url": "https://laurasilva.adv.br/",
|
||||
"telephone": "+5511999999999",
|
||||
"address": {
|
||||
"@type": "PostalAddress",
|
||||
"streetAddress": "Av. Paulista, 1000 - Bela Vista",
|
||||
"addressLocality": "São Paulo",
|
||||
"addressRegion": "SP",
|
||||
"postalCode": "01310-100",
|
||||
"addressCountry": "BR"
|
||||
},
|
||||
"geo": {
|
||||
"@type": "GeoCoordinates",
|
||||
"latitude": -23.561491,
|
||||
"longitude": -46.655881
|
||||
},
|
||||
"priceRange": "$$",
|
||||
"openingHoursSpecification": {
|
||||
"@type": "OpeningHoursSpecification",
|
||||
"dayOfWeek": [
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday"
|
||||
],
|
||||
"opens": "09:00",
|
||||
"closes": "18:00"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
|
||||
<!-- Theme Auto-Updater injected by autoblogia -->
|
||||
<script>
|
||||
window.addEventListener('message', (event) => {
|
||||
if (event.data && event.data.type === 'UPDATE_APPEARANCE') {
|
||||
const { primaryColor, backgroundColor, fontFamily } = event.data.settings;
|
||||
const root = document.documentElement;
|
||||
const style = root.style;
|
||||
|
||||
if (primaryColor) {
|
||||
style.setProperty('--primary', primaryColor);
|
||||
style.setProperty('--color-primary', primaryColor);
|
||||
style.setProperty('--color-tech-primary', primaryColor);
|
||||
style.setProperty('--color-seo-primary', primaryColor);
|
||||
style.setProperty('--color-finance-primary', primaryColor);
|
||||
style.setProperty('--color-recipe-primary', primaryColor);
|
||||
style.setProperty('--color-health-primary', primaryColor);
|
||||
style.setProperty('--color-corporate-primary', primaryColor);
|
||||
}
|
||||
if (backgroundColor) {
|
||||
style.setProperty('--background', backgroundColor);
|
||||
style.setProperty('--color-bg', backgroundColor);
|
||||
style.setProperty('--color-tech-surface', backgroundColor);
|
||||
style.setProperty('--color-seo-surface', backgroundColor);
|
||||
style.backgroundColor = backgroundColor;
|
||||
}
|
||||
if (fontFamily) {
|
||||
const fontString = '"' + fontFamily + '", sans-serif';
|
||||
style.setProperty('--font-family', fontString);
|
||||
style.setProperty('--font-sans', fontString);
|
||||
style.fontFamily = fontString;
|
||||
}
|
||||
}
|
||||
});
|
||||
// Ping parent window that we are ready
|
||||
window.parent.postMessage({ type: 'IFRAME_READY' }, '*');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
6
Template-02/metadata.json
Normal file
6
Template-02/metadata.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "Dra. Laura | Advocacia Humanizada",
|
||||
"description": "Website e blog para advogada com design suave, elegante e feminino.",
|
||||
"requestFramePermissions": [],
|
||||
"majorCapabilities": []
|
||||
}
|
||||
4443
Template-02/package-lock.json
generated
Normal file
4443
Template-02/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
38
Template-02/package.json
Normal file
38
Template-02/package.json
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"name": "react-example",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --port=3000 --host=0.0.0.0",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"clean": "rm -rf dist",
|
||||
"lint": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@google/genai": "^1.29.0",
|
||||
"@tailwindcss/typography": "^0.5.19",
|
||||
"@tailwindcss/vite": "^4.1.14",
|
||||
"@vitejs/plugin-react": "^5.0.4",
|
||||
"clsx": "^2.1.1",
|
||||
"dotenv": "^17.2.3",
|
||||
"express": "^4.21.2",
|
||||
"lucide-react": "^0.546.0",
|
||||
"motion": "^12.23.24",
|
||||
"react": "^19.0.1",
|
||||
"react-dom": "^19.0.1",
|
||||
"react-router-dom": "^7.15.0",
|
||||
"tailwind-merge": "^3.5.0",
|
||||
"vite": "^6.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/node": "^22.14.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"tailwindcss": "^4.1.14",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "~5.8.2",
|
||||
"vite": "^6.2.3"
|
||||
}
|
||||
}
|
||||
42
Template-02/src/App.tsx
Normal file
42
Template-02/src/App.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import React from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
||||
import { ScrollToTop } from './components/ScrollToTop';
|
||||
import { Navbar } from './components/layout/Navbar';
|
||||
import { Footer } from './components/layout/Footer';
|
||||
import { WhatsAppButton } from './components/layout/WhatsAppButton';
|
||||
import { CookieBanner } from './components/layout/CookieBanner';
|
||||
import { Analytics } from './components/layout/Analytics';
|
||||
import { Home } from './pages/Home';
|
||||
import { Sobre } from './pages/Sobre';
|
||||
import { Areas } from './pages/Areas';
|
||||
import { ComoFunciona } from './pages/ComoFunciona';
|
||||
import { Blog } from './pages/Blog';
|
||||
import { BlogPost } from './pages/BlogPost';
|
||||
import { Contact } from './pages/Contact';
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Router>
|
||||
<ScrollToTop />
|
||||
<Analytics />
|
||||
<div className="flex min-h-screen flex-col font-sans text-gray-800 antialiased selection:bg-brand-gold selection:text-white">
|
||||
<Navbar />
|
||||
<div className="flex-1">
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/sobre" element={<Sobre />} />
|
||||
<Route path="/areas" element={<Areas />} />
|
||||
<Route path="/funcionamento" element={<ComoFunciona />} />
|
||||
<Route path="/blog" element={<Blog />} />
|
||||
<Route path="/blog/:id" element={<BlogPost />} />
|
||||
<Route path="/contato" element={<Contact />} />
|
||||
</Routes>
|
||||
</div>
|
||||
<Footer />
|
||||
<WhatsAppButton />
|
||||
<CookieBanner />
|
||||
</div>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
|
||||
16
Template-02/src/components/ScrollToTop.tsx
Normal file
16
Template-02/src/components/ScrollToTop.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
export function ScrollToTop() {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
left: 0,
|
||||
behavior: 'instant',
|
||||
});
|
||||
}, [pathname]);
|
||||
|
||||
return null;
|
||||
}
|
||||
85
Template-02/src/components/layout/Analytics.tsx
Normal file
85
Template-02/src/components/layout/Analytics.tsx
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import { useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
gtag?: (...args: any[]) => void;
|
||||
dataLayer?: any[];
|
||||
fbq?: (...args: any[]) => void;
|
||||
_fbq?: any;
|
||||
}
|
||||
}
|
||||
|
||||
export function Analytics() {
|
||||
const location = useLocation();
|
||||
|
||||
// Inicialização dos Scripts (Executado apenas uma vez)
|
||||
useEffect(() => {
|
||||
// 1. Google Analytics (GA4)
|
||||
const gaId = import.meta.env.VITE_GA_MEASUREMENT_ID;
|
||||
if (gaId && !document.getElementById('ga-script')) {
|
||||
const script1 = document.createElement('script');
|
||||
script1.async = true;
|
||||
script1.src = `https://www.googletagmanager.com/gtag/js?id=${gaId}`;
|
||||
script1.id = 'ga-script';
|
||||
document.head.appendChild(script1);
|
||||
|
||||
const script2 = document.createElement('script');
|
||||
script2.innerHTML = `
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', '${gaId}');
|
||||
`;
|
||||
document.head.appendChild(script2);
|
||||
}
|
||||
|
||||
// 2. Facebook Pixel
|
||||
const fbPixelId = import.meta.env.VITE_FB_PIXEL_ID;
|
||||
if (fbPixelId && !document.getElementById('fb-pixel-script')) {
|
||||
const script = document.createElement('script');
|
||||
script.id = 'fb-pixel-script';
|
||||
script.innerHTML = `
|
||||
!function(f,b,e,v,n,t,s)
|
||||
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
|
||||
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
|
||||
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
|
||||
n.queue=[];t=b.createElement(e);t.async=!0;
|
||||
t.src=v;s=b.getElementsByTagName(e)[0];
|
||||
s.parentNode.insertBefore(t,s)}(window, document,'script',
|
||||
'https://connect.facebook.net/en_US/fbevents.js');
|
||||
fbq('init', '${fbPixelId}');
|
||||
fbq('track', 'PageView');
|
||||
`;
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
|
||||
// 3. Google AdSense
|
||||
const adsenseId = import.meta.env.VITE_ADSENSE_CLIENT_ID;
|
||||
if (adsenseId && !document.getElementById('adsense-script')) {
|
||||
const script = document.createElement('script');
|
||||
script.id = 'adsense-script';
|
||||
script.async = true;
|
||||
script.src = `https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${adsenseId}`;
|
||||
script.crossOrigin = 'anonymous';
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Rastreamento de Mudança de Rota (Page Views)
|
||||
useEffect(() => {
|
||||
const gaId = import.meta.env.VITE_GA_MEASUREMENT_ID;
|
||||
if (gaId && window.gtag) {
|
||||
window.gtag('config', gaId, {
|
||||
page_path: location.pathname + location.search,
|
||||
});
|
||||
}
|
||||
|
||||
const fbPixelId = import.meta.env.VITE_FB_PIXEL_ID;
|
||||
if (fbPixelId && typeof window.fbq === 'function') {
|
||||
window.fbq('track', 'PageView');
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
return null; // Este componente não renderiza nada na tela
|
||||
}
|
||||
69
Template-02/src/components/layout/CookieBanner.tsx
Normal file
69
Template-02/src/components/layout/CookieBanner.tsx
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { motion, AnimatePresence } from 'motion/react';
|
||||
import { X } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export function CookieBanner() {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Check if user has already accepted/declined
|
||||
const consent = localStorage.getItem('lgpd_consent');
|
||||
if (!consent) {
|
||||
// Small delay so it doesn't pop up instantly on page load
|
||||
const timer = setTimeout(() => setIsVisible(true), 1500);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleConsent = (accepted: boolean) => {
|
||||
localStorage.setItem('lgpd_consent', accepted ? 'accepted' : 'declined');
|
||||
setIsVisible(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{isVisible && (
|
||||
<motion.div
|
||||
initial={{ y: 100, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
exit={{ y: 100, opacity: 0 }}
|
||||
className="fixed bottom-0 left-0 right-0 z-[60] bg-brand-blue p-4 shadow-2xl md:p-6"
|
||||
>
|
||||
<div className="mx-auto flex max-w-7xl flex-col items-center justify-between gap-4 md:flex-row">
|
||||
<div className="text-sm text-gray-300 md:max-w-3xl">
|
||||
<strong className="block text-white mb-1">Privacidade e LGPD</strong>
|
||||
Utilizamos cookies para melhorar sua experiência em nosso site, personalizar conteúdo e analisar nosso tráfego.
|
||||
Ao continuar navegando, você concorda com a nossa{' '}
|
||||
<Link to="/contato" className="text-brand-gold hover:underline">
|
||||
Política de Privacidade
|
||||
</Link>.
|
||||
</div>
|
||||
|
||||
<div className="flex w-full flex-shrink-0 flex-row gap-3 md:w-auto">
|
||||
<button
|
||||
onClick={() => handleConsent(false)}
|
||||
className="flex-1 rounded-sm border border-white/30 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-white/10 md:flex-none"
|
||||
>
|
||||
Recusar
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleConsent(true)}
|
||||
className="flex-1 rounded-sm bg-brand-gold px-6 py-2 text-sm font-bold text-white transition-colors hover:bg-brand-gold-hover md:flex-none"
|
||||
>
|
||||
Aceitar
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleConsent(false)}
|
||||
className="hidden items-center justify-center text-gray-400 hover:text-white md:flex ml-2"
|
||||
aria-label="Fechar"
|
||||
>
|
||||
<X className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
75
Template-02/src/components/layout/Footer.tsx
Normal file
75
Template-02/src/components/layout/Footer.tsx
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import React from 'react';
|
||||
import { Scale, Mail, MapPin, Phone, Instagram, Linkedin } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<footer className="bg-brand-blue py-16 text-white text-opacity-80">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 gap-12 md:grid-cols-4 lg:grid-cols-5">
|
||||
{/* Brand */}
|
||||
<div className="lg:col-span-2">
|
||||
<Link to="/" className="mb-6 flex items-center gap-2">
|
||||
<Scale className="h-8 w-8 text-brand-gold" />
|
||||
<div className="flex flex-col">
|
||||
<span className="font-serif text-2xl font-bold leading-none tracking-tight text-white">
|
||||
Dra. Laura
|
||||
</span>
|
||||
<span className="text-xs font-medium tracking-widest text-brand-gold">
|
||||
ADVOCACIA HUMANIZADA
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
<p className="mb-6 max-w-xs text-sm leading-relaxed">
|
||||
Atendimento direto, acolhedor e estratégico focado na defesa dos seus direitos com sensibilidade e força.
|
||||
</p>
|
||||
<div className="flex items-center gap-4">
|
||||
<a href="#" className="flex h-10 w-10 items-center justify-center rounded-full border border-white/20 transition-colors hover:border-brand-gold hover:text-brand-gold">
|
||||
<Instagram className="h-4 w-4" />
|
||||
</a>
|
||||
<a href="#" className="flex h-10 w-10 items-center justify-center rounded-full border border-white/20 transition-colors hover:border-brand-gold hover:text-brand-gold">
|
||||
<Linkedin className="h-4 w-4" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Links */}
|
||||
<div>
|
||||
<h3 className="mb-6 font-serif text-lg font-semibold text-white">Navegação</h3>
|
||||
<ul className="flex flex-col gap-3 text-sm">
|
||||
<li><Link to="/sobre" className="hover:text-brand-gold transition-colors">Sobre a Advogada</Link></li>
|
||||
<li><Link to="/areas" className="hover:text-brand-gold transition-colors">Áreas de Atuação</Link></li>
|
||||
<li><Link to="/funcionamento" className="hover:text-brand-gold transition-colors">Como Funciona</Link></li>
|
||||
<li><Link to="/blog" className="hover:text-brand-gold transition-colors">Blog Jurídico</Link></li>
|
||||
<li><Link to="/contato" className="hover:text-brand-gold transition-colors">Contato</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Contact */}
|
||||
<div className="lg:col-span-2">
|
||||
<h3 className="mb-6 font-serif text-lg font-semibold text-white">Contato Direto</h3>
|
||||
<ul className="flex flex-col gap-4 text-sm">
|
||||
<li className="flex items-start gap-3">
|
||||
<Phone className="h-5 w-5 shrink-0 text-brand-gold" />
|
||||
<span>+55 (11) 99999-9999</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-3">
|
||||
<Mail className="h-5 w-5 shrink-0 text-brand-gold" />
|
||||
<span>contato@laurasilva.adv.br</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-3">
|
||||
<MapPin className="h-5 w-5 shrink-0 text-brand-gold" />
|
||||
<span>Av. Paulista, 1000 - Bela Vista<br />São Paulo - SP, 01310-100</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-16 flex flex-col items-center justify-between border-t border-white/10 pt-8 text-xs sm:flex-row">
|
||||
<p>© {new Date().getFullYear()} Laura Silva Advocacia. Todos os direitos reservados.</p>
|
||||
<p className="mt-2 sm:mt-0">OAB/SP 123.456</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
161
Template-02/src/components/layout/Navbar.tsx
Normal file
161
Template-02/src/components/layout/Navbar.tsx
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { Menu, X, Scale, Search } from 'lucide-react';
|
||||
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
||||
import { cn } from '../../lib/utils';
|
||||
|
||||
export function Navbar() {
|
||||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setIsScrolled(window.scrollY > 10);
|
||||
};
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
return () => window.removeEventListener('scroll', handleScroll);
|
||||
}, []);
|
||||
|
||||
// Handle hash scrolling when navigating across pages
|
||||
useEffect(() => {
|
||||
if (location.hash) {
|
||||
const element = document.querySelector(location.hash);
|
||||
if (element) {
|
||||
setTimeout(() => {
|
||||
element.scrollIntoView({ behavior: 'smooth' });
|
||||
}, 100);
|
||||
}
|
||||
} else {
|
||||
window.scrollTo(0,0);
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
const handleNavClick = (e: React.MouseEvent<HTMLAnchorElement>, href: string) => {
|
||||
setIsMobileMenuOpen(false);
|
||||
if (href.startsWith('/#')) {
|
||||
const hash = href.substring(1);
|
||||
if (location.pathname === '/') {
|
||||
e.preventDefault();
|
||||
const element = document.querySelector(hash);
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: 'smooth' });
|
||||
// Update URL hash without jumping
|
||||
window.history.pushState(null, '', hash);
|
||||
}
|
||||
} else {
|
||||
// Let it navigate to /#hash normally
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const navLinks = [
|
||||
{ name: 'Sobre', href: '/sobre' },
|
||||
{ name: 'Áreas de Atuação', href: '/areas' },
|
||||
{ name: 'Como Funciona', href: '/funcionamento' },
|
||||
{ name: 'Blog', href: '/blog' },
|
||||
{ name: 'Contato', href: '/contato' },
|
||||
];
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={cn(
|
||||
'fixed left-0 right-0 top-0 z-50 transition-all duration-300',
|
||||
isScrolled
|
||||
? 'bg-white shadow-md py-3'
|
||||
: 'bg-brand-blue py-5 text-white'
|
||||
)}
|
||||
>
|
||||
<div className="mx-auto flex max-w-7xl items-center justify-between px-6 lg:px-8">
|
||||
<Link to="/" className="flex items-center gap-2">
|
||||
<Scale className={cn("h-8 w-8", isScrolled ? "text-brand-gold" : "text-brand-gold")} />
|
||||
<div className="flex flex-col">
|
||||
<span className={cn("font-serif text-xl font-bold leading-none tracking-tight", isScrolled ? "text-brand-blue" : "text-white")}>
|
||||
Dra. Laura
|
||||
</span>
|
||||
<span className={cn("text-xs font-medium tracking-widest", isScrolled ? "text-gray-500" : "text-gray-300")}>
|
||||
ADVOGADA
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
{/* Desktop Nav */}
|
||||
<div className="hidden items-center gap-8 md:flex">
|
||||
{navLinks.map((link) => (
|
||||
<Link
|
||||
key={link.name}
|
||||
to={link.href}
|
||||
onClick={(e) => handleNavClick(e, link.href)}
|
||||
className={cn(
|
||||
'text-sm font-medium transition-colors hover:text-brand-gold',
|
||||
isScrolled ? 'text-gray-700' : 'text-gray-200'
|
||||
)}
|
||||
>
|
||||
{link.name}
|
||||
</Link>
|
||||
))}
|
||||
<div className="flex items-center gap-6 border-l border-white/20 pl-6">
|
||||
<Link
|
||||
to="/blog?focus=true"
|
||||
title="Buscar Artigos"
|
||||
className={cn(
|
||||
'transition-colors hover:text-brand-gold',
|
||||
isScrolled ? 'text-brand-blue' : 'text-white'
|
||||
)}
|
||||
>
|
||||
<Search className="h-5 w-5" />
|
||||
</Link>
|
||||
<Link
|
||||
to="/contato"
|
||||
className="rounded-none border-2 border-brand-gold bg-brand-gold px-6 py-2 text-sm font-semibold text-white transition-colors hover:bg-transparent hover:text-brand-gold"
|
||||
>
|
||||
Consulta Online
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Nav Toggle */}
|
||||
<button
|
||||
className="md:hidden"
|
||||
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
||||
>
|
||||
{isMobileMenuOpen ? (
|
||||
<X className={cn("h-6 w-6", isScrolled ? "text-brand-blue" : "text-white")} />
|
||||
) : (
|
||||
<Menu className={cn("h-6 w-6", isScrolled ? "text-brand-blue" : "text-white")} />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu */}
|
||||
{isMobileMenuOpen && (
|
||||
<div className="absolute left-0 right-0 top-full flex flex-col items-center gap-6 bg-white py-8 shadow-xl md:hidden">
|
||||
{navLinks.map((link) => (
|
||||
<Link
|
||||
key={link.name}
|
||||
to={link.href}
|
||||
onClick={(e) => handleNavClick(e, link.href)}
|
||||
className="text-base font-medium text-gray-800 transition-colors hover:text-brand-gold"
|
||||
>
|
||||
{link.name}
|
||||
</Link>
|
||||
))}
|
||||
<Link
|
||||
to="/blog?focus=true"
|
||||
onClick={() => setIsMobileMenuOpen(false)}
|
||||
className="flex items-center gap-2 text-base font-medium text-gray-800 transition-colors hover:text-brand-gold"
|
||||
>
|
||||
<Search className="h-5 w-5" /> Buscar Artigo
|
||||
</Link>
|
||||
<Link
|
||||
to="/contato"
|
||||
onClick={() => setIsMobileMenuOpen(false)}
|
||||
className="mt-4 rounded-none border-2 border-brand-gold bg-brand-gold px-8 py-3 text-base font-semibold text-white transition-colors hover:bg-white hover:text-brand-gold"
|
||||
>
|
||||
Consulta Online
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
18
Template-02/src/components/layout/WhatsAppButton.tsx
Normal file
18
Template-02/src/components/layout/WhatsAppButton.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { Phone } from 'lucide-react';
|
||||
|
||||
export function WhatsAppButton() {
|
||||
return (
|
||||
<div className="fixed bottom-6 right-6 z-50">
|
||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-400 opacity-75"></span>
|
||||
<a
|
||||
href="https://wa.me/5511999999999?text=Ol%C3%A1%2C%20gostaria%20de%20agendar%20uma%20consulta%20jur%C3%ADdica."
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="relative flex h-14 w-14 items-center justify-center rounded-full bg-green-500 text-white shadow-lg transition-transform hover:scale-110 hover:shadow-xl hover:bg-green-600 focus:outline-none"
|
||||
aria-label="Falar pelo WhatsApp"
|
||||
>
|
||||
<Phone className="h-7 w-7" fill="currentColor" />
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
113
Template-02/src/data/posts.ts
Normal file
113
Template-02/src/data/posts.ts
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
export interface BlogPostData {
|
||||
id: number;
|
||||
title: string;
|
||||
excerpt: string;
|
||||
category: string;
|
||||
date: string;
|
||||
imageUrl: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
const generateMockPosts = (): BlogPostData[] => {
|
||||
const categories = [
|
||||
"Direito Trabalhista", "Direito de Família", "Direito do Consumidor",
|
||||
"Direito Civil", "Contratos & Acordos", "Planejamento Sucessório"
|
||||
];
|
||||
|
||||
const titles = [
|
||||
"Direitos do Trabalhador: O que você precisa saber antes de pedir demissão",
|
||||
"Revisão da Vida Toda: Entenda se você tem direito a receber os atrasados",
|
||||
"Voo Cancelado: Quais são os seus direitos como consumidor?",
|
||||
"Guarda Compartilhada vs Guarda Unilateral: Entendendo as diferenças",
|
||||
"Contratos de Aluguel: Direitos e Deveres do Inquilino",
|
||||
"Responsabilidade Civil de Sócios: Até onde vai o risco empresarial?",
|
||||
"Atraso na Entrega de Imóveis: Direitos e Indenizações",
|
||||
"Pensão Alimentícia: Como é feito o cálculo?",
|
||||
"Assédio Moral no Trabalho: Como identificar e reunir provas",
|
||||
"Golpes no Pix e a Responsabilidade dos Bancos",
|
||||
"União Estável vs Casamento: Regimes de Bens",
|
||||
"LGPD nas Pequenas Empresas: O que é preciso adequar?",
|
||||
"Demissão por Justa Causa: Quando se aplica?",
|
||||
"Auxílio-Doença Negado pelo INSS: O que fazer?",
|
||||
"Cobranças Indevidas: Como agir e pedir reparação",
|
||||
"Inventário Extrajudicial: Requisitos e Prazos",
|
||||
"Horas Extras: Direitos, Limites e Reflexos",
|
||||
"Aposentadoria Especial: Exigências e Comprovação",
|
||||
"Direito de Arrependimento nas Compras Online",
|
||||
"Alienação Parental: Consequências Jurídicas",
|
||||
"Direitos Autorais na Era Digital: Protegendo suas Criações"
|
||||
];
|
||||
|
||||
const images = [
|
||||
"https://images.unsplash.com/photo-1589829085413-56de8ae18c73",
|
||||
"https://images.unsplash.com/photo-1454165804606-c3d57bc86b40",
|
||||
"https://images.unsplash.com/photo-1507679799987-c73779587ccf",
|
||||
"https://images.unsplash.com/photo-1503694978374-8a2fa686963a",
|
||||
"https://images.unsplash.com/photo-1554224155-6726b3ff858f",
|
||||
"https://images.unsplash.com/photo-1600880292203-757bb62b4baf",
|
||||
"https://images.unsplash.com/photo-1524813686514-a57563d77965",
|
||||
"https://images.unsplash.com/photo-1589391886645-d51941baf7fb",
|
||||
"https://images.unsplash.com/photo-1450101499163-c8848c66cb85",
|
||||
"https://images.unsplash.com/photo-1629904853716-f0bc54eea481",
|
||||
"https://images.unsplash.com/photo-1517486808906-6ca8b3f04846",
|
||||
"https://images.unsplash.com/photo-1505373877841-8d25f7d46678",
|
||||
"https://images.unsplash.com/photo-1593642532400-2682810df593",
|
||||
"https://images.unsplash.com/photo-1436491865332-7a61a109cc05",
|
||||
"https://images.unsplash.com/photo-1502086223501-7ea6ecd79368",
|
||||
"https://images.unsplash.com/photo-1521791136064-7986c2920216",
|
||||
"https://images.unsplash.com/photo-1573164713988-8665fc963095",
|
||||
"https://images.unsplash.com/photo-1536640712-4d4c36ef0e2c",
|
||||
"https://images.unsplash.com/photo-1523240795612-9a054b0db644",
|
||||
"https://images.unsplash.com/photo-1494526585095-c41746248156",
|
||||
"https://images.unsplash.com/photo-1486406146926-c627a92ad1ab",
|
||||
];
|
||||
|
||||
return titles.map((title, index) => {
|
||||
const category = categories[index % categories.length];
|
||||
return {
|
||||
id: index + 1,
|
||||
title: title,
|
||||
excerpt: `Entenda os principais aspectos legais, a jurisprudência atualizada e como garantir seus direitos em relação a ${title.split(':')[0].toLowerCase()}.`,
|
||||
category: category,
|
||||
date: `0${(21 - index) % 9 + 1} Mar 2024`,
|
||||
imageUrl: `${images[index]}?auto=format&fit=crop&q=80&w=800`,
|
||||
content: `
|
||||
<p>A área de <strong>${category}</strong> apresenta constantes desafios judiciais e extrajudiciais. O tema <em>"${title}"</em> tem sido objeto de grande debate nos tribunais superiores do Brasil.</p>
|
||||
|
||||
<h2>Contexto e Aplicação</h2>
|
||||
<p>Diante das recentes mudanças legislativas, é fundamental entender seus impactos práticos. O Superior Tribunal de Justiça (STJ) tem firmado entendimentos importantes que afetam diretamente as relações cotidianas.</p>
|
||||
|
||||
<blockquote>
|
||||
<p>"A proteção dos direitos fundamentais exige do operador do direito uma visão sistêmica e atualizada da jurisprudência." - Doutrina Jurídica Contemporânea</p>
|
||||
</blockquote>
|
||||
|
||||
<p>Entre os principais pontos a serem observados na prática, destacamos:</p>
|
||||
<ul>
|
||||
<li>Análise preventiva de riscos e <em>compliance</em>.</li>
|
||||
<li>Atuação extrajudicial para resolução pacífica de conflitos.</li>
|
||||
<li>Utilização das novas teses firmadas em recursos repetitivos (Temas do STJ e STF).</li>
|
||||
</ul>
|
||||
|
||||
<h2>Exemplo Prático e Decisões Recentes</h2>
|
||||
<p>Imagine o seguinte cenário: um cliente procura auxílio relatando um problema complexo envolvendo direitos não reconhecidos na esfera pertinente ao caso. A abordagem ideal combina a análise documental detalhada com a jurisprudência mais recente.</p>
|
||||
|
||||
<figure>
|
||||
<img src="${images[index]}?auto=format&fit=crop&q=80&w=800" alt="${title}" style="border-radius: 8px; margin: 20px 0; width: 100%; aspect-ratio: 16/9; object-fit: cover;" />
|
||||
<figcaption style="text-align: center; font-size: 0.9em; color: gray;">Reflexões e desdobramentos práticos na defesa dos direitos.</figcaption>
|
||||
</figure>
|
||||
|
||||
<h2>Como agir diante desta situação?</h2>
|
||||
<p>O primeiro passo é reunir todas as provas materiais possíveis: documentos, trocas de e-mails, conversas de WhatsApp, fotografias ou qualquer registro formal da relação firmada entre as partes.</p>
|
||||
<p>Após reunir a documentação, é vital buscar orientação técnica <strong>antes de assinar qualquer acordo ou recibo de quitação</strong>. Em muitos casos, a aceitação de termos genéricos pode inviabilizar a busca por uma reparação total no futuro.</p>
|
||||
|
||||
<p>Portanto, para mais informações e para análise pormenorizada do seu caso concreto, certifique-se de contar com assessoria jurídica especializada e combativa. <a href="/contato">Agende uma consulta conosco</a> para entender as particularidades da sua situação.</p>
|
||||
`
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const MOCK_POSTSList = generateMockPosts();
|
||||
export const MOCK_POSTS: Record<string, BlogPostData> = MOCK_POSTSList.reduce((acc, curr) => ({
|
||||
...acc,
|
||||
[curr.id.toString()]: curr
|
||||
}), {});
|
||||
37
Template-02/src/index.css
Normal file
37
Template-02/src/index.css
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Playfair+Display:ital,wght@0,400;0,500;0,600;0,700;1,400;1,600&display=swap');
|
||||
@import "tailwindcss";
|
||||
@plugin "@tailwindcss/typography";
|
||||
|
||||
@theme {
|
||||
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
|
||||
--font-serif: "Playfair Display", ui-serif, Georgia, serif;
|
||||
|
||||
--color-brand-blue: #3E3636;
|
||||
--color-brand-blue-light: #524747;
|
||||
--color-brand-gold: #DDA7A5;
|
||||
--color-brand-gold-hover: #C28F8D;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-sans);
|
||||
background-color: #FDFAFA; /* Very subtle warm white/pink */
|
||||
}
|
||||
|
||||
/* Modern clean scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: #FCF9F9;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #3E3636;
|
||||
border-radius: 4px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #DDA7A5;
|
||||
}
|
||||
6
Template-02/src/lib/utils.ts
Normal file
6
Template-02/src/lib/utils.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import { type ClassValue, clsx } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
10
Template-02/src/main.tsx
Normal file
10
Template-02/src/main.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import {StrictMode} from 'react';
|
||||
import {createRoot} from 'react-dom/client';
|
||||
import App from './App.tsx';
|
||||
import './index.css';
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
);
|
||||
92
Template-02/src/pages/Areas.tsx
Normal file
92
Template-02/src/pages/Areas.tsx
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
import React from 'react';
|
||||
import { Users, ShieldCheck, Scale, FileText, Home as HomeIcon, ArrowRight } from 'lucide-react';
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
const FadeIn = ({ children, delay = 0 }: { children: React.ReactNode, delay?: number }) => (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
transition={{ duration: 0.6, delay }}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
export function Areas() {
|
||||
const areas = [
|
||||
{
|
||||
title: "Direito de Família",
|
||||
icon: Users,
|
||||
desc: "Soluções acolhedoras com a sensibilidade que o caso exige. Orientação em divórcios consensuais ou litigiosos, fixação e revisão de pensão alimentícia, guarda, investigação de paternidade e inventários judiciais ou extrajudiciais."
|
||||
},
|
||||
{
|
||||
title: "Direito Civil",
|
||||
icon: Scale,
|
||||
desc: "Proteção do seu patrimônio e interesses civis. Elaboração e revisão de contratos, ações de indenizações por danos morais e materiais, responsabilidade civil, regularização de propriedades e processos de cobranças diversas para recuperação de crédito."
|
||||
},
|
||||
{
|
||||
title: "Direito Trabalhista",
|
||||
icon: Users,
|
||||
desc: "Defesa dos seus direitos no ambiente de trabalho. Atuamos com rescisões indiretas, cobrança de horas extras, combate ao assédio moral, reconhecimento de vínculo e proteção à gestante."
|
||||
},
|
||||
{
|
||||
title: "Direito do Consumidor",
|
||||
icon: FileText,
|
||||
desc: "Defesa firme contra abusos de fornecedores e planos de saúde. Atuamos em casos de negativações indevidas (SPC/Serasa), falhas em prestação de serviços, recusas abusivas de convênios médicos e defeitos não solucionados."
|
||||
},
|
||||
{
|
||||
title: "Contratos & Acordos",
|
||||
icon: ShieldCheck,
|
||||
desc: "Segurança total em transações, negócios e parcerias. Assessoramos na confecção e revisão de contratos particulares, locação, distratos, acordos extrajudiciais e documentações essenciais para proteção mútua."
|
||||
},
|
||||
{
|
||||
title: "Planejamento Sucessório",
|
||||
icon: HomeIcon,
|
||||
desc: "Organização do patrimônio com visão de futuro. Auxílio em testamentos, doações em vida, união estável (pacto antenupcial) e arranjos patrimoniais para evitar conflitos familiares futuros."
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<main className="w-full bg-[#f9fafb] pb-24 pt-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div className="mb-16 text-center">
|
||||
<FadeIn>
|
||||
<h1 className="mb-4 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl sm:text-5xl">
|
||||
Áreas de Atuação
|
||||
</h1>
|
||||
<p className="mx-auto max-w-2xl text-lg text-gray-600">
|
||||
Atuação jurídica ampla e estratégica focada nos ramos mais críticos para a defesa dos seus direitos processuais e patrimoniais.
|
||||
</p>
|
||||
</FadeIn>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{areas.map((area, index) => (
|
||||
<FadeIn key={index} delay={index * 0.1}>
|
||||
<div className="group relative flex h-full flex-col justify-between border border-gray-200 bg-white p-8 transition-all hover:border-brand-gold hover:shadow-xl">
|
||||
<div>
|
||||
<div className="mb-6 inline-flex h-12 w-12 items-center justify-center bg-brand-gold/10 text-brand-gold transition-colors group-hover:bg-brand-gold group-hover:text-white rounded-full">
|
||||
<area.icon className="h-6 w-6" />
|
||||
</div>
|
||||
<h3 className="mb-3 font-serif text-xl font-bold text-brand-blue">
|
||||
{area.title}
|
||||
</h3>
|
||||
<p className="mb-8 text-gray-600 leading-relaxed text-sm">
|
||||
{area.desc}
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="https://wa.me/5511999999999"
|
||||
className="mt-auto inline-flex items-center text-sm font-semibold text-brand-gold hover:underline"
|
||||
>
|
||||
Falar sobre o caso <ArrowRight className="ml-1 h-4 w-4" />
|
||||
</a>
|
||||
</div>
|
||||
</FadeIn>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
120
Template-02/src/pages/Blog.tsx
Normal file
120
Template-02/src/pages/Blog.tsx
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { ArrowLeft, Search, Calendar, User } from 'lucide-react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { motion } from 'motion/react';
|
||||
import { MOCK_POSTSList } from '../data/posts';
|
||||
|
||||
export function Blog() {
|
||||
const [activeCategory, setActiveCategory] = useState("Todos");
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const location = useLocation();
|
||||
const searchInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (location.state?.focusSearch || location.search.includes('focus=true')) {
|
||||
setTimeout(() => {
|
||||
searchInputRef.current?.focus();
|
||||
}, 100);
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
const filteredPosts = MOCK_POSTSList.filter(post => {
|
||||
const matchesCategory = activeCategory === "Todos" || post.category === activeCategory;
|
||||
const matchesSearch = post.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
post.excerpt.toLowerCase().includes(searchQuery.toLowerCase());
|
||||
return matchesCategory && matchesSearch;
|
||||
});
|
||||
|
||||
const categories = ["Todos", ...Array.from(new Set(MOCK_POSTSList.map(p => p.category)))];
|
||||
|
||||
return (
|
||||
<main className="w-full bg-[#fcfcfc] pb-24 pt-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
|
||||
{/* Header */}
|
||||
<div className="mb-16">
|
||||
<Link to="/" className="mb-6 inline-flex items-center gap-2 text-sm text-brand-gold hover:underline">
|
||||
<ArrowLeft className="h-4 w-4" /> Voltar para o início
|
||||
</Link>
|
||||
<div className="flex flex-col gap-6 md:flex-row md:items-end md:justify-between">
|
||||
<div>
|
||||
<h1 className="mb-4 font-serif text-4xl font-bold tracking-tight text-brand-blue md:text-5xl">
|
||||
Blog Jurídico
|
||||
</h1>
|
||||
<p className="max-w-xl text-lg text-gray-600">
|
||||
Artigos desmistificando o direito de forma clara. Saiba como se proteger nas mais diversas situações do dia a dia.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Search */}
|
||||
<div className="relative w-full max-w-md">
|
||||
<input
|
||||
ref={searchInputRef}
|
||||
type="text"
|
||||
placeholder="Buscar palavra-chave..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="w-full border-b-2 border-gray-200 bg-transparent py-2 pl-2 pr-10 text-brand-blue outline-none transition-colors focus:border-brand-gold"
|
||||
/>
|
||||
<Search className="absolute right-2 top-2 h-5 w-5 text-gray-400" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Categories (Mock) */}
|
||||
<div className="mb-12 flex flex-wrap gap-3">
|
||||
{categories.map(cat => (
|
||||
<button
|
||||
key={cat}
|
||||
onClick={() => setActiveCategory(cat)}
|
||||
className={`rounded-full px-5 py-2 text-sm font-medium transition-colors ${cat === activeCategory ? 'bg-brand-blue text-white' : 'bg-white border border-gray-200 text-gray-700 hover:border-brand-gold hover:text-brand-gold'}`}
|
||||
>
|
||||
{cat}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Posts Grid */}
|
||||
<div className="grid grid-cols-1 gap-10 md:grid-cols-2 lg:grid-cols-3">
|
||||
{filteredPosts.map((post, index) => (
|
||||
<motion.article
|
||||
key={post.id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: index * 0.1 }}
|
||||
className="group flex cursor-pointer flex-col overflow-hidden bg-white shadow-sm border border-gray-100 transition-all hover:-translate-y-1 hover:shadow-xl hover:border-brand-gold/30"
|
||||
>
|
||||
<div className="aspect-[16/10] w-full overflow-hidden">
|
||||
<img
|
||||
src={post.imageUrl}
|
||||
alt={post.title}
|
||||
className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-105"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col p-6">
|
||||
<span className="mb-3 text-xs font-bold uppercase tracking-wider text-brand-gold">
|
||||
{post.category}
|
||||
</span>
|
||||
<Link to={`/blog/${post.id}`} className="mb-3 font-serif text-xl font-bold leading-snug text-brand-blue group-hover:text-brand-gold-hover">
|
||||
{post.title}
|
||||
</Link>
|
||||
<p className="mb-6 flex-1 text-sm text-gray-600 line-clamp-3">
|
||||
{post.excerpt}
|
||||
</p>
|
||||
<div className="mt-auto flex items-center justify-between border-t border-gray-100 pt-4 text-xs text-gray-500">
|
||||
<div className="flex items-center gap-2">
|
||||
<User className="h-3 w-3" /> Dra. Laura Silva
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Calendar className="h-3 w-3" /> {post.date}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
212
Template-02/src/pages/BlogPost.tsx
Normal file
212
Template-02/src/pages/BlogPost.tsx
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { ArrowLeft, User, Calendar, Share2, Copy, Check, MessageCircle, Linkedin, Facebook, Twitter, X } from 'lucide-react';
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import { motion, AnimatePresence } from 'motion/react';
|
||||
import { MOCK_POSTS, MOCK_POSTSList } from '../data/posts';
|
||||
|
||||
export function BlogPost() {
|
||||
const { id } = useParams();
|
||||
const post = id ? MOCK_POSTS[id] : null;
|
||||
const [showShareMenu, setShowShareMenu] = useState(false);
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo({ top: 0, behavior: 'instant' });
|
||||
}, [id]);
|
||||
|
||||
if (!post) {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-gray-50 pt-20">
|
||||
<div className="text-center">
|
||||
<h2 className="mb-4 text-2xl font-bold text-brand-blue">Artigo não encontrado</h2>
|
||||
<Link to="/blog" className="text-brand-gold hover:underline">
|
||||
Voltar para o Blog
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const url = window.location.href;
|
||||
const encodedUrl = encodeURIComponent(url);
|
||||
const encodedTitle = encodeURIComponent(post.title);
|
||||
|
||||
const copyToClipboard = () => {
|
||||
navigator.clipboard.writeText(url);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
};
|
||||
|
||||
const shareLinks = [
|
||||
{
|
||||
name: 'WhatsApp',
|
||||
icon: MessageCircle,
|
||||
href: `https://api.whatsapp.com/send?text=${encodedTitle}%20${encodedUrl}`,
|
||||
color: 'hover:text-green-500 hover:bg-green-50'
|
||||
},
|
||||
{
|
||||
name: 'LinkedIn',
|
||||
icon: Linkedin,
|
||||
href: `https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`,
|
||||
color: 'hover:text-blue-600 hover:bg-blue-50'
|
||||
},
|
||||
{
|
||||
name: 'Facebook',
|
||||
icon: Facebook,
|
||||
href: `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`,
|
||||
color: 'hover:text-blue-500 hover:bg-blue-50'
|
||||
},
|
||||
{
|
||||
name: 'X (Twitter)',
|
||||
icon: Twitter,
|
||||
href: `https://twitter.com/intent/tweet?url=${encodedUrl}&text=${encodedTitle}`,
|
||||
color: 'hover:text-gray-900 hover:bg-gray-100'
|
||||
}
|
||||
];
|
||||
|
||||
const relatedPosts = MOCK_POSTSList.filter(p => p.category === post.category && p.id !== post.id).slice(0, 3);
|
||||
if (relatedPosts.length < 3) {
|
||||
const morePosts = MOCK_POSTSList.filter(p => !relatedPosts.includes(p) && p.id !== post.id);
|
||||
relatedPosts.push(...morePosts.slice(0, 3 - relatedPosts.length));
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="w-full bg-[#fcfcfc] pb-24 pt-32">
|
||||
<article className="mx-auto max-w-4xl px-6 lg:px-8">
|
||||
|
||||
{/* ... (rest of the content is unchanged) */}
|
||||
<Link to="/blog" className="mb-10 inline-flex items-center gap-2 text-sm text-gray-500 transition-colors hover:text-brand-gold">
|
||||
<ArrowLeft className="h-4 w-4" /> Todos os artigos
|
||||
</Link>
|
||||
|
||||
{/* Header */}
|
||||
<header className="mb-12 text-center">
|
||||
<span className="mb-4 inline-block bg-brand-blue px-3 py-1 text-xs font-bold uppercase tracking-widest text-brand-gold">
|
||||
{post.category}
|
||||
</span>
|
||||
<h1 className="mb-6 font-serif text-3xl font-bold leading-tight text-brand-blue md:text-5xl">
|
||||
{post.title}
|
||||
</h1>
|
||||
<div className="flex flex-wrap items-center justify-center gap-6 border-b border-gray-200 pb-8 text-sm text-gray-500">
|
||||
<div className="flex items-center gap-2">
|
||||
<User className="h-4 w-4" /> Dra. Laura Silva
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Calendar className="h-4 w-4" /> {post.date}
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={copyToClipboard}
|
||||
className={`flex items-center gap-2 transition-colors ${copied ? 'text-green-600' : 'hover:text-brand-blue'}`}
|
||||
>
|
||||
{copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
|
||||
{copied ? 'Copiado!' : 'Copiar Link'}
|
||||
</button>
|
||||
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={() => setShowShareMenu(!showShareMenu)}
|
||||
className="flex items-center gap-2 hover:text-brand-blue transition-colors"
|
||||
>
|
||||
{showShareMenu ? <X className="h-4 w-4" /> : <Share2 className="h-4 w-4" />}
|
||||
Compartilhar
|
||||
</button>
|
||||
|
||||
<AnimatePresence>
|
||||
{showShareMenu && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10, scale: 0.95 }}
|
||||
animate={{ opacity: 1, y: 0, scale: 1 }}
|
||||
exit={{ opacity: 0, y: 10, scale: 0.95 }}
|
||||
transition={{ duration: 0.15 }}
|
||||
className="absolute right-1/2 top-full mt-2 w-48 translate-x-1/2 sm:translate-x-0 sm:right-0 sm:left-auto rounded-lg border border-gray-100 bg-white p-2 shadow-xl z-10"
|
||||
>
|
||||
<div className="flex flex-col text-left">
|
||||
{shareLinks.map((link) => (
|
||||
<a
|
||||
key={link.name}
|
||||
href={link.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={`flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium text-gray-700 transition-colors ${link.color}`}
|
||||
onClick={() => setShowShareMenu(false)}
|
||||
>
|
||||
<link.icon className="h-4 w-4" /> {link.name}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Featured Image */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.98 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
className="mb-14 overflow-hidden rounded-sm"
|
||||
>
|
||||
<img
|
||||
src={post.imageUrl}
|
||||
alt={post.title}
|
||||
className="aspect-[2/1] w-full object-cover"
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
{/* Article Body */}
|
||||
<div className="mx-auto max-w-3xl prose prose-blue prose-lg prose-headings:font-serif prose-headings:text-brand-blue prose-a:text-brand-gold prose-a:no-underline hover:prose-a:underline">
|
||||
{/* Render HTML content safely since it's hardcoded mock, but natively prefer Markdown if CMS */}
|
||||
<div dangerouslySetInnerHTML={{ __html: post.content }} />
|
||||
</div>
|
||||
|
||||
{/* CTA Section */}
|
||||
<div className="mx-auto mt-20 max-w-3xl bg-brand-blue p-8 text-center sm:p-12 mb-20">
|
||||
<h3 className="mb-4 font-serif text-2xl font-bold text-white">Este conteúdo ajudou em sua situação?</h3>
|
||||
<p className="mb-8 text-gray-300">Cada caso demanda uma análise específica. Caso esteja passando por esta situação, avaliarei o seu cenário de forma técnica e sigilosa.</p>
|
||||
<Link to="/contato" className="inline-block bg-brand-gold px-8 py-3 font-semibold text-white transition-colors hover:bg-white hover:text-brand-blue">
|
||||
Solicitar Análise de Caso
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Artigos Relacionados */}
|
||||
{relatedPosts.length > 0 && (
|
||||
<div className="mx-auto max-w-5xl border-t border-gray-100 pt-16">
|
||||
<h3 className="mb-8 font-serif text-2xl font-bold text-brand-blue text-center">
|
||||
Você também pode se interessar por:
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 gap-8 md:grid-cols-3">
|
||||
{relatedPosts.map((relatedPost, index) => (
|
||||
<motion.article
|
||||
key={relatedPost.id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: index * 0.1 }}
|
||||
className="group flex flex-col overflow-hidden bg-white shadow-sm border border-gray-100 transition-all hover:-translate-y-1 hover:shadow-xl hover:border-brand-gold/30"
|
||||
>
|
||||
<Link to={`/blog/${relatedPost.id}`} className="aspect-[16/10] w-full overflow-hidden block">
|
||||
<img
|
||||
src={relatedPost.imageUrl}
|
||||
alt={relatedPost.title}
|
||||
className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-105"
|
||||
/>
|
||||
</Link>
|
||||
<div className="flex flex-1 flex-col p-6">
|
||||
<span className="mb-3 text-xs font-bold uppercase tracking-wider text-brand-gold">
|
||||
{relatedPost.category}
|
||||
</span>
|
||||
<Link to={`/blog/${relatedPost.id}`} className="font-serif text-lg font-bold leading-snug text-brand-blue group-hover:text-brand-gold-hover">
|
||||
{relatedPost.title}
|
||||
</Link>
|
||||
</div>
|
||||
</motion.article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</article>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
96
Template-02/src/pages/ComoFunciona.tsx
Normal file
96
Template-02/src/pages/ComoFunciona.tsx
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
import React from 'react';
|
||||
import { MessageCircle } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
const FadeIn = ({ children, delay = 0 }: { children: React.ReactNode, delay?: number }) => (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
transition={{ duration: 0.6, delay }}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
export function ComoFunciona() {
|
||||
const steps = [
|
||||
{ num: "01", title: "Envie sua Dúvida", desc: "Entre em contato via WhatsApp e explique brevemente o que aconteceu. Não há barreiras burocráticas no primeiro contato." },
|
||||
{ num: "02", title: "Avaliação Inicial", desc: "Faço uma análise rápida e honesta da viabilidade jurídica do seu caso, informando as chances de êxito." },
|
||||
{ num: "03", title: "Orientação e Estratégia", desc: "Apresento a melhor estratégia, os custos de forma totalmente transparente e enviamos o termo de prestação de serviços." },
|
||||
{ num: "04", title: "Acompanhamento", desc: "Inicio as medidas necessárias, mantendo você atualizado ativamente em cada etapa e movimentação chave do processo." }
|
||||
];
|
||||
|
||||
const points = [
|
||||
{ title: "Atendimento Direto", desc: "Você fala diretamente com a advogada responsável pelo caso, sem secretárias intermediando." },
|
||||
{ title: "Linguagem Clara", desc: "Sem 'juridiquês' complicado. Explico sua situação de forma simples e acolhedora." },
|
||||
{ title: "Transparência Total", desc: "Honorários acordados no início. Sem surpresas ou cobranças ocultas no meio do processo." },
|
||||
{ title: "Agilidade Online", desc: "Tenha todo o suporte por WhatsApp ou videoconferência, sem precisar sair de casa." }
|
||||
];
|
||||
|
||||
return (
|
||||
<main className="w-full bg-white pb-24 pt-32">
|
||||
<section className="bg-brand-blue py-24 sm:py-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<FadeIn>
|
||||
<h1 className="mb-6 text-center font-serif text-3xl font-bold tracking-tight text-white sm:text-4xl md:text-5xl">
|
||||
Como funciona o acompanhamento
|
||||
</h1>
|
||||
<p className="mx-auto mb-16 max-w-2xl text-center text-lg text-gray-300">
|
||||
Minha prioridade é oferecer um método de atendimento moderno, simplificado e altamente eficiente.
|
||||
</p>
|
||||
</FadeIn>
|
||||
|
||||
<div className="grid grid-cols-1 gap-12 md:grid-cols-2 lg:grid-cols-4 relative">
|
||||
<div className="absolute top-1/2 left-0 right-0 h-[1px] bg-white/20 hidden lg:block -translate-y-[15px] z-0"></div>
|
||||
|
||||
{steps.map((step, index) => (
|
||||
<FadeIn key={index} delay={index * 0.1}>
|
||||
<div className="relative z-10 flex flex-col items-center text-center">
|
||||
<div className="mb-6 flex h-20 w-20 items-center justify-center rounded-full border-4 border-brand-blue bg-brand-gold text-2xl font-serif font-bold text-white shadow-xl">
|
||||
{step.num}
|
||||
</div>
|
||||
<h3 className="mb-3 font-serif text-xl font-bold text-white">{step.title}</h3>
|
||||
<p className="text-gray-300 text-sm leading-relaxed">{step.desc}</p>
|
||||
</div>
|
||||
</FadeIn>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="py-24 sm:py-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 gap-16 lg:grid-cols-2 lg:items-center">
|
||||
<FadeIn>
|
||||
<h2 className="mb-6 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
|
||||
Por que me escolher para defender você?
|
||||
</h2>
|
||||
<p className="mb-10 text-lg text-gray-600 text-balance">
|
||||
Acredito que a advocacia moderna não precisa ser lenta, cheia de protocolos e distante do cliente. Meu modelo de atuação é focado no resultado e na tranquilidade do cliente.
|
||||
</p>
|
||||
<Link
|
||||
to="/contato"
|
||||
className="inline-flex items-center justify-center gap-2 bg-brand-blue px-6 py-3 font-semibold text-white transition hover:bg-brand-blue-light"
|
||||
>
|
||||
<MessageCircle className="h-5 w-5" /> Agendar Consulta
|
||||
</Link>
|
||||
</FadeIn>
|
||||
|
||||
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2">
|
||||
{points.map((point, i) => (
|
||||
<FadeIn key={i} delay={i * 0.1}>
|
||||
<div className="border-l-4 border-brand-gold pl-6">
|
||||
<h3 className="mb-2 font-serif text-lg font-bold text-brand-blue">{point.title}</h3>
|
||||
<p className="text-gray-600 leading-relaxed text-sm">{point.desc}</p>
|
||||
</div>
|
||||
</FadeIn>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
216
Template-02/src/pages/Contact.tsx
Normal file
216
Template-02/src/pages/Contact.tsx
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { motion } from 'motion/react';
|
||||
import { MapPin, Phone, Mail, Clock, Send, CheckCircle2 } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export function Contact() {
|
||||
const [formState, setFormState] = useState<'idle' | 'submitting' | 'success'>('idle');
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setFormState('submitting');
|
||||
// Simulate form submission
|
||||
setTimeout(() => {
|
||||
setFormState('success');
|
||||
}, 1500);
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="w-full bg-[#fcfcfc] pb-24 pt-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
|
||||
{/* Header */}
|
||||
<div className="mb-16 text-center">
|
||||
<h1 className="mb-4 font-serif text-4xl font-bold tracking-tight text-brand-blue md:text-5xl">
|
||||
Entre em Contato
|
||||
</h1>
|
||||
<p className="mx-auto max-w-2xl text-lg text-gray-600">
|
||||
Estamos prontos para ouvir você e encontrar a melhor solução jurídica para o seu caso. Escolha a forma de contato de sua preferência.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-16 lg:grid-cols-2">
|
||||
{/* Informações de Contato */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
className="flex flex-col justify-between"
|
||||
>
|
||||
<div>
|
||||
<h2 className="mb-8 font-serif text-2xl font-bold text-brand-blue">Informações de Atendimento</h2>
|
||||
|
||||
<div className="mb-12 space-y-8">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex h-12 w-12 shrink-0 items-center justify-center bg-brand-blue text-brand-gold">
|
||||
<Phone className="h-6 w-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="mb-1 font-serif text-lg font-bold text-brand-blue">WhatsApp & Telefone</h3>
|
||||
<p className="text-gray-600">+55 (11) 99999-9999</p>
|
||||
<p className="text-gray-600">Atendimento prioritário via WhatsApp</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex h-12 w-12 shrink-0 items-center justify-center bg-brand-blue text-brand-gold">
|
||||
<Mail className="h-6 w-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="mb-1 font-serif text-lg font-bold text-brand-blue">E-mail</h3>
|
||||
<p className="text-gray-600">contato@laurasilva.adv.br</p>
|
||||
<p className="text-gray-600">Para envio de documentos e propostas</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex h-12 w-12 shrink-0 items-center justify-center bg-brand-blue text-brand-gold">
|
||||
<MapPin className="h-6 w-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="mb-1 font-serif text-lg font-bold text-brand-blue">Escritório Físico</h3>
|
||||
<p className="text-gray-600">Av. Paulista, 1000 - Bela Vista</p>
|
||||
<p className="text-gray-600">São Paulo - SP, 01310-100</p>
|
||||
<p className="mt-1 text-sm text-brand-gold">Atendimento presencial apenas com agendamento.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex h-12 w-12 shrink-0 items-center justify-center bg-brand-blue text-brand-gold">
|
||||
<Clock className="h-6 w-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="mb-1 font-serif text-lg font-bold text-brand-blue">Horário de Funcionamento</h3>
|
||||
<p className="text-gray-600">Segunda a Sexta: 09h às 18h</p>
|
||||
<p className="text-gray-600">Plantão para urgências criminais 24h</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Map (Placeholder using generic image or iframe) */}
|
||||
<div className="aspect-[16/9] w-full overflow-hidden bg-gray-100 filter grayscale">
|
||||
<iframe
|
||||
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3657.1973618684784!2d-46.658097023769186!3d-23.56134956158784!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x94ce59c8da0aa315%3A0xd59f9431f2c9776a!2sAv.%20Paulista%2C%201000%20-%20Bela%20Vista%2C%20S%C3%A3o%20Paulo%20-%20SP%2C%2001310-100!5e0!3m2!1spt-BR!2sbr!4v1714571987515!5m2!1spt-BR!2sbr"
|
||||
width="100%"
|
||||
height="100%"
|
||||
style={{ border: 0 }}
|
||||
allowFullScreen={false}
|
||||
loading="lazy"
|
||||
referrerPolicy="no-referrer-when-downgrade">
|
||||
</iframe>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Formulário de Contato */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
className="flex flex-col justify-center bg-white p-8 shadow-xl md:p-12 border border-gray-100"
|
||||
>
|
||||
<h2 className="mb-8 font-serif text-2xl font-bold text-brand-blue">Envie sua Mensagem</h2>
|
||||
|
||||
{formState === 'success' ? (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
className="flex flex-col items-center justify-center p-8 text-center"
|
||||
>
|
||||
<CheckCircle2 className="mb-4 h-16 w-16 text-green-500" />
|
||||
<h3 className="mb-2 font-serif text-xl font-bold text-brand-blue">Mensagem Enviada!</h3>
|
||||
<p className="mb-8 text-gray-600">Recebemos seu contato. Retornaremos o mais breve possível para o e-mail ou telefone informado.</p>
|
||||
<button
|
||||
onClick={() => setFormState('idle')}
|
||||
className="bg-brand-blue px-6 py-3 font-semibold text-white transition-colors hover:bg-brand-gold"
|
||||
>
|
||||
Enviar Nova Mensagem
|
||||
</button>
|
||||
</motion.div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} className="flex flex-col gap-6">
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="name" className="text-sm font-semibold text-brand-blue uppercase tracking-wider">Seu Nome Completo</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
required
|
||||
className="border-b-2 border-gray-200 bg-transparent py-2 outline-none transition-colors focus:border-brand-gold"
|
||||
placeholder="Ex: Carlos Silva"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-6 md:flex-row">
|
||||
<div className="flex w-full flex-col gap-2">
|
||||
<label htmlFor="email" className="text-sm font-semibold text-brand-blue uppercase tracking-wider">E-mail</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
required
|
||||
className="border-b-2 border-gray-200 bg-transparent py-2 outline-none transition-colors focus:border-brand-gold"
|
||||
placeholder="seu@email.com"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex w-full flex-col gap-2">
|
||||
<label htmlFor="phone" className="text-sm font-semibold text-brand-blue uppercase tracking-wider">Telefone / WhatsApp</label>
|
||||
<input
|
||||
type="tel"
|
||||
id="phone"
|
||||
required
|
||||
className="border-b-2 border-gray-200 bg-transparent py-2 outline-none transition-colors focus:border-brand-gold"
|
||||
placeholder="(11) 99999-9999"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="area" className="text-sm font-semibold text-brand-blue uppercase tracking-wider">Área de Interesse (Opcional)</label>
|
||||
<select
|
||||
id="area"
|
||||
className="border-b-2 border-gray-200 bg-transparent py-2 outline-none transition-colors focus:border-brand-gold text-gray-600"
|
||||
>
|
||||
<option value="">Selecione a área...</option>
|
||||
<option value="trabalhista">Direito Trabalhista</option>
|
||||
<option value="previdenciario">Direito Previdenciário</option>
|
||||
<option value="civil">Direito Civil</option>
|
||||
<option value="familia">Direito de Família</option>
|
||||
<option value="consumidor">Direito do Consumidor</option>
|
||||
<option value="outros">Outros</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="message" className="text-sm font-semibold text-brand-blue uppercase tracking-wider">Resumo do Caso</label>
|
||||
<textarea
|
||||
id="message"
|
||||
rows={4}
|
||||
required
|
||||
className="resize-none border-b-2 border-gray-200 bg-transparent py-2 outline-none transition-colors focus:border-brand-gold"
|
||||
placeholder="Descreva brevemente o que aconteceu..."
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={formState === 'submitting'}
|
||||
className="mt-4 flex items-center justify-center gap-2 bg-brand-gold px-8 py-4 font-semibold text-white transition-all hover:bg-brand-blue disabled:opacity-70 disabled:hover:bg-brand-gold"
|
||||
>
|
||||
{formState === 'submitting' ? 'Enviando...' : (
|
||||
<>
|
||||
Enviar Mensagem <Send className="h-4 w-4" />
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
<p className="text-xs text-gray-500 text-center">
|
||||
Garantimos sigilo absoluto sobre todas as informações prestadas neste formulário.
|
||||
</p>
|
||||
</form>
|
||||
)}
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
471
Template-02/src/pages/Home.tsx
Normal file
471
Template-02/src/pages/Home.tsx
Normal file
|
|
@ -0,0 +1,471 @@
|
|||
import React, { useState } from 'react';
|
||||
import { motion, AnimatePresence } from 'motion/react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import {
|
||||
ArrowRight, ShieldCheck, Scale, FileText,
|
||||
Users, Building, Home as HomeIcon, CheckCircle2,
|
||||
MessageCircle, Plus, Minus
|
||||
} from 'lucide-react';
|
||||
import { cn } from '../lib/utils';
|
||||
|
||||
// Reusable animated container
|
||||
const FadeIn = ({ children, delay = 0 }: { children: React.ReactNode, delay?: number }) => (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
transition={{ duration: 0.6, delay }}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
export function Home() {
|
||||
return (
|
||||
<main className="w-full">
|
||||
<HeroSection />
|
||||
<AboutSection />
|
||||
<PracticeAreasSection />
|
||||
<HowItWorksSection />
|
||||
<DifferentiatorsSection />
|
||||
<TestimonialsSection />
|
||||
<FAQSection />
|
||||
<FinalCTASection />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
function HeroSection() {
|
||||
return (
|
||||
<section className="relative flex min-h-[90vh] items-center bg-brand-blue pt-20">
|
||||
{/* Background Pattern / Overlay */}
|
||||
<div className="absolute inset-0 z-0 opacity-20 bg-[url('https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?q=80&w=2000&auto=format&fit=crop')] bg-cover bg-center mix-blend-overlay"></div>
|
||||
|
||||
<div className="relative z-10 mx-auto grid w-full max-w-7xl grid-cols-1 gap-12 px-6 lg:grid-cols-2 lg:px-8">
|
||||
<motion.div
|
||||
className="flex flex-col justify-center pt-10 pb-16 lg:py-24"
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
>
|
||||
<div className="mb-6 flex items-center gap-4">
|
||||
<span className="h-[1px] w-12 bg-brand-gold"></span>
|
||||
<span className="text-sm font-semibold tracking-widest text-brand-gold uppercase">
|
||||
OAB/SP 123.456
|
||||
</span>
|
||||
</div>
|
||||
<h1 className="mb-6 font-serif text-4xl leading-tight font-bold text-white md:text-5xl lg:text-6xl">
|
||||
Advocacia estratégica e <span className="text-brand-gold">atendimento direto</span> para proteger seus direitos.
|
||||
</h1>
|
||||
<p className="mb-10 max-w-lg text-lg text-gray-300 leading-relaxed">
|
||||
Atendimento humanizado, sem intermediários. Foco em soluções ágeis e máxima segurança jurídica para você e seu patrimônio.
|
||||
</p>
|
||||
<div className="flex flex-col gap-4 sm:flex-row">
|
||||
<a
|
||||
href="https://wa.me/5511999999999"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="flex items-center justify-center gap-2 bg-brand-gold px-8 py-4 text-center font-semibold text-white transition-all hover:bg-brand-gold-hover hover:-translate-y-1"
|
||||
>
|
||||
Falar com a Advogada <ArrowRight className="h-5 w-5" />
|
||||
</a>
|
||||
<Link
|
||||
to="/areas"
|
||||
className="flex items-center justify-center gap-2 border border-white/30 px-8 py-4 text-center font-semibold text-white backdrop-blur-sm transition-all hover:bg-white hover:text-brand-blue"
|
||||
>
|
||||
Conheça as Áreas
|
||||
</Link>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
className="relative hidden items-end justify-center lg:flex"
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.8, delay: 0.2 }}
|
||||
>
|
||||
{/* Lawyer Photo - High quality professional portrait */}
|
||||
<div className="relative h-[85%] w-full max-w-md">
|
||||
<div className="absolute -inset-4 border border-brand-gold/30 translate-x-4 -translate-y-4"></div>
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?auto=format&fit=crop&q=80&w=800"
|
||||
alt="Dra. Laura Silva"
|
||||
className="relative h-full w-full object-cover object-top shadow-2xl grayscale-[20%] contrast-125 rounded-tl-3xl rounded-br-3xl"
|
||||
/>
|
||||
{/* Experience Badge */}
|
||||
<div className="absolute -left-12 bottom-12 flex items-center gap-4 bg-white p-4 shadow-xl">
|
||||
<div className="flex h-12 w-12 items-center justify-center bg-brand-blue text-brand-gold font-serif font-bold text-xl">
|
||||
15
|
||||
</div>
|
||||
<p className="font-serif text-sm font-bold leading-tight text-brand-blue uppercase">
|
||||
Anos de<br/>Experiência
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function AboutSection() {
|
||||
return (
|
||||
<section id="sobre" className="overflow-hidden bg-white py-24 sm:py-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 items-center gap-y-16 gap-x-16 lg:grid-cols-2">
|
||||
<FadeIn>
|
||||
<div className="relative">
|
||||
<div className="aspect-[3/4] w-full overflow-hidden bg-gray-100 lg:max-w-md rounded-t-full">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1573497019940-1c28c88b4f3e?auto=format&fit=crop&q=80&w=800"
|
||||
alt="Dra. Laura Silva em seu escritório"
|
||||
className="h-full w-full object-cover grayscale-[10%]"
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute -right-4 -bottom-4 h-32 w-32 bg-brand-gold opacity-20 -z-10"></div>
|
||||
</div>
|
||||
</FadeIn>
|
||||
|
||||
<FadeIn delay={0.2}>
|
||||
<div className="max-w-xl">
|
||||
<h2 className="mb-4 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
|
||||
Dra. Laura Silva
|
||||
</h2>
|
||||
<p className="mb-6 text-sm font-medium tracking-widest text-brand-gold uppercase">
|
||||
Especialista & Fundadora
|
||||
</p>
|
||||
<div className="mb-8 space-y-4 text-lg text-gray-600">
|
||||
<p>
|
||||
Com mais de uma década de dedicação exclusiva à advocacia, meu objetivo sempre foi o mesmo: entregar resultados concretos com um atendimento pautado pela transparência e proximidade.
|
||||
</p>
|
||||
<p>
|
||||
Não sou apenas um intermediário em processos. Sou um parceiro estratégico focado em defender seus interesses. Diferente dos grandes escritórios onde você é apenas um número, aqui o seu caso é tratado diretamente por mim, do início ao fim.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ul className="mb-10 space-y-4">
|
||||
{[
|
||||
"Formação em Direito pela Universidade de São Paulo (USP)",
|
||||
"Pós-graduado em Direito Civil e Processo Civil",
|
||||
"Atuação ética focada na resolução rápida de conflitos",
|
||||
"Atendimento 100% humanizado e sem burocracias"
|
||||
].map((item, i) => (
|
||||
<li key={i} className="flex items-start gap-3 text-gray-700">
|
||||
<CheckCircle2 className="mt-1 h-5 w-5 shrink-0 text-brand-gold" />
|
||||
<span>{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</FadeIn>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function PracticeAreasSection() {
|
||||
const areas = [
|
||||
{
|
||||
title: "Direito Trabalhista",
|
||||
icon: Users,
|
||||
desc: "Defesa dos seus direitos no ambiente de trabalho. Rescisões, horas extras, assédio moral e doenças ocupacionais."
|
||||
},
|
||||
{
|
||||
title: "Direito Previdenciário",
|
||||
icon: ShieldCheck,
|
||||
desc: "Garantia do seu benefício justo. Aposentadorias, BPC/LOAS, pensão por morte e auxílio-doença."
|
||||
},
|
||||
{
|
||||
title: "Direito Civil",
|
||||
icon: Scale,
|
||||
desc: "Proteção do seu patrimônio e interesses. Contratos, indenizações, responsabilidade civil e cobranças."
|
||||
},
|
||||
{
|
||||
title: "Direito de Família",
|
||||
icon: Users,
|
||||
desc: "Soluções com sensibilidade. Divórcios, pensão alimentícia, guarda, inventários e partilha de bens."
|
||||
},
|
||||
{
|
||||
title: "Direito do Consumidor",
|
||||
icon: FileText,
|
||||
desc: "Contra abusos de empresas. Negativações indevidas, problemas com planos de saúde e voos, produtos com defeito."
|
||||
},
|
||||
{
|
||||
title: "Direito Imobiliário",
|
||||
icon: HomeIcon,
|
||||
desc: "Segurança em transações. Regularização de imóveis, usucapião, contratos de aluguel e distratos."
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section id="areas" className="bg-[#f9fafb] py-24 sm:py-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div className="mb-16 text-center">
|
||||
<FadeIn>
|
||||
<h2 className="mb-4 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
|
||||
Áreas de Especialidade
|
||||
</h2>
|
||||
<p className="mx-auto max-w-2xl text-lg text-gray-600">
|
||||
Atuação jurídica ampla e estratégica focada nos ramos mais críticos para a defesa dos seus direitos processuais e patrimoniais.
|
||||
</p>
|
||||
</FadeIn>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{areas.map((area, index) => (
|
||||
<FadeIn key={index} delay={index * 0.1}>
|
||||
<div className="group relative flex h-full flex-col justify-between border border-gray-200 bg-white p-8 transition-all hover:border-brand-gold hover:shadow-xl">
|
||||
<div>
|
||||
<div className="mb-6 inline-flex h-12 w-12 items-center justify-center bg-blue-50 text-brand-blue transition-colors group-hover:bg-brand-gold group-hover:text-white">
|
||||
<area.icon className="h-6 w-6" />
|
||||
</div>
|
||||
<h3 className="mb-3 font-serif text-xl font-bold text-brand-blue">
|
||||
{area.title}
|
||||
</h3>
|
||||
<p className="mb-8 text-gray-600">
|
||||
{area.desc}
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="https://wa.me/5511999999999"
|
||||
className="mt-auto inline-flex items-center text-sm font-semibold text-brand-gold hover:underline"
|
||||
>
|
||||
Falar sobre o caso <ArrowRight className="ml-1 h-4 w-4" />
|
||||
</a>
|
||||
</div>
|
||||
</FadeIn>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function HowItWorksSection() {
|
||||
const steps = [
|
||||
{ num: "01", title: "Envie sua Dúvida", desc: "Entre em contato via WhatsApp e explique brevemente o que aconteceu." },
|
||||
{ num: "02", title: "Avaliação Inicial", desc: "Faço uma análise rápida e honesta da viabilidade jurídica do seu caso." },
|
||||
{ num: "03", title: "Orientação e Estratégia", desc: "Apresento a melhor estratégia, custos transparentes e fechamos o acordo." },
|
||||
{ num: "04", title: "Acompanhamento", desc: "Inicio as medidas necessárias, mantendo você atualizado em cada etapa." }
|
||||
];
|
||||
|
||||
return (
|
||||
<section id="funcionamento" className="bg-brand-blue py-24 sm:py-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<FadeIn>
|
||||
<h2 className="mb-16 text-center font-serif text-3xl font-bold tracking-tight text-white sm:text-4xl">
|
||||
Como funciona meu atendimento
|
||||
</h2>
|
||||
</FadeIn>
|
||||
|
||||
<div className="grid grid-cols-1 gap-12 md:grid-cols-2 lg:grid-cols-4 relative">
|
||||
<div className="absolute top-1/2 left-0 right-0 h-[1px] bg-white/20 hidden lg:block -translate-y-[45px] z-0"></div>
|
||||
|
||||
{steps.map((step, index) => (
|
||||
<FadeIn key={index} delay={index * 0.1}>
|
||||
<div className="relative z-10 flex flex-col items-center text-center">
|
||||
<div className="mb-6 flex h-20 w-20 items-center justify-center rounded-full border-4 border-brand-blue bg-brand-gold text-2xl font-serif font-bold text-white shadow-xl">
|
||||
{step.num}
|
||||
</div>
|
||||
<h3 className="mb-3 font-serif text-xl font-bold text-white">{step.title}</h3>
|
||||
<p className="text-gray-300">{step.desc}</p>
|
||||
</div>
|
||||
</FadeIn>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function DifferentiatorsSection() {
|
||||
const points = [
|
||||
{ title: "Atendimento Direto", desc: "Você fala diretamente com a advogada responsável pelo caso, sem secretárias intermediando." },
|
||||
{ title: "Linguagem Clara", desc: "Sem 'juridiquês' complicado. Explico sua situação de forma simples e acolhedora." },
|
||||
{ title: "Transparência Total", desc: "Honorários acordados no início. Sem surpresas ou cobranças ocultas no meio do processo." },
|
||||
{ title: "Agilidade Online", desc: "Tenha todo o suporte por WhatsApp ou videoconferência, sem precisar sair de casa." }
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="bg-white py-24 sm:py-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 gap-16 lg:grid-cols-2 lg:items-center">
|
||||
<FadeIn>
|
||||
<h2 className="mb-6 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
|
||||
Por que me escolher para defender você?
|
||||
</h2>
|
||||
<p className="mb-10 text-lg text-gray-600 text-balance">
|
||||
Advocacia não precisa ser lenta e distante. Meu modelo de atuação é focado no resultado e no cliente.
|
||||
</p>
|
||||
<Link
|
||||
to="/contato"
|
||||
className="inline-flex items-center justify-center gap-2 bg-brand-blue px-6 py-3 font-semibold text-white transition hover:bg-brand-blue-light"
|
||||
>
|
||||
<MessageCircle className="h-5 w-5" /> Agendar Consulta
|
||||
</Link>
|
||||
</FadeIn>
|
||||
|
||||
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2">
|
||||
{points.map((point, i) => (
|
||||
<FadeIn key={i} delay={i * 0.1}>
|
||||
<div className="border-l-4 border-brand-gold pl-6">
|
||||
<h3 className="mb-2 font-serif text-lg font-bold text-brand-blue">{point.title}</h3>
|
||||
<p className="text-gray-600 leading-relaxed text-sm">{point.desc}</p>
|
||||
</div>
|
||||
</FadeIn>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function TestimonialsSection() {
|
||||
const testimonials = [
|
||||
{ quote: "A Dra. Laura foi excepcional. Resolveu meu problema na área de família com muita empatia. Ela me manteve informada pelo WhatsApp durante todo o processo.", author: "Maria C.", role: "Cliente" },
|
||||
{ quote: "Senti muita confiança desde a primeira reunião. Ela explicou tudo sem usar aquele monte de palavras difíceis e foi muito transparente sobre os custos. Recomendo muito.", author: "Fernanda R.", role: "Cliente" },
|
||||
{ quote: "Meu processo de inventário estava travado, e em pouco tempo após ela assumir conseguimos destravar. Agradeço pela paciência e excelente trabalho.", author: "Juliana P.", role: "Cliente" }
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="bg-[#fdfbf6] py-24 sm:py-32 border-y border-amber-900/5">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<FadeIn>
|
||||
<h2 className="mb-16 text-center font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
|
||||
A opinião de quem já confia
|
||||
</h2>
|
||||
</FadeIn>
|
||||
|
||||
<div className="grid grid-cols-1 gap-8 md:grid-cols-3">
|
||||
{testimonials.map((item, i) => (
|
||||
<FadeIn key={i} delay={i * 0.1}>
|
||||
<div className="flex h-full flex-col justify-between bg-white p-8 shadow-sm border border-gray-100">
|
||||
<div>
|
||||
<div className="mb-6 text-brand-gold">
|
||||
{"★".repeat(5)}
|
||||
</div>
|
||||
<p className="mb-8 text-gray-700 font-medium italic">"{item.quote}"</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 border-t border-gray-100 pt-4">
|
||||
<div className="h-10 w-10 shrink-0 bg-gray-200 rounded-full"></div>
|
||||
<div>
|
||||
<h4 className="font-serif font-bold text-brand-blue">{item.author}</h4>
|
||||
<p className="text-xs text-gray-500 uppercase tracking-widest">{item.role}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FadeIn>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function FAQSection() {
|
||||
const faqs = [
|
||||
{
|
||||
q: "Preciso ir até o escritório para ser atendido?",
|
||||
a: "Não é necessário. Todo o atendimento pode ser realizado de forma 100% online via WhatsApp ou videoconferência. Você assina e envia os documentos pelo celular com validade jurídica."
|
||||
},
|
||||
{
|
||||
q: "A primeira consulta é cobrada?",
|
||||
a: "Avaliamos rapidamente o seu caso sem compromisso. Caso seja necessária uma análise documental profunda ou parecer jurídico detalhado, a consulta formal poderá ser cobrada, mas tudo é informado previamente."
|
||||
},
|
||||
{
|
||||
q: "Quanto custa um processo?",
|
||||
a: "Cada caso tem uma complexidade diferente. Meus honorários seguem a tabela ética da OAB e são acordados de forma clara antes de qualquer assinatura de contrato. Em muitos casos (trabalhista/previdenciário), o pagamento ocorre apenas no sucesso da ação."
|
||||
},
|
||||
{
|
||||
q: "Como saberei o andamento do meu processo?",
|
||||
a: "Você receberá atualizações periódicas por WhatsApp. Além disso, sempre que o juiz der uma movimentação importante, entrarei em contato para avisar."
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="bg-white py-24 sm:py-32">
|
||||
<div className="mx-auto max-w-3xl px-6 lg:px-8">
|
||||
<FadeIn>
|
||||
<h2 className="mb-12 text-center font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
|
||||
Perguntas Frequentes
|
||||
</h2>
|
||||
</FadeIn>
|
||||
|
||||
<div className="space-y-4">
|
||||
{faqs.map((faq, i) => (
|
||||
<FAQItem key={i} question={faq.q} answer={faq.a} delay={i * 0.1}/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function FAQItem({ question, answer, delay }: { question: string, answer: string, delay: number }) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<FadeIn delay={delay}>
|
||||
<div className="border border-gray-200 bg-white">
|
||||
<button
|
||||
className="flex w-full items-center justify-between px-6 py-4 text-left font-serif text-lg font-semibold text-brand-blue transition-colors hover:bg-gray-50"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
{question}
|
||||
<span className="ml-6 flex shrink-0 text-brand-gold">
|
||||
{isOpen ? <Minus className="h-5 w-5" /> : <Plus className="h-5 w-5" />}
|
||||
</span>
|
||||
</button>
|
||||
<AnimatePresence>
|
||||
{isOpen && (
|
||||
<motion.div
|
||||
initial={{ height: 0, opacity: 0 }}
|
||||
animate={{ height: 'auto', opacity: 1 }}
|
||||
exit={{ height: 0, opacity: 0 }}
|
||||
className="overflow-hidden"
|
||||
>
|
||||
<div className="px-6 pb-4 pt-2 text-gray-600">
|
||||
{answer}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</FadeIn>
|
||||
);
|
||||
}
|
||||
|
||||
function FinalCTASection() {
|
||||
return (
|
||||
<section className="relative overflow-hidden bg-brand-blue py-24 sm:py-32">
|
||||
<div className="absolute inset-0 z-0 opacity-20 bg-[url('https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?q=80&w=2000&auto=format&fit=crop')] bg-cover bg-center mix-blend-overlay"></div>
|
||||
<div className="relative z-10 mx-auto max-w-4xl px-6 text-center lg:px-8">
|
||||
<FadeIn>
|
||||
<Scale className="mx-auto mb-6 h-12 w-12 text-brand-gold" />
|
||||
<h2 className="mb-6 font-serif text-3xl font-bold tracking-tight text-white sm:text-5xl">
|
||||
Precisa de orientação jurídica?
|
||||
</h2>
|
||||
<p className="mx-auto mb-10 max-w-2xl text-lg text-gray-300">
|
||||
Não deixe para depois a proteção dos seus direitos e do seu patrimônio. Entre em contato agora e vamos encontrar a melhor solução para o seu caso.
|
||||
</p>
|
||||
<div className="flex flex-col gap-4 justify-center sm:flex-row">
|
||||
<a
|
||||
href="https://wa.me/5511999999999"
|
||||
className="inline-flex items-center justify-center gap-2 bg-brand-gold px-8 py-4 font-semibold text-white transition-colors hover:bg-white hover:text-brand-blue"
|
||||
>
|
||||
Falar pelo WhatsApp
|
||||
</a>
|
||||
<Link
|
||||
to="/contato"
|
||||
className="inline-flex items-center justify-center gap-2 border border-white/50 px-8 py-4 font-semibold text-white transition-colors hover:bg-white/10"
|
||||
>
|
||||
Enviar e-mail
|
||||
</Link>
|
||||
</div>
|
||||
</FadeIn>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
75
Template-02/src/pages/Sobre.tsx
Normal file
75
Template-02/src/pages/Sobre.tsx
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import React from 'react';
|
||||
import { CheckCircle2 } from 'lucide-react';
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
const FadeIn = ({ children, delay = 0 }: { children: React.ReactNode, delay?: number }) => (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
transition={{ duration: 0.6, delay }}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
export function Sobre() {
|
||||
return (
|
||||
<main className="w-full bg-white pb-24 pt-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 items-center gap-y-16 gap-x-16 lg:grid-cols-2">
|
||||
<FadeIn>
|
||||
<div className="relative">
|
||||
<div className="aspect-[3/4] w-full overflow-hidden bg-gray-100 lg:max-w-md rounded-t-full">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1573497019940-1c28c88b4f3e?auto=format&fit=crop&q=80&w=800"
|
||||
alt="Dra. Laura Silva em seu escritório"
|
||||
className="h-full w-full object-cover grayscale-[10%]"
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute -left-4 -bottom-4 h-32 w-32 bg-brand-gold opacity-20 -z-10 rounded-full"></div>
|
||||
</div>
|
||||
</FadeIn>
|
||||
|
||||
<FadeIn delay={0.2}>
|
||||
<div className="max-w-xl">
|
||||
<h1 className="mb-4 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
|
||||
Dra. Laura Silva
|
||||
</h1>
|
||||
<p className="mb-6 text-sm font-medium tracking-widest text-brand-gold uppercase">
|
||||
Especialista & Fundadora
|
||||
</p>
|
||||
<div className="mb-8 space-y-4 text-lg text-gray-600">
|
||||
<p>
|
||||
Com mais de uma década de dedicação exclusiva à advocacia moderna, meu objetivo sempre foi o mesmo: entregar resultados concretos com um atendimento pautado pela escuta ativa, empatia e proximidade.
|
||||
</p>
|
||||
<p>
|
||||
Acredito que a advocacia de excelência é baseada na confiança mútua e na sensibilidade. Por isso, não sou apenas uma intermediária em processos. Sou uma parceira estratégica focada em defender seus direitos de forma inteligente. Diferente dos grandes escritórios onde você é apenas um número, aqui o seu caso é tratado diretamente por mim, sempre.
|
||||
</p>
|
||||
<p>
|
||||
Minha abordagem é acolhedora na escuta e pragmática na solução. Seja na esfera familiar, civil ou do consumidor, meu compromisso é garantir que os direitos dos meus clientes sejam integralmente respeitados, com ética e determinação.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ul className="mb-10 space-y-4">
|
||||
{[
|
||||
"Formação em Direito pela Universidade de São Paulo (USP)",
|
||||
"Pós-graduada em Direito de Família e Sucessões",
|
||||
"Atuação estratégica focada no acolhimento e acordo",
|
||||
"Atendimento 100% humanizado e direcionado",
|
||||
"Membro da Ordem dos Advogados do Brasil – Seção São Paulo",
|
||||
"Presidente da comissão regional de defesa da mulher"
|
||||
].map((item, i) => (
|
||||
<li key={i} className="flex items-start gap-3 text-gray-700">
|
||||
<CheckCircle2 className="mt-1 h-5 w-5 shrink-0 text-brand-gold" />
|
||||
<span>{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</FadeIn>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
26
Template-02/tsconfig.json
Normal file
26
Template-02/tsconfig.json
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"experimentalDecorators": true,
|
||||
"useDefineForClassFields": false,
|
||||
"module": "ESNext",
|
||||
"lib": [
|
||||
"ES2022",
|
||||
"DOM",
|
||||
"DOM.Iterable"
|
||||
],
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"allowJs": true,
|
||||
"jsx": "react-jsx",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./*"
|
||||
]
|
||||
},
|
||||
"allowImportingTsExtensions": true,
|
||||
"noEmit": true
|
||||
}
|
||||
}
|
||||
24
Template-02/vite.config.ts
Normal file
24
Template-02/vite.config.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import tailwindcss from '@tailwindcss/vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import path from 'path';
|
||||
import {defineConfig, loadEnv} from 'vite';
|
||||
|
||||
export default defineConfig(({mode}) => {
|
||||
const env = loadEnv(mode, '.', '');
|
||||
return {
|
||||
plugins: [react(), tailwindcss()],
|
||||
define: {
|
||||
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY),
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, '.'),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
// HMR is disabled in AI Studio via DISABLE_HMR env var.
|
||||
// Do not modifyâfile watching is disabled to prevent flickering during agent edits.
|
||||
hmr: process.env.DISABLE_HMR !== 'true',
|
||||
},
|
||||
};
|
||||
});
|
||||
Loading…
Reference in a new issue