Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Danielsl4/TFG_DAM_2526/llms.txt

Use this file to discover all available pages before exploring further.

FutsalLeague Manager gives referees and administrators complete control over every match, from the moment it is scheduled through to final whistle. Each fixture moves through a well-defined lifecycle, supports granular live event recording, and triggers automatic updates to standings and player statistics the instant it is finalized — all within a single atomic database transaction.

Match lifecycle

Every match carries a status field that controls what actions are available on it.
StatusMeaning
pendienteScheduled, not yet started. Fans can vote on the outcome.
en_cursoIn progress. The referee is actively recording events. Voting is closed.
finalizadoFull time. Standings and player stats have been updated. Votes are settled.
Valid transitions are enforced server-side. Attempting to finalize a match that is already finalizado returns an error.

Match phases

The phase field places a match in the correct stage of the competition bracket.
PhaseDisplay name
fase_de_gruposGroup stage
octavosRound of 16
cuartosQuarter-finals
semisSemi-finals
finalFinal
Knockout matches (any phase other than fase_de_grupos) display a penalty-shootout option when the two sides are level at full time.

Live event recording

While a match is en_curso, referees record events one at a time. Each event is attached to a specific player and team side (home or away).
Event typeEffect
golIncrements home_goals or away_goals and adds 1 to the player’s season goal tally.
tarjeta_amarillaAdds 1 yellow card to the player’s and team’s season stats.
tarjeta_rojaAdds 1 red card to the player’s and team’s season stats.
penalti_tanda_marcadoIncrements home_penalty_goals or away_penalty_goals. Not counted in regular stats.
penalti_tanda_falladoRecorded for the shootout log. No score or stat change.
Every event insertion and deletion runs inside a database transaction so that the match scoreline and player/team statistics stay consistent at all times.

Match locking system

Before a referee can edit a match, they must acquire an exclusive lock on it. This prevents two officials from accidentally making conflicting changes at the same time.
A lock belongs to a single user and auto-expires after 2 minutes of inactivity. The frontend renews it automatically with a heartbeat every 60 seconds while an edit session is open.
  • POST /matches/:id/lock — Acquire or renew the lock. Returns 409 Conflict if another user holds a valid lock.
  • POST /matches/:id/unlock — Release the lock. Admins can force-release any lock with ?force=true.
  • If the referee closes the browser tab, the frontend sends a navigator.sendBeacon request to unlock the match immediately.

Referee workflow

1

Acquire the lock

Call POST /matches/:id/lock. If successful, the referee now holds exclusive edit rights on the match. A heartbeat keeps the lock alive every minute.
2

Set the status to en_curso

Call PUT /matches/:id/status with { "status": "en_curso" }. This signals to fans that the match has kicked off and disables further voting.
3

Record events

For each goal, card, or penalty, call POST /matches/:id/events with the event type, playerId, and teamSide. Events update the scoreline and individual/team stats immediately.
4

Finalize the match

Call PUT /matches/:id/finish (optionally with observations). The server runs a single transaction that marks the match finalizado, calculates team points, updates player match counts, settles fan votes, and releases the lock automatically.

Finalization transaction

When a match is finalized, the following all happen atomically:
  • Match status is set to finalizado and the lock is cleared.
  • player_stats.matches_played is incremented for every player who appeared in a match event.
  • team_stats is upserted with points (3 for a win, 1 for a draw, 0 for a loss), goal counts, and win/draw/loss tallies.
  • Fan votes that matched the actual result have points_awarded = 1 set and the corresponding user_points row is incremented.
Finalization cannot be undone through the UI. If a match is incorrectly finalized, an administrator must manually correct the underlying data.

Voting stats

Every match response includes a votingStats object regardless of status, so the UI can display community sentiment on both pending and finished fixtures.
"votingStats": {
  "local": 42,
  "draw": 18,
  "away": 27,
  "total": 87
}

Match object

The following is a representative JSON response from GET /matches/:id.
{
  "id": 15,
  "date": "2025-03-08T19:00:00",
  "homeTeam": {
    "id": 3,
    "name": "Rayo Vallecano FS",
    "logoUrl": "https://res.cloudinary.com/example/rayo.webp"
  },
  "awayTeam": {
    "id": 7,
    "name": "Atlético Pinto FS",
    "logoUrl": "https://res.cloudinary.com/example/atletico.webp"
  },
  "homeTeamPlaceholder": null,
  "awayTeamPlaceholder": null,
  "homeGoals": 3,
  "awayGoals": 2,
  "homePenaltyGoals": null,
  "awayPenaltyGoals": null,
  "field": {
    "id": 1,
    "name": "Pabellón Municipal Norte",
    "location": "Calle del Deporte, 12"
  },
  "status": "finalizado",
  "phase": "fase_de_grupos",
  "groupName": "Grupo A",
  "observations": "Partido disputado sin incidencias.",
  "userVote": "local",
  "votingStats": {
    "local": 42,
    "draw": 18,
    "away": 27,
    "total": 87
  },
  "events": [
    {
      "id": 101,
      "type": "goal",
      "player": { "id": 22, "name": "Carlos Martínez" },
      "team": "home"
    },
    {
      "id": 102,
      "type": "yellow_card",
      "player": { "id": 35, "name": "Javier López" },
      "team": "away"
    },
    {
      "id": 103,
      "type": "goal",
      "player": { "id": 22, "name": "Carlos Martínez" },
      "team": "home"
    },
    {
      "id": 104,
      "type": "red_card",
      "player": { "id": 41, "name": "Miguel Sánchez" },
      "team": "away"
    },
    {
      "id": 105,
      "type": "goal",
      "player": { "id": 29, "name": "Andrés Ruiz" },
      "team": "away"
    },
    {
      "id": 106,
      "type": "goal",
      "player": { "id": 18, "name": "Pablo García" },
      "team": "home"
    },
    {
      "id": 107,
      "type": "goal",
      "player": { "id": 35, "name": "Javier López" },
      "team": "away"
    }
  ]
}
Event types in API responses are translated from Spanish database values to English: golgoal, tarjeta_amarillayellow_card, tarjeta_rojared_card, penalti_tanda_marcadopenalty_shootout_goal, penalti_tanda_falladopenalty_shootout_miss.

Build docs developers (and LLMs) love