Case study · 2026
Liga Astra: a tennis league that publishes its own results
Klub Astra's amateur tennis league moved from WordPress, Google Sheets and embedded dashboards to a fast static site with a small admin panel, where every saved score updates the public tables and the club ranking.
- Client
- Sports club, amateur league
- Status
- Live
- Services
- Empes Studio, Empes Synthesis, Empes Infrastructure
- Scope
- Website design and build, data model, admin panel, newsletter, move off WordPress
Visit the live site: liga.tenisgrodzisk.pl (opens in a new tab) liga.tenisgrodzisk.pl


Problem
The league’s site ran on WordPress, but almost nothing on it was WordPress. Each league page showed five windows into one Google Sheet, cut out by hard-coded cell ranges. Rankings came from two more sheets, the home page and the history pages were Looker Studio dashboards, and scores were sent through an embedded Google Form.
That arrangement had real costs:
- It was wrong in places. A survey of the live site found rows for players who did not exist, a league table that contradicted its own results, and a ranking column showing a spreadsheet error to every visitor.
- It was invisible. Content inside an embedded sheet cannot be indexed, and on a phone it scrolls separately from the page. A league table is exactly what players search for.
- It had no memory. Each round overwrote the last, so history existed only in the dashboards.
- It was fragile. Every roster change meant editing embed addresses in WordPress, and 25 of the 41 indexed pages could not be reached from the menu at all.
Approach
We started with a written survey of what the old site actually was, page by page, before proposing anything. A cheaper option, keeping the spreadsheets and reading them at build time, was costed and set aside, because it would keep every one of the failures above.
The club chose a database as the single source of truth. Three decisions shaped the rest:
- Make wrong data impossible, not just unlikely. A match must name two real players, and the standings are calculated from the results rather than typed next to them, so the table can never contradict the results.
- Keep the public site static. Visitors get plain, fast pages. The database is never exposed to the internet; the site is rebuilt from it when something changes.
- Rebuild only what people use. The 16 pages in the menu were rebuilt, and the other old addresses are redirected.
Read the diagram as text
- Before: Scores sent through a Google Form, then One sheet tab per league, overwritten each round, then Five embedded windows per league page, then Looker Studio dashboards, the only history, then WordPress page around it all. Not indexable, hard to use on a phone, and able to contradict itself.
- one source of truth for players and results
- After: Scores typed on a phone with a short code, then Admin panel for the organiser, then League database: standings calculated from results, then Static website, rebuilt from the data, then Weekly results email, approved by the organiser. The tables cannot disagree with the results.
Implementation
- Public site. League tables, results grids, fixtures, player pages, a club ranking for men and women, statistics and the regulations, 32 pages in all. Navigation and wide tables were designed for phones first: a drawer instead of hover menus, and a results grid that scrolls sideways with the names fixed.
- Admin panel. In Polish, for one organiser: create a round by pasting the rosters (fixtures are generated), edit and merge players, move a player between leagues with a preview of what changes, import old seasons from CSV, and print empty A4 fixture sheets.
- Score entry from a phone. Whoever types in the scores opens one public address and enters a code made of three Polish words. The phone is remembered, and the code opens the results grid and nothing else.
- Publishing. Saving a score triggers a rebuild, and the public pages follow in about two minutes. A diagnostics page in the panel explains, step by step, why a saved score has not appeared yet.
- Weekly newsletter. Every Monday a results summary is drafted automatically and emailed to the organiser, who approves or rejects it. Nothing is sent without that approval. The subscriber list was recovered from the old WordPress plugin, with each person’s consent status kept.
- Regulations as real pages. The rules were moved out of an embedded PDF into readable pages with a link to every clause, so they can be found by search and quoted in a message.
- A switch-over that could be undone. The new site was staged at a private address first. Going live was a pair of renames with an automatic rollback, and the old WordPress install was moved aside rather than deleted.
- Checks on every change. Automated tests cover the data rules, the score parser and the contract between the PHP export and the site, and every internal link is checked on each build.
Read the diagram as text
- Person: Score typed on a phone (three-word code)
- Saved and checked (in one transaction)
- Standings recalculated (from the results, never typed)
- Site rebuilt from the data (automatic)
- Public tables updated (about 2 minutes)
Outcome
- The new site has been live since 17 September 2026, replacing WordPress, the embedded sheets, the Google Form and the Looker Studio dashboards.
- The tables and the club ranking are calculated from the results, so the inconsistencies found on the old site cannot reappear.
- The organiser runs the league from one panel, and scores entered from a phone reach the public pages without anyone pressing a button.
- League pages can be read on a phone and indexed by search engines.
- The front page hero image is served as responsive WebP: a phone downloads a 19 kB version instead of the 2.6 MB original upload.
Read the diagram as text
- 30 Aug: Survey of the old site; database chosen
- 31 Aug: Admin panel and data export started
- 4 Sep: First staging deploy; newsletter; CSV import
- 9 Sep: Real league data imported
- 10 Sep: Score entry link for the scorer
- 11 Sep: Three-word codes; switch-over tooling
- 17 Sep: Live
- 21 Sep: Regulations as pages
- 13 Oct: Monday draft sent for approval
What this shows
A small club’s website problem is often a data problem wearing a design problem’s clothes. Once there was one trustworthy source for players and results, the fast pages, the phone-friendly tables and the weekly email were straightforward to build on top of it.
- Stack
- Astro, Tailwind CSS, TypeScript, Zod, PHP, MariaDB, GitHub Actions
- Type
- Strategy + implementation
- website
- sports
- league
- admin-panel
- newsletter
- wordpress-migration
- static-site
- mobile-first