Modifification et suppression de ses propre posts (CRUD ! :D)

This commit is contained in:
Guams 2024-12-28 16:50:31 +01:00
parent 06d8bc2b86
commit 15ec7f68d3
18 changed files with 294 additions and 68 deletions

View File

@ -0,0 +1,18 @@
div {
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;
}

View File

@ -1 +1,27 @@
<p>post-form works!</p>
<div>
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<label for="title">Titre du post</label>
<input [(ngModel)]="title" id="title" type="text" pInputText formControlName="title"/>
<label for="category">Catégorie du post</label>
<input [(ngModel)]="category" pInputText id="category" formControlName="category" type="text"/>
<label for="desc">Description du post</label>
<textarea [(ngModel)]="description" formControlName="description" id="desc" pInputTextarea></textarea>
<label>Image descriptive du post</label>
<p-fileUpload accept="image/*"
maxFileSize="1000000"
[showUploadButton]="false"
[showCancelButton]="false"
chooseLabel="Sélectionner une image"
(onSelect)="onSelected($event)">
<ng-template pTemplate="content">
</ng-template>
</p-fileUpload>
<label>Contenu du post</label>
<p-editor [(ngModel)]="body" formControlName="body" [style]="{ height: '320px' }"/>
@if (isUpdateMode) {
<p-button class="send-button" type="submit" label="Mettre à jour"></p-button>
} @else {
<p-button class="send-button" type="submit" label="Ajouter"></p-button>
}
</form>
</div>

View File

@ -1,13 +1,155 @@
import { Component } from '@angular/core';
import { Component, Input, OnDestroy } from '@angular/core';
import { HeaderComponent } from '../../components/header/header.component';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { InputTextModule } from 'primeng/inputtext';
import { InputTextareaModule } from 'primeng/inputtextarea';
import { FileSelectEvent, FileUploadModule } from 'primeng/fileupload';
import {mergeMap, Subscription} from 'rxjs';
import { PostService } from '../../services/post.service';
import { CookieService } from 'ngx-cookie-service';
import { MessageService } from 'primeng/api';
import { EditorModule } from 'primeng/editor';
import { Router } from '@angular/router';
import {Author} from '../../models/author';
import {AuthorService} from '../../services/author.service';
@Component({
selector: 'app-post-form',
standalone: true,
imports: [],
imports: [
ReactiveFormsModule,
InputTextModule,
InputTextareaModule,
FileUploadModule,
EditorModule,
HeaderComponent
],
templateUrl: './post-form.component.html',
styleUrl: './post-form.component.css'
styleUrls: ['./post-form.component.css']
})
export class PostFormComponent {
export class PostFormComponent implements OnDestroy {
@Input() postId: bigint | undefined;
@Input() isUpdateMode: boolean = false;
@Input() actualAuthor: Author | undefined;
@Input() title: string = '';
@Input() category: string = '';
@Input() description: string = '';
@Input() body: string = '';
subs: Subscription[] = [];
form: FormGroup;
uploadedFile: File | undefined;
constructor(
private formBuilder: FormBuilder,
private postService: PostService,
private authorService: AuthorService,
private cookieService: CookieService,
private messageService: MessageService,
private router: Router
) {
this.form = this.formBuilder.group({
description: ['', [Validators.required, Validators.maxLength(512)]],
title: ['', [Validators.required, Validators.maxLength(50)]],
body: ['', [Validators.required]],
category: ['', [Validators.required, Validators.maxLength(50)]],
});
if (this.isUpdateMode && this.postId) {
this.loadPostData();
}
}
loadPostData(): void {
if (this.postId) {
this.subs.push(
this.postService.getPost(this.postId).subscribe({
next: (post) => {
this.form.patchValue({
title: post.title,
description: post.description,
body: post.body,
category: post.category
});
},
error: (err) => {
this.failureMessage('Erreur', `Impossible de charger le post : ${err.message}`);
}
})
);
}
}
onSelected(event: FileSelectEvent): void {
if (event.currentFiles && event.currentFiles.length > 0) {
this.uploadedFile = event.currentFiles[0];
}
}
onSubmit(): void {
if (this.form.valid && this.uploadedFile) {
const formData = this.form.value;
const postData: any = {
title: formData.title,
description: formData.description,
body: formData.body,
category: formData.category
};
if (this.isUpdateMode && this.postId) {
this.subs.push(
this.postService.updatePost(this.postId, postData, this.cookieService.get('token')).pipe(
mergeMap((_) => {
return this.postService.changeIllustration(this.postId, this.uploadedFile, this.cookieService.get('token'));
})
).subscribe({
next: (_) => this.successMessage('Succès', 'Post créé avec succès'),
error: (err) => this.failureMessage('Erreur', err.message)
})
);
} else {
this.subs.push(
this.postService.createPost(postData, this.cookieService.get("token")).pipe(
mergeMap(post =>
this.authorService.attributePost(this.actualAuthor?.id, post.id, this.cookieService.get("token")).pipe(
mergeMap((_) =>
this.postService.changeIllustration(post.id, this.uploadedFile, this.cookieService.get("token"))
)
)
)
).subscribe({
next: () => {
this.router.navigate(['/']).then(() => {
this.successMessage('Succès', 'Post créé avec succès')
});
},
error: (err) => this.failureMessage('Erreur', err.message)
})
);
}
}
}
successMessage(summary: string, detail: string): void {
this.messageService.add({
severity: 'success',
summary,
detail,
life: 3000,
closable: false
});
}
failureMessage(summary: string, detail: string): void {
this.messageService.add({
severity: 'error',
summary,
detail,
life: 3000,
closable: false
});
}
ngOnDestroy(): void {
this.subs.forEach((sub) => sub.unsubscribe());
}
}

View File

@ -1,5 +1,6 @@
<app-header></app-header>
<div class="main-content">
@if (posts.length) {
@for (post of posts; track post.id) {
<app-post-home [illustration]="post.illustration"
[date]="post.publicationDate"
@ -9,4 +10,7 @@
[username]="'Guams'"
[description]="post.description"/>
}
} @else {
<h1>Aucun post n'a été créé pour l'instant</h1>
}
</div>

View File

@ -0,0 +1,18 @@
.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;
}

View File

@ -1,14 +1,18 @@
<app-header></app-header>
<div class="form-container">
<div class="form">
<label for="username">Nom d'utilisateur</label>
<input type="text" id="username" placeholder="Jean Zay" pInputText [(ngModel)]="name"/>
<label for="password">Mot de passe</label>
<input type="password" id="password" placeholder="motDePasseTrèsSecret" pInputText [(ngModel)]="password"/>
<label for="confirm-password">Confirmez le mot de passe</label>
<input type="password" id="confirm-password" placeholder="motDePasseTrèsSecret" pInputText [(ngModel)]="confirmPassword" (keyup.enter)="sendLogins()" />
<input type="password" id="confirm-password" placeholder="motDePasseTrèsSecret" pInputText
[(ngModel)]="confirmPassword" (keyup.enter)="sendLogins()"/>
<p-button
class="send-button"
label="Se connecter"
icon="pi pi-check"
(onClick)="sendLogins()"
/>
{{ actualAuthor | json }}
</div>
</div>

View File

@ -0,0 +1,10 @@
:host ::ng-deep .p-dialog {
width: 800px;
}
.delete-dialog {
display: flex;
justify-content: center;
align-items: center;
gap: 3em;
}

View File

@ -25,7 +25,7 @@
<td>
<p-button icon="pi pi-eye" (click)="openDialog(previewDialogVisibility, rowIndex)" severity="info"
label="Prévisualiser"/>
<p-dialog header='Prévisualisation de "{{ post.title }}"' [modal]="true"
<p-dialog class="preview-dialog" header='Prévisualisation de "{{ post.title }}"' [modal]="true"
[(visible)]="previewDialogVisibility[rowIndex]">
<app-post-home [title]="post.title"
[description]="post.description"
@ -38,6 +38,13 @@
<p-button icon="pi pi-pencil" (click)="openDialog(updateDialogVisibility, rowIndex)" severity="warning"
label="Modifier"/>
<p-dialog header='Modifier "{{ post.title }}"' [modal]="true" [(visible)]="updateDialogVisibility[rowIndex]">
<app-post-form [actualAuthor]="actualAuthor"
[postId]="post.id"
[isUpdateMode]="true"
[title]="post.title"
[category]="post.category"
[description]="post.description"
[body]="post.body"/>
</p-dialog>
</td>
<td>
@ -45,6 +52,7 @@
label="Supprimer"/>
<p-dialog header='Êtes-vous sur de bien vouloir supprimer "{{ post.title }}"' [modal]="true"
[(visible)]="deleteDialogVisibility[rowIndex]">
<div class="delete-dialog">
<p-button label="Annuler"
icon="pi pi-times"
severity="info"
@ -52,6 +60,7 @@
<p-button (click)="deletePost(post.id, rowIndex)"
label="Oui" icon="pi pi-trash"
severity="danger"/>
</div>
</p-dialog>
</td>
</tr>

View File

@ -13,6 +13,7 @@ import {DialogModule} from 'primeng/dialog';
import {InputTextModule} from 'primeng/inputtext';
import {PostHomeComponent} from '../../components/post-home/post-home.component';
import {PostService} from '../../services/post.service';
import {PostFormComponent} from "../../components/post-form/post-form.component";
@Component({
selector: 'app-my-posts',
@ -24,7 +25,8 @@ import {PostService} from '../../services/post.service';
DatePipe,
DialogModule,
InputTextModule,
PostHomeComponent
PostHomeComponent,
PostFormComponent
],
templateUrl: './my-posts.component.html',
styleUrl: './my-posts.component.css'
@ -35,14 +37,14 @@ export class MyPostsComponent implements OnDestroy {
updateDialogVisibility: boolean[] = [];
deleteDialogVisibility: boolean[] = [];
posts: Post[] = [];
concernedAuthor: Author | undefined;
actualAuthor: Author | undefined;
constructor(private cookieService: CookieService,
private postService: PostService,
private authorService: AuthorService,
private messageService: MessageService) {
this.concernedAuthor = this.cookieService.get('author') ? JSON.parse(this.cookieService.get('author')) : undefined;
this.actualAuthor = this.cookieService.get('author') ? JSON.parse(this.cookieService.get('author')) : undefined;
this.updatePosts();
}
@ -58,7 +60,7 @@ export class MyPostsComponent implements OnDestroy {
updatePosts(): void {
if (this.cookieService.get('token')) {
this.authorService.getAuthorsPosts(this.concernedAuthor?.id, this.cookieService.get('token')).subscribe({
this.authorService.getAuthorsPosts(this.actualAuthor?.id, this.cookieService.get('token')).subscribe({
next: posts => this.posts = posts,
error: error => this.failureMessage("Erreur", error.message),
}

View File

@ -1,21 +1,2 @@
<app-header></app-header>
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<label for="title">Titre du post</label>
<input id="title" type="text" pInputText formControlName="title"/>
<label for="category">Catégorie du post</label>
<input pInputText id="category" formControlName="category" type="text"/>
<label for="desc">Description du post</label>
<textarea formControlName="description" id="desc" pInputTextarea></textarea>
<label>Image descriptive du post</label>
<p-fileUpload accept="image/*"
maxFileSize="1000000"
[showUploadButton]="false"
[showCancelButton]="false"
chooseLabel="Sélectionner une image"
(onSelect)="onSelected($event)">
<ng-template pTemplate="content">
</ng-template>
</p-fileUpload>
<p-editor formControlName="body" [style]="{ height: '320px' }"/>
<p-button type="submit" label="envoyer" ></p-button>
</form>
<app-post-form [actualAuthor]="actualAuthor"/>

View File

@ -13,6 +13,7 @@ import {EditorModule} from 'primeng/editor';
import {AuthorService} from '../../services/author.service';
import {Author} from '../../models/author';
import {Router} from '@angular/router';
import {PostFormComponent} from '../../components/post-form/post-form.component';
@Component({
selector: 'app-new-post',
@ -23,7 +24,8 @@ import {Router} from '@angular/router';
InputTextModule,
InputTextareaModule,
FileUploadModule,
EditorModule
EditorModule,
PostFormComponent
],
templateUrl: './new-post.component.html',
styleUrl: './new-post.component.css'

View File

@ -27,6 +27,16 @@ export class PostService {
return this.httpClient.delete(`${this.url}/${id}`, httpOptions);
}
updatePost(postId: bigint, postData: any, token: string) {
const httpOptions = {
headers: new HttpHeaders({
'Authorization': `Bearer ${token}`
})
};
return this.httpClient.put(`${this.url}/${postId}`, postData, httpOptions);
}
createPost(post: any, token: string | undefined): Observable<Post> {
const httpOptions = {
headers: new HttpHeaders({
@ -36,7 +46,7 @@ export class PostService {
return this.httpClient.post<Post>(this.url, post, httpOptions);
}
changeIllustration(id: bigint, image: File | undefined, token: string): Observable<Post> {
changeIllustration(id: bigint | undefined, image: File | undefined, token: string): Observable<Post> {
if (image) {
const formData: FormData = new FormData();
formData.append('illustration', image);