debug affreux du style de la page de post, implémentation de nouveaux style

This commit is contained in:
Guams 2025-01-18 18:35:06 +01:00
parent 53b18fc4e0
commit b1b5995ea8
16 changed files with 207 additions and 62 deletions

5
package-lock.json generated
View File

@ -24,6 +24,7 @@
"primeicons": "^7.0.0", "primeicons": "^7.0.0",
"primeng": "^17.18.10", "primeng": "^17.18.10",
"quill": "^2.0.3", "quill": "^2.0.3",
"review-front": "file:",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"zone.js": "~0.14.10" "zone.js": "~0.14.10"
@ -11371,6 +11372,10 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/review-front": {
"resolved": "",
"link": true
},
"node_modules/rfdc": { "node_modules/rfdc": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",

View File

@ -27,6 +27,7 @@
"primeicons": "^7.0.0", "primeicons": "^7.0.0",
"primeng": "^17.18.10", "primeng": "^17.18.10",
"quill": "^2.0.3", "quill": "^2.0.3",
"review-front": "file:",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"zone.js": "~0.14.10" "zone.js": "~0.14.10"

View File

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

View File

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

View File

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

View File

@ -1,5 +1,4 @@
import { Component, Input, OnDestroy } from '@angular/core'; import { Component, Input, OnDestroy } from '@angular/core';
import { HeaderComponent } from '../../components/header/header.component';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { InputTextModule } from 'primeng/inputtext'; import { InputTextModule } from 'primeng/inputtext';
import { InputTextareaModule } from 'primeng/inputtextarea'; import { InputTextareaModule } from 'primeng/inputtextarea';
@ -21,8 +20,7 @@ import {AuthorService} from '../../services/author.service';
InputTextModule, InputTextModule,
InputTextareaModule, InputTextareaModule,
FileUploadModule, FileUploadModule,
EditorModule, EditorModule
HeaderComponent
], ],
templateUrl: './post-form.component.html', templateUrl: './post-form.component.html',
styleUrls: ['./post-form.component.css'] styleUrls: ['./post-form.component.css']
@ -38,6 +36,14 @@ export class PostFormComponent implements OnDestroy {
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,
@ -85,9 +91,17 @@ export class PostFormComponent implements OnDestroy {
} }
} }
// transformParagraphs(): void {
// if (this.body) {
// this.body = this.body.replace(/&nbsp;/g, ' ');
// console.log(this.body);
// }
// }
onSubmit(): void { onSubmit(): void {
if (this.form.valid && this.uploadedFile) { if (this.form.valid && this.uploadedFile) {
const formData = this.form.value; const formData = this.form.value;
// this.transformParagraphs()
const postData: any = { const postData: any = {
title: formData.title, title: formData.title,
description: formData.description, description: formData.description,

View File

@ -7,7 +7,6 @@ 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 { CookieService } from 'ngx-cookie-service'; 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';
@ -20,7 +19,6 @@ import {Router} from '@angular/router';
InputTextModule, InputTextModule,
Button, Button,
ToastModule, ToastModule,
JsonPipe,
HeaderComponent HeaderComponent
], ],
templateUrl: './login.component.html', templateUrl: './login.component.html',

View File

@ -19,7 +19,7 @@ export class LogoutComponent implements OnInit{
private messageService: MessageService, private messageService: MessageService,
private router: Router) { } private router: Router) { }
ngOnInit(): void { ngOnInit(): void {
this.cookiesService.deleteAll(); this.cookiesService.deleteAll("/", "localhost");
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'));
} }

View File

@ -1,12 +1,68 @@
::ng-deep * { @import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&family=Spectral:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;0,800;1,200;1,300;1,400;1,500;1,600;1,700;1,800&display=swap');
box-sizing: border-box; @import url('https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400..700;1,400..700&family=Open+Sans:ital,wght@0,300..800;1,300..800&family=Spectral:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;0,800;1,200;1,300;1,400;1,500;1,600;1,700;1,800&display=swap');
.title-div {
text-align: center;
}
em {
color: rgba(55, 56, 58, 0.8);
} }
.body-content { .body-content {
max-width: 100%; margin: auto;
width: 45%;
}
::ng-deep p {
text-align: justify;
text-justify: inter-word;
}
::ng-deep img {
height: auto;
width: 100%;
}
::ng-deep pre {
background-color: #f0f0f0;
padding: 1em;
border-radius: 5px;
}
::ng-deep h2 {
font-family: "Lora", sans-serif;
}
::ng-deep p span {
font-family: "Spectral", serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
@media only screen and (min-width: 1080px) {
::ng-deep p span {
line-height: 30px;
font-size: 19px;
}
::ng-deep h2 {
font-size: 31px;
font-weight: 600;
line-height: 36px;
}
}
@media only screen and (max-width: 1080px) {
.body-content {
width: 100%;
}
}
.commentaires {
display: flex; display: flex;
flex-direction: column;
align-items: center; align-items: center;
gap: 2rem;
flex-direction: column;
margin-top: 5rem;
} }
.comment-div { .comment-div {
@ -15,17 +71,4 @@
gap: 1rem; gap: 1rem;
} }
::ng-deep .body-content * {
white-space: normal;
word-break: break-word;
width: 100%;
}
img {
height: 100%
}
.main-content {
margin: auto;
width: 45%;
}

View File

@ -1,9 +1,15 @@
<app-header></app-header> <app-header></app-header>
<div class="main-content"> <div class="title-div">
<h1>{{ concernedPost?.title }}</h1> <h1>{{ concernedPost?.title }}</h1>
<em>Publié le {{ concernedPost?.publicationDate | date: "dd/MM/yyyy à HH:mm" }}</em> <em>Publié le {{ concernedPost?.publicationDate | date: "dd/MM/yyyy à HH:mm" }}</em>
<div class="body-content" [innerHTML]="concernedPost?.body"></div> </div>
<div [innerHTML]="concernedPost?.body | safeHtml" class="body-content"></div>
<div class="commentaires">
<h1>Commentaires</h1>
@if (actualAuthor) {
{{actualAuthor | json}}
<app-comment-form></app-comment-form>
}
@for (comment of comments; track comment.id) { @for (comment of comments; track comment.id) {
@if (comment.profilePicture) { @if (comment.profilePicture) {
<div class="comment-div"> <div class="comment-div">

View File

@ -9,6 +9,11 @@ import {CommentService} from '../../services/comment.service';
import {MessageService} from 'primeng/api'; import {MessageService} from 'primeng/api';
import {Comment} from '../../models/comment'; import {Comment} from '../../models/comment';
import {AvatarModule} from 'primeng/avatar'; import {AvatarModule} from 'primeng/avatar';
import {CardModule} from 'primeng/card';
import {SafeHtmlPipe} from '../../pipes/safe-html-pipe';
import {CookieService} from 'ngx-cookie-service';
import {Author} from '../../models/author';
import {CommentFormComponent} from '../../components/comment-form/comment-form.component';
@Component({ @Component({
selector: 'app-post', selector: 'app-post',
@ -16,8 +21,11 @@ import {AvatarModule} from 'primeng/avatar';
imports: [ imports: [
HeaderComponent, HeaderComponent,
DatePipe, DatePipe,
JsonPipe, AvatarModule,
AvatarModule CardModule,
SafeHtmlPipe,
CommentFormComponent,
JsonPipe
], ],
templateUrl: './post.component.html', templateUrl: './post.component.html',
styleUrl: './post.component.css' styleUrl: './post.component.css'
@ -26,20 +34,27 @@ export class PostComponent {
subs: Subscription[] = []; subs: Subscription[] = [];
comments: Comment[] = []; comments: Comment[] = [];
concernedPost: Post | undefined; concernedPost: Post | undefined;
actualAuthor: Author | undefined;
constructor(private route: ActivatedRoute, constructor(private route: ActivatedRoute,
private postService: PostService, private postService: PostService,
private commentService: CommentService, private commentService: CommentService,
private messageService: MessageService,) { private messageService: MessageService,
private cookieService: CookieService,) {
this.route.paramMap.subscribe(params => { this.route.paramMap.subscribe(params => {
if (this.cookieService.get('author')) {
this.actualAuthor = JSON.parse(this.cookieService.get('author'))
}
const postId = params.get('postId'); const postId = params.get('postId');
if (postId) { if (postId) {
this.subs.push( this.subs.push(
forkJoin({ forkJoin({
post: this.postService.getPost(BigInt(postId)), post: this.postService.getPost(BigInt(postId)),
comment: this.commentService.list(BigInt(postId)) comment: this.commentService.list(BigInt(postId))
}).subscribe({ }).subscribe({
next: res => { next: res => {
res.post.body = res.post.body.replace(/&nbsp;/g, ' ')
this.concernedPost = res.post; this.concernedPost = res.post;
this.comments = res.comment.sort((a, b) => { this.comments = res.comment.sort((a, b) => {
if (a.commentDate < b.commentDate) { if (a.commentDate < b.commentDate) {
@ -53,6 +68,9 @@ export class PostComponent {
error: err => this.failureMessage("Erreur", err.message) error: err => this.failureMessage("Erreur", err.message)
})); }));
} }
if (this.concernedPost?.body) {
this.concernedPost.body = this.concernedPost.body.replace(/&nbsp;/g, ' ');
}
}); });
} }

View File

@ -1,6 +1,23 @@
.content { .content {
margin-top: 5rem;
width: 45%;
display: flex; display: flex;
align-items: stretch; align-items: center;
flex-direction: column; flex-direction: row;
justify-content: space-between;
gap: 3em; gap: 3em;
} }
h2 {
margin-bottom: 0.1em;
}
em {
color: rgba(55, 56, 58, 0.8);
margin: 0;
}
.user-info {
display: flex;
flex-direction: column;
}

View File

@ -1,12 +1,21 @@
<app-header></app-header> <app-header></app-header>
{{ concernedAuthor | json }}
<div class="content"> <div class="content">
<p-avatar image="https://primefaces.org/cdn/primeng/images/demo/avatar/amyelsner.png" class="mr-2" size="xlarge" <div class="user-info">
shape="circle"/> <h2>{{ authorName }}</h2>
<p-button label="Voir les posts de l'utilisateur"></p-button> <em>{{ concernedAuthor?.role }}</em>
@if (concernedAuthor?.id === actualAuthor?.id) { </div>
<p-button label="Mettre à jour les données du profil"/> <div class="profile-picture">
<p-button label="Changer le mot de passe"/> @if (concernedAuthor?.avatar) {
} <p-avatar image="data:image/jpeg;base64,{{ concernedAuthor?.avatar }}" styleClass="mr-2" size="xlarge"></p-avatar>
} @else {
<p-avatar label="{{ authorName.charAt(0).toUpperCase() }}" styleClass="mr-2" size="xlarge"></p-avatar>
}
</div>
<!-- <p-button label="Voir les posts de l'utilisateur"></p-button>-->
<!-- @if (concernedAuthor?.id === actualAuthor?.id) {-->
<!-- <p-button label="Mettre à jour les données du profil"/>-->
<!-- <p-button label="Changer le mot de passe"/>-->
<!-- }-->
</div> </div>

View File

@ -1,11 +1,10 @@
import {Component, OnDestroy} from '@angular/core'; import {Component, OnDestroy} from '@angular/core';
import {HeaderComponent} from '../../components/header/header.component'; import {HeaderComponent} from '../../components/header/header.component';
import {ActivatedRoute, Router} from '@angular/router'; import {ActivatedRoute} 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 {AvatarModule} from 'primeng/avatar'; import {AvatarModule} from 'primeng/avatar';
import {CardModule} from 'primeng/card'; import {CardModule} from 'primeng/card';
import {Button} from 'primeng/button'; import {Button} from 'primeng/button';
@ -15,7 +14,6 @@ import {Button} from 'primeng/button';
standalone: true, standalone: true,
imports: [ imports: [
HeaderComponent, HeaderComponent,
JsonPipe,
AvatarModule, AvatarModule,
CardModule, CardModule,
Button Button
@ -23,16 +21,19 @@ import {Button} from 'primeng/button';
templateUrl: './profile.component.html', templateUrl: './profile.component.html',
styleUrl: './profile.component.css' styleUrl: './profile.component.css'
}) })
export class ProfileComponent implements OnDestroy{ export class ProfileComponent implements OnDestroy {
actualAuthor: Author | undefined; actualAuthor: Author | undefined;
concernedAuthor: Author | undefined; concernedAuthor: Author | undefined;
authorName: string = "";
subs: Subscription[] = []; subs: Subscription[] = [];
constructor(private route: ActivatedRoute, constructor(private route: ActivatedRoute,
private authorService: AuthorService, private authorService: AuthorService,
private cookieService: CookieService) { private cookieService: CookieService) {
this.route.paramMap.subscribe(params => { this.route.paramMap.subscribe(params => {
this.subs.push(this.authorService.getAuthor(params.get('authorId')).subscribe(author => { this.subs.push(this.authorService.getAuthor(params.get('authorId')).subscribe(author => {
this.concernedAuthor = author; this.concernedAuthor = author;
this.authorName = author.name;
})); }));
}) })
if (this.cookieService.get('author')) { if (this.cookieService.get('author')) {

View File

@ -0,0 +1,14 @@
import {DomSanitizer} from '@angular/platform-browser';
import {Pipe} from '@angular/core';
@Pipe({
name: "safeHtml",
standalone: true,
})
export class SafeHtmlPipe {
constructor(private sanitizer: DomSanitizer) {}
transform(html: any) {
return this.sanitizer.bypassSecurityTrustHtml(html);
}
}