visiualistation des posts et crud partiellement commencé

This commit is contained in:
Guams 2024-12-28 01:31:19 +01:00
parent a7fed73ee5
commit 06d8bc2b86
34 changed files with 241 additions and 61 deletions

View File

@ -1,14 +1,15 @@
import {Routes} from '@angular/router'; import {Routes} from '@angular/router';
import {LoginComponent} from './login/login.component'; import {LoginComponent} from './pages/login/login.component';
import {HomeComponent} from './home/home.component'; import {HomeComponent} from './pages/home/home.component';
import {RegisterComponent} from './register/register.component'; import {RegisterComponent} from './pages/register/register.component';
import {LogoutComponent} from './logout/logout.component'; import {LogoutComponent} from './pages/logout/logout.component';
import {NotFoundComponent} from './not-found/not-found.component'; import {NotFoundComponent} from './pages/not-found/not-found.component';
import {authGuard} from './guards/auth.guard'; import {authGuard} from './guards/auth.guard';
import {ProfileComponent} from './profile/profile.component'; import {ProfileComponent} from './pages/profile/profile.component';
import {NewPostComponent} from './new-post/new-post.component'; import {NewPostComponent} from './pages/new-post/new-post.component';
import {writerGuard} from './guards/writer.guard'; import {writerGuard} from './guards/writer.guard';
import {PostComponent} from './post/post.component'; import {PostComponent} from './pages/post/post.component';
import {MyPostsComponent} from './pages/my-posts/my-posts.component';
export const routes: Routes = [ export const routes: Routes = [
{path: '', component: HomeComponent}, {path: '', component: HomeComponent},
@ -17,6 +18,7 @@ export const routes: Routes = [
{path: 'logout', component: LogoutComponent}, {path: 'logout', component: LogoutComponent},
{path: 'profile/:authorId', component: ProfileComponent}, {path: 'profile/:authorId', component: ProfileComponent},
{path: 'post/:postId', component: PostComponent}, {path: 'post/:postId', component: PostComponent},
{path: 'my-posts', component: MyPostsComponent, canActivate: [writerGuard]},
{path: 'new-post', component: NewPostComponent, canActivate: [writerGuard]}, {path: 'new-post', component: NewPostComponent, canActivate: [writerGuard]},
{path: '**', component: NotFoundComponent} {path: '**', component: NotFoundComponent}
]; ];

View File

@ -0,0 +1,2 @@
<img src="../../../assets/banner.png" height="494" width="1494"/>
<p-menubar [model]="items"></p-menubar>

View File

@ -3,7 +3,7 @@ import { CookieService } from 'ngx-cookie-service';
import { MenuItem } from 'primeng/api'; import { MenuItem } from 'primeng/api';
import { MenubarModule } from 'primeng/menubar'; import { MenubarModule } from 'primeng/menubar';
import { ToastModule } from 'primeng/toast'; import { ToastModule } from 'primeng/toast';
import { Author } from '../models/author'; import { Author } from '../../models/author';
@Component({ @Component({
selector: 'app-header', selector: 'app-header',
@ -70,8 +70,8 @@ export class HeaderComponent {
label: 'Mes posts', label: 'Mes posts',
icon: 'pi pi-file-edit', icon: 'pi pi-file-edit',
items: [ items: [
{ label: 'Ajouter un post', icon: 'pi pi-plus', routerLink: 'new-post' }, { label: 'Ajouter un post', icon: 'pi pi-plus', routerLink: '/new-post' },
{ label: 'Voir mes posts', icon: 'pi pi-eye', routerLink: ['posts', `${author.id}`] } { label: 'Voir mes posts', icon: 'pi pi-eye', routerLink: '/my-posts' }
] ]
}; };
} }

View File

@ -0,0 +1 @@
<p>post-form works!</p>

View File

@ -0,0 +1,13 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-post-form',
standalone: true,
imports: [],
templateUrl: './post-form.component.html',
styleUrl: './post-form.component.css'
})
export class PostFormComponent {
}

View File

@ -2,7 +2,7 @@
<img src="data:image/jpeg;base64,{{ illustration }}"/> <img src="data:image/jpeg;base64,{{ illustration }}"/>
<h2>{{ title }}</h2> <h2>{{ title }}</h2>
<p>{{ category }}</p> <p>{{ category }}</p>
<p>{{ date | date : "dd/MM/yyyy" }}</p> <em>Publié le {{ date | date : "dd/MM/yyyy à HH:mm" }}</em>
<p>{{ description }}</p> <p>{{ description }}</p>
<p-button routerLink="post/{{ postId }}" label="Lire la suite"/> <p-button routerLink="post/{{ postId }}" label="Lire la suite"/>
</p-card> </p-card>

View File

@ -1,2 +0,0 @@
<img src="assets/banner.png" height="494" width="1494"/>
<p-menubar [model]="items"></p-menubar>

View File

@ -1,27 +1,25 @@
import {Component, OnDestroy} from '@angular/core'; import {Component, OnDestroy} from '@angular/core';
import {AvatarModule} from 'primeng/avatar'; import {AvatarModule} from 'primeng/avatar';
import {Button} from 'primeng/button'; import {Button} from 'primeng/button';
import {AuthorService} from '../services/author.service'; import {AuthorService} from '../../services/author.service';
import {Author} from '../models/author'; import {Author} from '../../models/author';
import {JsonPipe} from '@angular/common'; import {JsonPipe} from '@angular/common';
import {Subscription} from 'rxjs'; import {Subscription} from 'rxjs';
import {MessageService} from 'primeng/api'; import {MessageService} from 'primeng/api';
import {HeaderComponent} from '../header/header.component'; import {HeaderComponent} from '../../components/header/header.component';
import {ToastModule} from 'primeng/toast'; import {ToastModule} from 'primeng/toast';
import {CookieService} from 'ngx-cookie-service'; import {CookieService} from 'ngx-cookie-service';
import {PostService} from '../services/post.service'; import {PostService} from '../../services/post.service';
import {Post} from '../models/post'; import {Post} from '../../models/post';
import {PostHomeComponent} from '../post-home/post-home.component'; import {PostHomeComponent} from '../../components/post-home/post-home.component';
@Component({ @Component({
selector: 'app-home', selector: 'app-home',
standalone: true, standalone: true,
imports: [ imports: [
AvatarModule, AvatarModule,
Button,
HeaderComponent, HeaderComponent,
ToastModule, ToastModule,
JsonPipe,
PostHomeComponent PostHomeComponent
], ],
templateUrl: './home.component.html', templateUrl: './home.component.html',
@ -33,7 +31,6 @@ export class HomeComponent implements OnDestroy {
subs: Subscription[] = [] subs: Subscription[] = []
constructor( constructor(
private messageService: MessageService,
private postService: PostService, private postService: PostService,
private cookieService: CookieService) { private cookieService: CookieService) {
@ -47,16 +44,6 @@ export class HomeComponent implements OnDestroy {
})); }));
} }
successMessage(summary: string, detail: string): void {
this.messageService.add({
severity: 'success',
summary: summary,
detail: detail,
life: 3000,
closable: false
});
}
ngOnDestroy(): void { ngOnDestroy(): void {
this.subs.forEach(sub => sub.unsubscribe()); this.subs.forEach(sub => sub.unsubscribe());
} }

View File

@ -2,14 +2,14 @@ import {ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA, OnDestroy} from '@
import {FormsModule} from '@angular/forms'; import {FormsModule} from '@angular/forms';
import {InputTextModule} from 'primeng/inputtext'; import {InputTextModule} from 'primeng/inputtext';
import {Button} from 'primeng/button'; import {Button} from 'primeng/button';
import {AuthorService} from '../services/author.service'; import {AuthorService} from '../../services/author.service';
import {ToastModule} from 'primeng/toast'; import {ToastModule} from 'primeng/toast';
import {MessageService} from 'primeng/api'; import {MessageService} from 'primeng/api';
import {Author} from '../models/author'; import {Author} from '../../models/author';
import {Subscription, switchMap} from 'rxjs'; import {Subscription, switchMap} from 'rxjs';
import {JsonPipe} from '@angular/common'; import {JsonPipe} from '@angular/common';
import { CookieService } from 'ngx-cookie-service'; import { CookieService } from 'ngx-cookie-service';
import {HeaderComponent} from '../header/header.component'; import {HeaderComponent} from '../../components/header/header.component';
import {Router} from '@angular/router'; import {Router} from '@angular/router';
@Component({ @Component({

View File

@ -1,6 +1,6 @@
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {CookieService} from 'ngx-cookie-service'; import {CookieService} from 'ngx-cookie-service';
import {HeaderComponent} from '../header/header.component'; import {HeaderComponent} from '../../components/header/header.component';
import {Router} from '@angular/router'; import {Router} from '@angular/router';
import {MessageService} from 'primeng/api'; import {MessageService} from 'primeng/api';

View File

@ -0,0 +1,59 @@
<app-header></app-header>
<p-table showGridlines
stripedRows
[value]="posts">
<ng-template pTemplate="header">
<tr>
<th>ID</th>
<th>Titre</th>
<th>Catégorie</th>
<th>Date de publication</th>
<th>Description</th>
<th></th>
<th></th>
<th></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-post let-rowIndex="rowIndex">
<tr>
<td>{{ post.id }}</td>
<td>{{ post.title }}</td>
<td>{{ post.category }}</td>
<td>{{ post.publicationDate | date: "dd/MM/yyyy à HH:mm" }}</td>
<td>{{ post.description }}</td>
<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"
[(visible)]="previewDialogVisibility[rowIndex]">
<app-post-home [title]="post.title"
[description]="post.description"
[category]="post.category"
[date]="post.publicationDate"
[illustration]="post.illustration"/>
</p-dialog>
</td>
<td>
<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]">
</p-dialog>
</td>
<td>
<p-button icon="pi pi-trash" (click)="openDialog(deleteDialogVisibility, rowIndex)" severity="danger"
label="Supprimer"/>
<p-dialog header='Êtes-vous sur de bien vouloir supprimer "{{ post.title }}"' [modal]="true"
[(visible)]="deleteDialogVisibility[rowIndex]">
<p-button label="Annuler"
icon="pi pi-times"
severity="info"
(click)="closeDialog(deleteDialogVisibility, rowIndex)"/>
<p-button (click)="deletePost(post.id, rowIndex)"
label="Oui" icon="pi pi-trash"
severity="danger"/>
</p-dialog>
</td>
</tr>
</ng-template>
</p-table>

View File

@ -0,0 +1,101 @@
import {Component, OnDestroy} from '@angular/core';
import {HeaderComponent} from '../../components/header/header.component';
import {TableModule} from 'primeng/table';
import {CookieService} from 'ngx-cookie-service';
import {AuthorService} from '../../services/author.service';
import {ReplaySubject, Subscription} from 'rxjs';
import {Post} from '../../models/post';
import {Author} from '../../models/author';
import {MessageService} from 'primeng/api';
import {DatePipe} from '@angular/common';
import {Button} from 'primeng/button';
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';
@Component({
selector: 'app-my-posts',
standalone: true,
imports: [
HeaderComponent,
TableModule,
Button,
DatePipe,
DialogModule,
InputTextModule,
PostHomeComponent
],
templateUrl: './my-posts.component.html',
styleUrl: './my-posts.component.css'
})
export class MyPostsComponent implements OnDestroy {
subs: Subscription[] = [];
previewDialogVisibility: boolean[] = [];
updateDialogVisibility: boolean[] = [];
deleteDialogVisibility: boolean[] = [];
posts: Post[] = [];
concernedAuthor: 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.updatePosts();
}
failureMessage(summary: string, detail: string): void {
this.messageService.add({
severity: 'error',
summary: summary,
detail: detail,
life: 3000,
closable: false
});
}
updatePosts(): void {
if (this.cookieService.get('token')) {
this.authorService.getAuthorsPosts(this.concernedAuthor?.id, this.cookieService.get('token')).subscribe({
next: posts => this.posts = posts,
error: error => this.failureMessage("Erreur", error.message),
}
)
}
}
deletePost(id: bigint, rowIndex: number) {
this.postService.deletePost(id, this.cookieService.get('token')).subscribe({
next: (_) => {
this.updatePosts()
this.successMessage("Post supprimé", "Ce post a été supprimé avec succès")
},
error: error => this.failureMessage("Erreur", error.message),
});
this.closeDialog(this.deleteDialogVisibility, rowIndex)
}
successMessage(summary: string, detail: string): void {
this.messageService.add({
severity: 'success',
summary: summary,
detail: detail,
life: 3000,
closable: false
});
}
openDialog(dialogBooleanTab: boolean[], index: number) {
dialogBooleanTab[index] = true;
}
closeDialog(dialogBooleanTab: boolean[], index: number) {
dialogBooleanTab[index] = false;
}
ngOnDestroy(): void {
this.subs.forEach(sub => sub.unsubscribe());
}
}

View File

@ -1,17 +1,17 @@
import {Component, OnDestroy} from '@angular/core'; import {Component, OnDestroy} from '@angular/core';
import {HeaderComponent} from '../header/header.component'; import {HeaderComponent} from '../../components/header/header.component';
import {FormBuilder, FormControl, FormGroup, ReactiveFormsModule, ValidationErrors, Validators} from '@angular/forms'; import {FormBuilder, FormControl, FormGroup, ReactiveFormsModule, ValidationErrors, Validators} from '@angular/forms';
import {InputTextModule} from 'primeng/inputtext'; import {InputTextModule} from 'primeng/inputtext';
import {InputTextareaModule} from 'primeng/inputtextarea'; import {InputTextareaModule} from 'primeng/inputtextarea';
import {FileSelectEvent, FileUploadModule} from 'primeng/fileupload'; import {FileSelectEvent, FileUploadModule} from 'primeng/fileupload';
import {mergeMap, Subscription, switchMap} from 'rxjs'; import {mergeMap, Subscription, switchMap} from 'rxjs';
import {Post} from '../models/post'; import {Post} from '../../models/post';
import {PostService} from '../services/post.service'; import {PostService} from '../../services/post.service';
import {CookieService} from 'ngx-cookie-service'; import {CookieService} from 'ngx-cookie-service';
import {MessageService} from 'primeng/api'; import {MessageService} from 'primeng/api';
import {EditorModule} from 'primeng/editor'; import {EditorModule} from 'primeng/editor';
import {AuthorService} from '../services/author.service'; import {AuthorService} from '../../services/author.service';
import {Author} from '../models/author'; import {Author} from '../../models/author';
import {Router} from '@angular/router'; import {Router} from '@angular/router';
@Component({ @Component({

View File

@ -1,4 +1,4 @@
<app-header></app-header> <app-header></app-header>
<h1>{{ concernedPost?.title }}</h1> <h1>{{ concernedPost?.title }}</h1>
<em>{{ concernedPost?.publicationDate | date: "dd/MM/yyyy" }}</em> <em>Publié le {{ concernedPost?.publicationDate | date: "dd/MM/yyyy à HH:mm" }}</em>
<div [innerHTML]="concernedPost?.body"></div> <div [innerHTML]="concernedPost?.body"></div>

View File

@ -1,19 +1,16 @@
import { Component } from '@angular/core'; import {Component} from '@angular/core';
import {ActivatedRoute} from '@angular/router'; import {ActivatedRoute} from '@angular/router';
import {AuthorService} from '../services/author.service';
import {CookieService} from 'ngx-cookie-service';
import {Subscription} from 'rxjs'; import {Subscription} from 'rxjs';
import {Post} from '../models/post'; import {Post} from '../../models/post';
import {PostService} from '../services/post.service'; import {PostService} from '../../services/post.service';
import {HeaderComponent} from '../header/header.component'; import {HeaderComponent} from '../../components/header/header.component';
import {DatePipe, JsonPipe} from '@angular/common'; import {DatePipe} from '@angular/common';
@Component({ @Component({
selector: 'app-post', selector: 'app-post',
standalone: true, standalone: true,
imports: [ imports: [
HeaderComponent, HeaderComponent,
JsonPipe,
DatePipe DatePipe
], ],
templateUrl: './post.component.html', templateUrl: './post.component.html',

View File

@ -1,10 +1,10 @@
import {Component, OnDestroy} from '@angular/core'; import {Component, OnDestroy} from '@angular/core';
import {HeaderComponent} from '../header/header.component'; import {HeaderComponent} from '../../components/header/header.component';
import {ActivatedRoute, Router} from '@angular/router'; import {ActivatedRoute, Router} from '@angular/router';
import {CookieService} from 'ngx-cookie-service'; import {CookieService} from 'ngx-cookie-service';
import {Author} from '../models/author'; import {Author} from '../../models/author';
import {Subscription} from 'rxjs'; import {Subscription} from 'rxjs';
import {AuthorService} from '../services/author.service'; import {AuthorService} from '../../services/author.service';
import {JsonPipe} from '@angular/common'; import {JsonPipe} from '@angular/common';
import {AvatarModule} from 'primeng/avatar'; import {AvatarModule} from 'primeng/avatar';
import {CardModule} from 'primeng/card'; import {CardModule} from 'primeng/card';

View File

@ -1,5 +1,5 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import {HeaderComponent} from '../header/header.component'; import {HeaderComponent} from '../../components/header/header.component';
@Component({ @Component({
selector: 'app-register', selector: 'app-register',

View File

@ -2,25 +2,27 @@ import {Injectable} from '@angular/core';
import {HttpClient, HttpHeaders} from '@angular/common/http'; import {HttpClient, HttpHeaders} from '@angular/common/http';
import {Observable} from 'rxjs'; import {Observable} from 'rxjs';
import {Author} from '../models/author'; import {Author} from '../models/author';
import {Post} from '../models/post';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class AuthorService { export class AuthorService {
url: string = "http://localhost:8080/api/authors";
constructor(private httpClient: HttpClient) {} constructor(private httpClient: HttpClient) {}
list(): Observable<Author[]> { list(): Observable<Author[]> {
return this.httpClient.get<Author[]>("http://localhost:8080/api/authors"); return this.httpClient.get<Author[]>(this.url);
} }
login(name: string, password: string) { login(name: string, password: string) {
return this.httpClient.post<any>("http://localhost:8080/api/authors/login", {name: name, password: password}) return this.httpClient.post<any>(`${this.url}/login`, {name: name, password: password})
} }
me(token: string): Observable<Author> { me(token: string): Observable<Author> {
const headers = new HttpHeaders().set("Authorization", "Bearer " + token); const headers = new HttpHeaders().set("Authorization", "Bearer " + token);
return this.httpClient.get<Author>("http://localhost:8080/api/authors/me", {'headers': headers}); return this.httpClient.get<Author>(`${this.url}/me`, {'headers': headers});
} }
attributePost(authorId: string | undefined, postId: bigint, token: string) { attributePost(authorId: string | undefined, postId: bigint, token: string) {
@ -29,14 +31,23 @@ export class AuthorService {
'Authorization': `Bearer ${token}` 'Authorization': `Bearer ${token}`
}) })
}; };
return this.httpClient.put<any>(`http://localhost:8080/api/authors/${authorId}/posts`, postId, httpOptions); return this.httpClient.put<any>(`${this.url}/${authorId}/posts`, postId, httpOptions);
} }
getAuthor(id: string | null): Observable<Author> { getAuthor(id: string | null): Observable<Author> {
if (id) { if (id) {
return this.httpClient.get<Author>("http://localhost:8080/api/authors/" + id); return this.httpClient.get<Author>(`${this.url}/${id}`);
} else { } else {
throw new Error("Not Found"); throw new Error("Not Found");
} }
} }
getAuthorsPosts(id: string | undefined, token: string): Observable<Post[]> {
const httpOptions = {
headers: new HttpHeaders({
'Authorization': `Bearer ${token}`
})
};
return this.httpClient.get<Post[]>(`${this.url}/${id}/posts`, httpOptions);
}
} }

View File

@ -18,6 +18,15 @@ export class PostService {
return this.httpClient.get<Post>(`${this.url}/${id}`); return this.httpClient.get<Post>(`${this.url}/${id}`);
} }
deletePost(id: bigint, token: string) {
const httpOptions = {
headers: new HttpHeaders({
'Authorization': `Bearer ${token}`
})
};
return this.httpClient.delete(`${this.url}/${id}`, httpOptions);
}
createPost(post: any, token: string | undefined): Observable<Post> { createPost(post: any, token: string | undefined): Observable<Post> {
const httpOptions = { const httpOptions = {
headers: new HttpHeaders({ headers: new HttpHeaders({

View File

@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>ReviewFront</title> <title>A BON ENTENDEUR</title>
<base href="/"> <base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="icon" type="image/x-icon" href="favicon.ico">