Read Mecómo está hecho
Este sitio es un export estático de Next.js alimentado por seis APIs externas. Toda la data se refresca en cada build, con fallback a caché en JSON.
El portafolio digital de Jorge Marquez Lander
Este es el portafolio digital de Jorge Marquez Lander: un solo sitio que reúne, con el mismo lenguaje visual, las dos carreras que definen su vida — la ingeniería civil y la actuación — en lugar de dos portafolios separados.
La idea nació de una constatación práctica: el trabajo de un ingeniero se mide en kilómetros de fibra, subestaciones y presupuesto; el de un actor, en personajes, escena y sets. Nada de eso encajaba en un portafolio convencional.
Aquí conviven ambas trayectorias sin renunciar a ninguna, y el sitio aprovecha para mostrar además lo que no cabe en un currículum: los viajes, los libros, la música, el cine y las redes.
Todo está construido sobre datos reales que se refrescan solos. No es una maqueta ni un demo: es el portafolio en producción, con su propia agenda de videollamadas, su asistente de IA y su formulario de contacto.
Ingeniero Civil
28+ años de trayectoria en infraestructura crítica, telecomunicaciones y gestión de proyectos en Latinoamérica: redes de fibra óptica y HFC, infraestructura energética y obra civil. Doce competencias técnicas que van desde la gerencia de proyectos y los sistemas eléctricos hasta el desarrollo web.
- Experiencias
- 15
- Formación académica
- 20
- Competencias técnicas
- 12
Actor
Trayectoria en cine, teatro, televisión, comerciales y casting: cortometrajes, videoclips, workshops y campañas. Miembro de la Academia de Ciencias y Artes Cinematográficas de Venezuela.
- Experiencias
- 68
- Formación artística
- 25
- Videos en videoteca
- 150
El proyecto en números
Cómo se publica
El flujo
Un script de PowerShell encadena los cuatro syncs, el timestamp, el build estático y la subida a Firebase Hosting. Una variante estricta valida typecheck y lint antes de sincronizar.
El hosting
- Plan
- Firebase Spark
- Raíz
- public/out
- URLs
- cleanUrls + 21 rewrites
- Producción
- https://jorgemarquezlander.web.app
Ver el código
El repositorio es público. La documentación completa, con comandos y variables de entorno, vive en el README de GitHub.
GitHubLas cuatro capas que hacen que el sitio responda
Además del contenido editorial, el sitio tiene cuatro integraciones que corren enteras en el navegador: un asistente de IA, la agenda de videollamadas, el canal de mensajes y el traductor. Ninguna pasa por un servidor propio.
GEORGEBOT
Asistente de IA personal, siempre accesible desde el botón flotante. Responde en primera persona, con mi voz, y conoce la trayectoria, los artículos, los viajes y el ADN musical del sitio.
- →Arma su contexto en el cliente: perfil, historial y ratings de Trakt, watchlist y los tops de Last.fm.
- →Consulta por streaming al proveedor configurado, con reintento entre modelos y respuesta sin streaming si la cuota choca.
- →Enriquece lo que responde: embebe los videos de YouTube que menciona, convierte un enlace de agenda en botón y construye rutas internas del sitio.
Gemini por defecto, o cualquier endpoint compatible con OpenAI (BlueMinds, OpenRouter) cambiando una variable.
Calendly
La agenda real de 30 minutos, embebida en un modal con la estética del sitio. Aparece en el pie, en el botón flotante, en la página de contacto y dentro del propio chat.
- →Loader con progreso que espera el evento
postMessagereal de Calendly, con red de seguridad a 10 segundos. - →CSS inyectado para forzar el fondo oscuro y evitar el destello blanco; en móvil el modal pasa a pantalla completa.
- →El idioma de la agenda sigue al idioma activo del traductor, con 10 locales mapeados.
calendly.com/jorgemarquezlander/30minTelegram
El formulario de contacto entrega el mensaje directamente a la Bot API de Telegram. No hay buzón ni servidor de por medio: el envío se hace desde el navegador. Aparece en la página de contacto y en el panel flotante.
- →Campos de nombre, email, teléfono opcional y mensaje, validados con
zod. - →Multimedia: grabar audio, grabar video con cambio de cámara frontal/trasera, o adjuntar archivos. Cada adjunto viaja como mensaje aparte, numerado.
- →Se incluye la IP del visitante para poder identificar el mensaje, y la interfaz lo dice de forma explícita.
Traductor
Todo el sitio es traducible a nueve idiomas desde el botón del globo en la cabecera. No es una traducido por ruta: se inyecta en el cliente y traduce la página completa en el sitio.
- →Selector propio con español, inglés, francés, italiano, portugués, alemán, chino, árabe y japonés.
- →La preferencia se guarda en cookie de Google Translate y sobrevive a la navegación; volver a español la limpia y recarga.
- →Se ocultan los spinners y el banner del traductor, y se bloquea el desplazamiento que Google inyecta en el body.
El idioma es puramente cosmético: no cambia las rutas ni el contenido, que sigue siendo el del sitio original en español.
El contexto del bot
El build genera además un archivo de contexto agregado con las cifras del sitio (posts, experiencias, títulos vistos, rutas). Conviene saber que hoy ese archivo no lo consume el chat: GEORGEBOT arma su contexto en el navegador a partir de los JSON individuales. El archivo se conserva como respaldo y como resumen para otras automatizaciones.
Cómo se clasifican 1.585 posts
El Hub Social clasifica cada post en cuatro capas, por orden de prioridad. Es first-match-wins: gana la primera capa que coincide.
- 1
Travel Atlas
Posts de viaje curados a mano por país. No se auto-clasifican: el clasificador de captions confundía «nyc» con un @handle y «francia» con una plaza de Caracas.
- 2
Matching a experiencias
instagram-matches.json cruza captions, @handles, hashtags, postIds y dateRange contra 83 experiencias.
- 3
Keywords de caption
Reglas por palabras sobre el texto del caption, en el client.
- 4
postIdOverrides
Asignación manual por ID de post. Tiene máxima prioridad sobre todo lo demás.
Orden de resolución
El orden importa más de lo que parece: Running y Viajes van segundo y tercero, muy al principio, no al final.
13 categorías resultantes
Resultado verificado: 457 posts matcheados a 83 experiencias y 1.128 sin match, que caen por keywords al resto de categorías.
Stack tecnológico
| Capa | Tecnología | Versión | Uso en el proyecto |
|---|---|---|---|
| Framework | Next.js (App Router) | 15.2.8 | Render estático + metadata por ruta |
| UI | React | 18.3.1 | Componentes cliente (*-client-page.tsx) |
| Lenguaje | TypeScript | 5.x | strict, tsc --noEmit |
| Estilos | Tailwind CSS | 3.4.1 | 45 componentes ui/ estilo shadcn/ui |
| Primitivas | Radix UI | varios | Dialog, Sheet, Tabs, Accordion, Tooltip |
| Carruseles | Embla Carousel | 8.1.3 | Videos, libros, posts, servicios |
| Tema | next-themes | 0.3.0 | Modo oscuro forzado |
| i18n | next-intl | 4.8.2 | messages/es.json, locale único 'es' |
| Analítica | @next/third-parties | 16.1.6 | Google Analytics |
| Validación | zod | 3.24.2 | Esquemas de datos y formularios |
| Gráficos | Recharts · three | 2.15 · 0.185 | Visualizaciones y escena 3D |
| Audio | wav | 1.0.2 | Reproductor de la Audioteca |
| Scraping | Puppeteer | 25.4.0 | Solo scripts de sync (Trakt) |
| Hosting | Firebase Hosting | Spark | Sirve public/out con cleanUrls |
@next/third-parties y eslint-config-next van por delante del next fijado en 15.2.8. El build funciona, pero conviene alinearlas en una pasada de mantenimiento.
Cómo está estructurado
Patrón de páginas
app/<seccion>/page.tsx # servidor: metadata, canonical, JSON-LD app/<seccion>/<seccion>-client-page.tsx # cliente: UI, hooks, interacciones
Las rutas dinámicas usan generateStaticParams para que output: 'export' emita un HTML por slug. Resultado: 23 páginas estáticas, sin servidor en producción.
Doble carpeta public/
jorgemarquezlander/public/ # proyecto Next.js └── public/ # assets que Next sirve
El doble public/ es intencional: es la ruta estática que Next.js sirve. Los JSON de caché se copian también a public/public/data/ para poder hacer fetch estático desde el cliente.
Datos: estático + live
- →Los hooks cliente (
use-trakt-data,use-music-data,use-blog-data,use-books-data) consultan las APIs externas en vivo y caen al JSON cacheado cuando la API falla o no hay red. - →El build refresca los JSON, de modo que el sitio nunca queda vacío aunque las APIs cambien o fallen.
Qué hay y dónde
Hero con video, sobre mí, servicios, blog, feed social, atlas
Trayectoria de ingeniería: tabs, modales, certificados
Trayectoria actoral, ficha técnica, galería, lightbox
9 servicios profesionales y su detalle
Hub Social: grilla de 1.585 posts categorizados, lightbox, stats
Ecosistema: bento de libros, Trakt, IG, música, mapa, atlas
150 videos de YouTube en 9 categorías + reproductor
Historial y watchlist de Trakt con pósters
Historial de Last.fm con filtros y reproducción
512 libros con estantes, búsqueda y modal
Artículos con HTML rico, share y metadatos
Atlas de viajes: mapa, carruseles por país, grilla por tag
Datos de contacto y formulario
Esta documentación del proyecto
Seis APIs, un solo build
Trakt.tv + TMDB
Historial y watchlist con Puppeteer, metadatos en español de TMDB
trakt_history.json, trakt_watchlist.jsonYouTube Data v3
Videos del canal con thumbnails, categorización por keywords
videoteca-data.ts, profile-data-es.tsInstagram Graph API
Pipeline fetch → diff → match → merge → imágenes
instagram-*.json, travel-atlas.jsonLast.fm
Historial, recent, top tracks/artists + enriquecimiento
lastfm_*.json (4 archivos)Google Books
Portadas, descripciones en español e ISBN
books-data.tsGoogle Analytics
Analítica del sitio vía @next/third-parties
—