Custom fields management and analysis - #130
Open
MangoSwirl wants to merge 15 commits into
Open
Conversation
Scouting leads can configure custom post-match questions, and their answers appear throughout analysis clearly marked as custom. - New "Custom Fields" page off the nav drawer (leads only): list, create/edit with an options editor, reorder, archive/unarchive; type is immutable after creation - LovatAPI custom_fields extension + in-memory definitions cache - Raw report gains an "Asked by your team" section - Team Lookup: number answers as an "Asked by your team" category with per-match detail graphs; select answers as breakdown distributions - Picklists: number fields become weights, resilient to archived/unknown fields; custom weights sent via a single customWeights param and stored on shared picklists - Shared "Custom" chip on picklist/breakdown surfaces; grouping headers mark the rest Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MangoSwirl
force-pushed
the
custom-fields
branch
from
August 2, 2026 04:48
428eacc to
928260b
Compare
This was referenced Aug 2, 2026
26.0.4 is a closed App Store train, so ship the custom fields build under 26.0.6 (aligned with Lovat Collection). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Tap a field row to edit it, and move Archive/Unarchive into the field's edit page instead of a per-row overflow menu (removed the menus on both the active and archived lists). - Gray out the InsetPicker when it's disabled, so the immutable type selector reads as locked when editing. - Lock existing select options when editing: their text fields and remove buttons are disabled (options are append-only), while new options stay editable. - Surface reorder failures — revert the list and show the server's error message (with a mounted guard) so a rejected reorder can't look like a success. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Link the empty state to the learn.lovat.app custom fields guide. Co-Authored-By: Claude <noreply@anthropic.com>
Each report's note card now shows that report's Text custom answers below a divider, labeled with the question plus the Custom marker. A card also appears for reports that have only a text answer. Co-Authored-By: Claude <noreply@anthropic.com>
MangoSwirl
force-pushed
the
custom-fields
branch
from
August 3, 2026 22:58
27bb7e0 to
a0d1cc7
Compare
Notes tab: merge each report's text custom answers into its note card, redesign the cards (concise match name, neutral 'plain' surface, clear hierarchy, subtle Custom marker), anonymize other teams' attribution as 'Scouter from <team>', and make each card open the report's raw data page with a chevron affordance. Raw report: show custom answers to any viewer (not just the source team), group them under a team-aware heading, and let scouting leads of the report's team edit text answers inline.
MangoSwirl
force-pushed
the
custom-fields
branch
from
August 4, 2026 00:40
a0d1cc7 to
4313a4d
Compare
AllianceAnalysis.fromJson hard-cast teams, the L1/L2/L3 start-time lists, and the fuel/ball totals, but the server returns null for these when an alliance has too little data (production guarded every one of them at the use site). Parse defensively (empty lists, nullable totals) and tolerate a String team number, so one weak alliance no longer throws and blanks the whole page.
…urnament Both queries resolve the current tournament inside queryFn but omitted it from their StaleRefreshBuilder queryKey, so the in-memory QueryCache (keyed only on queryKey) served the previous tournament's data after switching tournaments (and kept it if the background refetch failed). Add the tournament key to both keys, matching scouterScheduleQuery which already does this.
_keyEquals used element-wise != which compares nested List/Map key elements by identity. Keys with nested collections (e.g. picklistAnalysis' flags/weights, rebuilt fresh each build) therefore always registered as changed, causing refetch churn on every parent rebuild and — via the _activeKey guard in the success path — occasionally discarding completed results (stuck on skeleton). Compare using QueryCache's own jsonEncode serialization so equality matches the actual cache lookup.
CustomTextAnswer.fromJson hard-cast name/value as String, so a single answer with a null or blank value (e.g. a text field answered then cleared) threw and propagated out of the notes query, replacing the entire Notes tab with the error view. Parse defensively (skip null/non-string/whitespace-only), matching the raw-report surface which already hides blank text answers.
load() reset loaded/loadingTeam but not data, so on a team switch the previous team's value persisted. If the new team's flag fetch then failed with no cache, the catch's `data == null` guard was false — suppressing the error snackbar (a regression from production) and leaving the flag stuck on the skeleton with no feedback. Clear data at the start of each load.
…hint Opened from the Notes tab, the page received canModify: note.author != null, a heuristic that's true for any own-team member. Delete and Edit-notes were gated on that hint while the custom-answer editor already used the authoritative reportAnalysis.canModify, so a non-lead own-team member saw Delete/Edit buttons the server then rejected, and the three affordances could disagree. Gate all of them on reportAnalysis.canModify (verified identical rule to the list endpoint: SCOUTING_LEAD and viewer team == report source team).
When customFieldsAreOwnTeam is false but the server omits customFieldsSourceTeam (parsed to null), the raw report's custom-fields section rendered the literal "Asked by null". Fall back to "Asked by another team" in that case.
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.
Adds custom fields to the dashboard: scouting leads configure custom post-match questions, and their answers appear throughout analysis clearly marked as custom.
What's here
custom_fields.dartAPI extension + in-memory definitions cache.customWeightsparam and persisted on shared picklists.CustomFieldIndicator— a shared "Custom" chip on picklist/breakdown surfaces; grouping headers mark the rest. Never two markers on one surface.Every custom section is hidden when empty (no blank scaffolding).
Testing
Validated live on an iOS simulator against a local server: created a field through the real UI, and confirmed the category average, detail sparkline, and breakdown distributions all render with the correct seeded values, plus cross-team isolation (a 9470 viewer sees none of 8033's custom data).
flutter analyzeclean (no new issues).Related PRs — this feature spans four repos: