Initial commit
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import React from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import Navigation from '@/components/Navigation';
|
||||
import Footer from '@/components/Footer';
|
||||
import ScrollToTop from '@/components/ScrollToTop';
|
||||
import Home from '@/pages/Home';
|
||||
import Services from '@/pages/Services';
|
||||
import About from '@/pages/About';
|
||||
import Projects from '@/pages/Projects';
|
||||
import Contact from '@/pages/Contact';
|
||||
import { Toaster } from '@/components/ui/toaster';
|
||||
|
||||
function App() {
|
||||
const websiteUrl = typeof window !== 'undefined' ? window.location.origin : 'https://valea-inversiones.com';
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<ScrollToTop />
|
||||
<Helmet>
|
||||
<title>VALEA Inversiones - Soluciones Integrales en Ingeniería Industrial</title>
|
||||
<meta name="description" content="VALEA Inversiones ofrece soluciones integrales en ingeniería industrial y mantenimiento en Barcelona, Estado Anzoátegui, Venezuela." />
|
||||
<meta name="theme-color" content="#1F4E79" />
|
||||
{/* Open Graph / Facebook */}
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content={websiteUrl} />
|
||||
<meta property="og:title" content="VALEA Inversiones" />
|
||||
<meta property="og:description" content="Soluciones Integrales en Ingeniería Industrial y Mantenimiento" />
|
||||
<meta property="og:image" content="https://images.unsplash.com/photo-1675264481024-74e1dcde5d3e" />
|
||||
{/* Twitter */}
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:url" content={websiteUrl} />
|
||||
<meta name="twitter:title" content="VALEA Inversiones" />
|
||||
<meta name="twitter:description" content="Soluciones Integrales en Ingeniería Industrial y Mantenimiento" />
|
||||
<meta name="twitter:image" content="https://images.unsplash.com/photo-1675264481024-74e1dcde5d3e" />
|
||||
</Helmet>
|
||||
|
||||
<div className="min-h-screen bg-azulOscuro">
|
||||
<Navigation />
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/servicios" element={<Services />} />
|
||||
<Route path="/acerca-de" element={<About />} />
|
||||
<Route path="/proyectos" element={<Projects />} />
|
||||
<Route path="/contacto" element={<Contact />} />
|
||||
</Routes>
|
||||
<Footer />
|
||||
<Toaster />
|
||||
</div>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -0,0 +1,66 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Shield, Users, Target, Award, Lightbulb, Heart } from 'lucide-react';
|
||||
|
||||
const AboutSection = () => {
|
||||
const values = [
|
||||
{ icon: Shield, title: 'Conducta Ética', description: 'Integridad y transparencia en todas nuestras acciones' },
|
||||
{ icon: Users, title: 'Enfoque al Cliente', description: 'Compromiso total con la satisfacción del cliente' },
|
||||
{ icon: Target, title: 'Liderazgo Técnico', description: 'Excelencia técnica y conocimiento especializado' },
|
||||
{ icon: Award, title: 'Calidad', description: 'Estándares superiores en cada proyecto' },
|
||||
{ icon: Lightbulb, title: 'Innovación', description: 'Soluciones creativas y tecnología de vanguardia' },
|
||||
{ icon: Heart, title: 'Responsabilidad Social', description: 'Compromiso con nuestra comunidad y entorno' }
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="relative py-16 bg-grisClaro overflow-hidden">
|
||||
<div className="relative container mx-auto px-4">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="text-center mb-16"
|
||||
>
|
||||
<h2 className="text-3xl md:text-5xl font-bold mb-6 text-azulOscuro">
|
||||
Acerca de <span className="text-azulPrincipal">VALEA</span>
|
||||
</h2>
|
||||
<div className="max-w-3xl mx-auto space-y-6 text-grisMedio">
|
||||
<p className="text-lg">
|
||||
Fundada en 2017 en Barcelona, Estado Anzoátegui, Venezuela, VALEA Inversiones se ha consolidado como líder en soluciones integrales de ingeniería industrial y mantenimiento.
|
||||
</p>
|
||||
<p className="text-lg">
|
||||
Nuestra misión es ofrecer servicios de excelencia técnica, enfocados en las necesidades específicas de nuestros clientes, garantizando calidad, seguridad y eficiencia en cada proyecto que emprendemos.
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Values Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{values.map((value, index) => {
|
||||
const Icon = value.icon;
|
||||
return (
|
||||
<motion.div
|
||||
key={value.title}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: index * 0.1 }}
|
||||
whileHover={{ scale: 1.05, y: -5 }}
|
||||
className="bg-blanco border border-azulMedio/30 rounded-xl p-6 transition-all duration-300 shadow-md hover:shadow-xl hover:border-azulPrincipal"
|
||||
>
|
||||
<div className="w-12 h-12 bg-azulClaro/30 rounded-lg flex items-center justify-center mb-4">
|
||||
<Icon className="w-6 h-6 text-azulPrincipal" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-azulOscuro mb-2">{value.title}</h3>
|
||||
<p className="text-grisMedio">{value.description}</p>
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default AboutSection;
|
||||
@@ -0,0 +1,52 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Building } from 'lucide-react';
|
||||
|
||||
const ClientsSection = () => {
|
||||
const clients = [
|
||||
'Sinovensa', 'Chevron', 'Petromiranda', 'PDVSA',
|
||||
'Pequiven', 'Supermetanol', 'Metor', 'Fertilizantes de Oriente'
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="py-16 bg-grisClaro">
|
||||
<div className="container mx-auto px-4">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="text-center mb-12"
|
||||
>
|
||||
<h2 className="text-3xl md:text-5xl font-bold mb-6 text-azulOscuro">
|
||||
Nuestros <span className="text-naranjaAccento">Clientes</span>
|
||||
</h2>
|
||||
<p className="text-lg text-grisMedio max-w-2xl mx-auto">
|
||||
Confianza de las empresas líderes del sector energético e industrial
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
|
||||
{clients.map((client, index) => (
|
||||
<motion.div
|
||||
key={client}
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
whileInView={{ opacity: 1, scale: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
className="bg-blanco border border-azulMedio/20 rounded-lg p-6 transition-all duration-300 shadow-md hover:shadow-lg flex flex-col items-center justify-center text-center"
|
||||
>
|
||||
<div className="w-12 h-12 bg-azulClaro/30 rounded-full flex items-center justify-center mb-3">
|
||||
<Building className="w-6 h-6 text-azulPrincipal" />
|
||||
</div>
|
||||
<span className="text-azulOscuro font-bold">{client}</span>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ClientsSection;
|
||||
@@ -0,0 +1,105 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Send, Loader2 } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
|
||||
const ContactForm = () => {
|
||||
const { toast } = useToast();
|
||||
const form = useRef();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [formData, setFormData] = useState({
|
||||
nombre: '', email: '', telefono: '', empresa: '', asunto: '', mensaje: ''
|
||||
});
|
||||
const [errors, setErrors] = useState({});
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors = {};
|
||||
if (!formData.nombre.trim()) newErrors.nombre = 'El nombre es requerido';
|
||||
if (!formData.email.trim()) newErrors.email = 'El email es requerido';
|
||||
else if (!/\S+@\S+\.\S+/.test(formData.email)) newErrors.email = 'Email inválido';
|
||||
if (!formData.telefono.trim()) newErrors.telefono = 'El teléfono es requerido';
|
||||
if (!formData.asunto.trim()) newErrors.asunto = 'El asunto es requerido';
|
||||
if (!formData.mensaje.trim()) newErrors.mensaje = 'El mensaje es requerido';
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData(prev => ({ ...prev, [name]: value }));
|
||||
if (errors[name]) setErrors(prev => ({ ...prev, [name]: '' }));
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
if (!validateForm()) {
|
||||
toast({ title: "Error de validación", description: "Por favor complete todos los campos requeridos.", variant: "destructive" });
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 1500));
|
||||
toast({ title: "¡Mensaje enviado!", description: "Gracias por contactarnos. Le responderemos pronto." });
|
||||
setFormData({ nombre: '', email: '', telefono: '', empresa: '', asunto: '', mensaje: '' });
|
||||
} catch (error) {
|
||||
toast({ title: "Error", description: "Hubo un problema. Intente de nuevo.", variant: "destructive" });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const inputClasses = (error) => `w-full px-4 py-3 bg-blanco border ${error ? 'border-red-500' : 'border-azulClaro'} rounded-lg text-azulOscuro placeholder-grisMedio focus:outline-none focus:ring-2 focus:ring-azulClaro focus:border-azulPrincipal transition-colors`;
|
||||
|
||||
return (
|
||||
<motion.form
|
||||
ref={form}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
onSubmit={handleSubmit}
|
||||
className="bg-grisClaro backdrop-blur-sm border border-azulMedio/30 rounded-xl p-8 shadow-xl"
|
||||
>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
|
||||
<div>
|
||||
<label htmlFor="nombre" className="block text-sm font-medium text-azulPrincipal mb-2">Nombre <span className="text-naranjaAccento">*</span></label>
|
||||
<input type="text" id="nombre" name="nombre" value={formData.nombre} onChange={handleChange} className={inputClasses(errors.nombre)} placeholder="Su nombre completo" />
|
||||
{errors.nombre && <p className="mt-1 text-sm text-red-500">{errors.nombre}</p>}
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-sm font-medium text-azulPrincipal mb-2">Email <span className="text-naranjaAccento">*</span></label>
|
||||
<input type="email" id="email" name="email" value={formData.email} onChange={handleChange} className={inputClasses(errors.email)} placeholder="correo@ejemplo.com" />
|
||||
{errors.email && <p className="mt-1 text-sm text-red-500">{errors.email}</p>}
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="telefono" className="block text-sm font-medium text-azulPrincipal mb-2">Teléfono <span className="text-naranjaAccento">*</span></label>
|
||||
<input type="tel" id="telefono" name="telefono" value={formData.telefono} onChange={handleChange} className={inputClasses(errors.telefono)} placeholder="0414-1234567" />
|
||||
{errors.telefono && <p className="mt-1 text-sm text-red-500">{errors.telefono}</p>}
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="empresa" className="block text-sm font-medium text-azulPrincipal mb-2">Empresa <span className="text-grisMedio text-xs font-normal">(Opcional)</span></label>
|
||||
<input type="text" id="empresa" name="empresa" value={formData.empresa} onChange={handleChange} className={inputClasses(false)} placeholder="Nombre de su empresa" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<label htmlFor="asunto" className="block text-sm font-medium text-azulPrincipal mb-2">Asunto <span className="text-naranjaAccento">*</span></label>
|
||||
<input type="text" id="asunto" name="asunto" value={formData.asunto} onChange={handleChange} className={inputClasses(errors.asunto)} placeholder="Asunto de su consulta" />
|
||||
{errors.asunto && <p className="mt-1 text-sm text-red-500">{errors.asunto}</p>}
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<label htmlFor="mensaje" className="block text-sm font-medium text-azulPrincipal mb-2">Mensaje <span className="text-naranjaAccento">*</span></label>
|
||||
<textarea id="mensaje" name="mensaje" value={formData.mensaje} onChange={handleChange} rows={6} className={`${inputClasses(errors.mensaje)} resize-none`} placeholder="Escriba su mensaje aquí..." />
|
||||
{errors.mensaje && <p className="mt-1 text-sm text-red-500">{errors.mensaje}</p>}
|
||||
</div>
|
||||
|
||||
<Button type="submit" variant="cta" disabled={loading} className="w-full py-6 text-lg rounded-lg shadow-lg">
|
||||
{loading ? <><Loader2 className="w-5 h-5 mr-2 animate-spin" /> Enviando...</> : <><Send className="w-5 h-5 mr-2" /> Enviar Mensaje</>}
|
||||
</Button>
|
||||
</motion.form>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactForm;
|
||||
@@ -0,0 +1,71 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Mail, Phone, MapPin } from 'lucide-react';
|
||||
|
||||
const Footer = () => {
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
return (
|
||||
<footer className="bg-azulOscuro border-t border-azulMedio">
|
||||
<div className="container mx-auto px-4 py-10">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{/* Company Info */}
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-2xl font-bold text-blanco">
|
||||
VALEA Inversiones
|
||||
</h3>
|
||||
<p className="text-azulClaro text-sm">
|
||||
Soluciones Integrales en Ingeniería Industrial y Mantenimiento
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Quick Links */}
|
||||
<div className="space-y-4">
|
||||
<h4 className="text-lg font-semibold text-blanco border-b border-azulMedio pb-2 inline-block">Enlaces Rápidos</h4>
|
||||
<nav className="flex flex-col space-y-2">
|
||||
<Link to="/" className="text-blanco hover:text-naranjaAccento transition-colors text-sm">Inicio</Link>
|
||||
<Link to="/servicios" className="text-blanco hover:text-naranjaAccento transition-colors text-sm">Servicios</Link>
|
||||
<Link to="/acerca-de" className="text-blanco hover:text-naranjaAccento transition-colors text-sm">Acerca de</Link>
|
||||
<Link to="/proyectos" className="text-blanco hover:text-naranjaAccento transition-colors text-sm">Proyectos</Link>
|
||||
<Link to="/contacto" className="text-blanco hover:text-naranjaAccento transition-colors text-sm">Contacto</Link>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{/* Contact Info */}
|
||||
<div className="space-y-4">
|
||||
<h4 className="text-lg font-semibold text-blanco border-b border-azulMedio pb-2 inline-block">Contacto</h4>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-start space-x-3">
|
||||
<Mail className="w-5 h-5 text-azulClaro mt-0.5 flex-shrink-0" />
|
||||
<div className="text-blanco text-sm">
|
||||
<a href="mailto:valeainversiones@gmail.com" className="hover:text-naranjaAccento">valeainversiones@gmail.com</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start space-x-3">
|
||||
<Phone className="w-5 h-5 text-azulClaro mt-0.5 flex-shrink-0" />
|
||||
<div className="text-blanco text-sm">
|
||||
<span>0414-7748343</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start space-x-3">
|
||||
<MapPin className="w-5 h-5 text-azulClaro mt-0.5 flex-shrink-0" />
|
||||
<span className="text-blanco text-sm">
|
||||
Barcelona, Estado Anzoátegui, Venezuela
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Copyright */}
|
||||
<div className="mt-12 pt-8 border-t border-azulMedio">
|
||||
<p className="text-center text-blanco text-sm">
|
||||
© {currentYear} VALEA Inversiones. Todos los derechos reservados.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
@@ -0,0 +1,82 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { ArrowRight } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const Hero = () => {
|
||||
return (
|
||||
<section className="relative h-screen flex items-center justify-center overflow-hidden">
|
||||
{/* Background Image */}
|
||||
<div className="absolute inset-0">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1685302910286-59273f5aef4b"
|
||||
alt="Industrial background"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-azulOscuro/90 via-azulOscuro/80 to-azulOscuro/90" />
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-azulOscuro/80 to-transparent" />
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="relative container mx-auto px-4 z-10">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<motion.h1
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.2 }}
|
||||
className="text-4xl md:text-6xl lg:text-7xl font-bold mb-6"
|
||||
>
|
||||
<span className="text-blanco">
|
||||
VALEA Inversiones
|
||||
</span>
|
||||
</motion.h1>
|
||||
|
||||
<motion.p
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.4 }}
|
||||
className="text-xl md:text-2xl lg:text-3xl text-grisClaro mb-8 font-light"
|
||||
>
|
||||
Soluciones Integrales en <span className="text-azulClaro font-semibold">Ingeniería Industrial</span> y Mantenimiento
|
||||
</motion.p>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.6 }}
|
||||
>
|
||||
<Link to="/contacto">
|
||||
<Button
|
||||
variant="cta"
|
||||
size="lg"
|
||||
className="px-8 py-6 text-lg rounded-full shadow-lg transition-all duration-300 group"
|
||||
>
|
||||
Contáctenos
|
||||
<ArrowRight className="ml-2 w-5 h-5 group-hover:translate-x-1 transition-transform" />
|
||||
</Button>
|
||||
</Link>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Scroll Indicator */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 1, delay: 1 }}
|
||||
className="absolute bottom-8 left-1/2 transform -translate-x-1/2"
|
||||
>
|
||||
<motion.div
|
||||
animate={{ y: [0, 10, 0] }}
|
||||
transition={{ duration: 1.5, repeat: Infinity }}
|
||||
className="w-6 h-10 border-2 border-azulClaro rounded-full flex items-start justify-center p-2"
|
||||
>
|
||||
<motion.div className="w-1.5 h-1.5 bg-naranjaAccento rounded-full" />
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Hero;
|
||||
@@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
|
||||
const Logo = ({ className = '', width = 40, height = 40 }) => {
|
||||
return (
|
||||
<svg
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox="0 0 100 100"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
aria-label="VALEA Inversiones Logo"
|
||||
>
|
||||
<defs>
|
||||
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style={{ stopColor: '#60A5FA', stopOpacity: 1 }} />
|
||||
<stop offset="100%" style={{ stopColor: '#2563EB', stopOpacity: 1 }} />
|
||||
</linearGradient>
|
||||
<linearGradient id="grad2" x1="100%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style={{ stopColor: '#3B82F6', stopOpacity: 1 }} />
|
||||
<stop offset="100%" style={{ stopColor: '#1E40AF', stopOpacity: 1 }} />
|
||||
</linearGradient>
|
||||
<linearGradient id="grad3" x1="0%" y1="100%" x2="100%" y2="0%">
|
||||
<stop offset="0%" style={{ stopColor: '#93C5FD', stopOpacity: 1 }} />
|
||||
<stop offset="100%" style={{ stopColor: '#3B82F6', stopOpacity: 1 }} />
|
||||
</linearGradient>
|
||||
<linearGradient id="grad4" x1="100%" y1="100%" x2="0%" y2="0%">
|
||||
<stop offset="0%" style={{ stopColor: '#1E3A8A', stopOpacity: 1 }} />
|
||||
<stop offset="100%" style={{ stopColor: '#1E40AF', stopOpacity: 1 }} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
{/* Top Left Segment */}
|
||||
<path d="M50 5 L25 30 L50 55 L75 30 Z" fill="url(#grad3)" opacity="0.9" />
|
||||
|
||||
{/* Bottom Right Segment */}
|
||||
<path d="M50 95 L75 70 L50 45 L25 70 Z" fill="url(#grad1)" />
|
||||
|
||||
{/* Top Right Segment (Darker for depth) */}
|
||||
<path d="M75 30 L95 50 L70 75 L50 55 Z" fill="url(#grad2)" />
|
||||
|
||||
{/* Bottom Left Segment (Darkest for shadow) */}
|
||||
<path d="M25 70 L5 50 L30 25 L50 45 Z" fill="url(#grad4)" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default Logo;
|
||||
@@ -0,0 +1,90 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Menu, X } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
const Navigation = () => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const location = useLocation();
|
||||
const navLinks = [{
|
||||
path: '/',
|
||||
label: 'Inicio'
|
||||
}, {
|
||||
path: '/servicios',
|
||||
label: 'Servicios'
|
||||
}, {
|
||||
path: '/acerca-de',
|
||||
label: 'Acerca de'
|
||||
}, {
|
||||
path: '/proyectos',
|
||||
label: 'Proyectos'
|
||||
}, {
|
||||
path: '/contacto',
|
||||
label: 'Contacto'
|
||||
}];
|
||||
|
||||
const isActive = path => location.pathname === path;
|
||||
|
||||
return (
|
||||
<nav className="fixed top-0 left-0 right-0 z-50 bg-azulOscuro/95 backdrop-blur-sm border-b border-azulMedio/30">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="flex items-center justify-between h-20">
|
||||
{/* Logo */}
|
||||
<Link to="/" className="flex items-center space-x-3 group">
|
||||
<motion.div whileHover={{ scale: 1.05, rotate: 2 }} transition={{ type: "spring", stiffness: 300 }} className="relative flex items-center justify-center">
|
||||
<img src="https://horizons-cdn.hostinger.com/f282ce11-7db1-4077-b550-1caec7979db1/37ca80aa9cc5a842ca701b01a0ba6065.png" alt="VALEA Inversiones Logo" className="h-12 w-auto object-contain drop-shadow-md" />
|
||||
</motion.div>
|
||||
</Link>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
<div className="hidden md:flex items-center space-x-1">
|
||||
{navLinks.map(link => (
|
||||
<Link key={link.path} to={link.path}>
|
||||
<Button variant="ghost" className={`relative px-4 py-2 transition-colors ${isActive(link.path) ? 'text-azulPrincipal' : 'text-blanco hover:text-azulPrincipal hover:bg-azulOscuro/50'}`}>
|
||||
{link.label}
|
||||
{isActive(link.path) && (
|
||||
<motion.div layoutId="activeNav" className="absolute bottom-0 left-0 right-0 h-0.5 bg-naranjaAccento" transition={{ type: 'spring', stiffness: 380, damping: 30 }} />
|
||||
)}
|
||||
</Button>
|
||||
</Link>
|
||||
))}
|
||||
<Link to="/contacto">
|
||||
<Button variant="cta" className="ml-4 rounded-full">
|
||||
Solicitar Cotización
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu Button */}
|
||||
<button onClick={() => setIsOpen(!isOpen)} className="md:hidden p-2 text-blanco hover:text-azulPrincipal transition-colors">
|
||||
{isOpen ? <X size={24} /> : <Menu size={24} />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Navigation */}
|
||||
<AnimatePresence>
|
||||
{isOpen && (
|
||||
<motion.div initial={{ opacity: 0, height: 0 }} animate={{ opacity: 1, height: 'auto' }} exit={{ opacity: 0, height: 0 }} className="md:hidden bg-azulOscuro border-t border-azulMedio/30">
|
||||
<div className="container mx-auto px-4 py-4 space-y-2">
|
||||
{navLinks.map(link => (
|
||||
<Link key={link.path} to={link.path} onClick={() => setIsOpen(false)}>
|
||||
<motion.div whileHover={{ x: 4 }} className={`px-4 py-3 rounded-lg transition-colors ${isActive(link.path) ? 'bg-azulPrincipal/40 text-blanco' : 'text-blanco hover:bg-azulPrincipal/20 hover:text-blanco'}`}>
|
||||
{link.label}
|
||||
</motion.div>
|
||||
</Link>
|
||||
))}
|
||||
<Link to="/contacto" onClick={() => setIsOpen(false)}>
|
||||
<motion.div className="px-4 py-3 mt-4 rounded-lg bg-naranjaAccento text-blanco text-center font-medium">
|
||||
Solicitar Cotización
|
||||
</motion.div>
|
||||
</Link>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
export default Navigation;
|
||||
@@ -0,0 +1,68 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Building2, CheckCircle2 } from 'lucide-react';
|
||||
|
||||
const ProjectsSection = () => {
|
||||
const projects = [
|
||||
{ title: 'Mantenimiento Integral Planta Petroquímica', description: 'Mantenimiento preventivo y correctivo de equipos críticos en planta de procesamiento', client: 'Sinovensa' },
|
||||
{ title: 'Instalación Sistema de Instrumentación', description: 'Diseño e instalación de sistema completo de instrumentación y control automático', client: 'Chevron' },
|
||||
{ title: 'Saneamiento ambiental', description: 'Servicios de saneamiento ambiental y control de condiciones operativas en áreas industriales', client: 'Petromiranda' },
|
||||
{ title: 'Fabricación de Estructuras Metálicas', description: 'Diseño y fabricación de estructuras metálicas para plataformas industriales', client: 'PDVSA' },
|
||||
{ title: 'Servicio de Alquiler de Equipos', description: 'Alquiler y operación de maquinaria pesada para proyecto de expansión', client: 'Sinovensa' },
|
||||
{ title: 'Calibración de Instrumentos', description: 'Servicio de calibración y certificación de instrumentos de medición', client: 'Chevron' }
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="relative py-16 bg-azulOscuro overflow-hidden">
|
||||
{/* Background patterns */}
|
||||
<div className="absolute inset-0 opacity-10 bg-[url('https://images.unsplash.com/photo-1599277186677-fafcf8d96740')] bg-cover mix-blend-overlay"></div>
|
||||
|
||||
<div className="relative container mx-auto px-4">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="text-center mb-16"
|
||||
>
|
||||
<h2 className="text-3xl md:text-5xl font-bold mb-6 text-blanco">
|
||||
Experiencia y <span className="text-azulClaro">Proyectos</span>
|
||||
</h2>
|
||||
<p className="text-lg text-grisClaro max-w-2xl mx-auto">
|
||||
Desde 2017, hemos ejecutado exitosamente más de 200 proyectos para las principales empresas del sector industrial venezolano
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{projects.map((project, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: index * 0.1 }}
|
||||
whileHover={{ scale: 1.02, y: -5 }}
|
||||
className="bg-blanco rounded-lg p-6 border-b-4 border-naranjaAccento shadow-lg"
|
||||
>
|
||||
<div className="flex items-start space-x-3 mb-4">
|
||||
<div className="w-10 h-10 bg-azulClaro rounded-lg flex items-center justify-center flex-shrink-0">
|
||||
<Building2 className="w-5 h-5 text-azulOscuro" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-lg font-bold text-azulPrincipal mb-2">{project.title}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-grisMedio text-sm mb-4">{project.description}</p>
|
||||
<div className="flex items-center space-x-2 text-azulMedio">
|
||||
<CheckCircle2 className="w-4 h-4 text-naranjaAccento" />
|
||||
<span className="text-sm font-semibold">{project.client}</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProjectsSection;
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useLayoutEffect } from 'react';
|
||||
|
||||
const ScrollToTop = () => {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
window.scrollTo({ top: 0, left: 0, behavior: 'instant' });
|
||||
}, [pathname]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export default ScrollToTop;
|
||||
@@ -0,0 +1,71 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Wrench, Gauge, Package, Truck } from 'lucide-react';
|
||||
|
||||
const ServicesSection = () => {
|
||||
const services = [
|
||||
{
|
||||
icon: Wrench, title: 'Mantenimiento Industrial',
|
||||
description: 'Mantenimiento preventivo y correctivo, pruebas de válvulas, mantenimiento de bombas y equipos rotativos con los más altos estándares de calidad.'
|
||||
},
|
||||
{
|
||||
icon: Gauge, title: 'Instrumentación Industrial y Control',
|
||||
description: 'Instrumentación electrónica, mecánica, hidráulica y neumática. Calibración y mantenimiento de sistemas de control y medición.'
|
||||
},
|
||||
{
|
||||
icon: Package, title: 'Ingeniería, Procura y Fabricación',
|
||||
description: 'Diseño y fabricación de tuberías, válvulas y equipos. Suministro de componentes industriales y fabricación metalmecánica especializada.'
|
||||
},
|
||||
{
|
||||
icon: Truck, title: 'Alquiler de Maquinarias y Equipos',
|
||||
description: 'Alquiler de camiones de servicio, montacargas, generadores eléctricos, bombas y equipos especializados para la industria.'
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="relative py-16 bg-blanco overflow-hidden">
|
||||
<div className="relative container mx-auto px-4">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="text-center mb-16"
|
||||
>
|
||||
<h2 className="text-3xl md:text-5xl font-bold mb-6 text-azulOscuro">
|
||||
Nuestros <span className="text-azulPrincipal">Servicios</span>
|
||||
</h2>
|
||||
<p className="text-lg text-grisMedio max-w-2xl mx-auto">
|
||||
Ofrecemos soluciones integrales adaptadas a las necesidades específicas de su industria
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{services.map((service, index) => {
|
||||
const Icon = service.icon;
|
||||
return (
|
||||
<motion.div
|
||||
key={service.title}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: index * 0.1 }}
|
||||
whileHover={{ scale: 1.05, y: -5 }}
|
||||
className="bg-grisClaro border border-azulMedio/20 rounded-xl p-6 transition-all duration-300 shadow-md hover:shadow-xl hover:border-naranjaAccento"
|
||||
>
|
||||
<div className="w-16 h-16 bg-azulOscuro rounded-lg flex items-center justify-center mb-4">
|
||||
<Icon className="w-8 h-8 text-azulClaro" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-azulPrincipal mb-3">{service.title}</h3>
|
||||
<p className="text-grisMedio text-sm leading-relaxed">{service.description}</p>
|
||||
<button className="mt-4 text-naranjaAccento font-medium hover:text-azulPrincipal transition-colors text-sm">Saber más →</button>
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServicesSection;
|
||||
@@ -0,0 +1,48 @@
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Slot } from '@radix-ui/react-slot';
|
||||
import { cva } from 'class-variance-authority';
|
||||
import React from 'react';
|
||||
|
||||
const buttonVariants = cva(
|
||||
'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 text-blanco',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-azulPrincipal hover:bg-azulOscuro',
|
||||
cta: 'bg-naranjaAccento hover:bg-[#D9681A]',
|
||||
destructive:
|
||||
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
||||
outline:
|
||||
'border border-input bg-transparent hover:bg-grisClaro hover:text-azulOscuro',
|
||||
secondary:
|
||||
'bg-azulMedio hover:bg-azulPrincipal',
|
||||
ghost: 'hover:bg-azulPrincipal/20 hover:text-blanco',
|
||||
link: 'text-azulPrincipal underline-offset-4 hover:underline hover:text-naranjaAccento',
|
||||
},
|
||||
size: {
|
||||
default: 'h-10 px-4 py-2',
|
||||
sm: 'h-9 rounded-md px-3',
|
||||
lg: 'h-11 rounded-md px-8',
|
||||
icon: 'h-10 w-10',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const Button = React.forwardRef(({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : 'button';
|
||||
return (
|
||||
<Comp
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
Button.displayName = 'Button';
|
||||
|
||||
export { Button, buttonVariants };
|
||||
@@ -0,0 +1,101 @@
|
||||
import { cn } from '@/lib/utils';
|
||||
import * as ToastPrimitives from '@radix-ui/react-toast';
|
||||
import { cva } from 'class-variance-authority';
|
||||
import { X } from 'lucide-react';
|
||||
import React from 'react';
|
||||
|
||||
const ToastProvider = ToastPrimitives.Provider;
|
||||
|
||||
const ToastViewport = React.forwardRef(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Viewport
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
|
||||
|
||||
const toastVariants = cva(
|
||||
'data-[swipe=move]:transition-none group relative pointer-events-auto flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full data-[state=closed]:slide-out-to-right-full',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-background border',
|
||||
destructive:
|
||||
'group destructive border-destructive bg-destructive text-destructive-foreground',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const Toast = React.forwardRef(({ className, variant, ...props }, ref) => {
|
||||
return (
|
||||
<ToastPrimitives.Root
|
||||
ref={ref}
|
||||
className={cn(toastVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
Toast.displayName = ToastPrimitives.Root.displayName;
|
||||
|
||||
const ToastAction = React.forwardRef(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Action
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-destructive/30 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
ToastAction.displayName = ToastPrimitives.Action.displayName;
|
||||
|
||||
const ToastClose = React.forwardRef(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Close
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600',
|
||||
className,
|
||||
)}
|
||||
toast-close=""
|
||||
{...props}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</ToastPrimitives.Close>
|
||||
));
|
||||
ToastClose.displayName = ToastPrimitives.Close.displayName;
|
||||
|
||||
const ToastTitle = React.forwardRef(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Title
|
||||
ref={ref}
|
||||
className={cn('text-sm font-semibold', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
ToastTitle.displayName = ToastPrimitives.Title.displayName;
|
||||
|
||||
const ToastDescription = React.forwardRef(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Description
|
||||
ref={ref}
|
||||
className={cn('text-sm opacity-90', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
ToastDescription.displayName = ToastPrimitives.Description.displayName;
|
||||
|
||||
export {
|
||||
Toast,
|
||||
ToastAction,
|
||||
ToastClose,
|
||||
ToastDescription,
|
||||
ToastProvider,
|
||||
ToastTitle,
|
||||
ToastViewport,
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
import {
|
||||
Toast,
|
||||
ToastClose,
|
||||
ToastDescription,
|
||||
ToastProvider,
|
||||
ToastTitle,
|
||||
ToastViewport,
|
||||
} from '@/components/ui/toast';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import React from 'react';
|
||||
|
||||
export function Toaster() {
|
||||
const { toasts } = useToast();
|
||||
|
||||
return (
|
||||
<ToastProvider>
|
||||
{toasts.map(({ id, title, description, action, ...props }) => {
|
||||
return (
|
||||
<Toast key={id} {...props}>
|
||||
<div className="grid gap-1">
|
||||
{title && <ToastTitle>{title}</ToastTitle>}
|
||||
{description && (
|
||||
<ToastDescription>{description}</ToastDescription>
|
||||
)}
|
||||
</div>
|
||||
{action}
|
||||
<ToastClose />
|
||||
</Toast>
|
||||
);
|
||||
})}
|
||||
<ToastViewport />
|
||||
</ToastProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import { useState, useEffect } from "react"
|
||||
|
||||
const TOAST_LIMIT = 1
|
||||
|
||||
let count = 0
|
||||
function generateId() {
|
||||
count = (count + 1) % Number.MAX_VALUE
|
||||
return count.toString()
|
||||
}
|
||||
|
||||
const toastStore = {
|
||||
state: {
|
||||
toasts: [],
|
||||
},
|
||||
listeners: [],
|
||||
|
||||
getState: () => toastStore.state,
|
||||
|
||||
setState: (nextState) => {
|
||||
if (typeof nextState === 'function') {
|
||||
toastStore.state = nextState(toastStore.state)
|
||||
} else {
|
||||
toastStore.state = { ...toastStore.state, ...nextState }
|
||||
}
|
||||
|
||||
toastStore.listeners.forEach(listener => listener(toastStore.state))
|
||||
},
|
||||
|
||||
subscribe: (listener) => {
|
||||
toastStore.listeners.push(listener)
|
||||
return () => {
|
||||
toastStore.listeners = toastStore.listeners.filter(l => l !== listener)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const toast = ({ ...props }) => {
|
||||
const id = generateId()
|
||||
|
||||
const update = (props) =>
|
||||
toastStore.setState((state) => ({
|
||||
...state,
|
||||
toasts: state.toasts.map((t) =>
|
||||
t.id === id ? { ...t, ...props } : t
|
||||
),
|
||||
}))
|
||||
|
||||
const dismiss = () => toastStore.setState((state) => ({
|
||||
...state,
|
||||
toasts: state.toasts.filter((t) => t.id !== id),
|
||||
}))
|
||||
|
||||
toastStore.setState((state) => ({
|
||||
...state,
|
||||
toasts: [
|
||||
{ ...props, id, dismiss },
|
||||
...state.toasts,
|
||||
].slice(0, TOAST_LIMIT),
|
||||
}))
|
||||
|
||||
return {
|
||||
id,
|
||||
dismiss,
|
||||
update,
|
||||
}
|
||||
}
|
||||
|
||||
export function useToast() {
|
||||
const [state, setState] = useState(toastStore.getState())
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = toastStore.subscribe((state) => {
|
||||
setState(state)
|
||||
})
|
||||
|
||||
return unsubscribe
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const timeouts = []
|
||||
|
||||
state.toasts.forEach((toast) => {
|
||||
if (toast.duration === Infinity) {
|
||||
return
|
||||
}
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
toast.dismiss()
|
||||
}, toast.duration || 5000)
|
||||
|
||||
timeouts.push(timeout)
|
||||
})
|
||||
|
||||
return () => {
|
||||
timeouts.forEach((timeout) => clearTimeout(timeout))
|
||||
}
|
||||
}, [state.toasts])
|
||||
|
||||
return {
|
||||
toast,
|
||||
toasts: state.toasts,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-azulOscuro text-blanco;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
::selection {
|
||||
@apply bg-naranjaAccento text-blanco;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
@apply bg-azulOscuro;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
@apply bg-azulPrincipal rounded-full;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
@apply bg-azulMedio;
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.text-balance {
|
||||
text-wrap: balance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { clsx } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function cn(...inputs) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from '@/App';
|
||||
import '@/index.css';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<App />
|
||||
);
|
||||
@@ -0,0 +1,57 @@
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import AboutSection from '@/components/AboutSection';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
const About = () => {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Acerca de Nosotros - VALEA Inversiones</title>
|
||||
<meta name="description" content="Conozca la historia, misión y valores de VALEA Inversiones, líder en soluciones industriales desde 2017." />
|
||||
<meta name="theme-color" content="#1F4E79" />
|
||||
</Helmet>
|
||||
<main className="pt-20 bg-blanco">
|
||||
{/* Hero Section */}
|
||||
<section className="relative py-20 overflow-hidden bg-azulOscuro">
|
||||
<div className="absolute inset-0">
|
||||
<img src="https://images.unsplash.com/photo-1697869243487-76ab037f1f03" alt="About Us" className="w-full h-full object-cover mix-blend-overlay opacity-30" />
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-azulOscuro/95 via-azulOscuro/90 to-azulOscuro/95" />
|
||||
</div>
|
||||
<div className="relative container mx-auto px-4">
|
||||
<motion.div initial={{ opacity: 0, y: 30 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.6 }} className="text-center max-w-3xl mx-auto">
|
||||
<h1 className="text-4xl md:text-6xl font-bold mb-6 text-blanco">
|
||||
Acerca de <span className="text-naranjaAccento">VALEA</span>
|
||||
</h1>
|
||||
<p className="text-xl text-azulClaro">Excelencia técnica y compromiso con nuestros clientes desde 2017</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<AboutSection />
|
||||
|
||||
{/* Mission & Vision */}
|
||||
<section className="py-16 bg-azulOscuro text-blanco">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="grid md:grid-cols-2 gap-8">
|
||||
<motion.div initial={{ opacity: 0, x: -30 }} whileInView={{ opacity: 1, x: 0 }} viewport={{ once: true }} transition={{ duration: 0.6 }} className="bg-blanco/5 backdrop-blur-sm border border-azulMedio/30 rounded-xl p-8">
|
||||
<h2 className="text-2xl font-bold text-naranjaAccento mb-4">Nuestra Misión</h2>
|
||||
<p className="text-grisClaro leading-relaxed text-lg">
|
||||
Ofrecer servicios de excelencia técnica en ingeniería industrial y mantenimiento, enfocados en las necesidades específicas de nuestros clientes, garantizando calidad, seguridad y eficiencia en cada proyecto que emprendemos.
|
||||
</p>
|
||||
</motion.div>
|
||||
<motion.div initial={{ opacity: 0, x: 30 }} whileInView={{ opacity: 1, x: 0 }} viewport={{ once: true }} transition={{ duration: 0.6 }} className="bg-blanco/5 backdrop-blur-sm border border-azulMedio/30 rounded-xl p-8">
|
||||
<h2 className="text-2xl font-bold text-naranjaAccento mb-4">Nuestra Visión</h2>
|
||||
<p className="text-grisClaro leading-relaxed text-lg">
|
||||
Ser reconocidos como la empresa líder en soluciones integrales de ingeniería industrial en Venezuela, destacándonos por nuestra innovación, calidad de servicio y compromiso con la satisfacción de nuestros clientes.
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default About;
|
||||
@@ -0,0 +1,77 @@
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Mail, Phone, MapPin, Clock } from 'lucide-react';
|
||||
import ContactForm from '@/components/ContactForm';
|
||||
|
||||
const Contact = () => {
|
||||
const contactInfo = [
|
||||
{ icon: Mail, title: 'Email', items: ['valeainversiones@gmail.com'] },
|
||||
{ icon: Phone, title: 'Teléfono', items: ['Dayana Velásquez: 0414-7748343'] },
|
||||
{ icon: MapPin, title: 'Ubicación', items: ['Barcelona', 'Estado Anzoátegui', 'Venezuela'] },
|
||||
{ icon: Clock, title: 'Horario', items: ['Lunes - Viernes: 8:00 AM - 5:00 PM', 'Sábado: 8:00 AM - 12:00 PM'] }
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Contacto - VALEA Inversiones</title>
|
||||
<meta name="description" content="Contáctenos para solicitar información sobre nuestros servicios de ingeniería industrial y mantenimiento." />
|
||||
<meta name="theme-color" content="#1F4E79" />
|
||||
</Helmet>
|
||||
<main className="pt-20 bg-blanco">
|
||||
{/* Hero Section */}
|
||||
<section className="relative py-20 overflow-hidden bg-azulOscuro">
|
||||
<div className="absolute inset-0">
|
||||
<img src="https://images.unsplash.com/photo-1579719558505-ad4a5fee0847" alt="Contact" className="w-full h-full object-cover mix-blend-overlay opacity-30" />
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-azulOscuro/95 via-azulOscuro/90 to-azulOscuro/95" />
|
||||
</div>
|
||||
<div className="relative container mx-auto px-4">
|
||||
<motion.div initial={{ opacity: 0, y: 30 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.6 }} className="text-center max-w-3xl mx-auto">
|
||||
<h1 className="text-4xl md:text-6xl font-bold mb-6 text-blanco">
|
||||
<span className="text-naranjaAccento">Contáctenos</span>
|
||||
</h1>
|
||||
<p className="text-xl text-azulClaro">Estamos aquí para responder sus consultas y ayudarle con sus proyectos</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Contact Info & Form */}
|
||||
<section className="py-16 bg-grisClaro">
|
||||
<div className="container mx-auto px-4">
|
||||
{/* Contact Info Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-16">
|
||||
{contactInfo.map((info, index) => {
|
||||
const Icon = info.icon;
|
||||
return (
|
||||
<motion.div key={info.title} initial={{ opacity: 0, y: 30 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.6, delay: index * 0.1 }} className="bg-blanco border border-azulMedio/20 rounded-xl p-6 shadow-md hover:shadow-xl hover:border-naranjaAccento transition-all duration-300">
|
||||
<div className="w-12 h-12 bg-azulOscuro rounded-lg flex items-center justify-center mb-4">
|
||||
<Icon className="w-6 h-6 text-naranjaAccento" />
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-azulPrincipal mb-3">{info.title}</h3>
|
||||
<div className="space-y-2">
|
||||
{info.items.map((item, idx) => (
|
||||
<p key={idx} className="text-grisMedio text-sm">{item}</p>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Contact Form */}
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<motion.div initial={{ opacity: 0, y: 30 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.6 }} className="text-center mb-8">
|
||||
<h2 className="text-3xl font-bold text-azulOscuro mb-4">Envíenos un Mensaje</h2>
|
||||
<p className="text-grisMedio">Complete el formulario y nos pondremos en contacto con usted</p>
|
||||
</motion.div>
|
||||
<ContactForm />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Contact;
|
||||
@@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import Hero from '@/components/Hero';
|
||||
import AboutSection from '@/components/AboutSection';
|
||||
import ServicesSection from '@/components/ServicesSection';
|
||||
import ProjectsSection from '@/components/ProjectsSection';
|
||||
import ClientsSection from '@/components/ClientsSection';
|
||||
|
||||
const Home = () => {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>VALEA Inversiones - Soluciones Integrales en Ingeniería Industrial</title>
|
||||
<meta name="description" content="VALEA Inversiones ofrece mantenimiento industrial, instrumentación, ingeniería y alquiler de equipos en Barcelona, Venezuela desde 2017." />
|
||||
<meta name="theme-color" content="#1F4E79" />
|
||||
</Helmet>
|
||||
<main className="pt-20 bg-blanco">
|
||||
<Hero />
|
||||
<AboutSection />
|
||||
<ServicesSection />
|
||||
<ProjectsSection />
|
||||
<ClientsSection />
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
||||
@@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import ProjectsSection from '@/components/ProjectsSection';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
const Projects = () => {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Proyectos - VALEA Inversiones</title>
|
||||
<meta name="description" content="Conozca nuestros proyectos exitosos ejecutados para las principales empresas del sector industrial venezolano." />
|
||||
<meta name="theme-color" content="#1F4E79" />
|
||||
</Helmet>
|
||||
<main className="pt-20 bg-blanco">
|
||||
{/* Hero Section */}
|
||||
<section className="relative py-20 overflow-hidden bg-azulOscuro">
|
||||
<div className="absolute inset-0">
|
||||
<img src="https://images.unsplash.com/photo-1599277186677-fafcf8d96740" alt="Projects" className="w-full h-full object-cover mix-blend-overlay opacity-30" />
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-azulOscuro/95 via-azulOscuro/90 to-azulOscuro/95" />
|
||||
</div>
|
||||
<div className="relative container mx-auto px-4">
|
||||
<motion.div initial={{ opacity: 0, y: 30 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.6 }} className="text-center max-w-3xl mx-auto">
|
||||
<h1 className="text-4xl md:text-6xl font-bold mb-6 text-blanco">
|
||||
Nuestros <span className="text-naranjaAccento">Proyectos</span>
|
||||
</h1>
|
||||
<p className="text-xl text-azulClaro">Más de 200 proyectos exitosos desde 2017</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<ProjectsSection />
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Projects;
|
||||
@@ -0,0 +1,94 @@
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Wrench, Gauge, Package, Truck, CheckCircle2 } from 'lucide-react';
|
||||
|
||||
const Services = () => {
|
||||
const services = [
|
||||
{
|
||||
icon: Wrench, title: 'Mantenimiento Industrial',
|
||||
description: 'Servicios especializados de mantenimiento para mantener sus operaciones funcionando sin interrupciones.',
|
||||
features: ['Mantenimiento preventivo y correctivo', 'Pruebas de válvulas de alivio', 'Mantenimiento de bombas', 'Equipos rotativos', 'Análisis de vibraciones', 'Balanceo dinámico']
|
||||
},
|
||||
{
|
||||
icon: Gauge, title: 'Instrumentación Industrial y Control',
|
||||
description: 'Soluciones completas en instrumentación y sistemas de control automático.',
|
||||
features: ['Instrumentación electrónica', 'Instrumentación mecánica', 'Sistemas hidráulicos', 'Sistemas neumáticos', 'Calibración de instrumentos', 'Automatización de procesos']
|
||||
},
|
||||
{
|
||||
icon: Package, title: 'Ingeniería, Procura y Fabricación',
|
||||
description: 'Diseño, suministro y fabricación de componentes y sistemas industriales.',
|
||||
features: ['Diseño de tuberías', 'Fabricación de válvulas', 'Suministro de equipos', 'Fabricación metalmecánica', 'Estructuras metálicas', 'Ingeniería de detalle']
|
||||
},
|
||||
{
|
||||
icon: Truck, title: 'Alquiler de Maquinarias y Equipos',
|
||||
description: 'Amplia gama de equipos y maquinaria para sus proyectos industriales.',
|
||||
features: ['Camiones de servicio', 'Montacargas', 'Generadores eléctricos', 'Bombas industriales', 'Equipos de elevación', 'Herramientas especializadas', 'Brazo Pigman']
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Servicios - VALEA Inversiones</title>
|
||||
<meta name="description" content="Descubra nuestros servicios de mantenimiento industrial, instrumentación, ingeniería y alquiler de equipos." />
|
||||
<meta name="theme-color" content="#1F4E79" />
|
||||
</Helmet>
|
||||
<main className="pt-20 bg-grisClaro">
|
||||
{/* Hero Section */}
|
||||
<section className="relative py-20 overflow-hidden bg-azulOscuro">
|
||||
<div className="absolute inset-0">
|
||||
<img src="https://images.unsplash.com/photo-1610891015188-5369212db097" alt="Services" className="w-full h-full object-cover mix-blend-overlay opacity-30" />
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-azulOscuro/95 via-azulOscuro/90 to-azulOscuro/95" />
|
||||
</div>
|
||||
<div className="relative container mx-auto px-4">
|
||||
<motion.div initial={{ opacity: 0, y: 30 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.6 }} className="text-center max-w-3xl mx-auto">
|
||||
<h1 className="text-4xl md:text-6xl font-bold mb-6 text-blanco">
|
||||
Nuestros <span className="text-naranjaAccento">Servicios</span>
|
||||
</h1>
|
||||
<p className="text-xl text-azulClaro">Soluciones integrales y especializadas para la industria</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Services Details */}
|
||||
<section className="py-16">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="space-y-16">
|
||||
{services.map((service, index) => {
|
||||
const Icon = service.icon;
|
||||
return (
|
||||
<motion.div key={service.title} initial={{ opacity: 0, y: 30 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.6, delay: index * 0.1 }} className="bg-blanco border-l-4 border-azulPrincipal rounded-r-xl p-8 md:p-12 shadow-xl">
|
||||
<div className="grid md:grid-cols-2 gap-8 items-start">
|
||||
<div>
|
||||
<div className="w-16 h-16 bg-azulClaro/30 rounded-lg flex items-center justify-center mb-6">
|
||||
<Icon className="w-8 h-8 text-azulPrincipal" />
|
||||
</div>
|
||||
<h2 className="text-3xl font-bold text-azulOscuro mb-4">{service.title}</h2>
|
||||
<p className="text-grisMedio text-lg">{service.description}</p>
|
||||
<button className="mt-6 bg-naranjaAccento text-blanco px-6 py-2 rounded font-medium hover:bg-opacity-90 transition">
|
||||
Solicitar Servicio
|
||||
</button>
|
||||
</div>
|
||||
<div className="space-y-3 bg-grisClaro p-6 rounded-lg">
|
||||
<h3 className="font-bold text-azulOscuro mb-4">Incluye:</h3>
|
||||
{service.features.map((feature, idx) => (
|
||||
<motion.div key={idx} initial={{ opacity: 0, x: 20 }} whileInView={{ opacity: 1, x: 0 }} viewport={{ once: true }} transition={{ duration: 0.4, delay: idx * 0.1 }} className="flex items-start space-x-3">
|
||||
<CheckCircle2 className="w-5 h-5 text-naranjaAccento mt-1 flex-shrink-0" />
|
||||
<span className="text-azulOscuro font-medium">{feature}</span>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Services;
|
||||
Reference in New Issue
Block a user