update du nom d'utilisateur ainsi que de la photo de profil
This commit is contained in:
parent
b1b5995ea8
commit
2017b16d4c
@ -46,7 +46,7 @@ export class HeaderComponent {
|
||||
if (author.role === 'WRITER') {
|
||||
return [
|
||||
...commonItems,
|
||||
this.getWriterMenu(author),
|
||||
this.getWriterMenu(),
|
||||
this.getUserMenu(author)
|
||||
];
|
||||
} else if (author.role === 'ADMIN') {
|
||||
@ -65,7 +65,7 @@ export class HeaderComponent {
|
||||
return commonItems;
|
||||
}
|
||||
|
||||
private getWriterMenu(author: Author): MenuItem {
|
||||
private getWriterMenu(): MenuItem {
|
||||
return {
|
||||
label: 'Mes posts',
|
||||
icon: 'pi pi-file-edit',
|
||||
@ -97,7 +97,7 @@ export class HeaderComponent {
|
||||
|
||||
private getUserMenu(author: Author): MenuItem {
|
||||
return {
|
||||
label: author.name.toUpperCase(),
|
||||
label: 'MOI',
|
||||
icon: 'pi pi-user',
|
||||
items: [
|
||||
{ label: 'Voir mon profil', icon: 'pi pi-user', routerLink: [`/profile`, `${author.id}`] },
|
||||
|
@ -16,3 +16,23 @@ form {
|
||||
.send-button {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.add-file-button {
|
||||
margin: 0;
|
||||
font-size: 40px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.pi-plus {
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.add-file-button :hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -17,7 +17,14 @@
|
||||
[showCancelButton]="false"
|
||||
chooseLabel="Sélectionner une image"
|
||||
(onSelect)="onSelected($event)">
|
||||
<ng-template pTemplate="content"></ng-template>
|
||||
<ng-template pTemplate="header"></ng-template>
|
||||
<ng-template let-chooseCallback="chooseCallback" pTemplate="content" let-files>
|
||||
@if (!files?.length) {
|
||||
<div (click)="choose(chooseCallback)" class="add-file-button">
|
||||
<i class="pi pi-plus"></i><span>Ajouter un fichier</span>
|
||||
</div>
|
||||
}
|
||||
</ng-template>
|
||||
</p-fileUpload>
|
||||
|
||||
<label>Contenu du post</label>
|
||||
|
@ -11,6 +11,7 @@ import { EditorModule } from 'primeng/editor';
|
||||
import { Router } from '@angular/router';
|
||||
import {Author} from '../../models/author';
|
||||
import {AuthorService} from '../../services/author.service';
|
||||
import {JsonPipe} from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-post-form',
|
||||
@ -20,7 +21,8 @@ import {AuthorService} from '../../services/author.service';
|
||||
InputTextModule,
|
||||
InputTextareaModule,
|
||||
FileUploadModule,
|
||||
EditorModule
|
||||
EditorModule,
|
||||
JsonPipe
|
||||
],
|
||||
templateUrl: './post-form.component.html',
|
||||
styleUrls: ['./post-form.component.css']
|
||||
@ -91,13 +93,6 @@ export class PostFormComponent implements OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
// transformParagraphs(): void {
|
||||
// if (this.body) {
|
||||
// this.body = this.body.replace(/ /g, ' ');
|
||||
// console.log(this.body);
|
||||
// }
|
||||
// }
|
||||
|
||||
onSubmit(): void {
|
||||
if (this.form.valid && this.uploadedFile) {
|
||||
const formData = this.form.value;
|
||||
@ -143,6 +138,10 @@ export class PostFormComponent implements OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
choose(callback: Function) {
|
||||
callback();
|
||||
}
|
||||
|
||||
successMessage(summary: string, detail: string): void {
|
||||
this.messageService.add({
|
||||
severity: 'success',
|
||||
|
@ -0,0 +1,38 @@
|
||||
.form-container {
|
||||
margin-top: 2em;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.form {
|
||||
width: 100%;
|
||||
max-width: 50em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap:1em;
|
||||
}
|
||||
|
||||
.send-button {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.add-file-button {
|
||||
margin: 0;
|
||||
font-size: 25px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.pi-plus {
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.add-file-button :hover {
|
||||
cursor: pointer;
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<div class="form-container">
|
||||
<form class="form" [formGroup]="form" (ngSubmit)="onSubmit()">
|
||||
<label for="username">Nom d'utilisateur</label>
|
||||
<input [(ngModel)]="username" id="username" type="text" pInputText formControlName="username"/>
|
||||
<label for="password">Mot de passe</label>
|
||||
<input type="password" [(ngModel)]="password" id="password" pInputText formControlName="password"/>
|
||||
<label for="passwordConfirm">Confirmez le mot de passe</label>
|
||||
<input type="password" [(ngModel)]="passwordConfirm" id="passwordConfirm" pInputText
|
||||
formControlName="passwordConfirm"/>
|
||||
<p-fileUpload
|
||||
accept="image/*"
|
||||
maxFileSize="1000000"
|
||||
[showUploadButton]="false"
|
||||
[showCancelButton]="false"
|
||||
chooseLabel="Sélectionner une image"
|
||||
(onSelect)="onSelected($event)">
|
||||
<ng-template pTemplate="header"></ng-template>
|
||||
<ng-template let-chooseCallback="chooseCallback" pTemplate="content" let-files>
|
||||
@if (!files?.length) {
|
||||
<div (click)="choose(chooseCallback)" class="add-file-button">
|
||||
<i class="pi pi-plus"></i><span>Ajouter un fichier</span>
|
||||
</div>
|
||||
}
|
||||
</ng-template>
|
||||
</p-fileUpload>
|
||||
<p-button class="send-button" type="submit" label="Modifier"></p-button>
|
||||
</form>
|
||||
</div>
|
@ -0,0 +1,121 @@
|
||||
import {Component, EventEmitter, Input, OnDestroy, Output} from '@angular/core';
|
||||
import {Button} from 'primeng/button';
|
||||
import {InputTextModule} from 'primeng/inputtext';
|
||||
import {FormBuilder, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||
import {SelectButtonModule} from 'primeng/selectbutton';
|
||||
import {Subscription, switchMap} from 'rxjs';
|
||||
import {AuthorService} from '../../services/author.service';
|
||||
import {MessageService} from 'primeng/api';
|
||||
import {FileSelectEvent, FileUploadModule} from 'primeng/fileupload';
|
||||
import {CookieService} from 'ngx-cookie-service';
|
||||
import {Author} from '../../models/author';
|
||||
import {Router} from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-update-profile',
|
||||
standalone: true,
|
||||
imports: [
|
||||
Button,
|
||||
InputTextModule,
|
||||
ReactiveFormsModule,
|
||||
SelectButtonModule,
|
||||
FileUploadModule
|
||||
],
|
||||
templateUrl: './update-profile-form.component.html',
|
||||
styleUrl: './update-profile-form.component.css'
|
||||
})
|
||||
export class UpdateProfileFormComponent implements OnDestroy {
|
||||
@Input() username: string = "";
|
||||
@Input() authorId: string = "";
|
||||
password: string = "";
|
||||
passwordConfirm: string = "";
|
||||
@Output() updatedAuthorEvent: EventEmitter<Author> = new EventEmitter();
|
||||
uploadedFile: File | undefined;
|
||||
subs: Subscription[] = [];
|
||||
form: FormGroup;
|
||||
|
||||
constructor(private formBuilder: FormBuilder,
|
||||
private authorService: AuthorService,
|
||||
private messageService: MessageService,
|
||||
private cookieService: CookieService,
|
||||
private router: Router,
|
||||
) {
|
||||
this.form = this.formBuilder.group({
|
||||
username: ['', [Validators.required, Validators.maxLength(255)]],
|
||||
password: ['', [Validators.required, Validators.maxLength(50)]],
|
||||
passwordConfirm: ['', [Validators.required, Validators.maxLength(50)]],
|
||||
});
|
||||
}
|
||||
|
||||
onSelected(event: FileSelectEvent): void {
|
||||
if (event.currentFiles && event.currentFiles.length > 0) {
|
||||
this.uploadedFile = event.currentFiles[0];
|
||||
}
|
||||
}
|
||||
|
||||
choose(callback: Function) {
|
||||
callback();
|
||||
}
|
||||
|
||||
successMessage(summary: string, detail: string): void {
|
||||
this.messageService.add({
|
||||
severity: 'success',
|
||||
summary: summary,
|
||||
detail: detail,
|
||||
life: 3000,
|
||||
closable: false
|
||||
});
|
||||
}
|
||||
|
||||
failureMessage(summary: string, detail: string): void {
|
||||
this.messageService.add({
|
||||
severity: 'error',
|
||||
summary: summary,
|
||||
detail: detail,
|
||||
life: 3000,
|
||||
closable: false
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
const token: string | undefined = this.cookieService.get('token');
|
||||
if (this.form.valid && token && this.password === this.passwordConfirm) {
|
||||
const newUsername = this.form.value.username;
|
||||
if (this.uploadedFile) {
|
||||
this.subs.push(this.authorService.updateAuthor(this.authorId, token, newUsername, this.password).pipe(
|
||||
switchMap((_) => {
|
||||
return this.authorService.changeAvatar(this.authorId, this.uploadedFile, token)
|
||||
})
|
||||
).subscribe({
|
||||
next: (author: Author) => {
|
||||
this.successMessage("Mise à jour réussie", "Profil mit à jour avec succès");
|
||||
this.updatedAuthorEvent.emit(author);
|
||||
this.cookieService.set('author', JSON.stringify(author));
|
||||
this.router.navigate(['/']);
|
||||
},
|
||||
error: (err) => {
|
||||
this.failureMessage("Erreur", err.message);
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
this.subs.push(this.authorService.updateAuthor(this.authorId, token, newUsername, this.password).subscribe({
|
||||
next: (author: Author) => {
|
||||
this.successMessage("Mise à jour réussie", "Profil mit à jour avec succès");
|
||||
this.updatedAuthorEvent.emit(author);
|
||||
this.cookieService.set('author', JSON.stringify(author));
|
||||
this.router.navigate(['/']);
|
||||
},
|
||||
error: (err) => {
|
||||
this.failureMessage("Erreur", err.message);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.subs.forEach(sub => sub.unsubscribe())
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
export interface Author {
|
||||
id: string
|
||||
name: string
|
||||
avatar: string
|
||||
profilePicture: string
|
||||
role: string
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
.main-content {
|
||||
margin: auto;
|
||||
width: 45%;
|
||||
padding: 2rem;
|
||||
@media only screen and (min-width: 1080px) {
|
||||
.main-content {
|
||||
margin: auto;
|
||||
width: 45%;
|
||||
padding: 2rem;
|
||||
}
|
||||
}
|
||||
|
@ -47,8 +47,8 @@ export class LoginComponent implements OnDestroy {
|
||||
}))
|
||||
.subscribe({
|
||||
next: (author: Author) => {
|
||||
console.log(author)
|
||||
this.cookieService.set("author", JSON.stringify(author));
|
||||
this.cookieService.set("just-authenticated", "true");
|
||||
this.getAuthorCookie();
|
||||
this.router.navigate(['/']).then(() => {
|
||||
this.successMessage('Connecté avec succès', 'Heureux de vous revoir ' + this.actualAuthor?.name)
|
||||
@ -57,7 +57,6 @@ export class LoginComponent implements OnDestroy {
|
||||
error: (err) => this.failureMessage('Erreur de connexion', err.message)
|
||||
})
|
||||
);
|
||||
|
||||
} else {
|
||||
this.failureMessage('Erreur de connexion', 'Les deux mots de passe ne correspondent pas')
|
||||
}
|
||||
|
@ -19,7 +19,10 @@ export class LogoutComponent implements OnInit{
|
||||
private messageService: MessageService,
|
||||
private router: Router) { }
|
||||
ngOnInit(): void {
|
||||
this.cookiesService.deleteAll("/", "localhost");
|
||||
// this.cookiesService.deleteAll("/", "localhost"); // deleteAll est bugué
|
||||
this.cookiesService.delete('author')
|
||||
this.cookiesService.delete('just-authenticated')
|
||||
this.cookiesService.delete('token')
|
||||
this.router.navigate(['/']).then(() => this.successMessage('Déconnexion', 'Vous avez été deconnecté avec succès'));
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,7 @@
|
||||
.content {
|
||||
margin-top: 5rem;
|
||||
width: 45%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
justify-content: center;
|
||||
gap: 3em;
|
||||
}
|
||||
|
||||
@ -17,7 +14,17 @@ em {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
.user-infos {
|
||||
margin-bottom: 4rem;
|
||||
display: flex;
|
||||
align-self: stretch;
|
||||
justify-content: space-between;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.panel {
|
||||
gap: 0.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
@ -1,21 +1,39 @@
|
||||
<app-header></app-header>
|
||||
|
||||
<div class="content">
|
||||
<div class="user-info">
|
||||
<h2>{{ authorName }}</h2>
|
||||
<em>{{ concernedAuthor?.role }}</em>
|
||||
</div>
|
||||
<div class="profile-picture">
|
||||
@if (concernedAuthor?.avatar) {
|
||||
<p-avatar image="data:image/jpeg;base64,{{ concernedAuthor?.avatar }}" styleClass="mr-2" size="xlarge"></p-avatar>
|
||||
} @else {
|
||||
<p-avatar label="{{ authorName.charAt(0).toUpperCase() }}" styleClass="mr-2" size="xlarge"></p-avatar>
|
||||
}
|
||||
</div>
|
||||
@if (concernedAuthor) {
|
||||
<div class="content">
|
||||
<div class="panel">
|
||||
<div class="user-infos">
|
||||
<div>
|
||||
<h2>{{ authorName }}</h2>
|
||||
<em>{{ concernedAuthor.role }}</em>
|
||||
</div>
|
||||
<div class="profile-picture">
|
||||
@if (concernedAuthor.profilePicture) {
|
||||
<p-avatar image="data:image/jpeg;base64,{{ concernedAuthor.profilePicture }}" shape="circle" styleClass="mr-2"
|
||||
size="xlarge"></p-avatar>
|
||||
} @else {
|
||||
<p-avatar label="{{ authorName.charAt(0).toUpperCase() }}" styleClass="mr-2" size="xlarge"></p-avatar>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<p-button label="Voir les posts de l'utilisateur"></p-button>
|
||||
@if (actualAuthor) {
|
||||
@if (concernedAuthor.id === actualAuthor.id) {
|
||||
<p-button label="Mettre à jour les données du profil" (onClick)="updateProfileDialog=true"/>
|
||||
<p-dialog header='Mettre à jour le profil' [modal]="true" [(visible)]="updateProfileDialog">
|
||||
<app-update-profile (updatedAuthorEvent)="updateAuthor($event)" [authorId]="concernedAuthor.id"
|
||||
[username]="concernedAuthor!.name"></app-update-profile>
|
||||
</p-dialog>
|
||||
<p-button label="Changer le mot de passe" (onClick)="changePasswordDialog=true"/>
|
||||
<p-dialog header='Changer le mot de passe' [modal]="true"
|
||||
[(visible)]="changePasswordDialog">
|
||||
|
||||
<!-- <p-button label="Voir les posts de l'utilisateur"></p-button>-->
|
||||
<!-- @if (concernedAuthor?.id === actualAuthor?.id) {-->
|
||||
<!-- <p-button label="Mettre à jour les données du profil"/>-->
|
||||
<!-- <p-button label="Changer le mot de passe"/>-->
|
||||
<!-- }-->
|
||||
</div>
|
||||
</p-dialog>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<h1>Loading...</h1>
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ import {AuthorService} from '../../services/author.service';
|
||||
import {AvatarModule} from 'primeng/avatar';
|
||||
import {CardModule} from 'primeng/card';
|
||||
import {Button} from 'primeng/button';
|
||||
import {DialogModule} from 'primeng/dialog';
|
||||
import {UpdateProfileFormComponent} from '../../components/update-profile-form/update-profile-form.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-profile',
|
||||
@ -16,7 +18,9 @@ import {Button} from 'primeng/button';
|
||||
HeaderComponent,
|
||||
AvatarModule,
|
||||
CardModule,
|
||||
Button
|
||||
Button,
|
||||
DialogModule,
|
||||
UpdateProfileFormComponent
|
||||
],
|
||||
templateUrl: './profile.component.html',
|
||||
styleUrl: './profile.component.css'
|
||||
@ -26,6 +30,8 @@ export class ProfileComponent implements OnDestroy {
|
||||
concernedAuthor: Author | undefined;
|
||||
authorName: string = "";
|
||||
subs: Subscription[] = [];
|
||||
updateProfileDialog: boolean = false;
|
||||
changePasswordDialog: boolean = false;
|
||||
|
||||
constructor(private route: ActivatedRoute,
|
||||
private authorService: AuthorService,
|
||||
@ -41,6 +47,11 @@ export class ProfileComponent implements OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
updateAuthor(author: Author) {
|
||||
this.concernedAuthor = author;
|
||||
this.actualAuthor = author;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.subs.forEach(sub => sub.unsubscribe());
|
||||
}
|
||||
|
@ -34,6 +34,30 @@ export class AuthorService {
|
||||
return this.httpClient.put<any>(`${this.url}/${authorId}/posts`, postId, httpOptions);
|
||||
}
|
||||
|
||||
updateAuthor(authorId: string, token: string, username: string, password: string): Observable<Author> {
|
||||
const httpOptions = {
|
||||
headers: new HttpHeaders({
|
||||
'Authorization': `Bearer ${token}`
|
||||
})
|
||||
}
|
||||
return this.httpClient.put<Author>(`${this.url}/${authorId}`, {name: username, password: password}, httpOptions);
|
||||
}
|
||||
|
||||
changeAvatar(id: string | undefined, image: File | undefined, token: string) {
|
||||
if (image) {
|
||||
const formData: FormData = new FormData();
|
||||
formData.append('avatar', image);
|
||||
const httpOptions = {
|
||||
headers: new HttpHeaders({
|
||||
'Authorization': `Bearer ${token}`
|
||||
})
|
||||
};
|
||||
return this.httpClient.put<Author>(`${this.url}/${id}/avatar`, formData, httpOptions);
|
||||
} else {
|
||||
throw new Error('Image doesn\'t exist');
|
||||
}
|
||||
}
|
||||
|
||||
getAuthor(id: string | null): Observable<Author> {
|
||||
if (id) {
|
||||
return this.httpClient.get<Author>(`${this.url}/${id}`);
|
||||
|
Loading…
Reference in New Issue
Block a user