✨ Place creation: GMaps API integration
This commit is contained in:
parent
028b9ab64f
commit
0e3addeb94
@ -232,15 +232,48 @@
|
||||
|
||||
<div class="mt-6 flex justify-between items-center">
|
||||
<div class="min-w-0 truncate">
|
||||
<div class="flex gap-2 items-center">
|
||||
<h1 class="font-semibold tracking-tight text-xl">TOTP</h1>
|
||||
@if (settings?.totp_enabled) {
|
||||
<span
|
||||
class="inline-flex items-center gap-x-1.5 bg-green-100 text-green-800 text-xs font-medium px-2.5 py-0.5 rounded min-w-fit dark:bg-green-400">
|
||||
<span class="size-1.5 inline-block rounded-full bg-green-800 dark:bg-green-500"></span>enabled</span>
|
||||
} @else {
|
||||
<span
|
||||
class="inline-flex items-center gap-x-1.5 bg-red-100 text-red-800 text-xs font-medium px-2.5 py-0.5 rounded min-w-fit dark:bg-red-400">
|
||||
<span class="size-1.5 inline-block rounded-full bg-red-800 dark:bg-red-500"></span>disabled</span>
|
||||
}
|
||||
</div>
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400">Add a second layer of security to your
|
||||
account</span>
|
||||
</div>
|
||||
|
||||
<p-button [icon]="settings?.totp_enabled ? 'pi pi-lock' : 'pi pi-lock-open'" (click)="toggleTOTP()"
|
||||
[severity]="settings?.totp_enabled ? 'success' : 'danger'"
|
||||
[pTooltip]="settings?.totp_enabled ? 'Click to disable' : 'Click to enable'"
|
||||
[label]="settings?.totp_enabled ? 'Enabled' : 'Disabled'" text />
|
||||
<p-button [icon]="settings?.totp_enabled ? 'pi pi-trash' : 'pi pi-lock'" (click)="toggleTOTP()"
|
||||
[severity]="settings?.totp_enabled ? 'danger' : 'success'"
|
||||
[label]="settings?.totp_enabled ? 'Disable' : 'Enable'" text />
|
||||
</div>
|
||||
|
||||
<div [formGroup]="settingsForm">
|
||||
@if (settings?.google_apikey) {
|
||||
<div class="mt-4 flex justify-between items-center">
|
||||
<div class="min-w-0 truncate">
|
||||
<h1 class="font-semibold tracking-tight text-xl">Google API Key</h1>
|
||||
</div>
|
||||
|
||||
<p-button icon="pi pi-trash" (click)="deleteGoogleApiKey()" severity="danger" label="Delete" text />
|
||||
</div>
|
||||
} @else {
|
||||
<h1 class="mt-4 font-semibold tracking-tight text-xl">Google API Key</h1>
|
||||
|
||||
<p-floatlabel variant="in" class="mt-4">
|
||||
<input id="_google_apikey" formControlName="_google_apikey" pInputText fluid />
|
||||
<label for="_google_apikey">API Key</label>
|
||||
</p-floatlabel>
|
||||
<div class="mt-2 w-full text-right">
|
||||
<p-button (click)="updateSettings()" label="Update" text
|
||||
[disabled]="!settingsForm.valid || settingsForm.pristine" />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</p-tabpanel>
|
||||
<p-tabpanel [value]="2">
|
||||
|
||||
@ -133,6 +133,7 @@ export class DashboardComponent implements OnInit, AfterViewInit {
|
||||
currency: ['', Validators.required],
|
||||
do_not_display: [],
|
||||
tile_layer: ['', Validators.required],
|
||||
_google_apikey: [null, { validators: [Validators.pattern('AIza[0-9A-Za-z\\-]{35}')] }],
|
||||
});
|
||||
|
||||
// HACK: Subscribe in constructor for takeUntilDestroyed
|
||||
@ -666,7 +667,7 @@ export class DashboardComponent implements OnInit, AfterViewInit {
|
||||
|
||||
updateSettings() {
|
||||
this.apiService
|
||||
.putSettings(this.settingsForm.value)
|
||||
.putSettings({ ...this.settingsForm.value, google_apikey: this.settingsForm.get('_google_apikey')?.value })
|
||||
.pipe(take(1))
|
||||
.subscribe({
|
||||
next: (settings) => {
|
||||
@ -980,4 +981,27 @@ export class DashboardComponent implements OnInit, AfterViewInit {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
deleteGoogleApiKey() {
|
||||
const modal = this.dialogService.open(YesNoModalComponent, {
|
||||
header: 'Confirm',
|
||||
modal: true,
|
||||
closable: true,
|
||||
dismissableMask: true,
|
||||
breakpoints: {
|
||||
'640px': '90vw',
|
||||
},
|
||||
data: 'Are you sure you want to delete GMaps API Key ?',
|
||||
})!;
|
||||
|
||||
modal.onClose.subscribe({
|
||||
next: (bool: boolean) => {
|
||||
if (!bool) return;
|
||||
this.apiService.putSettings({ google_apikey: null }).subscribe({
|
||||
next: () => (this.settings!.google_apikey = false),
|
||||
error: () => this.utilsService.toast('error', 'Error', 'Error deleting GMaps API key'),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
<section>
|
||||
<div pFocusTrap class="grid grid-cols-2 md:grid-cols-4 gap-4" [formGroup]="placeForm">
|
||||
<p-floatlabel variant="in" class="col-span-2">
|
||||
<div class="relative col-span-2">
|
||||
<p-floatlabel variant="in">
|
||||
<input id="name" formControlName="name" pInputText autofocus fluid />
|
||||
<label for="name">Name</label>
|
||||
</p-floatlabel>
|
||||
<p-button icon="pi pi-sparkles" variant="text" [disabled]="!placeForm.get('name')!.value"
|
||||
class="absolute right-2 top-1/2 -translate-y-1/2" pTooltip="Query GMaps API" (click)="gmapsSearchText()" />
|
||||
</div>
|
||||
|
||||
<p-floatlabel variant="in">
|
||||
<input id="lat" formControlName="lat" pInputText fluid placeholder="Lat or Lat, Lng" />
|
||||
@ -17,7 +21,7 @@
|
||||
|
||||
<p-inputgroup class="col-span-2 lg:col-span-3">
|
||||
<p-floatlabel variant="in">
|
||||
<input id="place" formControlName="place" pInputText fluid placeholder="" />
|
||||
<input id="place" formControlName="place" pInputText fluid />
|
||||
<label for="place">Place</label>
|
||||
</p-floatlabel>
|
||||
<p-inputgroup-addon>
|
||||
@ -70,10 +74,17 @@
|
||||
</div>
|
||||
|
||||
<div class="grid col-span-full md:grid-cols-4">
|
||||
<p-floatlabel variant="in" class="col-span-full md:col-span-3">
|
||||
<textarea pTextarea id="description" formControlName="description" rows="3" autoResize fluid></textarea>
|
||||
<div class="relative col-span-full md:col-span-3">
|
||||
<p-floatlabel variant="in">
|
||||
<textarea pTextarea id="description" formControlName="description" rows="3" #descriptionInput autoResize
|
||||
fluid></textarea>
|
||||
<label for="description">Description</label>
|
||||
</p-floatlabel>
|
||||
@if (descriptionInput.value) {
|
||||
<p-button icon="pi pi-times" severity="danger" variant="text" class="absolute right-2 top-2"
|
||||
(click)="placeForm.get('description')?.setValue('')" pTooltip="Clear" />
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="mt-4 md:mt-0 grid place-items-center col-span-full md:col-span-1">
|
||||
@if (placeForm.get("image_id")?.value) {
|
||||
|
||||
@ -203,4 +203,19 @@ export class PlaceCreateModalComponent {
|
||||
this.placeForm.get('gpx')?.setValue(null);
|
||||
this.placeForm.get('gpx')?.markAsDirty();
|
||||
}
|
||||
|
||||
gmapsSearchText() {
|
||||
const query = this.placeForm.get('name')?.value;
|
||||
if (!query) return;
|
||||
this.apiService.gmapsSearchText(query).subscribe({
|
||||
next: (results) => {
|
||||
if (results.length == 1) {
|
||||
const r = results[0];
|
||||
this.placeForm.patchValue({ ...r, lat: formatLatLng(r.lat), lng: formatLatLng(r.lng), place: r.name || '' });
|
||||
this.placeForm.get('category')?.markAsDirty();
|
||||
return;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Category, Place } from '../types/poi';
|
||||
import { Category, GooglePlaceResult, Place } from '../types/poi';
|
||||
import { BehaviorSubject, map, Observable, shareReplay, tap } from 'rxjs';
|
||||
import { Info } from '../types/info';
|
||||
import { Backup, ImportResponse, Settings } from '../types/settings';
|
||||
@ -321,4 +321,8 @@ export class ApiService {
|
||||
verifyTOTP(code: string): Observable<any> {
|
||||
return this.httpClient.post<any>(this.apiBaseUrl + '/settings/totp/verify', { code });
|
||||
}
|
||||
|
||||
gmapsSearchText(q: string): Observable<GooglePlaceResult[]> {
|
||||
return this.httpClient.get<GooglePlaceResult[]>(`${this.apiBaseUrl}/places/google-search`, { params: { q } });
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,3 +25,13 @@ export interface Place {
|
||||
visited?: boolean;
|
||||
favorite?: boolean;
|
||||
}
|
||||
|
||||
export interface GooglePlaceResult {
|
||||
name: string;
|
||||
lat: number;
|
||||
lng: number;
|
||||
price: number;
|
||||
types: string[];
|
||||
allowdog: boolean;
|
||||
description: string;
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ export interface Settings {
|
||||
mode_dark?: boolean;
|
||||
mode_gpx_in_place?: boolean;
|
||||
totp_enabled?: boolean;
|
||||
google_apikey?: boolean | null;
|
||||
}
|
||||
|
||||
export interface ImportResponse {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user