Skip to content

feat(sports): add inter-house fixtures with clash detection and derived standings (fixes #305) - #310

Open
MOHITKOURAV01 wants to merge 1 commit into
Sitaram8472:mainfrom
MOHITKOURAV01:feat/issue-305-sports-fixtures
Open

feat(sports): add inter-house fixtures with clash detection and derived standings (fixes #305)#310
MOHITKOURAV01 wants to merge 1 commit into
Sitaram8472:mainfrom
MOHITKOURAV01:feat/issue-305-sports-fixtures

Conversation

@MOHITKOURAV01

Copy link
Copy Markdown
Contributor

Related Issue

Closes #305

Description

Fixtures for the inter-house championship, with the points table computed from results rather than stored.

The clash check is the reason the module exists. createFixture and updateFixture both run the same check against every non-cancelled fixture on that date, comparing integer minute ranges rather than time strings, and reject three cases with a 409 naming the fixture that already owns the slot:

  • a house already playing in an overlapping window
  • the venue already booked in an overlapping window
  • an official already committed elsewhere

It runs on update as well as create. The double booking that actually happens in a school is a time being moved, not a fixture being added, and an update path that skips the check is the same as having no check. There is no override flag — a flag would be used by exactly the person in a hurry on the morning of the match.

Standings are derived, never stored. GET /standings folds completed and walkover fixtures into a table on every request. There is no standings document that can drift away from the results it came from, so the table and the scorelines printed beside it cannot disagree. The ordering breaks every tie — points, wins, score difference, score for, head-to-head between exactly the tied pair, then house name — so two identical requests can never come back in a different order. Abandoned fixtures are excluded rather than counted as goalless draws, because those are different things and only one of them can be replayed.

The outcome is a function of the scoreline. A client sending homeScore: 1, awayScore: 3 alongside outcome: "home" is corrected in the model's pre-validate hook, not believed. A walkover has no scoreline but must name a house that is actually playing. A result cannot be recorded against a fixture whose date has not arrived, and clearResult reopens a fixture for correction so a mistyped score is fixable without touching the table by hand.

The /sports page shows the same standings to everyone and puts result entry inline for staff, rather than keeping a separate admin screen that drifts out of sync.

Pages / Components Added or Modified

  • backend/models/Fixture.js — new; clash primitives, derived outcome, buildStandings
  • backend/controllers/sportsController.js — new; findClashes runs on create and update
  • backend/routes/sportsRoutes.js — new; /api/sports
  • frontend/src/pages/SportsBoard.jsx — new; standings / fixtures / day schedule
  • backend/server.js — +5 lines, one self-contained require-and-mount block
  • frontend/src/App.jsx — +8 lines, one lazy import and one route

Screenshots

No screenshots attached. The page needs a populated fixture list and a live MongoDB to show anything meaningful, and I did not want to attach a screenshot of an empty state and imply it had been exercised against real data. The layout is standard Tailwind and matches the existing pages.

Checklist

  • npx eslint src passes — 18 problems, unchanged from origin/main; none in the files this PR adds
  • npx vite build succeeds
  • node --check passes on every backend file added or changed
  • Tested in dev server — not run against a live database; the handlers are exercised by reading, not by execution
  • Responsive layout verified (flex-wrap and md: grid breakpoints throughout)
  • No console errors or warnings introduced
  • Animations and transitions intact — this page adds none

Merge conflicts

Checked rather than assumed. This branch was merged pairwise against all 22 other open feature PRs: no conflicts with any of them. The three shared files are additions-only (server.js +5/-0, App.jsx +8/-0), and the insertion points were deliberately chosen to sit clear of the lines every other open PR is appending to.

Eight of the other open PRs already conflict with each other on server.js, App.jsx and TeacherDashboard.jsx. That set is identical with and without this branch, so none of it originates here.

…ed standings

Fixtures for the inter-house championship, with the points table computed
from results rather than stored.

The clash check is the reason the module exists. createFixture and
updateFixture both run it against every non-cancelled fixture on the same
date, comparing integer minute ranges rather than time strings, and reject
three cases with a 409 naming the fixture that already owns the slot: a
house already playing, a venue already booked, an official already
committed. It runs on update as well as create, because the double booking
that actually happens is a time being moved rather than a fixture being
added.

Standings are folded out of completed and walkover fixtures on every
request. There is no standings model to fall out of step with the results,
and the ordering breaks every tie down to house name so two identical
requests cannot come back in a different order. Abandoned fixtures are
excluded rather than counted as goalless draws.

The outcome of a match is derived from its scoreline in a pre-validate
hook, so a client sending 1-3 alongside an outcome of 'home' is corrected
rather than believed, and a result cannot be recorded against a fixture
whose date has not arrived.

The /sports page shows the same table to everyone and puts result entry
inline for staff.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Inter-house sports fixtures with clash-free scheduling and a derived points table

1 participant