Conversation
* feat: add doubles and team-profile localization keys
* feat: doubles data layer — side-join, pair rating and match queries
* feat: 2v2 match creation, side picker and team-aware result UI
* feat: team (pair) profile screen with per-sport stats
* feat: global doubles leaderboard with pair rows and search
* fix: move leaderboard singles/doubles toggle to its own row
The mode toggle shared a row with the scrolling sport chips and collided
with them on a long chip list. Give it a full-width row at the top (like
the scope selector), with sport chips and scope each on their own rows.
* feat: move singles/doubles + side selection to a dedicated setup screen
Split the mode/team picker out of the dark QR viewfinder onto a plain
themed MatchSetupView. The match flow routes readyToConfirm/checkingIn
there whenever an explicit setup is needed (deep-link entry or a far-table
scan); a nearby blind scan still auto-confirms as singles on the
viewfinder. QrScanView sheds the now-dead inline controls and GPS banner.
Adds matchSetupTitle/matchSetupSubtitle ARB keys; setup-screen tests carry
the check-in/GPS/doubles assertions.
* chore: expand doubles seed data around the two real users
Christian+Mirko now carry pair ratings across all three sports, plus each
real user gets a rated real+synth alternate team and filler opponent pairs
so every doubles board has depth. New doubles() helper seeds the C+M pair
across states/sports (win, loss, awaiting, disputed, live) so their team
profile stats and history populate fully.
* fix: lead leaderboard filters with sport, group the segmented selectors
Sport chips now come first as the primary axis; the two like-styled
segmented controls (mode, then scope) sit together below, so the differing
control styles no longer read as three scattered rows.
* feat: give the doubles leaderboard a podium like singles
Make LeaderboardPodium pair-aware via optional label/initials resolvers,
and render doubles with the same podium-plus-rows shape as singles — a pair
shows both members joined ("Anna & Bruno") with combined initials ("AB").
* fix: bump workaholic achievement threshold to 15 matches/month
Data-only edit to the achievementDefs seed (was 10) and the matching
threshold documented in the firestore schema, ahead of the Phase 7A
achievements client work.
* feat: add achievement data layer (models, evaluator, repository)
New client-side-only achievements slice, mirroring the elo_calculator
pattern:
- AchievementDefModel — the achievementDefs/{id} schema shape.
- AchievementEvaluator — pure, side-effect-free evaluation of all 6
seeded conditions (first_victory, win_streak, giant_slayer, explorer,
monthly_count) against a per-match context, write-once by construction
(never re-emits an id already in currentBadges). tournament_champion
is intentionally never evaluated here.
- AchievementRepository — bounded reads over achievementDefs.
- ARB keys for all 6 badge names/descriptions plus the profile grid and
unlock-reveal strings (EN + IT), the assets/badges/ block in
pubspec.yaml, and the 6 provided badge PNGs.
Each piece ships with its own unit test (evaluator covers every
condition's boundaries and the write-once rule).
* feat: evaluate and persist achievements in the match confirmation transaction
confirmResult now returns the newly-earned achievementIds per player
(Map<uid, Set<achievementId>>) instead of void. MatchRepository builds
the AchievementEvaluator's context in _prepareConfirmation (reusing the
per-player confirmed-history read already done for streak/monthly/
explorer — no new query) and writes badges.{id} = Timestamp for every
player inside the SAME atomic transaction as the match/ELO/facility
writes (Phase 4 Transaction Rule), via SetOptions(merge: true).
Write-once is enforced end-to-end: the evaluator never returns an id
already present in the user's badges map, so the merge write can never
clobber an existing unlock timestamp. Achievement reads are cosmetic
(Spark-Tier Trust Boundary) and degrade to "no badges" on any failure
rather than failing the confirmation.
* feat: award tournament_champion badge on tournament completion
completeIfDecided now awards the winner the tournament_champion badge
in the same commit as the completed/winnerUid write — the only place
this condition is evaluated (never at match confirm). Write-once: the
winner's badges map is read before any write, and the merge write is
skipped when the id is already present, so a re-derived champion never
overwrites an existing unlock timestamp.
* feat: show client-local achievement unlock reveal on match summary
ResultCubit.confirm() resolves the confirming user's own newly-earned
ids (from confirmResult's new return value) to their AchievementDefModels
and carries them as ResultState.newBadges. ResultSummaryView renders an
inline reveal (badge PNG(s) + localized name, scale-in animation, medium
haptic) when non-empty — no FCM, no local-notifications dependency,
mirroring the established Spark-tier client-local alert pattern (queue-
ready, challenge notification). Resolution is decorative: a failed defs
read just skips the reveal, since the badge is already persisted.
* feat: add tappable achievement badge grid to the profile screen
ProfileCubit loads the achievementDefs list (best-effort, decorative —
a failed read just hides the section) and ProfileScreen renders it as a
grid: one art file per badge, earned full-color vs locked desaturated
(ColorFilter greyscale matrix), driven by the seed's iconAsset field.
Works unchanged for a foreign profile.
Each tile is tappable and surfaces its detail as a system-message
SnackBar (the reset-password idiom) instead of a permanent on-grid
caption — the unlock date for an earned badge, or how to earn it for a
locked one — keeping the grid compact.
AchievementRepository is threaded through createRouter/ProfileScreen
alongside the other per-tab repositories.
* fix: remove empty space under profile achievement badges
GridView.count's childAspectRatio forces every row to a fixed height —
once the on-grid caption was replaced by the tap-to-reveal SnackBar, the
row height no longer matched the (now shorter) content, leaving a gap
under every badge. Swapped to a Wrap of fixed-width tiles, which sizes
each row to its actual content instead of a computed aspect ratio.
* fix: give Christian's profile fun-stats enough matchups to clear the gate
Favorite Victim and Black Beast only render once a head-to-head hits
kFunStatMinMatchups (3). Rework the seeded win-streak matches to be all
vs. the same opponent (Favorite Victim) and add two more losses vs.
another (Black Beast, on top of the existing confirmed loss), so both
fun-stat cards have seed data to demo on the profile screen.
* fix: restore full-width 3-column layout for the achievement grid
The Wrap from the previous commit fixed the empty-space bug but
regressed the grid to a centered cluster of fixed-width tiles instead
of stretching across the card. Switched to GridView.builder with a
SliverGridDelegateWithFixedCrossAxisCount: crossAxisCount keeps the 3
columns stretching to fill the full width like before, while a fixed
mainAxisExtent (not childAspectRatio) sizes rows to the tile's actual
content height, so the empty-space fix is kept.
* fix: stream the user doc instead of fetching it once in ProfileCubit
ProfileCubit._loadUser() fetched users/{uid} one-shot at cubit creation.
Because StatefulShellRoute keeps each tab's branch (and its cubit)
alive across tab switches rather than rebuilding it, that fetch never
re-ran — so a newly-earned achievement badge (or any other user-doc
change) only appeared after a full app restart, when a fresh
ProfileCubit finally re-read the doc.
Switched to UserRepository.watchUser(uid), the same live-stream pattern
already used by FriendsCubit/AuthCubit, so state.user (and its badges
map) stays current for as long as the cubit is alive.
* chore: mark Phase 7A achievements tasks complete on the task board
Models/conditions, client-side confirm-transaction evaluation, and the
profile badge display all ship in this branch. Drops the "push
notification" line — the client-local in-app reveal (no FCM) is the
shipped behavior, called out as a deviation in the PR description.
* fix: write concrete Timestamp for tournament_champion badge
Address PR 29 review:
- completeIfDecided writes Timestamp.fromDate(now) instead of
serverTimestamp(), keeping the optimistic snapshot parseable so the
champion's Profile tab doesn't flip to an error state mid-write.
- UserModel.fromMap tolerates an unresolved/null badge value defensively.
- Document the match-path non-transactional write-once read asymmetry.
- Sort achievement_repository import before challenge_repository.
* feat(assistant): add chatbot tools service and repository queries
- FacilityRepository.getFacilitiesByStatus, TournamentRepository.getTournamentsInWindow/getParticipantCount, RatingRepository.getUserRank, QueueRepository.getQueueLength/getUserQueueEntry - all one-shot, bounded (limit/count()), no listeners, for Spark-tier read discipline.
- ChatbotToolsService: read-only tool layer over the existing repositories (getEmptyTables, getFacilitiesByStatus, getTournamentsToday, getUserQueuePosition, getUserEloAndRank, explainHowEloWorks). Typed tool_results.dart result objects so the UI can render cards without re-parsing strings.
* feat(assistant): add multi-provider LLM client and settings
Deviation from the original chatbot plan (which assumed a single Gemini key via --dart-define): a runtime-configurable multi-provider LLM layer instead, so the API key/provider/model are user-supplied and persisted on-device rather than baked into the client binary.
- AssistantProvider enum (anthropic, openai, openrouter, gemini) with per-provider default models, all user-editable.
- AssistantSettingsModel + AssistantSettingsRepository, persisted via shared_preferences (added to pubspec.yaml alongside http, already present).
- LlmClient (package:http): OpenAI-compatible /chat/completions for openai + openrouter, Anthropic Messages API (x-api-key + anthropic-version), Gemini generateContent (key as query param). Low temperature throughout. testConnection() fires a minimal request and maps common HTTP failures (401/403 invalid key, 429 rate limit, network error) to short reason codes for localized UI feedback.
* feat(assistant): add chatbot orchestrator, intent matcher, and system prompt
- RuleBasedIntentMatcher: keyword classification (EN+IT) into empty tables / tournaments today / ELO explainer / queue position / general.
- ChatbotSystemPrompt: distills static app facts (ELO rules, not-a-booking-app framing) plus the current turn's tool results as the only source of live data, explicitly forbidding invented facilities/data.
- ChatbotRepository: orchestrates classify -> run tool(s) -> LLM-grounded answer if a key is configured, else deterministic rule-based fallback (limited mode) on missing key or LlmException. Hard rule enforced here in code (never trusted to the LLM or the templates): the localized availabilityDisclaimerShort is appended unconditionally whenever an availability tool fired this turn.
- ChatMessageModel: in-memory (non-persisted) chat message, optionally carrying FacilityToolResult/TournamentToolResult for rich card rendering.
* feat(assistant): localization for Arena Assistant
Adds 42 new ARB keys (EN + IT) covering the chat screen, settings screen, suggested-question chips, limited-mode/error strings, and the four exact disclaimer strings from the plan (availabilityDisclaimerFull/Short, chatbotIntroDisclaimer, plus a one-line tutorialSlide3Disclaimer). Hand-edited app_localizations.dart / app_localizations_en.dart / app_localizations_it.dart to match — flutter gen-l10n is unavailable in this environment, so these were generated by script from the arb content and matched to the existing doc-comment/getter style key-for-key; not verified to compile.
Also appends the short disclaimer as a muted caption on the onboarding mini-tutorial's map/queue slide (Task C4 placement 4).
* feat(assistant): add chat UI and cubit
- ChatCubit/ChatState: initial/sending/answered/error lifecycle, in-memory-only messages (no persistence across restarts), limited-mode tracking driven by whether an API key is configured.
- ChatScreen (route /chatbot, full-screen, pushed outside the shell): app bar with settings gear + overflow 'Clear chat', limited-mode pill linking to settings, dismissible first-open disclaimer banner (chatbotIntroDisclaimer), suggested-question chips, message list with typing indicator, rounded input + 48px circular send button disabled while sending.
- Widgets: MessageBubble (user -> primaryContainer right-aligned, assistant -> surfaceContainer left-aligned, error bubble with retry), FacilityStatusCard (status dot, sport icon, queue count, Open Map, short-disclaimer footer), TournamentCard (name/sport/time, x/max participants, status chip, View), SuggestedChips, DisclaimerBanner, TypingIndicator (animated 3-dot).
- AssistantSettingsCubit/State + AssistantSettingsScreen (route /chatbot/settings): provider dropdown, obscured API key field with show/hide toggle, user-editable model field (prompts to reset to the new provider's default on provider change), Test Connection with loading/success/error feedback, 48px Save button. Works fully with no key saved (chat stays in limited/rule-based mode) with a banner linking here.
- Wiring: route_constants.dart + app_router.dart (both routes registered, /chatbot auth-guarded the same way courtPath/challengePath already are), main.dart provides ChatbotToolsService/AssistantSettingsRepository/ChatbotRepository via MultiRepositoryProvider, Home screen gets a minimal FAB entry point (small surgical diff, dashboard rebuild is a separate in-progress phase).
* test(assistant): unit and widget tests for the Arena Assistant
- RuleBasedIntentMatcher: classifies EN+IT sample phrases into each of the 4 intents plus the general fallback.
- ChatbotToolsService: mocked-repository tests for getEmptyTables, getTournamentsToday, getUserQueuePosition, getUserEloAndRank, and the static explainHowEloWorks (asserts zero repository interactions).
- ChatbotRepository: no-key -> rule-based, LLM-configured-but-errors -> rule-based fallback, LLM-configured-and-succeeds -> LLM text used; hard disclaimer-rule tests proving availabilityDisclaimerShort is appended on both the rule-based path and the LLM path (even when the LLM's own text omits it), and NOT appended when no availability tool fired.
- ChatCubit: limited-mode detection on init, sendMessage emits user bubble then answer, blank input is a no-op, repository errors surface as a retryable error bubble, clearChat resets state.
- ChatScreen widget tests: suggested chips + intro disclaimer banner visible on first open, limited-mode pill shown with no key configured, sending a message renders a user bubble and the assistant reply, a facility card renders with its disclaimer footer visible.
- Repository tests extended: FacilityRepository.getFacilitiesByStatus, TournamentRepository.getTournamentsInWindow/getParticipantCount, RatingRepository.getUserRank, QueueRepository.getQueueLength/getUserQueueEntry (all against fake_cloud_firestore, asserting the bounded limit/count() behavior).
* fix(assistant): verification fixes - analyzer errors, Italian intent matching, queue lookup, DI wiring
* arena assistant
* merge develop, fix map navigation
* fixed the format error
* fix(assistant): localize fallback answers and dynamic card labels
Rule-based/limited-mode answers were hardcoded English, violating the
i18n rule and task_board C2 ("templated ARB answer"). Move them all to
ARB (EN+IT) and pass AppLocalizations into ChatbotRepository.answer();
the English toolContext stays as LLM grounding only.
Also localize two user-facing data tokens the cards printed raw:
- TournamentCard now uses the shared TournamentStatusPill instead of
Text(tournament.status), which rendered "in_progress" untranslated.
- FacilityStatusCard shows a people icon + count instead of "x2".
Map LlmClient TimeoutException to a distinct 'timeout' reason with its
own localized Test-Connection message (chatbotErrorTimeout).
Drop the duplicate short-disclaimer footer from FacilityStatusCard: the
repository already appends it unconditionally to the message text above
the card, so it was rendering twice in one bubble. Tests updated.
* fix(assistant): DI wiring, drop redundant index, parallelize tool reads
- main.dart: ChatbotRepository now reads the root-provided
ChatbotToolsService and AssistantSettingsRepository instead of
constructing its own, so the registered providers are actually used.
- firestore.indexes.json: remove the (ownerType, sportId, elo ASC)
ratings index. RatingRepository.getUserRank (no degreeProgram) is
already served by the existing (sportId, ownerType, elo) index, which
the dashboard uses in production — the new one was a duplicate.
- ChatbotToolsService: fan out the per-row queue-length and
participant-count reads with Future.wait (still bounded <= maxResults)
instead of one sequential round-trip each.
* docs(task-board): check off Arena Assistant tasks (C1–C4)
Core (C1/C2) and UI (C3/C4) are delivered by the assistant feature and
now meet their specs (C2's rule-based path produces localized ARB
answers). Marked done per the PR review.
* fix(assistant): include leaderboard rank in limited-mode ELO answer
The rule-based ELO line printed elo + match count but dropped the rank,
while the LLM toolContext included it — a content asymmetry between the
grounded and limited-mode paths. Add a chatbotAnswerYourEloRanked ARB
variant (EN+IT) and branch on rank nullability so limited mode reaches
parity. Tests cover both the ranked and unranked branches.
---------
Co-authored-by: Abdullah Saeed <abdullahsaeed.engineer@gmail.com>
Co-authored-by: Mirko Pica <mirko.pica@mail.polimi.it>
Co-authored-by: Christian Prendin <christianprendin2002@gmail.com>
* fix: reliable 1v1/2v2 match join Second-player join was routing through a stale/cached match probe and getting rejected or misreported as a network error; resolve it via the facility's own currentMatchId instead. Also stop seeding phantom live matches so every facility starts free. * fix: register & profile provisioning flow Removes the profile-setup/preset-avatar screen in favor of provisioning the profile doc directly on register or on recovery (profile-missing session), and keeps the register button's spinner state correctly gated so recovery mode never flashes during a normal signup. * fix: allow the confirm transaction to write both players' badges * docs: general clean up * fix: registration, opponent's badged, ongoing-tournament * fix(auth): stop empty RegisterScreen flashing during registration Landing/login/register navigated between auth screens with push / pushReplacement, creating an imperative /register match. While the router redirect holds on /register during `registrationInProgress`, go_router stacks a fresh, empty declarative RegisterScreen over the filled pushed one — the "middle" register page (empty fields, still-spinning button) seen between submitting and the tutorial. Switch all auth-screen transitions to `context.go` so the redirect is the sole navigation authority and only one /register is ever mounted. Adds a regression test that reproduces the duplicate (asserts a single RegisterScreen throughout the register -> onboarding flow). * fix(match): resolve mobile_scanner "already started" on the QR screen QrScanView created its own MobileScannerController and passed it to MobileScanner. Per the package contract, supplying a controller makes the widget skip its WidgetsBindingObserver, so the app must drive the camera lifecycle itself. It didn't — on background/resume or hot-restart the native session desynced from the Dart state and start() threw MOBILE_SCANNER_ALREADY_STARTED_ERROR, surfaced as the intermittent "already started" error over the viewfinder. The controller was never used (no torch/lens control), so let MobileScanner own it: the widget then registers its lifecycle observer and handles start/stop on resume/inactive plus the internal hot-restart mitigation. * fix(challenges): clear an accepted challenge from the outbox once played An accepted outgoing challenge lingered on the dashboard forever: nothing ever marked it done. Complete it lazily, client-side (no write back to the challenge doc): ChallengeCubit now also watches the signed-in user's own recent matches and hides an accepted challenge once a confirmed match with the same opponent and sport, played after the challenge was accepted, appears. The match query reuses the dashboard's exact `watchUserMatches(uid, limit: 20)` so the Firestore SDK shares one server-side listener rather than paying for a second. Pending / declined / expired rows are untouched. Send-only cubits (friends list) never open the stream. Wires matchRepository through HomeScreen into the ChallengeCubit; adds cubit coverage for the played / not-played / order-independent / pre-accept-match / unconfirmed cases. * fix(map): debounce tile loads to stop the freeze on fast zoom-out A fast pinch zoom-out crosses several zoom levels in a few hundred ms. With flutter_map's default tile-update transformer, every intermediate camera frame kicks off a full tile reload, flooding the cached provider's dio + sqlite pipeline and stalling the UI on a physical device. Wire TileUpdateTransformers.debounce(100ms) on the TileLayer so only the resting zoom fetches tiles — the flutter_map-recommended pairing with a cancellable provider (CachedTileProvider is one), which also cancels the superseded intermediate requests. Adds a regression test asserting a non-default transformer is wired. * fix(challenges): open the scan-to-play screen when a challenge is accepted Accepting an incoming challenge from the dashboard only flipped its status; the pending card then left the inbox and the recipient was dropped back onto an empty dashboard with no way to reach the accepted challenge's "scan to play" screen. accept() now reports whether the write committed, and a successful accept routes the recipient to /challenge/:id — the accepted-state screen with the QR check-in CTA the design intends. Splits the section's accept/decline widget test to assert the navigation on accept and its absence on decline. * fix(challenges): reject a table whose sport differs from the challenge An accepted challenge's scan-to-play routed to the blind scanner with no knowledge of the challenge's sport, so a ping-pong challenge could be played on a foosball table. Thread the challenge's sportId through /play/scan?sport= into MatchCubit, which now rejects a facility of a different sport with a new sportMismatch error instead of starting the match. * test: cross-vertical integration pass for the 3 shared contracts Add app/test/integration/ exercising each shared contract's real writer transaction and real reader over one shared FakeFirebaseFirestore: - match confirm -> profile history (singles uid-keyed, doubles pairId-keyed) - facility status flip -> map FacilityCubit listener - queue join transaction -> match check-in derived standing Fix QueueEntryModel.fromMap coercing position via 'as int' (a Firestore double would throw and drop the ticket); use 'as num' like the sibling facility numeric fields, with a regression assertion. * docs: correct ASSISTANT_API_KEY env var and tick Phase 7 integration tasks - Rename the stale CHATBOT_API_KEY references to ASSISTANT_API_KEY (the actual --dart-define the assistant reads) in README + brain docs. - Mark the Phase 7 cross-vertical integration spine items done. * i18n: de-literalize language switcher, add locale render guard Move the EN/IT segmented-control labels out of settings_screen into languageEnglishShort/languageItalianShort ARB keys (values stay EN/IT in both locales) so no user-facing string literals remain in lib/. Add test/l10n/locale_render_test.dart: boots the app shell in each supported locale and asserts no RenderFlex overflow / FlutterError is captured -- guards against longer Italian copy overflowing tight rows, which ARB-parity checks cannot catch. * test(integration): assert the queue vertical's facility-status writes reach the map listener; tick localization spine Contract #2 (facility status flip -> map listens) previously only drove the match vertical as the status writer, and the test comment wrongly called it the 'canonical' writer. joinQueue/leaveQueue are a second writer of facilities/{id}.status. Add a test that occupies a table via a real match, then drives the real joinQueue (occupied -> queued) and leaveQueue (queued -> occupied) transactions, asserting the real FacilityCubit stream observes both. Tick the Phase-7 Localization Pass spine items: the audit found no hardcoded user-facing strings, app_en/app_it are at full 433-key parity, and locale_render_test boots both locales without overflow. * test(match): add fake_cloud_firestore integration coverage for the full scan-to-ELO flow Drives createMatch → joinMatch → submitResult → confirmResult as one continuous sequence for singles and doubles, asserting the shared contracts (match doc shape, facility status flip, queue position) hold across the whole chain rather than at isolated states. * docs(design): catalog screen mockups from the CampusArena design system Adds §5.4 to the design document, referencing docs/design/CampusArena.dc.html as the mockup source (no separate Figma file) and embedding the docs/design/screenshots/ catalog grouped by the §5.2 screen inventory. * ci(coverage): exclude generated l10n and firebase_options from the metric The l10n string tables (app_localizations_en/it.dart) and firebase_options.dart are machine-generated and contribute ~557 permanently-uncovered lines, dragging the reported total well below the code that is actually tested. Strip them with lcov --remove after the coverage run so the number reflects hand-written code. * test(chatbot): cover llm_client, assistant-settings cubit, repo intents, tool_results - llm_client_test: MockClient-backed coverage of every provider transport (OpenAI/OpenRouter/Regolo/Anthropic/Gemini), HTTP error-code mapping, timeout/socket/generic failure -> LlmException, and testConnection. - assistant_settings_cubit_test: load, provider/model/key edits, testConnection success+failure, and save persistence. - chatbot_repository_test: extend to the tournaments/queue/general intents, non-empty grounding, and per-user personalization context. - tool_results_test: value-equality of the four typed tool results. * test: foosball result-entry widget and forgot-password form Closes the two spine-named gaps: the FoosballResultEntry widget (its strategy logic was tested but the stepper UI was not) and the password-reset auth form (validation, submit, success notice, and error path). * test: widget coverage for team profile, assistant settings, and small widgets - team_profile_screen: loading/error/ready states, two-member header, ELO chips (rated + placeholder), and empty vs populated match history. - assistant_settings_screen: banner, provider-change reset prompt, key visibility toggle, save, and the missing-key Test Connection path (no network). - confirm_dialog, result_message_view, typing_indicator, chatbot tournament_card: render + interaction tests. - ci: lcov 2.5 needs --ignore-errors unused,empty for the filter step. * test(match,tournaments): cover match-flow routing and organizer sheets - match_flow_page: MatchFlowView builder across idle/active/readyToConfirm (deep-link + far-blind -> setup, nearby-blind -> auto check-in) and the error listener (snackbar + scanner re-arm). Was 0% covered. - organizer_sheets: forfeit and advance bottom sheets, empty and populated paths through to slot/winner selection. * test: cover doubles active-match, result-flow terminal phases, privacy - active_match_screen: doubles roster (join slots + rostered tiles + waiting slots), resultSubmitted (submitter vs reviewer CTA), and disputed states. - result_flow_page: ResultFlowView loading/closed(3 kinds)/error branches with routing + retry. - privacy_screen: static-content render in both locales. * test: cover result summary, QR manual entry, and tournament browse/detail - result_summary_view: win/loss headers, animated ELO block, rank pill (singles only), achievement reveal, and Done -> home. - qr_scan_screen: manual fallback-code entry (submit + empty-ignore) and the busy overlay. - tournament_browser: empty state, ongoing section, and stream-error retry. - tournament_detail: stream-error retry, register-bar registered/full labels, and the organizer cancel menu + confirm dismiss. * test(result): cover the opponent-confirmation ReviewView Both variants (submitter waiting footer vs opponent Confirm/Dispute bar), the confirm action, the dispute confirmation dialog, the in-flight spinner, and the doubles team-orientation of the score card. * ci(coverage): surface an lcov report and a coverage summary Adds an inline lcov report (read-lcov-report-action) and a totals block written to the job's Summary tab, both reading the generated-file-filtered lcov.info. * polish(ui): scan-lock haptic, queue transitions, status-card theme tokens - QR capture fires HapticFeedback.selectionClick so a lock-on is felt while the phone is over the table, not just shown as a spinner. - Queue section animates height (AnimatedSize) and cross-fades the position counter as the line advances, instead of snapping. - FacilityStatusCard reuses the canonical facilityStatusColor helper (error/tertiary/primary roles) instead of raw Colors.green/red/amber. * docs(task-board): mark Phase 7B testing + responsive polish done * seed(firestore): fold real facilities into populate script with mixed statuses Facility docs are now seeded from the hand-verified production data (coordinates, QR codes, fallbackCodes, isOutdoor) instead of being treated as read-only prerequisites. Statuses are mixed for the demo: fac_fb is occupied by the active tournament's live semifinal, leo_gardella_pp_2 is queued (walk-up occupant + two waiting players), every other table free so the Assistant 'empty tables' query stays rich. queue subcollections cleared on wipe; facility docs overwritten, never deleted, to keep FK IDs stable. Marks the seeding-script and demo-dataset task-board items done. * fix: CI pipeline --------- Co-authored-by: Christian Prendin <christianprendin2002@gmail.com>
* feat: design document structure * feat: starting design document and images * fix: merged other files into the design document * docs: trim conclusions and constraints sections - Drop the course-rules bullets from the constraints section (no social network / not a business app), keeping only genuine technical constraints; rename the section to Constraints. - Remove the Course Requirement Compliance table from Conclusions. - Remove the Effort Spent section and table. - Strip development-timeline and verification-run dates so the document no longer states the build window. * docs: swap screenshots for light/dark, en/it mockups across screen catalog * docs: rewrite Chapter 1 (Introduction) tone and structure Drop exam/course-compliance framing in favor of plain product rationale, trim the Core/Sport-Specific split down to one sentence (detail already lives in Ch.3), cut the placeholder hero figure and the Reference Documents section (duplicates Ch.8 References), and reflow the Scope feature list out of a single run-on sentence. * docs: fix Chapter 2 accuracy and tone, wire in use-case diagram Notification Dispatch previously claimed tournament bracket advancement as a third client-local alert class; the app only fires an alert for queue-front and Direct Challenge, bracket progress is passive live-listener UI. Also dropped grader-facing phrasing from the Screen Inventory intro, and swapped the use-case placeholder for the exported diagram. * docs: fix Chapter 3 tone, drop unverifiable claim, wire in diagrams Dropped grader-facing phrasing from the Trust Boundary section and an unverifiable historical claim about queue ticket renumbering. Swapped the architecture and ER placeholders for the exported diagrams. * docs: fix Chapter 3 BracketEngine naming, wire in match-state diagram BracketEngine was referenced as a class alongside real singleton classes (EloCalculator, AchievementEvaluator), but bracket_engine.dart is a top-level function/data-class module with no such class. Corrected the two Ch.3 mentions and wired in the match-state-machine diagram, the third figure in this chapter that was still a placeholder. * docs: fix Chapter 2 screen count and BracketEngine naming Same audit that flagged Ch.3's BracketEngine issue found it leaking into Ch.2's traceability table, and found "Profile Setup" still counted in the Screen Inventory — that screen was cut in Phase 2B (task_board.md) and never existed as a route or file. Corrected the count to 29 and renumbered the table. * docs: adjust match-state diagram routing * docs: rewrite Chapter 4 (Implementation Details) tone and wire in diagrams Fixed a stale /onboarding/profile-setup route reference (now /onboarding/tutorial), dropped two grader-facing tone violations, reworded a TA-familiarity line and an overclaimed disclaimer-reuse sentence. Authored confirm_txn and queue_sequence as hand-written .drawio sources and wired both figures in. * docs: rewrite Chapter 5 (UI/UX) screen catalog and wire in navigation diagram Fix standings/settings mockup mix-up in the screen catalog (standings_en_dark is actually Tournament Detail, settings_en_dark is actually Settings), drop the Profile setup mockup for a screen that no longer exists in the app, and author the navigation map as a drawio diagram in place of the placeholder. * docs: fix Chapter 6 test counts, inventory table, and BracketEngine naming * docs: fix Chapter 7 Future Work Basketball squad-size claim * feat: added app icon * docs: finalize design document, add user manual, presentation outline, and README deliverables - Finalize main.tex: page-breakable tech-stack and traceability tables (xltabular), wrap long snake_case identifiers, correct title/release date, wire diagram figure paths, foosball indoor/outdoor parity. - Add compiled design_document.pdf and exported diagram PNGs (architecture, ER, use-case, match-state, queue-sequence, confirm-txn, navigation). - Add user_manual.md and presentation_outline.md. - README: point /docs at the final deliverables (design_document.pdf, presentation.pdf, user_manual.md, design system). - task_board: mark Spine design-doc/demo/README items complete. * docs: fix user manual profile-setup screen and presentation stale test/screen counts Address Christian's PR #32 review: - user_manual §1.3: drop the non-existent Profile Setup screen (no avatar picker exists; permissions are requested at point-of-use), fold the tutorial into a single onboarding step - presentation_outline: correct test-case count 739 -> 864 (slides 10, 11) and screen count 30 -> 29 (slide 11) to match the audited main.tex figures --------- Co-authored-by: Christian Prendin <christianprendin2002@gmail.com>
* fix: changed app icon * feat: added final presentation
ChristianPrendin
approved these changes
Jul 13, 2026
ChristianPrendin
left a comment
Collaborator
There was a problem hiding this comment.
We did it lesssggoooo
LCOV of commit
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Promotes
developtomainfor the 1.0 release — the final phase gate.Included since last
main(phase-6)brain/project_description.md§3.10).docs/presentation.pdf).Shared Contracts / schema
phase-6.