← All projects
Case study · Full stack · Product, design and engineering

963 Court

An Arabic-first basketball statistics platform for Syrian leagues: a public site for standings, matches, players and teams, and an admin dashboard with two AI features — box scores read from photos, and a chat assistant whose edits wait for approval.

Role
Project manager, UI/UX designer and full stack developer
Audience
Fans, clubs and league organisers, in Arabic
Scope
Design, front end, API, data model, AI features, access control, deployment
963 Court home page with the latest results and group standings
01 — The brief

Real stats, without the typing

Syrian leagues needed one Arabic place for results, standings and player statistics — and entering those stats by hand from FIBA box scores was slow and error-prone.

I designed the interface in UX Pilot, managed the project, then built it full stack as two independent apps: a Next.js public site and admin dashboard, and a NestJS API on Prisma and MySQL where standings and averages are computed on read, never stored.

02 — What I built

Three hard parts

AI · Extraction

Box scores from photos

The admin uploads a FIBA box score and the model extracts it under a strict JSON Schema. Zod then checks what the schema can’t — no negatives, made ≤ attempted — and player names are fuzzy-matched. Nothing saves before a human review.

AI · Assistant

An assistant that asks first

A streaming (SSE) chat answers questions about the data instantly. Its write tools never change anything: they stage a pending action the admin approves from a card in the chat, which then runs through the same validated services as manual entry.

Access

Prime content and fan subscriptions

Any league, season, match or player can be marked Prime. Fans sign in separately and subscribe by month or year; the admin activates each one with a three-level scope — leagues, seasons, phases. Locked pages return a teaser instead of the data.

03 — Screens

Public site and admin, right to left

League standings table with wins, losses, point difference and points
Points-per-game leaders podium above the full player stats table
Match box score with minutes, points, rebounds and assists per player
Player profile with points, assists and rebounds charts across matches
Head-to-head comparison of two teams with per-game stat bars
Admin AI assistant explaining why an extracted box score can’t be saved yet
Match editor with the original box-score image, Prime toggle and validation rules
Subscriber access dialog with league, season and phase scope
04 — Architecture

Decisions that keep the data correct

League → Season → Phase → MatchAggregates computed on readAtomic Prisma transactionsIn-memory cache per seasonUnit-tested FIBA standingsAI vendor isolated in one fileStorage: FTP · Vercel Blob · disk

Standings come from a pure, unit-tested function with the client’s FIBA tiebreakers: head-to-head only counts once all its scheduled meetings are played, otherwise overall point difference decides. Every compound write runs in a transaction, which also clears the season’s cache.

05 — Stack

Built with

Next.js 16ReactTailwind CSS 4shadcn/uiTanStack QuerySSENestJS 11PrismaMySQLJWTOpenAI APIZodGitLab CI/CDPM2i18n / RTL

Two independent apps, back end and front end, deployed automatically by GitLab CI to a VPS I manage, with PM2 keeping them running.

Result: launched in September 2026 at 963court.com, with Prime content and fan subscriptions.

Next projectAl-Ahli SC — academy platform with live streams
Contact

Need a platform like this?

mohammadnoursammane@gmail.com