Aller au contenu principal
Petanque Life

Premium Mobile-First UX

F15.11 12 fonctionnalités Planifié

En bref

The Premium Mobile-First UX is a ground-up redesign of the player app: a dual-mode shell that flips between mobile bottom-tabs and desktop sidebar at 640 px, hero-driven home, fullscreen offline-capable license card, ranking sparklines, status-badged match list, an overflow "More" tab, root-level offline banner, tenant-branded theming, a reusable Banner primitive, gated haptics and pull-to-refresh everywhere.

Comment ça fonctionne

The dual-mode app shell is the structural backbone. `useDeviceClass({ cutoff: 640 })` determines whether to render `BottomTabNav` or `SidebarNav` from a single `AppShell` component, with no route change or data remount when the viewport crosses the cutoff. The mobile shell exposes five slots — Home, Matches, Rankings, License, More — while the desktop shell uses a 260 px sidebar and a max-1024 px centred content column.

The home screen leads with the next match as a full-bleed hero: a LinearGradient from primary to secondary, a Fraunces display title, a live countdown, CalendarClock and MapPin meta and a sticky CTA. Below sit two MiniCards (License + Ranking), a recent-results strip and a discover/getting-started slot. Pull-to-refresh works on every primary view (Home, Matches, Rankings, License) using `RefreshControl` tinted with the primary colour, calling the same loader as the initial fetch so refresh behaviour is uniform.

The license tab is a fullscreen card with a hologram gradient ring (primary→secondary→accent), a Fraunces display title, a 220 px QrPlaceholder and status pin. `verifyLicense()` falls back to a signed JWT bundle from cache when offline, surfacing an explicit offline-verification banner. The QrPlaceholder renders a deterministic 25×25 SVG bitmap from the license payload using three QR finder patterns and an FNV-1a-seeded LCG — documented as not scannable; federation scanners read the visible license number — and is swappable for a real QR component without touching surrounding layout.

Rankings shows a "You are #N" highlight card above the leaderboard with current position, Movement (ArrowUp/Down/Minus) and a 56×18 SVG sparkline; every row carries position, name, club, movement and sparkline. The current-user row gets primary-tint background and border. Matches uses MatchRow components with Round badges, LIVE pins, W/L badges, CalendarClock+MapPin meta and chevron CTAs, separated into Upcoming and Recent.

More is the overflow surface with an identity card (avatar + tenant badges), a sign-in CTA for guests, grouped rows for Profile, Equipment, Discover, Search, Community, Weather and Settings, plus a logout at the bottom. Root-level OfflineBanner uses the new Banner primitive (variants info/success/warning/error/offline, optional icon, title, message, action and dismiss) and shows last-sync time plus a Sync-now CTA when pending writes exist. The tenant-branded ThemeProvider hooks `useTenantBranding()` into `activeTenantId` changes, fetching `/me/tenant-profile` and applying a theme via `applyTenantTheme()`, cached per tenant ID. Haptic feedback is gated per interaction via `useHaptics().fire('selection')` on taps and `'warning')` on logout, respecting both accessibility toggle and OS reduce-motion.

Capacités clés

  • Dual-mode shell at 640 px cutoff: bottom-tab on mobile, sidebar on desktop, no remount
  • Hero-led home with next-match countdown, MiniCards, recent results and pull-to-refresh
  • Fullscreen offline-first license card with cached JWT fallback and offline-verify banner
  • Rankings with "You are #N" highlight, movement indicators and per-row sparklines
  • Match list with status badges (LIVE, W/L, Round) and chevron CTAs
  • Tenant-branded ThemeProvider and reusable Banner primitive with offline variant
  • Haptic feedback gated by accessibility and reduce-motion preferences

En pratique

A player launches the app on their phone — bottom tabs, hero showing next match in 2h 15m at Boulodrome Lyon-Sud, primary CTA "Get directions". They pull-to-refresh; tint matches the brand colour, data updates with a soft selection-haptic. Tap License — the fullscreen card glows, QR centred, valid-until pinned.

They enter a basement with no signal; the offline-verification banner appears and the card still renders from cache. Later, on a tablet at home, the same login renders with a sidebar and centred content column — exact same data, different posture. They open Rankings and instantly see "You are #34, up two" with a sparkline showing the last six weeks.

Logout fires a warning-haptic and they are back at sign-in.

Fonctionnalités de ce sous-système

12
ID Status Fonctionnalités
F15.11.01 Livré Dual-mode app shell — useDeviceClass({ cutoff: 640 }) växlar mellan mobile bottom-tab-nav (5 slottar: Hemma / Matcher / Ranking / Licens / Mer) och desktop sidebar (260 px) + centrerat innehåll (max 1024 px) utan route-byte eller data-remount. app/src/components/navigation/AppShell.tsx, BottomTabNav.tsx, SidebarNav.tsx. ✅ PL-T157
F15.11.02 Livré Hero-baserad startsida — nästa match som full-bredd-hero med LinearGradient (primary→secondary), Fraunces display-titel, countdown, CalendarClock/MapPin-meta och sticky CTA. Två-up MiniCards (Licens + Ranking) + senaste resultat + discover-getting-started. Pull-to-refresh. app/src/components/home/PlayerHome.tsx. ✅ PL-T157
F15.11.03 Livré Fullscreen licenskort — hologram-gradient-ring (primary→secondary→accent), Fraunces display-titel, 220 px QrPlaceholder, valid-until + status-pin, offline-verification-banner. Läser från verifyLicense() som faller tillbaka på signerad JWT-bundle från cache när offline. app/app/(tabs)/license.tsx, app/src/components/license/QrPlaceholder.tsx. ✅ PL-T157
F15.11.04 Livré Rankings med "Du är #N"-highlight + sparklines — highlight Card ovanför leaderboard med aktuell position + Movement + mini-sparkline; varje RankingRow har position, namn, klubb, Movement-indikator (ArrowUp/Down/Minus) och 56×18 SVG-sparkline. Current-user row har primary-tint-bakgrund + border. app/app/(tabs)/rankings.tsx. ✅ PL-T157
F15.11.05 Livré Matcher-lista med status-badges — MatchRow med Round-badge (eller LIVE-pin för live-matcher), W/L-badge för finished, CalendarClock+MapPin-meta, score + chevron-CTA. Separata sektioner "Upcoming" och "Recent". app/app/(tabs)/matches.tsx. ✅ PL-T157
F15.11.06 Livré "Mer"-fliken — overflow-meny för sekundära sektioner (Profile, Equipment, Discover, Search, Community, Weather, Settings). Logged-in identity-card med avatar + tenant-badges, sign-in-CTA för gäster, grupperade rows med ikon-bubbla + description. Logout-knapp längst ned. app/app/(tabs)/more.tsx. ✅ PL-T157
F15.11.07 Livré Offline-banner på root-nivå — prominent <Banner variant="offline"> med WifiOff-ikon, "senast synkad HH:MM"-text och optional "Sync now"-CTA när pendingCount > 0. Separat från det tunna OfflineIndicator sync-strippen. app/src/components/OfflineBanner.tsx. ✅ PL-T157
F15.11.08 Livré Tenant-branded ThemeProvider — useTenantBranding()-hook hämtar /me/tenant-profile vid activeTenantId-byte och applicerar theme-objektet via applyTenantTheme(). Caching per tenantId via lastAppliedRef — best-effort, inga fel bubblar upp. app/src/hooks/useTenantBranding.ts. ✅ PL-T157
F15.11.09 Livré Banner-primitiv — ny <Banner>-komponent med variants info/success/warning/error/offline, optional icon (lucide), title, message, action-button och dismiss. Matchar admin-appens Banner-API men med offline-variant (warning-palett) unikt för app-paradigmet. app/src/components/ui/Banner.tsx. ✅ PL-T157
F15.11.10 Livré Haptic-feedback-gating per interaktion — useHaptics().fire('selection') på alla bottom-tab-trycky, match-row-trycky, more-entry-trycky; fire('warning') på logout. Respekterar både accessibility-toggle och OS reduce-motion. app/src/hooks/useHaptics.ts. ✅ PL-T157
F15.11.11 Livré QrPlaceholder — deterministisk 25×25 SVG-bitmap från licens-payload, tre klassiska QR-finder-patterns + FNV-1a-hash-seedad LCG. Dokumenterat ej scanbar; federationens skannare läser license-number från visibel text. Swappbar mot riktig QR-komponent utan ändring av omgivande layout. app/src/components/license/QrPlaceholder.tsx. ✅ PL-T157
F15.11.12 Livré Pull-to-refresh på alla primärvyer — RefreshControl på Home, Matches, Rankings, License. Tint = colors.primary. Samma callback som initial load() — enhetligt refresh-beteende. ✅ PL-T157