BikeControl (SwiftControl)
使用 Zwift Click、Zwift Play、Shimano Di2,或任何藍牙控制器搭配 MyWhoosh、Rouvy、Zwift 等。可於 iOS、Android、Windows 與 macOS 免費試用。
AI 翻譯
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證App for areto management
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
Aplicación web para la gestión de un club de fútbol sala: personas, equipos, calendario, cuotas y comunicación en una sola herramienta. • Next.js 16 (App Router) + React 19 + TypeScript • Tailwind CSS v4 + shadcn/ui (base-ui, estilo nova) • PostgreSQL + Drizzle ORM • Supabase Auth (email+contraseña) con roles El proyecto usa dos proyectos Supabase separados:檢視完整 README ↗
Aplicación web para la gestión de un club de fútbol sala: personas, equipos, calendario, cuotas y comunicación en una sola herramienta.
nova)El proyecto usa dos proyectos Supabase separados:
.env.local) y por los entornos "Preview" y
"Development" de Vercel (incluidos los Preview Deployments de cada PR).Nunca apuntes .env.local al proyecto de producción. Ver Puesta en
marcha para crear areto-dev.
Los PRs a main corren CI (.github/workflows/ci.yml): lint, typecheck,
db:check, comprobación de que src/db/schema.ts no tiene cambios sin
migración generada, db:migrate contra areto-dev y next build.
Las migraciones a producción se aplican automáticamente al mergear un PR
que traiga ficheros en drizzle/ (migrate-prod.yml, que hace un backup
cifrado justo antes). El mismo workflow sigue siendo lanzable a mano desde
Actions para relanzarlo tras un fallo. El disparo es el push a main y no el
final del despliegue por una razón concreta: con cacheComponents: true,
next build ejecuta consultas reales, así que el esquema nuevo tiene que
estar aplicado antes de que Vercel termine de construir el código nuevo. Si
alguna vez el build sale antes que la migración, falla, producción sigue
sirviendo el despliegue anterior y basta con pulsar Redeploy en Vercel.
La rama main está protegida: no se puede empujar directamente a ella.
Todo desarrollo va en su propia rama y entra a main mediante un Pull Request:
git switch -c feat/nombre-corto # o fix/…, chore/…
# … trabajo, commits …
git push -u origin feat/nombre-corto
Abre el PR contra main en GitHub, espera a que pase el CI y mergéalo. El PR
genera además un Preview Deployment en Vercel (contra areto-dev) para probar
los cambios antes de mergear.
El procedimiento detallado (comandos, decisión expand/contract al tocar el
esquema y recetas para cuando algo falla) vive en
.claude/skills/desarrollar-funcionalidad/, como skill de Claude Code.
pnpm install
areto-dev (nunca reutilices el de producción para desarrollo local) y
configura el entorno. Copia el ejemplo y rellena DATABASE_URL,
NEXT_PUBLIC_SUPABASE_URL y la clave pública:
cp .env.example .env.local
pnpm run db:push
pnpm run db:seed
Con eso la app arranca, pero casi todas las pantallas salen vacías. Para
trabajar con un club entero inventado (plantillas, socios, patrocinadores,
calendario, inscripciones) siembra además el juego de demostración:
pnpm run db:seed:demo
supabase/setup.sql en el SQL Editor de Supabase.pnpm run dev
Abre http://localhost:3000.La app arranca aunque Supabase no esté configurado: la landing pública funciona y las rutas internas redirigen a
/login.
Si al abrir cualquier página que lea datos ves un 500 con este error:
Failed query: select ... from "club_settings"
[cause]: write CONNECT_TIMEOUT aws-N-[region].pooler.supabase.com:6543
no es un problema del proyecto ni de Supabase: hay una red que filtra toda
salida TCP que no sea 80/443. Auth y Storage siguen funcionando (van por HTTPS
al dominio [ref].supabase.co), pero la conexión Postgres de Drizzle
(src/db/index.ts) no puede abrirse. Para comprobarlo:
Test-NetConnection aws-1-eu-west-1.pooler.supabase.com -Port 6543 # falla
Test-NetConnection aws-1-eu-west-1.pooler.supabase.com -Port 443 # conecta
Solución definitiva: pedir al departamento de Sistemas la apertura de los
puertos 6543 y 5432 salientes hacia *.pooler.supabase.com.
Apaño mientras tanto: conectar un hotspot móvil y enrutar por él solo el
tráfico al pooler, con scripts/dev-route-supabase.ps1:
# 1. Conecta el hotspot (sin desconectar el cable corporativo).
# 2. En PowerShell COMO ADMINISTRADOR, en la raíz del repo:
pnpm run dev:route
# 3. En tu terminal habitual:
pnpm run dev
El script crea rutas /32 hacia las IPs actuales del pooler por el adaptador
Wi-Fi, así que el resto del tráfico —incluida la red interna— sigue saliendo por
el cable. Detalles a tener en cuenta:
CONNECT_TIMEOUT, lo primero es re-ejecutar pnpm run dev:route.postgres-js reutiliza la conexión entre recargas, así que las rutas deben
estar puestas antes de arrancar next dev; si no, reinicia el servidor.powershell -File scripts/dev-route-supabase.ps1 -Remove.Conectar el portátil corporativo a una red ajena conviene contrastarlo antes con Sistemas, por si la política interna no permite el dual-homing.
Auth con Supabase (email+contraseña). El acceso a la
aplicación es por invitación: no hay alta pública. Un administrador invita
desde /administracion/usuarios y la persona recibe un correo con un enlace
para poner su contraseña.
Los roles viven en la tabla roles y se crean y editan desde
/administracion/roles. El club arranca con cuatro de fábrica —Administrador,
Secretaría, Entrenador y Socio—, que no se pueden borrar pero cuyos permisos sí
se ajustan.
El catálogo de permisos está en el código (src/lib/permissions.ts), no en
la base de datos: un permiso solo significa algo si hay un
requirePermission() que lo comprueba. La base de datos guarda únicamente qué
rol tiene cuál (role_permissions). Añadir un permiso nuevo es tocar ese array
y sus traducciones; no hace falta migración.
La autorización en servidor se hace con los helpers de src/lib/auth.ts
(getCurrentUser, requireUser, requirePermission, hasPermission). El
refresco de sesión y el corte de las rutas privadas está en src/proxy.ts, que
solo comprueba que haya sesión: los permisos se miran en cada página y en cada
Server Action, y el estado de la cuenta (activa, pendiente o desactivada) lo
comprueba requireUser en cada petición.
pnpm run db:migrate (crea roles,
role_permissions y siembra los cuatro roles de fábrica).supabase/setup.sql en el SQL Editor. Crea el
trigger que genera el perfil al registrarse, publica
public.user_has_permission() y escribe con ella las políticas de Storage.
El propio fichero falla en claro si lo lanzas antes que las migraciones./auth/confirm,
/auth/confirm en la lista de Redirect URLs, "Allow new users to sign up"
apagado y un SMTP propio. Están detallados en la cabecera de
supabase/setup.sql./login y promociónate a
mano (es la última operación en SQL que queda; a partir de ahí, el alta del
resto se hace desde la aplicación):
update public.users
set role_id = (select id from public.roles where key = 'admin'),
status = 'active'
where email = '[email protected]';
| Script | Descripción |
|---|---|
pnpm run dev | Servidor de desarrollo |
pnpm run build | Build de producción |
pnpm run db:generate | Genera archivos de migración SQL desde el esquema |
pnpm run db:migrate | Aplica las migraciones |
pnpm run db:check | Valida el historial de migraciones (colisiones) |
pnpm run db:push | Empuja el esquema directamente (rápido en dev) |
pnpm run db:studio | Abre Drizzle Studio (explorador de la BD) |
pnpm run db:seed | Datos iniciales (temporada y equipos de ejemplo) |
pnpm run db:seed:demo | Juego de datos completo inventado, para desarrollo |
pnpm run db:seed:demo llena areto-dev con un club entero inventado —dos
temporadas, seis equipos, 116 personas con sus tutores, 39 socios, 14
patrocinadores con sus facturas, la temporada de cancha y once inscripciones—
para poder ver las pantallas con datos delante en vez de vacías.
Todo es inventado: los nombres, los DNI, los IBAN y las empresas los genera
src/db/seed-data.ts, y los DNI e IBAN llevan su dígito de control correcto
para que la app no los marque como erróneos. Los emails van sobre el dominio
reservado .test, que no resuelve.
pnpm run db:seed:demo -- --clean borra lo que sembró y no siembra nada.-- --force se lo salta.src/db/seed-demo.ts.src/
├─ app/
│ ├─ page.tsx # Portal público (landing)
│ ├─ layout.tsx # Layout raíz
│ └─ (app)/ # App interna (con barra lateral)
│ ├─ layout.tsx
│ ├─ dashboard/
│ ├─ personas/
│ ├─ equipos/
│ ├─ calendario/
│ ├─ cuotas/
│ └─ avisos/
├─ components/
│ ├─ app-sidebar.tsx # Navegación de la app interna
│ ├─ section-placeholder.tsx
│ └─ ui/ # Componentes shadcn/ui
├─ db/
│ ├─ schema.ts # Esquema de datos
│ ├─ index.ts # Cliente de base de datos
│ ├─ seed.ts # Datos iniciales
│ ├─ seed-demo.ts # Juego de datos de demostración
│ └─ seed-data.ts # Catálogos y generadores del seed de demostración
└─ lib/utils.ts
Aplicación de club único: seasons, teams, persons, memberships,
events, attendances, fees, payments, announcements, users, roles
y role_permissions. Sin capa de multi-club ni catálogo de deportes — todo el
modelo es específico de fútbol sala.
Temporadas: la gestión es multi-temporada. seasons es el ancla de la que
cuelgan teams.seasonId y fees.seasonId — cada temporada tiene sus propios
equipos, jugadores (vía memberships → teams) y economía. Una única
temporada puede tener isCurrent = true (índice único parcial); la UI filtra
por ella por defecto y permite consultar temporadas pasadas.