Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added public/img/jacquie-cover.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 7 additions & 7 deletions scripts/verify-prerender.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const expectedRoutes = [
content: "Algunos trabajos",
metadataHash:
"e71cd38df788eae0835cd411fed71d20e75aec9e2b8659841559758753416cc7",
noJavaScriptMarkers: 9,
noJavaScriptMarkers: 10,
},
{
pathname: "/portfolio/lem-box",
Expand Down Expand Up @@ -90,7 +90,7 @@ const expectedRoutes = [
content: "Some Work",
metadataHash:
"c1ac372e3b0249fa16ff041e2e97b7bde63aa062a851f287ea2aebec92d0549b",
noJavaScriptMarkers: 9,
noJavaScriptMarkers: 10,
},
{
pathname: "/en/portfolio/lem-box",
Expand Down Expand Up @@ -569,7 +569,7 @@ function assertNoJavaScriptOnlyControls(html, { lang, pathname }) {
button.includes('aria-expanded="false"') &&
button.includes(`aria-label="${detailLabelPrefix}`),
);
assert.equal(detailButtons.length, 7, `${pathname}: detail toggle count`);
assert.equal(detailButtons.length, 8, `${pathname}: detail toggle count`);
assert.ok(
detailButtons.every((button) =>
button.includes('data-nojs-hide="true"')
Expand All @@ -585,12 +585,12 @@ function assertNoJavaScriptOnlyControls(html, { lang, pathname }) {
html,
lang === "es" ? ">Ver más</button>" : ">View details</button>",
),
7,
8,
`${pathname}: localized detail toggle copy`,
);
assert.equal(
count(html, 'id="portfolio-case-'),
8,
9,
`${pathname}: prerendered Portfolio cards`,
);
assert.ok(
Expand All @@ -601,7 +601,7 @@ function assertNoJavaScriptOnlyControls(html, { lang, pathname }) {
);
assert.equal(
count(html, 'data-nojs-hide="true"'),
10,
11,
`${pathname}: approved no-JavaScript hide markers`,
);
assert.equal(nativeSubmitButtons.length, 0, `${pathname}: no form submit`);
Expand Down Expand Up @@ -1224,7 +1224,7 @@ for (const artifact of expectedNotFoundArtifacts) {
}
assert.equal(new Set(notFoundDocuments).size, expectedNotFoundArtifacts.length);
assert.equal(noJavaScriptDocuments.length, 16);
assert.equal(prerenderedNoJavaScriptMarkerTotal, 64);
assert.equal(prerenderedNoJavaScriptMarkerTotal, 66);

const expectedHtmlArtifacts = [
...expectedRoutes.map((route) => route.file),
Expand Down
3 changes: 2 additions & 1 deletion src/Components/PortfolioSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export default function PortfolioSection() {
category: portfolioCase.content[language].tags[0],
key: portfolioCase.key,
cover: portfolioCase.cover,
coverAlt: portfolioCase.coverAlt?.[language] ?? "",
coverFit: getPortfolioCoverFit(portfolioCase.responsiveCover),
responsiveCover: portfolioCase.responsiveCover,
status: portfolioCase.content[language].status,
Expand Down Expand Up @@ -106,7 +107,7 @@ export default function PortfolioSection() {
<div className="aspect-[2/1] shrink-0 overflow-hidden bg-white">
<PortfolioCoverImage
cover={p.cover}
alt=""
alt={p.coverAlt}
className={`h-full w-full ${
p.coverFit === "cover"
? "object-cover"
Expand Down
4 changes: 3 additions & 1 deletion src/Components/portfolio/PortfolioCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export function PortfolioCoverImage({
interface PortfolioCardProps {
actions: ReactNode;
cover: string;
coverAlt?: string;
desc: string;
details: ReactNode;
headingLevel?: "h2" | "h3";
Expand All @@ -83,6 +84,7 @@ interface PortfolioCardProps {
export default function PortfolioCard({
actions,
cover,
coverAlt = "",
desc,
details,
headingLevel = "h3",
Expand Down Expand Up @@ -110,7 +112,7 @@ export default function PortfolioCard({
<div className="w-full p-4 sm:p-5 rounded-2xl bg-white overflow-hidden flex items-center justify-center">
<PortfolioCoverImage
cover={cover}
alt=""
alt={coverAlt}
className={`w-full h-auto max-w-[280px] sm:max-w-[360px] lg:max-w-none aspect-[40/21] object-center rounded-xl ${
coverFit === "cover" ? "object-cover" : "object-contain"
}`}
Expand Down
Binary file added src/assets/portfolio/jacquie/jacquie-1200.avif
Binary file not shown.
Binary file added src/assets/portfolio/jacquie/jacquie-1200.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/portfolio/jacquie/jacquie-480.avif
Binary file not shown.
Binary file added src/assets/portfolio/jacquie/jacquie-480.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/portfolio/jacquie/jacquie-768.avif
Binary file not shown.
Binary file added src/assets/portfolio/jacquie/jacquie-768.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/data/portfolio/cases/boating.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const integrationsEn = integrationsEs;

export const boatingCase = definePortfolioCase({
key: "boating",
portfolioOrder: 6,
portfolioOrder: 7,
category: "web",
cover: "/img/Fondo.jpg",
actions: [
Expand Down
2 changes: 1 addition & 1 deletion src/data/portfolio/cases/campingsDemo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const integrationsEn = [

export const campingsDemoCase = definePortfolioCase({
key: "campings_demo",
portfolioOrder: 7,
portfolioOrder: 8,
category: "systems",
cover: "/img/campings-concept-cover.jpg",
responsiveCover: campingsResponsiveCover,
Expand Down
9 changes: 1 addition & 8 deletions src/data/portfolio/cases/esteban.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const integrationsEn = integrationsEs;

export const estebanCase = definePortfolioCase({
key: "esteban",
portfolioOrder: 2,
portfolioOrder: 3,
category: "web",
cover: "/img/esteban.png",
responsiveCover: estebanResponsiveCover,
Expand Down Expand Up @@ -118,11 +118,4 @@ export const estebanCase = definePortfolioCase({
},
},
},
home: {
order: 2,
summary: {
"es": "Real estate · Landing, catálogo y SEO técnico para captar leads de preconstrucción.",
"en": "Real estate · Landing, catalog and technical SEO to capture pre-construction leads."
},
},
});
2 changes: 1 addition & 1 deletion src/data/portfolio/cases/federico.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const integrationsEn = integrationsEs;

export const federicoCase = definePortfolioCase({
key: "federico",
portfolioOrder: 5,
portfolioOrder: 6,
category: "web",
cover: "/img/federico-cover.jpg",
responsiveCover: federicoResponsiveCover,
Expand Down
115 changes: 115 additions & 0 deletions src/data/portfolio/cases/jacquie.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { en, es } from "../../../i18n";
import jacquie480Avif from "../../../assets/portfolio/jacquie/jacquie-480.avif";
import jacquie480Webp from "../../../assets/portfolio/jacquie/jacquie-480.webp";
import jacquie768Avif from "../../../assets/portfolio/jacquie/jacquie-768.avif";
import jacquie768Webp from "../../../assets/portfolio/jacquie/jacquie-768.webp";
import jacquie1200Avif from "../../../assets/portfolio/jacquie/jacquie-1200.avif";
import jacquie1200Webp from "../../../assets/portfolio/jacquie/jacquie-1200.webp";
import {
definePortfolioCase,
type ResponsivePortfolioCover,
} from "../types";

const jacquieResponsiveCover = {
width: 1200,
height: 630,
fit: "cover",
sources: {
avif: [
{ src: jacquie480Avif, width: 480 },
{ src: jacquie768Avif, width: 768 },
{ src: jacquie1200Avif, width: 1200 },
],
webp: [
{ src: jacquie480Webp, width: 480 },
{ src: jacquie768Webp, width: 768 },
{ src: jacquie1200Webp, width: 1200 },
],
},
} as const satisfies ResponsivePortfolioCover;

const stack = [
"Next.js 15",
"React 19",
"TypeScript",
"Tailwind CSS",
"next-intl",
"ImageKit",
"Vercel",
] as const;

export const jacquieCase = definePortfolioCase({
key: "jacquie",
portfolioOrder: 2,
category: "web",
cover: "/img/jacquie-cover.jpg",
responsiveCover: jacquieResponsiveCover,
coverAlt: {
es: "Portada del sitio de Jacquie Zárate, con su retrato editorial junto al monograma JZ, su nombre y la referencia Realtor en Florida.",
en: "Jacquie Zárate website cover, featuring her editorial portrait next to the JZ monogram, her name, and the Realtor in Florida credential.",
},
actions: [
{
href: "https://jacquiezarate.com",
label: {
es: es.portfolio.jacquie.link,
en: en.portfolio.jacquie.link,
},
},
],
content: {
es: {
title: es.portfolio.jacquie.title,
description: es.portfolio.jacquie.desc,
tags: ["Real Estate", "Trilingüe", "Next.js"],
details: {
summary:
"Sitio inmobiliario trilingüe para compradores e inversores en Miami. Incluye catálogo de preconstrucción con búsqueda, filtros, ordenamiento y carga progresiva, fichas de proyecto, propiedades publicadas, contenido de financiación y contacto directo.",
stack,
integrations: [
"WhatsApp (contacto directo)",
"Google Maps embebido (ubicación de proyectos)",
],
challenges: [
"Ordenar una oferta inmobiliaria amplia para clientes que deciden a distancia, sostener contenido real en tres idiomas y permitir explorar proyectos sin convertir la experiencia en un marketplace genérico.",
],
solution: [
"Una arquitectura estática por idioma, con catálogo filtrable, fichas reutilizables, SEO localizado, imágenes optimizadas y caminos de contacto de baja fricción.",
],
impact: [
"Sitio publicado en tres idiomas, con catálogo de preconstrucción, propiedades, orientación financiera y una experiencia responsive centrada en claridad y conversación.",
],
},
},
en: {
title: en.portfolio.jacquie.title,
description: en.portfolio.jacquie.desc,
tags: ["Real Estate", "Trilingual", "Next.js"],
details: {
summary:
"Trilingual real estate site for buyers and investors in Miami. It includes a pre-construction catalog with search, filters, sorting and progressive loading, project pages, published properties, financing content and direct contact.",
stack,
integrations: [
"WhatsApp (direct contact)",
"Embedded Google Maps (project location)",
],
challenges: [
"Organize a broad real estate offering for clients making decisions remotely, maintain real content in three languages, and let users explore projects without turning the experience into a generic marketplace.",
],
solution: [
"A static per-language architecture with a filterable catalog, reusable detail pages, localized SEO, optimized images and low-friction contact paths.",
],
impact: [
"A live site in three languages, with a pre-construction catalog, properties, financing guidance and a responsive experience focused on clarity and conversation.",
],
},
},
},
home: {
order: 2,
summary: {
es: "Sitio inmobiliario trilingüe con catálogo filtrable, SEO por idioma y contacto directo por WhatsApp.",
en: "Trilingual real estate site with a filterable catalog, per-language SEO, and direct WhatsApp contact.",
},
},
});
2 changes: 1 addition & 1 deletion src/data/portfolio/cases/magenta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const integrationsEn = [

export const magentaCase = definePortfolioCase({
key: "magenta",
portfolioOrder: 4,
portfolioOrder: 5,
category: "web",
cover: "/img/magenta-cover.png",
actions: [
Expand Down
2 changes: 1 addition & 1 deletion src/data/portfolio/cases/mutter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const integrationsEn = integrationsEs;

export const mutterCase = definePortfolioCase({
key: "mutter",
portfolioOrder: 3,
portfolioOrder: 4,
category: "ecommerce",
cover: "/img/mutter-cover.png",
actions: [
Expand Down
2 changes: 2 additions & 0 deletions src/data/portfolio/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { magentaCase } from "./cases/magenta";
import { jacquieCase } from "./cases/jacquie";
import { estebanCase } from "./cases/esteban";
import { lemBoxCase } from "./cases/lemBox";
import { zentraCase } from "./cases/zentra";
Expand All @@ -11,6 +12,7 @@ import type { Localized, ProjectCategory } from "./types";
const registeredCases = [
lemBoxCase,
zentraCase,
jacquieCase,
estebanCase,
mutterCase,
magentaCase,
Expand Down
1 change: 1 addition & 0 deletions src/data/portfolio/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ type PortfolioCaseDefinitionBase<
category: ProjectCategory;
cover: string;
responsiveCover?: ResponsivePortfolioCover;
coverAlt?: Localized<string>;
actions: readonly PortfolioAction[];
content: Localized<Content>;
home?: {
Expand Down
19 changes: 16 additions & 3 deletions src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,16 +244,20 @@ export const en = {
},
cases: {
title: "Real websites",
intro: "Some of the websites I've designed and built for businesses:",
intro: "Some of the websites I designed and developed for businesses and personal brands:",
items: [
{
name: "Esteban Firpo · Miami Real Estate",
text: "Bilingual real-estate site with a project catalog and WhatsApp integration.",
name: "Jacquie Zárate · Real Estate & Investment",
text: "Trilingual real estate site with a filterable pre-construction catalog, per-language SEO, and direct WhatsApp contact.",
},
{
name: "Mutter Games",
text: "E-commerce with a dynamic catalog and Mercado Pago checkout.",
},
{
name: "Esteban Firpo · Miami Real Estate",
text: "Bilingual real-estate site with a project catalog and WhatsApp integration.",
},
{
name: "Imprenta Magenta",
text: "Optimized catalog with a dynamic quote form. Functional MVP currently live.",
Expand All @@ -262,6 +266,10 @@ export const en = {
name: "ZENTRA Scent",
text: "In development: website and e-commerce with subscriptions, an admin panel, and inventory management.",
},
{
name: "Federico Roma",
text: "Personal and professional website with biography, video courses, photography, and exclusive products.",
},
],
note: "Each project had a different scope. In the portfolio, you can see the role and technologies used in each one.",
portfolioLink: "See these projects in the portfolio",
Expand Down Expand Up @@ -414,6 +422,11 @@ export const en = {
status: "In development",
link: "Visit site in development",
},
jacquie: {
title: "Jacquie Zárate · Real Estate & Investment",
desc: "Trilingual real estate site for buying, selling, and investing in Miami, with a filterable pre-construction catalog, property pages, financing guidance, and direct WhatsApp contact.",
link: "View website",
},
esteban: {
title: "Esteban Firpo · Miami Real Estate",
desc: "Miami preconstruction real‑estate site. Project catalog, galleries, payment plans and ES/EN pages with WhatsApp integration and optimized SEO.",
Expand Down
19 changes: 16 additions & 3 deletions src/i18n/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,16 +241,20 @@ export const es = {
},
cases: {
title: "Sitios reales",
intro: "Algunos de los sitios que diseñé y desarrollé para empresas:",
intro: "Algunos de los sitios que diseñé y desarrollé para empresas y marcas personales:",
items: [
{
name: "Esteban Firpo · Miami Real Estate",
text: "Sitio inmobiliario bilingüe con catálogo de proyectos e integración con WhatsApp.",
name: "Jacquie Zárate · Real Estate e Inversión",
text: "Sitio inmobiliario trilingüe con catálogo de preconstrucción filtrable, SEO por idioma y contacto directo por WhatsApp.",
},
{
name: "Mutter Games",
text: "E-commerce con catálogo dinámico y checkout con Mercado Pago.",
},
{
name: "Esteban Firpo · Miami Real Estate",
text: "Sitio inmobiliario bilingüe con catálogo de proyectos e integración con WhatsApp.",
},
{
name: "Imprenta Magenta",
text: "Catálogo optimizado con formulario dinámico de cotización. MVP funcional ya activo.",
Expand All @@ -259,6 +263,10 @@ export const es = {
name: "ZENTRA Scent",
text: "Proyecto en desarrollo: sitio web y e-commerce con suscripciones, panel administrativo y gestión de stock.",
},
{
name: "Federico Roma",
text: "Sitio web personal y profesional con biografía, cursos en video, fotografías y productos exclusivos.",
},
],
note: "Cada proyecto tuvo un alcance distinto. En el portfolio podés ver el rol y las tecnologías utilizadas en cada uno.",
portfolioLink: "Ver estos proyectos en el portfolio",
Expand Down Expand Up @@ -410,6 +418,11 @@ export const es = {
status: "En desarrollo",
link: "Visitar sitio en desarrollo",
},
jacquie: {
title: "Jacquie Zárate · Real Estate e Inversión",
desc: "Sitio inmobiliario trilingüe para comprar, vender e invertir en Miami, con catálogo de preconstrucción filtrable, fichas de propiedades, guía de financiación y contacto directo por WhatsApp.",
link: "Ver sitio web",
},
esteban: {
title: "Esteban Firpo · Miami Real Estate",
desc: "Sitio inmobiliario de preconstrucción en Miami. Catálogo de proyectos, galerías, planes de pago y fichas ES/EN con integración a WhatsApp y SEO optimizado.",
Expand Down
Loading
Loading