From d69a96ee912991ab8730dbe66bf4b1fd0016ca1e Mon Sep 17 00:00:00 2001 From: hokiepokedad2 <38219945+hokiepokedad2@users.noreply.github.com> Date: Wed, 19 Aug 2026 02:09:00 -0400 Subject: [PATCH] feat(where): the remaining nine alarm types, and translations Cards - Raids, eggs, quests and max battles share app-alarm-info, so that component now renders the Where chip instead of its own distance display. One change, four types, and no chance of the four drifting apart. - Gyms, lures, nests, invasions and fort changes each carried their own distance-chip markup; all five now use the chip. - Every list loads the profile's selected areas, used only to keep the inherited wording honest. - Clicking a chip opens the scope sheet, on all ten types. Raids and eggs share a list, so the tracking type is a parameter rather than two methods. Edit dialogs - The areas-or-distance control cannot express an area-confined alarm, and switching it to a distance would have sent both and been refused by PoracleNG. It is hidden for those alarms, and every dialog shows the scope read-only above it. Translations - All 37 WHERE strings plus the Places menu entry into de, es, fr, it, nl, pl, pt, pt-BR, sv and da. The applier asserts each locale carries exactly the English key set and that every {{placeholder}} survives, since a dropped one renders as literal braces to the user. RADIUS_KM matching English in Danish and German, and PLACE_NAME in German, are cognates. Verified with npm run build, not just tsc and jest -- template errors only surface in the production build, which is how NG8004 reached CI last time. --- .../fort-change-edit-dialog.component.html | 73 +++++++++++------- .../fort-change-edit-dialog.component.scss | 13 ++++ .../fort-change-edit-dialog.component.ts | 7 ++ .../fort-change-list.component.html | 11 +-- .../fort-change-list.component.ts | 38 ++++++++++ .../gyms/gym-edit-dialog.component.html | 73 +++++++++++------- .../gyms/gym-edit-dialog.component.scss | 13 ++++ .../modules/gyms/gym-edit-dialog.component.ts | 7 ++ .../app/modules/gyms/gym-list.component.html | 11 +-- .../app/modules/gyms/gym-list.component.ts | 38 ++++++++++ .../invasion-edit-dialog.component.html | 73 +++++++++++------- .../invasion-edit-dialog.component.scss | 13 ++++ .../invasion-edit-dialog.component.ts | 7 ++ .../invasions/invasion-list.component.html | 11 +-- .../invasions/invasion-list.component.ts | 38 ++++++++++ .../lures/lure-edit-dialog.component.html | 73 +++++++++++------- .../lures/lure-edit-dialog.component.scss | 13 ++++ .../lures/lure-edit-dialog.component.ts | 7 ++ .../modules/lures/lure-list.component.html | 11 +-- .../app/modules/lures/lure-list.component.ts | 38 ++++++++++ .../max-battle-edit-dialog.component.html | 73 +++++++++++------- .../max-battle-edit-dialog.component.scss | 13 ++++ .../max-battle-edit-dialog.component.ts | 7 ++ .../max-battle-list.component.html | 10 ++- .../max-battles/max-battle-list.component.ts | 38 +++++++++- .../nests/nest-edit-dialog.component.html | 73 +++++++++++------- .../nests/nest-edit-dialog.component.scss | 13 ++++ .../nests/nest-edit-dialog.component.ts | 7 ++ .../modules/nests/nest-list.component.html | 11 +-- .../app/modules/nests/nest-list.component.ts | 38 ++++++++++ .../quests/quest-edit-dialog.component.html | 73 +++++++++++------- .../quests/quest-edit-dialog.component.scss | 13 ++++ .../quests/quest-edit-dialog.component.ts | 7 ++ .../modules/quests/quest-list.component.html | 10 ++- .../modules/quests/quest-list.component.ts | 38 +++++++++- .../raids/raid-edit-dialog.component.html | 73 +++++++++++------- .../raids/raid-edit-dialog.component.scss | 13 ++++ .../raids/raid-edit-dialog.component.ts | 7 ++ .../modules/raids/raid-list.component.html | 20 ++++- .../app/modules/raids/raid-list.component.ts | 44 ++++++++++- .../alarm-info/alarm-info.component.html | 10 ++- .../alarm-info/alarm-info.component.ts | 20 ++++- .../ClientApp/src/assets/i18n/da.json | 74 +++++++++--------- .../ClientApp/src/assets/i18n/de.json | 72 +++++++++--------- .../ClientApp/src/assets/i18n/es.json | 76 +++++++++---------- .../ClientApp/src/assets/i18n/fr.json | 76 +++++++++---------- .../ClientApp/src/assets/i18n/it.json | 76 +++++++++---------- .../ClientApp/src/assets/i18n/nl.json | 76 +++++++++---------- .../ClientApp/src/assets/i18n/pl.json | 76 +++++++++---------- .../ClientApp/src/assets/i18n/pt-BR.json | 76 +++++++++---------- .../ClientApp/src/assets/i18n/pt.json | 76 +++++++++---------- .../ClientApp/src/assets/i18n/sv.json | 76 +++++++++---------- CHANGELOG.md | 2 + 53 files changed, 1286 insertions(+), 649 deletions(-) diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/fort-changes/fort-change-edit-dialog.component.html b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/fort-changes/fort-change-edit-dialog.component.html index 9ec0564d..f953a52a 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/fort-changes/fort-change-edit-dialog.component.html +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/fort-changes/fort-change-edit-dialog.component.html @@ -40,39 +40,54 @@

Change Types

{{ 'ALARM.LOCATION_MODE' | translate }}

- - -
- map -
- {{ 'ALARM.USE_AREAS' | translate }} -

{{ 'ALARM.USE_AREAS_HINT' | translate }}

+ +

+ + {{ 'WHERE.EDIT_FROM_CARD' | translate }} +

+ + @if (isAreaScoped()) { + + } @else { + + +
+ map +
+ {{ 'ALARM.USE_AREAS' | translate }} +

{{ 'ALARM.USE_AREAS_HINT' | translate }}

+
-
- - -
- straighten -
- {{ 'ALARM.SET_DISTANCE' | translate }} -

{{ 'ALARM.SET_DISTANCE_HINT' | translate }}

+ + +
+ straighten +
+ {{ 'ALARM.SET_DISTANCE' | translate }} +

{{ 'ALARM.SET_DISTANCE_HINT' | translate }}

+
-
- - + + - @if (form.controls.distanceMode.value === 'distance') { - - {{ 'ALARM.DISTANCE_LABEL' | translate }} - - {{ 'ALARM.DISTANCE_SUFFIX' | translate }} - - } + @if (form.controls.distanceMode.value === 'distance') { + + {{ 'ALARM.DISTANCE_LABEL' | translate }} + + {{ 'ALARM.DISTANCE_SUFFIX' | translate }} + + } - - + + + }

{{ 'ALARM.MESSAGE_SETTINGS' | translate }}

diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/fort-changes/fort-change-edit-dialog.component.scss b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/fort-changes/fort-change-edit-dialog.component.scss index 3dacfdc6..8331fcd6 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/fort-changes/fort-change-edit-dialog.component.scss +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/fort-changes/fort-change-edit-dialog.component.scss @@ -67,3 +67,16 @@ mat-slide-toggle { min-width: 0; } } + +.scope-current { + align-items: center; + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin: 0 0 1rem; +} + +.scope-hint { + color: var(--mat-sys-on-surface-variant, rgb(0 0 0 / 60%)); + font-size: 0.75rem; +} diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/fort-changes/fort-change-edit-dialog.component.ts b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/fort-changes/fort-change-edit-dialog.component.ts index 4004699e..44505bf0 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/fort-changes/fort-change-edit-dialog.component.ts +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/fort-changes/fort-change-edit-dialog.component.ts @@ -19,6 +19,7 @@ import { FortChangeService } from '../../core/services/fort-change.service'; import { I18nService } from '../../core/services/i18n.service'; import { DeliveryPreviewComponent } from '../../shared/components/delivery-preview/delivery-preview.component'; import { TemplateSelectorComponent } from '../../shared/components/template-selector/template-selector.component'; +import { WhereChipComponent } from '../../shared/components/where-chip/where-chip.component'; @Component({ imports: [ @@ -37,6 +38,7 @@ import { TemplateSelectorComponent } from '../../shared/components/template-sele TranslatePipe, TemplateSelectorComponent, DeliveryPreviewComponent, + WhereChipComponent, ], selector: 'app-fort-change-edit-dialog', standalone: true, @@ -67,6 +69,11 @@ export class FortChangeEditDialogComponent { saving = signal(false); + /** True when the alarm is confined to areas, which the areas-or-distance control cannot express. */ + isAreaScoped(): boolean { + return (this.data.overrideAreas?.length ?? 0) > 0; + } + onDistanceModeChange(): void { if (this.form.controls.distanceMode.value === 'areas') this.form.controls.distanceKm.setValue(0); else if (!this.form.controls.distanceKm.value) this.form.controls.distanceKm.setValue(1); diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/fort-changes/fort-change-list.component.html b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/fort-changes/fort-change-list.component.html index 03f4e855..580398e6 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/fort-changes/fort-change-list.component.html +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/fort-changes/fort-change-list.component.html @@ -80,11 +80,12 @@

{{ formatFortType(item.fortType) }}

- @if (item.distance === 0) { - map {{ 'ALARM.USING_AREAS' | translate }} - } @else { - straighten {{ formatDistance(item.distance) }} - } +
diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/fort-changes/fort-change-list.component.ts b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/fort-changes/fort-change-list.component.ts index 3b5e630f..5f21dfb5 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/fort-changes/fort-change-list.component.ts +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/fort-changes/fort-change-list.component.ts @@ -15,10 +15,14 @@ import { firstValueFrom } from 'rxjs'; import { FortChangeAddDialogComponent } from './fort-change-add-dialog.component'; import { FortChangeEditDialogComponent } from './fort-change-edit-dialog.component'; import { FortChange } from '../../core/models'; +import { AreaService } from '../../core/services/area.service'; import { FortChangeService } from '../../core/services/fort-change.service'; import { I18nService } from '../../core/services/i18n.service'; import { ConfirmDialogComponent, ConfirmDialogData } from '../../shared/components/confirm-dialog/confirm-dialog.component'; import { DistanceDialogComponent } from '../../shared/components/distance-dialog/distance-dialog.component'; +import { WhereChipComponent } from '../../shared/components/where-chip/where-chip.component'; +import { WhereSheetComponent, WhereSheetData } from '../../shared/components/where-sheet/where-sheet.component'; +import { AlarmScope, scopeOf, scopeToFields } from '../../shared/utils/alarm-scope'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -33,6 +37,7 @@ import { DistanceDialogComponent } from '../../shared/components/distance-dialog MatSnackBarModule, MatProgressSpinnerModule, TranslatePipe, + WhereChipComponent, ], selector: 'app-fort-change-list', standalone: true, @@ -40,6 +45,7 @@ import { DistanceDialogComponent } from '../../shared/components/distance-dialog templateUrl: './fort-change-list.component.html', }) export class FortChangeListComponent implements OnInit { + private readonly areaService = inject(AreaService); private readonly destroyRef = inject(DestroyRef); private readonly dialog = inject(MatDialog); private readonly fortChangeService = inject(FortChangeService); @@ -47,7 +53,11 @@ export class FortChangeListComponent implements OnInit { private readonly snackBar = inject(MatSnackBar); readonly fortChanges = signal([]); readonly loading = signal(true); + /** Only used to word the inherited scope honestly; empty produces the more cautious wording. */ + readonly profileAreas = signal([]); + readonly selectedIds = signal(new Set()); + readonly selectMode = signal(false); async bulkDelete(): Promise { @@ -170,6 +180,29 @@ export class FortChangeListComponent implements OnInit { }); } + /** Change one alarm's delivery scope from its card, without opening the whole edit dialog. */ + editScope(item: FortChange): void { + const data: WhereSheetData = { + profileAreas: this.profileAreas(), + scope: scopeOf(item.overrideLocationLabel, item.overrideAreas, item.distance), + }; + + this.dialog + .open(WhereSheetComponent, { width: '520px', autoFocus: false, data }) + .afterClosed() + .subscribe((scope?: AlarmScope) => { + if (!scope) return; + + this.fortChangeService.update(item.uid, scopeToFields(scope)).subscribe({ + error: () => this.snackBar.open(this.i18n.instant('WHERE.SCOPE_SAVE_ERROR'), this.i18n.instant('COMMON.OK'), { duration: 4000 }), + next: () => { + this.snackBar.open(this.i18n.instant('WHERE.SCOPE_SAVED'), this.i18n.instant('COMMON.OK'), { duration: 2500 }); + this.loadItems(); + }, + }); + }); + } + formatChangeTypes(types: string[]): string { if (!types || types.length === 0) return this.i18n.instant('FORT_CHANGES.ALL_CHANGES'); return types @@ -222,6 +255,7 @@ export class FortChangeListComponent implements OnInit { } ngOnInit(): void { + this.loadProfileAreas(); this.loadItems(); } @@ -265,4 +299,8 @@ export class FortChangeListComponent implements OnInit { } }); } + + private loadProfileAreas(): void { + this.areaService.getSelected().subscribe({ error: () => undefined, next: areas => this.profileAreas.set(areas) }); + } } diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/gyms/gym-edit-dialog.component.html b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/gyms/gym-edit-dialog.component.html index 61c04cbd..cced0bba 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/gyms/gym-edit-dialog.component.html +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/gyms/gym-edit-dialog.component.html @@ -32,39 +32,54 @@

{{ 'RAIDS.SPECIFIC_GYM' | translate }}

{{ 'ALARM.LOCATION_MODE' | translate }}

- - -
- map -
- {{ 'ALARM.USE_AREAS' | translate }} -

{{ 'ALARM.USE_AREAS_HINT' | translate }}

+ +

+ + {{ 'WHERE.EDIT_FROM_CARD' | translate }} +

+ + @if (isAreaScoped()) { + + } @else { + + +
+ map +
+ {{ 'ALARM.USE_AREAS' | translate }} +

{{ 'ALARM.USE_AREAS_HINT' | translate }}

+
-
- - -
- straighten -
- {{ 'ALARM.SET_DISTANCE' | translate }} -

{{ 'ALARM.SET_DISTANCE_HINT' | translate }}

+ + +
+ straighten +
+ {{ 'ALARM.SET_DISTANCE' | translate }} +

{{ 'ALARM.SET_DISTANCE_HINT' | translate }}

+
-
- - + + - @if (form.controls.distanceMode.value === 'distance') { - - {{ 'ALARM.DISTANCE_LABEL' | translate }} - - {{ 'ALARM.DISTANCE_SUFFIX' | translate }} - - } + @if (form.controls.distanceMode.value === 'distance') { + + {{ 'ALARM.DISTANCE_LABEL' | translate }} + + {{ 'ALARM.DISTANCE_SUFFIX' | translate }} + + } - - + + + }

{{ 'ALARM.MESSAGE_SETTINGS' | translate }}

diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/gyms/gym-edit-dialog.component.scss b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/gyms/gym-edit-dialog.component.scss index 759c39c7..7f9c5f86 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/gyms/gym-edit-dialog.component.scss +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/gyms/gym-edit-dialog.component.scss @@ -78,3 +78,16 @@ mat-slide-toggle { min-width: 0; } } + +.scope-current { + align-items: center; + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin: 0 0 1rem; +} + +.scope-hint { + color: var(--mat-sys-on-surface-variant, rgb(0 0 0 / 60%)); + font-size: 0.75rem; +} diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/gyms/gym-edit-dialog.component.ts b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/gyms/gym-edit-dialog.component.ts index 8bcbc1e4..d3d65e88 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/gyms/gym-edit-dialog.component.ts +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/gyms/gym-edit-dialog.component.ts @@ -18,6 +18,7 @@ import { I18nService } from '../../core/services/i18n.service'; import { DeliveryPreviewComponent } from '../../shared/components/delivery-preview/delivery-preview.component'; import { GymPickerComponent } from '../../shared/components/gym-picker/gym-picker.component'; import { TemplateSelectorComponent } from '../../shared/components/template-selector/template-selector.component'; +import { WhereChipComponent } from '../../shared/components/where-chip/where-chip.component'; import { AUTO_DELETE, isAutoDelete, preserve } from '../../shared/utils/clean-flags'; @Component({ @@ -36,6 +37,7 @@ import { AUTO_DELETE, isAutoDelete, preserve } from '../../shared/utils/clean-fl TemplateSelectorComponent, DeliveryPreviewComponent, GymPickerComponent, + WhereChipComponent, ], selector: 'app-gym-edit-dialog', standalone: true, @@ -81,6 +83,11 @@ export class GymEditDialogComponent { } } + /** True when the alarm is confined to areas, which the areas-or-distance control cannot express. */ + isAreaScoped(): boolean { + return (this.data.overrideAreas?.length ?? 0) > 0; + } + onDistanceModeChange(): void { if (this.form.controls.distanceMode.value === 'areas') this.form.controls.distanceKm.setValue(0); else if (!this.form.controls.distanceKm.value) this.form.controls.distanceKm.setValue(1); diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/gyms/gym-list.component.html b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/gyms/gym-list.component.html index 1dcdeb6c..1d743787 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/gyms/gym-list.component.html +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/gyms/gym-list.component.html @@ -79,11 +79,12 @@

{{ getTeamName(gym.team) }}

- @if (gym.distance === 0) { - map {{ 'ALARM.USING_AREAS' | translate }} - } @else { - straighten {{ formatDistance(gym.distance) }} - } +
diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/gyms/gym-list.component.ts b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/gyms/gym-list.component.ts index 9c6cb61a..1be4475c 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/gyms/gym-list.component.ts +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/gyms/gym-list.component.ts @@ -15,12 +15,16 @@ import { firstValueFrom, forkJoin } from 'rxjs'; import { GymAddDialogComponent } from './gym-add-dialog.component'; import { GymEditDialogComponent } from './gym-edit-dialog.component'; import { Gym } from '../../core/models'; +import { AreaService } from '../../core/services/area.service'; import { GymService } from '../../core/services/gym.service'; import { I18nService } from '../../core/services/i18n.service'; import { ScannerService } from '../../core/services/scanner.service'; import { TestAlertService } from '../../core/services/test-alert.service'; import { ConfirmDialogComponent, ConfirmDialogData } from '../../shared/components/confirm-dialog/confirm-dialog.component'; import { DistanceDialogComponent } from '../../shared/components/distance-dialog/distance-dialog.component'; +import { WhereChipComponent } from '../../shared/components/where-chip/where-chip.component'; +import { WhereSheetComponent, WhereSheetData } from '../../shared/components/where-sheet/where-sheet.component'; +import { AlarmScope, scopeOf, scopeToFields } from '../../shared/utils/alarm-scope'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -35,6 +39,7 @@ import { DistanceDialogComponent } from '../../shared/components/distance-dialog MatSnackBarModule, MatProgressSpinnerModule, TranslatePipe, + WhereChipComponent, ], selector: 'app-gym-list', standalone: true, @@ -42,6 +47,7 @@ import { DistanceDialogComponent } from '../../shared/components/distance-dialog templateUrl: './gym-list.component.html', }) export class GymListComponent implements OnInit { + private readonly areaService = inject(AreaService); private readonly destroyRef = inject(DestroyRef); private readonly dialog = inject(MatDialog); private readonly gymService = inject(GymService); @@ -51,8 +57,12 @@ export class GymListComponent implements OnInit { readonly gymNames = signal>({}); readonly gyms = signal([]); readonly loading = signal(true); + /** Only used to word the inherited scope honestly; empty produces the more cautious wording. */ + readonly profileAreas = signal([]); readonly selectedIds = signal(new Set()); + readonly selectMode = signal(false); + readonly testAlertService = inject(TestAlertService); async bulkDelete(): Promise { @@ -175,6 +185,29 @@ export class GymListComponent implements OnInit { }); } + /** Change one alarm's delivery scope from its card, without opening the whole edit dialog. */ + editScope(item: Gym): void { + const data: WhereSheetData = { + profileAreas: this.profileAreas(), + scope: scopeOf(item.overrideLocationLabel, item.overrideAreas, item.distance), + }; + + this.dialog + .open(WhereSheetComponent, { width: '520px', autoFocus: false, data }) + .afterClosed() + .subscribe((scope?: AlarmScope) => { + if (!scope) return; + + this.gymService.update(item.uid, scopeToFields(scope)).subscribe({ + error: () => this.snackBar.open(this.i18n.instant('WHERE.SCOPE_SAVE_ERROR'), this.i18n.instant('COMMON.OK'), { duration: 4000 }), + next: () => { + this.snackBar.open(this.i18n.instant('WHERE.SCOPE_SAVED'), this.i18n.instant('COMMON.OK'), { duration: 2500 }); + this.loadGyms(); + }, + }); + }); + } + formatDistance(meters: number): string { return meters >= 1000 ? `${(meters / 1000).toFixed(1)} km` : `${meters} m`; } @@ -234,6 +267,7 @@ export class GymListComponent implements OnInit { } ngOnInit(): void { + this.loadProfileAreas(); this.loadGyms(); } @@ -282,6 +316,10 @@ export class GymListComponent implements OnInit { }); } + private loadProfileAreas(): void { + this.areaService.getSelected().subscribe({ error: () => undefined, next: areas => this.profileAreas.set(areas) }); + } + private resolveGymNames(gyms: Gym[]): void { const ids = [...new Set(gyms.filter(g => g.gymId).map(g => g.gymId!))]; if (ids.length === 0) return; diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/invasions/invasion-edit-dialog.component.html b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/invasions/invasion-edit-dialog.component.html index b56b8b89..bcc4cffd 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/invasions/invasion-edit-dialog.component.html +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/invasions/invasion-edit-dialog.component.html @@ -53,39 +53,54 @@

{{ getDisplayName() }}

{{ 'ALARM.LOCATION_MODE' | translate }}

- - -
- map -
- {{ 'ALARM.USE_AREAS' | translate }} -

{{ 'ALARM.USE_AREAS_HINT' | translate }}

+ +

+ + {{ 'WHERE.EDIT_FROM_CARD' | translate }} +

+ + @if (isAreaScoped()) { + + } @else { + + +
+ map +
+ {{ 'ALARM.USE_AREAS' | translate }} +

{{ 'ALARM.USE_AREAS_HINT' | translate }}

+
-
- - -
- straighten -
- {{ 'ALARM.SET_DISTANCE' | translate }} -

{{ 'ALARM.SET_DISTANCE_HINT' | translate }}

+ + +
+ straighten +
+ {{ 'ALARM.SET_DISTANCE' | translate }} +

{{ 'ALARM.SET_DISTANCE_HINT' | translate }}

+
-
- - + + - @if (form.controls.distanceMode.value === 'distance') { - - {{ 'ALARM.DISTANCE_LABEL' | translate }} - - {{ 'ALARM.DISTANCE_SUFFIX' | translate }} - - } + @if (form.controls.distanceMode.value === 'distance') { + + {{ 'ALARM.DISTANCE_LABEL' | translate }} + + {{ 'ALARM.DISTANCE_SUFFIX' | translate }} + + } - - + + + }

{{ 'ALARM.MESSAGE_SETTINGS' | translate }}

diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/invasions/invasion-edit-dialog.component.scss b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/invasions/invasion-edit-dialog.component.scss index eb2e87bf..995ab883 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/invasions/invasion-edit-dialog.component.scss +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/invasions/invasion-edit-dialog.component.scss @@ -98,3 +98,16 @@ mat-slide-toggle { min-width: 0; } } + +.scope-current { + align-items: center; + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin: 0 0 1rem; +} + +.scope-hint { + color: var(--mat-sys-on-surface-variant, rgb(0 0 0 / 60%)); + font-size: 0.75rem; +} diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/invasions/invasion-edit-dialog.component.ts b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/invasions/invasion-edit-dialog.component.ts index 3bc44563..f6b558c0 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/invasions/invasion-edit-dialog.component.ts +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/invasions/invasion-edit-dialog.component.ts @@ -20,6 +20,7 @@ import { I18nService } from '../../core/services/i18n.service'; import { InvasionService } from '../../core/services/invasion.service'; import { DeliveryPreviewComponent } from '../../shared/components/delivery-preview/delivery-preview.component'; import { TemplateSelectorComponent } from '../../shared/components/template-selector/template-selector.component'; +import { WhereChipComponent } from '../../shared/components/where-chip/where-chip.component'; import { AUTO_DELETE, isAutoDelete, preserve } from '../../shared/utils/clean-flags'; @Component({ @@ -38,6 +39,7 @@ import { AUTO_DELETE, isAutoDelete, preserve } from '../../shared/utils/clean-fl TranslatePipe, TemplateSelectorComponent, DeliveryPreviewComponent, + WhereChipComponent, ], selector: 'app-invasion-edit-dialog', standalone: true, @@ -98,6 +100,11 @@ export class InvasionEditDialogComponent { return getGruntIconUrl(this.data.gruntType, this.selectedGender()); } + /** True when the alarm is confined to areas, which the areas-or-distance control cannot express. */ + isAreaScoped(): boolean { + return (this.data.overrideAreas?.length ?? 0) > 0; + } + onDistanceModeChange(): void { if (this.form.controls.distanceMode.value === 'areas') this.form.controls.distanceKm.setValue(0); else if (!this.form.controls.distanceKm.value) this.form.controls.distanceKm.setValue(1); diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/invasions/invasion-list.component.html b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/invasions/invasion-list.component.html index 73e31f84..5ec7b9e1 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/invasions/invasion-list.component.html +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/invasions/invasion-list.component.html @@ -96,11 +96,12 @@

{{ getDisplayName(invasion.gruntType, invasion.gender) }}

- @if (invasion.distance === 0) { - map {{ 'ALARM.USING_AREAS' | translate }} - } @else { - straighten {{ formatDistance(invasion.distance) }} - } +
diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/invasions/invasion-list.component.ts b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/invasions/invasion-list.component.ts index 2ad1cbd4..a0e9aa2e 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/invasions/invasion-list.component.ts +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/invasions/invasion-list.component.ts @@ -22,12 +22,16 @@ import { isGenderFixed as checkGenderFixed, } from './invasion.constants'; import { Invasion } from '../../core/models'; +import { AreaService } from '../../core/services/area.service'; import { I18nService } from '../../core/services/i18n.service'; import { InvasionService } from '../../core/services/invasion.service'; import { MasterDataService } from '../../core/services/masterdata.service'; import { TestAlertService } from '../../core/services/test-alert.service'; import { ConfirmDialogComponent, ConfirmDialogData } from '../../shared/components/confirm-dialog/confirm-dialog.component'; import { DistanceDialogComponent } from '../../shared/components/distance-dialog/distance-dialog.component'; +import { WhereChipComponent } from '../../shared/components/where-chip/where-chip.component'; +import { WhereSheetComponent, WhereSheetData } from '../../shared/components/where-sheet/where-sheet.component'; +import { AlarmScope, scopeOf, scopeToFields } from '../../shared/utils/alarm-scope'; import { isAutoDelete as cleanIsAutoDelete } from '../../shared/utils/clean-flags'; @Component({ @@ -43,6 +47,7 @@ import { isAutoDelete as cleanIsAutoDelete } from '../../shared/utils/clean-flag MatSnackBarModule, MatProgressSpinnerModule, TranslatePipe, + WhereChipComponent, ], selector: 'app-invasion-list', standalone: true, @@ -50,6 +55,7 @@ import { isAutoDelete as cleanIsAutoDelete } from '../../shared/utils/clean-flag templateUrl: './invasion-list.component.html', }) export class InvasionListComponent implements OnInit { + private readonly areaService = inject(AreaService); private readonly destroyRef = inject(DestroyRef); private readonly dialog = inject(MatDialog); private readonly i18n = inject(I18nService); @@ -58,8 +64,12 @@ export class InvasionListComponent implements OnInit { private readonly snackBar = inject(MatSnackBar); readonly invasions = signal([]); readonly loading = signal(true); + /** Only used to word the inherited scope honestly; empty produces the more cautious wording. */ + readonly profileAreas = signal([]); readonly selectedIds = signal(new Set()); + readonly selectMode = signal(false); + readonly testAlertService = inject(TestAlertService); async bulkDelete(): Promise { @@ -184,6 +194,29 @@ export class InvasionListComponent implements OnInit { }); } + /** Change one alarm's delivery scope from its card, without opening the whole edit dialog. */ + editScope(item: Invasion): void { + const data: WhereSheetData = { + profileAreas: this.profileAreas(), + scope: scopeOf(item.overrideLocationLabel, item.overrideAreas, item.distance), + }; + + this.dialog + .open(WhereSheetComponent, { width: '520px', autoFocus: false, data }) + .afterClosed() + .subscribe((scope?: AlarmScope) => { + if (!scope) return; + + this.invasionService.update(item.uid, scopeToFields(scope)).subscribe({ + error: () => this.snackBar.open(this.i18n.instant('WHERE.SCOPE_SAVE_ERROR'), this.i18n.instant('COMMON.OK'), { duration: 4000 }), + next: () => { + this.snackBar.open(this.i18n.instant('WHERE.SCOPE_SAVED'), this.i18n.instant('COMMON.OK'), { duration: 2500 }); + this.loadInvasions(); + }, + }); + }); + } + formatDistance(meters: number): string { return meters >= 1000 ? `${(meters / 1000).toFixed(1)} km` : `${meters} m`; } @@ -241,6 +274,7 @@ export class InvasionListComponent implements OnInit { } ngOnInit(): void { + this.loadProfileAreas(); this.masterData.loadData().pipe(takeUntilDestroyed(this.destroyRef)).subscribe(); this.loadInvasions(); } @@ -289,4 +323,8 @@ export class InvasionListComponent implements OnInit { } }); } + + private loadProfileAreas(): void { + this.areaService.getSelected().subscribe({ error: () => undefined, next: areas => this.profileAreas.set(areas) }); + } } diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/lures/lure-edit-dialog.component.html b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/lures/lure-edit-dialog.component.html index 8496963b..b72f3226 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/lures/lure-edit-dialog.component.html +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/lures/lure-edit-dialog.component.html @@ -29,39 +29,54 @@

{{ getLureName(data.lureId) }} {{ 'LURES.LURE_SUFFIX' | translate }}

{{ 'ALARM.LOCATION_MODE' | translate }}

- - -
- map -
- {{ 'ALARM.USE_AREAS' | translate }} -

{{ 'ALARM.USE_AREAS_HINT' | translate }}

+ +

+ + {{ 'WHERE.EDIT_FROM_CARD' | translate }} +

+ + @if (isAreaScoped()) { + + } @else { + + +
+ map +
+ {{ 'ALARM.USE_AREAS' | translate }} +

{{ 'ALARM.USE_AREAS_HINT' | translate }}

+
-
- - -
- straighten -
- {{ 'ALARM.SET_DISTANCE' | translate }} -

{{ 'ALARM.SET_DISTANCE_HINT' | translate }}

+ + +
+ straighten +
+ {{ 'ALARM.SET_DISTANCE' | translate }} +

{{ 'ALARM.SET_DISTANCE_HINT' | translate }}

+
-
- - + + - @if (form.controls.distanceMode.value === 'distance') { - - {{ 'ALARM.DISTANCE_LABEL' | translate }} - - {{ 'ALARM.DISTANCE_SUFFIX' | translate }} - - } + @if (form.controls.distanceMode.value === 'distance') { + + {{ 'ALARM.DISTANCE_LABEL' | translate }} + + {{ 'ALARM.DISTANCE_SUFFIX' | translate }} + + } - - + + + }

{{ 'ALARM.MESSAGE_SETTINGS' | translate }}

diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/lures/lure-edit-dialog.component.scss b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/lures/lure-edit-dialog.component.scss index e6b51a65..5425acf8 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/lures/lure-edit-dialog.component.scss +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/lures/lure-edit-dialog.component.scss @@ -56,3 +56,16 @@ mat-slide-toggle { min-width: 0; } } + +.scope-current { + align-items: center; + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin: 0 0 1rem; +} + +.scope-hint { + color: var(--mat-sys-on-surface-variant, rgb(0 0 0 / 60%)); + font-size: 0.75rem; +} diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/lures/lure-edit-dialog.component.ts b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/lures/lure-edit-dialog.component.ts index b56d11ec..f03dde06 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/lures/lure-edit-dialog.component.ts +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/lures/lure-edit-dialog.component.ts @@ -17,6 +17,7 @@ import { I18nService } from '../../core/services/i18n.service'; import { LureService } from '../../core/services/lure.service'; import { DeliveryPreviewComponent } from '../../shared/components/delivery-preview/delivery-preview.component'; import { TemplateSelectorComponent } from '../../shared/components/template-selector/template-selector.component'; +import { WhereChipComponent } from '../../shared/components/where-chip/where-chip.component'; import { AUTO_DELETE, compose, EDIT, isAutoDelete, isEdit, preserve } from '../../shared/utils/clean-flags'; @Component({ @@ -34,6 +35,7 @@ import { AUTO_DELETE, compose, EDIT, isAutoDelete, isEdit, preserve } from '../. TranslatePipe, TemplateSelectorComponent, DeliveryPreviewComponent, + WhereChipComponent, ], selector: 'app-lure-edit-dialog', standalone: true, @@ -81,6 +83,11 @@ export class LureEditDialogComponent { } } + /** True when the alarm is confined to areas, which the areas-or-distance control cannot express. */ + isAreaScoped(): boolean { + return (this.data.overrideAreas?.length ?? 0) > 0; + } + onDistanceModeChange(): void { if (this.form.controls.distanceMode.value === 'areas') this.form.controls.distanceKm.setValue(0); else if (!this.form.controls.distanceKm.value) this.form.controls.distanceKm.setValue(1); diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/lures/lure-list.component.html b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/lures/lure-list.component.html index c574494f..4cd07fb8 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/lures/lure-list.component.html +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/lures/lure-list.component.html @@ -78,11 +78,12 @@

{{ getLureName(lure.lureId) }} {{ 'LURES.LURE_SUFFIX' | translate }}

- @if (lure.distance === 0) { - map {{ 'ALARM.USING_AREAS' | translate }} - } @else { - straighten {{ formatDistance(lure.distance) }} - } +
diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/lures/lure-list.component.ts b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/lures/lure-list.component.ts index 4048ffaa..4d901f92 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/lures/lure-list.component.ts +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/lures/lure-list.component.ts @@ -15,11 +15,15 @@ import { firstValueFrom } from 'rxjs'; import { LureAddDialogComponent } from './lure-add-dialog.component'; import { LureEditDialogComponent } from './lure-edit-dialog.component'; import { Lure } from '../../core/models'; +import { AreaService } from '../../core/services/area.service'; import { I18nService } from '../../core/services/i18n.service'; import { LureService } from '../../core/services/lure.service'; import { TestAlertService } from '../../core/services/test-alert.service'; import { ConfirmDialogComponent, ConfirmDialogData } from '../../shared/components/confirm-dialog/confirm-dialog.component'; import { DistanceDialogComponent } from '../../shared/components/distance-dialog/distance-dialog.component'; +import { WhereChipComponent } from '../../shared/components/where-chip/where-chip.component'; +import { WhereSheetComponent, WhereSheetData } from '../../shared/components/where-sheet/where-sheet.component'; +import { AlarmScope, scopeOf, scopeToFields } from '../../shared/utils/alarm-scope'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -34,6 +38,7 @@ import { DistanceDialogComponent } from '../../shared/components/distance-dialog MatSnackBarModule, MatProgressSpinnerModule, TranslatePipe, + WhereChipComponent, ], selector: 'app-lure-list', standalone: true, @@ -41,6 +46,7 @@ import { DistanceDialogComponent } from '../../shared/components/distance-dialog templateUrl: './lure-list.component.html', }) export class LureListComponent implements OnInit { + private readonly areaService = inject(AreaService); private readonly destroyRef = inject(DestroyRef); private readonly dialog = inject(MatDialog); private readonly i18n = inject(I18nService); @@ -48,8 +54,12 @@ export class LureListComponent implements OnInit { private readonly snackBar = inject(MatSnackBar); readonly loading = signal(true); readonly lures = signal([]); + /** Only used to word the inherited scope honestly; empty produces the more cautious wording. */ + readonly profileAreas = signal([]); readonly selectedIds = signal(new Set()); + readonly selectMode = signal(false); + readonly testAlertService = inject(TestAlertService); async bulkDelete(): Promise { @@ -172,6 +182,29 @@ export class LureListComponent implements OnInit { }); } + /** Change one alarm's delivery scope from its card, without opening the whole edit dialog. */ + editScope(item: Lure): void { + const data: WhereSheetData = { + profileAreas: this.profileAreas(), + scope: scopeOf(item.overrideLocationLabel, item.overrideAreas, item.distance), + }; + + this.dialog + .open(WhereSheetComponent, { width: '520px', autoFocus: false, data }) + .afterClosed() + .subscribe((scope?: AlarmScope) => { + if (!scope) return; + + this.lureService.update(item.uid, scopeToFields(scope)).subscribe({ + error: () => this.snackBar.open(this.i18n.instant('WHERE.SCOPE_SAVE_ERROR'), this.i18n.instant('COMMON.OK'), { duration: 4000 }), + next: () => { + this.snackBar.open(this.i18n.instant('WHERE.SCOPE_SAVED'), this.i18n.instant('COMMON.OK'), { duration: 2500 }); + this.loadLures(); + }, + }); + }); + } + formatDistance(meters: number): string { return meters >= 1000 ? `${(meters / 1000).toFixed(1)} km` : `${meters} m`; } @@ -243,6 +276,7 @@ export class LureListComponent implements OnInit { } ngOnInit(): void { + this.loadProfileAreas(); this.loadLures(); } @@ -290,4 +324,8 @@ export class LureListComponent implements OnInit { } }); } + + private loadProfileAreas(): void { + this.areaService.getSelected().subscribe({ error: () => undefined, next: areas => this.profileAreas.set(areas) }); + } } diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/max-battles/max-battle-edit-dialog.component.html b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/max-battles/max-battle-edit-dialog.component.html index 2325f898..e06cc1f6 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/max-battles/max-battle-edit-dialog.component.html +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/max-battles/max-battle-edit-dialog.component.html @@ -58,39 +58,54 @@

{{ getTitle() }}

{{ 'ALARM.LOCATION_MODE' | translate }}

- - -
- map -
- {{ 'ALARM.USE_AREAS' | translate }} -

{{ 'ALARM.USE_AREAS_HINT' | translate }}

+ +

+ + {{ 'WHERE.EDIT_FROM_CARD' | translate }} +

+ + @if (isAreaScoped()) { + + } @else { + + +
+ map +
+ {{ 'ALARM.USE_AREAS' | translate }} +

{{ 'ALARM.USE_AREAS_HINT' | translate }}

+
-
- - -
- straighten -
- {{ 'ALARM.SET_DISTANCE' | translate }} -

{{ 'ALARM.SET_DISTANCE_HINT' | translate }}

+ + +
+ straighten +
+ {{ 'ALARM.SET_DISTANCE' | translate }} +

{{ 'ALARM.SET_DISTANCE_HINT' | translate }}

+
-
- - + + - @if (form.controls.distanceMode.value === 'distance') { - - {{ 'ALARM.DISTANCE_LABEL' | translate }} - - {{ 'ALARM.DISTANCE_SUFFIX' | translate }} - - } + @if (form.controls.distanceMode.value === 'distance') { + + {{ 'ALARM.DISTANCE_LABEL' | translate }} + + {{ 'ALARM.DISTANCE_SUFFIX' | translate }} + + } - - + + + }

{{ 'ALARM.MESSAGE_SETTINGS' | translate }}

diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/max-battles/max-battle-edit-dialog.component.scss b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/max-battles/max-battle-edit-dialog.component.scss index be3d0882..4dc9ddcd 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/max-battles/max-battle-edit-dialog.component.scss +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/max-battles/max-battle-edit-dialog.component.scss @@ -121,3 +121,16 @@ mat-slide-toggle { min-width: 0; } } + +.scope-current { + align-items: center; + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin: 0 0 1rem; +} + +.scope-hint { + color: var(--mat-sys-on-surface-variant, rgb(0 0 0 / 60%)); + font-size: 0.75rem; +} diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/max-battles/max-battle-edit-dialog.component.ts b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/max-battles/max-battle-edit-dialog.component.ts index 92713f55..489f5a7a 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/max-battles/max-battle-edit-dialog.component.ts +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/max-battles/max-battle-edit-dialog.component.ts @@ -20,6 +20,7 @@ import { MasterDataService } from '../../core/services/masterdata.service'; import { MaxBattleService } from '../../core/services/max-battle.service'; import { DeliveryPreviewComponent } from '../../shared/components/delivery-preview/delivery-preview.component'; import { TemplateSelectorComponent } from '../../shared/components/template-selector/template-selector.component'; +import { WhereChipComponent } from '../../shared/components/where-chip/where-chip.component'; import { AUTO_DELETE, isAutoDelete, preserve } from '../../shared/utils/clean-flags'; export interface MaxBattleEditDialogData { @@ -60,6 +61,7 @@ const LEVEL_OPTION_KEYS: { gmax: boolean; i18nKey: string; value: number }[] = [ TranslatePipe, TemplateSelectorComponent, DeliveryPreviewComponent, + WhereChipComponent, ], selector: 'app-max-battle-edit-dialog', standalone: true, @@ -119,6 +121,11 @@ export class MaxBattleEditDialogComponent { return this.i18n.instant('MAX_BATTLES.ANY_POKEMON'); } + /** True when the alarm is confined to areas, which the areas-or-distance control cannot express. */ + isAreaScoped(): boolean { + return (this.data.item.overrideAreas?.length ?? 0) > 0; + } + isGmax(): boolean { return this.data.item.gmax === 1 || this.data.item.level === 7 || this.data.item.level === 8; } diff --git a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/max-battles/max-battle-list.component.html b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/max-battles/max-battle-list.component.html index ba29549b..e18d67b9 100644 --- a/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/max-battles/max-battle-list.component.html +++ b/Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/modules/max-battles/max-battle-list.component.html @@ -119,7 +119,15 @@

{{ getTitle(mb) }}

{{ getFormName(mb.pokemonId, mb.form) }} }
- +
- +