App para tiradores deportivos: importás los resultados de tus torneos (PractiScore HTML para Tiro Práctico / Steel Challenge, CSV de Google Sheets para Tiro FBI, PDF de WinMSS para IPSC), seguís tu evolución match a match con KPIs y gráficos, y opcionalmente llevás el registro de las armas usadas y los tiros disparados.
npm install
cp .env.example .env.local # completar con credenciales de Supabase
# aplicar TODAS las migraciones de supabase/migrations/ en el SQL Editor,
# en orden numérico (0001 → 0002).
npm run dev # http://localhost:3000Next.js 16 (App Router) · React 19 · TypeScript · Supabase (Postgres + Auth + RLS) · Tailwind 4 · Vitest.
| Comando | Descripción |
|---|---|
npm run dev |
Dev server |
npm run build |
Build de producción |
npm test |
Tests (parsers, importer, stats, claim, estimador de tiros) |
npm run test:watch |
Tests en watch mode |
- Importación multi-formato: HTML de PractiScore (IPSC + Steel Challenge) y CSV de Google Sheets para Tiro FBI. Detección automática de formato.
- Multi-identidad por usuario: el mismo tirador puede aparecer escrito distinto en cada disciplina ("Apellido, Nombre" en IPSC vs "Apellido Nombre" en FBI); el sistema soporta claimar varias identidades por usuario.
- Auto-detección de claim: al importar, sugiere los shooters que parecen ser el usuario logueado (matching por tokens del nombre + número de socio).
- Dashboard con KPIs: torneos disputados, promedio %, mejor %, mejor puesto, percentil promedio, consistencia (desvío estándar), tendencia (regresión lineal sobre todo el historial), cadencia (matches/mes).
- Gráfico de evolución SVG (sin librerías) con tooltip por match.
- Catálogo de armas opcional: registrá tus armas y asignalas por torneo para llevar el conteo de tiros (auto-derivado para FBI = 45 y Steel = 25 × stages cargados; manual para IPSC).
- Theming: light / dark / system con persistencia (next-themes).
Documentación funcional en docs/:
src/
├── app/ # rutas (route groups: (auth) y (app))
│ ├── (app)/
│ │ ├── dashboard/
│ │ ├── firearms/ # catálogo y detalle por arma
│ │ ├── import/
│ │ └── matches/
│ └── (auth)/ # login + signup
├── components/ # UI reutilizable + StatsOverview, PerformanceChart, etc.
├── lib/
│ ├── actions/ # Server Actions compartidos (claim, firearms)
│ ├── db/ # capa de acceso a datos (matches, shooters, firearms, ...)
│ ├── firearms/ # estimador de tiros por disciplina
│ ├── import/ # lógica de importación + auto-claim
│ ├── parsers/ # parsers de archivos externos (PractiScore, Steel, FBI CSV)
│ ├── stats/ # cómputo de KPIs (pure function, testeada)
│ ├── supabase/ # clientes server / browser / middleware
│ ├── types/ # tipos de dominio del parser
│ ├── disciplines.ts # constantes + helpers de disciplinas
│ ├── redirects.ts # helper redirectWithError
│ └── utils.ts # cn, formatters
└── proxy.ts # refresh de sesión por request
supabase/migrations/ # SQL del schema (numeradas 0001…0002)
docs/ # documentación funcional
tests/ # vitest + fixtures reales de PractiScore y FBI
profiles·disciplines·divisions— base.shooters— identidades de tiradores (un usuario puede claimar varias).matches·match_entries·stages·stage_results— datos de torneos (compartidos entre usuarios; UNIQUE NULLS NOT DISTINCT en matches para evitar duplicados con region NULL).firearms·match_firearm_log— privados por usuario, RLS por owner.
Ver docs/data-model.md para el detalle.
Pull Requests bienvenidos. Antes de mandar uno, leé CONTRIBUTING.md — tiene el setup local, las convenciones de commit (usamos conventional commits para que release-please calcule las versiones automáticamente), y el flujo de PR.
¿Encontraste un bug o tenés una idea? Abrí un issue. Para bugs de importación de archivos, adjuntá el archivo que falla — sin él no podemos reproducir.
AGPL v3 o posterior. En resumen: podés usar, modificar y redistribuir el código libremente; si lo hostás como servicio público (propio o forkeado), tu versión modificada también tiene que estar disponible bajo AGPL. Esto preserva que las mejoras de la comunidad queden abiertas para la comunidad.