diff --git a/apps/portfolio/messages/en.json b/apps/portfolio/messages/en.json index 256c9f8..5a6f5c2 100644 --- a/apps/portfolio/messages/en.json +++ b/apps/portfolio/messages/en.json @@ -107,28 +107,40 @@ { "role": "Consultant & Tech Lead", "company": "Fastbyte SL", - "period": { "start": "2019", "end": "2025" }, + "period": { + "start": "2019", + "end": "2025" + }, "headline": "Requirements gathering with clients/teams and translation into practical solutions.", "achievements": ["Constant communication, task follow-up and phased delivery."] }, { "role": "Program Specialist", "company": "Vita Liber S.L.U.", - "period": { "start": "2020", "end": "2025" }, + "period": { + "start": "2020", + "end": "2025" + }, "headline": "Managing internal user needs: prioritization, incidents, validation and functional support.", "achievements": [] }, { "role": "Co-founder & Lead Dev", "company": "GestionoMiNegocio", - "period": { "start": "2020", "end": "2025" }, + "period": { + "start": "2020", + "end": "2025" + }, "headline": "Working with beta users: collecting feedback, improvements and product onboarding support.", "achievements": ["Focus on clarity, quality and user experience."] }, { "role": "Developer", "company": "240dots", - "period": { "start": "2020", "end": "2023" }, + "period": { + "start": "2020", + "end": "2023" + }, "headline": "Development of headless solutions combining WordPress and Symfony.", "achievements": [ "Building plugins connected to custom APIs.", @@ -138,7 +150,10 @@ { "role": "Webmaster", "company": "Seedstockers (BCG)", - "period": { "start": "2023", "end": "2024" }, + "period": { + "start": "2023", + "end": "2024" + }, "headline": "Multilingual e-commerce management (PrestaShop) and collaboration with payment/logistics areas.", "achievements": [ "Coordination to resolve operational incidents in the purchase flow (order, payment, shipping).", @@ -463,11 +478,26 @@ "summary": { "totalRepositories": 35, "mainLanguagesShare": [ - { "language": "PHP", "usagePercentage": 31.4 }, - { "language": "JavaScript", "usagePercentage": 20.0 }, - { "language": "Twig", "usagePercentage": 14.3 }, - { "language": "TypeScript", "usagePercentage": 8.6 }, - { "language": "Python", "usagePercentage": 2.9 } + { + "language": "PHP", + "usagePercentage": 31.4 + }, + { + "language": "JavaScript", + "usagePercentage": 20 + }, + { + "language": "Twig", + "usagePercentage": 14.3 + }, + { + "language": "TypeScript", + "usagePercentage": 8.6 + }, + { + "language": "Python", + "usagePercentage": 2.9 + } ], "contributionsLastYear": 1706, "activeRepositories": 9, @@ -537,126 +567,59 @@ ], "items": [ { - "id": "verifactur-gmn", - "title": "Electronic Invoicing System", - "description": "End-to-end electronic invoicing system compliant with VeriFactu/AEAT, offering SaaS flows and fiscal automation.", - "tech": ["Symfony 7", "API Platform", "PostgreSQL", "Docker", "CI/CD"], - "links": { - "github": "https://github.com/GMNAPI/VerifacturGMN" - }, - "categoryId": "facturacion-compliance" - }, - { - "id": "facturae-docusaurus", - "title": "Technical Documentation Portal", - "description": "Technical documentation portal for VeriFactu and Facturae with guides, examples and automated deployment.", - "tech": ["Docusaurus", "TypeScript", "MDX", "GitHub Actions"], + "id": "gestiono-mi-negocio-facturacion", + "title": "GestionoMiNegocio - Billing Software", + "description": "Led development as Co-founder & Lead Dev. Comprehensive platform for basic administrative management, including billing and collection control. Architecture oriented to handle workload peaks while maintaining quality and response times.", + "tech": [ + "Symfony 7", + "API Platform", + "PostgreSQL", + "React", + "TypeScript", + "Docker", + "GitHub Actions" + ], "links": { - "github": "https://github.com/GMNAPI/FacturaeDocusaurus" + "demo": "https://gestionominegocio.com/" }, - "categoryId": "facturacion-compliance" + "categoryId": "facturacion-compliance", + "imageUrl": "/images/projects/gestiono-mi-negocio.webp" }, { - "id": "apigns", - "title": "REST API for Service Management", - "description": "Modern REST API for comprehensive service management with advanced authentication and external dashboards.", - "tech": ["Symfony 7.3", "API Platform", "MariaDB", "Redis", "Docker"], - "links": { - "github": "https://github.com/GMNAPI/apiGns" - }, - "categoryId": "gestion-servicios" + "id": "vita-liber-erp-interno", + "title": "Vita Liber - ERP Rescue & Modernisation", + "description": "Following a critical local server failure that corrupted backups in 2021/2022, I took over the legacy code. Led data recovery and built a new corporate system from scratch using MVC architecture and modern paradigms.", + "tech": [ + "PHP", + "Symfony", + "Laravel", + "MySQL", + "Redis", + "Bootstrap", + "Tailwind CSS", + "JavaScript" + ], + "links": {}, + "categoryId": "erp-transformacion", + "imageUrl": "/images/projects/vita-liber-erp-interno.webp" }, { - "id": "frontend-json", - "title": "Enterprise Frontend", - "description": "Enterprise frontend in Next.js 15 with clean architecture, strict typing and atomic design system.", - "tech": ["Next.js 15", "TypeScript", "Tailwind CSS", "Clean Architecture"], - "links": { - "github": "https://github.com/GMNAPI/FrontendJson" - }, + "id": "fastbyte-api-servicios", + "title": "Fastbyte - Service Management REST API", + "description": "Consultant and Tech Lead focused on requirement gathering with clients and translating them into practical solutions. Robust REST API for service management with phased deliveries and constant client communication.", + "tech": ["Symfony 7.3", "API Platform", "MariaDB", "Docker"], + "links": {}, "categoryId": "gestion-servicios" }, { - "id": "fynkus", - "title": "Booking Platform", - "description": "Sports booking platform using hexagonal architecture, DDD and CQRS with a scalability focus.", - "tech": ["PHP 8.2", "Symfony 5.4", "CQRS", "React 18", "Docker"], + "id": "seedstockers-b2b-platform", + "title": "Seedstockers - B2B Platform & Web Integration", + "description": "Maintenance and development of a React-based B2B platform with complex Web Services integration for Prestashop. Technical coordination with logistics and payment teams to resolve operational incidents.", + "tech": ["React", "TypeScript", "Prestashop", "Node.js", "Express", "Jest", "Tailwind CSS"], "links": { - "github": "https://github.com/GMNAPI/FYNKUS" - }, - "categoryId": "arquitectura-avanzada" - }, - { - "id": "gestiono-mi-negocio", - "title": "ERP SaaS Platform", - "description": "Legacy ERP migration to modular SaaS with multi-tenant support, automated billing and analytic dashboards.", - "tech": ["Symfony 7", "Next.js", "RabbitMQ", "PostgreSQL", "Terraform"], - "links": { - "github": "https://github.com/GMNAPI/gestionominegocio" + "demo": "https://seedstockers.com/" }, "categoryId": "erp-transformacion" - }, - { - "id": "vita-liber", - "title": "Enterprise Management Platform", - "description": "Full-stack platform for logistics and academic management integrating Symfony + Next.js.", - "tech": ["Next.js 14", "Symfony 6", "MariaDB", "API Platform", "Jest"], - "links": { - "github": "https://github.com/GMNAPI/vitaliber" - }, - "categoryId": "erp-transformacion" - }, - { - "id": "genesis-enterprise", - "title": "Enterprise Ecosystem", - "description": "Enterprise ecosystem (4M LOC) modernized into microservices with Symfony, messaging and DevOps practices.", - "tech": ["Symfony", "RabbitMQ", "Microservices", "Docker", "CI/CD"], - "links": { - "github": "https://github.com/GMNAPI/genesis" - }, - "categoryId": "erp-transformacion" - }, - { - "id": "streamlit-gmn", - "title": "Business Model Simulator", - "description": "SaaS business model simulator with real-time analytics and interactive dashboards.", - "tech": ["Python", "Streamlit", "Pandas", "Plotly"], - "links": { - "github": "https://github.com/GMNAPI/streamlitGMN" - }, - "categoryId": "herramientas" - }, - { - "id": "zend-laminas-tech-tests", - "title": "Technical Tests Collection", - "description": "Collection of 10 realistic technical tests for Laminas MVC and Mezzio aimed at professional training.", - "tech": ["Laminas", "PHP", "Mezzio", "Docker", "Testing"], - "links": { - "github": "https://github.com/GMNAPI/zend-laminas-tech-tests" - }, - "categoryId": "educacion" - }, - { - "id": "llarjove", - "title": "LlarJove - AI Housing Assistant", - "description": "RAG chatbot helping young people (18-35) in Catalonia find rental subsidies, understand their rights (LAU, deposit rules) and access public housing resources.", - "tech": [ - "TypeScript", - "React 19", - "Vite 6", - "Tailwind v4", - "Fastify", - "RAG", - "OpenAI GPT-4", - "ChromaDB", - "Docker", - "Railway" - ], - "links": { - "github": "https://github.com/GMNAPI/llarjove", - "demo": "https://llarjove-production-754a.up.railway.app/" - }, - "categoryId": "ia-chatbots" } ] }, diff --git a/apps/portfolio/messages/es.json b/apps/portfolio/messages/es.json index 73d20dd..ca896fc 100644 --- a/apps/portfolio/messages/es.json +++ b/apps/portfolio/messages/es.json @@ -107,28 +107,40 @@ { "role": "Consultor & Tech Lead", "company": "Fastbyte SL", - "period": { "start": "2019", "end": "2025" }, + "period": { + "start": "2019", + "end": "2025" + }, "headline": "Toma de requisitos con clientes/equipos y traducción a soluciones prácticas.", "achievements": ["Comunicación constante, seguimiento de tareas y entrega por fases."] }, { "role": "Especialista de Programa", "company": "Vita Liber S.L.U.", - "period": { "start": "2020", "end": "2025" }, + "period": { + "start": "2020", + "end": "2025" + }, "headline": "Gestión de necesidades de usuarios internos: priorización, incidencias, validación y soporte funcional.", "achievements": [] }, { "role": "Cofundador & Lead Dev", "company": "GestionoMiNegocio", - "period": { "start": "2020", "end": "2025" }, + "period": { + "start": "2020", + "end": "2025" + }, "headline": "Trabajo con usuarios beta: recogida de feedback, mejoras y acompañamiento en uso del producto.", "achievements": ["Enfoque en claridad, calidad y experiencia de usuario."] }, { "role": "Developer", "company": "240dots", - "period": { "start": "2020", "end": "2023" }, + "period": { + "start": "2020", + "end": "2023" + }, "headline": "Desarrollo de soluciones headless combinando WordPress y Symfony.", "achievements": [ "Construcción de plugins conectados con APIs personalizadas.", @@ -138,7 +150,10 @@ { "role": "Webmaster", "company": "Seedstockers (BCG)", - "period": { "start": "2023", "end": "2024" }, + "period": { + "start": "2023", + "end": "2024" + }, "headline": "Gestión de e-commerce multilingüe (PrestaShop) y colaboración con áreas de pagos/logística.", "achievements": [ "Coordinación para resolver incidencias operativas en el flujo de compra (pedido, pago, envío).", @@ -463,11 +478,26 @@ "summary": { "totalRepositories": 35, "mainLanguagesShare": [ - { "language": "PHP", "usagePercentage": 31.4 }, - { "language": "JavaScript", "usagePercentage": 20.0 }, - { "language": "Twig", "usagePercentage": 14.3 }, - { "language": "TypeScript", "usagePercentage": 8.6 }, - { "language": "Python", "usagePercentage": 2.9 } + { + "language": "PHP", + "usagePercentage": 31.4 + }, + { + "language": "JavaScript", + "usagePercentage": 20 + }, + { + "language": "Twig", + "usagePercentage": 14.3 + }, + { + "language": "TypeScript", + "usagePercentage": 8.6 + }, + { + "language": "Python", + "usagePercentage": 2.9 + } ], "contributionsLastYear": 1706, "activeRepositories": 9, @@ -537,126 +567,59 @@ ], "items": [ { - "id": "verifactur-gmn", - "title": "Sistema de Facturación Electrónica", - "description": "Sistema completo de facturación electrónica compatible con VeriFactu/AEAT con flujos SaaS y automatización fiscal.", - "tech": ["Symfony 7", "API Platform", "PostgreSQL", "Docker", "CI/CD"], - "links": { - "github": "https://github.com/GMNAPI/VerifacturGMN" - }, - "categoryId": "facturacion-compliance" - }, - { - "id": "facturae-docusaurus", - "title": "Portal de Documentación Técnica", - "description": "Portal de documentación técnica para VeriFactu y Facturae con guías, ejemplos y despliegue automatizado.", - "tech": ["Docusaurus", "TypeScript", "MDX", "GitHub Actions"], + "id": "gestiono-mi-negocio-facturacion", + "title": "GestionoMiNegocio - Software de Facturación", + "description": "Desarrollo liderado como Cofundador y Lead Dev. Plataforma integral para la gestión administrativa básica, facturación y control de cobros. Arquitectura orientada a resolver picos de trabajo manteniendo calidad y tiempos de respuesta.", + "tech": [ + "Symfony 7", + "API Platform", + "PostgreSQL", + "React", + "TypeScript", + "Docker", + "GitHub Actions" + ], "links": { - "github": "https://github.com/GMNAPI/FacturaeDocusaurus" + "demo": "https://gestionominegocio.com/" }, - "categoryId": "facturacion-compliance" + "categoryId": "facturacion-compliance", + "imageUrl": "/images/projects/gestiono-mi-negocio.webp" }, { - "id": "apigns", - "title": "API REST de Gestión de Servicios", - "description": "API REST moderna para la gestión integral de servicios con autenticación avanzada y paneles externos.", - "tech": ["Symfony 7.3", "API Platform", "MariaDB", "Redis", "Docker"], - "links": { - "github": "https://github.com/GMNAPI/apiGns" - }, - "categoryId": "gestion-servicios" + "id": "vita-liber-erp-interno", + "title": "Vita Liber - Rescate y Modernización de ERP", + "description": "Tras un fallo crítico en servidores locales que corrompió backups en 2021/2022, asumí el control del código legacy. Lideré la recuperación de datos y desarrollé desde cero un nuevo sistema corporativo con arquitectura MVC y paradigmas modernos.", + "tech": [ + "PHP", + "Symfony", + "Laravel", + "MySQL", + "Redis", + "Bootstrap", + "Tailwind CSS", + "JavaScript" + ], + "links": {}, + "categoryId": "erp-transformacion", + "imageUrl": "/images/projects/vita-liber-erp-interno.webp" }, { - "id": "frontend-json", - "title": "Frontend Empresarial", - "description": "Frontend empresarial en Next.js 15 con arquitectura limpia, tipado estricto y diseño atómico.", - "tech": ["Next.js 15", "TypeScript", "Tailwind CSS", "Clean Architecture"], - "links": { - "github": "https://github.com/GMNAPI/FrontendJson" - }, + "id": "fastbyte-api-servicios", + "title": "Fastbyte - API REST de Gestión de Servicios", + "description": "Consultor y Tech Lead en toma de requisitos con clientes y su traducción a soluciones prácticas. API REST robusta para gestión de servicios con entregas por fases y comunicación constante con el cliente.", + "tech": ["Symfony 7.3", "API Platform", "MariaDB", "Docker"], + "links": {}, "categoryId": "gestion-servicios" }, { - "id": "fynkus", - "title": "Plataforma de Reservas", - "description": "Plataforma de reservas deportivas con arquitectura hexagonal, DDD y CQRS, enfocada en escalabilidad.", - "tech": ["PHP 8.2", "Symfony 5.4", "CQRS", "React 18", "Docker"], + "id": "seedstockers-b2b-platform", + "title": "Seedstockers - Plataforma B2B & Integración Web", + "description": "Mantenimiento y desarrollo de plataforma B2B en React e integración de Web Services complejos con Prestashop. Coordinación técnica con áreas de logística y pagos para resolución de incidencias operativas.", + "tech": ["React", "TypeScript", "Prestashop", "Node.js", "Express", "Jest", "Tailwind CSS"], "links": { - "github": "https://github.com/GMNAPI/FYNKUS" - }, - "categoryId": "arquitectura-avanzada" - }, - { - "id": "gestiono-mi-negocio", - "title": "Plataforma ERP SaaS", - "description": "Migración de ERP legacy a SaaS modular con multi-tenant, automatización de facturación y paneles analíticos.", - "tech": ["Symfony 7", "Next.js", "RabbitMQ", "PostgreSQL", "Terraform"], - "links": { - "github": "https://github.com/GMNAPI/gestionominegocio" + "demo": "https://seedstockers.com/" }, "categoryId": "erp-transformacion" - }, - { - "id": "vita-liber", - "title": "Plataforma de Gestión Empresarial", - "description": "Plataforma full-stack para gestión logística y académica con integración Symfony + Next.js.", - "tech": ["Next.js 14", "Symfony 6", "MariaDB", "API Platform", "Jest"], - "links": { - "github": "https://github.com/GMNAPI/vitaliber" - }, - "categoryId": "erp-transformacion" - }, - { - "id": "genesis-enterprise", - "title": "Ecosistema Enterprise", - "description": "Ecosistema enterprise (4M LOC) modernizado hacia microservicios con Symfony, mensajería y DevOps.", - "tech": ["Symfony", "RabbitMQ", "Microservices", "Docker", "CI/CD"], - "links": { - "github": "https://github.com/GMNAPI/genesis" - }, - "categoryId": "erp-transformacion" - }, - { - "id": "streamlit-gmn", - "title": "Simulador de Modelos de Negocio", - "description": "Simulador de modelos de negocio SaaS con analítica en tiempo real y dashboards interactivos.", - "tech": ["Python", "Streamlit", "Pandas", "Plotly"], - "links": { - "github": "https://github.com/GMNAPI/streamlitGMN" - }, - "categoryId": "herramientas" - }, - { - "id": "zend-laminas-tech-tests", - "title": "Colección de Pruebas Técnicas", - "description": "Colección de 10 pruebas técnicas realistas para Laminas MVC y Mezzio orientadas a formación profesional.", - "tech": ["Laminas", "PHP", "Mezzio", "Docker", "Testing"], - "links": { - "github": "https://github.com/GMNAPI/zend-laminas-tech-tests" - }, - "categoryId": "educacion" - }, - { - "id": "llarjove", - "title": "LlarJove - Asistente de Vivienda con IA", - "description": "Chatbot RAG que ayuda a jóvenes de 18-35 años en Catalunya a encontrar ayudas de alquiler, conocer sus derechos (LAU, fianza) y acceder a recursos de vivienda públicos.", - "tech": [ - "TypeScript", - "React 19", - "Vite 6", - "Tailwind v4", - "Fastify", - "RAG", - "OpenAI GPT-4", - "ChromaDB", - "Docker", - "Railway" - ], - "links": { - "github": "https://github.com/GMNAPI/llarjove", - "demo": "https://llarjove-production-754a.up.railway.app/" - }, - "categoryId": "ia-chatbots" } ] }, diff --git a/apps/portfolio/public/images/projects/gestiono-mi-negocio.webp b/apps/portfolio/public/images/projects/gestiono-mi-negocio.webp new file mode 100644 index 0000000..7b714e9 Binary files /dev/null and b/apps/portfolio/public/images/projects/gestiono-mi-negocio.webp differ diff --git a/apps/portfolio/public/images/projects/vita-liber-erp-interno.webp b/apps/portfolio/public/images/projects/vita-liber-erp-interno.webp new file mode 100644 index 0000000..a71c325 Binary files /dev/null and b/apps/portfolio/public/images/projects/vita-liber-erp-interno.webp differ diff --git a/apps/portfolio/src/shared/constants/project-slugs.ts b/apps/portfolio/src/shared/constants/project-slugs.ts index 36b589f..9f8986b 100644 --- a/apps/portfolio/src/shared/constants/project-slugs.ts +++ b/apps/portfolio/src/shared/constants/project-slugs.ts @@ -6,17 +6,10 @@ */ export const PROJECT_SLUGS = [ - 'apigns', - 'facturae-docusaurus', - 'frontend-json', - 'fynkus', - 'genesis-enterprise', - 'gestiono-mi-negocio', - 'llarjove', - 'streamlit-gmn', - 'verifactur-gmn', - 'vita-liber', - 'zend-laminas-tech-tests', + 'gestiono-mi-negocio-facturacion', + 'vita-liber-erp-interno', + 'fastbyte-api-servicios', + 'seedstockers-b2b-platform', ] as const; export type ProjectSlug = (typeof PROJECT_SLUGS)[number]; diff --git a/apps/portfolio/tests/features/projects/Projects.test.tsx b/apps/portfolio/tests/features/projects/Projects.test.tsx index eeea605..0a79b54 100644 --- a/apps/portfolio/tests/features/projects/Projects.test.tsx +++ b/apps/portfolio/tests/features/projects/Projects.test.tsx @@ -67,21 +67,29 @@ describe('Projects Section', () => { it('should display tech stack in cards', () => { renderProjects(); - const nextjsTags = screen.getAllByText(/Next\.js/i); + // Use tech tags present in the current project data + const symfonyTags = screen.getAllByText(/Symfony/i); const typescriptTags = screen.getAllByText(/TypeScript/i); - expect(nextjsTags.length).toBeGreaterThan(0); + expect(symfonyTags.length).toBeGreaterThan(0); expect(typescriptTags.length).toBeGreaterThan(0); }); }); describe('Links (in modal)', () => { - it('should render GitHub links inside modal after clicking a card', () => { + it('should render external links inside modal after clicking a card', () => { renderProjects(); - // Open the first project modal - const articles = screen.getAllByRole('article'); - fireEvent.click(articles[0]); - const githubLinks = screen.getAllByRole('link', { name: /github/i }); - expect(githubLinks.length).toBeGreaterThan(0); + const projectsData = esMessages.projects.items as unknown as ProjectData[]; + // Find first project with any external link (github or demo) + const linkedIndex = projectsData.findIndex( + (p) => Boolean(p.links.github) || Boolean(p.links.demo) + ); + if (linkedIndex >= 0) { + const articles = screen.getAllByRole('article'); + fireEvent.click(articles[linkedIndex]); + const allLinks = screen.getAllByRole('link'); + const externalLinks = allLinks.filter((l) => l.getAttribute('href')?.startsWith('http')); + expect(externalLinks.length).toBeGreaterThan(0); + } }); it('should render demo links in modal when available for that project', () => { @@ -96,22 +104,40 @@ describe('Projects Section', () => { } }); - it('should have proper href attributes on GitHub links in modal', () => { + it('should have proper href attributes on external links in modal', () => { renderProjects(); - const articles = screen.getAllByRole('article'); - fireEvent.click(articles[0]); - const link = screen.getAllByRole('link', { name: /github/i })[0]; - expect(link).toHaveAttribute('href'); - expect(link.getAttribute('href')).toContain('github.com'); + const projectsData = esMessages.projects.items as unknown as ProjectData[]; + const linkedIndex = projectsData.findIndex( + (p) => Boolean(p.links.github) || Boolean(p.links.demo) + ); + if (linkedIndex >= 0) { + const articles = screen.getAllByRole('article'); + fireEvent.click(articles[linkedIndex]); + const allLinks = screen.getAllByRole('link'); + const externalLinks = allLinks.filter((l) => l.getAttribute('href')?.startsWith('http')); + expect(externalLinks.length).toBeGreaterThan(0); + externalLinks.forEach((link) => { + expect(link).toHaveAttribute('href'); + }); + } }); it('should open external links in new tab', () => { renderProjects(); - const articles = screen.getAllByRole('article'); - fireEvent.click(articles[0]); - const githubLink = screen.getAllByRole('link', { name: /github/i })[0]; - expect(githubLink).toHaveAttribute('target', '_blank'); - expect(githubLink).toHaveAttribute('rel', 'noopener noreferrer'); + const projectsData = esMessages.projects.items as unknown as ProjectData[]; + const linkedIndex = projectsData.findIndex( + (p) => Boolean(p.links.github) || Boolean(p.links.demo) + ); + if (linkedIndex >= 0) { + const articles = screen.getAllByRole('article'); + fireEvent.click(articles[linkedIndex]); + const allLinks = screen.getAllByRole('link'); + const externalLinks = allLinks.filter((l) => l.getAttribute('href')?.startsWith('http')); + if (externalLinks.length > 0) { + expect(externalLinks[0]).toHaveAttribute('target', '_blank'); + expect(externalLinks[0]).toHaveAttribute('rel', 'noopener noreferrer'); + } + } }); it('should render private repository notice in modal', () => {