Merge branch 'master' into friend

pull/26/head
Maxence JOUANNET 3 weeks ago
commit 37a978c640

@ -15,9 +15,9 @@ import {
} from 'rxjs/operators';
import { AutocompleteService } from '../../services/auto-complete/auto-complete.service';
import { ExifService } from '../../services/exif/exif.service';
import { MapReloadService } from '../../services/map-reload/map-reload.service';
import { PinService } from '../../services/pin/pin.service';
import { DragDropComponent } from '../drag-drop/drag-drop.component';
@Component({
selector: 'app-add-pin-popup',
standalone: true,
@ -36,7 +36,8 @@ export class AddPinPopupComponent implements OnInit {
private fb: FormBuilder,
private autocompleteService: AutocompleteService,
private pinService: PinService,
private exifService: ExifService
private exifService: ExifService,
private mapReloadService: MapReloadService
) {
this.form = this.fb.group({
title: new FormControl(''),
@ -121,7 +122,9 @@ export class AddPinPopupComponent implements OnInit {
};
this.pinService.addPin(pinData).subscribe(() => {
this.mapReloadService.requestReload(); // Demander le rechargement de la carte
this.closePinModal();
this.form.reset(); // Réinitialiser le formulaire après soumission
});
} else {
console.error('Le formulaire est invalide');

@ -0,0 +1,71 @@
<!-- Fond assombri -->
<div
class="fixed inset-0 bg-gray-900 bg-opacity-50 w-full h-full z-40 transition-opacity duration-300 ease-in-out"
[ngClass]="{
'opacity-0 pointer-events-none': !isOpen,
'opacity-100': isOpen
}"
(click)="cancel()"
id="confirm-modal-background"
></div>
<!-- Contenu principal -->
<div
class="fixed inset-0 z-50 flex justify-center items-center w-full h-full"
[ngClass]="{
'opacity-0 scale-50 pointer-events-none': !isOpen,
'opacity-100 scale-100': isOpen
}"
id="confirm-modal"
>
<div
class="bg-white dark:bg-gray-700 rounded-lg shadow p-6 w-full max-w-md transition-transform duration-300 ease-in-out"
>
<!-- Modal header -->
<div
class="flex items-center justify-between border-b rounded-t dark:border-gray-600 mb-6 pb-2"
>
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">
Confirmation
</h2>
<button
type="button"
(click)="closeModal()"
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">Fermer la modal</span>
</button>
</div>
<p class="text-sm text-gray-700 dark:text-gray-300 mb-6">{{ message }}</p>
<div class="flex justify-end space-x-4">
<button
class="px-4 py-2 text-white bg-red-600 hover:bg-red-700 rounded"
(click)="confirm()"
>
Supprimer
</button>
<button
class="px-4 py-2 bg-gray-300 dark:bg-gray-600 text-gray-800 dark:text-white rounded hover:bg-gray-400 dark:hover:bg-gray-500"
(click)="cancel()"
>
Annuler
</button>
</div>
</div>
</div>

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

@ -0,0 +1,75 @@
import { CommonModule } from '@angular/common';
import {
Component,
EventEmitter,
Input,
OnDestroy,
OnInit,
Output,
} from '@angular/core';
import { Subscription } from 'rxjs';
import { ModalService } from '../../services/modal/modal.service';
@Component({
selector: 'app-confirm-modal',
standalone: true,
imports: [CommonModule],
templateUrl: './confirm-modal.component.html',
})
export class ConfirmModalComponent implements OnInit, OnDestroy {
@Input() modalId: string = 'confirm-modal';
@Input() message: string = 'Es-tu sûr de vouloir supprimer ?';
@Output() confirmed = new EventEmitter<void>();
@Output() cancelled = new EventEmitter<void>();
isOpen = false;
private subscription!: Subscription;
constructor(private modalService: ModalService) {}
ngOnInit() {
this.subscription = this.modalService
.getModalState(this.modalId)
.subscribe((state) => {
this.isOpen = state;
if (state) {
setTimeout(() => this.moveModalToBody(), 0);
}
});
}
ngAfterViewInit() {
// Appel initial pour déplacer le modal
this.moveModalToBody();
}
ngOnDestroy() {
this.subscription?.unsubscribe();
}
confirm() {
this.confirmed.emit();
this.modalService.closeModal(this.modalId);
}
cancel() {
this.cancelled.emit();
this.modalService.closeModal(this.modalId);
}
closeModal() {
this.isOpen = false;
this.modalService.closeModal(this.modalId);
}
private moveModalToBody(): void {
const modal = document.getElementById('confirm-modal');
if (modal && modal.parentElement !== document.body) {
document.body.appendChild(modal);
}
const bg = document.getElementById('confirm-modal-background');
if (bg && bg.parentElement !== document.body) {
document.body.appendChild(bg);
}
}
}

@ -31,6 +31,7 @@
'opacity-100': isPinModalOpen
}"
(click)="closePinModal()"
id="pin-modal-background"
></div>
<!-- Main modal -->

@ -25,6 +25,7 @@ import {
import { Pin } from '../../model/Pin';
import { AutocompleteService } from '../../services/auto-complete/auto-complete.service';
import { ExifService } from '../../services/exif/exif.service';
import { MapReloadService } from '../../services/map-reload/map-reload.service';
import { ModalService } from '../../services/modal/modal.service';
import { PinService } from '../../services/pin/pin.service';
import { DragDropComponent } from '../drag-drop/drag-drop.component';
@ -56,7 +57,8 @@ export class EditPinPopupComponent implements OnInit, AfterViewInit, OnDestroy {
private pinService: PinService,
private exifService: ExifService,
private modalService: ModalService,
private router: Router
private router: Router,
private mapReloadService: MapReloadService
) {
// Initialiser le formulaire avec des valeurs par défaut
this.form = this.fb.group({
@ -190,6 +192,10 @@ export class EditPinPopupComponent implements OnInit, AfterViewInit, OnDestroy {
if (modal && modal.parentElement !== document.body) {
document.body.appendChild(modal);
}
const bg = document.getElementById('pin-modal-background');
if (bg && bg.parentElement !== document.body) {
document.body.appendChild(bg);
}
}
selectSuggestion(suggestion: any): void {
@ -248,6 +254,7 @@ export class EditPinPopupComponent implements OnInit, AfterViewInit, OnDestroy {
};
this.pinService.updatePin(this.pin.id, pinData).subscribe(() => {
this.mapReloadService.requestReload(); // Demander le rechargement de la carte
this.closePinModal();
});
} else {

@ -6,6 +6,7 @@ import * as L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import { Pin } from '../../model/Pin';
import { AutocompleteService } from '../../services/auto-complete/auto-complete.service';
import { MapReloadService } from '../../services/map-reload/map-reload.service';
import { PinService } from '../../services/pin/pin.service';
import { PinMarkerComponent } from '../pin-marker/pin-marker.component';
@ -31,12 +32,17 @@ export class LeafletMapComponent implements OnInit {
private pinsService: PinService,
private autocompleteService: AutocompleteService,
private route: ActivatedRoute,
private router: Router
private router: Router,
private mapReloadService: MapReloadService
) {}
ngOnInit(): void {
this.initializeMap();
this.mapReloadService.reload$.subscribe(() => {
this.loadPins(); // recharge les pins quand demandé
});
this.route.queryParams.subscribe((params) => {
const pinId = params['pin'];
if (pinId) {
@ -68,6 +74,12 @@ export class LeafletMapComponent implements OnInit {
this.allPins = pins;
this.extractPersons(pins);
this.renderPins(); // Afficher d'abord les pins sans les filtres
this.loadCountriesForFiltrers(pins); // Ensuite, charger les pays en arrière-plan
});
}
private loadCountriesForFiltrers(pins: Pin[]): void {
const countrySet = new Set<string>();
const requests = pins.map((pin) =>
this.autocompleteService
@ -94,8 +106,6 @@ export class LeafletMapComponent implements OnInit {
Promise.all(requests).then(() => {
this.availableCountries = Array.from(countrySet).sort();
this.renderPins();
});
});
}
@ -210,4 +220,14 @@ export class LeafletMapComponent implements OnInit {
popupAnchor: [0, -24],
});
}
public loadPins(): void {
this.pinsService.getPins().subscribe((pins: Pin[]) => {
this.allPins = pins;
this.extractPersons(pins);
this.renderPins(); // Afficher d'abord les pins sans les filtres
this.loadCountriesForFiltrers(pins); // Ensuite, charger les pays en arrière-plan
});
}
}

@ -124,7 +124,7 @@
Vous n'êtes pas encore inscrit ?
<a
(click)="openRegisterModal()"
class="text-blue-700 hover:underline dark:text-blue-500"
class="text-blue-700 hover:cursor-pointer hover:underline dark:text-blue-500"
>Créer un compte</a
>
</div>

@ -1,3 +1,8 @@
<app-confirm-modal
(confirmed)="handleConfirm()"
(cancelled)="handleCancel()"
></app-confirm-modal>
<div class="flex mb-2 justify-end items-center">
<app-edit-pin-popup [pin]="pin" [modalId]="pin.id"></app-edit-pin-popup>
<button
@ -5,6 +10,7 @@
aria-label="Delete"
(click)="onDelete()"
>
<!-- Modale si demandée -->
<svg
class="w-[1.125rem] h-[1.125rem] text-gray-800"
aria-hidden="true"

@ -2,13 +2,15 @@ import { CommonModule } from '@angular/common';
import { Component, Input } from '@angular/core';
import * as L from 'leaflet';
import { Pin } from '../../model/Pin';
import { ModalService } from '../../services/modal/modal.service';
import { PinService } from '../../services/pin/pin.service';
import { ConfirmModalComponent } from '../confirm-modal/confirm-modal.component';
import { EditPinPopupComponent } from '../edit-pin-popup/edit-pin-popup.component';
@Component({
selector: 'app-pin-marker',
templateUrl: './pin-marker.component.html',
imports: [CommonModule, EditPinPopupComponent],
imports: [CommonModule, EditPinPopupComponent, ConfirmModalComponent],
})
export class PinMarkerComponent {
@Input() pin!: Pin;
@ -16,7 +18,10 @@ export class PinMarkerComponent {
currentIndex: number = 0;
constructor(private pinService: PinService) {}
constructor(
private pinService: PinService,
private modalService: ModalService
) {}
onClosePopup() {
this.marker.closePopup();
@ -29,11 +34,20 @@ export class PinMarkerComponent {
}
onDelete() {
this.modalService.openModal('confirm-modal');
}
handleConfirm() {
this.pinService.deletePin(this.pin.id).subscribe(() => {
this.marker.remove();
this.modalService.closeModal('confirm-modal');
});
}
handleCancel() {
this.modalService.closeModal('confirm-modal');
}
get formattedDescription(): string {
return this.formatDescription(this.pin.description);
}

@ -126,7 +126,7 @@
Déjà un compte ?
<a
(click)="openLoginModal()"
class="text-blue-700 hover:underline dark:text-blue-500"
class="text-blue-700 hover:cursor-pointer hover:underline dark:text-blue-500"
>Se connecter</a
>
</div>

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

@ -0,0 +1,14 @@
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class MapReloadService {
private reloadSubject = new Subject<void>();
public reload$ = this.reloadSubject.asObservable();
requestReload(): void {
this.reloadSubject.next();
}
}
Loading…
Cancel
Save