Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,6 @@
<mat-icon>tune</mat-icon>
<span>{{ 'MENU.ALERT_DEFAULTS' | translate }}</span>
</button>
<button mat-menu-item (click)="openPlaces()">
<mat-icon>place</mat-icon>
<span>{{ 'MENU.PLACES' | translate }}</span>
</button>
<button mat-menu-item [matMenuTriggerFor]="accentMenu">
<mat-icon>palette</mat-icon>
<span>{{ 'MENU.ACCENT_THEME' | translate }}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ export const routes: Routes = [
loadComponent: () => import('./modules/geofences/geofence-list.component').then(m => m.GeofenceListComponent),
path: 'geofences',
},
{
canActivate: [authGuard, disabledFeatureGuard('disable_location')],
loadComponent: () => import('./modules/places/places.component').then(m => m.PlacesComponent),
path: 'places',
},
{
canActivate: [authGuard],
loadComponent: () => import('./modules/cleaning/cleaning.component').then(m => m.CleaningComponent),
Expand Down
13 changes: 8 additions & 5 deletions Applications/Pgan.PoracleWebNet.App/ClientApp/src/app/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { DashboardService } from './core/services/dashboard.service';
import { I18nService } from './core/services/i18n.service';
import { SettingsService } from './core/services/settings.service';
import { AlertDefaultsDialogComponent } from './shared/components/alert-defaults-dialog/alert-defaults-dialog.component';
import { PlacesDialogComponent } from './shared/components/places-dialog/places-dialog.component';

interface NavItem {
adminOnly?: boolean;
Expand Down Expand Up @@ -185,6 +184,14 @@ export class App implements OnInit {
route: '/profiles',
},
{ disableKey: 'disable_areas', group: 'settings', icon: 'map', iconColor: '#ff9800', label: 'NAV.AREAS', route: '/areas' },
{
disableKey: 'disable_location',
group: 'settings',
icon: 'place',
iconColor: '#e91e63',
label: 'NAV.PLACES',
route: '/places',
},
{
disableKey: 'disable_user_geofences',
group: 'settings',
Expand Down Expand Up @@ -382,10 +389,6 @@ export class App implements OnInit {
this.dialog.open(AlertDefaultsDialogComponent, { width: '480px', autoFocus: false });
}

openPlaces(): void {
this.dialog.open(PlacesDialogComponent, { width: '480px', autoFocus: false });
}

setAccentTheme(theme: string): void {
this.accentTheme.set(theme);
localStorage.setItem('poracle-accent', theme);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ <h3>{{ 'AREAS.METHOD_LOCATION' | translate }}</h3>
<mat-icon>edit_location</mat-icon>
{{ location() ? ('AREAS.CHANGE_LOCATION' | translate) : ('AREAS.SET_LOCATION' | translate) }}
</button>
<a mat-button routerLink="/places">
<mat-icon>place</mat-icon>
{{ 'AREAS.MANAGE_PLACES' | translate }}
</a>
</div>
</div>
<p class="method-description">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
import { RouterLink } from '@angular/router';
import { TranslatePipe } from '@ngx-translate/core';

import { AreaDefinition, GeofenceData, Location } from '../../core/models';
Expand Down Expand Up @@ -52,6 +53,7 @@ interface GroupInfo {
AreaMapComponent,
LanguageSelectorComponent,
RegionSelectorComponent,
RouterLink,
],
selector: 'app-area-list',
standalone: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
<h2 mat-dialog-title>{{ 'WHERE.PLACES_TITLE' | translate }}</h2>
<div class="page-header">
<div class="page-header-text">
<h1>{{ 'WHERE.PLACES_TITLE' | translate }}</h1>
<p class="page-description">{{ 'WHERE.PLACES_PAGE_DESC' | translate }}</p>
</div>
<button mat-flat-button [disabled]="busy() || loading()" (click)="addPlace()">
<mat-icon>add</mat-icon>
{{ 'WHERE.ADD_PLACE' | translate }}
</button>
</div>

<mat-dialog-content class="places">
@if (loading()) {
<div class="places-loading"><mat-spinner diameter="32" /></div>
} @else {
@if (loading()) {
<div class="loading-container"><mat-spinner diameter="48" /></div>
} @else {
<div class="places">
@if (places.pin(); as pin) {
<div class="places-pin">
<mat-icon class="places-pin-icon">my_location</mat-icon>
Expand Down Expand Up @@ -36,13 +45,5 @@ <h2 mat-dialog-title>{{ 'WHERE.PLACES_TITLE' | translate }}</h2>
}
</mat-list>
}
}
</mat-dialog-content>

<mat-dialog-actions align="end">
<button mat-button mat-dialog-close>{{ 'COMMON.CLOSE' | translate }}</button>
<button mat-flat-button [disabled]="busy() || loading()" (click)="addPlace()">
<mat-icon>add</mat-icon>
{{ 'WHERE.ADD_PLACE' | translate }}
</button>
</mat-dialog-actions>
</div>
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { HttpErrorResponse, provideHttpClient } from '@angular/common/http';
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { MatDialog } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { provideTranslateService } from '@ngx-translate/core';
import { of, throwError } from 'rxjs';

import { PlacesDialogComponent } from './places-dialog.component';
import { ConfigService } from '../../../core/services/config.service';
import { PlacesService } from '../../../core/services/places.service';
import { PlacesComponent } from './places.component';
import { ConfigService } from '../../core/services/config.service';
import { PlacesService } from '../../core/services/places.service';

describe('PlacesDialogComponent', () => {
describe('PlacesComponent', () => {
let dialog: { open: jest.Mock };
let places: {
add: jest.Mock;
Expand All @@ -26,7 +26,7 @@ describe('PlacesDialogComponent', () => {
results.forEach(result => dialog.open.mockReturnValueOnce({ afterClosed: () => of(result) }));
}

function create(): PlacesDialogComponent {
function create(): PlacesComponent {
dialog = { open: jest.fn() };
snackBar = { open: jest.fn() };
places = {
Expand All @@ -41,20 +41,19 @@ describe('PlacesDialogComponent', () => {
TestBed.configureTestingModule({
providers: [
provideTranslateService(),
{ provide: MatDialogRef, useValue: { close: jest.fn() } },
{ provide: MatDialog, useValue: dialog },
{ provide: MatSnackBar, useValue: snackBar },
{ provide: PlacesService, useValue: places },
{ provide: ConfigService, useValue: { apiHost: 'http://test' } },
provideHttpClient(),
provideHttpClientTesting(),
],
imports: [PlacesDialogComponent],
imports: [PlacesComponent],
});

// MatDialogModule is in the component's own imports, so its MatDialog wins over the TestBed
// provider. Overriding at the component injector is the only level that beats it.
TestBed.overrideComponent(PlacesDialogComponent, {
TestBed.overrideComponent(PlacesComponent, {
set: {
providers: [
{ provide: MatDialog, useValue: dialog },
Expand All @@ -63,7 +62,7 @@ describe('PlacesDialogComponent', () => {
},
});

const component = TestBed.createComponent(PlacesDialogComponent).componentInstance;
const component = TestBed.createComponent(PlacesComponent).componentInstance;
component.ngOnInit();
return component;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,39 @@ import { DecimalPipe } from '@angular/common';
import { HttpErrorResponse } from '@angular/common/http';
import { ChangeDetectionStrategy, Component, OnInit, inject, signal } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { MatDialog } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { MatListModule } from '@angular/material/list';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSnackBar } from '@angular/material/snack-bar';
import { TranslatePipe, TranslateService } from '@ngx-translate/core';

import { Location, SavedPlace } from '../../../core/models';
import { PlacesService } from '../../../core/services/places.service';
import { ConfirmDialogComponent } from '../confirm-dialog/confirm-dialog.component';
import { LocationDialogComponent } from '../location-dialog/location-dialog.component';
import { Location, SavedPlace } from '../../core/models';
import { PlacesService } from '../../core/services/places.service';
import { ConfirmDialogComponent } from '../../shared/components/confirm-dialog/confirm-dialog.component';
import { LocationDialogComponent } from '../../shared/components/location-dialog/location-dialog.component';

/**
* The places a user's alarms can be aimed at: the profile pin, plus whatever they have named.
*
* A page rather than a dialog, and in the nav beside Areas and My Geofences, because these are the
* user's data and not a preference. The Areas page already frames alerts as reaching you by area or by
* a radius from a point; named points are more of the second, so hiding them in a settings menu split
* one idea across two surfaces.
*
* Adding a place borrows the location dialog as a coordinate picker rather than growing a second map,
* then asks for the name separately, because picking a point and naming it are two decisions and
* putting them on one screen makes both feel like a form.
*/
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [DecimalPipe, MatButtonModule, MatDialogModule, MatIconModule, MatListModule, MatProgressSpinnerModule, TranslatePipe],
selector: 'app-places-dialog',
imports: [DecimalPipe, MatButtonModule, MatIconModule, MatListModule, MatProgressSpinnerModule, TranslatePipe],
selector: 'app-places',
standalone: true,
styleUrl: './places-dialog.component.scss',
templateUrl: './places-dialog.component.html',
styleUrl: './places.component.scss',
templateUrl: './places.component.html',
})
export class PlacesDialogComponent implements OnInit {
export class PlacesComponent implements OnInit {
private readonly dialog = inject(MatDialog);
private readonly snackBar = inject(MatSnackBar);
private readonly translate = inject(TranslateService);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"NAV": {
"PLACES": "Steder",
"GROUP_ALARMS": "Alarmer",
"GROUP_SETTINGS": "Indstillinger",
"GROUP_ADMIN": "Admin",
Expand Down Expand Up @@ -63,8 +64,7 @@
"ACCENT_MYSTIC": "Mystic",
"ACCENT_VALOR": "Valor",
"ACCENT_INSTINCT": "Instinct",
"ALERT_DEFAULTS": "Standardindstillinger for advarsler",
"PLACES": "Steder"
"ALERT_DEFAULTS": "Standardindstillinger for advarsler"
},
"SHORTCUTS": {
"TITLE": "Tastaturgenveje",
Expand Down Expand Up @@ -775,6 +775,7 @@
"SNACK_UPDATED": "Max Battle-alarm opdateret"
},
"AREAS": {
"MANAGE_PLACES": "Administrer steder",
"PAGE_TITLE": "Områder og placering",
"PAGE_DESC": "Kontrollér, hvor du modtager notifikationer.",
"METHOD_AREAS": "Områder",
Expand Down Expand Up @@ -1722,6 +1723,7 @@
"LAST_PAGE": "Sidste side"
},
"WHERE": {
"PLACES_PAGE_DESC": "Navngivne punkter, dine beskeder kan rettes mod i stedet for din position.",
"ADD_PLACE": "Tilføj et sted",
"AREAS_LABEL": "Områder",
"AREA_LIST_MORE": "{{areas}} og {{count}} mere",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"NAV": {
"PLACES": "Orte",
"GROUP_ALARMS": "Alarme",
"GROUP_SETTINGS": "Einstellungen",
"GROUP_ADMIN": "Admin",
Expand Down Expand Up @@ -63,8 +64,7 @@
"ACCENT_MYSTIC": "Mystic",
"ACCENT_VALOR": "Valor",
"ACCENT_INSTINCT": "Instinct",
"ALERT_DEFAULTS": "Benachrichtigungs-Standards",
"PLACES": "Orte"
"ALERT_DEFAULTS": "Benachrichtigungs-Standards"
},
"SHORTCUTS": {
"TITLE": "Tastenkürzel",
Expand Down Expand Up @@ -775,6 +775,7 @@
"SNACK_UPDATED": "Dynamax-Kampfalarm aktualisiert"
},
"AREAS": {
"MANAGE_PLACES": "Orte verwalten",
"PAGE_TITLE": "Gebiete & Standort",
"PAGE_DESC": "Bestimme, wo du Benachrichtigungen erhältst.",
"METHOD_AREAS": "Gebiete",
Expand Down Expand Up @@ -1722,6 +1723,7 @@
"LAST_PAGE": "Letzte Seite"
},
"WHERE": {
"PLACES_PAGE_DESC": "Benannte Punkte, auf die deine Meldungen statt auf deinen Standort ausgerichtet werden können.",
"ADD_PLACE": "Ort hinzufügen",
"AREAS_LABEL": "Gebiete",
"AREA_LIST_MORE": "{{areas}} und {{count}} weitere",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"NAV": {
"PLACES": "Places",
"GROUP_ALARMS": "Alarms",
"GROUP_SETTINGS": "Settings",
"GROUP_ADMIN": "Admin",
Expand Down Expand Up @@ -63,8 +64,7 @@
"ACCENT_MYSTIC": "Mystic",
"ACCENT_VALOR": "Valor",
"ACCENT_INSTINCT": "Instinct",
"ALERT_DEFAULTS": "Alert Defaults",
"PLACES": "Places"
"ALERT_DEFAULTS": "Alert Defaults"
},
"SHORTCUTS": {
"TITLE": "Keyboard Shortcuts",
Expand Down Expand Up @@ -775,6 +775,7 @@
"LEVEL_GMAX_LEGENDARY": "Legendary Gigantamax"
},
"AREAS": {
"MANAGE_PLACES": "Manage places",
"PAGE_TITLE": "Areas & Location",
"PAGE_DESC": "Control where you receive notifications.",
"METHOD_AREAS": "Areas",
Expand Down Expand Up @@ -1722,6 +1723,7 @@
"LAST_PAGE": "Last page"
},
"WHERE": {
"PLACES_PAGE_DESC": "Named points your alerts can be aimed at, instead of your pin.",
"EDIT_FROM_CARD": "Change this from the card.",
"MEASURED_FROM": "Measured from",
"SCOPE_SAVED": "Where updated.",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"NAV": {
"PLACES": "Lugares",
"GROUP_ALARMS": "Alarmas",
"GROUP_SETTINGS": "Ajustes",
"GROUP_ADMIN": "Admin",
Expand Down Expand Up @@ -63,8 +64,7 @@
"ACCENT_MYSTIC": "Mystic",
"ACCENT_VALOR": "Valor",
"ACCENT_INSTINCT": "Instinct",
"ALERT_DEFAULTS": "Valores predeterminados de alertas",
"PLACES": "Lugares"
"ALERT_DEFAULTS": "Valores predeterminados de alertas"
},
"SHORTCUTS": {
"TITLE": "Atajos de teclado",
Expand Down Expand Up @@ -775,6 +775,7 @@
"SNACK_UPDATED": "Alarma de Combate Dinamax actualizada"
},
"AREAS": {
"MANAGE_PLACES": "Gestionar lugares",
"PAGE_TITLE": "Zonas y ubicación",
"PAGE_DESC": "Controla dónde recibes las notificaciones.",
"METHOD_AREAS": "Zonas",
Expand Down Expand Up @@ -1722,6 +1723,7 @@
"LAST_PAGE": "Última página"
},
"WHERE": {
"PLACES_PAGE_DESC": "Puntos con nombre a los que puedes dirigir tus alertas en vez de a tu ubicación.",
"ADD_PLACE": "Añadir un lugar",
"AREAS_LABEL": "Áreas",
"AREA_LIST_MORE": "{{areas}} y {{count}} más",
Expand Down
Loading
Loading