MEMORIA FUNCIONAL — ImageOptim
Producto: ImageOptim — Compresión y optimización inteligente de imágenes
URL live: https://imageoptim.theboomer.dev (canonical: https://imageoptim.app/)
Stack: React + Vite + React Router (frontend), FastAPI (backend), Clerk (auth), Stripe (billing), react-helmet-async (SEO), i18n propio ES/EN
Fuente de verdad: code/frontend/src/App.tsx (44 líneas, router shell), pages/Landing.tsx, pages/ToolPage.tsx (613 líneas), i18n.ts, components/
Fecha de documentación: 2026-08-05
1. Introducción
Propósito
ImageOptim es una herramienta web de compresión de imágenes: el usuario arrastra/sube una imagen (JPEG, PNG, WebP, AVIF — hasta 20MB), ajusta calidad, formato de salida, metadatos y dimensiones máximas, y el backend (POST /api/v1/optimize) devuelve la imagen optimizada con comparación visual antes/después, estadísticas de ahorro y descarga directa.
Público objetivo
- Desarrolladores y diseñadores que necesitan imágenes ligeras para web.
- Creadores de contenido y e-commerce managers que optimizan catálogos.
- Equipos (plan Business) con pipelines de alto volumen (API, batch).
Nota de arquitectura
App con dos rutas: / (Landing orientada a SEO, con Helmet + JSON-LD) y /app (ToolPage, la herramienta con vista interna Optimizar/Facturación). Cualquier otra ruta redirige a /. El ToolPage funciona sin sesión (modo demo/dev): Clerk se importa condicionalmente y main.tsx envuelve con un AppShell fallback si falta VITE_CLERK_PUBLISHABLE_KEY. Toda la UI es bilingüe ES/EN vía función t(lang, key) con mapas en i18n.ts.
2. Tipos de usuario (roles)
| Rol | Identificación | Capacidades |
|---|---|---|
| Visitante | Sin sesión (o app sin Clerk key) | Ver Landing completa (SEO). Usar el ToolPage en /app sin límite duro local (contador diario anon_optimizations); tras la primera optimización del día aparece el UpgradeDialog. |
| Usuario registrado (Free) | Sesión Clerk activa | Quota diaria (3/día por defecto del QuotaBadge), badge de uso, acceso a la vista Facturación completa, sin bloqueos. (La Landing publicita 50 optimizaciones/día gratis — ver Reglas de negocio, discrepancia documentada.) |
| Pro | Suscripción Stripe (plan pro, €12/mes según Landing) |
1.000 optimizaciones/día, hasta 50MB por imagen, AVIF + todos los formatos, control de metadatos, resize/crop, API (1K req/día). |
| Business | Suscripción Stripe (plan business, €39/mes) |
10.000 optimizaciones/día, hasta 100MB, batch processing, API (10K req/día), webhooks, soporte prioritario. |
3. Funcionalidades
F3.1 Landing page (SEO) /
Descripción: Página pública de marketing con SEO completo (Helmet: title, meta description, OG, Twitter cards, canonical, hreflang, JSON-LD SoftwareApplication con precios 0–39 EUR y rating 4.8/127).
Flujo:
1. Nav fija (backdrop-blur): logo 🖼️ ImageOptim, enlaces ancla Features/Pricing/Testimonials, botón de idioma EN/ES, CTA "Empezar a Optimizar" → /app.
2. Hero (min-h-screen, fondo con grid + glow): badge "10.000+ imágenes optimizadas", H1 "Comprime imágenes sin perder calidad" (highlight con gradiente), subtítulo, CTAs "Empezar Gratis" → /app y "Ver Funcionalidades" → #features. Barra de stats: -73% compresión media, 1.2s procesamiento medio, 4.8★ valoración + botón "Pruébalo ahora".
3. Features (6 cards): 🎯 Compresión Inteligente, 🔄 Conversión de Formato, 📏 Redimensionar y Recortar, 🧹 Eliminar Metadatos, 📦 Procesamiento por Lotes, 🔌 Acceso API.
4. Pricing (3 cards, hardcodeadas): Free €0/mo (50 optimizaciones/día, hasta 5MB, JPEG/PNG/WebP, compresión básica → "Comenzar"), Pro €12/mo badge "Más popular" (1.000/día, 50MB, AVIF + todos, control metadatos, resize/crop, API 1K → "Prueba Gratuita"), Business €39/mo (10.000/día, 100MB, batch, API 10K, webhooks, soporte prioritario → "Comenzar"). Nota anual: "Planes anuales: 34% descuento — Pro a €95/año, Business a €310/año".
5. Testimonials (3 cards con estrellas ★★★★★/★★★★½, cita, avatar con iniciales, autor y rol).
6. CTA final: "¿Listo para imágenes más rápidas y ligeras?" → "Empezar Gratis" → /app.
7. Footer: copyright, Privacidad, Términos.
Validaciones: Ninguna (página estática). El cambio de idioma persiste vía onSetLang (localStorage lang, default es).
F3.2 Subida de imagen (dropzone) — ToolPage /app
Descripción: Zona de arrastre/clic para seleccionar la imagen.
Flujo:
1. Sin archivo: dropzone con borde punteado, icono Upload, texto "Arrastra una imagen aquí o haz clic para subir", hint "Soporta JPEG, PNG, WebP, AVIF — hasta 20MB".
2. Click → abre el file picker oculto (accept="image/*").
3. Drag & drop: onDragOver activa estado visual (borde primario + fondo); onDrop captura dataTransfer.files[0].
4. Al seleccionar: se muestra la card del archivo con nombre, tamaño formateado (B/KB/MB/GB), botón X para limpiar, y las opciones de optimización. Se genera preview local con URL.createObjectURL.
Validaciones:
- Tipo: si !file.type.startsWith('image/') → error "Please select an image file".
- Tamaño: si file.size > 20MB (20 * 1024 * 1024) → error "File too large. Max size: 20MB".
- El error se muestra en banner rojo en el panel derecho; se limpia al seleccionar otro archivo válido.
F3.3 Opciones de optimización
Descripción: Controles del panel izquierdo (solo visibles con archivo seleccionado).
- Calidad (slider range 1–100, default 80, etiqueta "Calidad: N").
- Formato de salida (pills 2x2): WebP (default) | JPEG | PNG | AVIF.
- Eliminar metadatos (checkbox, default activado): strip EXIF/GPS.
- Ancho máximo / Alto máximo (inputs numéricos, placeholder "Auto"; vacíos = sin redimensionar).
F3.4 Optimizar imagen
Descripción: Envía la imagen y opciones al backend.
Flujo:
1. Pulsar "Optimizar Imagen" (deshabilitado mientras loading; muestra "Optimizando..." con spinner).
2. POST /api/v1/optimize con FormData: file, quality, output_format, strip_metadata, max_width?, max_height?.
3. Si hay sesión: header Authorization: Bearer <token Clerk> + guarda token en localStorage.clerk_token.
4. Respuesta OptimizeResult: {id, original_filename, original_size, optimized_size, compression_ratio, format, width, height, download_url, timestamp}.
5. Tras éxito: anónimo → incrementa anon_optimizations (localStorage, por día) y muestra UpgradeDialog si count > 0; logueado → incrementa usage_log.
6. Errores: banner rojo con errData.detail || 'Error <status>'.
F3.5 Comparación antes/después
Descripción: Panel derecho con comparación visual tras optimizar.
Flujo: 1. Card "Antes / Después" (grid 2 columnas divididas): original (preview local, etiqueta "Original") vs optimizado (desde result.download_url, etiqueta "Optimizado" resaltada en primario). 2. Ambos sobre fondo de damero oscuro (oklch(0.2 0.01 260/0.5)).
F3.6 Estadísticas de ahorro
Descripción: Grid de 4 métricas: Tamaño original (formatBytes(original_size)), Tamaño optimizado (verde), Ahorrado (-{round((1-compression_ratio)*100)}%, primario), Dimensiones (width x height).
F3.7 Descargar optimizada
Flujo: 1. Botón "Descargar (.webp)" → crea <a href=download_url download="optimized.<format>"> y dispara click. 2. Botón "Optimizar Otra" → limpia archivo/preview/resultado y vuelve a la dropzone.
F3.8 Autenticación Clerk (modal)
Descripción: Igual patrón que CaptionAI: SignInButton mode="modal" en header (texto "Iniciar sesión"); tras login: QuotaBadge + pill "Gratis" + UserButton (afterSignOutUrl="/"). useClerkToken() sincroniza el JWT a localStorage.
F3.9 QuotaBadge
Descripción: Badge usados/límite para logueados (default free 3/día; límite real del plan vía GET /api/v1/billing/pricing-plans + summary). Click → vista Facturación si plan free. Uso desde localStorage.usage_log.
F3.10 UpgradeDialog
Descripción: Modal para anónimos tras la primera optimización del día: "Desbloquea mas optimizaciones" / "Crea una cuenta gratis y optimiza hasta 3 imagenes al dia!" + botón "Iniciar sesion con Google" (icono 🖼️, X para cerrar, backdrop clicable).
F3.11 Facturación (vista interna)
Descripción: Misma estructura que CaptionAI/HookGenerator: tarjeta "Tu Plan", PlanCards (con límites daily_captions, max_characters, languages, has_api, ai_model), paquetes de créditos (€, "Comprar" → buy-credits), historial de facturas (nº, fecha, importe, estado coloreado, Ver → hostedUrl), botón "Gestionar facturación" (Customer Portal). Sin token Clerk → solo planes con botones "Inicia sesión" + prompt de login. Back button "Volver".
F3.12 Selector de tema e idioma
Descripción: En el header del ToolPage: 3 botones de tema (Moon/Sun/Monitor, persistidos en localStorage.theme, default dark; system sigue a prefers-color-scheme) y select EN/ES (persistido en localStorage.lang). En la Landing solo el selector de idioma (botón circular).
F3.13 VersionBadge
Descripción: Badge de versión global renderizado por App.tsx en todas las rutas.
4. Pantallas (wireframes textuales)
P4.1 Landing /
┌─ NAV fija ─────────────────────────────────────────────────┐
│ 🖼️ ImageOptim | Features Pricing Testimonials | [ES] [Empezar a Optimizar] │
├─ HERO (min-h-screen, grid de fondo) ───────────────────────┤
│ ✨ 10.000+ imágenes optimizadas │
│ Comprime imágenes sin perder CALIDAD (gradiente) │
│ Arrastra, suelta y optimiza. AVIF, WebP, JPEG, PNG… │
│ [Empezar Gratis →] [Ver Funcionalidades] │
│ ┌─ stats bar ────────────────────────────────┐ │
│ │ -73% | 1.2s | 4.8★ [Pruébalo ahora] │ │
│ └────────────────────────────────────────────┘ │
├─ FEATURES (6 cards) ───────────────────────────────────────┤
│ 🎯 Compresión Inteligente 🔄 Conversión de Formato │
│ 📏 Redimensionar y Recortar 🧹 Eliminar Metadatos │
│ 📦 Procesamiento por Lotes 🔌 Acceso API │
├─ PRICING (3 cards) ────────────────────────────────────────┤
│ [Gratis €0] [Pro €12 ★Más popular] [Business €39] │
│ …planes anuales: 34% descuento… │
├─ TESTIMONIALS (3 cards con ★) ─────────────────────────────┤
├─ CTA: ¿Listo para imágenes más rápidas y ligeras? │
│ [Empezar Gratis →] [Más Información] │
├─ FOOTER: © 2026 ImageOptim | Privacidad | Términos │
└────────────────────────────────────────────────────────────┘
P4.2 ToolPage /app — vista Optimizar (grid 3 col: izq 1 / der 2)
┌─ HEADER ────────────────────────────────────────────────────┐
│ 🖼️ ImageOptim | Optimizar | Facturación │
│ [QuotaBadge] [Gratis] [avatar] [🌙☀️🖥] [EN|ES] | [Iniciar sesión] │
│ MOBILE: tabs Optimizar | Facturación │
├─────────────────────────────────────────────────────────────┤
│ PANEL IZQ (col-1) │ PANEL DER (col-2) │
│ ┌────────────────────────┐ │ ┌───────────────────────────┐ │
│ │ SIN ARCHIVO: │ │ │ SIN ARCHIVO (vacío): │ │
│ │ ╔════════════════════╗ │ │ │ [🖼 icono] │ │
│ │ ║ [⬆ icono] ║ │ │ │ Ningún archivo seleccion.│ │
│ │ ║ Arrastra una imagen ║ │ │ │ Sube una imagen para │ │
│ │ ║ aquí o haz clic ║ │ │ │ ver opciones de compresión│ │
│ │ ║ (dashed border) ║ │ │ └───────────────────────────┘ │
│ │ ╚════════════════════╝ │ │ [banner error rojo si error] │
│ │ CON ARCHIVO: │ │ CON ARCHIVO (preview): │
│ │ [🖼 nombre.jpg 12MB X]│ │ ┌─ ORIGINAL ────────────────┐ │
│ │ Calidad: 80 │ │ │ [preview imagen local] │ │
│ │ [slider 1───100] │ │ └───────────────────────────┘ │
│ │ Formato de salida │ │ CON RESULTADO: │
│ │ [WebP][JPEG][PNG][AVIF]│ │ ┌─ ANTES / DESPUÉS ─────────┐ │
│ │ ☑ Eliminar metadatos │ │ │ ORIGINAL │ OPTIMIZADO │ │
│ │ Ancho máx [Auto] │ │ │ [img] │ [img] │ │
│ │ Alto máx [Auto] │ │ └───────────────────────────┘ │
│ │ [🛠 Optimizar Imagen] │ │ ┌─ stats ───────────────────┐ │
│ │ (u "Optimizando...") │ │ │ T.orig | T.optim | Ahorro │ │
│ └────────────────────────┘ │ │ 12MB | 3.2MB | -73% │ │
│ │ │ Dimensiones: 1920x1080 │ │
│ │ └───────────────────────────┘ │
│ │ [⬇ Descargar (.webp)] [🔄 Optimizar Otra] │
└────────────────────────────┴─────────────────────────────────┘
│ [UpgradeDialog] si anónimo tras 1ª optimización del día │
P4.3 ToolPage /app — vista Facturación
[← Volver]
Facturación — Gestiona tu suscripción y facturación [Gestionar facturación ↗]
┌─ TU PLAN ───────────────────────────────┐
│ Suscrito a: Pro [Activo🟢] │
│ Fecha de renovación: … │
│ 12.00 EUR/month │
└─────────────────────────────────────────┘
┌─ PLANES DE PRECIOS ─────────────────────┐
│ [PlanCard Free] [Pro] [Enterprise] │
└─────────────────────────────────────────┘
┌─ PAQUETES DE CRÉDITOS ──────────────────┐
│ [1000 créditos €X Comprar] … │
└─────────────────────────────────────────┘
┌─ HISTORIAL DE FACTURAS ─────────────────┐
│ Nº | Fecha | Importe | Estado | Ver │
└─────────────────────────────────────────┘
P4.4 Modal UpgradeDialog
┌─ overlay ───────────────────────────────┐
│ ┌────────────────────────────────────┐ │
│ │ [🖼️] [X] │ │
│ │ Desbloquea mas optimizaciones │ │
│ │ Crea una cuenta gratis y optimiza │ │
│ │ hasta 3 imagenes al dia! │ │
│ │ [G Iniciar sesion con Google] │ │
│ └────────────────────────────────────┘ │
└─────────────────────────────────────────┘
5. Flujos de trabajo
Flujo A: Alta y onboarding
- Visitante llega a
/(Landing SEO) o directo a/app. - Pulsa "Empezar Gratis" →
/app(no requiere registro para probar). - Usa la herramienta como anónimo; tras la primera optimización del día aparece el UpgradeDialog.
- "Iniciar sesion con Google" → modal Clerk → cuenta creada; el header pasa a mostrar QuotaBadge
0/3, pill "Gratis" y avatar. - (Opcional) Facturación → suscribirse a un plan de pago.
Flujo B: Optimización completa
- En
/app, arrastrar o hacer clic para subir una imagen (≤20MB, tipo image/*). - Ajustar opciones: calidad (slider), formato de salida, strip de metadatos, ancho/alto máximos.
- Pulsar "Optimizar Imagen" → estado "Optimizando..." → POST
/api/v1/optimize. - Ver comparación antes/después + estadísticas (tamaños, % ahorrado, dimensiones).
- "Descargar (.formato)" → descarga local
optimized.<format>, o "Optimizar Otra" para reiniciar.
Flujo C: Errores y validaciones
- Archivo no-imagen → banner "Please select an image file".
- Archivo >20MB → banner "File too large. Max size: 20MB".
- Fallo de API → banner con
detaildel backend. - En todos los casos la dropzone/card del archivo permanece y se puede reintentar.
Flujo D: Billing
- Tab "Facturación" (o clic en QuotaBadge si free).
- Sin login: solo planes (botones "Inicia sesión") + prompt.
- Con login: ver plan actual, suscribirse (Stripe Checkout), gestionar (Customer Portal), comprar créditos, ver facturas.
Flujo E: Upgrade y límites
- Plan Free: el badge muestra
usados/3; al agotarse, el backend rechaza con error (frontend muestra banner). - Upgrade a Pro/Business vía Facturación → el badge usa
limits.daily_captionsdel plan (/api/v1/billing/pricing-plans). - Enterprise (si aplica): acceso API según
limits.has_api(la Landing menciona API en Pro/Business; la UI de billing lo refleja en PlanCard).
6. Reglas de negocio
| Regla | Detalle |
|---|---|
| Tamaño máximo de imagen | 20MB en frontend (validación local file.size > 20 * 1024 * 1024). La Landing anuncia límites por plan (5MB free, 50MB Pro, 100MB Business) — el límite real de backend prevalece. |
| Tipos aceptados | Solo image/* (validación por MIME type en cliente). |
| Quota anónima | Contador diario anon_optimizations en localStorage (fecha + count); tras la 1ª optimización del día se muestra el UpgradeDialog en cada generación posterior. |
| Quota logueados | localStorage.usage_log alimenta el QuotaBadge; límite del plan desde Stripe (pricing-plans.limits.daily_captions), default free 3/día. Discrepancia documentada: la Landing publicita "50 optimizaciones/día" para el plan Free y "hasta 5MB por imagen", mientras que el QuotaBadge/UpgradeDialog del frontend usan 3/día. El límite autoritativo lo impone el backend por plan. |
| Formato de salida | WebP (default), JPEG, PNG, AVIF. |
| Calidad | Slider 1–100, default 80. |
| Metadatos | Checkbox "Eliminar metadatos" default ON (envía strip_metadata=true). |
| Redimensionado | Opcional vía max_width/max_height (solo se envían si tienen valor; vacío = Auto). |
| Suscripción | GET /api/v1/billing/summary determina plan/estado; active verde, resto ámbar; aviso de cancelación a fin de período si cancelAtPeriodEnd. |
| Planes (Landing) | Hardcodeados: Free €0 (50/día, 5MB, JPEG/PNG/WebP, compresión básica), Pro €12 (1.000/día, 50MB, AVIF, metadatos, resize/crop, API 1K), Business €39 (10.000/día, 100MB, batch, API 10K, webhooks, soporte prioritario). Anual: Pro €95/año, Business €310/año (-34%). Los precios del billing real provienen de Stripe. |
| SEO | Landing con canonical https://imageoptim.app/, hreflang EN/ES/x-default, JSON-LD SoftwareApplication (precio 0–39 EUR, rating 4.8/127). La ruta /app tiene noindex, nofollow. |
| Auth | Clerk; la app funciona en modo demo sin publishable key (ClerkProvider condicional en main.tsx). Token persistido en localStorage.clerk_token. |
| Tema/Idioma | Tema: dark default (light/system disponibles), persistido localStorage.theme. Idioma: es default, persistido localStorage.lang, select EN/ES en header de la herramienta y botón en la Landing. |
Endpoints consumidos por el frontend
POST /api/v1/optimize— multipart{file, quality, output_format, strip_metadata, max_width?, max_height?}→OptimizeResult.GET /api/v1/billing/pricing-plans·POST /api/v1/billing/create-checkout-session·POST /api/v1/billing/create-portal-session·GET /api/v1/billing/summary·GET /api/v1/billing/invoices·GET /api/v1/billing/credit-packs·POST /api/v1/billing/buy-credits— billing Stripe (víaservices/stripe.service.ts).