El código de este portfolio: un sitio en producción que también es la muestra de trabajo.
Next.js 16 (App Router), React 19 con React Compiler, TypeScript estricto, bilingüe ES/EN con next-intl, Sanity como CMS headless, PostgreSQL vía Prisma, tests con Vitest + Playwright, deploy en Vercel. No es una landing de una tarde: blog con CMS headless, newsletter con double opt-in, panel admin con NextAuth, flujos GDPR completos, 207 tests unit/integration + 47 E2E en 5 navegadores, y CI con 6 workflows.
- Qué incluye
- Stack Tecnológico
- Setup y Desarrollo
- Estructura del Proyecto
- Testing
- Deployment
- Licencia
- Créditos
| Área | Detalle |
|---|---|
| i18n | ES (default, sin prefijo) / EN bajo /en, next-intl, diccionarios en messages/ |
| Blog | CMS headless en Sanity (Markdown o Portable Text), búsqueda full-text con debounce, comentarios vía Giscus (GitHub Discussions), TOC automático, posts relacionados |
| Proyectos | Fuente dual Sanity + GitHub API (topics portfolio/featured), filtros por tecnología, ISR de 1 hora sobre el output renderizado |
| Newsletter | Double opt-in, rate limiting (Upstash Redis), templates con React Email, panel admin protegido con NextAuth (GitHub OAuth + allowlist de ADMIN_EMAILS) |
| Contacto | Validación Zod server + client, rate limiting, envío transaccional con Resend |
| GDPR | Cookie consent, exportación y eliminación de datos, registro de consentimientos en Postgres vía Prisma |
| Analytics / Monitoring | GA4, Vercel Analytics + Speed Insights, Web Vitals, Sentry (client/server/edge) |
| SEO | Structured data (schema-dts), Open Graph dinámico, sitemap y robots.txt generados |
| Accesibilidad | WCAG 2.1 AA: skip links, focus trap, navegación por teclado, contraste de color |
| Performance | React Server Components + React Compiler, ISR, code splitting, bundle analysis |
| Seguridad | Rate limiting en API routes, sanitización de inputs, CSRF con tokens, security headers |
| Testing | Pirámide completa: Vitest (unit/integration), Playwright (E2E multi-browser + visual + a11y con axe-core) |
| Calidad / CI | Biome, Husky, Commitlint, 6 workflows de GitHub Actions (CI, tests, E2E, Lighthouse, security, release) |
- Framework: Next.js (App Router)
- UI Library: React con React Compiler
- Language: TypeScript (strict mode)
- Styling: Tailwind CSS con OKLCH color space (config CSS-first en
globals.css, sintailwind.config.ts) - Node: >= 22.12.0 LTS
- next-intl para enrutamiento localizado (
app/[locale]/), diccionarios yLanguageSwitcher
- Headless CMS: Sanity Studio +
@sanity/client+next-sanity - GitHub API: Octokit (incluye lectura del rate limit propio de GitHub)
- Comments: Giscus (GitHub Discussions) vía
@giscus/react - Auth (Admin): NextAuth con provider GitHub, gate por
ADMIN_EMAILS - Theme System: next-themes con SSR support
- Portable Text: @portabletext/react
- Syntax Highlighting: react-syntax-highlighter + Prism.js
- Code Blocks: @sanity/code-input
- Diagrams: Mermaid (
MermaidDiagram.tsx) - Markdown: react-markdown con remark-gfm y rehype plugins
- Design System: shadcn/ui
- Icons: Lucide React + React Icons
- Image Optimization: Next.js Image con blur placeholders
- Animations: hook custom
useScrollReveal+ CSS (no usa Framer Motion)
- Database: PostgreSQL (vía Prisma; proveedor de hosting agnóstico)
- ORM: Prisma con cliente tipado (
prisma-clientgenerator) - Rate Limiting: Upstash Redis + @upstash/ratelimit
- Email Service: Resend
- Email Templates: @react-email/components
- PDF Generation: jsPDF (CV descargable en
/cv)
- Production Analytics: Vercel Analytics + Speed Insights
- Google Analytics: GA4 con @next/third-parties
- Error Tracking: Sentry (
@sentry/nextjs, client/server/edge)
- Structured Data: schema-dts
- Meta Tags: Next.js metadata API
- Dynamic Images: Open Graph image generation
- Validation: Zod
- Rate Limiting: Upstash Redis + @upstash/ratelimit
- Sanitization: sanitize-html
- CSRF Protection: Tokens con nanoid
- Cookie Management: js-cookie
- Unit/Integration: Vitest con happy-dom
- Testing Library: @testing-library/react + user-event
- E2E: Playwright con soporte multi-browser
- Accessibility: axe-core + @axe-core/playwright
- Coverage: @vitest/coverage-v8
- Mocking: msw
- Bundle Analysis: @next/bundle-analyzer
- Lighthouse: @lhci/cli para CI
- Critical CSS: critters
- Image Optimization: sharp
- Code Quality: Biome (linting + formatting)
- Git Hooks: Husky + lint-staged
- Commits: Commitlint con Conventional Commits
- Versioning: commit-and-tag-version para CHANGELOG automático
- CI/CD: GitHub Actions con 6 workflows (CI, Tests, E2E, Lighthouse, Security, Release)
- Node.js >= 22.12.0 (compatible con Prisma, Vitest y jsdom)
- npm >= 10.0.0
- Cuenta de Sanity (gratis en sanity.io)
- Repositorio de GitHub con Discussions habilitadas
- Base de datos PostgreSQL (cualquier proveedor: Neon, Supabase, Vercel Postgres, Railway…)
- Cuenta de Resend para emails (opcional)
- Cuenta de Upstash para Redis (obligatoria para rate limiting y confirmaciones GDPR)
- GitHub Personal Access Token (opcional, para rate limits mejorados)
- GitHub OAuth App (opcional, solo para el panel
/admincon NextAuth)
# Clonar repositorio
git clone https://github.com/JNZader/portfolio-2025.git
cd portfolio-2025
# Instalar dependencias
npm installCrear archivo .env.local en la raíz del proyecto:
# Site
NEXT_PUBLIC_SITE_URL=http://localhost:3000
NEXT_PUBLIC_SITE_NAME="Tu Nombre - Portfolio"
# Feature Flags
NEXT_PUBLIC_ENABLE_ANALYTICS=false
# Sanity CMS (obligatorio)
NEXT_PUBLIC_SANITY_PROJECT_ID="tu-project-id"
NEXT_PUBLIC_SANITY_DATASET="production"
NEXT_PUBLIC_SANITY_API_VERSION="2024-01-01"
SANITY_API_READ_TOKEN="" # Opcional para datos privados
SANITY_API_WRITE_TOKEN="" # Solo para seed script
# Database (PostgreSQL — cualquier proveedor)
DATABASE_URL="postgresql://user:password@host:5432/database"
# Email Service (Resend)
RESEND_API_KEY="re_tu_api_key"
RESEND_FROM_EMAIL="noreply@tudominio.com"
RESEND_TO_EMAIL="tu-email@tudominio.com"
# Rate Limiting (Upstash Redis)
UPSTASH_REDIS_REST_URL="https://tu-endpoint.upstash.io"
UPSTASH_REDIS_REST_TOKEN="tu-token-aqui"
# GitHub API (opcional - mejora rate limits)
GITHUB_TOKEN="ghp_tu_token_aqui"
NEXT_PUBLIC_GITHUB_USERNAME="tu-username"
# NextAuth - Panel Admin (opcional, solo si querés usar /admin)
AUTH_SECRET="genera-uno-con-openssl-rand-base64-32"
GITHUB_CLIENT_ID="tu-oauth-app-client-id"
GITHUB_CLIENT_SECRET="tu-oauth-app-client-secret"
ADMIN_EMAILS="tu-email@tudominio.com"
# Giscus Comments (obtener de https://giscus.app/)
NEXT_PUBLIC_GISCUS_REPO="tu-usuario/tu-repo"
NEXT_PUBLIC_GISCUS_REPO_ID="R_kgDO..."
NEXT_PUBLIC_GISCUS_CATEGORY="Announcements"
NEXT_PUBLIC_GISCUS_CATEGORY_ID="DIC_kwDO..."
# Google Analytics (opcional)
NEXT_PUBLIC_GA_MEASUREMENT_ID="G-XXXXXXXXXX"# Generar Prisma Client
npx prisma generate
# Ejecutar migraciones
npx prisma migrate dev
# (Opcional) Abrir Prisma Studio
npx prisma studio# 1. Obtener token de escritura desde https://sanity.io/manage
# 2. Agregar SANITY_API_WRITE_TOKEN a .env.local
# 3. Ejecutar seed script
node scripts/seed-sanity.mjsEl script creará:
- 4 categorías de blog
- 6 posts de prueba (2 destacados)
- 4 proyectos de ejemplo (2 destacados)
Ver documentación completa en scripts/README.md
# Acceder a Sanity Studio
# http://localhost:3000/studio- Habilita GitHub Discussions en tu repositorio (Settings → General → Features → ✅ Discussions)
- Instala la app de Giscus: https://github.com/apps/giscus → Install
- Configura en https://giscus.app/, seleccioná "Announcements" como categoría y copiá los valores generados
- Agregá las variables a
.env.local(ver arriba)
# Desarrollo
npm run dev # Next.js dev server (localhost:3000)
# Sanity Studio
# http://localhost:3000/studio - Gestionar contenido
# Quality Checks
npm run verify # Type-check + Biome check (usar antes de commits)
npm run type-check # Solo TypeScript
npm run check # Solo Biome lint + format check
npm run check:write # Biome con auto-fix
npm run format # Format con Biome
npm run lint # Solo Biome linting
# Testing
npm run test # Unit tests con Vitest (watch mode)
npm run test:run # Unit tests (single run)
npm run test:coverage # Tests con coverage report
npm run test:ui # Tests con interfaz gráfica
npm run e2e # E2E tests con Playwright
npm run e2e:ui # E2E con interfaz gráfica
npm run e2e:debug # E2E en modo debug
npm run e2e:headed # E2E con browser visible
npm run e2e:chromium # E2E solo en Chromium
npm run e2e:report # Ver reporte de E2E tests
npm run e2e:codegen # Generar tests con Playwright Codegen
# Performance
npm run analyze # Analizar tamaño de bundles
npm run lighthouse # Ejecutar Lighthouse CI
npm run lighthouse:collect # Solo recolectar datos
npm run lighthouse:assert # Solo validar assertions
# Build
npm run build # Production build
npm start # Production server
# Versioning
npm run release # Patch version (0.0.x)
npm run release:minor # Minor version (0.x.0)
npm run release:major # Major version (x.0.0)
# Database
npx prisma generate # Generar Prisma Client
npx prisma migrate dev # Ejecutar migraciones
npx prisma studio # Abrir Prisma Studio
# Seed
node scripts/seed-sanity.mjs # Poblar Sanity con datos de pruebaportfolio/
├── __tests__/ # Tests unitarios e integración
│ ├── unit/ # Tests de utilities y funciones puras
│ ├── integration/ # Tests de componentes y hooks
│ ├── setup.ts # Setup global de Vitest
│ └── vitest.d.ts # Type definitions para Vitest
├── e2e/ # Tests End-to-End con Playwright
│ ├── fixtures/ # Datos de prueba
│ └── tests/ # Test specs (accessibility, blog, contact,
│ # navigation, newsletter, visual)
├── app/ # Next.js App Router
│ ├── [locale]/ # Rutas localizadas (next-intl: es sin prefijo, /en)
│ │ ├── (pages)/ # Route group (páginas principales)
│ │ │ ├── admin/ # Panel admin (NextAuth, protegido)
│ │ │ │ ├── login/ # Login (GitHub OAuth)
│ │ │ │ └── unauthorized/
│ │ │ ├── blog/ # Blog listing + búsqueda
│ │ │ │ └── [slug]/ # Blog post individual
│ │ │ ├── contacto/ # Formulario de contacto
│ │ │ ├── cv/ # CV / resume (descarga PDF con jsPDF)
│ │ │ ├── data-request/ # Solicitud de datos GDPR
│ │ │ ├── design-system/ # Documentación de diseño
│ │ │ ├── newsletter/ # Newsletter signup
│ │ │ ├── privacy/ # Política de privacidad (ES/EN)
│ │ │ ├── proyectos/ # Proyectos con filtros
│ │ │ │ └── [id]/ # Detalle de proyecto
│ │ │ ├── secret-achievements/ # Easter eggs / gamification
│ │ │ └── sobre-mi/ # About page
│ │ ├── error.tsx # Error boundary de la sección localizada
│ │ ├── layout.tsx # Layout con provider de i18n
│ │ ├── not-found.tsx # 404 page
│ │ └── page.tsx # Homepage
│ ├── actions/ # Server Actions (contact, newsletter, admin-newsletter)
│ ├── api/ # API Routes
│ │ ├── admin/ # Health check y uptime del panel admin
│ │ ├── auth/[...nextauth]/ # NextAuth handler
│ │ ├── data-deletion/ # GDPR data deletion (+ confirm)
│ │ ├── data-export/ # GDPR data export (+ confirm)
│ │ ├── health/ # Health check general
│ │ ├── newsletter/ # Newsletter endpoints (confirm, unsubscribe)
│ │ └── resume/ # Descarga de CV/resume
│ ├── studio/[[...tool]]/ # Sanity Studio route
│ ├── feed.xml/route.ts # RSS feed
│ ├── globals.css # Tailwind CSS + @theme config
│ ├── layout.tsx # Root layout con analytics + Sentry
│ ├── opengraph-image.tsx # OG image dinámica
│ ├── robots.ts # robots.txt generado
│ └── sitemap.ts # Sitemap generado
├── components/
│ ├── a11y/ # ScreenReaderAnnouncer, SkipLinks
│ ├── admin/ # AdminDashboard, NewsletterBroadcaster, UptimeStatus
│ ├── analytics/ # GoogleAnalytics, ThirdPartyScripts
│ ├── animations/ # AnimationProvider, RevealOnScroll (sin Framer Motion)
│ ├── blog/ # BlogFilters, Comments (Giscus), MarkdownRenderer,
│ │ # PortableTextRenderer, PostCard/Grid, TableOfContents, etc.
│ ├── error/ # ErrorFeedback
│ ├── features/ # EasterEggs, MatrixRain
│ ├── forms/ # ContactForm, FormField
│ ├── gdpr/ # CookieConsent, DataDeletionForm, DataRequestForm
│ ├── layout/ # Footer, Header, LanguageSwitcher, MobileMenu, ThemeToggle
│ ├── markdown/ # MarkdownContent, MermaidDiagram
│ ├── newsletter/ # NewsletterForm, NewsletterHero, NewsletterSkeleton
│ ├── projects/ # ProjectCard, ProjectDetail, ProjectsClient
│ ├── sections/ # hero-section, HeroTerminal
│ ├── seo/ # Breadcrumbs, JsonLd
│ └── ui/ # shadcn/ui + custom (button, card, Modal, SkillBadge, etc.)
├── lib/
│ ├── analytics/ # consent, debug, errors, events, vercel
│ ├── auth/ # config.ts, index.ts (NextAuth)
│ ├── config/ # site-config, site-url
│ ├── data/ # projects.ts, resume.json/resume.en.json, case-studies/
│ ├── db/ # prisma.ts
│ ├── email/ # resend.ts, templates/
│ ├── generated/prisma/ # Prisma Client generado
│ ├── github/ # client.ts, queries.ts (sin cache in-process, usa ISR)
│ ├── monitoring/ # logger.ts, performance.ts
│ ├── pdf/ # resume-pdf.ts (jsPDF)
│ ├── rate-limit/ # redis.ts
│ ├── seo/ # alternates, metadata, schema
│ ├── services/ # gdpr.ts
│ ├── utils/ # blog, cn, format, project, search, tech-icons, toc, etc.
│ └── validations/ # Schemas Zod (contact, gdpr, newsletter, email-validator)
├── i18n/ # next-intl: routing.ts, navigation.ts, request.ts
├── messages/ # es.json, en.json (diccionarios de traducción)
├── hooks/ # useGdprRequest, useNewsletterSubscription, useScrollReveal
├── mocks/ # MSW handlers para tests
├── ci-local/ # Simulación local del pipeline de CI (Docker)
├── prisma/
│ └── schema.prisma # Prisma schema (Subscriber, ConsentLog) — PostgreSQL
├── sanity/
│ ├── schemas/ # category, post, project, resume
│ ├── lib/ # client.ts, image.ts, queries.ts (GROQ)
│ └── sanity.config.ts # Configuración de Sanity Studio
├── scripts/
│ ├── seed-sanity.mjs # Script de seed para Sanity
│ ├── audit-sanity-data.mjs / clean-sanity-data.mjs
│ └── README.md # Documentación de scripts
├── public/
│ └── giscus-theme.css # Tema personalizado para comentarios
├── types/ # TypeScript type definitions
├── docs/ # Documentación adicional (CI/CD, publishing guide, etc.)
├── .github/workflows/ # ci.yml, test.yml, e2e.yml, lighthouse.yml,
│ # security.yml, release.yml
├── .husky/ # commit-msg, pre-commit, pre-push
├── proxy.ts # Middleware (Next 16): i18n routing + rate limiting
├── instrumentation.ts # Registro de Sentry
├── sentry.{client,server,edge}.config.ts
├── biome.json # Configuración de Biome
├── prisma.config.ts # Configuración de Prisma CLI
├── next.config.ts # Next.js configuration (sin tailwind.config.ts: Tailwind 4 es CSS-first)
├── vitest.config.ts # Vitest configuration
├── CHANGELOG.md # Changelog automático
└── package.json # Dependencies y scripts
npm run test # Watch mode
npm run test:run # Single run
npm run test:coverage # Con coverage
npm run test:ui # UI modeTests ubicados en __tests__/:
- unit/: Utilities, validaciones, helpers
- integration/: Componentes, hooks
Herramientas: Vitest + Happy DOM, Testing Library. 207 tests en 29 archivos; coverage con v8 y umbral mínimo enforced en CI.
npm run e2e # Todos los browsers
npm run e2e:ui # UI mode
npm run e2e:debug # Debug mode
npm run e2e:headed # Con browser visible
npm run e2e:chromium # Solo Chromium
npm run e2e:report # Ver reporte HTML
npm run e2e:codegen # Generar testsTests ubicados en e2e/tests/: accessibility (axe-core), blog, contact, navigation, newsletter, visual regression.
47 tests E2E corren en 5 navegadores (Chromium, Firefox, WebKit, Mobile Chrome / Pixel 5, Mobile Safari / iPhone 13) = 235 ejecuciones por corrida.
GitHub Actions ejecuta 6 workflows: CI (Biome + TypeScript + build), Tests (unit con coverage vía Codecov), E2E (multi-browser con Playwright), Lighthouse (performance budgets), Security (CodeQL, dependency review, npm audit) y Release (versionado + CHANGELOG).
Documentación completa: CI/CD Documentation · CI/CD Quick Reference · Lighthouse CI
- Push a GitHub
- Conecta el repo en Vercel
- Agrega las variables de entorno
- Deploy automático en cada push
Asegúrate de configurar todas las variables de .env.local en tu plataforma de deployment:
- Variables de Sanity (obligatorias)
- Variables de Database (obligatorias)
- Variables de Email (Resend)
- Variables de Rate Limiting (Upstash Redis)
- Variables de Giscus (para comentarios)
- Variables de GitHub (opcional)
- Variables de NextAuth/Admin (opcional, solo si usás
/admin) - Variables de Analytics (opcional)
NEXT_PUBLIC_SITE_URLcon tu dominio final
- Verificar que Sanity Studio funcione en
/studio - Probar formulario de contacto
- Verificar newsletter signup
- Probar comentarios en blog posts
- Verificar analytics y Web Vitals
- Ejecutar Lighthouse en producción
- Verificar SEO con herramientas (Google Search Console, etc.)
Este proyecto está bajo la Licencia MIT. Ver el archivo LICENSE para más detalles.
- Next.js - Framework React
- Sanity - CMS Headless
- Giscus - Sistema de comentarios
- shadcn/ui - Componentes UI
- Biome - Linting y formatting
- Vitest - Testing framework
- Playwright - E2E testing
- Vercel - Hosting y Analytics
- Upstash - Redis para rate limiting y tokens GDPR
- Resend - Email transaccional
- Prisma - ORM para PostgreSQL
- Sentry - Error tracking
- NextAuth.js - Autenticación del panel admin
- next-intl - Internacionalización (ES/EN)
