Merge branch 'master' into friend

friend
Maxence JOUANNET 3 months ago
commit 79d5fd5aef

@ -1,4 +1,4 @@
<app-navbar *ngIf="isAuth"></app-navbar>
<app-home-navbar *ngIf="!isAuth"></app-home-navbar>
<app-navbar *ngIf="localStorageService.getToken()"></app-navbar>
<app-home-navbar *ngIf="!localStorageService.getToken()"></app-home-navbar>
<router-outlet />

@ -3,6 +3,7 @@ import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { HomeNavbarComponent } from './components/home-navbar/home-navbar.component';
import { NavbarComponent } from './components/navbar/navbar.component';
import { LocalStorageService } from './services/localstorage.service';
@Component({
selector: 'app-root',
@ -11,13 +12,6 @@ import { NavbarComponent } from './components/navbar/navbar.component';
})
export class AppComponent {
title = 'Memory Map';
isAuth: boolean = false;
constructor() {}
ngOnInit(): void {
if (localStorage.getItem('auth_token') !== null) {
this.isAuth = true;
}
}
constructor(protected localStorageService: LocalStorageService) {}
}

@ -1,65 +1,55 @@
<!-- Modal toggle -->
<button
data-modal-target="authentication-modal"
data-modal-toggle="authentication-modal"
(click)="openPinModal()"
class="block py-2 text-gray-900 dark:text-white hover:text-gray-700 dark:hover:text-gray-300"
type="button"
>
<p *ngIf="!isHomePage">Ajouter un pin</p>
</button>
<!-- Fond assombri -->
<div
class="fixed inset-0 bg-gray-900 bg-opacity-50 w-full h-full transition-opacity duration-300 ease-in-out z-40"
[ngClass]="{'opacity-0 pointer-events-none': !isPinModalOpen, 'opacity-100': isPinModalOpen}"
(click)="closePinModal()"
></div>
<!-- Main modal -->
<div
id="authentication-modal"
id="pin-modal"
tabindex="-1"
aria-hidden="true"
class="hidden overflow-auto absolute top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full"
[ngClass]="{
'opacity-0 scale-50 pointer-events-none': !isPinModalOpen,
'opacity-100 scale-100': isPinModalOpen
}"
class="fixed top-0 right-0 left-0 z-50 flex justify-center items-center w-full h-full transition-opacity transition-transform duration-300 ease-in-out"
>
<div class="relative p-4 w-full max-w-xl max-h-full">
<!-- Modal content -->
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700 transition-transform duration-300 ease-in-out">
<!-- Modal header -->
<div
class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600"
>
<h3 class="text-xl font-semibold text-gray-900 dark:text-white">
Créer ton souvenir !
</h3>
<div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600">
<h3 class="text-xl font-semibold text-gray-900 dark:text-white">Créer ton souvenir !</h3>
<button
type="button"
(click)="closePinModal()"
class="end-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white"
data-modal-hide="authentication-modal"
>
<svg
class="w-3 h-3"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 14 14"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"
/>
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
</svg>
<span class="sr-only">Fermer la modal</span>
</button>
</div>
<!-- Modal body -->
<div class="p-4 md:p-5">
<form class="grid gap-6 mb-1 md:grid-cols-2" [formGroup]="form">
<div>
<label
for="title"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Titre</label
>
<label for="title" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Titre</label>
<input
type="title"
name="title"
type="text"
id="title"
formControlName="title"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white"
@ -69,11 +59,7 @@
</div>
<div>
<label
for="localisation"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Localisation</label
>
<label for="localisation" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Localisation</label>
<input
type="text"
id="localisation"
@ -83,31 +69,19 @@
(focus)="onFocus()"
(blur)="onBlur()"
/>
<ul
*ngIf="suggestions.length > 0 && inputFocused"
class="bg-white dark:bg-gray-700 border border-gray-300 mt-2 rounded shadow absolute z-10 mr-5 max-h-60 overflow-auto"
>
<li
*ngFor="let suggestion of suggestions"
(click)="selectSuggestion(suggestion)"
class="p-2 block mb-2 text-sm font-medium text-gray-900 dark:text-white hover:bg-gray-500 cursor-pointer"
>
<ul *ngIf="suggestions.length > 0 && inputFocused" class="bg-white dark:bg-gray-700 border border-gray-300 mt-2 rounded shadow absolute z-10 mr-5 max-h-60 overflow-auto">
<li *ngFor="let suggestion of suggestions" (click)="selectSuggestion(suggestion)" class="p-2 block mb-2 text-sm font-medium text-gray-900 dark:text-white hover:bg-gray-500 cursor-pointer">
{{ suggestion.display_name }}
</li>
</ul>
</div>
<div>
<app-drag-drop
(filesSelected)="onFilesReceived($event)"
></app-drag-drop>
<app-drag-drop (filesSelected)="onFilesReceived($event)"></app-drag-drop>
</div>
<div>
<label
for="description"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Description</label
>
<label for="description" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Description</label>
<textarea
id="description"
rows="4"
@ -118,20 +92,12 @@
</div>
<div class="flex justify-between">
<button
type="reset"
data-modal-hide="authentication-modal"
class="w-full text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-800"
>
<button type="reset" (click)="closePinModal()" class="w-full text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-800">
Annuler
</button>
</div>
<div class="flex justify-between">
<button
type="submit"
(click)="submitForm()"
class="w-full text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
>
<button type="submit" (click)="submitForm()" class="w-full text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
Valider
</button>
</div>
@ -139,4 +105,4 @@
</div>
</div>
</div>
</div>
</div>

@ -8,9 +8,8 @@ describe('AddPinPopupComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AddPinPopupComponent]
})
.compileComponents();
imports: [AddPinPopupComponent],
}).compileComponents();
fixture = TestBed.createComponent(AddPinPopupComponent);
component = fixture.componentInstance;

@ -13,8 +13,8 @@ import {
distinctUntilChanged,
switchMap,
} from 'rxjs/operators';
import { AddPinService } from '../../services/add-pin.service';
import { AutocompleteService } from '../../services/auto-complete.service';
import { PinService } from '../../services/pin.service';
import { DragDropComponent } from '../drag-drop/drag-drop.component';
@Component({
@ -29,11 +29,12 @@ export class AddPinPopupComponent implements OnInit {
inputFocused: boolean = false;
@Input() isHomePage: boolean = false;
files: any[] = [];
isPinModalOpen: boolean = false;
constructor(
private fb: FormBuilder,
private autocompleteService: AutocompleteService,
private addPinService: AddPinService
private pinService: PinService
) {
this.form = this.fb.group({
title: new FormControl(''),
@ -96,14 +97,22 @@ export class AddPinPopupComponent implements OnInit {
const pinData = {
...this.form.value,
files: this.files, // Ajoutez les fichiers au payload
files: this.files,
};
console.log('Form submitted with data:', pinData);
this.addPinService.addPin(pinData);
this.pinService.addPin(pinData).subscribe(() => {
this.closePinModal();
});
} else {
console.error('Le formulaire est invalide');
}
}
openPinModal() {
this.isPinModalOpen = true;
}
closePinModal() {
this.isPinModalOpen = false;
}
}

@ -17,7 +17,6 @@
>
<li class="flex items-center">
<a
href="#"
class="block text-gray-900 dark:text-white hover:text-gray-700 dark:hover:text-gray-300"
aria-current="page"
>
@ -28,9 +27,10 @@
</li>
<li class="flex items-center space-x-2">
<a
href="#"
class="block py-2 text-gray-900 dark:text-white hover:text-gray-700 dark:hover:text-gray-300"
>Inscription
><span>
<app-register-page></app-register-page>
</span>
</a>
</li>
</ul>

@ -1,9 +1,11 @@
import { Component } from '@angular/core';
import { LoginPageComponent } from '../login-page/login-page.component';
import { RegisterPageComponent } from '../register-page/register-page.component';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-home-navbar',
imports: [LoginPageComponent],
imports: [LoginPageComponent, RegisterPageComponent, CommonModule],
templateUrl: './home-navbar.component.html',
})
export class HomeNavbarComponent {

@ -1,7 +1,8 @@
import { Component, OnInit, ViewContainerRef } from '@angular/core';
import * as L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import { monuments } from '../../data/stub';
import { Monument } from '../../model/Monument';
import { PinService } from '../../services/pin.service';
import { MonumentMarkerComponent } from '../monument-marker/monument-marker.component';
@Component({
@ -11,7 +12,10 @@ import { MonumentMarkerComponent } from '../monument-marker/monument-marker.comp
export class LeafletMapComponent implements OnInit {
private map!: L.Map;
constructor(private viewContainerRef: ViewContainerRef) {}
constructor(
private viewContainerRef: ViewContainerRef,
private pinsService: PinService
) {}
ngOnInit(): void {
this.initializeMap();
@ -22,7 +26,7 @@ export class LeafletMapComponent implements OnInit {
this.map = L.map('map', {
maxBounds: L.latLngBounds(
L.latLng(-90, -180), // South-West
L.latLng(90, 180) // North-East
L.latLng(90, 180) // North-East
),
maxBoundsViscosity: 1.0, // Prevent dragging the map out of bounds
minZoom: 2, // Prevent zooming out too much
@ -47,24 +51,28 @@ export class LeafletMapComponent implements OnInit {
</svg>
`);
// Add markers
monuments.forEach((monument) => {
const icon = monument.visited ? visitedIcon : notVisitedIcon;
this.pinsService.getPins().subscribe((monuments: Monument[]) => {
console.log(monuments);
// Add markers
monuments.forEach((monument: Monument) => {
//const icon = monument.visited ? visitedIcon : notVisitedIcon;
const icon = visitedIcon;
const marker = L.marker(monument.coords as [number, number], {
icon,
}).addTo(this.map);
const marker = L.marker(monument.location as [number, number], {
icon,
}).addTo(this.map);
// Dynamically create Angular component and attach it to popup
const popupDiv = document.createElement('div');
const componentRef = this.viewContainerRef.createComponent(
MonumentMarkerComponent
);
// Dynamically create Angular component and attach it to popup
const popupDiv = document.createElement('div');
const componentRef = this.viewContainerRef.createComponent(
MonumentMarkerComponent
);
componentRef.instance.monument = monument;
popupDiv.appendChild(componentRef.location.nativeElement);
componentRef.instance.monument = monument;
popupDiv.appendChild(componentRef.location.nativeElement);
marker.bindPopup(popupDiv);
marker.bindPopup(popupDiv);
});
});
}

@ -1,23 +1,35 @@
<!-- Modal toggle -->
<button
data-modal-target="authentication-modal"
data-modal-toggle="authentication-modal"
(click)="openLoginModal()"
class="block py-2 text-gray-900 dark:text-white hover:text-gray-700 dark:hover:text-gray-300"
type="button"
>
Connexion
</button>
<!-- Fond assombri -->
<div
class="fixed inset-0 bg-gray-900 bg-opacity-50 w-full h-full transition-opacity duration-300 ease-in-out z-40"
[ngClass]="{'opacity-0 pointer-events-none': !isLoginModalOpen, 'opacity-100': isLoginModalOpen}"
(click)="closeLoginModal()"
></div>
<!-- Main modal -->
<div
id="authentication-modal"
tabindex="-1"
aria-hidden="true"
class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full transition-opacity duration-300 ease-in-out"
[ngClass]="{
'opacity-0 scale-50 pointer-events-none': !isLoginModalOpen,
'opacity-100 scale-100': isLoginModalOpen
}"
class="fixed top-0 right-0 left-0 z-50 flex justify-center items-center w-full h-full transition-opacity transition-transform duration-300 ease-in-out"
>
<div class="relative p-4 w-full max-w-md max-h-full">
<!-- Modal content -->
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
<div
class="relative bg-white rounded-lg shadow dark:bg-gray-700 transition-transform duration-300 ease-in-out"
>
<!-- Modal header -->
<div
class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600"
@ -27,9 +39,8 @@
</h3>
<button
type="button"
id="close-login-modal"
(click)="closeLoginModal()"
class="end-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white"
data-modal-hide="authentication-modal"
>
<svg
class="w-3 h-3"
@ -60,7 +71,7 @@
>
<input
formControlName="login"
type="login"
type="text"
name="login"
id="login"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white"
@ -125,4 +136,4 @@
</div>
</div>
</div>
</div>
</div>

@ -1,5 +1,5 @@
import { NgIf } from '@angular/common';
import { Component, Renderer2 } from '@angular/core';
import { CommonModule, NgIf } from '@angular/common';
import { Component } from '@angular/core';
import {
FormBuilder,
FormGroup,
@ -14,7 +14,7 @@ import { LoginService } from '../../services/login.service';
@Component({
selector: 'app-login-page',
imports: [FormsModule, ReactiveFormsModule, NgIf],
imports: [FormsModule, ReactiveFormsModule, NgIf, CommonModule],
templateUrl: './login-page.component.html',
styleUrl: './login-page.component.css',
})
@ -22,26 +22,27 @@ export class LoginPageComponent {
userForm: FormGroup;
user: User = { login: '', password: '' };
errorMessage: string = '';
isLoginModalOpen: boolean = false;
constructor(
private loginService: LoginService,
private fb: FormBuilder,
private router: Router,
private localStorageService: LocalStorageService,
private renderer: Renderer2
private localStorageService: LocalStorageService
) {
this.userForm = this.fb.group({
login: [this.user.login, [Validators.required, Validators.minLength(3)]],
password: [
this.user.password,
[Validators.required, Validators.minLength(3)],
[Validators.required, Validators.minLength(6)],
],
});
}
public login() {
if (this.userForm.invalid) {
this.errorMessage = 'Veuillez remplir tous les champs';
this.errorMessage =
'Veuillez remplir tous les champs (identifiant de 3 caractères et mot de passe de 6 caractères minimum)';
return;
}
@ -52,11 +53,10 @@ export class LoginPageComponent {
next: (response) => {
console.log('Connexion OK: ', response);
this.localStorageService.setToken(response.access_token);
this.closeModal();
this.closeLoginModal();
setTimeout(() => {
this.router.navigate(['/map']);
window.location.reload(); // Pas ouf mais ça marche
}, 500);
}, 1);
},
error: (response) => {
@ -66,10 +66,11 @@ export class LoginPageComponent {
});
}
private closeModal() {
const modal = document.getElementById('close-login-modal');
if (modal) {
modal.click();
}
openLoginModal() {
this.isLoginModalOpen = true;
}
closeLoginModal() {
this.isLoginModalOpen = false;
}
}

@ -1,7 +1,7 @@
<div class="text-center">
<strong>{{ monument.name }}</strong>
<strong>{{ monument.title }}</strong>
<div
*ngIf="monument.images.length > 0"
*ngIf="monument.files.length > 0"
class="relative carousel overflow-hidden"
>
<!-- Carousel wrapper -->
@ -9,7 +9,7 @@
class="relative h-40 mt-2 overflow-hidden rounded-lg flex items-center justify-center"
>
<div
*ngFor="let image of monument.images; let index = index"
*ngFor="let image of monument.files; let index = index"
[class]="
'absolute inset-0 transition-opacity duration-700 ease-in-out' +
(index === currentIndex ? ' opacity-100' : ' opacity-0')
@ -17,14 +17,14 @@
>
<img
[src]="image"
[alt]="monument.name"
[alt]="monument.title"
class="object-contain max-h-full max-w-full h-full w-auto mx-auto"
/>
</div>
</div>
<!-- Slider controls -->
<div *ngIf="monument.images.length > 1">
<div *ngIf="monument.files.length > 1">
<button
type="button"
class="absolute top-0 left-0 z-30 flex items-center justify-center h-full cursor-pointer group focus:outline-none"

@ -26,11 +26,11 @@ export class MonumentMarkerComponent {
prevSlide(): void {
this.currentIndex =
(this.currentIndex - 1 + this.monument.images.length) %
this.monument.images.length;
(this.currentIndex - 1 + this.monument.files.length) %
this.monument.files.length;
}
nextSlide(): void {
this.currentIndex = (this.currentIndex + 1) % this.monument.images.length;
this.currentIndex = (this.currentIndex + 1) % this.monument.files.length;
}
}

@ -9,7 +9,7 @@
>Memory Map</span
>
</a>
<div class="flex lg:order-2">
<div class="flex lg:order-2 justify-between">
<button
type="button"
data-collapse-toggle="navbar-search"
@ -104,6 +104,14 @@
/>
</svg>
</button>
<button
(click)="logout()"
type="button"
class="focus:outline-none text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 ml-10 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-900"
>
Déconnection
</button>
</div>
<div
class="items-center justify-between hidden w-full lg:flex lg:w-auto lg:order-1"
@ -141,14 +149,14 @@
>
<li class="flex items-center space-x-2">
<a
href="#"
routerLink="/"
*ngIf="!isHome"
class="block py-2 text-gray-900 dark:text-white hover:text-gray-700 dark:hover:text-gray-300"
aria-current="page"
>Accueil
</a>
<a
href="/map"
routerLink="/map"
*ngIf="isHome"
class="block py-2 text-gray-900 dark:text-white hover:text-gray-700 dark:hover:text-gray-300"
aria-current="page"
@ -157,7 +165,6 @@
</li>
<li class="flex items-center space-x-2">
<a
href="#"
class="block py-2 text-gray-900 dark:text-white hover:text-gray-700 dark:hover:text-gray-300"
>Quêtes
</a>

@ -1,19 +1,23 @@
import { NgIf } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { NavigationEnd, Router, RouterLink } from '@angular/router';
import { AddPinPopupComponent } from '../add-pin-popup/add-pin-popup.component';
import { LocalStorageService } from '../../services/localstorage.service';
import { FriendPageComponent } from "../friend-page/friend-page.component";
@Component({
selector: 'app-navbar',
imports: [AddPinPopupComponent, NgIf, FriendPageComponent],
imports: [AddPinPopupComponent, NgIf],
templateUrl: './navbar.component.html',
})
export class NavbarComponent implements OnInit {
isHome: boolean = false;
isModalOpen: boolean = false;
constructor(private router: Router) {}
constructor(
private router: Router,
private localStorageService: LocalStorageService
) {}
ngOnInit(): void {
this.isHome = this.router.url === '/';
@ -23,4 +27,9 @@ export class NavbarComponent implements OnInit {
}
});
}
public logout() {
this.localStorageService.removeToken();
this.router.navigate(['/']);
}
}

@ -0,0 +1,129 @@
<!-- Modal toggle -->
<button
(click)="openRegisterModal()"
class="block py-2 text-gray-900 dark:text-white hover:text-gray-700 dark:hover:text-gray-300"
type="button"
>
Inscription
</button>
<!-- Main modal -->
<div
class="fixed inset-0 z-40 bg-gray-900 bg-opacity-50 w-full h-full transition-opacity duration-300 ease-in-out"
[ngClass]="{
'opacity-0 pointer-events-none': !isRegisterModalOpen,
'opacity-100': isRegisterModalOpen
}"
(click)="closeRegisterModal()"
></div>
<div
id="register-modal"
tabindex="-1"
aria-hidden="true"
[ngClass]="{
'opacity-0 scale-0 pointer-events-none': !isRegisterModalOpen,
'opacity-100 scale-100': isRegisterModalOpen
}"
class="fixed top-0 right-0 left-0 z-50 flex justify-center items-center w-full h-full transition-opacity transition-transform duration-300 ease-in-out"
>
<div class="relative p-4 w-full max-w-md max-h-full">
<!-- Modal content -->
<div
class="relative bg-white rounded-lg shadow dark:bg-gray-700 transition-transform duration-300 ease-in-out"
>
<!-- Modal header -->
<div
class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600"
>
<h3 class="text-xl font-semibold text-gray-900 dark:text-white">
Formulaire d'inscription
</h3>
<button
type="button"
(click)="closeRegisterModal()"
class="end-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white"
>
<svg
class="w-3 h-3"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 14 14"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"
/>
</svg>
<span class="sr-only">Close modal</span>
</button>
</div>
<!-- Modal body -->
<div class="p-4 md:p-5">
<form [formGroup]="userForm" class="space-y-4">
<div>
<label
for="login"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Identifiant</label
>
<input
formControlName="login"
type="text"
name="login"
id="login"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white"
placeholder="ex: captain24"
required
/>
</div>
<div>
<label
for="password"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Mot de passe (6 caractères minimum)</label
>
<input
formControlName="password"
type="password"
name="password"
id="password"
placeholder="••••••••"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white"
required
/>
</div>
<div>
<label
for="verifyPassword"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>Entrez le mot de passe à nouveau</label
>
<input
formControlName="verifyPassword"
type="password"
name="verifyPassword"
id="verifyPassword"
placeholder="••••••••"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white"
required
/>
</div>
<div *ngIf="errorMessage" class="text-red-500 text-sm">
{{ errorMessage }}
</div>
<button
(click)="register()"
class="w-full text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
>
Démarrer l'aventure !
</button>
</form>
</div>
</div>
</div>
</div>

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RegisterPageComponent } from './register-page.component';
describe('RegisterPageComponent', () => {
let component: RegisterPageComponent;
let fixture: ComponentFixture<RegisterPageComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [RegisterPageComponent]
})
.compileComponents();
fixture = TestBed.createComponent(RegisterPageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,91 @@
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import {
FormBuilder,
FormGroup,
FormsModule,
ReactiveFormsModule,
Validators,
} from '@angular/forms';
import { Router } from '@angular/router';
import { User } from '../../model/User';
import { LocalStorageService } from '../../services/localstorage.service';
import { RegisterService } from '../../services/register.service';
@Component({
selector: 'app-register-page',
imports: [FormsModule, ReactiveFormsModule, CommonModule],
templateUrl: './register-page.component.html',
styleUrl: './register-page.component.css',
})
export class RegisterPageComponent {
userForm: FormGroup;
user: User = { login: '', password: '' };
errorMessage: string = '';
isRegisterModalOpen: boolean = false;
constructor(
private registerService: RegisterService,
private fb: FormBuilder,
private localStorageService: LocalStorageService,
private router: Router
) {
this.userForm = this.fb.group(
{
login: [
this.user.login,
[Validators.required, Validators.minLength(3)],
],
password: [
this.user.password,
[Validators.required, Validators.minLength(6)],
],
verifyPassword: ['', [Validators.required]],
},
{ validator: this.passwordMatchValidator }
);
}
passwordMatchValidator(formGroup: FormGroup) {
const password = formGroup.get('password')?.value;
const verifyPassword = formGroup.get('verifyPassword')?.value;
return password === verifyPassword ? null : { mismatch: true };
}
public register() {
if (this.userForm.invalid) {
this.errorMessage =
'Veuillez remplir tous les champs (identifiant de 3 caractères et mot de passe de 6 caractères minimum)';
return;
}
this.user.login = this.userForm.value.login;
this.user.password = this.userForm.value.password;
this.registerService
.register(this.user.login, this.user.password)
.subscribe({
next: (response) => {
console.log('Register OK: ', response);
this.localStorageService.setToken(response.access_token);
this.closeRegisterModal();
setTimeout(() => {
this.router.navigate(['/map']);
}, 1);
},
error: (response) => {
console.log('Register KO: ', response.error.detail);
this.errorMessage = response.error.detail;
},
});
}
openRegisterModal() {
this.isRegisterModalOpen = true;
}
closeRegisterModal() {
this.isRegisterModalOpen = false;
}
}

@ -1,7 +1,6 @@
export interface Monument {
coords: number[];
name: string;
images: string[];
location: number[];
title: string;
files: string[];
description: string;
visited: boolean;
}

@ -1,37 +0,0 @@
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { environment } from '../../environments/environment';
@Injectable({
providedIn: 'root',
})
export class AddPinService {
private apiURL = environment.apiURL;
private token = localStorage.getItem('auth_token');
constructor(private http: HttpClient) {}
addPin(pin: {
title: string;
description: string;
location: string;
files: any[];
}) {
const url = `${this.apiURL}/pin/add`;
const headers = new HttpHeaders({
'Content-Type': 'application/json',
Authorization: 'Bearer ' + this.token,
});
return this.http
.post<any>(
url,
{
title: pin.title,
description: pin.description,
location: pin.location,
files: pin.files,
},
{ headers }
)
.subscribe();
}
}

@ -6,12 +6,12 @@ import { Observable } from 'rxjs';
providedIn: 'root',
})
export class AutocompleteService {
private apiUrl = 'https://nominatim.openstreetmap.org/search';
private apiUrl = 'https://nominatim.openstreetmap.org';
constructor(private http: HttpClient) {}
getAddressSuggestions(query: string): Observable<any> {
return this.http.get(this.apiUrl, {
return this.http.get(this.apiUrl + '/search', {
params: {
q: query,
format: 'json',
@ -20,4 +20,15 @@ export class AutocompleteService {
},
});
}
getAdressCoordinates(query: string): Observable<any> {
return this.http.get(this.apiUrl + '/search', {
params: {
q: query,
format: 'json',
addressdetails: '1',
limit: '1',
},
});
}
}

@ -1,23 +1,22 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
providedIn: 'root',
})
export class LocalStorageService {
private readonly AUTH_TOKEN_KEY = 'auth_token';
private readonly AUTH_TOKEN_KEY = 'auth_token';
constructor() {}
constructor() { }
setToken(token: string): void {
localStorage.setItem(this.AUTH_TOKEN_KEY, token);
}
setToken(token: string): void {
localStorage.setItem(this.AUTH_TOKEN_KEY, token);
}
getToken(): string | null {
return localStorage.getItem(this.AUTH_TOKEN_KEY);
}
getToken(): string | null {
return localStorage.getItem(this.AUTH_TOKEN_KEY);
}
removeToken(): void {
localStorage.removeItem(this.AUTH_TOKEN_KEY);
}
}
removeToken(): void {
localStorage.removeItem(this.AUTH_TOKEN_KEY);
}
}

@ -1,13 +1,13 @@
import { TestBed } from '@angular/core/testing';
import { AddPinService } from './add-pin.service';
import { PinService } from './pin.service';
describe('AddPinService', () => {
let service: AddPinService;
describe('PinService', () => {
let service: PinService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(AddPinService);
service = TestBed.inject(PinService);
});
it('should be created', () => {

@ -0,0 +1,55 @@
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { switchMap } from 'rxjs';
import { environment } from '../../environments/environment';
import { AutocompleteService } from './auto-complete.service';
@Injectable({
providedIn: 'root',
})
export class PinService {
private apiURL = environment.apiURL;
private token = localStorage.getItem('auth_token');
constructor(
private http: HttpClient,
private autoCompleteService: AutocompleteService
) {}
getPins(): any {
const url = `${this.apiURL}/pins`;
const headers = new HttpHeaders({
'Content-Type': 'application/json',
Authorization: 'Bearer ' + this.token,
});
return this.http.get<any>(url, { headers });
}
addPin(pin: {
title: string;
description: string;
location: string;
files: any[];
}) {
const url = `${this.apiURL}/pin/add`;
const headers = new HttpHeaders({
'Content-Type': 'application/json',
Authorization: 'Bearer ' + this.token,
});
return this.autoCompleteService.getAdressCoordinates(pin.location).pipe(
switchMap((response: any) => {
const coords: [string, string] = [response[0].lat, response[0].lon];
return this.http.post<any>(
url,
{
title: pin.title,
description: pin.description,
location: coords,
files: pin.files,
user_id: '',
},
{ headers }
);
})
);
}
}

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { RegisterService } from './register.service';
describe('RegisterService', () => {
let service: RegisterService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(RegisterService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

@ -0,0 +1,17 @@
import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { environment } from '../../environments/environment';
@Injectable({
providedIn: 'root',
})
export class RegisterService {
private apiUrl = environment.apiURL;
constructor(private http: HttpClient) {}
register(username: string, password: string): Observable<any> {
return this.http.post(this.apiUrl + '/register', { username, password });
}
}

@ -1,4 +1,4 @@
export const environment = {
production: false,
apiURL: 'http://127.0.0.1:8000/api/v1',
apiURL: 'https://api.memorymap.fr/api/v1',
};

Loading…
Cancel
Save