01-overview.md
Visión general · Concert-Connect (ConcierGo)
Concert-Connect es el repo de ConcierGo: una app web mobile-first para ir a conciertos cuando no tienes compañía. Login con Clerk, feed y mapa de bolos, matching automático al apuntarte y chat 1-1.
El producto vive en un workspace pnpm: front React+Vite (artifacts/conciergo), API Express (artifacts/api-server), contratos OpenAPI (lib/api-spec) y Postgres + Drizzle (lib/db).
Enlaces
- GitHub: Criscode2022/Concert-Connect
- Replit: replit.com/@cristiancode202/Concert-Connect
- Health:
GET /api/healthz→{ "status": "ok" }
Stack
| Capa | Tecnología |
|---|---|
| Monorepo | pnpm workspaces, TypeScript 5.9 |
| Web | React, Vite, Tailwind 4, shadcn/ui, Framer Motion, Wouter |
| Auth | Clerk (@clerk/react + @clerk/express), proxy /api/__clerk |
| API | Express 5, OpenAPI + Orval + Zod |
| Datos | PostgreSQL + Drizzle ORM |
| Mapas | Google Maps JS + Places |
02-product.md
Producto y features
Problema
Un gran bolo se pierde si no hay con quién ir. Los grupos de WhatsApp son caos; Tinder no es para conciertos; las apps de entradas no conectan a extraños con el mismo cartel.
Solución
ConcierGo: feed de eventos, mapa, “voy” y match automático con el resto de asistentes. Cada match abre un chat 1-1 para cuadrar el viaje o el foso.
Características
Landing
Copy de cartel: «No dejes que un gran bolo se pierda». Entrar / Unirse (Clerk). Invitados pueden explorar eventos y mapa.
Eventos
Lista con filtro por género. Cards compactas (artista, venue, fecha). Usuarios logueados se apuntan; FAB para crear evento (venue via Places).
Mapa
Pins de Google Maps. Click → ficha y apuntarse. Invitados ven el mapa.
Conexiones
Matches del usuario + badge de no leídos. Chat por polling (React Query, 3 s).
Perfil (Backstage)
Nombre, ciudad, géneros, avatar de Clerk.
Invitados
Eventos y mapa abiertos. Conexiones, perfil y crear evento piden sesión.
03-architecture.md
Arquitectura
Diagrama lógico
React (Vite) ConcierGo
│ Clerk (sesión)
│ React Query → /api/*
▼
Express 5 (/api)
│ requireAuth → JIT user en Postgres
│ clerkProxyMiddleware → /api/__clerk
▼
PostgreSQL (Drizzle)
users, events, event_attendees, matches, messages
Capas
- Front —
artifacts/conciergo: Wouter, shadcn, layout sidebar desktop + bottom nav móvil. - API —
artifacts/api-server: rutasusers,events,matches,health. - Contrato —
lib/api-spec/openapi.yaml→ Orval genera hooks (lib/api-client-react) y Zod (lib/api-zod). - DB —
lib/dbDrizzle.
Decisiones de runtime
- Match automático al marcar asistencia: un match (y chat) con cada otro asistente.
- Chat por polling, no WebSocket (deploy Replit más simple).
- JIT provisioning: el primer
requireAuthinserta al usuario desde claims de Clerk.
04-monorepo.md
Estructura (pnpm workspace)
No es Angular/Nest. Es un monorepo Replit (pnpm-workspace.yaml).
| Path | Rol |
|---|---|
artifacts/conciergo | App React + Vite |
artifacts/api-server | Express + Clerk |
artifacts/mockup-sandbox | Sandbox de UI (no prod) |
lib/api-spec | OpenAPI + Orval |
lib/api-client-react | Hooks generados |
lib/api-zod | Schemas Zod generados |
lib/db | Drizzle + schema |
replit.md | Runbook del producto |
Tras cambiar el spec: pnpm --filter @workspace/api-spec run codegen.
05-api.md
Superficie API
Base: /api. Fuente: lib/api-spec/openapi.yaml.
| Método | Ruta | Auth | Qué hace |
|---|---|---|---|
| GET | /healthz | pública | { status: "ok" } |
| GET/PUT | /users/me | Clerk | Perfil propio |
| GET | /users/:userId | Clerk | Perfil ajeno |
| GET | /events | opcional | Feed + isAttending |
| POST | /events | Clerk | Crear bolo |
| POST/DELETE | /events/:id/attend | Clerk | Apuntarse / salir (crea matches) |
| GET | /matches | Clerk | Conexiones + unread |
| GET/POST | /matches/:id/messages | Clerk | Chat |
Código de ejemplo (health)
router.get("/healthz", (_req, res) => {
const data = HealthCheckResponse.parse({ status: "ok" });
res.json(data);
});
HealthCheckResponse sale de Zod generado. Si el spec y el handler se desalinean, falla el typecheck.
06-auth.md
Autenticación (Clerk)
Flujo
- Landing → Entrar / Unirse (
@clerk/react). - El front habla con Clerk vía proxy
/api/__clerk(mismo host en Replit). - Las rutas protegidas usan
requireAuth:- Sin
userId→ 401. - Primera llamada →
INSERTenusers(nombre + avatar de Clerk).
- Sin
- Invitados ven Eventos y Mapa; Conexiones/Perfil/crear evento piden sesión.
Por qué proxy
En .replit.app y custom domains, el proxy evita problemas de cookies de Clerk entre orígenes.
Qué no hay
No hay PIN ni JWT propio (eso es Task Cloud). La sesión es de Clerk; la fila de negocio es users.clerk_id.
07-data-model.md
Modelo de datos (Drizzle / Postgres)
users
id, clerk_id (único), name, bio, avatar_url, music_genres[], city, created_at.
events
title, artist_name, venue_name, venue_address, date, lat, lng, genre, image_url, created_by_user_id.
event_attendees
Quién va a qué bolo. Al insertar, el servidor crea matches con el resto de asistentes.
matches
event_id + user_id_1 + user_id_2 (unique). Un chat por pareja y evento.
messages
Mensajes del match. El front cuenta unreadCount para el badge de Conexiones.
08-deploy-vercel.md
Deploy (Replit Autoscale)
No es Vercel/Netlify. .replit declara:
[deployment]
router = "application"
deploymentTarget = "autoscale"
El mismo proceso sirve el Vite build y Express (/api, /api/__clerk).
Env
| Variable | Dónde |
|---|---|
DATABASE_URL | Postgres 16 del Repl |
CLERK_SECRET_KEY / CLERK_PUBLISHABLE_KEY | API |
VITE_CLERK_PUBLISHABLE_KEY | Front |
VITE_GOOGLE_MAPS_API_KEY | Maps + Places |
La key de Maps debe tener Maps JavaScript API y Places API.
Local
pnpm --filter @workspace/api-server run dev
pnpm --filter @workspace/conciergo run dev 09-decisions.md
Decisiones
Match automático, no swipe
Apuntarse a un evento es el gesto de matching. Evita un segundo feed tipo dating.
Polling frente a WebSocket
3 s con React Query. Suficiente para cuadrar un bolo; el deploy Replit no necesita un proceso WS aparte.
OpenAPI como fuente de verdad
Orval genera hooks y Zod. El typecheck del workspace pilla desvíos entre front y API.
Clerk, no auth casera
Email/social y sesión los resuelve Clerk. El dominio de negocio es el perfil y los matches.
Invitados ven el cartel
Eventos + mapa sin login para que el landing no sea un muro. Crear y chatear sí piden cuenta.
10-challenges-results.md
Retos y aprendizajes
Google Maps Loader
La API clásica Loader de @googlemaps/js-api-loader v2 ya no existe (setOptions + importLibrary). Un preview Replit llegó a mostrar el overlay rojo de Vite. Hay que alinear la versión del loader con el código.
Keys de Maps
Sin Maps JS + Places activados, el mapa enseña «This page can’t load Google Maps correctly». Es configuración de GCP, no de UI.
JIT user vs “Anonymous”
Si Clerk aún no ha enviado el nombre, el primer insert queda como Anonymous. requireAuth reintenta actualizar el perfil.
Resultado
Un workspace que se entiende: spec → codegen → Express → React. El producto (compañía para un bolo) cabe en cuatro tabs.
11-angular-skill.md
Front en Concert-Connect
Este repo no usa Angular. El cliente es React + Vite + Tailwind 4 + shadcn. La skill Angular de Task Cloud no aplica.
Qué se aplica
| Práctica | Dónde |
|---|---|
| Wouter | Rutas (/events, /events/map, /matches, /profile) |
| React Query | Listados y polling del chat |
Clerk Show | Landing signed-in → redirect /events |
| Framer Motion | Landing y cards |
| Bottom nav + sidebar | app-layout.tsx (móvil / desktop) |
Por qué está este doc
La suite featured 01–11 se llama igual que en Task Cloud / Presencia. Aquí el “11” documenta el techo real del front para no fingir un stack Angular.