Skip to content
Draft
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
17 changes: 17 additions & 0 deletions src/app/core/guards/native-block.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { Capacitor } from '@capacitor/core';

/**
* Blocks navigation on native platforms by redirecting to the root route.
* Use for pages that should only be available on web/desktop.
*/
export const nativeBlockGuard: CanActivateFn = () => {
const router = inject(Router);

if (Capacitor.isNativePlatform()) {
router.navigate(['/']);
return false;
}
return true;
};
21 changes: 21 additions & 0 deletions src/app/core/guards/snow-only.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { firstValueFrom } from 'rxjs';
import { GeoHazard } from 'src/app/modules/common-core/models';
import { UserSettingService } from '../services/user-setting/user-setting.service';

/**
* Allow activation only when the current geohazard selection includes Snow.
* Other geohazards are redirected to root.
*/
export const snowOnlyGuard: CanActivateFn = async () => {
const router = inject(Router);
const userSettingService = inject(UserSettingService);

const geoHazards = await firstValueFrom(userSettingService.currentGeoHazard$);
if (geoHazards?.includes(GeoHazard.Snow)) {
return true;
}
router.navigate(['/']);
return false;
};
46 changes: 46 additions & 0 deletions src/app/core/services/analysis-filter/analysis-filter.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Injectable, computed, linkedSignal, signal } from '@angular/core';
import moment from 'moment';

/**
* State holder for the Analyse-tab filters.
*
* Holds:
* - which observation types (faretegn / skred) to render
* - the reference date defining the end of a 14-day window
* - the currently active day inside that window (used by the time slider for dimming)
*/
@Injectable({ providedIn: 'root' })
export class AnalysisFilterService {
/** Number of days the reference date looks back. */
static readonly DAYS_BACK = 14;

readonly showDangerSigns = signal(true);
readonly showAvalanches = signal(true);

/** End of the 14-day analysis window. ISO date (yyyy-MM-dd). Defaults to today. */
readonly referenceDate = signal<string>(moment().format('YYYY-MM-DD'));

/** Start of the analysis window (referenceDate - 14 days). */
readonly fromDate = computed(() =>
moment(this.referenceDate()).subtract(AnalysisFilterService.DAYS_BACK, 'days').format('YYYY-MM-DD')
);

/** All dates (oldest -> newest) inside the analysis window, inclusive. */
readonly windowDates = computed<string[]>(() => {
const end = moment(this.referenceDate());
const dates: string[] = [];
for (let i = AnalysisFilterService.DAYS_BACK; i >= 0; i--) {
dates.push(end.clone().subtract(i, 'days').format('YYYY-MM-DD'));
}
return dates;
});

/**
* The currently selected "active" day in the analysis window.
* Re-syncs to the reference date whenever it changes (linkedSignal).
*/
readonly activeDate = linkedSignal<string, string>({
source: this.referenceDate,
computation: (ref) => ref,
});
}
30 changes: 30 additions & 0 deletions src/app/pages/analysis/analysis.page.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<ion-split-pane contentId="main-content-analysis-page">
<ion-menu side="start" menuId="analysis-filter" contentId="main-content-analysis-page" max-edge-start="0">
<app-analysis-filter-menu></app-analysis-filter-menu>
</ion-menu>

<div id="main-content-analysis-page" class="ion-page">
<app-header>
<ion-buttons slot="end">
<ion-menu-button color="light"></ion-menu-button>
</ion-buttons>
<ion-buttons slot="start">
<ion-menu-button menu="analysis-filter" color="light">
<ion-icon name="options-outline"></ion-icon>
</ion-menu-button>
</ion-buttons>
</app-header>
<ion-content>
<div class="analysis-map-host" #mapHost>
<app-map
(mapReady)="onMapReady($event)"
[autoActivate]="true"
[geoTag]="'AnalysisPage'"
[showUserLocation]="false"
></app-map>
<app-analysis-time-slider></app-analysis-time-slider>
<app-data-load [show]="isLoading()" label="DATA_LOAD.SPINNER_FETCH_OBSERVATIONS"></app-data-load>
</div>
</ion-content>
</div>
</ion-split-pane>
11 changes: 11 additions & 0 deletions src/app/pages/analysis/analysis.page.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.analysis-map-host {
position: relative;
width: 100%;
height: 100%;
}

app-map {
display: block;
width: 100%;
height: 100%;
}
Loading
Loading