/* Portafolio Cristian — single React component bundle */ const { useState, useEffect, useRef, useMemo } = React; const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "accent": "amber", "showSpotlight": true, "heroLayout": "split" } /*EDITMODE-END*/; const ACCENT_PRESETS = { amber: { a: "oklch(0.82 0.14 75)", b: "oklch(0.72 0.12 40)" }, emerald: { a: "oklch(0.78 0.14 155)", b: "oklch(0.68 0.13 200)" }, violet: { a: "oklch(0.78 0.13 305)", b: "oklch(0.68 0.16 270)" }, rose: { a: "oklch(0.80 0.13 15)", b: "oklch(0.70 0.15 345)" } }; /* ---------- Reveal hook ---------- */ function useReveal() { useEffect(() => { const els = document.querySelectorAll('.reveal'); const io = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add('in'); io.unobserve(e.target); } }); }, { threshold: .12, rootMargin: '0px 0px -8% 0px' }); els.forEach((el) => io.observe(el)); return () => io.disconnect(); }, []); } /* ---------- Count-up ---------- */ function CountUp({ to, suffix = "" }) { const [v, setV] = useState(0); const ref = useRef(null); useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver(([e]) => { if (e.isIntersecting) { const start = performance.now(); const dur = 1200; const tick = (t) => { const p = Math.min(1, (t - start) / dur); const eased = 1 - Math.pow(1 - p, 3); setV(Math.round(eased * to)); if (p < 1) requestAnimationFrame(tick); }; requestAnimationFrame(tick); io.disconnect(); } }); io.observe(el); return () => io.disconnect(); }, [to]); return {v}{suffix}; } /* ---------- Icons (inline svg, simple) ---------- */ const Arrow = ({ size = 14 }) => ; const ArrowDown = () => ; const Mail = () => ; const GitHub = () => ; const LinkedIn = () => ; const WhatsApp = () => ; const FileDown = () => ; /* ---------- Tech icons (simple stylized marks) ---------- */ const TechIcon = ({ name, size = 32 }) => { const props = { width: size, height: size, viewBox: "0 0 32 32", fill: "none" }; switch (name) { case "java":return ( Java); case "spring":return ( ); case "angular":return ( ); case "postgres":return ( PostgreSQL); case "docker":return ( Docker); case "laravel":return ( Laravel); case "ts":return ( ); default:return null; } }; const NAV = [ { id: "inicio", label: "Inicio" }, { id: "sobre", label: "Sobre mí" }, { id: "habilidades", label: "Habilidades" }, { id: "proyectos", label: "Proyectos" }, { id: "experiencia", label: "Experiencia" }, { id: "educacion", label: "Educación" }, { id: "contacto", label: "Contacto" }]; const SKILLS = [ { num: "01", title: "Backend", items: ["Java 21", "Spring Boot", "Spring Security", "Spring Data JPA", "Spring WebFlux", "Flyway", "Laravel 12", "PHP 8.3"] }, { num: "02", title: "Frontend", items: ["Angular 20", "TypeScript", "JavaScript", "Tailwind CSS", "Electron", "Standalone Components", "Signals"] }, { num: "03", title: "Datos", items: ["PostgreSQL", "MySQL", "Redis", "Flyway Migrations", "Apache POI", "Reportes en Excel", "Web3j"] }, { num: "04", title: "Plataforma", items: ["Docker", "Digital Ocean", "Git / GitHub", "Maven", "JWT", "OAuth2 / Keycloak", "Swagger / OpenAPI", "SourceAFIS"] }]; const PROJECTS = [ { n: "01", cat: "Plataforma SaaS", title: "Sistema Revolución Atlética", desc: "Plataforma integral de gestión de gimnasios: socios, membresías, inventario por turno, punto de venta, corte de caja y asesorías. Multitenant para administrar múltiples sucursales desde un único panel.", stack: ["Java 21", "Spring Boot", "Angular 20", "Electron", "PostgreSQL", "Docker", "SourceAFIS", "JWT"], role: "Full Stack · 2024 – Presente", link: "#", private: true, visual: { kind: "badge", label: "REVATL · v1.4", big: "R/A" }, gallery: [ "assets/proyectos/ra-0.png", "assets/proyectos/ra-1.png", "assets/proyectos/ra-2.png", "assets/proyectos/ra-3.png", "assets/proyectos/ra-4.png", "assets/proyectos/ra-5.png", "assets/proyectos/ra-6.png"] }, { n: "02", cat: "Backend · Transporte", title: "Yajalones App", desc: "Backend completo para sistema de transporte interurbano Yajalón – Tuxtla Gutiérrez. Cálculo automático de tarifas por tipo de pasajero (Adulto, Niño, INAPAM) y gestión de viajes, paquetería, choferes y turnos.", stack: ["Java 21", "Spring Boot", "PostgreSQL", "JWT", "Docker", "Digital Ocean"], role: "Backend · 2025", link: "#", private: true, visual: { kind: "badge", label: "API · v1.0", big: "YJL" }, gallery: [ "assets/proyectos/yl-1.jpeg", "assets/proyectos/yl-2.jpeg"] }, { n: "03", cat: "Fintech · Blockchain", title: "Green Wallet", desc: "API REST de una plataforma de fidelización para PYMES con sistema de puntos y cashback descentralizado en blockchain. Roles de cliente, empresa afiliada y administrador con Keycloak.", stack: ["Spring Boot", "Keycloak", "OAuth2", "Web3j", "PostgreSQL", "Swagger"], role: "Backend · 2025 — Completado", link: "https://github.com/CristianVazquez09/Green-Wallet", visual: { kind: "badge", label: "GW · v1.0", big: "GW₿" }, gallery: [ "assets/proyectos/gw-1.jpg", "assets/proyectos/gw-2.jpg", "assets/proyectos/gw-3.jpg", "assets/proyectos/gw-4.png"] }, { n: "04", cat: "Sector público", title: "Gestión de proyectos — CEAS Chiapas", desc: "API REST para el seguimiento de proyectos, subproyectos y metas institucionales de una entidad gubernamental. Flujo de aprobación trimestral, indicadores automáticos e historial auditado de presupuestos.", stack: ["Spring Boot", "Spring Security", "PostgreSQL", "Flyway", "Problem Details RFC 7807"], role: "Backend · 2025 — En desarrollo", link: "#", private: true, visual: { kind: "terminal", label: "CEAS · API", title: "ceas-api · REST", request: { method: "GET", path: "/api/v1/proyectos/42/avances?trimestre=Q3" }, status: "200 OK · 38ms", response: [ "{", ' "proyecto": "Modernización hídrica",', ' "trimestre": "Q3",', ' "meta": "Cobertura 85%",', ' "cumplimiento": 0.82,', ' "estado": "APROBADO",', ' "revisadoPor": "ADMIN",', ' "historial": 4', "}"] } }, { n: "05", cat: "CRM · Sector financiero", title: "CRM Gea Corp", desc: "CRM a medida para Gea Corp (geacorp.uk) con dos paneles diferenciados: administrativo para asesores y staff, y portal exclusivo para clientes. Migración del sistema legado desde CodeIgniter 2 a Laravel 12.", stack: ["Laravel 12", "Filament 3", "PHP 8.3", "MySQL", "Redis", "OPcache"], role: "Full Stack · 2025 — Completado", link: "https://geacorp.uk/", visual: { kind: "badge", label: "geacorp.uk", big: "GC" }, gallery: [ "assets/proyectos/gp-1.png", "assets/proyectos/gp-2.png", "assets/proyectos/gp-3.png", "assets/proyectos/gp-4.png", "assets/proyectos/gp-5.png", "assets/proyectos/gp-6.png"] }]; const JOBS = [ { year: "2024 – Presente", title: "Desarrollador Full Stack — Revolución Atlética", role: "Diseño de arquitectura · Backend & desktop app", desc: "Diseñé y desarrollé una plataforma para centralizar socios, membresías, inventario, punto de venta, cortes de caja, asesorías y control de acceso biométrico. Implementé backend con Spring Boot, seguridad con JWT, arquitectura multitenant, reportes en Excel y documentación OpenAPI. También desarrollé la aplicación de escritorio con Angular y Electron, desplegada en Digital Ocean con Docker.", tags: ["Spring Boot", "Angular", "Electron", "Docker", "PostgreSQL", "JWT"] }, { year: "2025", title: "Desarrollador Backend — Yajalones App", role: "API REST de transporte interurbano", desc: "Desarrollé el backend de un sistema de transporte para gestionar viajes, pasajeros, paquetería, choferes y turnos. Implementé lógica de negocio para el cálculo automático de tarifas por tipo de pasajero, ruta y dirección, con recálculo de totales al agregar o confirmar registros. El sistema integra autenticación JWT, Spring Security y manejo global de excepciones.", tags: ["Spring Boot", "Spring Security", "JWT", "Docker", "Digital Ocean"] }, { year: "2025 — En desarrollo", title: "Desarrollador Backend — CEAS Chiapas", role: "Sistema de gestión de proyectos institucionales", desc: "Desarrollo una API REST para el seguimiento de proyectos, subproyectos y metas institucionales de una entidad gubernamental. Implementé autenticación con JWT, control de roles ADMIN/AREA, flujo de aprobación y rechazo de avances trimestrales, recálculo automático de indicadores de cumplimiento e historial auditado de cambios presupuestales.", tags: ["Spring Boot", "PostgreSQL", "Flyway", "JWT", "Problem Details"] }, { year: "2025", title: "Desarrollador Backend — Green Wallet", role: "Fidelización con cashback en blockchain", desc: "Desarrollé la API REST de una plataforma de fidelización para PYMES con sistema de puntos y cashback. Implementé autenticación y autorización con Keycloak, roles para clientes, empresas afiliadas y administradores, además de integración con Web3j para registrar transacciones sensibles en blockchain.", tags: ["Spring Boot", "Keycloak", "OAuth2", "Web3j", "Swagger"] }, { year: "2025", title: "Desarrollador Full Stack — Gea Corp", role: "CRM remoto sector financiero", desc: "Desarrollé y desplegué un CRM para el sector financiero con panel administrativo, portal de clientes, gestión de estados de cuenta, sistema de tickets, reportes PDF y control de acceso por roles. Migré el sistema desde CodeIgniter 2 a Laravel 12 con Filament 3, integrando Redis y OPcache para optimización.", tags: ["Laravel", "Filament", "Redis", "MySQL", "Digital Ocean"] }]; const CERTS = [ { n: "01", title: "Desarrollo Backend con Java 17", year: "2024", file: "assets/certificados/certificado-java-backend.pdf" }, { n: "02", title: "Desarrollo Full Stack con Java", year: "2024", file: "assets/certificados/certificado-java-fullstack.pdf" }, { n: "03", title: "Spring WebFlux — Programación Reactiva", year: "2024", file: "assets/certificados/certificado-java-webflux.pdf" }]; /* ---------- Nav ---------- */ function Nav({ onMenu, isLight }) { const [scrolled, setScrolled] = useState(false); useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 60); addEventListener('scroll', onScroll, { passive: true }); onScroll(); return () => removeEventListener('scroll', onScroll); }, []); return (
Cristian Vázquez
Hablemos
); } function Drawer({ open, onClose }) { return (
Cristian Vázquez
Descargar CV
); } /* ---------- Hero ---------- */ function Hero() { return (
Disponible para colaborar · CDMX / Remoto

Transformo
necesidades de negocio
en sistemas funcionales.

Diseño y desarrollo plataformas web orientadas a resolver procesos reales de negocio, integrando APIs seguras, lógica backend sólida y despliegues en la nube. Mi enfoque está en construir software mantenible, escalable y listo para operar en entornos reales.

Ver proyectos Conóceme
Cristian Vázquez EN LÍNEA · BACKEND
ID · 0967.145.9575 CHIAPAS, MX
Stack principal
en producción
Java  Spring Boot Angular 20 PostgreSQL Docker Laravel 12 TypeScript
SCROLL
); } /* ---------- About ---------- */ function About() { return (
SOBRE MÍ

Construyo backends
que sostienen
sistemas reales.

Soy Cristian Ivan Vázquez Gómez, desarrollador de software especializado en backend con Java y Spring Boot. Me enfoco en crear sistemas seguros, escalables y bien estructurados, pensados para resolver necesidades reales de negocio.

He trabajado en proyectos donde no solo importa que el sistema funcione, sino que sea mantenible, claro y preparado para crecer. Por eso cuido la arquitectura, la lógica de negocio, la seguridad, la documentación y el despliegue en producción.

Además del backend, también puedo integrar soluciones full stack cuando el proyecto lo necesita, conectando interfaces, APIs, bases de datos y servicios en la nube.

PROYECTOS REALES
SISTEMAS DESPLEGADOS
SECTORES DISTINTOS
); } /* ---------- Skills ---------- */ function Skills() { return (
HABILIDADES

El stack con el que construyo sistemas listos para producción.

{SKILLS.map((s, i) =>
{s.num}.

{s.title}

    {s.items.map((it) =>
  • {it}
  • )}
)}
); } /* ---------- Projects ---------- */ function ProjectGallery({ images, label }) { const [idx, setIdx] = useState(0); const [paused, setPaused] = useState(false); const n = images.length; const go = (i) => setIdx((i + n) % n); const next = () => go(idx + 1); const prev = () => go(idx - 1); // auto-advance useEffect(() => { if (paused) return undefined; const t = setInterval(() => setIdx((i) => (i + 1) % n), 3200); return () => clearInterval(t); }, [paused, n]); return (
setPaused(true)} onMouseLeave={() => setPaused(false)}>
{label} {String(idx + 1).padStart(2, '0')} / {String(n).padStart(2, '0')}
{images.map((src, i) => {`${label} )}
{images.map((_, i) =>
); } function ProjectVisual({ v, gallery }) { if (gallery && gallery.length) { return ; } if (v.kind === "terminal") { return (
{v.title}
WIP
{v.request.method} {v.request.path}
{v.status}
{v.response.map((line, i) => 
{line}
)}
); } return (
{v.label}↘ CASE
{v.big}
[ Captura · drop image ]
); } function Projects() { return (
PROYECTOS

Casos reales en
producción.

Cinco sistemas desplegados para clientes en transporte, fitness, sector público, fintech y servicios financieros. Cada uno se entregó con documentación OpenAPI completa .

{PROJECTS.map((p, i) =>
{p.n}. {p.cat}

{p.title}

{p.desc}

{p.stack.map((s) => {s})}
{p.role}
{!p.private && Ver caso }
)}
); } /* ---------- Experience ---------- */ function Experience() { return (
EXPERIENCIA

Roles, responsabilidades
y resultados.

{JOBS.map((j, i) =>
{j.year}

{j.title}{j.role}

{j.desc}
{j.tags.map((t) => {t})}
)}
); } /* ---------- Education + Certs ---------- */ function Edu() { return (
EDUCACIÓN

Base formal y aprendizaje continuo.

Instituto Tecnológico de Tuxtla Gutiérrez

Ingeniería en Sistemas Computacionales

8° Semestre (cursando) · 2021 – Presente · Tuxtla Gutiérrez, Chiapas
CERTIFICACIONES

Especialización
en backend Java.

{CERTS.map((c, i) =>
{c.n}.

{c.title}

{c.year}
Ver
)}
); } /* ---------- CV download CTA ---------- */ function CVCta() { return (
CURRÍCULUM

Conoce mi recorrido profesional completo.

Un resumen claro de mi experiencia construyendo sistemas backend, soluciones full stack, APIs seguras y plataformas listas para producción.

Descargar CV (PDF) Abrir en pestaña
PDF · 2 pp
); } /* ---------- Contact ---------- */ function Contact() { const items = [ { lab: "Correo", value: "cristian09tr@gmail.com", href: "mailto:cristian09tr@gmail.com", ic: }, { lab: "LinkedIn", value: "linkedin.com/in/cristian-ivan-vg-09t", href: "https://www.linkedin.com/in/cristian-ivan-vg-09t", ic: }, { lab: "GitHub", value: "github.com/CristianVazquez09", href: "https://github.com/CristianVazquez09", ic: }, { lab: "WhatsApp", value: "+52 967 145 9575", href: "https://wa.me/529671459575", ic: }]; return (
CONTACTO

¿Tienes un proyecto en mente?
Conversemos.

Estoy abierto a colaboraciones, proyectos freelance y oportunidades de tiempo completo en equipos que valoren la ingeniería de software con buenas prácticas. Respondo en menos de 24 horas.

{items.map((it, i) =>
{it.ic}
{it.lab}

{it.value}

Ir
)}
); } /* ---------- Footer ---------- */ function Footer() { const year = new Date().getFullYear(); return ( ); } /* ---------- Tweaks ---------- */ function Tweaks({ t, setTweak }) { if (!window.TweaksPanel) return null; const { TweaksPanel, TweakSection, TweakRadio, TweakToggle } = window; return ( setTweak('accent', v)} /> setTweak('showSpotlight', v)} /> ); } /* ---------- App ---------- */ function App() { const [tweaks, setTweaks] = useState(TWEAK_DEFAULTS); const setTweak = (k, v) => { setTweaks((prev) => { const next = typeof k === 'object' ? { ...prev, ...k } : { ...prev, [k]: v }; try {window.parent.postMessage({ type: '__edit_mode_set_keys', edits: typeof k === 'object' ? k : { [k]: v } }, '*');} catch {} return next; }); }; const [drawer, setDrawer] = useState(false); const [navLight, setNavLight] = useState(false); // accent variables useEffect(() => { const a = ACCENT_PRESETS[tweaks.accent] || ACCENT_PRESETS.amber; document.documentElement.style.setProperty('--accent', a.a); document.documentElement.style.setProperty('--accent-2', a.b); }, [tweaks.accent]); // spotlight toggle useEffect(() => { const sp = document.getElementById('spotlight'); if (sp) sp.style.display = tweaks.showSpotlight ? '' : 'none'; }, [tweaks.showSpotlight]); useReveal(); // detect nav over light sections useEffect(() => { const lightSections = document.querySelectorAll('.skills, .edu, .contact, footer'); const onScroll = () => { const navH = 70; let isLight = false; lightSections.forEach((s) => { const r = s.getBoundingClientRect(); if (r.top <= navH && r.bottom >= navH) isLight = true; }); setNavLight(isLight); }; addEventListener('scroll', onScroll, { passive: true }); onScroll(); return () => removeEventListener('scroll', onScroll); }, []); // tweaks panel handles its own host registration; no app-level listener needed // hero portrait parallax useEffect(() => { const el = document.getElementById('heroPortrait'); if (!el) return; const onScroll = () => { const y = window.scrollY; el.style.transform = `translateY(${y * -0.04}px) scale(${1 + Math.min(.04, y * 0.0001)})`; }; addEventListener('scroll', onScroll, { passive: true }); return () => removeEventListener('scroll', onScroll); }, []); return ( <>