feat(raids): redesign level selector with named tiers + custom palette (#259)#280
Open
hokiepokedad2 wants to merge 5 commits into
Open
feat(raids): redesign level selector with named tiers + custom palette (#259)#280hokiepokedad2 wants to merge 5 commits into
hokiepokedad2 wants to merge 5 commits into
Conversation
#259) The raid/egg add dialog had three level-pickers (raid checkboxes, egg checkboxes, boss-level dropdown) all driven by a hardcoded `levels = [1, 2, 3, 4, 5, 6]` array, even though PoracleNG accepts any positive integer. Users with Elite Raids (level 7) or custom server schemes had to configure those via the bot's `!command` interface; the UI silently locked them out. Replace all three sites with a new `<app-level-selector>` shared component — a Material 3 chip listbox in three sections: - STANDARD: T1-T5 - SPECIAL: Mega (6), Elite (7), plus "Any" (9000) when showAny=true - CUSTOM: any user-added integer, persisted per-user in localStorage Power users add a custom level via an inline "+ Add level" affordance that transforms into a numeric input. The chip then persists across dialog opens (one-click selection on subsequent alarms) and seeds itself from saved alarm data on open, so editing an existing level-42 alarm renders the chip pre-selected rather than orphaned. Single source of truth for label resolution lives in `core/models/raid-level.models.ts`. `resolveLevel(value)` maps any integer to the right LevelOption — adopted by raid-list cards too, so the dialog and the cards now speak the same vocabulary ("Elite", not "Level 7" vs "7" on different surfaces). Edge cases: - 0 / negatives / non-integers in custom input -> inline validation error - 9000 in custom input -> snaps to the "Any" chip (no duplicate) - duplicate of built-in -> flashes existing chip + selects, no new entry - 20-entry LRU cap on the localStorage palette i18n: new `RAIDS.LEVEL.*` keys added in all 11 locales with English fallbacks for non-en (translation volunteers can localize later, per discussion #211). Bonus correctness: the boss tab used to default level=0 ("any") but PoracleNG's canonical wildcard sentinel is 9000. New alarms now use 9000; old alarms with 0 continue to work and edit fine. 37 new unit tests across the model, store, pipe, and component. Closes #259, reported by @prof-miles0.
|
Sorry for another comment here… I have edited my previous comment adding info from where you can get raid level info. You can find it in the gamemaster. Currently raid levels range from 1 to 19. You can find the gamemaster here: https://github.com/WatWowMap/Masterfile-Generator/blob/8db1fd5c1a9401db59f4ce053be76f04e0e88812/master-latest-poracle-v2.json Search for raid_{level}, you can also see game tags/names there. |
…nblock save Follow-up on d58752e (the initial #259 redesign), addressing visual, correctness, and backend-validation issues found during testing. UI / UX - Collapse the three-section (Standard/Special/Custom) chip layout into one wrapping row per picker. Categories are encoded in chip content (T1 / "Mega · 6" / "42 ⊗") rather than container labels; cuts dialog height roughly in half. - Replace the heavy mat-form-field "+ Add custom level" with a chip-sized inline numeric input. Enter commits, Esc cancels, blur commits. Help text only renders when the input is open or there's a validation error. - Override Material 3 selected-chip font-weight so the selected state actually pops; bind `hideSingleSelectionIndicator` to `!multiple` so multi-select chips get a leading checkmark. - Cap card star icons to levels 1-7 (was 1-100) — alarms at level 23 no longer render 23 stars in the card. Per-type palette - Adding a custom level on the raid picker was leaking it into the egg and boss pickers. `CustomLevelStore` now keys palettes by `paletteKey` ("raid" / "egg" / "boss"), each persisted to its own localStorage slot. Required `paletteKey` input on `<app-level-selector>`. Any chip surfaced where PoracleNG actually honors it - Raid + boss pickers show the `Any` chip (PoracleNG treats level=9000 as the wildcard sentinel — see trackingRaid.go). - Egg picker deliberately omits Any: PoracleNG's trackingEgg.go only validates level >= 1 with no wildcard semantic, so an "Any egg" alarm at 9000 would simply never fire. Server-side fix that was blocking custom-level alarms - `[Range(0, 10)]` on `RaidCreate.Level`, `RaidUpdate.Level`, `EggCreate.Level`, `EggUpdate.Level` was rejecting custom integers (8+) and the new Any=9000 sentinel with 400 Bad Request before they could reach PoracleNG. Relaxed to `[Range(0, int.MaxValue)]` matching PoracleNG's actual range. Label vocabulary consistency - Edit dialog (raid/egg) now uses the same `resolveLevel` resolver as the cards via the new `LevelLabelPipe` — an alarm at level 7 reads "Elite" on the card AND in the edit dialog (was "Level 7" in the dialog before). Egg image alt-text in the card list now uses the pipe too. Error UX - Removing a custom chip (`⊗`) opens a 3-second snackbar with Undo — accidental click is recoverable; intentional removal still wipes the palette entry. State-machine clarity - `addInputOpen: signal(boolean)` replaced with an explicit `addMode: signal<'closed' | 'open'>` and named `isAddClosed()` / `isAddOpen()` getters in the template — removes the `!` negation pattern that prior renders sometimes appeared to misread. Tests - Updated `custom-level-store.service.spec.ts` for the keyed API. - Updated `level-selector.component.spec.ts` to set `paletteKey` per test and assert per-key isolation. - 697/697 frontend tests pass, 1063/1063 backend tests pass. i18n - Added `RAIDS.LEVEL.REMOVED` and `COMMON.UNDO` keys in all 11 locales (English placeholder text for non-en — translation volunteers per discussion #211).
…levels) Builds on the v2 review-pass (2c2f0aa). Follow-up driven by the issue reporter pointing to the canonical Pokémon GO raid level vocabulary in the WatWowMap masterfile — there are 19 named raid types (1-Star through Coordinated 2), not 7, and the prior UI labeled level 7 as "Elite" when the masterfile says it's "Mega Legendary". Backend - `GET /api/masterdata/raid-levels` returns the canonical list with per-level integer, category, and singular/plural English names. - New `IRaidLevelService` / `RaidLevelService` returns a baked-in snapshot of the masterfile. A TODO documents how to swap the implementation for a live fetch from raw.githubusercontent.com/WatWowMap/Masterfile-Generator without changing the wire contract. - 6 new unit tests cover the service + controller endpoint. Frontend - `RaidLevelService` (Angular) calls the new API on first dialog use, caches the result in a signal. Falls back to `KNOWN_LEVELS` baked-in constants when the network fails or before resolve. - `raid-level.models.ts` rewritten around 19 canonical levels keyed by `RAIDS.LEVEL.RAID_1` through `RAID_19` (plus `_PLURAL` variants). Removed the bogus `T1`-`T5` / `MEGA` / `ELITE` keys. - `LevelSelectorComponent` simplified to a `pickerType` input (`'raid' | 'egg' | 'boss'`). Inputs: • raid → primary chips 1-7, overflow menu for 8-19, Any chip, +Add • egg → star tiers (1-5) only, +Add (no overflow, no Any) • boss → single-select with the same primary + overflow as raid "More raid types…" overlay menu (mat-menu) surfaces the 12 less common levels without crowding the chip row. - i18n: 19 singular + 19 plural keys in all 11 locales, with English placeholders for the 10 non-en locales (volunteers per #211). - Card star icons now render only for the literal 1-5 "N Star Raid" tier (was 1-7, producing ~23 stars for custom-level alarms). - Label vocabulary consistency: alarm at level 7 now reads "Mega Legendary Raid" on the card and in the edit dialog (was "Elite" on the card, "Level 7" in the edit dialog). Forward compatibility - Any positive integer remains addable via the `+ Add` chip. When the WatWowMap masterfile adds raid_20+ in the future, the backend service can pick it up automatically (once the live-fetch path is wired); existing custom alarms at that level continue to work. Tests: 711/711 frontend, 1069/1069 backend. Lint + prettier clean.
Follow-up on 12be778 (v3 masterfile alignment). User feedback + internal PR review revealed several issues; this commit addresses them. User feedback - Custom levels typed via `+ Add` were persisting across modal close AND page refresh because the per-type palette was backed by localStorage. Deleted CustomLevelStore entirely; LevelSelector now tracks the palette in a local `customPalette` signal that lives for the component lifetime only. Refresh or close-and-reopen wipes typed-but-not-saved chips. Existing alarms still seed the palette through the `[value]` input. - Chip labels were too long ("Mega Legendary Raid"), and the same string caused "All Mega Legendary Raid Raids" double-Raid in card titles. Dropped the "Raid" suffix from the 19 RAID_N keys in all 11 locales — chips now read "Mega Legendary", "Legendary", "1 Star", etc. The card-title template (`RAIDS.ALL_LEVEL_RAIDS = "All {{level}} Raids"`) supplies the noun once; result reads natural English. Also dropped the unused `pluralKey` from `LevelOption` and the `_PLURAL` i18n keys (the shortened `RAID_N` strings work in both card and chip contexts). Backend `RaidLevelInfo.Name` is now the modifier form ("Mega Legendary") while `NamePlural` retains the full "Mega Legendary Raids" for any future standalone use. Review fixes (MUST FIX) - Snackbar undo subscription in `LevelSelector.removeCustom` now pipes through `takeUntilDestroyed(this.destroyRef)` so closing the dialog mid-toast can't fire the callback against a destroyed component. - `raid-list.getRaidLevelName` was bypassing the live `RaidLevelService.byValue()` and using the baked-in `KNOWN_LEVELS` constant — cards would drift from the dialog if the API ever extended the canonical list. Cards now consult the service first, fall back to the baked-in resolver. `raid-list.ngOnInit` primes the cache so the list page doesn't depend on a dialog open. - `LevelLabelPipe` now detects ngx-translate's "key not found" pass-through (translated string === key) and falls back to "Level {n}" instead of leaking "RAIDS.LEVEL.RAID_20" into the UI. Graceful degradation for future masterfile additions before locales catch up. Review fixes (SHOULD FIX) - `raid-edit-dialog.formatLevel` deleted — the dialog now injects `LevelLabelPipe` and calls `.transform()`, eliminating the duplicate label-resolution logic. Single source of truth. Analyzers - xUnit2032 in `MasterDataControllerRaidLevelsTests`: switched `Assert.IsAssignableFrom<T>` to `Assert.IsType<T>(..., exactMatch: false)`. - CA1707 in `RaidLevelServiceTests` and the new controller test: test method names renamed to PascalCase to match the project's preferred style and silence the analyzer. Dev workflow - New `proxy.conf.json` forwards `/api/*` and `/auth/*` from the Angular dev server to the API. `environment.development.ts` apiUrl set to `''` so all HTTP calls are same-origin from the browser's view — works identically for `ng serve` + proxy and for the production single-port deployment. OAuth flows survive the proxy because Host is preserved. Tests: 700/700 frontend (+1 fallback test), 1069/1069 backend. Lint + prettier + dotnet format (scoped) all clean.
Contributor
Author
|
@prof-miles0 How does this look?
|
- features/alarms.md: replace the generic "tier" wording on Raids/Eggs rows with pointers to a new "Raid level selector" subsection that documents the chip layout, the 19 masterfile-defined raid types, primary vs overflow split per pickerType, the ephemeral custom-add affordance, and the wildcard sentinel. - architecture/backend.md: add a "Raid level service" section documenting IRaidLevelService, GET /api/masterdata/raid-levels, the baked-in fallback + live-fetch upgrade path, and the [Range] relax that lets PoracleNG-accepted custom integers pass validation. Also register the singleton in the service lifetimes table. - architecture/frontend.md: document LevelSelectorComponent + the Angular RaidLevelService consumer (signal cache, baked-in fallback, LevelLabelPipe missing-key fallback), and the ephemeral palette behavior. - getting-started/development-setup.md: the "proxies API requests" claim is now accurate thanks to the committed proxy.conf.json — describe it explicitly (changeOrigin: false to preserve Host for OAuth callbacks), note the empty apiUrl + same-origin dev flow, and document the --port override for matching a non-default Discord OAuth redirect URI.
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.




Summary
Closes #259 (reported by @prof-miles0). Supersedes the closed
[1..10]hardcode PR #278.The raid/egg add dialog had three level-pickers (raid checkboxes, egg checkboxes, boss-level dropdown) all driven by
levels = [1, 2, 3, 4, 5, 6], even though PoracleNG accepts any positive integer. Users with Elite Raids (level 7) or custom server schemes had to configure those via the bot's!commandinterface; the UI silently locked them out.Design
<app-level-selector>— a new shared Material 3 chip listbox in three sections:+ Add levelaffordance that transforms into a numeric input (Enter to commit, Esc to cancel).localStorage('poracle.custom-raid-levels')(LRU cap 20) so "I always alarm on level 8" is a one-time setup.level: 42) seed the palette on dialog open and pre-select their chip.resolveLevel(value)incore/models/raid-level.models.tsis the mapping from stored integer → display option. Adopted by raid alarm cards too, so dialog and cards now agree (an alarm at level 7 says "Elite" on both surfaces).Edge cases
0/ negatives / non-integers in custom input → inline validation error9000typed in → snaps to the canonical "Any" chip, never creates a duplicatei18n
New
RAIDS.LEVEL.*keys added in all 11 locales with English fallbacks for non-en. Translation volunteers can localize the placeholder English text in a follow-up (see discussion #211).Subtle correctness fix
The boss tab used to default the level filter to
0("any") and pass that to PoracleNG, but the canonical "any" sentinel is9000(the same one used everywhere else in the codebase including the alarm cards). New alarms now use 9000. Existing alarms saved withlevel: 0continue to render and edit fine; they just keep using 0.Tests
37 new unit tests across:
raid-level.models.spec.ts— resolveLevel + isBuiltInLevel coveragecustom-level-store.service.spec.ts— localStorage, LRU eviction, malformed JSON resilience, seedFrom semanticslevel-label.pipe.spec.ts— display rendering for each categorylevel-selector.component.spec.ts— toggle, single-vs-multi, add/remove, snap-to-Any, dupe handling, EscapeTest plan
+ Add level→ chip appears, selected, persists on next dialog open