[
  {
    "id": "complejo-deportivo",
    "type": "web",
    "year": null,
    "featured": true,
    "tech": [
      "C#",
      "SQL"
    ],
    "title": {
      "es": "Complejo Deportivo",
      "en": "Complejo Deportivo"
    },
    "desc": {
      "es": "Aplicación web para la gestión de reservas, clientes y canchas de un complejo deportivo. Desarrollada con ASP.NET WebForms y C# consumiendo una base de datos SQL Server, incluye control de turnos, roles de acceso y motor de fidelización con cupones.",
      "en": "Web application for managing reservations, clients, and courts of a sports complex. Built with ASP.NET WebForms and C# consuming a SQL Server database, it features turn scheduling, role‑based access, and an automated loyalty engine with coupons."
    },
    "notes": "Proyecto en equipo (3 colaboradores) para la materia Base de Datos II (TPI).",
    "images": [
      "img/proj-complejo-deportivo-mvcsqfoz0.webp",
      "img/proj-complejo-deportivo-mvcsqfoz1.webp",
      "img/proj-complejo-deportivo-mvcsqfoz2.webp",
      "img/proj-complejo-deportivo-mvcsqfoz3.webp",
      "img/proj-complejo-deportivo-mvcsqfoz4.webp"
    ],
    "tags": [
      "C#",
      "ASP.NET",
      "SQL Server",
      "WebForms",
      "TSQL"
    ],
    "live": "",
    "repo": "https://github.com/Facu-Ferreyra/BD2-TPI-Grupo45-ComplejoDep",
    "case": {
      "problem": {
        "es": "",
        "en": ""
      },
      "solution": {
        "es": "",
        "en": ""
      }
    },
    "og": "img/og-complejo-deportivo-mvcxa4yl.jpg",
    "ogSrc": "img/proj-complejo-deportivo-mvcsqfoz0.webp"
  },
  {
    "id": "catalogo",
    "type": "desktop",
    "year": null,
    "featured": true,
    "tech": [
      "C#",
      "SQL"
    ],
    "title": {
      "es": "Catálogo de Productos",
      "en": "Product Catalog"
    },
    "desc": {
      "es": "App de escritorio en C# con WinForms para gestionar artículos, marcas y categorías. Búsqueda avanzada y persistencia en SQL Server.",
      "en": "C# WinForms desktop app to manage articles, brands and categories. Advanced search and SQL Server persistence."
    },
    "notes": "Proyecto en equipo.",
    "images": [
      "img/proj-catalogo.webp"
    ],
    "tags": [
      "C#",
      "WinForms",
      "SQL Server"
    ],
    "live": "",
    "repo": "https://github.com/AngelMed43/TPWinForm_Equipo22A",
    "case": {
      "problem": {
        "es": "",
        "en": ""
      },
      "solution": {
        "es": "",
        "en": ""
      }
    },
    "og": "img/og-catalogo-mvcxa4yl.jpg",
    "ogSrc": "img/proj-catalogo.webp"
  },
  {
    "id": "asistente-whatsapp-ia",
    "type": "automation",
    "year": 2026,
    "featured": true,
    "tech": [
      "JavaScript",
      "IA"
    ],
    "title": {
      "es": "Asistente de WhatsApp con IA",
      "en": "AI WhatsApp Assistant"
    },
    "desc": {
      "es": "Asistente con IA que responde consultas por WhatsApp sobre servicios y proyectos, sobre la API oficial de WhatsApp Cloud. Cada mensaje llega a Telegram, desde donde se puede pausar la IA o responder en persona.",
      "en": "AI assistant that answers WhatsApp questions about services and projects, built on the official WhatsApp Cloud API. Every message is forwarded to Telegram, where the AI can be paused or the chat answered in person."
    },
    "notes": "Usa WhatsApp Cloud API. Integrado en el backend del portfolio.",
    "images": [
      "img/proj-asistente-whatsapp-ia-mven5i3e0.webp",
      "img/proj-asistente-whatsapp-ia-mven5i3e1.webp"
    ],
    "tags": [
      "WhatsApp Cloud API",
      "Groq",
      "Telegram Bot",
      "Upstash Redis",
      "Vercel"
    ],
    "live": "",
    "repo": "",
    "case": {
      "problem": {
        "es": "Las consultas de posibles clientes llegan a cualquier hora y muchas son las mismas: qué servicios hay, cuánto cuesta un sitio, cuánto tarda, qué proyectos hay hechos. Responderlas a mano demora la respuesta y corta el trabajo del día.\n\nAl mismo tiempo, un bot que contesta solo no alcanza: tiene que existir una forma rápida de ver qué está diciendo y de tomar la conversación cuando hace falta una persona. Y todo tenía que funcionar sin costo y sin herramientas no oficiales que pongan en riesgo el número.",
        "en": "Questions from potential clients arrive at any time, and many are the same: which services are offered, how much a website costs, how long it takes, what has been built. Answering them by hand delays the reply and interrupts the day's work.\n\nAt the same time, a bot that answers on its own isn't enough: there has to be a quick way to see what it says and to take over the conversation when a person is needed. And everything had to run at no cost and without unofficial tools that put the number at risk."
      },
      "solution": {
        "es": "Un webhook en Vercel recibe los mensajes de la API oficial de WhatsApp Cloud, verifica la firma de Meta y le pasa cada texto a un modelo de IA en Groq con el mismo perfil que usa el chat del sitio, así que responde con datos reales de servicios y proyectos. Guarda el historial de cada chat durante 7 días en Upstash Redis para mantener el contexto.\n\nCada mensaje se reenvía a un bot de Telegram con un botón para pausar o reactivar la IA en ese chat. Respondiendo al mensaje en Telegram, el texto sale por WhatsApp y la IA se pausa sola; si alguien escribe \"humano\", también. Hay comandos para ver el estado y apagar o prender la IA para todos.\n\nIncluye límites para no abusar del plan gratis (30 respuestas por hora por chat), ignora entregas repetidas de Meta y permite pedir \"borrar mis datos\", que elimina el historial de ese chat como indica la política de privacidad.",
        "en": "A Vercel webhook receives messages from the official WhatsApp Cloud API, verifies Meta's signature and passes each text to an AI model on Groq with the same profile the site chat uses, so it answers with real information about services and projects. Each chat's history is kept for 7 days in Upstash Redis to keep the context.\n\nEvery message is forwarded to a Telegram bot with a button to pause or resume the AI in that chat. Replying to the message in Telegram sends the text through WhatsApp and pauses the AI automatically; so does someone writing \"human\". There are commands to check the status and to turn the AI off or on for everyone.\n\nIt includes limits to stay within the free plan (30 answers per hour per chat), ignores Meta's repeated deliveries and supports \"delete my data\", which erases that chat's history as the privacy policy states."
      }
    },
    "og": "img/og-asistente-whatsapp-ia-mvenbgt4.jpg",
    "ogSrc": "img/proj-asistente-whatsapp-ia-mven5i3e0.webp"
  },
  {
    "id": "minifinance",
    "type": "web",
    "year": null,
    "featured": false,
    "tech": [
      "JavaScript"
    ],
    "title": {
      "es": "Mini Finance",
      "en": "Mini Finance"
    },
    "desc": {
      "es": "Aplicación web para registrar ingresos y gastos y visualizar el balance personal en tiempo real. Desarrollada durante la beca IntegrarTEC con JavaScript, HTML y CSS, con persistencia en el navegador.",
      "en": "Web app to track income and expenses and see your personal balance in real time. Built during the IntegrarTEC program with JavaScript, HTML and CSS, persisting data in the browser."
    },
    "notes": "",
    "images": [
      "img/proj-minifinance.webp"
    ],
    "tags": [
      "JavaScript",
      "HTML",
      "CSS"
    ],
    "live": "https://facu-ferreyra.github.io/Mini_Finance_INTEGRAR/index.html",
    "repo": "https://github.com/Facu-Ferreyra/Mini_Finance_INTEGRAR",
    "case": {
      "problem": {
        "es": "",
        "en": ""
      },
      "solution": {
        "es": "",
        "en": ""
      }
    },
    "og": "img/og-minifinance-mvcxa4yl.jpg",
    "ogSrc": "img/proj-minifinance.webp"
  },
  {
    "id": "diaria",
    "type": "automation",
    "year": 2026,
    "featured": false,
    "tech": [
      "JavaScript",
      "IA"
    ],
    "title": {
      "es": "DiarIA de Facu",
      "en": "DiarIA de Facu"
    },
    "desc": {
      "es": "Asistente personal de agenda por Telegram sobre Google Calendar. Entiende texto y audios para consultar, crear, mover y borrar eventos, y cada mañana envía el resumen del día y pregunta la disponibilidad para reuniones.",
      "en": "Personal calendar assistant on Telegram, built on Google Calendar. It understands text and voice notes to check, create, move and delete events, and every morning it sends the day's summary and asks about availability for meetings."
    },
    "notes": "Herramienta de uso personal (no es pública). Es la primera fase: después se suma una página para que clientes reserven reuniones.",
    "images": [
      "img/proj-diaria-mven7qkp0.webp",
      "img/proj-diaria-mven7qkp1.webp"
    ],
    "tags": [
      "Google Calendar API",
      "Groq + Whisper",
      "Telegram Bot",
      "OAuth 2.0",
      "Vercel Cron"
    ],
    "live": "",
    "repo": "https://github.com/Facu-Ferreyra/PortFolio_Web",
    "case": {
      "problem": {
        "es": "Manejar la agenda entre el estudio, el bootcamp y los proyectos implicaba abrir Google Calendar para cada cosa: ver qué había en el día, anotar una reunión que surgía en una charla o mover algo que se corría. Es justo el tipo de tarea que se posterga y termina en olvidos.\n\nAdemás, para ofrecer reuniones a clientes hacía falta decidir cada día si había disponibilidad, sin tener que configurarlo a mano en ningún lado.",
        "en": "Managing the calendar between university, the bootcamp and projects meant opening Google Calendar for everything: checking what was on for the day, writing down a meeting that came up in a conversation or moving something that shifted. It is exactly the kind of task that gets postponed and ends in forgotten things.\n\nOn top of that, offering meetings to clients required deciding each day whether there was availability, without having to set it up by hand anywhere."
      },
      "solution": {
        "es": "Un bot de Telegram recibe pedidos en lenguaje natural, por texto o por audio (transcripto con Whisper en Groq). Un modelo de IA con herramientas (tool calling) decide qué hacer: listar, crear, mover o borrar eventos, o abrir y cerrar días para reuniones. La IA nunca toca el calendario directo: el servidor valida cada dato, avisa si hay superposiciones y, para mover o borrar, espera que se toque \"Confirmar\".\n\nLa conexión con Google Calendar usa OAuth con la API oficial; el refresh token se guarda cifrado con AES-256-GCM en Redis y solo se puede conectar desde el panel privado.\n\nDos tareas programadas de Vercel completan el ciclo: a las 8:00 llega el resumen del día y la pregunta de disponibilidad para el día hábil siguiente (todo el día, mañana, tarde o no), y a las 20:00 un recordatorio si no hubo respuesta.",
        "en": "A Telegram bot takes requests in natural language, as text or voice notes (transcribed with Whisper on Groq). An AI model with tool calling decides what to do: list, create, move or delete events, or open and close days for meetings. The AI never touches the calendar directly: the server validates every value, warns about overlaps and, to move or delete, waits for a \"Confirm\" tap.\n\nThe Google Calendar connection uses OAuth with the official API; the refresh token is stored encrypted with AES-256-GCM in Redis and can only be connected from the private admin panel.\n\nTwo Vercel scheduled jobs close the loop: at 8:00 the day's summary arrives along with the availability question for the next working day (all day, morning, afternoon or no), and at 20:00 a reminder if there was no answer."
      }
    },
    "og": "img/og-diaria-mvenbgt4.jpg",
    "ogSrc": "img/proj-diaria-mven7qkp0.webp"
  },
  {
    "id": "reclamos",
    "type": "cli",
    "year": null,
    "featured": false,
    "tech": [
      "C++"
    ],
    "title": {
      "es": "Sistema de Gestión de Reclamos",
      "en": "Complaint Management System"
    },
    "desc": {
      "es": "Aplicación de consola en C++ para una compañía eléctrica. ABM de usuarios y reclamos, con persistencia en archivos binarios.",
      "en": "C++ console application for an electric utility. User and complaint CRUD, with persistence in binary files."
    },
    "notes": "",
    "images": [
      "img/proj-reclamos.webp"
    ],
    "tags": [
      "C++",
      "POO",
      "CLI"
    ],
    "live": "",
    "repo": "https://github.com/Facu-Ferreyra/SGRyS",
    "case": {
      "problem": {
        "es": "",
        "en": ""
      },
      "solution": {
        "es": "",
        "en": ""
      }
    },
    "og": "img/og-reclamos-mvcxa4yl.jpg",
    "ogSrc": "img/proj-reclamos.webp"
  },
  {
    "id": "contacto-automatizado",
    "type": "automation",
    "year": 2026,
    "featured": false,
    "tech": [
      "JavaScript"
    ],
    "title": {
      "es": "Formulario de contacto automatizado",
      "en": "Automated Contact Form"
    },
    "desc": {
      "es": "Automatización del formulario de contacto del portfolio: cada mensaje se valida contra spam, se guarda en Google Sheets, avisa por Telegram y le envía al visitante una respuesta automática en su idioma.",
      "en": "Automation for the portfolio's contact form: every message is checked against spam, saved to Google Sheets, announced on Telegram and answered with an automatic reply in the visitor's language."
    },
    "notes": "Funciona en el formulario de devff.tech/contacto. Make.com orquesta Google Sheets y Telegram.",
    "images": [
      "img/proj-contacto-automatizado-mven96q10.webp",
      "img/proj-contacto-automatizado-mven96q11.webp",
      "img/proj-contacto-automatizado-mven96q12.webp",
      "img/proj-contacto-automatizado-mven96q13.webp"
    ],
    "tags": [
      "Make",
      "Google Sheets",
      "Resend",
      "Cloudflare Turnstile",
      "Telegram Bot"
    ],
    "live": "https://devff.tech/contacto",
    "repo": "https://github.com/Facu-Ferreyra/PortFolio_Web",
    "case": {
      "problem": {
        "es": "Un formulario de contacto que solo manda un mail tiene tres problemas: el mensaje puede perderse entre otros correos, no queda un registro ordenado de los contactos y la persona que escribe no sabe si su mensaje llegó.\n\nA eso se suma el spam: un formulario público es un blanco fácil para bots, y una respuesta automática mal hecha puede usarse para mandar correos a terceros.",
        "en": "A contact form that only sends an email has three problems: the message can get lost among other emails, there is no organized record of contacts, and the person writing doesn't know whether their message arrived.\n\nThen there is spam: a public form is an easy target for bots, and a badly built auto-reply can be abused to send emails to third parties."
      },
      "solution": {
        "es": "El backend en Vercel filtra antes de hacer nada: verifica el origen, limita los envíos por IP, usa un campo trampa (honeypot), mide el tiempo de llenado y valida el desafío de Cloudflare Turnstile. Recién ahí envía el mensaje por mail con Resend.\n\nDespués pasa el contacto a un webhook de Make.com (protegido con un secreto), que lo agrega como fila en Google Sheets y manda un aviso a Telegram. Al visitante le llega una respuesta automática en su idioma, enviada desde el dominio propio verificado en Resend, con un texto fijo que no repite su mensaje para que el formulario no sirva para enviar spam.\n\nSi Make o la respuesta automática fallan, el formulario igual confirma el envío y el error queda registrado: lo importante, que el mensaje llegue, nunca depende de los pasos extra.",
        "en": "The Vercel backend filters before doing anything: it checks the origin, rate-limits by IP, uses a honeypot field, measures how long the form took to fill in and validates the Cloudflare Turnstile challenge. Only then does it send the message by email through Resend.\n\nIt then hands the contact to a Make.com webhook (protected with a secret), which adds a row to Google Sheets and sends a Telegram alert. The visitor gets an automatic reply in their language, sent from the custom domain verified in Resend, with a fixed text that never repeats their message so the form can't be used to send spam.\n\nIf Make or the auto-reply fail, the form still confirms the message was sent and the error is logged: what matters, the message arriving, never depends on the extra steps."
      }
    },
    "og": "img/og-contacto-automatizado-mvenbgt4.jpg",
    "ogSrc": "img/proj-contacto-automatizado-mven96q10.webp"
  },
  {
    "id": "portfolio",
    "type": "web",
    "year": null,
    "featured": false,
    "tech": [
      "JavaScript"
    ],
    "title": {
      "es": "Portfolio Web Personal",
      "en": "Personal Web Portfolio"
    },
    "desc": {
      "es": "Sitio personal desarrollado desde cero con HTML, CSS y JavaScript puro. Modo oscuro, animaciones y formulario de contacto funcional.",
      "en": "Personal site built from scratch with vanilla HTML, CSS and JavaScript. Dark mode, animations and a working contact form."
    },
    "notes": "Es este mismo sitio. Bilingüe ES/EN, 5 paletas de color (2 de alto contraste accesibles) y este asistente con IA.",
    "images": [
      "img/proj-portfolio.webp"
    ],
    "tags": [
      "HTML",
      "CSS",
      "JavaScript"
    ],
    "live": "https://devff.tech",
    "repo": "",
    "case": {
      "problem": {
        "es": "",
        "en": ""
      },
      "solution": {
        "es": "",
        "en": ""
      }
    },
    "og": "img/og-portfolio-mvcxa4yl.jpg",
    "ogSrc": "img/proj-portfolio.webp"
  }
]
