cambios
This commit is contained in:
1 parent
9dfced04f1
commit
d7f584059e
12 files changed
+524
-30
No files matched your search
@@ -0,0 +1,162 @@
|
||||
"use client";
|
||||
import { useState, useEffect } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import Loader from "../components/Loader";
|
||||
import { usePortfolio } from "../services/portfolio";
|
||||
import { assetUrl } from "../services/api";
|
||||
import type { ThemeProps } from "../types";
|
||||
|
||||
function PortfolioInner({ isNavyBg }: ThemeProps) {
|
||||
const { data, isLoading } = usePortfolio();
|
||||
|
||||
const bg = isNavyBg ? "#080c14" : "#f0efec";
|
||||
const text = isNavyBg ? "#ffffff" : "#0a0a0a";
|
||||
const sub = isNavyBg ? "rgba(255,255,255,0.45)" : "#6a6866";
|
||||
const border = isNavyBg ? "rgba(255,255,255,0.08)" : "rgba(0,0,0,0.08)";
|
||||
const card = isNavyBg ? "rgba(255,255,255,0.03)" : "#ffffff";
|
||||
const tagBg = isNavyBg ? "rgba(255,255,255,0.07)" : "rgba(0,0,0,0.05)";
|
||||
|
||||
const projects = data ?? [];
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<section style={{ background: bg, borderBottom: `1px solid ${border}` }} className="px-8 py-28 md:px-14">
|
||||
<Loader className="py-20" />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
if (projects.length === 0) return null;
|
||||
|
||||
return (
|
||||
<section
|
||||
id="portafolio"
|
||||
aria-labelledby="portfolio-heading"
|
||||
style={{ background: bg, borderBottom: `1px solid ${border}` }}
|
||||
className="px-8 py-28 md:px-14 transition-colors duration-500"
|
||||
>
|
||||
<div className="mx-auto max-w-7xl">
|
||||
<motion.div
|
||||
className="mb-16"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, ease: [0.22, 1, 0.36, 1] }}
|
||||
>
|
||||
<span
|
||||
className="mb-3 block text-xs font-semibold uppercase tracking-[0.14em]"
|
||||
style={{ color: "#3b82f6" }}
|
||||
>
|
||||
Casos de éxito
|
||||
</span>
|
||||
<h2
|
||||
id="portfolio-heading"
|
||||
className="text-headline"
|
||||
style={{ maxWidth: "28ch", color: text }}
|
||||
>
|
||||
Construimos el sistema. <span style={{ color: "#3b82f6" }}>Tú operas el negocio.</span>
|
||||
</h2>
|
||||
<p className="mt-4 max-w-xl text-base leading-relaxed" style={{ color: sub }}>
|
||||
Proyectos reales, resultados medibles. Cada implementación parte de entender la operación antes de escribir una sola línea de código.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
|
||||
{projects.map((p, i) => {
|
||||
const cover = p.images.find((img) => img.isCover) ?? p.images.sort((a, b) => a.order - b.order)[0];
|
||||
return (
|
||||
<motion.article
|
||||
key={p.id}
|
||||
initial={{ opacity: 0, y: 24 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-40px" }}
|
||||
transition={{ duration: 0.55, delay: i * 0.1, ease: [0.22, 1, 0.36, 1] }}
|
||||
style={{
|
||||
borderRadius: 14,
|
||||
border: `1px solid ${border}`,
|
||||
background: card,
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
{/* Cover image */}
|
||||
<div
|
||||
style={{
|
||||
aspectRatio: "16/9",
|
||||
overflow: "hidden",
|
||||
background: isNavyBg ? "rgba(255,255,255,0.04)" : "rgba(0,0,0,0.03)",
|
||||
borderBottom: `1px solid ${border}`,
|
||||
}}
|
||||
>
|
||||
{cover ? (
|
||||
<img
|
||||
src={assetUrl(cover.url)}
|
||||
alt={p.title}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }}
|
||||
/>
|
||||
) : (
|
||||
<div style={{ width: "100%", height: "100%", display: "flex", alignItems: "center", justifyContent: "center" }}>
|
||||
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke={sub} strokeWidth="1.5">
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" />
|
||||
<circle cx="8.5" cy="8.5" r="1.5" />
|
||||
<path d="m21 15-5-5L5 21" />
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div style={{ padding: "24px 24px 28px", display: "flex", flexDirection: "column", flex: 1 }}>
|
||||
{p.clientCompanyName && (
|
||||
<span className="text-xs font-semibold uppercase tracking-wider mb-2 block" style={{ color: "#3b82f6" }}>
|
||||
{p.clientCompanyName}
|
||||
</span>
|
||||
)}
|
||||
<h3
|
||||
className="font-display font-bold mb-3"
|
||||
style={{ fontSize: "1.05rem", color: text, letterSpacing: "-0.01em" }}
|
||||
>
|
||||
{p.title}
|
||||
</h3>
|
||||
{p.publicSummary && (
|
||||
<p className="text-sm leading-relaxed mb-5" style={{ color: sub }}>
|
||||
{p.publicSummary}
|
||||
</p>
|
||||
)}
|
||||
{p.technologies.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2 mt-auto">
|
||||
{p.technologies.map((t) => (
|
||||
<span
|
||||
key={t.id}
|
||||
style={{
|
||||
fontSize: 10.5,
|
||||
fontWeight: 600,
|
||||
padding: "3px 9px",
|
||||
borderRadius: 99,
|
||||
background: tagBg,
|
||||
color: t.color ?? sub,
|
||||
letterSpacing: "0.01em",
|
||||
}}
|
||||
>
|
||||
{t.name}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</motion.article>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export const Portfolio = (props: ThemeProps) => {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
useEffect(() => setMounted(true), []);
|
||||
if (!mounted) return null;
|
||||
return <PortfolioInner {...props} />;
|
||||
};
|
||||
Reference in new issue
Block a user