Ux Patterns
So what: WeCourts's UX is mobile-first, WhatsApp-native, drawer-driven, and operator-console-like for power users. The patterns worth copying are the bottom-sheet drawers, WhatsApp deep-link templating, the Operator Console drilldown sheets, the Path-to-Victory post-tournament narrative, the auto-tagged club facilities, and the public-roadmap-as-trust-signal play. The patterns to avoid are leaky player identity, English-only despite global ambition, and confetti-heavy delight-UX that distracts from organizer workflows.
Source: walked surfaces 2026-05-22; screenshots in screenshots/2026-05-22-wecourts/. Each pattern lists evidence first, recommendation second.
Pattern catalog
1. Bottom-sheet drawer for tournament detail
Evidence: screenshots/43-may-grand-clash.png; raw-html/43-may-grand-clash.text.txt
What: Clicking a tournament card on the homepage opens a bottom drawer (mobile) / modal-side-sheet (desktop) with full tournament details, LFP list, registered teams placeholder, About markdown, club location with map, and "View Full Details" button to navigate to the full page.
Recommendation: Copy — drawer is the right pattern for tournament discovery in feed-based UIs. OpenRally v0.4 W33 routes directly to /[locale]/tournaments/[organizerSlug]/[tournamentSlug] on click. Consider an intermediate drawer for fast-scrolling mobile users.
Effort: Medium. Implement using shadcn Sheet primitive + intersection-observer to defer-fetch tournament detail when card enters viewport.
2. WhatsApp-native click-to-chat + share
Evidence: 20-club-detail.text.txt shows "WhatsApp", "Send to WhatsApp", "Copy Number", "Share Club" actions; 46-jordan-drawer.text.txt shows organizer contact via WhatsApp.
What: Every club, organizer, tournament has primary contact via WhatsApp (wa.me/{e164} deep link). Quick Actions block: Send to WhatsApp / Share Club / Copy Details / Copy Number.
Recommendation: Copy for SEA — WhatsApp is dominant in Thailand and Indonesia (LINE is dominant in TH but WhatsApp has 50%+ secondary penetration). Provide locale-aware messenger selection: TH = LINE primary, MENA = WhatsApp primary, both supported.
Effort: Low. Locale-aware wa.me / line:// deep-link templating.
3. Operator Console with drilldown sheets (not modal stack)
Evidence: 34-featurebase-changelog.text.txt 2026-05-04 entry "Scoring Settings tab is now a console-style interface with drilldown sheets for format, scoring, KOC, TV, magic links, DQ, incidents, seeds, advanced controls."
What: Configuration UI uses drilldown sheets within a parent console instead of stacked modals. Each tab opens a slide-in sheet on top of the parent; clicking out collapses; no nested modals.
Recommendation: Copy for OpenRally's tournament-edit page (currently v0.4 W28 management hub + W28a edit form — drilldown sheets would be cleaner than the current section-block layout for the Operator surface).
Effort: Medium. Refactor v0.4 W28a edit form to slide-in sheets per concern when complexity grows in v0.6+.
4. Global Command Palette (Ctrl+K / Cmd+K)
Evidence: 34-featurebase-changelog.text.txt 2026-05-04 "Organizer Command Palette — Finds organizer actions, players, tournaments, setup routes."
What: Ctrl+K opens a fuzzy-search palette that finds organizer actions, players, tournaments, setup routes — single keyboard shortcut for power users.
Recommendation: Copy — extremely high leverage for power organizers running 10+ tournaments. Existing shadcn cmdk (Command primitive) makes this trivially shippable. Defer to v0.6+ once organizer workflows mature.
Effort: Low-Medium. cmdk + a route + action registry.
5. SOS Console — emergency-action centralization
Evidence: 34-featurebase-changelog.text.txt Apr 18 entry "SOS — Your Tournament Safety Net"
What: Single button labeled "SOS" on the live-tournament screen opens a console with 12+ recovery actions categorized by Teams & Players / Draw & Schedule / Reset & Undo. Each action is destructive but well-scoped (e.g., "Replace a Team — swap in a different team, keep all matches and scores").
Recommendation: Copy — this is the operator-trust feature. Tournament directors lose sleep over what to do when a player no-shows. OpenRally v1 should ship even a minimal version (No-Show, Replace Team, Restart Tournament) before going public.
Effort: High (each action has cascading state implications). Start with 4 actions in v0.6, add 4 per version.
6. Path-to-Victory narrative (post-tournament storytelling)
Evidence: 13-sample-event.text.txt, 29-event-5GLP.text.txt: every completed event shows champion's path with auto-text like "A perfect tournament: won every match from group to final" + W-L record, win rate, game differential, biggest win.
What: Post-tournament, auto-generated per-team narrative summary with 5-7 stats. Shareable. Templated text. No AI required — pure template substitution.
Recommendation: Copy — high social-share leverage, low engineering cost. Players share these on WhatsApp / Instagram. OpenRally should ship this when bracket finalization lands in v0.5.
Effort: Low. Compute stats during VERIFY-bracket-published step; render as a templated section on player tournament-detail page.
7. Auto-tagged club facilities
Evidence: 20-club-detail.text.txt, 47-padel-art-club.text.txt: "Why Choose This Club" block lists 5-6 auto-tags (Excellent Customer Reviews / Popular Destination / Easy to Locate / Online Presence / Open 7 Days / Extended Hours / Established Business / Very Popular Destination).
What: Club detail page shows a curated set of tags derived from metadata (hours, location verification, Google review presence, tournament count). Not user-entered — algorithmically computed.
Recommendation: Copy with caution — wecourts's tags read marketing-positive (only positive descriptors shown). OpenRally should adopt the auto-tag pattern but include neutral tags (e.g., "200+ tournaments hosted", "Joined 2025") rather than only superlatives.
Effort: Low. Tag engine = rule set over club fields.
8. Tournament status states with public visibility on Overdue Results
Evidence: 04-organizers.text.txt shows section "Organizers with Overdue Results — Robro Padel".
What: Organizer directory publicly shames organizers whose tournaments have unsubmitted results past SLA. Public accountability for data quality.
Recommendation: Copy with judgment — strong moat for data quality but politically risky if applied during onboarding. OpenRally could ship this as opt-in initially, mandatory for WPPR-Verified-tier-equivalent organizers later.
Effort: Low. Compute has_overdue_results = COUNT(tournaments WHERE status='ended' AND wppr_status='not_submitted' AND ended_at < NOW() - 14d) > 0.
9. AI poster-to-form (marketed; not yet observed)
Evidence: 15-new-tournament.text.txt: "Upload your tournament poster → AI extracts all data automatically → Form pre-filled with tournament info"
What: OCR + LLM extraction from a tournament poster image, autofilling the create-tournament form.
Recommendation: Beat — OpenRally could ship a cleaner version using Claude Vision or GPT-4V with a structured prompt. Or, more pragmatically, ship a "paste WhatsApp message → autofill" variant which is what organizers actually do today (every observed Jordan / UAE tournament's About has been pasted from WhatsApp).
Effort: Low-Medium. Server-side: image → vision model → JSON → form prefill. Cost: ~$0.01-0.05 per poster. Cheaper to ship "WhatsApp text → structured JSON" first.
10. Public Featurebase roadmap as trust signal
Evidence: wecourts.featurebase.app accessible publicly, listing What's New / Feedback / Roadmap with Planned / In Progress / Completed status.
What: A public roadmap on a separate subdomain. Anyone can read upcoming features and shipped changes. Organizers can vote and comment.
Recommendation: Copy when OpenRally hits v1.0 ship. Visible roadmap signals stability and listening. Sweet spot: Featurebase has a free tier; switching cost is low. Alternatively: Notion public page, but Featurebase's vote / status / changelog integration is best-in-class for SaaS.
Effort: Low. Sign up Featurebase, import roadmap, link from footer.
11. Magic links (auth-less shareable scoped URLs)
Evidence: 34-featurebase-changelog.text.txt 2026-05-04 Operator Console "magic links" tab.
What: Organizers generate signed URLs with granular permission scopes — e.g., scorer-only access, TV-screen access, public-unlisted-view access — usable without login.
Recommendation: Copy — this is how organizers actually run tournaments at the venue: they hand a phone to a scorer (not their phone, not their account). OpenRally should ship signed JWT URLs scoped per tournament + role, generated server-side by Edge Functions.
Effort: Medium. Edge Function generates signed token; route handler validates token + role. Permission model is the hard part.
12. WhatsApp deep-link templating (context-aware CTAs)
Evidence: 34-featurebase-changelog.text.txt Apr 30 entry "Better WhatsApp message links": Solo registrants → Add a partner or track your registration; LFP listings → Manage your listing; Waitlist → Check your waitlist position; Match day → See match schedule and details.
What: Outbound WhatsApp messages (sent via WhatsApp Business API or wa.me share) include deep-links whose target URL varies by recipient context.
Recommendation: Copy for SEA. Locale-aware messenger (LINE in TH, WhatsApp in MENA + SEA secondary). Templating engine driven by recipient role + tournament state.
Effort: Low-Medium. Server-side deep-link templating in openrally-backend Edge Functions, returning the correct URL slot for the recipient's context.
13. WeCourts TV — venue display product
Evidence: screenshots/10-tv.png, raw-html/10-tv.text.txt; "OPEN PAIRING SCREEN", "FULLSCREEN", "Auto refresh every 30s".
What: Big-screen-friendly view of live results and brackets, paired to a tournament via QR code from the operator's phone. 30s auto-refresh; "poster mode" before draw publishes; fullscreen toggle for kiosk mode.
Recommendation: Copy — venues that adopt this become net promoters because their TVs look professional. OpenRally could ship this in v0.7+ once realtime bracket updates land (openrally-backend already plans realtime channel tournament:{id}:bracket).
Effort: Medium. New Next.js route at /tv with QR pair flow + WebSocket subscription to bracket channel + auto-refresh fallback.
14. Player profile with shareable rich-preview URL
Evidence: 34-featurebase-changelog.text.txt Mar 13 entry "Share Your Profile — Every profile has a shareable link that generates a rich preview automatically — on WhatsApp, social, anywhere."
What: Player profile at a stable URL with Open Graph metadata so WhatsApp / Instagram / Twitter render a rich preview when shared.
Recommendation: Copy — viral mechanism. Cost: OG metadata + image generation for the share card. OpenRally v0.4 W29 organizer public profile already exists; player profile is the natural extension.
Effort: Low. OG meta on player page + Vercel OG image generator for the share card.
15. Looking-for-Partner (LFP) with structured player meta
Evidence: 43-may-grand-clash.text.txt LFP block shows player WPPR rating, ID, Hand, Side, Experience years; mobile masked until login.
What: Per-tournament LFP listings with structured padel-domain fields (Hand R/L, Side R/L/Both, years EXP) — not free-text bios.
Recommendation: Copy — Hand + Side are padel-domain-specific and matter enormously for compatible-partner matching (left-side players need right-side partners typically). OpenRally should add these fields to Player profile and surface them in registration UI.
Effort: Low. Schema add + form field + LFP listing UI.
16. Tournament card "Looking for partner: N players" chip
Evidence: 34-featurebase-changelog.text.txt Apr 30 "Looking-for-Partner spotlight on tournament cards"; homepage cards show "5 looking for partner".
What: Tournament card surface includes a chip showing how many players are seeking partners. Clicking jumps to LFP list.
Recommendation: Copy — high-leverage discovery driver. Players browse for tournaments where they can quickly find a partner instead of having to recruit one.
Effort: Low.
17. Multi-currency by club country (AED, JOD observed)
Evidence: 43-may-grand-clash.text.txt "300 AED / player"; 46-jordan-drawer.text.txt "30 JOD / player".
What: Tournament displays fees in the currency of the club's country, not a single platform-wide currency.
Recommendation: Copy — OpenRally will expand from THB-only to MYR + IDR + SGD + VND quickly when shipping to Malaysia, Indonesia, Singapore, Vietnam. Currency on Tournament.entry_fee_currency, defaulting to Club.country.default_currency.
Effort: Low. Schema decision + display formatter.
18. Anti-patterns observed (do NOT copy)
| Anti-pattern | Evidence | Why not |
|---|---|---|
| Duplicate player profiles with no merge UI | Featurebase complaints (Jiten, Elie Nasr) — 32-featurebase.text.txt |
Identity graph leaks compound over time; ship merge from day 1 |
| English-only after 4 years despite "Global Platform" branding | Every page footer | i18n is harder retrofitted; OpenRally's day-1 TH+EN is a real moat |
Marketing-page stats inconsistency (9,400+ players vs 25K+ players vs 15K+ players across pages) |
15-new-tournament.text.txt vs footer vs other surfaces |
Erodes trust on close inspection; pick one number and centralize the source |
| Inconsistent "Tournamants" spelling on Live Now banner | 45-americano-event.text.txt "THIS IS NOT REAL TOURNAMENT. WE ARE TESING THE NEW AMERICANO FORMAT TOURNAMANTS" |
i18n discipline catches this for us automatically — TH bundle would never let "tournamants" through |
| Confetti / celebration animations on completed tournament pages | 13-sample-event.text.txt "Tap to view celebration" |
Delight is fine, but burns CPU on low-end devices — make it opt-in for organizer pages, never auto-play on player pages |
| Top-seed drag-drop without explicit keyboard fallback | Inferred from changelog — OpenRally's v0.5 D13 KeyboardSensor + native <select> fallback is superior |
Don't regress to wecourts's level; keep our a11y story |
Storyblok-style separate /c/changelog/* subpath |
Network capture observed | Adds an extra vendor dependency; Featurebase covers this; pick one |
Quick-wins prioritized (cheap × high-leverage)
In Nestor-priority order — best ROI first:
- WhatsApp deep-link templating with locale-aware messenger picker (effort: ~3 days; impact: massive in SEA acquisition)
- LFP with structured player meta (Hand, Side, EXP) (effort: ~5 days; impact: major player-engagement driver)
- Looking-for-partner chip on tournament card (effort: ~1 day; impact: better discovery)
- Multi-currency on Tournament (effort: ~2 days; impact: unblocks Malaysia/Indonesia/Singapore/Vietnam expansion)
- Path-to-Victory narrative (effort: ~2 days; impact: viral share)
- Shareable player profile rich-preview URL (effort: ~3 days; impact: viral share)
- Auto-tagged club facilities (effort: ~3 days; impact: better venue browse)
- Tournament card status states (Awaiting verification / Published) (effort: ~2 days; impact: trust signal)
- Public roadmap on Featurebase (effort: ~1 day; impact: trust signal)
- Magic links (scorer-only auth-less URL) (effort: ~5 days; impact: unlocks venue-side workflows)
Big bets (separate decision): - Bracket-format breadth (Groups+KO, Round Robin, KOC, Americano) — strategic question for proposal - TV / venue display product — strategic question for proposal - Public Developer API — strategic question for proposal
Post-onboarding-walk patterns (2026-05-22)
The signup → /onboarding → /onboarding/pending-review walk surfaced internal-flow patterns not visible from public-only browsing. Each follows the same Evidence / What / Recommendation / Effort format.
19. Two-persona organizer choose-path on first login
Evidence: screenshots/52-onboarding-root.png + raw-html/62-onboarding-root.text.txt
What: First step after email verification asks "How do you run tournaments?" with two cards: "I'm part of a Padel Club" (club hosts the tournaments) vs "I'm an Organizer Company" (organizes across venues). The choice forks the downstream onboarding into different entity-creation forms with different field requirements.
Recommendation: Copy if OpenRally adopts a multi-venue / event-management-company concept. The persona split is meaningful because the data shapes (Club vs Organizer Company) really do differ. If OpenRally stays single-Organizer model, the split is unnecessary.
Effort: Medium. Schema change (organizer_type discriminator) + 2 forms + branching middleware.
20. Dedupe-search-before-create on entity onboarding
Evidence: screenshots/53, 59, 63 — both Org Company and Padel Club flows show "Find your X" with existing-entity list BEFORE the "Can't find it? Create new" fallback.
What: Before letting a user create a new entity (Org or Club), show them the existing entries that might match. Reduces accidental duplicates dramatically.
Recommendation: Copy with priority — OpenRally's v0.3 W20-W21 admin organizer-application flow doesn't currently show applicants the existing organizers list before they create one. Adding this is cheap and prevents the duplicate-records pain wecourts has on the player side from happening on the organizer side.
Effort: Low. Add Find your X server-side search + render existing-entities list above "Create new" CTA.
21. Identity-graph pre-fill on signup
Evidence: screenshots/52-onboarding-root.png — "✓ WhatsApp verified" appeared on first arrival without the user entering a phone. The system pulled Nestor's +66620230022 from the existing WPPR identity graph (where his email or name was already associated with a player record).
What: When a new account is created, look up the email/name in the existing player-identity database. If matched (with appropriate confidence), pre-fill phone + name + country + city. Marks the account as "linked".
Recommendation: Copy when OpenRally has an identity graph (probably v3+). Until then it's premature, but it's a known-good UX. Note this requires careful privacy handling (don't reveal personal data on partial match).
Effort: Medium when identity graph exists. Match + confidence-score + fill-or-prompt UX.
22. WhatsApp-as-approval-channel (not email)
Evidence: screenshots/51-onboarding-pending.png: "You'll get a WhatsApp message as soon as you're approved — usually within a few hours. We'll notify you at +66620230022."
What: The platform-driven approval moment (organizer-application accept/reject) is signaled via WhatsApp message, NOT email. Email is the secondary fallback at best.
Recommendation: Copy if OpenRally targets MENA. In SEA, mirror to LINE (Thailand) + WhatsApp (Indonesia / Singapore). OpenRally's v0.3 W20-W21 admin Approve/Reject likely sends only email today.
Effort: Low-Medium. Edge Function to send WhatsApp Business API / LINE Notify message on state transition.
23. Manual organizer-approval gate (human review)
Evidence: screenshots/51-onboarding-pending.png — entire dashboard gated behind manual review by WeCourts staff.
What: Every new organizer signup queues for human review. ALL write-action routes (/tournaments/new, /matches/create, /organizer, /profile, etc.) redirect to /onboarding/pending-review until approved.
Recommendation: Consider carefully — this is wecourts's anti-abuse / quality moat. The trade-off:
- Pro: Filters spam, prevents fake organizers from polluting the player-identity graph, maintains "trusted organizer" brand
- Con: Human-review SLA "usually a few hours" is a friction point for genuine self-serve organizers; doesn't scale linearly
OpenRally's choice: (a) ship self-serve and use abuse-controls + reputation to keep quality up (the modern SaaS path), or (b) ship manual-review like wecourts (the marketplace-trust path). Decision is in proposal as Decision 10 (added). Effort: N/A — OpenRally's v0.3 already has the admin Approve/Reject flow; the question is whether to make it a HARD gate (block-everything-until-approved) or SOFT gate (allow-but-limit-visibility-until-approved).
24. Auto-polling for state change on pending-review screen
Evidence: raw-html/52-onboarding-pending-review.text.txt shows "Waiting for approval — checking automatically" + observed /api/auth/me XHR re-fires every poll cycle.
What: The pending-review page polls server-side every N seconds for state change (rather than requiring user to refresh). When state changes, the page auto-redirects to the new state's URL.
Recommendation: Copy for any waiting-on-server-event UX. OpenRally already has a Supabase realtime channel infrastructure (tournament:{id}:bracket per openrally-backend/README.md); could extend with user:{id}:onboarding or user:{id}:approval for the same pattern.
Effort: Low (with realtime infra already planned). Add subscription + redirect-on-state-change.
25. Dual-mode logo upload (file OR URL)
Evidence: screenshots/64-create-company-form.png — Logo section has file input ("Upload Logo, Click to browse • PNG, JPG up to 10MB") AND below it "or Enter Image URL" with a separate URL input.
What: Logos can be uploaded as a file OR pasted as a URL pointing to an existing hosted image.
Recommendation: Copy — small but meaningful. Many organizers already have hosted logos on Instagram / their own websites. Letting them paste a URL skips re-upload friction. OpenRally's v0.3 W11 cropperjs flow accepts file-only.
Effort: Low. URL input + fetch-and-validate (size, format, accessibility) on server.
26. Inline UX explainer text on form fields ("This number will be pre-filled for new tournaments")
Evidence: screenshots/64-create-company-form.png — WhatsApp Number field has inline explainer text directly below it.
What: Form fields that have non-obvious downstream effects (a field that pre-fills another flow, a field that gates a feature) have inline explainer text right under the input — not in a tooltip, not in a help icon, not in a separate doc.
Recommendation: Copy — high-leverage. OpenRally's forms (v0.3 tournament-form, venue-form) could benefit. Especially for the messenger fields (which we'd add per Delta D2).
Effort: Low. Add description slot to form field components.
27. Country-coupled City dropdown (cascade picker)
Evidence: screenshots/61-country-dropdown.png — Country dropdown shows full ISO-3166; selecting a country populates the City dropdown.
What: City dropdown's options depend on the Country dropdown's selected value. Standard cascade picker.
Recommendation: Copy as the default cascading-picker pattern. OpenRally v0.3 W26 venue-form should follow this.
Effort: Low. Cities table with country FK.
28. Role-split signup AND login URLs (/login/organizer + /login/player)
Evidence: Phase 1 walk screenshots/11-signup-organizer.png (/signup/organizer) + Phase 3 walk screenshots/49-login-filled.png (/login/organizer).
What: Signup AND login routes have role-specific URLs that show different forms and different OAuth providers per role. Different "choose your path" entry points exist at /signup and /login root.
Recommendation: Adopt selectively — at v1 we likely have a single sign-in / signup. But the role-split is useful for marketing pages and SEO (organic search for "padel organizer signup" vs "padel player signup"). OpenRally's v0.4 already has player auth + v0.3 has organizer-application admin flow; the URL-split is small.
Effort: Low.
29. Static "Hub" PWA naming for the organizer admin app
Evidence: _buildManifest.js extract revealed wecourts-hub-pwa-32px.png, wecourts-hub-pwa-180px.png, wecourts-hub-pwa-192px.png — distinct "hub" asset naming.
What: WeCourts splits the marketing site (Next.js Pages Router at wecourts.com) from a separate "Hub" SPA (same domain, deeper routes) for the organizer admin. The Hub is PWA-installable with its own icon.
Recommendation: Consider — OpenRally currently runs admin (v0.3) and organizer (v0.3 W25-W31) surfaces inside the same Next.js 16 App Router. Splitting Hub vs Marketing into separate apps is more eng overhead; staying with App Router (Server Components, partial prerendering) is the modern path and arguably better than wecourts's split. Recommend NOT splitting unless we have a specific reason (e.g., very heavy organizer-side JS bundle that hurts marketing-page perf).
Effort: N/A (recommendation is don't-do).
30. Cache-Control no-cache on index.html
Evidence: _buildManifest.js body included <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> etc.
What: The SPA's index.html is never cached by CDN or browser — every page load fetches fresh HTML. Prevents stale JS chunk references after deploys.
Recommendation: Adopt for any future OpenRally SPA route (e.g., /tv if we ship it). For Next.js App Router pages the framework handles this differently (RSC streams are revalidated server-side), so likely not needed at the framework level.
Effort: N/A.
New anti-patterns observed in onboarding
| Anti-pattern | Evidence | Why not |
|---|---|---|
Two separate URL surfaces (wecourts.com marketing + Hub SPA) for organizer admin |
_buildManifest.js extract — Hub PWA assets |
Adds complexity. With Next.js 16 App Router, OpenRally can have admin + marketing in one repo with shared design system, locale, components. Don't split. |
| Manual organizer-approval as hard gate on ALL write actions | screenshots/51-onboarding-pending.png |
Friction for genuine self-serve users. OpenRally should consider soft gate (limit visibility / require flag) rather than block-everything-until-approved. |
| "Endurance Sports Servies" typo in production organizer directory | raw-html/63-onboarding-org-company-list.text.txt |
Symptom of no name-canonicalization step; another input to the duplicates problem |
| "Tournamants" typo on production test event banner (Sami's Americano test) | raw-html/45-americano-event.text.txt (Phase 1) |
i18n discipline catches this for OpenRally automatically — already noted in earlier anti-patterns |