@v4/code-of-conduct
The In-Arena Code of Conduct incident report: a signed-in fan picks a game, describes what happened and it is emailed to ELB.
Overview
@v4/code-of-conduct is the In-Arena Code of Conduct incident report, which
replaces the third-party form at inform.euroleague.net. A signed-in fan chooses the game they
attended, says what happened, attaches photos or videos, and the report is emailed to ELB. The
design is ELB – Web – Content, section 29238:5802. Club-only.
The page is apps/web/src/app/[locale]/code-of-conduct/page.tsx (noindex), and its layout
loads the ghost-basketball theme part, because the game cards are basketball components.
The three entries
| Entry | Contents |
|---|---|
@v4/code-of-conduct |
The screens — sign-in card, game picker, report, sent — and their parts (stepper, fields, tick box, drop zone, file rows, notices), all plain React over ghost-core, ghost-content and ghost-basketball; the rules; the sample season |
@v4/code-of-conduct/page |
CodeOfConductPage, the client page body: asks /api/auth/session who is signed in and shows the sign-in card or the form |
@v4/code-of-conduct/server |
The three route factories the app mounts under app/api/code-of-conduct/, and the email |
The screens carry no sign-in code, so Storybook (Code of Conduct/*) renders exactly what the
site does, on the sample season.
The endpoints
| Route | What it does |
|---|---|
GET /api/code-of-conduct/games |
One season's played games, with the competitions and seasons offered (EuroLeague and EuroCup, the newest two seasons each), from the Competition Engine. The engine filters by round and one club only, so venue, team and date are filtered in the browser |
GET /api/code-of-conduct/reporter |
The signed-in reporter's name and email, read on the server from the session's ID token (readSessionProfile in @v4/cortex-sso/server). 401 signed out |
POST /api/code-of-conduct/report |
Send. Takes the game, the description and the files as a multipart form; the reporter is always the session's, never the form's. Checks everything the form checks again, then emails the report. Answers 401 signed out, 422 for a profile with no email, 400 for a report that breaks a rule, 413 for a file over 50 MB, 503 when sending is not set up and 502 when the email failed |
The rules, the same in the browser and on the server (src/report.ts): a description of 20 to
2,000 characters, the consent box ticked, at most five files of JPG, PNG, HEIC, MP4 or MOV, up to
50 MB each. A multipart body sends each line break as CRLF, so the server counts it once.
The email
renderIncidentEmail writes the report in the agreed format: the template attached to the ticket,
whose *|merge_tags|* (reporter, game, round, date, venue, description, files) it fills by their
own names, plus the same content as plain text and HTML. The game is dated in ELB's time zone
(Europe/Madrid), the reply goes to the reporter, and the files travel as attachments. The
template's "Open report in the browser" link is left out: reports are not stored.
Sending is a seam, deliver. No email provider is chosen yet, so:
- Locally (
NODE_ENVnotproduction), the email is written to the server log instead (logIncidentEmail) and the form goes through to its confirmation. - Everywhere else, Send answers 503 and the form shows its "couldn't send" notice with Try again.
The ticket names the recipients: three ELB addresses for production, one testing address for dev and staging. They are configured with the provider.
Running it locally
The sample season stands in for the Competition Engine wherever the engine cannot be reached at all, locally only; production shows the load error instead, never made-up games. A timeout is not "cannot be reached", so point the engine at a closed port to get the sample at once:
COMPETITION_ENGINE_BASE_URL=http://127.0.0.1:9 pnpm --filter @v4/web exec next dev --webpack -p 3000
Signing in needs http://localhost:3000 (the only address Cortex stage returns to) and the
CORTEX_SSO_* settings. With sign-on off, the form opens with a placeholder reporter, and on a
local site Send logs the email as that reporter.
What it changed elsewhere
- ghost-basketball
Game:layout="small"keeps the Small card at every width (the report's game panel);action.trailingIconandaction.accessibleLabelfor the card's own button. - ghost-core
FilterDropdownTrigger:ariaHasPopup={false}for a trigger that opens an inline panel ("More filters") rather than a list. - cortex-sso:
readSessionProfile, the signed-in visitor's first name, last name and email from the session's ID token, for a server route.