Skip to content
Open
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
103 changes: 103 additions & 0 deletions apps/portfolio/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,66 @@
{ "label": "Data model", "value": "Database-per-tenant" }
]
},
"llarjove-rag-chatbot": {
"sections": [
{
"heading": "Context",
"body": "Conversational assistant for young people aged 18-35 in Catalonia looking for housing: available aid (Bono Alquiler Joven, regional and municipal grants), tenant rights (LAU, deposits, rent updates) and active programs with their deadlines (Borsa Jove, HPO). Deployed in production on Railway with a public repository."
},
{
"heading": "RAG pipeline",
"body": "Offline ingestion of curated official documentation (aid programs, rights, guides and laws): loading, chunking, embeddings and a vector store. At runtime each question is embedded, context is retrieved through semantic search with reranking, and the answer is generated with GPT-4, validated and returned with cited sources and a confidence level."
},
{
"heading": "Backend",
"body": "Fastify + TypeScript API with the pipeline organized into modules (ingestion, retrieval, generation) and a chat endpoint. Responses return answer, sources and confidence so users can verify the information."
},
{
"heading": "Frontend",
"body": "React 19 + Vite 6 + Tailwind CSS v4 interface with a custom design system (brand identity, color and typography tokens) and 59 component tests with Vitest and Testing Library."
},
{
"heading": "Approach",
"body": "An applied-AI project with social impact: official housing information is scattered and hard to interpret; the assistant centralizes it and answers in plain language, always citing the official source."
}
],
"metrics": [
{ "label": "Stack", "value": "Fastify · GPT-4" },
{ "label": "Frontend", "value": "React 19 + Vite" },
{ "label": "Frontend tests", "value": "59" },
{ "label": "Deployment", "value": "Railway" }
]
},
"vitaliber-crm-erp-v2": {
"sections": [
{
"heading": "Context",
"body": "Complete rewrite of the legacy CRM/ERP of VitaLiber, a telemarketing company with 5-20 agents in intensive daily use. The previous system (MySQL, 28 tables) carried severe technical debt. V2 replaces it with a clean domain model: CRM, telemarketing, sales (publishing catalogue), billing with SEPA direct debit, and email."
},
{
"heading": "Multi-tenant with Row-Level Security",
"body": "Per-provider data isolation enforced in the database itself: PostgreSQL 15 with Row-Level Security policies. A Doctrine postConnect listener sets the active tenant per connection and verifies it against the user-provider access table. JWT authentication with refresh tokens and an active-provider selector. GDPR-aware design."
},
{
"heading": "Backend and schema",
"body": "REST API with Symfony 7 + API Platform 4 and Swagger UI. The schema is owned by SQL migrations with DBmate (Doctrine acts as a query layer only), organized into 7 logical PostgreSQL schemas (core, crm, tmk, sales, billing, comms, audit). Outbox-lite auditing: domain events are written in the same transaction."
},
{
"heading": "End-to-end typed frontend",
"body": "React 18 + strict TypeScript + Vite with Tailwind and shadcn/ui. The API client is generated from OpenAPI with orval: TanStack Query hooks, types and Zod schemas kept in sync with the backend. Internationalization with react-intl (Spanish base, ready for Catalan and English)."
},
{
"heading": "Process and status",
"body": "Phased delivery mirroring the migration plan (8 phases with GitHub milestones and issues): identity & access, catalogs, CRM, telemarketing, sales and communications already merged to main. Decisions documented in 7 ADRs, a PNPM + Composer + Taskfile monorepo, CI/CD and PHPUnit + Vitest test suites, including RLS isolation tests. Live-data-only migration, keeping the legacy database as a read-only archive."
}
],
"metrics": [
{ "label": "Phases delivered", "value": "6 of 8" },
{ "label": "ADRs", "value": "7" },
{ "label": "PostgreSQL schemas", "value": "7" },
{ "label": "Isolation", "value": "Per-tenant RLS" }
]
},
"fastbyte-api-servicios": {
"sections": [
{
Expand Down Expand Up @@ -713,6 +773,49 @@
"categoryId": "facturacion-compliance",
"imageUrl": "/images/projects/gestiono-mi-negocio.webp"
},
{
"id": "llarjove-rag-chatbot",
"title": "LlarJove — RAG housing assistant for young people",
"description": "RAG chatbot that helps young people aged 18-35 in Catalonia find housing: available aid (Bono Alquiler Joven, regional and municipal grants), tenant rights (LAU) and active programs with deadlines. Full RAG pipeline over official sources: document ingestion and chunking, embeddings with semantic search and reranking, and GPT-4 generation that cites sources and reports a confidence level. Fastify + TypeScript backend and React 19 + Vite frontend with a custom design system. Deployed in production on Railway.",
"tech": [
"TypeScript",
"Node.js",
"Fastify",
"RAG",
"OpenAI GPT-4",
"React 19",
"Vite",
"Tailwind CSS",
"Docker",
"Railway"
],
"links": {
"github": "https://github.com/GMNAPI/llarjove",
"demo": "https://llarjove-production.up.railway.app/"
},
"categoryId": "ia-chatbots"
},
{
"id": "vitaliber-crm-erp-v2",
"title": "VitaLiber CRM/ERP v2 — Multi-tenant rewrite with PostgreSQL RLS",
"description": "Complete rewrite of a telemarketing company's legacy CRM/ERP (MySQL, 28 tables, severe technical debt) onto a modern, multi-tenant, GDPR-aware stack. Per-provider data isolation with PostgreSQL 15 Row-Level Security, a REST API built with Symfony 7 + API Platform 4 (JWT + refresh tokens) and a React 18 + TypeScript frontend with an OpenAPI-generated client (orval → TanStack Query + Zod). Phased delivery documented with ADRs: identity & access, catalogs, CRM, telemarketing, sales and email already merged to main; live-data-only migration keeping the legacy database as a read-only archive.",
"tech": [
"Symfony 7",
"API Platform 4",
"PHP 8.3",
"PostgreSQL",
"Row-Level Security",
"React 18",
"TypeScript",
"TanStack Query",
"Tailwind CSS",
"shadcn/ui",
"Docker",
"Multi-tenant"
],
"links": {},
"categoryId": "arquitectura-avanzada"
},
{
"id": "vita-liber-erp-interno",
"title": "Vita Liber - ERP Rescue & Modernisation",
Expand Down
103 changes: 103 additions & 0 deletions apps/portfolio/messages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,66 @@
{ "label": "Modelo de datos", "value": "Database-per-tenant" }
]
},
"llarjove-rag-chatbot": {
"sections": [
{
"heading": "Contexto",
"body": "Asistente conversacional para jóvenes de 18 a 35 años en Cataluña que buscan vivienda: ayudas disponibles (Bono Alquiler Joven, ayudas de la Generalitat y municipales), derechos como inquilino (LAU, fianza, actualizaciones de alquiler) y recursos activos con sus plazos (Borsa Jove, HPO). Desplegado en producción en Railway con repositorio público."
},
{
"heading": "Pipeline RAG",
"body": "Ingesta offline de documentación oficial curada (ayudas, derechos, guías y leyes): carga, chunking, embeddings y vector store. En runtime, cada pregunta se convierte en embedding, se recupera contexto por búsqueda semántica con reranking y se genera la respuesta con GPT-4, validada y acompañada de fuentes citadas y nivel de confianza."
},
{
"heading": "Backend",
"body": "API en Fastify + TypeScript con el pipeline organizado por módulos (ingestion, retrieval, generation) y un endpoint de chat. Las respuestas devuelven answer, sources y confidence para que el usuario pueda contrastar la información."
},
{
"heading": "Frontend",
"body": "Interfaz en React 19 + Vite 6 + Tailwind CSS v4 con design system propio (identidad de marca, tokens de color y tipografía) y 59 tests de componentes con Vitest y Testing Library."
},
{
"heading": "Enfoque",
"body": "Proyecto de IA aplicada con impacto social: la información oficial sobre vivienda está dispersa y es difícil de interpretar; el asistente la centraliza y la responde en lenguaje claro, citando siempre la fuente oficial."

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

En la descripción del enfoque de llarjove-rag-chatbot, la frase "el asistente la centraliza y la responde en lenguaje claro" utiliza un pronombre redundante e incorrecto ("la responde"), ya que no se "responde la información". Sería más natural y correcto decir "el asistente la centraliza y responde en lenguaje claro".

Suggested change
"body": "Proyecto de IA aplicada con impacto social: la información oficial sobre vivienda está dispersa y es difícil de interpretar; el asistente la centraliza y la responde en lenguaje claro, citando siempre la fuente oficial."
"body": "Proyecto de IA aplicada con impacto social: la información oficial sobre vivienda está dispersa y es difícil de interpretar; el asistente la centraliza y responde en lenguaje claro, citando siempre la fuente oficial."

}
],
"metrics": [
{ "label": "Stack", "value": "Fastify · GPT-4" },
{ "label": "Frontend", "value": "React 19 + Vite" },
{ "label": "Tests frontend", "value": "59" },
{ "label": "Despliegue", "value": "Railway" }
]
},
"vitaliber-crm-erp-v2": {
"sections": [
{
"heading": "Contexto",
"body": "Reescritura completa del CRM/ERP legacy de VitaLiber, una empresa de telemarketing con 5-20 agentes en uso diario intensivo. El sistema anterior (MySQL, 28 tablas) acumulaba una deuda técnica severa. La v2 lo sustituye con un modelo de dominio limpio: CRM, telemarketing, ventas (catálogo editorial), facturación con SEPA y email."
},
{
"heading": "Multi-tenant con Row-Level Security",
"body": "Aislamiento de datos por proveedor implementado en la propia base de datos: PostgreSQL 15 con políticas de Row-Level Security. Un listener postConnect de Doctrine establece el tenant activo por conexión y lo verifica contra la tabla de accesos usuario-proveedor. Autenticación JWT con refresh tokens y selector de proveedor activo. Diseño alineado con RGPD."
},
{
"heading": "Backend y esquema",
"body": "API REST con Symfony 7 + API Platform 4 y Swagger UI. El esquema lo gobiernan migraciones SQL con DBmate (Doctrine actúa solo como capa de consulta), organizado en 7 esquemas lógicos de PostgreSQL (core, crm, tmk, sales, billing, comms, audit). Auditoría outbox-lite: los eventos de dominio se escriben en la misma transacción."
},
{
"heading": "Frontend tipado extremo a extremo",
"body": "React 18 + TypeScript estricto + Vite con Tailwind y shadcn/ui. El cliente de API se genera desde OpenAPI con orval: hooks de TanStack Query, tipos y schemas de Zod sincronizados con el backend. Internacionalización con react-intl (base español, preparado para catalán e inglés)."
},
{
"heading": "Proceso y estado",
"body": "Entrega por fases espejando el plan de migración (8 fases con milestones e issues en GitHub): identidad y acceso, catálogos, CRM, telemarketing, ventas y comunicaciones ya fusionadas en main. Decisiones documentadas en 7 ADRs, monorepo PNPM + Composer + Taskfile, CI/CD y tests de PHPUnit y Vitest, incluidos tests de aislamiento RLS. Migración solo de datos vivos, con el legacy como archivo de solo lectura."

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

En la sección "Proceso y estado" de vitaliber-crm-erp-v2, se utiliza "ya fusionadas en main" para referirse a una lista mixta de elementos masculinos y femeninos ("identidad y acceso, catálogos, CRM, telemarketing, ventas y comunicaciones"). En español, la concordancia para listas mixtas debe ser en masculino plural ("ya fusionados en main"), tal como se hace correctamente en la descripción corta (línea 801).

Suggested change
"body": "Entrega por fases espejando el plan de migración (8 fases con milestones e issues en GitHub): identidad y acceso, catálogos, CRM, telemarketing, ventas y comunicaciones ya fusionadas en main. Decisiones documentadas en 7 ADRs, monorepo PNPM + Composer + Taskfile, CI/CD y tests de PHPUnit y Vitest, incluidos tests de aislamiento RLS. Migración solo de datos vivos, con el legacy como archivo de solo lectura."
"body": "Entrega por fases espejando el plan de migración (8 fases con milestones e issues en GitHub): identidad y acceso, catálogos, CRM, telemarketing, ventas y comunicaciones ya fusionados en main. Decisiones documentadas en 7 ADRs, monorepo PNPM + Composer + Taskfile, CI/CD y tests de PHPUnit y Vitest, incluidos tests de aislamiento RLS. Migración solo de datos vivos, con el legacy como archivo de solo lectura."

}
],
"metrics": [
{ "label": "Fases entregadas", "value": "6 de 8" },
{ "label": "ADRs", "value": "7" },
{ "label": "Esquemas PostgreSQL", "value": "7" },
{ "label": "Aislamiento", "value": "RLS por tenant" }
]
},
"fastbyte-api-servicios": {
"sections": [
{
Expand Down Expand Up @@ -713,6 +773,49 @@
"categoryId": "facturacion-compliance",
"imageUrl": "/images/projects/gestiono-mi-negocio.webp"
},
{
"id": "llarjove-rag-chatbot",
"title": "LlarJove — Asistente RAG de vivienda para jóvenes",
"description": "Chatbot RAG que ayuda a jóvenes de 18-35 años en Cataluña a encontrar vivienda: ayudas (Bono Alquiler Joven, Generalitat, municipales), derechos como inquilino (LAU) y recursos activos con plazos. Pipeline RAG completo sobre fuentes oficiales: ingesta y chunking de documentos, embeddings con búsqueda semántica y reranking, y generación con GPT-4 que responde citando fuentes y con nivel de confianza. Backend Fastify + TypeScript y frontend React 19 + Vite con design system propio. Desplegado en producción en Railway.",
"tech": [
"TypeScript",
"Node.js",
"Fastify",
"RAG",
"OpenAI GPT-4",
"React 19",
"Vite",
"Tailwind CSS",
"Docker",
"Railway"
],
"links": {
"github": "https://github.com/GMNAPI/llarjove",
"demo": "https://llarjove-production.up.railway.app/"
},
"categoryId": "ia-chatbots"
},
{
"id": "vitaliber-crm-erp-v2",
"title": "VitaLiber CRM/ERP v2 — Reescritura multi-tenant con PostgreSQL RLS",
"description": "Reescritura completa del CRM/ERP legacy de una empresa de telemarketing (MySQL, 28 tablas, deuda técnica severa) sobre un stack moderno, multi-tenant y alineado con RGPD. Aislamiento de datos por proveedor con Row-Level Security en PostgreSQL 15, API REST con Symfony 7 + API Platform 4 (JWT + refresh tokens) y frontend React 18 + TypeScript con cliente generado desde OpenAPI (orval → TanStack Query + Zod). Entrega por fases documentada con ADRs: identidad y acceso, catálogos, CRM, telemarketing, ventas y email ya fusionados en main; migración solo de datos vivos manteniendo el legacy como archivo de solo lectura.",
"tech": [
"Symfony 7",
"API Platform 4",
"PHP 8.3",
"PostgreSQL",
"Row-Level Security",
"React 18",
"TypeScript",
"TanStack Query",
"Tailwind CSS",
"shadcn/ui",
"Docker",
"Multi-tenant"
],
"links": {},
"categoryId": "arquitectura-avanzada"
},
{
"id": "vita-liber-erp-interno",
"title": "Vita Liber - Rescate y Modernización de ERP",
Expand Down
2 changes: 2 additions & 0 deletions apps/portfolio/src/shared/constants/project-slugs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
export const PROJECT_SLUGS = [
'devfreelancer-client-tracker',
'gestiono-mi-negocio-facturacion',
'llarjove-rag-chatbot',
'vitaliber-crm-erp-v2',
'vita-liber-erp-interno',
'fastbyte-api-servicios',
'seedstockers-b2b-platform',
Expand Down
Loading