Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
47c769813d |
README.mdangular.jsonpackage.json
src
app
app.component.htmlapp.component.tsapp.config.tsauth.service.tspreset.ts
components
comment-form
header
loading
modal
preview-modal
update-modal
post-form
post-home
register-form
update-profile-form
guards
pages
admin-authors
home
login
logout
my-posts
new-post
post
profile
services
assets
index.htmlstyles.css@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
## Liste des améliorations
|
## Liste des améliorations
|
||||||
|
|
||||||
|
- [ ] Faire du sessionStorage à la place d'un stockage avec cookie
|
||||||
- [ ] Bouton "Voir les posts de l'utilisateur" à enlever (un seul écrivain donc inutile...)
|
- [ ] Bouton "Voir les posts de l'utilisateur" à enlever (un seul écrivain donc inutile...)
|
||||||
- [ ] Bug CSS concernant le footer
|
- [ ] Bug CSS concernant le footer
|
||||||
- [ ] Mauvaise actualisation du pseudo quand on se renomme
|
- [ ] Mauvaise actualisation du pseudo quand on se renomme
|
||||||
@ -10,10 +11,10 @@
|
|||||||
- [ ] Garder l'avatar de l'utilisateur quand il met à jour uniquement son pseudo
|
- [ ] Garder l'avatar de l'utilisateur quand il met à jour uniquement son pseudo
|
||||||
- [ ] Ne pas avoir à confirmer son mot de passe lors de la connexion
|
- [ ] Ne pas avoir à confirmer son mot de passe lors de la connexion
|
||||||
- [ ] Pouvoir modifier son commentaire
|
- [ ] Pouvoir modifier son commentaire
|
||||||
- [x] L'avatar s'affiche pas quand on upload un commentaire (il faut recharger la page)
|
- [ ] L'avatar s'affiche pas quand on upload un commentaire (il faut recharger la page)
|
||||||
- [ ] Faire des meilleurs modal
|
- [ ] Faire des meilleurs modal
|
||||||
- [ ] Terminer l'interface admin
|
- [ ] Terminer l'interface admin
|
||||||
- [x] Bug (de temps en temps) pour stocker les données utilisateur
|
- [ ] Bug (de temps en temps) pour stocker les cookies utilisateur
|
||||||
|
|
||||||
pour run le docker :
|
pour run le docker :
|
||||||
```
|
```
|
||||||
|
@ -28,10 +28,14 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
"@angular/material/prebuilt-themes/azure-blue.css",
|
|
||||||
"src/styles.css"
|
"src/styles.css"
|
||||||
],
|
],
|
||||||
"scripts": []
|
"scripts": [],
|
||||||
|
"server": "src/main.server.ts",
|
||||||
|
"prerender": true,
|
||||||
|
"ssr": {
|
||||||
|
"entry": "server.ts"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
@ -93,7 +97,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
"@angular/material/prebuilt-themes/magenta-violet.css",
|
|
||||||
"src/styles.css"
|
"src/styles.css"
|
||||||
],
|
],
|
||||||
"scripts": []
|
"scripts": []
|
||||||
|
11
package.json
11
package.json
@ -12,23 +12,20 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^18.2.0",
|
"@angular/animations": "^18.2.0",
|
||||||
"@angular/cdk": "^18.2.14",
|
|
||||||
"@angular/common": "^18.2.0",
|
"@angular/common": "^18.2.0",
|
||||||
"@angular/compiler": "^18.2.0",
|
"@angular/compiler": "^18.2.0",
|
||||||
"@angular/core": "^18.2.0",
|
"@angular/core": "^18.2.0",
|
||||||
"@angular/forms": "^18.2.0",
|
"@angular/forms": "^18.2.0",
|
||||||
"@angular/material": "^18.2.14",
|
|
||||||
"@angular/platform-browser": "^18.2.0",
|
"@angular/platform-browser": "^18.2.0",
|
||||||
"@angular/platform-browser-dynamic": "^18.2.0",
|
"@angular/platform-browser-dynamic": "^18.2.0",
|
||||||
"@angular/platform-server": "^18.2.0",
|
"@angular/platform-server": "^18.2.0",
|
||||||
"@angular/router": "^18.2.0",
|
"@angular/router": "^18.2.0",
|
||||||
"@angular/ssr": "^18.2.18",
|
"@angular/ssr": "^18.2.12",
|
||||||
"@primeng/themes": "^19.1.0",
|
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"luxon": "^3.5.0",
|
"luxon": "^3.5.0",
|
||||||
"ngx-cookie-service": "^18.0.0",
|
"ngx-cookie-service": "^18.0.0",
|
||||||
"primeicons": "^7.0.0",
|
"primeicons": "^7.0.0",
|
||||||
"primeng": "^18.0.2",
|
"primeng": "^17.18.10",
|
||||||
"quill": "^2.0.3",
|
"quill": "^2.0.3",
|
||||||
"review-front": "file:",
|
"review-front": "file:",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
@ -36,8 +33,8 @@
|
|||||||
"zone.js": "~0.14.10"
|
"zone.js": "~0.14.10"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^18.2.18",
|
"@angular-devkit/build-angular": "^18.2.12",
|
||||||
"@angular/cli": "^18.2.18",
|
"@angular/cli": "^18.2.12",
|
||||||
"@angular/compiler-cli": "^18.2.0",
|
"@angular/compiler-cli": "^18.2.0",
|
||||||
"@types/express": "^4.17.17",
|
"@types/express": "^4.17.17",
|
||||||
"@types/jasmine": "~5.1.0",
|
"@types/jasmine": "~5.1.0",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
@if (isBrowser() && (authService.isSessionExpired() && authService.isAuthenticated())) {
|
@if (isBrowser()) {
|
||||||
<p-dialog header="ATTENTION !" [modal]="true" [closable]="false" [visible]="isSessionExpired">
|
<p-dialog header="ATTENTION !" [modal]="true" [closable]="false" [visible]="isSessionExpired">
|
||||||
<span>Votre session a <strong>expiré</strong> ! Il va falloir se reconnecter.</span>
|
<span>Votre session a <strong>expiré</strong> ! Il va falloir se reconnecter.</span>
|
||||||
<div class="expired-dialog">
|
<div class="expired-dialog">
|
||||||
|
@ -7,12 +7,12 @@ import {DialogModule} from 'primeng/dialog';
|
|||||||
import {isPlatformBrowser} from '@angular/common';
|
import {isPlatformBrowser} from '@angular/common';
|
||||||
import {Button} from 'primeng/button';
|
import {Button} from 'primeng/button';
|
||||||
import {AuthService} from './auth.service';
|
import {AuthService} from './auth.service';
|
||||||
import {Router, RouterOutlet} from '@angular/router';
|
import {CookieService} from 'ngx-cookie-service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [MenubarModule, FloatLabelModule, ToastModule, DialogModule, Button, RouterOutlet],
|
imports: [MenubarModule, FloatLabelModule, ToastModule, DialogModule, Button],
|
||||||
providers: [
|
providers: [
|
||||||
MessageService,
|
MessageService,
|
||||||
],
|
],
|
||||||
@ -23,18 +23,17 @@ export class AppComponent implements OnInit {
|
|||||||
isSessionExpired: boolean = false;
|
isSessionExpired: boolean = false;
|
||||||
|
|
||||||
constructor(@Inject(PLATFORM_ID) private platformId: object,
|
constructor(@Inject(PLATFORM_ID) private platformId: object,
|
||||||
protected authService: AuthService,
|
private authService: AuthService,
|
||||||
private router: Router,) {
|
private cookieService: CookieService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
isBrowser(): boolean {
|
isBrowser(): boolean {
|
||||||
return isPlatformBrowser(this.platformId)
|
return isPlatformBrowser(this.platformId);
|
||||||
}
|
}
|
||||||
|
|
||||||
setSessionExpiredFalse(): void {
|
setSessionExpiredFalse(): void {
|
||||||
this.isSessionExpired = false;
|
this.isSessionExpired = false;
|
||||||
this.authService.setSessionExpired(false);
|
this.authService.setSessionExpired(false);
|
||||||
this.router.navigate(['/logout']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import {ApplicationConfig, importProvidersFrom, provideZoneChangeDetection} from '@angular/core';
|
import {ApplicationConfig, importProvidersFrom, provideZoneChangeDetection} from '@angular/core';
|
||||||
import {provideRouter} from '@angular/router';
|
import {provideRouter} from '@angular/router';
|
||||||
|
|
||||||
import {routes} from './app.routes';
|
import {routes} from './app.routes';
|
||||||
import {provideClientHydration} from '@angular/platform-browser';
|
import {provideClientHydration} from '@angular/platform-browser';
|
||||||
import {provideHttpClient, withFetch} from '@angular/common/http';
|
import {provideHttpClient, withFetch} from '@angular/common/http';
|
||||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||||
import {provideAnimationsAsync} from '@angular/platform-browser/animations/async';
|
|
||||||
import {providePrimeNG} from 'primeng/config';
|
|
||||||
import {myPreset} from './preset'
|
|
||||||
|
|
||||||
export const appConfig: ApplicationConfig = {
|
export const appConfig: ApplicationConfig = {
|
||||||
providers: [
|
providers: [
|
||||||
@ -14,14 +12,5 @@ export const appConfig: ApplicationConfig = {
|
|||||||
provideRouter(routes),
|
provideRouter(routes),
|
||||||
provideClientHydration(),
|
provideClientHydration(),
|
||||||
provideHttpClient(withFetch()),
|
provideHttpClient(withFetch()),
|
||||||
provideAnimationsAsync(),
|
importProvidersFrom([BrowserAnimationsModule])]
|
||||||
providePrimeNG({
|
|
||||||
theme: {
|
|
||||||
preset: myPreset,
|
|
||||||
options: {
|
|
||||||
darkModeSelector: '.my-app-dark' // class css pour activer le dark mode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
importProvidersFrom([BrowserAnimationsModule]), provideAnimationsAsync()]
|
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {Injectable} from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import {Author} from './models/author';
|
import { CookieService } from 'ngx-cookie-service';
|
||||||
import {BehaviorSubject} from 'rxjs';
|
import { Author } from './models/author';
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
import {DateTime} from 'luxon';
|
import {DateTime} from 'luxon';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
@ -10,38 +11,33 @@ export class AuthService {
|
|||||||
private sessionExpiredSubject = new BehaviorSubject<boolean>(false);
|
private sessionExpiredSubject = new BehaviorSubject<boolean>(false);
|
||||||
sessionExpired$ = this.sessionExpiredSubject.asObservable();
|
sessionExpired$ = this.sessionExpiredSubject.asObservable();
|
||||||
|
|
||||||
constructor() {
|
constructor(private cookieService: CookieService) {
|
||||||
this.checkSessionExpiration();
|
this.checkSessionExpiration();
|
||||||
}
|
}
|
||||||
|
|
||||||
isAuthenticated(): boolean {
|
isAuthenticated(): boolean {
|
||||||
return sessionStorage.getItem("author") !== null &&
|
return this.cookieService.check("author") &&
|
||||||
sessionStorage.getItem("token") !== null &&
|
this.cookieService.check("token") &&
|
||||||
sessionStorage.getItem("token-expiration-date") !== null;
|
this.cookieService.check("token-expiration-date") &&
|
||||||
|
this.cookieService.get("author") !== '' &&
|
||||||
|
this.cookieService.get("token-expiration-date") !== '' &&
|
||||||
|
this.cookieService.get("token") !== '';
|
||||||
}
|
}
|
||||||
|
|
||||||
getTokenExpirationDate(): string | null {
|
getTokenExpirationDate(): DateTime {
|
||||||
return sessionStorage.getItem("token-expiration-date");
|
return DateTime.fromISO(this.cookieService.get("token-expiration-date"));
|
||||||
}
|
}
|
||||||
|
|
||||||
isSessionExpired(): boolean {
|
isSessionExpired(): boolean {
|
||||||
const tokenExpirationDate = this.getTokenExpirationDate();
|
return this.getTokenExpirationDate() < DateTime.now() && this.isAuthenticated();
|
||||||
if (tokenExpirationDate) {
|
|
||||||
return DateTime.fromISO(tokenExpirationDate) < DateTime.now() && this.isAuthenticated();
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getAuthenticatedAuthor(): Author | null {
|
getAuthenticatedAuthor(): Author {
|
||||||
const authorStr = sessionStorage.getItem('author')
|
return JSON.parse(this.cookieService.get('author'));
|
||||||
if (authorStr) {
|
|
||||||
return JSON.parse(authorStr);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getAuthenticatedAuthorToken(): string | null{
|
getAuthenticatedAuthorToken(): string {
|
||||||
return sessionStorage.getItem('token');
|
return this.cookieService.get('token');
|
||||||
}
|
}
|
||||||
|
|
||||||
setSessionExpired(expired: boolean) {
|
setSessionExpired(expired: boolean) {
|
||||||
|
@ -1,19 +1,21 @@
|
|||||||
import {Component, EventEmitter, Input, Output} from '@angular/core';
|
import {Component, EventEmitter, Input, Output} from '@angular/core';
|
||||||
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||||
|
import {InputTextareaModule} from 'primeng/inputtextarea';
|
||||||
import {Button} from 'primeng/button';
|
import {Button} from 'primeng/button';
|
||||||
import {CommentService} from '../../services/comment.service';
|
import {CommentService} from '../../services/comment.service';
|
||||||
import {Subscription, switchMap} from 'rxjs';
|
import {Author} from '../../models/author';
|
||||||
|
import {Subscription} from 'rxjs';
|
||||||
import {Comment} from '../../models/comment';
|
import {Comment} from '../../models/comment';
|
||||||
import {MessageService} from 'primeng/api';
|
import {MessageService} from 'primeng/api';
|
||||||
import {NgStyle} from '@angular/common';
|
import {NgStyle} from '@angular/common';
|
||||||
import {AuthService} from '../../auth.service';
|
import {AuthService} from '../../auth.service';
|
||||||
import {AuthorService} from '../../services/author.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-comment-form',
|
selector: 'app-comment-form',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
|
InputTextareaModule,
|
||||||
Button,
|
Button,
|
||||||
NgStyle
|
NgStyle
|
||||||
],
|
],
|
||||||
@ -27,36 +29,25 @@ export class CommentFormComponent {
|
|||||||
@Input({required: true}) postId: bigint = BigInt(1);
|
@Input({required: true}) postId: bigint = BigInt(1);
|
||||||
@Output() commentToEmit = new EventEmitter<Comment>();
|
@Output() commentToEmit = new EventEmitter<Comment>();
|
||||||
subs: Subscription[] = [];
|
subs: Subscription[] = [];
|
||||||
createdComment: Comment = {} as Comment;
|
|
||||||
|
|
||||||
constructor(private commentService: CommentService,
|
constructor(private commentService: CommentService,
|
||||||
private messageService: MessageService,
|
private messageService: MessageService,
|
||||||
private authService: AuthService,
|
private authService: AuthService,) {
|
||||||
private authorService: AuthorService) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
let token = this.authService.getAuthenticatedAuthorToken();
|
let token: string = this.authService.getAuthenticatedAuthorToken();
|
||||||
let author = this.authService.getAuthenticatedAuthor();
|
let author: Author = this.authService.getAuthenticatedAuthor();
|
||||||
if (this.commentForm.valid && author && token && this.commentForm.value.content) {
|
if (this.commentForm.valid && author && token && this.commentForm.value.content) {
|
||||||
// get l'image de profile après avoir créé le commentaire
|
// get l'image de profile après avoir créé le commentaire
|
||||||
this.subs.push(this.commentService.create(this.commentForm.value.content, this.postId, author.id, token).pipe(
|
this.subs.push(this.commentService.create(this.commentForm.value.content, this.postId, author.id, token).subscribe({
|
||||||
switchMap((comment: Comment) => {
|
next: (comment: Comment) => {
|
||||||
this.createdComment.authorId = author.id;
|
comment.authorId = author.id;
|
||||||
this.createdComment.content = comment.content;
|
comment.authorName = author.name;
|
||||||
this.createdComment.id = comment.id;
|
comment.profilePicture = author.profilePicture;
|
||||||
this.createdComment.commentDate = comment.commentDate;
|
comment.authorRole = author.role;
|
||||||
this.createdComment.authorName = author.name;
|
|
||||||
this.createdComment.authorRole = author.role;
|
|
||||||
this.commentForm.value.content = "";
|
this.commentForm.value.content = "";
|
||||||
return this.authorService.getAvatar(author?.id)
|
this.commentToEmit.emit(comment);
|
||||||
})
|
|
||||||
).subscribe({
|
|
||||||
next: (profilePicture: string) => {
|
|
||||||
this.createdComment.profilePicture = profilePicture; // c'est de la merde
|
|
||||||
this.commentForm.value.content = "";
|
|
||||||
this.commentToEmit.emit(this.createdComment);
|
|
||||||
console.log(this.createdComment)
|
|
||||||
this.successMessage("Succès", "Commentaire créé avec succès");
|
this.successMessage("Succès", "Commentaire créé avec succès");
|
||||||
},
|
},
|
||||||
error: (error) => {
|
error: (error) => {
|
||||||
|
@ -25,9 +25,8 @@ export class HeaderComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private initializeMenu(): void {
|
private initializeMenu(): void {
|
||||||
const authenticatedAuthor = this.authService.getAuthenticatedAuthor();
|
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated()) {
|
||||||
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated() && authenticatedAuthor) {
|
this.actualAuthor = this.authService.getAuthenticatedAuthor();
|
||||||
this.actualAuthor = authenticatedAuthor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.actualAuthor) {
|
if (this.actualAuthor) {
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
div {
|
|
||||||
margin-top: 10em;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
animation-name: spin;
|
|
||||||
animation-duration: 2000ms;
|
|
||||||
animation-iteration-count: infinite;
|
|
||||||
animation-timing-function: linear;
|
|
||||||
max-width: 40%;
|
|
||||||
max-height: 40%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
from {
|
|
||||||
transform:rotate(0deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform:rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
<div>
|
|
||||||
<img src="./assets/icon.jpg" alt="loadign">
|
|
||||||
</div>
|
|
@ -1,15 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
import {NgOptimizedImage} from '@angular/common';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-loading',
|
|
||||||
standalone: true,
|
|
||||||
imports: [
|
|
||||||
NgOptimizedImage
|
|
||||||
],
|
|
||||||
templateUrl: './loading.component.html',
|
|
||||||
styleUrl: './loading.component.css'
|
|
||||||
})
|
|
||||||
export class LoadingComponent {
|
|
||||||
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
@if (post) {
|
|
||||||
<p-dialog class="preview-dialog"
|
|
||||||
header='Prévisualisation de "{{ post.title }}"'
|
|
||||||
[modal]="true"
|
|
||||||
[(visible)]="opened"
|
|
||||||
[closable]="true">
|
|
||||||
<app-post-home [title]="post.title"
|
|
||||||
[description]="post.description"
|
|
||||||
[category]="post.category"
|
|
||||||
[date]="post.publicationDate"
|
|
||||||
[illustration]="post.illustration"
|
|
||||||
[authorProfilePicture]="profilePicture"
|
|
||||||
[username]="username"/>
|
|
||||||
</p-dialog>
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
import {Component, Input} from '@angular/core';
|
|
||||||
import {Dialog} from 'primeng/dialog';
|
|
||||||
import {PostHomeComponent} from '../../post-home/post-home.component';
|
|
||||||
import {Post} from '../../../models/post';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-preview-modal',
|
|
||||||
standalone: true,
|
|
||||||
imports: [
|
|
||||||
Dialog,
|
|
||||||
PostHomeComponent
|
|
||||||
],
|
|
||||||
templateUrl: './preview-modal.component.html',
|
|
||||||
styleUrl: './preview-modal.component.css'
|
|
||||||
})
|
|
||||||
export class PreviewModalComponent {
|
|
||||||
opened: boolean = true;
|
|
||||||
@Input({required: true}) post: Post | undefined;
|
|
||||||
@Input() username: string = '';
|
|
||||||
@Input() profilePicture: string = '';
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
@if (post) {
|
|
||||||
<p-dialog header='Modifier "{{ post.title }}"'
|
|
||||||
[modal]="true"
|
|
||||||
[closable]="true"
|
|
||||||
[(visible)]="opened">
|
|
||||||
<app-post-form [actualAuthor]="actualAuthor"
|
|
||||||
[postId]="post.id"
|
|
||||||
[isUpdateMode]="true"
|
|
||||||
[title]="post.title"
|
|
||||||
[category]="post.category"
|
|
||||||
[description]="post.description"
|
|
||||||
[body]="post.body"
|
|
||||||
(postUpdate)="onSubmit(post)"/>
|
|
||||||
</p-dialog>
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
import {Component, EventEmitter, Input, Output} from '@angular/core';
|
|
||||||
import {Dialog} from 'primeng/dialog';
|
|
||||||
import {PostFormComponent} from '../../post-form/post-form.component';
|
|
||||||
import {Post} from '../../../models/post';
|
|
||||||
import {AuthService} from '../../../auth.service';
|
|
||||||
import {Author} from '../../../models/author';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-update-modal',
|
|
||||||
standalone: true,
|
|
||||||
imports: [
|
|
||||||
Dialog,
|
|
||||||
PostFormComponent
|
|
||||||
],
|
|
||||||
templateUrl: './update-modal.component.html',
|
|
||||||
styleUrl: './update-modal.component.css'
|
|
||||||
})
|
|
||||||
export class UpdateModalComponent {
|
|
||||||
actualAuthor: Author | undefined;
|
|
||||||
opened: boolean = true;
|
|
||||||
@Input({required: true}) post: Post | undefined;
|
|
||||||
@Output() updatedPost: EventEmitter<Post> = new EventEmitter<Post>();
|
|
||||||
|
|
||||||
constructor(private authService: AuthService) {
|
|
||||||
this.authService.getAuthenticatedAuthor();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
onSubmit(updatedPost: Post) {
|
|
||||||
this.updatedPost.emit(updatedPost);
|
|
||||||
this.opened = false
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +1,13 @@
|
|||||||
<div>
|
<div>
|
||||||
<form [formGroup]="form" (ngSubmit)="onSubmit()">
|
<form [formGroup]="form" (ngSubmit)="onSubmit()">
|
||||||
<p-floatlabel variant="on">
|
<label for="title">Titre du post</label>
|
||||||
<input [(ngModel)]="title" id="title" type="text" pInputText formControlName="title"/>
|
<input [(ngModel)]="title" id="title" type="text" pInputText formControlName="title" />
|
||||||
<label for="title">Titre du post</label>
|
|
||||||
</p-floatlabel>
|
|
||||||
|
|
||||||
<p-floatlabel variant="on">
|
<label for="category">Catégorie du post</label>
|
||||||
<input [(ngModel)]="category" pInputText id="category" formControlName="category" type="text"/>
|
<input [(ngModel)]="category" pInputText id="category" formControlName="category" type="text" />
|
||||||
<label for="category">Catégorie du post</label>
|
|
||||||
</p-floatlabel>
|
|
||||||
|
|
||||||
<p-floatlabel variant="on">
|
|
||||||
<textarea pTextarea id="desc" rows="5" cols="30" pSize="large" formControlName="description"></textarea>
|
|
||||||
<label for="desc">Description du post</label>
|
|
||||||
</p-floatlabel>
|
|
||||||
|
|
||||||
|
<label for="desc">Description du post</label>
|
||||||
|
<textarea [(ngModel)]="description" formControlName="description" id="desc" pInputTextarea></textarea>
|
||||||
|
|
||||||
<label>Image descriptive du post</label>
|
<label>Image descriptive du post</label>
|
||||||
<p-fileUpload
|
<p-fileUpload
|
||||||
@ -35,43 +28,7 @@
|
|||||||
</p-fileUpload>
|
</p-fileUpload>
|
||||||
|
|
||||||
<label>Contenu du post</label>
|
<label>Contenu du post</label>
|
||||||
<p-editor [(ngModel)]="body" formControlName="body" [style]="{ height: '320px' }">
|
<p-editor [(ngModel)]="body" formControlName="body" [modules]="editorModules" [style]="{ height: '320px' }">
|
||||||
<ng-template #header>
|
|
||||||
<span class="ql-formats">
|
|
||||||
<!-- <button type="button" class="ql-list" value="ordered"></button>-->
|
|
||||||
<!-- <button type="button" class="ql-bullet" value="bullet"></button>-->
|
|
||||||
<select class="ql-header">
|
|
||||||
<option value="1">Titre 1</option>
|
|
||||||
<option value="2">Titre 2</option>
|
|
||||||
<option value="3">Titre 3</option>
|
|
||||||
<option value="4">Titre 4</option>
|
|
||||||
<option value="5">Titre 5</option>
|
|
||||||
<option value="6">Titre 6</option>
|
|
||||||
<option value="">Normal</option>
|
|
||||||
</select>
|
|
||||||
<button class="ql-bold"></button>
|
|
||||||
<button class="ql-italic"></button>
|
|
||||||
<button class="ql-underline"></button>
|
|
||||||
<button class="ql-strike"></button>
|
|
||||||
<span class="ql-formats">
|
|
||||||
<select class="ql-color"></select>
|
|
||||||
<select class="ql-background"></select>
|
|
||||||
</span>
|
|
||||||
<span class="ql-formats">
|
|
||||||
<button class="ql-script" value="sub"></button>
|
|
||||||
<button class="ql-script" value="super"></button>
|
|
||||||
</span>
|
|
||||||
<span class="ql-formats">
|
|
||||||
<button class="ql-blockquote"></button>
|
|
||||||
<button class="ql-code-block"></button>
|
|
||||||
</span>
|
|
||||||
<button type="button" class="ql-list" value="bullet"></button>
|
|
||||||
<button type="button" class="ql-list" value="ordered"></button>
|
|
||||||
<button type="button" class="ql-link" aria-label="Link"></button>
|
|
||||||
<button type="button" class="ql-image" aria-label="Image"></button>
|
|
||||||
<button type="button" class="ql-video" aria-label="Video"></button>
|
|
||||||
</span>
|
|
||||||
</ng-template>
|
|
||||||
</p-editor>
|
</p-editor>
|
||||||
|
|
||||||
<p-button
|
<p-button
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {Component, EventEmitter, Input, OnDestroy, Output} from '@angular/core';
|
import {Component, Input, OnDestroy} from '@angular/core';
|
||||||
import {FormBuilder, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
import {FormBuilder, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||||
import {InputTextModule} from 'primeng/inputtext';
|
import {InputTextModule} from 'primeng/inputtext';
|
||||||
|
import {InputTextareaModule} from 'primeng/inputtextarea';
|
||||||
import {FileSelectEvent, FileUploadModule} from 'primeng/fileupload';
|
import {FileSelectEvent, FileUploadModule} from 'primeng/fileupload';
|
||||||
import {mergeMap, Subscription} from 'rxjs';
|
import {mergeMap, Subscription} from 'rxjs';
|
||||||
import {PostService} from '../../services/post.service';
|
import {PostService} from '../../services/post.service';
|
||||||
@ -10,10 +11,6 @@ import {Router} from '@angular/router';
|
|||||||
import {Author} from '../../models/author';
|
import {Author} from '../../models/author';
|
||||||
import {AuthorService} from '../../services/author.service';
|
import {AuthorService} from '../../services/author.service';
|
||||||
import {AuthService} from '../../auth.service';
|
import {AuthService} from '../../auth.service';
|
||||||
import {Button} from 'primeng/button';
|
|
||||||
import {Textarea} from 'primeng/textarea';
|
|
||||||
import {FloatLabel} from 'primeng/floatlabel';
|
|
||||||
import {Post} from '../../models/post';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-post-form',
|
selector: 'app-post-form',
|
||||||
@ -21,11 +18,9 @@ import {Post} from '../../models/post';
|
|||||||
imports: [
|
imports: [
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
InputTextModule,
|
InputTextModule,
|
||||||
|
InputTextareaModule,
|
||||||
FileUploadModule,
|
FileUploadModule,
|
||||||
EditorModule,
|
EditorModule
|
||||||
Button,
|
|
||||||
Textarea,
|
|
||||||
FloatLabel
|
|
||||||
],
|
],
|
||||||
templateUrl: './post-form.component.html',
|
templateUrl: './post-form.component.html',
|
||||||
styleUrls: ['./post-form.component.css']
|
styleUrls: ['./post-form.component.css']
|
||||||
@ -38,10 +33,17 @@ export class PostFormComponent implements OnDestroy {
|
|||||||
@Input() category: string = '';
|
@Input() category: string = '';
|
||||||
@Input() description: string = '';
|
@Input() description: string = '';
|
||||||
@Input() body: string = '';
|
@Input() body: string = '';
|
||||||
@Output() postUpdate: EventEmitter<Post> = new EventEmitter();
|
|
||||||
subs: Subscription[] = [];
|
subs: Subscription[] = [];
|
||||||
form: FormGroup;
|
form: FormGroup;
|
||||||
uploadedFile: File | undefined;
|
uploadedFile: File | undefined;
|
||||||
|
editorModules = {
|
||||||
|
toolbar: [
|
||||||
|
['bold', 'italic', 'underline', 'code'], // Styles de texte
|
||||||
|
[{header: [2, false]}], // Permet d'ajouter un `<h2>`
|
||||||
|
[{list: 'ordered'}, {list: 'bullet'}], // Listes
|
||||||
|
['link', 'image', 'video'], // Ajout de liens et images
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
@ -103,13 +105,12 @@ export class PostFormComponent implements OnDestroy {
|
|||||||
|
|
||||||
if (this.isUpdateMode && this.postId) {
|
if (this.isUpdateMode && this.postId) {
|
||||||
this.subs.push(
|
this.subs.push(
|
||||||
this.postService.updatePost(this.postId, postData, this.authService.getAuthenticatedAuthorToken()!).pipe(
|
this.postService.updatePost(this.postId, postData, this.authService.getAuthenticatedAuthorToken()).pipe(
|
||||||
mergeMap((_) => {
|
mergeMap((_) => {
|
||||||
return this.postService.changeIllustration(this.postId, this.uploadedFile, this.authService.getAuthenticatedAuthorToken()!);
|
return this.postService.changeIllustration(this.postId, this.uploadedFile, this.authService.getAuthenticatedAuthorToken());
|
||||||
})
|
})
|
||||||
).subscribe({
|
).subscribe({
|
||||||
next: (_) => {
|
next: (_) => {
|
||||||
this.postUpdate.emit(_);
|
|
||||||
this.successMessage('Succès', 'Post mis à jour avec succès')
|
this.successMessage('Succès', 'Post mis à jour avec succès')
|
||||||
},
|
},
|
||||||
error: (err) => this.failureMessage('Erreur', err.error.message)
|
error: (err) => this.failureMessage('Erreur', err.error.message)
|
||||||
@ -117,11 +118,11 @@ export class PostFormComponent implements OnDestroy {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.subs.push(
|
this.subs.push(
|
||||||
this.postService.createPost(postData, this.authService.getAuthenticatedAuthorToken()!).pipe(
|
this.postService.createPost(postData, this.authService.getAuthenticatedAuthorToken()).pipe(
|
||||||
mergeMap(post =>
|
mergeMap(post =>
|
||||||
this.authorService.attributePost(this.actualAuthor?.id, post.id, this.authService.getAuthenticatedAuthorToken()!).pipe(
|
this.authorService.attributePost(this.actualAuthor?.id, post.id, this.authService.getAuthenticatedAuthorToken()).pipe(
|
||||||
mergeMap((_) =>
|
mergeMap((_) =>
|
||||||
this.postService.changeIllustration(post.id, this.uploadedFile, this.authService.getAuthenticatedAuthorToken()!),
|
this.postService.changeIllustration(post.id, this.uploadedFile, this.authService.getAuthenticatedAuthorToken()),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -139,7 +140,6 @@ export class PostFormComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private transformYouTubeLinksToIframes(html: string): string {
|
private transformYouTubeLinksToIframes(html: string): string {
|
||||||
// Magie noire
|
|
||||||
return html.replace(/<a[^>]*href="(https?:\/\/(?:www\.)?(youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([a-zA-Z0-9_-]+)[^"]*)".*?<\/a>/g,
|
return html.replace(/<a[^>]*href="(https?:\/\/(?:www\.)?(youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([a-zA-Z0-9_-]+)[^"]*)".*?<\/a>/g,
|
||||||
(_, _url, _prefix, videoId) => {
|
(_, _url, _prefix, videoId) => {
|
||||||
return `<iframe width="560" height="315" src="https://www.youtube.com/embed/${videoId}" frameborder="0" allowfullscreen></iframe>`;
|
return `<iframe width="560" height="315" src="https://www.youtube.com/embed/${videoId}" frameborder="0" allowfullscreen></iframe>`;
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<span>{{ category }}</span>
|
<span>{{ category }}</span>
|
||||||
<em>Publié le {{ date | date : "dd/MM/yyyy à HH:mm" }}</em>
|
<em>Publié le {{ date | date : "dd/MM/yyyy à HH:mm" }}</em>
|
||||||
<span class="desc">{{ description }}</span>
|
<span class="desc">{{ description }}</span>
|
||||||
<p-button routerLink="post/{{ postId }}" >Lire la suite</p-button>
|
<p-button routerLink="post/{{ postId }}" label="Lire la suite"/>
|
||||||
<a routerLink="/profile/{{ authorId }}" class="user-profile">
|
<a routerLink="/profile/{{ authorId }}" class="user-profile">
|
||||||
@if (authorProfilePicture) {
|
@if (authorProfilePicture) {
|
||||||
<p-avatar image="data:image/jpeg;base64,{{ authorProfilePicture }}" shape="circle" styleClass="mr-2"
|
<p-avatar image="data:image/jpeg;base64,{{ authorProfilePicture }}" shape="circle" styleClass="mr-2"
|
||||||
|
@ -4,17 +4,16 @@ import {CardModule} from 'primeng/card';
|
|||||||
import {DatePipe} from '@angular/common';
|
import {DatePipe} from '@angular/common';
|
||||||
import {RouterLink} from '@angular/router';
|
import {RouterLink} from '@angular/router';
|
||||||
import {AvatarModule} from 'primeng/avatar';
|
import {AvatarModule} from 'primeng/avatar';
|
||||||
import {MatButton, MatFabButton} from '@angular/material/button';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-post-home',
|
selector: 'app-post-home',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [
|
||||||
|
Button,
|
||||||
CardModule,
|
CardModule,
|
||||||
DatePipe,
|
DatePipe,
|
||||||
RouterLink,
|
RouterLink,
|
||||||
AvatarModule,
|
AvatarModule
|
||||||
Button,
|
|
||||||
],
|
],
|
||||||
templateUrl: './post-home.component.html',
|
templateUrl: './post-home.component.html',
|
||||||
styleUrl: './post-home.component.css'
|
styleUrl: './post-home.component.css'
|
||||||
|
@ -37,7 +37,7 @@ export class RegisterFormComponent implements OnDestroy {
|
|||||||
];
|
];
|
||||||
subs: Subscription[] = [];
|
subs: Subscription[] = [];
|
||||||
form: FormGroup;
|
form: FormGroup;
|
||||||
actualAuthor: string | undefined;
|
actualAuthor: Author | undefined;
|
||||||
|
|
||||||
constructor(private formBuilder: FormBuilder,
|
constructor(private formBuilder: FormBuilder,
|
||||||
private authorService: AuthorService,
|
private authorService: AuthorService,
|
||||||
@ -45,9 +45,8 @@ export class RegisterFormComponent implements OnDestroy {
|
|||||||
private messageService: MessageService,
|
private messageService: MessageService,
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
) {
|
) {
|
||||||
const authenticatedAuthor = this.authService.getAuthenticatedAuthorToken();
|
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated()) {
|
||||||
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated() && authenticatedAuthor) {
|
this.actualAuthor = this.authService.getAuthenticatedAuthor();
|
||||||
this.actualAuthor = authenticatedAuthor;
|
|
||||||
}
|
}
|
||||||
this.form = this.formBuilder.group({
|
this.form = this.formBuilder.group({
|
||||||
username: ['', [Validators.required, Validators.maxLength(255)]],
|
username: ['', [Validators.required, Validators.maxLength(255)]],
|
||||||
@ -84,7 +83,7 @@ export class RegisterFormComponent implements OnDestroy {
|
|||||||
this.username,
|
this.username,
|
||||||
this.password,
|
this.password,
|
||||||
this.role,
|
this.role,
|
||||||
this.authService.getAuthenticatedAuthorToken()!).subscribe({
|
this.authService.getAuthenticatedAuthorToken()).subscribe({
|
||||||
next: (author: Author) => {
|
next: (author: Author) => {
|
||||||
this.successMessage('Succès', `Auteur ${author.name} créé avec succès`);
|
this.successMessage('Succès', `Auteur ${author.name} créé avec succès`);
|
||||||
this.createdAuthor.emit(author);
|
this.createdAuthor.emit(author);
|
||||||
|
@ -7,6 +7,7 @@ import {Subscription, switchMap} from 'rxjs';
|
|||||||
import {AuthorService} from '../../services/author.service';
|
import {AuthorService} from '../../services/author.service';
|
||||||
import {MessageService} from 'primeng/api';
|
import {MessageService} from 'primeng/api';
|
||||||
import {FileSelectEvent, FileUploadModule} from 'primeng/fileupload';
|
import {FileSelectEvent, FileUploadModule} from 'primeng/fileupload';
|
||||||
|
import {CookieService} from 'ngx-cookie-service';
|
||||||
import {Author} from '../../models/author';
|
import {Author} from '../../models/author';
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
import {AuthService} from '../../auth.service';
|
import {AuthService} from '../../auth.service';
|
||||||
@ -37,6 +38,7 @@ export class UpdateProfileFormComponent implements OnDestroy {
|
|||||||
constructor(private formBuilder: FormBuilder,
|
constructor(private formBuilder: FormBuilder,
|
||||||
private authorService: AuthorService,
|
private authorService: AuthorService,
|
||||||
private messageService: MessageService,
|
private messageService: MessageService,
|
||||||
|
private cookieService: CookieService,
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
) {
|
) {
|
||||||
@ -78,7 +80,7 @@ export class UpdateProfileFormComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
const token = this.authService.getAuthenticatedAuthorToken();
|
const token: string = this.authService.getAuthenticatedAuthorToken();
|
||||||
if (this.form.valid && token && this.password === this.passwordConfirm) {
|
if (this.form.valid && token && this.password === this.passwordConfirm) {
|
||||||
const newUsername = this.form.value.username;
|
const newUsername = this.form.value.username;
|
||||||
if (this.uploadedFile) {
|
if (this.uploadedFile) {
|
||||||
@ -90,7 +92,7 @@ export class UpdateProfileFormComponent implements OnDestroy {
|
|||||||
next: (author: Author) => {
|
next: (author: Author) => {
|
||||||
this.successMessage("Mise à jour réussie", "Profil mit à jour avec succès");
|
this.successMessage("Mise à jour réussie", "Profil mit à jour avec succès");
|
||||||
this.updatedAuthorEvent.emit(author);
|
this.updatedAuthorEvent.emit(author);
|
||||||
sessionStorage.setItem('author', JSON.stringify(author));
|
this.cookieService.set('author', JSON.stringify(author));
|
||||||
this.router.navigate(['/']);
|
this.router.navigate(['/']);
|
||||||
},
|
},
|
||||||
error: (err) => {
|
error: (err) => {
|
||||||
@ -102,7 +104,7 @@ export class UpdateProfileFormComponent implements OnDestroy {
|
|||||||
next: (author: Author) => {
|
next: (author: Author) => {
|
||||||
this.successMessage("Mise à jour réussie", "Profil mit à jour avec succès");
|
this.successMessage("Mise à jour réussie", "Profil mit à jour avec succès");
|
||||||
this.updatedAuthorEvent.emit(author);
|
this.updatedAuthorEvent.emit(author);
|
||||||
sessionStorage.setItem('author', JSON.stringify(author));
|
this.cookieService.set('author', JSON.stringify(author));
|
||||||
this.router.navigate(['/']);
|
this.router.navigate(['/']);
|
||||||
},
|
},
|
||||||
error: (err) => {
|
error: (err) => {
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
import {CanActivateFn, Router} from '@angular/router';
|
import {CanActivateFn, Router} from '@angular/router';
|
||||||
import {inject} from '@angular/core';
|
import {inject} from '@angular/core';
|
||||||
|
import {CookieService} from 'ngx-cookie-service';
|
||||||
import {AuthService} from '../auth.service';
|
import {AuthService} from '../auth.service';
|
||||||
import {Role} from '../models/role';
|
import {Role} from '../models/role';
|
||||||
|
|
||||||
export const adminGuard: CanActivateFn = (route, state) => {
|
export const adminGuard: CanActivateFn = (route, state) => {
|
||||||
const router = inject(Router);
|
const router = inject(Router);
|
||||||
|
const cookieService = inject(CookieService);
|
||||||
const authService: AuthService = inject(AuthService);
|
const authService: AuthService = inject(AuthService);
|
||||||
if ((authService.isAuthenticated() && JSON.parse(sessionStorage.getItem("author")!).role !== Role.ADMIN) || !authService.isAuthenticated()) {
|
|
||||||
|
if ((authService.isAuthenticated() && JSON.parse(cookieService.get("author")).role !== Role.ADMIN) || !authService.isAuthenticated()) {
|
||||||
router.navigate(['/']);
|
router.navigate(['/']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import {CanActivateFn, Router} from '@angular/router';
|
import {CanActivateFn, Router} from '@angular/router';
|
||||||
import {inject} from '@angular/core';
|
import {inject} from '@angular/core';
|
||||||
|
import {CookieService} from 'ngx-cookie-service';
|
||||||
|
|
||||||
export const authGuard: CanActivateFn = (route, state) => {
|
export const authGuard: CanActivateFn = (route, state) => {
|
||||||
const router = inject(Router);
|
const router = inject(Router);
|
||||||
if (sessionStorage.getItem("author") !== null || sessionStorage.getItem("token") !== null) {
|
const cookieService = inject(CookieService);
|
||||||
|
if (cookieService.check("author") || cookieService.check("token")) {
|
||||||
router.navigate(['/']);
|
router.navigate(['/']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
import {CanActivateFn, Router} from '@angular/router';
|
import {CanActivateFn, Router} from '@angular/router';
|
||||||
import {inject} from '@angular/core';
|
import {inject} from '@angular/core';
|
||||||
|
import {CookieService} from 'ngx-cookie-service';
|
||||||
import {AuthService} from '../auth.service';
|
import {AuthService} from '../auth.service';
|
||||||
import {Role} from '../models/role';
|
import {Role} from '../models/role';
|
||||||
|
|
||||||
export const writerGuard: CanActivateFn = (route, state) => {
|
export const writerGuard: CanActivateFn = (route, state) => {
|
||||||
const router = inject(Router);
|
const router = inject(Router);
|
||||||
|
const cookieService = inject(CookieService);
|
||||||
const authService = inject(AuthService);
|
const authService = inject(AuthService);
|
||||||
const authorStr = sessionStorage.getItem("author");
|
|
||||||
|
|
||||||
if (authorStr) {
|
if ((authService.isAuthenticated() && JSON.parse(cookieService.get("author")).role !== Role.WRITER) || !authService.isAuthenticated()) {
|
||||||
if ((authService.isAuthenticated() && JSON.parse(authorStr).role !== Role.WRITER) || !authService.isAuthenticated()) {
|
router.navigate(['/']);
|
||||||
router.navigate(['/']);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>{{ author.role }}</td>
|
<td>{{ author.role }}</td>
|
||||||
<td>
|
<td>
|
||||||
<p-button icon="pi pi-pencil" (click)="openDialog(updateDialogVisibility, rowIndex)" severity="warn"
|
<p-button icon="pi pi-pencil" (click)="openDialog(updateDialogVisibility, rowIndex)" severity="warning"
|
||||||
label="Modifier"/>
|
label="Modifier"/>
|
||||||
<p-dialog header='Modifier "{{ author.name }}"' [modal]="true" [(visible)]="updateDialogVisibility[rowIndex]">
|
<p-dialog header='Modifier "{{ author.name }}"' [modal]="true" [(visible)]="updateDialogVisibility[rowIndex]">
|
||||||
<app-register-form [adminForm]="true" [username]="author.name">
|
<app-register-form [adminForm]="true" [username]="author.name">
|
||||||
|
@ -13,6 +13,6 @@
|
|||||||
[authorProfilePicture]="post.authorProfilePicture"/>
|
[authorProfilePicture]="post.authorProfilePicture"/>
|
||||||
}
|
}
|
||||||
} @else {
|
} @else {
|
||||||
<app-loading></app-loading>
|
<h1>Aucun post n'a été créé pour l'instant</h1>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,7 +8,6 @@ import {PostService} from '../../services/post.service';
|
|||||||
import {PostHomeComponent} from '../../components/post-home/post-home.component';
|
import {PostHomeComponent} from '../../components/post-home/post-home.component';
|
||||||
import {AuthorWithPost} from '../../models/author-with-post';
|
import {AuthorWithPost} from '../../models/author-with-post';
|
||||||
import {AuthService} from '../../auth.service';
|
import {AuthService} from '../../auth.service';
|
||||||
import {LoadingComponent} from '../../components/loading/loading.component';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-home',
|
selector: 'app-home',
|
||||||
@ -18,7 +17,6 @@ import {LoadingComponent} from '../../components/loading/loading.component';
|
|||||||
HeaderComponent,
|
HeaderComponent,
|
||||||
ToastModule,
|
ToastModule,
|
||||||
PostHomeComponent,
|
PostHomeComponent,
|
||||||
LoadingComponent,
|
|
||||||
],
|
],
|
||||||
templateUrl: './home.component.html',
|
templateUrl: './home.component.html',
|
||||||
styleUrl: './home.component.css'
|
styleUrl: './home.component.css'
|
||||||
@ -31,9 +29,9 @@ export class HomeComponent implements OnDestroy {
|
|||||||
constructor(
|
constructor(
|
||||||
private postService: PostService,
|
private postService: PostService,
|
||||||
private authService: AuthService) {
|
private authService: AuthService) {
|
||||||
const authenticatedAuthor = this.authService.getAuthenticatedAuthor();
|
|
||||||
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated() && authenticatedAuthor) {
|
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated()) {
|
||||||
this.actualAuthor = authenticatedAuthor;
|
this.actualAuthor = this.authService.getAuthenticatedAuthor();
|
||||||
} else {
|
} else {
|
||||||
this.authService.checkSessionExpiration();
|
this.authService.checkSessionExpiration();
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,10 @@
|
|||||||
<label for="username">Nom d'utilisateur</label>
|
<label for="username">Nom d'utilisateur</label>
|
||||||
<input type="text" id="username" pInputText [(ngModel)]="name"/>
|
<input type="text" id="username" pInputText [(ngModel)]="name"/>
|
||||||
<label for="password">Mot de passe</label>
|
<label for="password">Mot de passe</label>
|
||||||
<input type="password" id="password" pInputText [(ngModel)]="password" (keyup.enter)="sendLogins()"/>
|
<input type="password" id="password" pInputText [(ngModel)]="password"/>
|
||||||
|
<label for="confirm-password">Confirmez le mot de passe</label>
|
||||||
|
<input type="password" id="confirm-password" pInputText
|
||||||
|
[(ngModel)]="confirmPassword" (keyup.enter)="sendLogins()"/>
|
||||||
<p-button
|
<p-button
|
||||||
class="send-button"
|
class="send-button"
|
||||||
label="Se connecter"
|
label="Se connecter"
|
||||||
|
@ -7,6 +7,7 @@ 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 {CookieService} from 'ngx-cookie-service';
|
||||||
import {HeaderComponent} from '../../components/header/header.component';
|
import {HeaderComponent} from '../../components/header/header.component';
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
import {ConfigurationService} from '../../configuration.service';
|
import {ConfigurationService} from '../../configuration.service';
|
||||||
@ -29,43 +30,56 @@ export class LoginComponent implements OnDestroy {
|
|||||||
name: string = "";
|
name: string = "";
|
||||||
actualAuthor: Author | undefined;
|
actualAuthor: Author | undefined;
|
||||||
password: string = "";
|
password: string = "";
|
||||||
|
confirmPassword: string = "";
|
||||||
subs: Subscription[] = [];
|
subs: Subscription[] = [];
|
||||||
|
|
||||||
constructor(private authorService: AuthorService,
|
constructor(private authorService: AuthorService,
|
||||||
private messageService: MessageService,
|
private messageService: MessageService,
|
||||||
|
private cookieService: CookieService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private configurationService: ConfigurationService,) {
|
private configurationService: ConfigurationService,) {}
|
||||||
}
|
|
||||||
|
|
||||||
sendLogins(): void {
|
sendLogins(): void {
|
||||||
this.subs.push
|
if (this.password === this.confirmPassword) {
|
||||||
(
|
this.subs.push
|
||||||
this.authorService.login(this.name, this.password).pipe(
|
(
|
||||||
switchMap((tokenObj: any) => {
|
this.authorService.login(this.name, this.password).pipe(
|
||||||
// sessionStorage.removeItem('token');
|
switchMap((tokenObj: any) => {
|
||||||
sessionStorage.setItem('token', tokenObj.token);
|
this.cookieService.delete('token', '/', this.configurationService.getDomain())
|
||||||
return this.authorService.me(tokenObj.token)
|
this.cookieService.set("token", tokenObj.token, {
|
||||||
}))
|
domain: this.configurationService.getDomain(),
|
||||||
.subscribe({
|
secure: true,
|
||||||
next: (author: Author) => {
|
path: '/'
|
||||||
// sessionStorage.removeItem('author');
|
|
||||||
sessionStorage.setItem('author', JSON.stringify(author));
|
|
||||||
sessionStorage.setItem('token-expiration-date', DateTime.now().plus({millisecond: this.configurationService.getTokenTTL()}).toISO())
|
|
||||||
this.getAuthorCookie();
|
|
||||||
this.router.navigate(['/']).then(() => {
|
|
||||||
this.successMessage('Connecté avec succès', 'Heureux de vous revoir ' + this.actualAuthor?.name)
|
|
||||||
});
|
});
|
||||||
},
|
return this.authorService.me(tokenObj.token)
|
||||||
error: (err) => this.failureMessage('Erreur de connexion', err.error.message)
|
}))
|
||||||
})
|
.subscribe({
|
||||||
);
|
next: (author: Author) => {
|
||||||
|
this.cookieService.delete('author', '/', this.configurationService.getDomain())
|
||||||
|
this.cookieService.set("author", JSON.stringify(author), {
|
||||||
|
domain: this.configurationService.getDomain(),
|
||||||
|
secure : true,
|
||||||
|
path: '/' });
|
||||||
|
this.cookieService.set('token-expiration-date', DateTime.now().plus({millisecond: this.configurationService.getTokenTTL()}).toISO(), {
|
||||||
|
domain: this.configurationService.getDomain(),
|
||||||
|
secure: true,
|
||||||
|
path: '/',
|
||||||
|
})
|
||||||
|
this.getAuthorCookie();
|
||||||
|
this.router.navigate(['/']).then(() => {
|
||||||
|
this.successMessage('Connecté avec succès', 'Heureux de vous revoir ' + this.actualAuthor?.name)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: (err) => this.failureMessage('Erreur de connexion', err.error.message)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.failureMessage('Erreur de connexion', 'Les deux mots de passe ne correspondent pas')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getAuthorCookie(): void {
|
getAuthorCookie(): void {
|
||||||
const authorStr = sessionStorage.getItem('author');
|
this.actualAuthor = JSON.parse(this.cookieService.get("author"));
|
||||||
if (authorStr) {
|
|
||||||
this.actualAuthor = JSON.parse(authorStr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
successMessage(summary: string, detail: string): void {
|
successMessage(summary: string, detail: string): void {
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {CookieService} from 'ngx-cookie-service';
|
||||||
import {HeaderComponent} from '../../components/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';
|
||||||
|
import {ConfigurationService} from '../../configuration.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-logout',
|
selector: 'app-logout',
|
||||||
@ -12,16 +14,19 @@ import {MessageService} from 'primeng/api';
|
|||||||
templateUrl: './logout.component.html',
|
templateUrl: './logout.component.html',
|
||||||
styleUrl: './logout.component.css'
|
styleUrl: './logout.component.css'
|
||||||
})
|
})
|
||||||
export class LogoutComponent implements OnInit {
|
export class LogoutComponent implements OnInit{
|
||||||
|
|
||||||
constructor(private messageService: MessageService,
|
|
||||||
private router: Router) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
constructor(private cookieService: CookieService,
|
||||||
|
private messageService: MessageService,
|
||||||
|
private router: Router,
|
||||||
|
private configurationService: ConfigurationService,) { }
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
sessionStorage.removeItem("author");
|
const routes: string[] = ['/', '/login', '/register', '/logout', '/profile', '/post', '/new-post', '/admin']
|
||||||
sessionStorage.removeItem("token");
|
Object.keys(this.cookieService.getAll()).forEach(key => {
|
||||||
sessionStorage.removeItem("token-expiration-date");
|
routes.forEach(route => {
|
||||||
|
this.cookieService.delete(key, route, this.configurationService.getDomain());
|
||||||
|
})
|
||||||
|
});
|
||||||
this.router.navigate(['/']).then(() => this.successMessage('Déconnexion', 'Vous avez été deconnecté avec succès'));
|
this.router.navigate(['/']).then(() => this.successMessage('Déconnexion', 'Vous avez été deconnecté avec succès'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,10 +23,29 @@
|
|||||||
<td>{{ post.publicationDate | date: "dd/MM/yyyy à HH:mm" }}</td>
|
<td>{{ post.publicationDate | date: "dd/MM/yyyy à HH:mm" }}</td>
|
||||||
<td>{{ post.description }}</td>
|
<td>{{ post.description }}</td>
|
||||||
<td>
|
<td>
|
||||||
<p-button icon="pi pi-eye" (click)="openDialogPreview(post)" severity="info" label="Prévisualiser"/>
|
<p-button icon="pi pi-eye" (click)="openDialog(previewDialogVisibility, rowIndex)" severity="info"
|
||||||
|
label="Prévisualiser"/>
|
||||||
|
<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"
|
||||||
|
[category]="post.category"
|
||||||
|
[date]="post.publicationDate"
|
||||||
|
[illustration]="post.illustration"/>
|
||||||
|
</p-dialog>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<p-button icon="pi pi-pencil" (click)="openDialogUpdate(post)" severity="warn" label="Modifier"/>
|
<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>
|
||||||
<td>
|
<td>
|
||||||
<p-button icon="pi pi-trash" (click)="openDialog(deleteDialogVisibility, rowIndex)" severity="danger"
|
<p-button icon="pi pi-trash" (click)="openDialog(deleteDialogVisibility, rowIndex)" severity="danger"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {Component, OnDestroy, ViewContainerRef} from '@angular/core';
|
import {Component, OnDestroy} from '@angular/core';
|
||||||
import {HeaderComponent} from '../../components/header/header.component';
|
import {HeaderComponent} from '../../components/header/header.component';
|
||||||
import {TableModule} from 'primeng/table';
|
import {TableModule} from 'primeng/table';
|
||||||
import {AuthorService} from '../../services/author.service';
|
import {AuthorService} from '../../services/author.service';
|
||||||
@ -14,8 +14,6 @@ import {PostHomeComponent} from '../../components/post-home/post-home.component'
|
|||||||
import {PostService} from '../../services/post.service';
|
import {PostService} from '../../services/post.service';
|
||||||
import {PostFormComponent} from "../../components/post-form/post-form.component";
|
import {PostFormComponent} from "../../components/post-form/post-form.component";
|
||||||
import {AuthService} from '../../auth.service';
|
import {AuthService} from '../../auth.service';
|
||||||
import {PreviewModalComponent} from '../../components/modal/preview-modal/preview-modal.component';
|
|
||||||
import {UpdateModalComponent} from '../../components/modal/update-modal/update-modal.component';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-my-posts',
|
selector: 'app-my-posts',
|
||||||
@ -35,22 +33,18 @@ import {UpdateModalComponent} from '../../components/modal/update-modal/update-m
|
|||||||
})
|
})
|
||||||
export class MyPostsComponent implements OnDestroy {
|
export class MyPostsComponent implements OnDestroy {
|
||||||
subs: Subscription[] = [];
|
subs: Subscription[] = [];
|
||||||
|
previewDialogVisibility: boolean[] = [];
|
||||||
updateDialogVisibility: boolean[] = [];
|
updateDialogVisibility: boolean[] = [];
|
||||||
deleteDialogVisibility: boolean[] = [];
|
deleteDialogVisibility: boolean[] = [];
|
||||||
posts: Post[] = [];
|
posts: Post[] = [];
|
||||||
actualAuthor: Author | undefined;
|
actualAuthor: Author;
|
||||||
|
|
||||||
|
|
||||||
constructor(private authService: AuthService,
|
constructor(private authService: AuthService,
|
||||||
private postService: PostService,
|
private postService: PostService,
|
||||||
private viewContainer: ViewContainerRef,
|
|
||||||
private authorService: AuthorService,
|
private authorService: AuthorService,
|
||||||
private messageService: MessageService) {
|
private messageService: MessageService) {
|
||||||
const authenticatedAuthor = this.authService.getAuthenticatedAuthor();
|
this.actualAuthor = this.authService.getAuthenticatedAuthor();
|
||||||
if (authenticatedAuthor) {
|
|
||||||
this.actualAuthor = authenticatedAuthor;
|
|
||||||
this.authorService.getAuthorAvatar(this.actualAuthor.id).subscribe(avatar => this.actualAuthor!.profilePicture = avatar);
|
|
||||||
}
|
|
||||||
this.updatePosts();
|
this.updatePosts();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,9 +59,8 @@ export class MyPostsComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updatePosts(): void {
|
updatePosts(): void {
|
||||||
const authorToken = this.authService.getAuthenticatedAuthorToken()
|
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated()) {
|
||||||
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated() && authorToken) {
|
this.authorService.getAuthorsPosts(this.actualAuthor?.id, this.authService.getAuthenticatedAuthorToken()).subscribe({
|
||||||
this.authorService.getAuthorsPosts(this.actualAuthor?.id, authorToken).subscribe({
|
|
||||||
next: posts => this.posts = posts,
|
next: posts => this.posts = posts,
|
||||||
error: error => this.failureMessage("Erreur", error.error.message),
|
error: error => this.failureMessage("Erreur", error.error.message),
|
||||||
}
|
}
|
||||||
@ -78,16 +71,13 @@ export class MyPostsComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deletePost(id: bigint, rowIndex: number) {
|
deletePost(id: bigint, rowIndex: number) {
|
||||||
const authorToken = this.authService.getAuthenticatedAuthorToken()
|
this.postService.deletePost(id, this.authService.getAuthenticatedAuthorToken()).subscribe({
|
||||||
if (authorToken) {
|
next: (_) => {
|
||||||
this.postService.deletePost(id, authorToken).subscribe({
|
this.updatePosts()
|
||||||
next: (_) => {
|
this.successMessage("Post supprimé", "Ce post a été supprimé avec succès")
|
||||||
this.updatePosts()
|
},
|
||||||
this.successMessage("Post supprimé", "Ce post a été supprimé avec succès")
|
error: error => this.failureMessage("Erreur", error.error.message),
|
||||||
},
|
});
|
||||||
error: error => this.failureMessage("Erreur", error.error.message),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.closeDialog(this.deleteDialogVisibility, rowIndex)
|
this.closeDialog(this.deleteDialogVisibility, rowIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,24 +91,6 @@ export class MyPostsComponent implements OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
openDialogPreview(post: Post): void {
|
|
||||||
const modalInstance = this.viewContainer.createComponent(PreviewModalComponent);
|
|
||||||
modalInstance.setInput("post", post)
|
|
||||||
modalInstance.setInput("username", this.actualAuthor?.name)
|
|
||||||
modalInstance.setInput("profilePicture", this.actualAuthor?.profilePicture)
|
|
||||||
}
|
|
||||||
|
|
||||||
openDialogUpdate(post: Post): void {
|
|
||||||
const modalInstance = this.viewContainer.createComponent(UpdateModalComponent);
|
|
||||||
modalInstance.setInput("post", post)
|
|
||||||
modalInstance.instance.updatedPost.subscribe(post => {
|
|
||||||
console.log(this.posts.map(a => a.id).indexOf(post.id))
|
|
||||||
this.posts[this.posts.map(a => a.id).indexOf(post.id)] = post
|
|
||||||
this.posts = [... this.posts]
|
|
||||||
modalInstance.destroy();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
openDialog(dialogBooleanTab: boolean[], index: number) {
|
openDialog(dialogBooleanTab: boolean[], index: number) {
|
||||||
dialogBooleanTab[index] = true;
|
dialogBooleanTab[index] = true;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import {Component, EventEmitter, OnDestroy} from '@angular/core';
|
|||||||
import {HeaderComponent} from '../../components/header/header.component';
|
import {HeaderComponent} from '../../components/header/header.component';
|
||||||
import {FormBuilder, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
import {FormBuilder, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||||
import {InputTextModule} from 'primeng/inputtext';
|
import {InputTextModule} from 'primeng/inputtext';
|
||||||
|
import {InputTextareaModule} from 'primeng/inputtextarea';
|
||||||
import {FileSelectEvent, FileUploadModule} from 'primeng/fileupload';
|
import {FileSelectEvent, FileUploadModule} from 'primeng/fileupload';
|
||||||
import {mergeMap, Subscription} from 'rxjs';
|
import {mergeMap, Subscription} from 'rxjs';
|
||||||
import {PostService} from '../../services/post.service';
|
import {PostService} from '../../services/post.service';
|
||||||
@ -20,6 +21,7 @@ import {AuthService} from '../../auth.service';
|
|||||||
HeaderComponent,
|
HeaderComponent,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
InputTextModule,
|
InputTextModule,
|
||||||
|
InputTextareaModule,
|
||||||
FileUploadModule,
|
FileUploadModule,
|
||||||
EditorModule,
|
EditorModule,
|
||||||
PostFormComponent,
|
PostFormComponent,
|
||||||
@ -28,6 +30,7 @@ import {AuthService} from '../../auth.service';
|
|||||||
styleUrl: './new-post.component.css'
|
styleUrl: './new-post.component.css'
|
||||||
})
|
})
|
||||||
export class NewPostComponent implements OnDestroy {
|
export class NewPostComponent implements OnDestroy {
|
||||||
|
isSessionExpired: EventEmitter<boolean> = new EventEmitter<boolean>();
|
||||||
subs: Subscription[] = []
|
subs: Subscription[] = []
|
||||||
actualAuthor: Author | undefined;
|
actualAuthor: Author | undefined;
|
||||||
uploadedFile: File | undefined;
|
uploadedFile: File | undefined;
|
||||||
@ -37,7 +40,7 @@ export class NewPostComponent implements OnDestroy {
|
|||||||
private postService: PostService,
|
private postService: PostService,
|
||||||
private authorService: AuthorService,
|
private authorService: AuthorService,
|
||||||
private messageService: MessageService,
|
private messageService: MessageService,
|
||||||
private authService: AuthService,
|
private authService : AuthService,
|
||||||
private router: Router) {
|
private router: Router) {
|
||||||
this.form = this.formBuilder.group({
|
this.form = this.formBuilder.group({
|
||||||
description: ['', [Validators.required, Validators.maxLength(512)]],
|
description: ['', [Validators.required, Validators.maxLength(512)]],
|
||||||
@ -46,10 +49,7 @@ export class NewPostComponent implements OnDestroy {
|
|||||||
category: ['', [Validators.required, Validators.maxLength(50)]],
|
category: ['', [Validators.required, Validators.maxLength(50)]],
|
||||||
});
|
});
|
||||||
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated()) {
|
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated()) {
|
||||||
const authenticatedAuthor = this.authService.getAuthenticatedAuthor();
|
this.actualAuthor = this.authService.getAuthenticatedAuthor();
|
||||||
if (authenticatedAuthor) {
|
|
||||||
this.actualAuthor = authenticatedAuthor;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this.authService.checkSessionExpiration();
|
this.authService.checkSessionExpiration();
|
||||||
}
|
}
|
||||||
@ -72,31 +72,26 @@ export class NewPostComponent implements OnDestroy {
|
|||||||
category: formData.category as string
|
category: formData.category as string
|
||||||
};
|
};
|
||||||
|
|
||||||
const authenticatedAuthor = this.authService.getAuthenticatedAuthorToken();
|
this.subs.push(
|
||||||
if (authenticatedAuthor) {
|
this.postService.createPost(postToPost, this.authService.getAuthenticatedAuthorToken()).pipe(
|
||||||
this.subs.push(
|
mergeMap(post =>
|
||||||
this.postService.createPost(postToPost, authenticatedAuthor).pipe(
|
this.authorService.attributePost(this.actualAuthor?.id, post.id, this.authService.getAuthenticatedAuthorToken()).pipe(
|
||||||
mergeMap(post =>
|
mergeMap((_) =>
|
||||||
this.authorService.attributePost(this.actualAuthor?.id, post.id, authenticatedAuthor).pipe(
|
this.postService.changeIllustration(post.id, this.uploadedFile, this.authService.getAuthenticatedAuthorToken()),
|
||||||
mergeMap((_) =>
|
|
||||||
this.postService.changeIllustration(post.id, this.uploadedFile, authenticatedAuthor),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).subscribe({
|
)
|
||||||
next: () => {
|
).subscribe({
|
||||||
this.router.navigate(['/']).then(() => {
|
next: () => {
|
||||||
this.successMessage('Succès', 'Post créé avec succès')
|
this.router.navigate(['/']).then(() => {
|
||||||
});
|
this.successMessage('Succès', 'Post créé avec succès')
|
||||||
},
|
});
|
||||||
error: (err) => {
|
},
|
||||||
this.failureMessage('Erreur', err.error.message);
|
error: (err) => {
|
||||||
}
|
this.failureMessage('Erreur', err.error.message);
|
||||||
})
|
}
|
||||||
);
|
})
|
||||||
} else {
|
);
|
||||||
console.error("Profil mal chargé")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,12 +49,7 @@ export class PostComponent {
|
|||||||
private authService: AuthService,) {
|
private authService: AuthService,) {
|
||||||
this.route.paramMap.subscribe(params => {
|
this.route.paramMap.subscribe(params => {
|
||||||
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated()) {
|
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated()) {
|
||||||
const authenticatedAuthor = this.authService.getAuthenticatedAuthor();
|
this.actualAuthor = this.authService.getAuthenticatedAuthor();
|
||||||
if (authenticatedAuthor) {
|
|
||||||
this.actualAuthor = authenticatedAuthor;
|
|
||||||
} else {
|
|
||||||
console.error('Profil mal chargé');
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this.authService.checkSessionExpiration();
|
this.authService.checkSessionExpiration();
|
||||||
}
|
}
|
||||||
|
@ -30,5 +30,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
} @else {
|
} @else {
|
||||||
<app-loading></app-loading>
|
<h1>Loading...</h1>
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import {Button} from 'primeng/button';
|
|||||||
import {DialogModule} from 'primeng/dialog';
|
import {DialogModule} from 'primeng/dialog';
|
||||||
import {UpdateProfileFormComponent} from '../../components/update-profile-form/update-profile-form.component';
|
import {UpdateProfileFormComponent} from '../../components/update-profile-form/update-profile-form.component';
|
||||||
import {AuthService} from '../../auth.service';
|
import {AuthService} from '../../auth.service';
|
||||||
import {LoadingComponent} from '../../components/loading/loading.component';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-profile',
|
selector: 'app-profile',
|
||||||
@ -22,7 +21,6 @@ import {LoadingComponent} from '../../components/loading/loading.component';
|
|||||||
Button,
|
Button,
|
||||||
DialogModule,
|
DialogModule,
|
||||||
UpdateProfileFormComponent,
|
UpdateProfileFormComponent,
|
||||||
LoadingComponent,
|
|
||||||
],
|
],
|
||||||
templateUrl: './profile.component.html',
|
templateUrl: './profile.component.html',
|
||||||
styleUrl: './profile.component.css'
|
styleUrl: './profile.component.css'
|
||||||
@ -45,12 +43,7 @@ export class ProfileComponent implements OnDestroy {
|
|||||||
}));
|
}));
|
||||||
})
|
})
|
||||||
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated()) {
|
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated()) {
|
||||||
const authenticatedAuthor = this.authService.getAuthenticatedAuthor();
|
this.actualAuthor = this.authService.getAuthenticatedAuthor();
|
||||||
if (authenticatedAuthor) {
|
|
||||||
this.actualAuthor = authenticatedAuthor;
|
|
||||||
} else {
|
|
||||||
console.error("Profil mal chargé");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this.authService.checkSessionExpiration();
|
this.authService.checkSessionExpiration();
|
||||||
}
|
}
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
import {definePreset} from '@primeng/themes';
|
|
||||||
import Aura from '@primeng/themes/aura';
|
|
||||||
|
|
||||||
|
|
||||||
export const myPreset = definePreset(Aura, {
|
|
||||||
semantic: {
|
|
||||||
primary: {
|
|
||||||
50: '{indigo.50}',
|
|
||||||
100: '{indigo.100}',
|
|
||||||
200: '{indigo.200}',
|
|
||||||
300: '{indigo.300}',
|
|
||||||
400: '{indigo.400}',
|
|
||||||
500: '{indigo.500}',
|
|
||||||
600: '{indigo.600}',
|
|
||||||
700: '{indigo.700}',
|
|
||||||
800: '{indigo.800}',
|
|
||||||
900: '{indigo.900}',
|
|
||||||
950: '{indigo.950}'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
@ -62,11 +62,6 @@ export class AuthorService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getAvatar(id: string): Observable<string> {
|
|
||||||
return this.httpClient.get(`${this.apiUrl}/${id}/avatar`, { responseType: 'text' });
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
getAuthor(id: string | null): Observable<Author> {
|
getAuthor(id: string | null): Observable<Author> {
|
||||||
if (id) {
|
if (id) {
|
||||||
return this.httpClient.get<Author>(`${this.apiUrl}/${id}`);
|
return this.httpClient.get<Author>(`${this.apiUrl}/${id}`);
|
||||||
@ -94,14 +89,6 @@ export class AuthorService {
|
|||||||
'Authorization': `Bearer ${token}`
|
'Authorization': `Bearer ${token}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return this.httpClient.post<Author>(`${this.apiUrl}/register/admin`, {
|
return this.httpClient.post<Author>(`${this.apiUrl}/register/admin`, {name: username, password: password, role: role}, httpOptions);
|
||||||
name: username,
|
|
||||||
password: password,
|
|
||||||
role: role
|
|
||||||
}, httpOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
getAuthorAvatar(id: string) {
|
|
||||||
return this.httpClient.get<string>(`${this.apiUrl}/${id}/avatar`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Before ![]() (image error) Size: 32 KiB |
@ -7,11 +7,9 @@
|
|||||||
<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="icon.jpg">
|
<link rel="icon" type="image/x-icon" href="icon.jpg">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
|
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="mat-typography">
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<p class="footer-creator">Site web réalisé par <strong>Guams</strong>.</p>
|
<p class="footer-creator">Site web réalisé par <strong>Guams</strong>.</p>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*@import '../node_modules/primeng/resources/themes/lara-light-indigo/theme.css';*/
|
@import '../node_modules/primeng/resources/themes/lara-light-indigo/theme.css';
|
||||||
@import '../node_modules/primeicons/primeicons.css';
|
@import '../node_modules/primeicons/primeicons.css';
|
||||||
@import '../node_modules/quill/dist/quill.bubble.css';
|
@import '../node_modules/quill/dist/quill.bubble.css';
|
||||||
@import '../node_modules/quill/dist/quill.snow.css';
|
@import '../node_modules/quill/dist/quill.snow.css';
|
||||||
@ -53,6 +53,3 @@ app-root {
|
|||||||
.footer-link:hover {
|
.footer-link:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body { height: 100%; }
|
|
||||||
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
|
|
||||||
|
Loading…
Reference in New Issue
Block a user