avancement tuto
continuous-integration/drone/push Build is passing Details

tutorial
Maxence JOUANNET 7 days ago
parent cb62ad1605
commit ec59956625

@ -67,7 +67,7 @@
<!-- Modal body --> <!-- Modal body -->
<div class="p-4 md:p-5"> <div class="p-4 md:p-5">
<form class="grid gap-6 mb-1 md:grid-cols-2" [formGroup]="form"> <form class="grid gap-6 mb-1 md:grid-cols-2" [formGroup]="form">
<div> <div id="add-pin-modal-title">
<label <label
for="title" for="title"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
@ -83,7 +83,7 @@
/> />
</div> </div>
<div> <div id="add-pin-modal-localisation">
<label <label
for="localisation" for="localisation"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
@ -112,7 +112,7 @@
</ul> </ul>
</div> </div>
<div> <div id="add-pin-modal-image">
<label <label
for="files" for="files"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
@ -123,7 +123,7 @@
></app-drag-drop> ></app-drag-drop>
</div> </div>
<div> <div id="add-pin-modal-description">
<label <label
for="description" for="description"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
@ -158,7 +158,7 @@
</div> </div>
</div> --> </div> -->
<div> <div id="add-pin-modal-date">
<label <label
for="date" for="date"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
@ -182,6 +182,7 @@
Annuler Annuler
</button> </button>
<button <button
id="add-pin-modal-validate"
type="submit" type="submit"
(click)="submitForm()" (click)="submitForm()"
class="w-1/2 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" class="w-1/2 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"

@ -55,7 +55,7 @@ export class AddPinPopupComponent implements OnInit {
}); });
} }
onFocus(): void { onFocus(): void {
this.inputFocused = true; this.inputFocused = true;
} }

@ -47,8 +47,8 @@
</div> </div>
<!-- Barre de recherche --> <!-- Barre de recherche -->
<div class="p-4"> <div class="p-4" id="friend-search-bar">
<input <input
type="text" type="text"
id="search-friends" id="search-friends"
class="w-full p-2 border rounded-lg dark:bg-gray-700 dark:text-white" class="w-full p-2 border rounded-lg dark:bg-gray-700 dark:text-white"
@ -97,7 +97,7 @@
</div> </div>
</div> </div>
<div class="p-4 space-y-3"> <div class="p-4 space-y-3" id="friend-list">
<p class="dark:text-white">Amis</p> <p class="dark:text-white">Amis</p>
<div <div
*ngIf="hasNoAcceptedFriends()" *ngIf="hasNoAcceptedFriends()"

@ -1,17 +1,18 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { Component, OnInit } from '@angular/core'; import { Component, OnInit, OnDestroy } from '@angular/core';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { debounceTime, distinctUntilChanged, Subject } from 'rxjs'; import { debounceTime, distinctUntilChanged, Subject, Subscription } from 'rxjs';
import { FriendsService } from '../../services/friends/friends.service'; import { FriendsService } from '../../services/friends/friends.service';
import { LocalStorageService } from '../../services/local-storage/local-storage.service'; import { LocalStorageService } from '../../services/local-storage/local-storage.service';
import { UserService } from '../../services/user/user.service'; import { UserService } from '../../services/user/user.service';
import { ModalService } from '../../services/modal/modal.service';
@Component({ @Component({
selector: 'app-friend-page', selector: 'app-friend-page',
imports: [CommonModule, FormsModule], imports: [CommonModule, FormsModule],
templateUrl: './friend-page.component.html', templateUrl: './friend-page.component.html',
}) })
export class FriendPageComponent implements OnInit { export class FriendPageComponent implements OnInit, OnDestroy {
protected listFriend: { protected listFriend: {
username: string; username: string;
status: string; status: string;
@ -27,19 +28,25 @@ export class FriendPageComponent implements OnInit {
userId: string = ''; userId: string = '';
status: string = ''; status: string = '';
isFriendModalOpen: boolean = false; isFriendModalOpen: boolean = false;
hasAcceptedFriends: boolean = false;
hasPendingFriends: boolean = false;
searchTerm: string = ''; searchTerm: string = '';
searchTermChanged = new Subject<string>(); searchTermChanged = new Subject<string>();
modalId: string = 'friend-modal';
private modalSub!: Subscription;
constructor( constructor(
private friendService: FriendsService, private friendService: FriendsService,
private userService: UserService, private userService: UserService,
private localStorage: LocalStorageService private localStorage: LocalStorageService,
private modalService: ModalService
) {} ) {}
ngOnInit(): void { ngOnInit(): void {
this.getFriendData(); this.getFriendData();
this.modalSub = this.modalService.getModalState(this.modalId).subscribe(open => {
this.isFriendModalOpen = open;
});
this.searchTermChanged this.searchTermChanged
.pipe(debounceTime(200), distinctUntilChanged()) .pipe(debounceTime(200), distinctUntilChanged())
.subscribe((username: string) => { .subscribe((username: string) => {
@ -47,6 +54,10 @@ export class FriendPageComponent implements OnInit {
}); });
} }
ngOnDestroy(): void {
this.modalSub.unsubscribe();
}
protected searchUser(username: string) { protected searchUser(username: string) {
this.searchTerm = username; this.searchTerm = username;
if (this.searchTerm) { if (this.searchTerm) {
@ -118,7 +129,6 @@ export class FriendPageComponent implements OnInit {
onAcceptOrDeny(id: string, choice: string) { onAcceptOrDeny(id: string, choice: string) {
if (choice == 'accept') { if (choice == 'accept') {
// If return code is 200, then the friend has been accepted so we can change the status of the friend to accepted
this.friendService.acceptFriendById(id).subscribe((data: any) => { this.friendService.acceptFriendById(id).subscribe((data: any) => {
if (data.message == 'Friend request accepted') { if (data.message == 'Friend request accepted') {
this.listFriend.forEach((friend) => { this.listFriend.forEach((friend) => {
@ -129,7 +139,6 @@ export class FriendPageComponent implements OnInit {
} }
}); });
} else { } else {
// If return code is 200, then the friend has been denied so we can delete the friend from the list
this.friendService.denyFriendById(id).subscribe((data: any) => { this.friendService.denyFriendById(id).subscribe((data: any) => {
if (data.message == 'Friend request denied') { if (data.message == 'Friend request denied') {
this.listFriend.forEach((friend, index) => { this.listFriend.forEach((friend, index) => {
@ -143,11 +152,11 @@ export class FriendPageComponent implements OnInit {
} }
openFriendModal() { openFriendModal() {
this.isFriendModalOpen = true; this.modalService.openModal(this.modalId);
} }
closeFriendModal() { closeFriendModal() {
this.isFriendModalOpen = false; this.modalService.closeModal(this.modalId);
} }
deleteFriend(id: string) { deleteFriend(id: string) {

@ -190,7 +190,7 @@
</li> </li>
<li id="add"> <li id="add">
<app-add-pin-popup></app-add-pin-popup> <app-add-pin-popup></app-add-pin-popup>
</li> </li>
<li id="friend"> <li id="friend">
<app-friend-page></app-friend-page> <app-friend-page></app-friend-page>
</li> </li>

@ -82,12 +82,11 @@ export class NavbarComponent implements OnInit {
} }
}); });
// Initialise la barre de recherche avec debounce et distinctUntilChanged
this.searchForm this.searchForm
.get('searchControl') .get('searchControl')
?.valueChanges.pipe( ?.valueChanges.pipe(
debounceTime(300), // Attendre 300ms après la dernière frappe debounceTime(300),
distinctUntilChanged(), // Ignorer si la nouvelle valeur est la même que la précédente distinctUntilChanged(),
switchMap((searchTerm) => { switchMap((searchTerm) => {
const trimmedQuery = searchTerm?.trim(); const trimmedQuery = searchTerm?.trim();
if (trimmedQuery && trimmedQuery.length > 1) { if (trimmedQuery && trimmedQuery.length > 1) {
@ -131,11 +130,11 @@ export class NavbarComponent implements OnInit {
clickSuggestion(pin: Pin): void { clickSuggestion(pin: Pin): void {
this.searchForm.reset(); this.searchForm.reset();
const queryParams = { pin: pin.id }; // Remplacer "id" par la bonne propriété si nécessaire const queryParams = { pin: pin.id };
this.router.navigate([], { this.router.navigate([], {
relativeTo: this.route, relativeTo: this.route,
queryParams: queryParams, queryParams: queryParams,
queryParamsHandling: 'merge', // Conserve les autres paramètres de requête queryParamsHandling: 'merge',
}); });
} }
@ -146,7 +145,7 @@ export class NavbarComponent implements OnInit {
onBlur(): void { onBlur(): void {
setTimeout(() => { setTimeout(() => {
this.inputFocus = false; this.inputFocus = false;
}, 200); // Petit délai pour laisser l'utilisateur cliquer }, 200);
} }
public logout() { public logout() {

@ -1,41 +1,110 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import introJs from 'intro.js'; import introJs from 'intro.js';
import { ModalService } from '../modal/modal.service';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class IntroService { export class IntroService {
private intro = introJs(); constructor(private modalService: ModalService) {}
constructor() {} private sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
startIntro() { }
this.intro.setOptions({
steps: [ async startIntro() {
{ await new Promise<void>((resolve) => {
intro: 'Welcome to the web let me show you around!' const intro = introJs();
}, intro.setOptions({
{ tooltipClass: 'custom-tooltip-with-avatar',
element: '#timeline', steps: [
intro: 'This is the logo of Memory Map.' { intro: 'Welcome to the web let me show you around!' },
}, { element: '#timeline', intro: 'This is the logo of Memory Map.' },
{ { element: '#quete', intro: 'Click here to open the search bar on mobile.' },
element: '#quete', ],
intro: 'Click here to open the search bar on mobile.' exitOnOverlayClick: false,
}, disableInteraction: false,
{ });
element: '#add',
intro: 'This button toggles the mobile navigation menu.' intro.oncomplete(() => {
}, resolve();
{ });
element: '#friend',
intro: 'Here is the main navigation menu.' intro.start();
}, });
], await this.sleep(300);
exitOnOverlayClick: false,
disableInteraction: false, await new Promise<void>((resolve) => {
const intro = introJs();
intro.setOptions({
tooltipClass: 'custom-tooltip-with-avatar',
steps: [
{ element: '#add', intro: 'salut voici le bouton' },
{ element: '#add-pin-modal-title', intro: 'salut voici le bouton' },
{ element: '#add-pin-modal-localisation', intro: 'Here is the modal to add a new pin!' },
{ element: '#add-pin-modal-image', intro: 'Here is the modal to add a new pin!' },
{ element: '#add-pin-modal-description', intro: 'Here is the modal to add a new pin!' },
{ element: '#add-pin-modal-date', intro: 'Here is the modal to add a new pin!' },
{ element: '#add-pin-modal-validate', intro: 'Here is the modal to add a new pin!' },
],
exitOnOverlayClick: false,
disableInteraction: false,
});
intro.onchange(async (element) => {
if (element?.id === 'add-pin-modal-title') {
this.modalService.openModal('add-pin-modal');
await this.sleep(300);
}
});
intro.onexit(() => {
this.modalService.closeModal('add-pin-modal');
resolve();
});
intro.oncomplete(() => {
this.modalService.closeModal('add-pin-modal');
resolve();
});
intro.start();
}); });
this.intro.start(); await this.sleep(300);
await new Promise<void>((resolve) => {
const intro = introJs();
intro.setOptions({
tooltipClass: 'custom-tooltip-with-avatar',
steps: [
{ element: '#friend', intro: 'Here is the main navigation menu.' },
{ element: '#friend-search-bar', intro: 'Here is the main navigation menu.' },
{ element: '#friend-list', intro: 'Here is the main navigation menu.' }
],
exitOnOverlayClick: false,
disableInteraction: false,
});
intro.onchange(async (element) => {
if (element?.id === 'friend-search-bar') {
this.modalService.openModal('friend-modal');
await this.sleep(300);
}
});
intro.onexit(() => {
this.modalService.closeModal('friend-modal');
resolve();
});
intro.oncomplete(() => {
this.modalService.closeModal('friend-modal');
resolve();
});
intro.start();
});
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

@ -35,8 +35,22 @@
} }
} }
.custom-center-tooltip { .custom-tooltip-with-avatar {
z-index: 9999; position: relative;
padding-left: 60px !important;
}
.custom-tooltip-with-avatar::before {
content: "";
position: absolute;
left: 10px;
top: 10px;
width: 80px;
height: 80px;
background-image: url('assets/perso.png');
background-size: contain;
background-repeat: no-repeat;
border-radius: 50%;
} }

Loading…
Cancel
Save