Sube documentacion del proyecto
This commit is contained in:
@@ -0,0 +1,315 @@
|
||||
# Despliegue — CI/CD con GitHub Actions
|
||||
|
||||
Este documento describe cómo quedan configurados el pipeline de build/publish a GHCR y el
|
||||
despliegue automático en el servidor privado, y qué pasos manuales (una sola vez) hacen falta
|
||||
para que funcione.
|
||||
|
||||
Los workflows viven en `.github/workflows/deploy-backend.yml` y
|
||||
`.github/workflows/deploy-frontend.yml`. Cada uno tiene dos jobs: `build` (en un runner de GitHub,
|
||||
compila y publica la imagen en GHCR) y `deploy` (en el runner self-hosted del servidor, la baja y
|
||||
reinicia el contenedor correspondiente). Un push a `develop` que solo toca `Club12-Backend/**`
|
||||
dispara únicamente el workflow de backend, y viceversa; un cambio en `docker-compose.yml` o en los
|
||||
workflows dispara ambos.
|
||||
|
||||
## 1. Prerrequisitos del servidor
|
||||
|
||||
- Host Debian con Docker Engine + Docker Compose v2 instalados (`docker compose version` debe
|
||||
responder `v2.x`).
|
||||
- El usuario que corre el runner (`ghrunner`) debe pertenecer al grupo `docker`:
|
||||
|
||||
```bash
|
||||
sudo usermod -aG docker ghrunner
|
||||
# cerrar sesión y volver a entrar (o `newgrp docker`) para que el cambio de grupo tome efecto
|
||||
```
|
||||
|
||||
Verificar como ese usuario, sin `sudo`:
|
||||
|
||||
```bash
|
||||
docker ps
|
||||
```
|
||||
|
||||
Si falla por permisos, el grupo no se aplicó todavía.
|
||||
|
||||
## 2. Registrar los runners self-hosted (manual, una vez por runner)
|
||||
|
||||
Se necesitan **dos runners registrados por separado**, uno por cada `runs-on` label. Un mismo
|
||||
runner no puede sostener dos jobs en simultáneo.
|
||||
|
||||
Obtener un token de registro en:
|
||||
`https://github.com/FrancoRu/Taller-de-Integracion/settings/actions/runners/new`
|
||||
|
||||
Backend, en `/home/ghrunner/actions-runner` (o el directorio equivalente):
|
||||
|
||||
```bash
|
||||
./config.sh --url https://github.com/FrancoRu/Taller-de-Integracion \
|
||||
--token <REGISTRATION_TOKEN> \
|
||||
--name club12-back \
|
||||
--labels Club-12-back-runner \
|
||||
--work _work
|
||||
sudo ./svc.sh install ghrunner && sudo ./svc.sh start
|
||||
```
|
||||
|
||||
Repetir en un **segundo directorio de runner, separado**, con:
|
||||
|
||||
```bash
|
||||
./config.sh --url https://github.com/FrancoRu/Taller-de-Integracion \
|
||||
--token <REGISTRATION_TOKEN> \
|
||||
--name club12-front \
|
||||
--labels Club-12-front-runner \
|
||||
--work _work
|
||||
sudo ./svc.sh install ghrunner && sudo ./svc.sh start
|
||||
```
|
||||
|
||||
Notas importantes:
|
||||
|
||||
- `self-hosted` se agrega automáticamente al registrar el runner — **no** hay que pasarlo en
|
||||
`--labels`.
|
||||
- Los strings de label son sensibles a mayúsculas/minúsculas y deben coincidir exactamente con los
|
||||
workflows: `Club-12-back-runner` y `Club-12-front-runner`.
|
||||
- Ambos runners corren en el mismo host (`192.168.0.200`), sobre el mismo proyecto de compose
|
||||
(`club12`) — por diseño, para que ambos jobs de `deploy` puedan compartir el mismo directorio de
|
||||
compose sin pisarse.
|
||||
|
||||
## 3. Bootstrap del `.env` de producción (manual, una sola vez)
|
||||
|
||||
El pipeline **nunca** crea, lee ni sobrescribe el `.env` de producción. Si falta, el paso de
|
||||
sincronización del job `deploy` falla de forma explícita y detiene el workflow.
|
||||
|
||||
```bash
|
||||
mkdir -p /home/docker-compose/Club12
|
||||
cp .env.example /home/docker-compose/Club12/.env
|
||||
# completar cada valor CHANGE_ME en /home/docker-compose/Club12/.env
|
||||
chmod 600 /home/docker-compose/Club12/.env
|
||||
```
|
||||
|
||||
Este archivo vive de forma permanente en `/home/docker-compose/Club12/`, un directorio distinto al
|
||||
workspace efímero que usa `actions/checkout` — por eso `git clean -ffdx` del checkout nunca lo
|
||||
toca.
|
||||
|
||||
## 4. Visibilidad del paquete en GHCR
|
||||
|
||||
Los paquetes de GHCR son **privados por defecto**, incluso en un repositorio público. El job de
|
||||
`deploy` ya hace `docker login ghcr.io` con un token con permiso `read:packages`, así que el
|
||||
`pull` funciona sin cambios adicionales. Alternativa: si se prefiere evitar el login, se puede
|
||||
cambiar la visibilidad del paquete a público desde la UI de GHCR
|
||||
(`Package settings → Change visibility`).
|
||||
|
||||
## 5. Rollback manual
|
||||
|
||||
Si un deploy deja el servicio en mal estado, se puede volver a la imagen anterior directamente en
|
||||
el servidor, sin pasar por GitHub:
|
||||
|
||||
```bash
|
||||
cd /home/docker-compose/Club12
|
||||
docker tag ghcr.io/francoru/club12-backend:previous ghcr.io/francoru/club12-backend:latest
|
||||
docker compose up -d --no-deps --no-build backend
|
||||
```
|
||||
|
||||
(Reemplazar `backend` por `frontend` y el nombre de imagen correspondiente para el frontend.)
|
||||
|
||||
## 6. Desarrollo local (sin cambios)
|
||||
|
||||
`docker-compose.yml` conserva los bloques `build:` como fallback local — el pipeline de CI nunca
|
||||
los usa (`--no-build` en cada deploy), pero siguen funcionando para levantar el proyecto en la
|
||||
máquina de desarrollo:
|
||||
|
||||
```bash
|
||||
docker compose build && docker compose up -d
|
||||
```
|
||||
|
||||
En un host que no sea Linux, la ruta absoluta del bind mount del servicio `db`
|
||||
(`/home/docker/club12/db`) no aplica: usar un `docker-compose.override.yml` (ignorado por
|
||||
git) que reemplace esa línea de volumen por un volumen nombrado local.
|
||||
|
||||
## 7. Migración a Postgres self-hosted (manual, una sola vez)
|
||||
|
||||
A partir del cambio `selfhosted-postgres-db`, `docker-compose.yml` incluye un servicio
|
||||
`db` (`postgres:17-alpine`) en la red interna `club12`, **sin puerto publicado**. El
|
||||
backend deja de hablar con Supabase Postgres y pasa a usar ese contenedor. Supabase
|
||||
**Storage** (buckets de imágenes y fichas médicas) se sigue usando igual — no se toca.
|
||||
|
||||
El pipeline de CI **no** levanta `db` (`deploy-backend.yml` usa `--no-deps`). El servicio
|
||||
se levanta una vez en el cutover y se mantiene solo por `restart: unless-stopped`.
|
||||
|
||||
### 7.1 Preparar el host
|
||||
|
||||
```bash
|
||||
# Directorio de datos de Postgres — en /home (420 GB), NO en la partición raíz (31 GB)
|
||||
sudo mkdir -p /home/docker/club12/db
|
||||
sudo chown 999:999 /home/docker/club12/db # uid del usuario postgres en la imagen alpine
|
||||
|
||||
# Directorio de backups
|
||||
sudo mkdir -p /home/docker/backups/club12
|
||||
```
|
||||
|
||||
### 7.2 Completar el `.env` de producción
|
||||
|
||||
En `/home/docker-compose/Club12/.env` (owner `gh-runner:gh-runner`, mode 600) agregar las
|
||||
tres claves nuevas y reescribir la connection string. Elegir la contraseña ahora
|
||||
(`openssl rand -base64 24`); `Username`/`Password` de la connection string **deben
|
||||
coincidir** con `POSTGRES_USER`/`POSTGRES_PASSWORD`.
|
||||
|
||||
```bash
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_DB=postgres
|
||||
POSTGRES_PASSWORD=<contraseña-fuerte-elegida-ahora>
|
||||
|
||||
# reemplaza el valor anterior (pooler de Supabase):
|
||||
ConnectionStrings__DbConnection=Host=db;Port=5432;Database=postgres;Username=postgres;Password=<misma-contraseña>;SSL Mode=Disable
|
||||
|
||||
# activa el backup periódico + restore-desde-panel ya incluido en la app
|
||||
# (reemplaza al cron manual — ver §7.5):
|
||||
Backup__Enabled=true
|
||||
```
|
||||
|
||||
> Las variables `POSTGRES_*` sólo inicializan la base en el **primer arranque** del
|
||||
> contenedor (directorio de datos vacío). Después, para cambiar la contraseña hay que
|
||||
> hacerlo con `ALTER USER` dentro de la base.
|
||||
|
||||
### 7.3 Dump fresco de Supabase
|
||||
|
||||
Desde el host, usando `postgres:17-alpine` como cliente (no depende de la versión de
|
||||
`pg_dump` del host). Los datos del app viven en **dos** schemas: `public` (Identity /
|
||||
`AspNet*`) y `Club12` (todo lo demás). Pasá las credenciales como variables de entorno
|
||||
para no pelear con el URL-encoding de la contraseña; sacá los valores del `.env` actual
|
||||
(`grep -i DbConnection /home/docker-compose/Club12/.env`) — `User Id=` → `PGUSER`,
|
||||
`Password=` → `PGPASSWORD`, `Server=` → `PGHOST`.
|
||||
|
||||
```bash
|
||||
set +H # bash: desactiva la expansión de '!' en la password
|
||||
cd /home/docker/backups/club12
|
||||
|
||||
docker run --rm -v "$PWD:/out" \
|
||||
-e PGHOST=aws-1-us-east-2.pooler.supabase.com -e PGPORT=5432 -e PGDATABASE=postgres \
|
||||
-e PGUSER='<User Id>' -e PGPASSWORD='<Password>' -e PGSSLMODE=require \
|
||||
postgres:17-alpine \
|
||||
pg_dump --format=custom --no-owner --no-privileges \
|
||||
--schema=public --schema='"Club12"' \
|
||||
--file=/out/club12-cutover-$(date +%F).dump
|
||||
```
|
||||
|
||||
> `--schema='"Club12"'` **con comillas dobles adentro**: `pg_dump` pasa los patrones de
|
||||
> `--schema` a minúsculas, así que `--schema=Club12` no matchea nada y el dump sale con
|
||||
> solo la mitad de las tablas.
|
||||
|
||||
Verificá que estén los dos schemas:
|
||||
|
||||
```bash
|
||||
docker run --rm -v "$PWD:/out" postgres:17-alpine \
|
||||
pg_restore --list "/out/club12-cutover-$(date +%F).dump" | grep "TABLE DATA"
|
||||
```
|
||||
|
||||
Tenés que ver ~22 tablas en `Club12` (`Teams`, `Players`, `Tournaments`, `Matches`, …) y
|
||||
8 en `public` (`AspNetUsers`, `__EFMigrationsHistory`, …).
|
||||
|
||||
### 7.4 Cutover (ventana corta — el backend se reinicia)
|
||||
|
||||
```bash
|
||||
cd /home/docker-compose/Club12
|
||||
DUMP=/home/docker/backups/club12/club12-cutover-$(date +%F).dump
|
||||
|
||||
# 1. Levantar SOLO la base y esperar a que esté healthy
|
||||
docker compose up -d db
|
||||
docker compose ps db # STATUS = healthy
|
||||
|
||||
# 2. Restaurar el dump dentro del contenedor
|
||||
docker compose cp "$DUMP" db:/tmp/restore.dump
|
||||
docker compose exec -T db pg_restore --no-owner --no-privileges -U postgres -d postgres /tmp/restore.dump
|
||||
docker compose exec -T db rm /tmp/restore.dump
|
||||
# El único error esperado es `schema "public" already exists` (1, ignorado) — benigno.
|
||||
# Cualquier otro error rojo: frená y revisá antes de seguir.
|
||||
|
||||
# 3. Verificación
|
||||
docker compose exec -T db psql -U postgres -d postgres -c '\dt "Club12".*'
|
||||
docker compose exec -T db psql -U postgres -d postgres -c 'select count(*) from "Club12"."Teams";'
|
||||
docker compose exec -T db psql -U postgres -d postgres -c 'select count(*) from "public"."AspNetUsers";'
|
||||
|
||||
# 4. Con el .env ya editado (7.2 — cambiar ConnectionStrings__DbConnection a Host=db;…),
|
||||
# recrear el backend
|
||||
docker compose up -d backend
|
||||
docker compose logs --tail=40 backend # sin errores de migración, "Now listening on: http://[::]:8080"
|
||||
|
||||
# 5. Bouncear el frontend: su nginx tiene cacheada la IP vieja del backend → si no,
|
||||
# todo /api/* da 502 hasta reiniciarlo.
|
||||
docker compose restart frontend
|
||||
|
||||
# 6. Verificar (el backend no está publicado en el host — va por el proxy :5001)
|
||||
docker compose ps
|
||||
curl -s -o /dev/null -w 'http=%{http_code}\n' 'http://localhost:5001/api/tournaments?pageSize=300'
|
||||
```
|
||||
|
||||
En el arranque, `MigrateAsync` ve el schema ya restaurado y no hace nada (o aplica sólo
|
||||
migraciones más nuevas que el dump). El seed está en `Seed:Enabled=false` en producción.
|
||||
|
||||
> El paso 5 (`restart frontend`) queda automatizado en `deploy-backend.yml` para los
|
||||
> deploys de CI; en el cutover manual hay que hacerlo a mano.
|
||||
|
||||
### 7.5 Backup automático (feature integrado del backend)
|
||||
|
||||
El backend ya trae un sistema de backups completo — no hace falta cron ni script en el
|
||||
host. Se activa con la variable agregada en §7.2:
|
||||
|
||||
```bash
|
||||
Backup__Enabled=true
|
||||
# opcionales, si se quiere otra cadencia (defaults entre paréntesis):
|
||||
# Backup__IntervalHours=24 (24)
|
||||
# Backup__RetentionCount=7 (7)
|
||||
```
|
||||
|
||||
Con `Backup__Enabled=true`, un `DatabaseBackupHostedService` corre dentro del propio
|
||||
proceso backend cada `Backup__IntervalHours` horas, genera el dump con `pg_dump` (ya
|
||||
instalado en la imagen del backend) y lo guarda en el volumen `backup-data`
|
||||
(`Backup__LocalStoragePath`, default `/app/backups`), podando al `Backup__RetentionCount`
|
||||
más reciente.
|
||||
|
||||
- **Ver los backups:** panel admin → sección de backups. Cada fila muestra su origen
|
||||
("Programado" para los automáticos, "Manual" para los que se disparan a mano desde el
|
||||
mismo panel).
|
||||
- **Restaurar:** botón de restore en el panel (`POST /api/backups/{id}/restore`) — sin
|
||||
SSH al servidor. Antes de restaurar, la app crea automáticamente un backup de
|
||||
seguridad del estado actual.
|
||||
- **Verificar la integridad de un dump sin arriesgar producción:** descargarlo desde el
|
||||
panel y restaurarlo en una base "scratch" separada (no usar el botón de restore de
|
||||
producción solo para testear que un dump sirve).
|
||||
|
||||
### 7.6 Rollback
|
||||
|
||||
```bash
|
||||
# En el .env, volver ConnectionStrings__DbConnection al valor del pooler de Supabase
|
||||
docker compose up -d backend
|
||||
```
|
||||
|
||||
No hay migración de schema que revertir. Dejar el contenedor `db` y
|
||||
`/home/docker/club12/db` en su lugar hasta confirmar que el cutover quedó estable; recién
|
||||
ahí dar de baja el proyecto de Supabase (los buckets de Storage siguen en uso).
|
||||
|
||||
### 7.7 `docker compose down` — cuidado
|
||||
|
||||
Los datos de `db` son un bind mount: sobreviven `down` y `down -v`. Aun así, `-v`
|
||||
elimina los volúmenes nombrados (`backup-data`), y borrar `/home/docker/club12/db` a mano
|
||||
destruye la base.
|
||||
|
||||
## Checklist post-merge
|
||||
|
||||
Estos pasos **no están cubiertos por el cambio de código en sí** — son responsabilidad del
|
||||
usuario/operador una vez que este cambio se mergea a `develop`, y deben verificarse a mano porque
|
||||
ningún runner self-hosted existe todavía en este momento:
|
||||
|
||||
- [ ] Registrar ambos runners con las labels exactas `Club-12-back-runner` y
|
||||
`Club-12-front-runner` (sección 2).
|
||||
- [ ] Crear el `.env` real en `/home/docker-compose/Club12/.env` a partir de `.env.example`
|
||||
(sección 3).
|
||||
- [ ] Hacer un push chico a `develop` que solo toque `Club12-Backend/**` y confirmar que:
|
||||
- solo corre `deploy-backend.yml` (no `deploy-frontend.yml`);
|
||||
- el job `build` publica `ghcr.io/francoru/club12-backend:latest`;
|
||||
- el job `deploy` corre después de que `build` termina, en el runner `Club-12-back-runner`;
|
||||
- `docker images` muestra `club12-backend:previous` (o el mensaje de "primer deploy" si es la
|
||||
primera vez);
|
||||
- `docker compose ps backend` muestra el contenedor recreado y saludable;
|
||||
- el contenedor de `frontend` **no** se reinicia.
|
||||
- [ ] Repetir el mismo push de prueba tocando solo `Club12-WebClient/**` y confirmar el mismo
|
||||
comportamiento en espejo para `deploy-frontend.yml`.
|
||||
- [ ] Confirmar que `/home/docker-compose/Club12/.env` sigue existiendo después del deploy y que
|
||||
el servicio toma sus valores (por ejemplo, que la API arranca sin errores de configuración).
|
||||
- [ ] Confirmar que `docker image prune -f` no deja crecer una pila de imágenes colgantes, y que
|
||||
las tags `:latest` y `:previous` sobreviven.
|
||||
@@ -0,0 +1,179 @@
|
||||
# Manual de Usuario — Liga Club12
|
||||
|
||||
Guía de uso del sistema para administradores y visitantes. Para información técnica (arquitectura, instalación, requisitos cubiertos) ver [README.md](./README.md).
|
||||
|
||||
## 1. Perfiles de usuario
|
||||
|
||||
El sistema tiene dos tipos de acceso:
|
||||
|
||||
- **Visitante (sin cuenta)**: puede consultar toda la información pública de la liga desde la página principal, sin necesidad de registrarse ni iniciar sesión.
|
||||
- **Usuario administrador (con cuenta)**: accede al panel privado para cargar y mantener toda la información de las temporadas y torneos. Los roles definen qué secciones puede administrar cada usuario.
|
||||
|
||||
## 2. Vista pública (sin iniciar sesión)
|
||||
|
||||
El menú principal tiene las secciones: **Inicio**, **Temporadas**, **Campeones**, **Sanciones**, **Novedades**, **Quiénes somos** e **Información** (con las sub-páginas **Ficha médica** y **Reglamento**).
|
||||
|
||||
La navegación de la competición sigue el orden **Temporada → Torneo → División**: no hay un listado plano de "torneos" — se entra por la temporada, se elige el torneo dentro de ella y ahí se accede a cada división.
|
||||
|
||||
Desde estas secciones, cualquier visitante puede:
|
||||
|
||||
- Ver la lista de **temporadas** y, dentro de cada una, sus **torneos** (activos y finalizados).
|
||||
- Consultar los **equipos** participantes de cada división, con su plantel de jugadores.
|
||||
- Ver el **fixture** y los **resultados** de los partidos jugados y a jugarse.
|
||||
- Consultar la **tabla de posiciones** de cada división/etapa. El equipo en 1er puesto se destaca con una corona apenas toma la punta — no hace falta que termine la fase para verlo.
|
||||
- Ver la **tabla de goleadores** (limitada a los 10 primeros en la vista pública).
|
||||
- Ver las **llaves de eliminación** ("Llaves") como un árbol de bracket, con los cruces de cada ronda hasta la final.
|
||||
- **Imprimir** la tabla de posiciones de una división.
|
||||
- Consultar las **sanciones vigentes** aplicadas a jugadores.
|
||||
- Leer las **novedades** publicadas en el blog de la liga.
|
||||
- Ver la sección **Campeones**: el historial de campeones por división/copa de los torneos ya finalizados.
|
||||
- Leer **Quiénes somos** y, dentro de **Información**, la página de **Ficha médica** (qué es y por qué se pide) y el **Reglamento** de la liga.
|
||||
|
||||
No se requiere ninguna acción de registro para acceder a esta información.
|
||||
|
||||
### 2.1 Ver las llaves de eliminación
|
||||
|
||||
1. Entrar a la división deseada dentro de un torneo.
|
||||
2. Ir a la pestaña **"Llaves"** (junto a "Partidos" y "Posiciones").
|
||||
3. El sistema muestra el árbol de la fase eliminatoria, incluyendo — cuando el torneo tiene más de una copa (por ejemplo Copa Oro y Copa Plata, según el rango de posiciones de la fase de grupos) — cada copa por separado.
|
||||
4. Si un partido todavía no se jugó, ese cruce se muestra como "A definir" (TBD). Cuando la cantidad de equipos no es potencia de 2 (por ejemplo 5, 6 o 7 equipos), los mejores puestos de la fase de grupos reciben "bye" (pasan directo a la siguiente ronda) — es un comportamiento esperado, no un error. Si el sistema no puede determinar con certeza qué equipo avanza a la siguiente ronda, muestra las rondas en columnas sin línea de conexión en vez de arriesgar una conexión incorrecta.
|
||||
|
||||
### 2.2 Imprimir posiciones
|
||||
|
||||
1. Entrar a la vista de **posiciones** de una división.
|
||||
2. Usar el botón **"Imprimir"**.
|
||||
3. Se abre el diálogo de impresión del navegador (permite imprimir en papel o guardar como PDF desde ahí); la hoja se genera sin el menú ni otros elementos de navegación, solo la tabla de posiciones.
|
||||
|
||||
## 3. Iniciar sesión
|
||||
|
||||
1. Hacer clic en "Iniciar sesión" desde la página principal.
|
||||
2. Ingresar correo electrónico y contraseña.
|
||||
3. Si es la primera vez que se ingresa con una cuenta creada por un administrador, el sistema puede solicitar cambiar la contraseña antes de continuar.
|
||||
4. Si se olvidó la contraseña, usar la opción "Recuperar contraseña": el sistema envía un correo con las instrucciones para restablecerla.
|
||||
|
||||
Al iniciar sesión correctamente, el sistema habilita el menú del panel administrativo según el rol del usuario.
|
||||
|
||||
## 4. Panel administrativo
|
||||
|
||||
El menú del panel se organiza en los siguientes grupos:
|
||||
|
||||
- **Competición**: Temporadas, Sanciones, Canchas.
|
||||
- **Gestión de equipos**: Equipos, Jugadores.
|
||||
- **Novedades** (blog).
|
||||
- **Usuarios**.
|
||||
- **Sistema**: Estadísticas, Registro de auditoría, Administración de datos.
|
||||
- **Configuración**: Cambiar contraseña, Editar perfil.
|
||||
|
||||
Los torneos, divisiones, fases y partidos no tienen un ítem propio en el menú: se llega a ellos entrando a una temporada y navegando hacia adentro (Temporada → Torneo → División → Fase/Partido).
|
||||
|
||||
### 4.1 Temporadas, torneos y divisiones
|
||||
|
||||
1. **Crear una temporada**: agrupa uno o más torneos (por ejemplo, Apertura y Clausura del mismo año).
|
||||
2. **Crear un torneo** dentro de la temporada: cargar nombre, fecha de inicio y demás datos generales.
|
||||
3. **Crear divisiones** dentro del torneo (por ejemplo, categorías por edad, nivel o género).
|
||||
4. **Inscribir equipos** a cada división.
|
||||
5. **Generar la etapa** del torneo: el sistema arma automáticamente la fase de grupos y, una vez que termina, las llaves de eliminación directa según la cantidad de equipos clasificados (con "bye" a los mejores puestos si esa cantidad no es potencia de 2). Si el torneo define más de una copa (por ejemplo Copa Oro para los primeros puestos y Copa Plata para el resto), el sistema arma cada bracket por separado, tomando la posición de arranque de cada copa como el "1er sembrado" de su propio cuadro.
|
||||
6. Una zona puede dividirse en **varios sub-grupos** (por ejemplo "Grupo A" y "Grupo B" dentro de la misma zona) para repartir muchos equipos en fases de grupos más chicas. Una zona con 2 o más sub-grupos siempre necesita al menos una copa configurada: sin ella no habría manera de determinar un campeón entre los sub-grupos, así que el sistema rechaza guardar esa combinación.
|
||||
7. El torneo no puede pasar a **"En curso"** si alguna zona tiene menos de 2 equipos o si algún equipo inscripto tiene menos de 4 jugadores habilitados (ver 4.3) — el sistema avisa qué falta corregir antes de poder arrancar.
|
||||
|
||||
### 4.2 Equipos
|
||||
|
||||
Esta pestaña lista **clubes**: la identidad estable de una institución (por ejemplo "Central Entrerriano"), que se mantiene igual de una temporada a la otra. Un club agrupa todos sus equipos por temporada — así un club que jugó varios años aparece **una sola vez** en la lista, en vez de una fila repetida por cada temporada en la que participó.
|
||||
|
||||
- **Alta de un equipo nuevo**: nombre, código de tres letras, color de camiseta, escudo. Si ya existe un club con ese nombre, el equipo nuevo queda vinculado a él automáticamente; si el nombre no existe todavía, el sistema crea el club correspondiente.
|
||||
- Búsqueda y filtrado por nombre de club.
|
||||
- **Ver** un club abre su historial: la lista de sus equipos, uno por cada temporada/torneo en el que se inscribió, con el código de tres letras y el torneo correspondiente de cada uno.
|
||||
- **Eliminar** un club solo está permitido si no tiene ningún equipo ni escuadra vinculada; si todavía tiene historial, el sistema rechaza el borrado.
|
||||
- Desde el historial del club se puede eliminar un equipo puntual (una temporada específica) sin afectar el resto del historial del club.
|
||||
- Cuerpo técnico: cargar el director técnico y demás staff se hace desde el equipo de esa temporada (dentro del historial del club), no desde la lista de clubes.
|
||||
|
||||
**Escuadras (club matriz)**: cuando una institución tiene más de un equipo dentro de la misma temporada (por ejemplo "Echagüe A" y "Echagüe B"), cada uno se da de alta como su propio club. Para agruparlos bajo la institución:
|
||||
|
||||
1. Entrar al historial de la escuadra (por ejemplo "Echagüe B").
|
||||
2. Elegir el club matriz en **"Vincular con club matriz"** y confirmar con **"Vincular"**.
|
||||
3. El historial de la institución matriz pasa a listar cada escuadra vinculada, y el de la escuadra muestra un enlace a su institución.
|
||||
|
||||
La vinculación es de un solo nivel: una institución no puede a su vez ser escuadra de otro club, y un club que ya tiene sus propias escuadras no puede pasar a ser escuadra de otro. Un club vinculado puede **desvincularse** en cualquier momento desde su propio historial.
|
||||
|
||||
### 4.3 Jugadores
|
||||
|
||||
- Alta de un jugador: datos personales, DNI (único por jugador), equipo al que pertenece.
|
||||
- Edición o baja de jugadores.
|
||||
- Búsqueda y filtrado.
|
||||
- Número de camiseta (dorsal): se asigna por equipo y temporada, no es fijo para el jugador — puede cambiar si pasa a otro equipo o temporada.
|
||||
|
||||
**Ficha médica y habilitación**: para que un jugador pueda sumar puntos en un partido tiene que estar **habilitado** para esa temporada:
|
||||
|
||||
1. Subir el PDF de la ficha médica del jugador (por equipo y temporada).
|
||||
2. Un administrador la revisa y la **aprueba** o **rechaza**. Solo queda habilitado cuando está aprobada y tiene un archivo real cargado — una aprobación sin archivo no habilita a nadie.
|
||||
3. Una vez aprobada, la ficha no se puede volver a subir (queda de solo lectura); si hace falta corregirla, primero hay que rechazarla.
|
||||
4. La habilitación es específica de esa temporada: un jugador que jugó habilitado el año pasado empieza la temporada nueva sin habilitar, aunque no haya cambiado de equipo.
|
||||
|
||||
### 4.4 Partidos
|
||||
|
||||
- Los partidos de fase de grupos y de eliminación se generan automáticamente al crear la etapa.
|
||||
- Cargar el **resultado** de un partido una vez jugado, planilla por planilla: se cargan los puntos de cada jugador de ambos equipos y el resultado final se calcula sumándolos (no se tipea aparte). El sistema actualiza automáticamente la tabla de posiciones y de goleadores.
|
||||
- Reglas al cargar el resultado:
|
||||
- Solo pueden sumar puntos jugadores **habilitados** y sin sanción activa. Si algún jugador cargado no cumple esto, el sistema rechaza el guardado y lista, con nombre y equipo, a todos los jugadores con problema (no solo el primero).
|
||||
- Un equipo necesita **al menos 4 jugadores habilitados** para que se le pueda cargar un resultado normal; si no llega a ese mínimo, el partido tiene que cargarse como **walkover** ("Marcar W.O.") en lugar de una planilla común.
|
||||
- No se permiten empates: en fase de grupos el desempate en la tabla es PTS → PG (partidos ganados) → DG (diferencia de gol) → resultado entre los propios equipos empatados → DG de esos cruces; en fase eliminatoria, si el partido termina empatado, se juega tiempo suplementario.
|
||||
- **Walkover**: un administrador puede marcar un partido como walkover indicando qué equipo se presentó — el sistema carga el resultado reglamentario a favor de ese equipo.
|
||||
- Consultar el estado de cada partido (programado / jugado / walkover).
|
||||
|
||||
### 4.5 Estadísticas y goleadores
|
||||
|
||||
- Las estadísticas de puntos por jugador se cargan como parte de la planilla del resultado del partido (ver 4.4), no por separado.
|
||||
- El sistema arma automáticamente la tabla de goleadores del torneo a partir de esta carga.
|
||||
|
||||
### 4.6 Sanciones
|
||||
|
||||
1. Registrar una sanción a un jugador (tipo, motivo, duración).
|
||||
2. El jugador o su equipo puede presentar una **apelación** desde el sistema.
|
||||
3. Un administrador revisa la apelación y la marca como **aceptada** o **rechazada**.
|
||||
4. Si es aceptada, la sanción se levanta; si es rechazada, la sanción sigue vigente.
|
||||
5. Un jugador sancionado no puede sumar puntos en un partido mientras la sanción esté activa (ver 4.4).
|
||||
|
||||
### 4.7 Canchas (Venues)
|
||||
|
||||
- Alta, edición y baja de las canchas/sedes donde se juegan los partidos.
|
||||
- Al programar un partido, dos partidos en la misma cancha necesitan al menos 2 horas de diferencia entre sí.
|
||||
|
||||
### 4.8 Usuarios
|
||||
|
||||
- Alta de nuevos usuarios administradores, con asignación de rol.
|
||||
- Activar o desactivar cuentas existentes.
|
||||
- Un usuario puede cambiar su propia contraseña o editar su perfil desde **Configuración**.
|
||||
|
||||
### 4.9 Novedades (Blog)
|
||||
|
||||
- Publicar noticias o crónicas de partidos, visibles en la vista pública.
|
||||
- Editar o eliminar publicaciones existentes.
|
||||
- Una publicación puede guardarse como borrador antes de hacerla visible al público.
|
||||
|
||||
### 4.10 Sistema
|
||||
|
||||
- **Estadísticas**: panel con métricas generales de uso/carga de la liga.
|
||||
- **Registro de auditoría**: historial de acciones administrativas relevantes (quién hizo qué y cuándo), para trazabilidad.
|
||||
- **Administración de datos**: herramientas internas de mantenimiento de datos, de uso ocasional por el equipo técnico.
|
||||
|
||||
## 5. Copias de seguridad
|
||||
|
||||
El sistema incluye un mecanismo de respaldo automático programado de la base de datos. Esta función es de uso interno (no requiere acción del usuario administrador) y se activa desde la configuración del servidor por el equipo técnico responsable del despliegue.
|
||||
|
||||
## 6. Resolución de problemas frecuentes
|
||||
|
||||
| Situación | Qué hacer |
|
||||
|---|---|
|
||||
| No puedo iniciar sesión | Verificar correo y contraseña; usar "Recuperar contraseña" si es necesario. Si la cuenta fue desactivada, contactar a un administrador. |
|
||||
| No veo la opción para administrar cierta sección | El rol asignado a la cuenta no tiene permiso sobre esa sección; contactar a un administrador para revisar el rol. |
|
||||
| Un partido no aparece en el fixture | Verificar que la etapa del torneo haya sido generada y que el equipo esté correctamente inscripto en la división. |
|
||||
| Cargué un resultado y la tabla de posiciones no cambió | Revisar que el resultado se haya guardado correctamente (no quedó en estado pendiente); recargar la página. |
|
||||
| No puedo cargar el resultado de un partido | Revisar que todos los jugadores cargados estén habilitados (ficha médica aprobada con archivo) y sin sanción activa; si el equipo tiene menos de 4 jugadores habilitados, hay que cargar el partido como walkover en lugar de una planilla común. |
|
||||
| No puedo iniciar el torneo | Revisar que cada zona tenga al menos 2 equipos y que cada equipo inscripto tenga al menos 4 jugadores habilitados; el sistema indica qué falta corregir. |
|
||||
| No encuentro el equipo de una temporada anterior en la pestaña Equipos | Esa pestaña lista clubes, no equipos por temporada — buscar el club (la institución) y entrar a **Ver** para encontrar el equipo de la temporada buscada en su historial. |
|
||||
| No puedo eliminar un club | El club todavía tiene equipos o escuadras vinculadas; eliminar primero esos equipos desde el historial del club, o desvincular las escuadras, antes de poder eliminar el club. |
|
||||
| No puedo guardar una zona con varios sub-grupos | Una zona con 2 o más sub-grupos necesita al menos una copa configurada, para poder determinar un campeón entre los sub-grupos; agregar una copa o reducir la zona a un solo grupo. |
|
||||
|
||||
## 7. Soporte técnico
|
||||
|
||||
Para consultas sobre instalación, configuración del servidor o errores técnicos, ver la documentación técnica en [README.md](./README.md) o contactar al equipo de desarrollo del proyecto.
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user