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/campings-concept-cover.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 15 additions & 1 deletion src/Components/portfolio/PortfolioCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ interface PortfolioCardProps {
desc: string;
tags: string;
expanded: boolean;
status?: string;
disclaimer?: string;
children?: ReactNode;
}

Expand All @@ -19,11 +21,13 @@ export default function PortfolioCard({
desc,
tags,
expanded,
status,
disclaimer,
children,
}: PortfolioCardProps) {
return (
<motion.div
className={`border border-gray-200 rounded-2xl shadow-sm bg-white ${expanded ? "" : "md:h-[280px] md:overflow-hidden"}`}
className={`border border-gray-200 rounded-2xl shadow-sm bg-white ${status ? "text-gray-900" : ""} ${expanded || status ? "" : "md:h-[280px] md:overflow-hidden"}`}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.45 }}
Expand All @@ -42,8 +46,18 @@ export default function PortfolioCard({
</div>
</div>
<div className="md:w-7/12 p-6">
{status && (
<p className="mb-2 inline-flex rounded-full bg-blue-50 px-3 py-1 text-xs font-semibold text-blue-800">
{status}
</p>
)}
<h3 className="text-xl font-semibold tracking-tight">{title}</h3>
<p className="text-gray-700 mt-2 max-w-[62ch]">{desc}</p>
{disclaimer && (
<p className="mt-3 max-w-[62ch] border-l-2 border-blue-200 pl-3 text-sm text-gray-700">
{disclaimer}
</p>
)}
<p className="mt-3 text-sm text-gray-600">• {tags}</p>
<div className="mt-4 flex items-center gap-4 flex-wrap">
{Children.toArray(children).filter((child) => {
Expand Down
24 changes: 12 additions & 12 deletions src/data/portfolioCases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ export type ProjectKey = "lem_web" | "lem_portal" | "esteban" | "mutter" | "fede
export type Project = { key: ProjectKey; href?: string; cover: string };

export const projects: Project[] = [
{ key: "campings_demo", href: "https://reservas-campings-nacionales.vercel.app", cover: "/img/parques-nacionales-logo.png" },
{ key: "magenta", href: "https://magenta-paysandu-m5in.vercel.app", cover: "/img/magenta-cover.png" },
{ key: "esteban", href: "https://estebanfirpo.com", cover: "/img/esteban.png" },
{ key: "lem_web", href: "https://lem-box.com.uy", cover: "/img/lem-box-cover.png" },
{ key: "lem_portal", href: "https://portal.lem-box.com", cover: "/img/lem-box-cover.png" },
{ key: "mutter", href: "https://www.muttergames.com", cover: "/img/mutter-cover.png" },
{ key: "federico", href: "https://www.federicoroma.com", cover: "/img/federico-cover.jpg" },
{ key: "boating", href: "https://www.boatingadventuresmiami.com", cover: "/img/Fondo.jpg" },
{ key: "campings_demo", href: "https://github.com/devrodri-com/reservas-campings-nacionales", cover: "/img/campings-concept-cover.jpg" },
];

export type Category = "all" | "ecom" | "personal" | "services";
Expand All @@ -28,7 +28,7 @@ export const projectMeta: Record<ProjectKey, { category: Category; tags: string[
federico: { category: "personal", tags: ["Marca personal", "Cursos"], tagsEn: ["Personal brand", "Courses"] },
magenta: { category: "services", tags: ["Imprenta", "Next.js"], tagsEn: ["Print shop", "Next.js"] },
boating: { category: "services", tags: ["Servicios", "Reservas"], tagsEn: ["Services", "Bookings"] },
campings_demo: { category: "services", tags: ["Reservas", "Next.js", "Firebase", "Admin", "MVP"], tagsEn: ["Bookings", "Next.js", "Firebase", "Admin", "MVP"] },
campings_demo: { category: "services", tags: ["Labs", "Prototipo", "Next.js", "Firebase", "Admin"], tagsEn: ["Labs", "Prototype", "Next.js", "Firebase", "Admin"] },
};

export const filters: { key: Category; es: string; en: string }[] = [
Expand Down Expand Up @@ -423,9 +423,9 @@ export const caseDetails: Record<ProjectKey, PortfolioCaseDetail> = {
},
campings_demo: {
summaryEs:
"MVP funcional avanzado para gestionar reservas de campings y áreas recreativas. Incluye catálogo público, flujo de reserva, consulta por código, panel administrativo con roles y soporte de inventario híbrido: por capacidad o por unidades físicas.",
"Prototipo técnico con flujo público, consulta de reservas, panel administrativo con roles e inventario híbrido por capacidad o unidades físicas.",
summaryEn:
"Advanced functional MVP to manage campground and recreational area bookings. It includes a public catalog, booking flow, code-based booking lookup, an admin panel with roles, and hybrid inventory support: by capacity or by physical units.",
"Technical prototype with a public flow, booking lookup, role-based admin panel and hybrid inventory by capacity or physical units.",
stack: [
"Frontend: Next.js 16 + React 19 + TypeScript + App Router",
"Backend/servicios: Firebase Auth + Cloud Firestore",
Expand Down Expand Up @@ -477,16 +477,16 @@ export const caseDetails: Record<ProjectKey, PortfolioCaseDetail> = {
"Separation between private booking data and a public availability projection.",
],
resultsEs: [
"Núcleo del producto resuelto en versión demostrable.",
"Operación administrativa principal ya modelada dentro del MVP.",
"Base técnica preparada para evolucionar a pagos reales, mapa interactivo y automatizaciones.",
"Caso apto para portfolio si se presenta explícitamente como MVP/demo.",
"Flujo público y panel administrativo disponibles en un prototipo navegable.",
"Disponibilidad, reservas e inventario híbrido modelados de extremo a extremo.",
"Pagos mantenidos de forma explícitamente simulada y no productiva.",
"Base técnica preparada para experimentación futura.",
],
resultsEn: [
"Core product flow solved in a demonstrable version.",
"Primary admin operations already modeled within the MVP.",
"Technical foundation prepared for real payments, interactive maps and automations.",
"Portfolio-ready only if presented explicitly as an MVP/demo.",
"Public flow and admin panel available in a navigable prototype.",
"Availability, bookings and hybrid inventory modeled end to end.",
"Payments kept explicitly simulated and non-production.",
"Technical foundation prepared for future experimentation.",
],
},
};
Expand Down
2 changes: 2 additions & 0 deletions src/pages/PortfolioPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ export default function PortfolioPage() {
desc={P[p.key].desc}
tags={(language === "es" ? projectMeta[p.key].tags : projectMeta[p.key].tagsEn).join(" · ")}
expanded={expanded[p.key]}
status={p.key === "campings_demo" ? t.portfolio.campings_demo.status : undefined}
disclaimer={p.key === "campings_demo" ? t.portfolio.campings_demo.disclaimer : undefined}
>
{p.key === "lem_web" ? (
<>
Expand Down
20 changes: 13 additions & 7 deletions src/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ export type TranslationsStructure = {
campings_demo: {
title: string;
desc: string;
status: string;
disclaimer: string;
link: string;
};
seo: {
Expand Down Expand Up @@ -334,9 +336,11 @@ const translations: Record<LanguageKeys, TranslationsStructure> = {
link: "Ver sitio",
},
campings_demo: {
title: "Campings reservas (demo)",
desc: "MVP funcional avanzado para reservas de campings, con flujo público, panel administrativo e inventario híbrido por cupo o por unidades.",
link: "Ver caso",
title: "Plataforma de reservas de campings",
desc: "Prototipo full-stack presentado como propuesta para explorar la gestión de disponibilidad, reservas, pagos simulados y administración de campings. No fue encargado ni adoptado por la organización destinataria.",
status: "Prototipo conceptual",
disclaimer: "Proyecto conceptual e independiente. No es un sistema oficial ni está afiliado a Administración de Parques Nacionales.",
link: "Ver código",
},
seo: {
title: "Portfolio | Rodrigo Opalo",
Expand Down Expand Up @@ -608,9 +612,11 @@ const translations: Record<LanguageKeys, TranslationsStructure> = {
link: "Visit site",
},
campings_demo: {
title: "Campings bookings (demo)",
desc: "Advanced functional MVP for campground bookings, with public flow, admin panel and hybrid inventory by capacity or physical units.",
link: "View case",
title: "Campground booking platform",
desc: "Full-stack prototype presented as a proposal to explore campground availability, bookings, simulated payments and administration. It was not commissioned or adopted by the intended organization.",
status: "Concept prototype",
disclaimer: "Independent concept project. It is not an official system and is not affiliated with Administración de Parques Nacionales.",
link: "View code",
},
seo: {
title: "Portfolio | Rodrigo Opalo",
Expand Down Expand Up @@ -733,4 +739,4 @@ const translations: Record<LanguageKeys, TranslationsStructure> = {
},
};

export default translations;
export default translations;
Loading