Code cleaning about publication edition component.
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
<button type="button" class="close" (click)="closeDialog()">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
<header>
|
||||
<h1>Add a code block</h1>
|
||||
</header>
|
||||
<form [formGroup]="formGroup" (submit)="closeAndValidate()" ngNativeValidate>
|
||||
<div class="form-content">
|
||||
<mat-form-field>
|
||||
<mat-label>Programming language</mat-label>
|
||||
<mat-select #programmingLanguageSelect formControlName="programmingLanguage">
|
||||
@for(programmingLanguage of programmingLanguages; track programmingLanguage) {
|
||||
<mat-option [value]="programmingLanguage.code">
|
||||
{{programmingLanguage.label}}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<mat-label>Code block</mat-label>
|
||||
<textarea matInput formControlName="codeBlock"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button type="submit">
|
||||
Validate
|
||||
</button>
|
||||
<button type="button" (click)="closeDialog()" class="secondary">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -0,0 +1,78 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1em;
|
||||
gap: 1em;
|
||||
position: relative;
|
||||
max-height: 90vh;
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
top: 1em;
|
||||
right: 1em;
|
||||
width: 2.5em;
|
||||
height: 2.5em;
|
||||
border-radius: 10em;
|
||||
border: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
header {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
form {
|
||||
div {
|
||||
&.form-content {
|
||||
mat-form-field {
|
||||
width: 100%;
|
||||
|
||||
textarea {
|
||||
height: 30vh;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.actions {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
button {
|
||||
padding: .8em 1.2em;
|
||||
border-radius: 10em;
|
||||
border: none;
|
||||
background-color: #3f51b5;
|
||||
color: white;
|
||||
transition: background-color .2s ease-in-out;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
background-color: #5b6ed8;
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
color: #3f51b5;
|
||||
background-color: white;
|
||||
|
||||
&:hover {
|
||||
background-color: #f2f4ff;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import { Component, inject } from "@angular/core";
|
||||
import { FormBuilder, FormControl, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||
import { MatDialogRef } from "@angular/material/dialog";
|
||||
import { MatFormFieldModule } from "@angular/material/form-field";
|
||||
import { MatIcon } from "@angular/material/icon";
|
||||
import { MatInputModule } from "@angular/material/input";
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
|
||||
export interface ProgramingLanguage {
|
||||
code: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export const PROGRAMMING_LANGUAGES: ProgramingLanguage[] = [
|
||||
{
|
||||
code: 'bash',
|
||||
label: 'Bash'
|
||||
},
|
||||
{
|
||||
code: 'c',
|
||||
label: 'C'
|
||||
},
|
||||
{
|
||||
code: 'cpp',
|
||||
label: 'C++'
|
||||
},
|
||||
{
|
||||
code: 'cs',
|
||||
label: 'C#'
|
||||
},
|
||||
{
|
||||
code: 'lua',
|
||||
label: 'Lua'
|
||||
},
|
||||
{
|
||||
code: 'java',
|
||||
label: 'Java'
|
||||
},
|
||||
{
|
||||
code: 'json5',
|
||||
label: 'JSON'
|
||||
},
|
||||
{
|
||||
code: 'kt',
|
||||
label: 'Kotlin'
|
||||
},
|
||||
{
|
||||
code: 'markup',
|
||||
label: 'html/xml'
|
||||
},
|
||||
{
|
||||
code: 'php',
|
||||
label: 'PHP'
|
||||
},
|
||||
{
|
||||
code: 'plsql',
|
||||
label: 'PL/SQL'
|
||||
},
|
||||
{
|
||||
code: 'python',
|
||||
label: 'Python'
|
||||
},
|
||||
{
|
||||
code: 'powershell',
|
||||
label: 'PowerShell'
|
||||
},
|
||||
{
|
||||
code: 'rust',
|
||||
label: 'Rust'
|
||||
},
|
||||
{
|
||||
code: 'sql',
|
||||
label: 'SQL'
|
||||
},
|
||||
{
|
||||
code: 'ts',
|
||||
label: 'Typescript'
|
||||
},
|
||||
{
|
||||
code: 'yml',
|
||||
label: 'YAML'
|
||||
},
|
||||
];
|
||||
|
||||
@Component({
|
||||
selector: 'app-code-block-dialog',
|
||||
standalone: true,
|
||||
templateUrl: './code-block-dialog.component.html',
|
||||
styleUrl: './code-block-dialog.component.scss',
|
||||
imports: [
|
||||
MatFormFieldModule,
|
||||
MatIcon,
|
||||
MatInputModule,
|
||||
MatSelectModule,
|
||||
ReactiveFormsModule,
|
||||
]
|
||||
})
|
||||
export class CodeBlockDialog {
|
||||
private readonly dialogRef = inject(MatDialogRef<CodeBlockDialog>);
|
||||
private formBuilder = inject(FormBuilder);
|
||||
programmingLanguages = PROGRAMMING_LANGUAGES;
|
||||
formGroup = this.formBuilder.group({
|
||||
programmingLanguage: new FormControl('', Validators.required),
|
||||
codeBlock: new FormControl('', Validators.required)
|
||||
});
|
||||
|
||||
closeAndValidate(): void {
|
||||
if (this.formGroup.valid) {
|
||||
this.dialogRef.close(this.formGroup.value);
|
||||
}
|
||||
}
|
||||
|
||||
closeDialog(): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<button type="button" class="close" (click)="closeDialog()">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
<header>
|
||||
<h1>Select an illustration:</h1>
|
||||
</header>
|
||||
<div class="picture-container">
|
||||
@if (isLoading) {
|
||||
<h2>Pictures loading...</h2>
|
||||
<mat-spinner></mat-spinner>
|
||||
} @else {
|
||||
@if (pictures.length) {
|
||||
@for(picture of pictures; track picture) {
|
||||
<img src="/api/pictures/{{picture.id}}" (click)="selectPicture(picture)" matTooltip="Choose this illustration"/>
|
||||
}
|
||||
} @else {
|
||||
<h2>There is no any picture.</h2>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<footer>
|
||||
<button type="button" class="secondary" matRipple (click)="closeDialog()">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="button" (click)="fileUpload.click()" matRipple>
|
||||
<mat-icon>upload_file</mat-icon>
|
||||
Add new picture
|
||||
</button>
|
||||
<input type="file" (change)="uploadPicture($event)" #fileUpload/>
|
||||
</footer>
|
||||
@@ -0,0 +1,97 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1em;
|
||||
gap: 1em;
|
||||
position: relative;
|
||||
max-height: 90vh;
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
top: 1em;
|
||||
right: 1em;
|
||||
width: 2.5em;
|
||||
height: 2.5em;
|
||||
border-radius: 10em;
|
||||
border: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
header {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.picture-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 1em;
|
||||
max-height: 30em;
|
||||
overflow-y: auto;
|
||||
min-height: 10em;
|
||||
padding: .5em 0;
|
||||
|
||||
img {
|
||||
width: 15em;
|
||||
height: 10em;
|
||||
object-fit: cover;
|
||||
border-radius: 1em;
|
||||
opacity: .9;
|
||||
box-shadow: 0 2px 5px 0 rgba(0,0,0,.16),0 2px 10px 0 rgba(0,0,0,.12);
|
||||
transition: opacity .2s ease-in-out, box-shadow .2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
box-shadow: 0 2px 5px 0 rgba(0,0,0,.32),0 2px 10px 0 rgba(0,0,0,.24);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
button {
|
||||
padding: .8em 1.2em;
|
||||
border-radius: 10em;
|
||||
border: none;
|
||||
background-color: #3f51b5;
|
||||
color: white;
|
||||
transition: background-color .2s ease-in-out;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
background-color: #5b6ed8;
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
color: #3f51b5;
|
||||
background-color: white;
|
||||
|
||||
&:hover {
|
||||
background-color: #f2f4ff;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input[type=file] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { Component, inject, OnInit } from "@angular/core";
|
||||
import { Picture } from "../../../core/rest-services/picture/model/picture";
|
||||
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
|
||||
import { MatSnackBar } from "@angular/material/snack-bar";
|
||||
import { PictureRestService } from "../../../core/rest-services/picture/picture.rest-service";
|
||||
import { MatIcon } from "@angular/material/icon";
|
||||
import { MatDialogRef } from "@angular/material/dialog";
|
||||
import {MatRippleModule} from '@angular/material/core';
|
||||
import { MatTooltip } from "@angular/material/tooltip";
|
||||
|
||||
@Component({
|
||||
selector: 'app-picture-selection',
|
||||
standalone: true,
|
||||
imports: [MatProgressSpinnerModule, MatIcon, MatRippleModule, MatTooltip],
|
||||
templateUrl: './picture-selection-dialog.component.html',
|
||||
styleUrl: './picture-selection-dialog.component.scss',
|
||||
})
|
||||
export class PictureSelectionDialog implements OnInit {
|
||||
private readonly pictureRestService = inject(PictureRestService);
|
||||
private readonly snackBar = inject(MatSnackBar);
|
||||
private readonly dialogRef = inject(MatDialogRef<PictureSelectionDialog>);
|
||||
|
||||
isLoading: boolean = false;
|
||||
isLoaded: boolean = false;
|
||||
pictures: Picture[] = [];
|
||||
|
||||
ngOnInit(): void {
|
||||
this.isLoading = true;
|
||||
this.pictureRestService.getAllOfCurrentUser()
|
||||
.then(pictures => {
|
||||
this.pictures = pictures;
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.status === 401) {
|
||||
this.dialogRef.close();
|
||||
} else {
|
||||
const errorMessage = 'An error occured while loading pictures.';
|
||||
console.error(errorMessage, error);
|
||||
this.snackBar.open(errorMessage, 'Close', { duration: 5000 });
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
this.isLoaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
selectPicture(picture: Picture): void {
|
||||
this.dialogRef.close(picture.id);
|
||||
}
|
||||
|
||||
closeDialog(): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
uploadPicture(fileSelectionEvent: any): void {
|
||||
const pictureFile = fileSelectionEvent.target.files[0];
|
||||
if (pictureFile) {
|
||||
this.pictureRestService.uploadPicture(pictureFile)
|
||||
.then(pictureId => {
|
||||
this.dialogRef.close(pictureId);
|
||||
})
|
||||
.catch(error => {
|
||||
const errorMessage = 'A technical error occured while uploading your picture.';
|
||||
console.error(errorMessage, error);
|
||||
this.snackBar.open(errorMessage, 'Close', { duration: 5000 });
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { inject, Injectable } from "@angular/core";
|
||||
import { PictureRestService } from "../../../core/rest-services/picture/picture.rest-service";
|
||||
import { MatSnackBar } from "@angular/material/snack-bar";
|
||||
import { MatDialogRef } from "@angular/material/dialog";
|
||||
import { PictureSelectionDialog } from "./picture-selection-dialog.component";
|
||||
|
||||
@Injectable()
|
||||
export class PictureSelectionDialogService {
|
||||
private pictureRestService = inject(PictureRestService);
|
||||
private snackBar = inject(MatSnackBar);
|
||||
private readonly dialogRef = inject(MatDialogRef<PictureSelectionDialog>);
|
||||
|
||||
uploadPicture(pictureFile: File): void {
|
||||
this.pictureRestService.uploadPicture(pictureFile)
|
||||
.then(pictureId => {
|
||||
this.dialogRef.close(pictureId);
|
||||
})
|
||||
.catch(error => {
|
||||
const errorMessage = 'An error occured while uploading a picture...';
|
||||
console.error(errorMessage, error);
|
||||
this.snackBar.open(errorMessage, 'Close', { duration: 5000 });
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import { MatTabsModule } from "@angular/material/tabs";
|
||||
import { MatTooltipModule } from "@angular/material/tooltip";
|
||||
import { map, Observable, of, Subscription } from "rxjs";
|
||||
import { Publication } from "../../core/rest-services/publications/model/publication";
|
||||
import { PictureSelectionDialog } from "../../pages/publication-edition/picture-selection-dialog/picture-selection-dialog.component";
|
||||
import { PictureSelectionDialog } from "./picture-selection-dialog/picture-selection-dialog.component";
|
||||
import { SubmitButtonComponent } from "../submit-button/submit-button.component";
|
||||
import { PublicationEditionService } from "./publication-edition.service";
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ import { BehaviorSubject, Observable, Subscription } from "rxjs";
|
||||
import { copy } from "../../core/utils/ObjectUtils";
|
||||
import { Publication } from "../../core/rest-services/publications/model/publication";
|
||||
import { Location } from "@angular/common";
|
||||
import { PictureSelectionDialog } from "../../pages/publication-edition/picture-selection-dialog/picture-selection-dialog.component";
|
||||
import { CodeBlockDialog } from "../../pages/publication-edition/code-block-dialog/code-block-dialog.component";
|
||||
import { PictureSelectionDialog } from "./picture-selection-dialog/picture-selection-dialog.component";
|
||||
import { CodeBlockDialog } from "./code-block-dialog/code-block-dialog.component";
|
||||
|
||||
declare let Prism: any;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user