Add submit button component and style it.
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
<button type="submit" [class]="color" [disabled]="disabled || requestPending" (click)="click.emit()">
|
||||||
|
@if (requestPending) {
|
||||||
|
<mat-spinner class="spinner {{color}}" [diameter]="25"></mat-spinner>
|
||||||
|
}
|
||||||
|
<span>
|
||||||
|
{{ label }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
button {
|
||||||
|
padding: .8em 1.2em;
|
||||||
|
border-radius: 10em;
|
||||||
|
border: none;
|
||||||
|
background-color: #3f51b5;
|
||||||
|
color: white;
|
||||||
|
transition: background-color .2s ease-in-out;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #5b6ed8;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.secondary {
|
||||||
|
color: #3f51b5;
|
||||||
|
background-color: white;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f2f4ff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: #6d7ac5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-spinner {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(50% - 12px);
|
||||||
|
left: calc(50% - 12px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .spinner circle {
|
||||||
|
stroke: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .spinner.secondary circle {
|
||||||
|
stroke: #3f51b5;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
||||||
|
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-submit-button',
|
||||||
|
standalone: true,
|
||||||
|
imports: [MatProgressSpinnerModule, CommonModule],
|
||||||
|
templateUrl: 'submit-button.component.html',
|
||||||
|
styleUrl: 'submit-button.component.scss'
|
||||||
|
})
|
||||||
|
export class SubmitButtonComponent {
|
||||||
|
@Input() requestPending: boolean = false;
|
||||||
|
@Input() label: string = '';
|
||||||
|
@Input() disabled: boolean = false;
|
||||||
|
@Input() color?: 'secondary';
|
||||||
|
@Output() click = new EventEmitter<void>();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -16,4 +16,8 @@ export class PublicationRestService {
|
|||||||
getById(publicationId: string): Promise<Publication> {
|
getById(publicationId: string): Promise<Publication> {
|
||||||
return lastValueFrom(this.httpClient.get<Publication>(`/api/publications/${publicationId}`));
|
return lastValueFrom(this.httpClient.get<Publication>(`/api/publications/${publicationId}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update(publication: Publication): Promise<void> {
|
||||||
|
return lastValueFrom(this.httpClient.put<void>(`/api/publications/${publication.id}`, publication));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,20 @@
|
|||||||
color: #3f51b5;
|
color: #3f51b5;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button, a.button {
|
||||||
|
padding: .8em 1.2em;
|
||||||
|
border-radius: 10em;
|
||||||
|
border: none;
|
||||||
|
background-color: #3f51b5;
|
||||||
|
color: white;
|
||||||
|
transition: background-color .2s ease-in-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #5b6ed8;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export class LoginComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
const passwordSubscription = this.loginForm.controls['password'].valueChanges
|
const passwordSubscription = this.loginForm.controls['password'].valueChanges
|
||||||
.pipe(
|
.pipe(
|
||||||
debounceTime(300),
|
debounceTime(100),
|
||||||
map(value => value?.length ? value as string : '')
|
map(value => value?.length ? value as string : '')
|
||||||
)
|
)
|
||||||
.subscribe(password => {
|
.subscribe(password => {
|
||||||
|
|||||||
@@ -42,9 +42,7 @@
|
|||||||
</mat-tab-group>
|
</mat-tab-group>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<button type="submit">
|
<app-submit-button label="Save" [requestPending]="(isSaving$ | async) == true"></app-submit-button>
|
||||||
Save
|
|
||||||
</button>
|
|
||||||
<button type="button" class="secondary" (click)="goPreviousLocation()">
|
<button type="button" class="secondary" (click)="goPreviousLocation()">
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -119,3 +119,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .test circle {
|
||||||
|
stroke: white;
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ import { CommonModule, Location } from '@angular/common';
|
|||||||
import { Component, inject, OnDestroy, OnInit } from '@angular/core';
|
import { Component, inject, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatProgressSpinner } from '@angular/material/progress-spinner';
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
import { MatTabsModule } from '@angular/material/tabs';
|
import { MatTabsModule } from '@angular/material/tabs';
|
||||||
import { debounceTime, map, Observable, Subscription } from 'rxjs';
|
import { debounceTime, map, Observable, Subscription } from 'rxjs';
|
||||||
import { Publication } from '../../core/rest-services/publications/model/publication';
|
import { Publication } from '../../core/rest-services/publications/model/publication';
|
||||||
@@ -10,11 +10,22 @@ import { PublicationEditionService } from './publication-edition.service';
|
|||||||
import {MatDialogModule} from '@angular/material/dialog';
|
import {MatDialogModule} from '@angular/material/dialog';
|
||||||
import { PictureSelectionDialog } from './picture-selection-dialog/picture-selection-dialog.component';
|
import { PictureSelectionDialog } from './picture-selection-dialog/picture-selection-dialog.component';
|
||||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
import { SubmitButtonComponent } from '../../components/submit-button/submit-button.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-publication-edition',
|
selector: 'app-publication-edition',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [ReactiveFormsModule, MatInputModule, MatProgressSpinner, MatTabsModule, MatDialogModule, CommonModule, PictureSelectionDialog, MatTooltipModule],
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
MatDialogModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatProgressSpinnerModule,
|
||||||
|
MatTabsModule,
|
||||||
|
MatTooltipModule,
|
||||||
|
PictureSelectionDialog,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
SubmitButtonComponent
|
||||||
|
],
|
||||||
templateUrl: './publication-edition.component.html',
|
templateUrl: './publication-edition.component.html',
|
||||||
styleUrl: './publication-edition.component.scss',
|
styleUrl: './publication-edition.component.scss',
|
||||||
providers: [PublicationEditionService]
|
providers: [PublicationEditionService]
|
||||||
@@ -38,6 +49,10 @@ export class PublicationEditionComponent implements OnInit, OnDestroy {
|
|||||||
return this.publicationEditionService.isLoading$;
|
return this.publicationEditionService.isLoading$;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isSaving$(): Observable<boolean> {
|
||||||
|
return this.publicationEditionService.isSaving$;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
['title', 'description', 'text'].forEach(fieldName => {
|
['title', 'description', 'text'].forEach(fieldName => {
|
||||||
const fieldSubscription = this.publicationEditionForm.controls[fieldName].valueChanges
|
const fieldSubscription = this.publicationEditionForm.controls[fieldName].valueChanges
|
||||||
@@ -86,7 +101,7 @@ export class PublicationEditionComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
save(): void {
|
save(): void {
|
||||||
|
this.publicationEditionService.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
displayPictureSectionDialog(): void {
|
displayPictureSectionDialog(): void {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Location } from "@angular/common";
|
import { Location } from "@angular/common";
|
||||||
import { inject, Injectable, OnDestroy } from "@angular/core";
|
import { inject, Injectable, OnDestroy } from "@angular/core";
|
||||||
import { MatSnackBar } from "@angular/material/snack-bar";
|
import { MatSnackBar } from "@angular/material/snack-bar";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { BehaviorSubject, Observable, Subscription } from "rxjs";
|
import { BehaviorSubject, Observable, Subscription, timeout } from "rxjs";
|
||||||
import { Publication } from "../../core/rest-services/publications/model/publication";
|
import { Publication } from "../../core/rest-services/publications/model/publication";
|
||||||
import { PublicationRestService } from "../../core/rest-services/publications/publication.rest-service";
|
import { PublicationRestService } from "../../core/rest-services/publications/publication.rest-service";
|
||||||
import { copy } from '../../core/utils/ObjectUtils';
|
import { copy } from '../../core/utils/ObjectUtils';
|
||||||
@@ -32,12 +32,14 @@ export class PublicationEditionService implements OnDestroy {
|
|||||||
private readonly activatedRoute = inject(ActivatedRoute);
|
private readonly activatedRoute = inject(ActivatedRoute);
|
||||||
private readonly publicationRestService = inject(PublicationRestService);
|
private readonly publicationRestService = inject(PublicationRestService);
|
||||||
private readonly location = inject(Location);
|
private readonly location = inject(Location);
|
||||||
|
private readonly router = inject(Router);
|
||||||
private readonly snackBar = inject(MatSnackBar);
|
private readonly snackBar = inject(MatSnackBar);
|
||||||
private readonly dialog = inject(MatDialog);
|
private readonly dialog = inject(MatDialog);
|
||||||
|
|
||||||
private isLoadingSubject = new BehaviorSubject<boolean>(false);
|
private isLoadingSubject = new BehaviorSubject<boolean>(false);
|
||||||
private publicationSubject = new BehaviorSubject<Publication>(copy(DEFAULT_PUBLICATION));
|
private publicationSubject = new BehaviorSubject<Publication>(copy(DEFAULT_PUBLICATION));
|
||||||
private subscriptions: Subscription[] = [];
|
private subscriptions: Subscription[] = [];
|
||||||
|
private isSavingSubject = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.subscriptions.forEach(subscription => subscription.unsubscribe());
|
this.subscriptions.forEach(subscription => subscription.unsubscribe());
|
||||||
@@ -55,6 +57,10 @@ export class PublicationEditionService implements OnDestroy {
|
|||||||
return this.isLoadingSubject.asObservable();
|
return this.isLoadingSubject.asObservable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isSaving$(): Observable<boolean> {
|
||||||
|
return this.isSavingSubject.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
get publication$(): Observable<Publication> {
|
get publication$(): Observable<Publication> {
|
||||||
return this.publicationSubject.asObservable();
|
return this.publicationSubject.asObservable();
|
||||||
}
|
}
|
||||||
@@ -117,4 +123,22 @@ export class PublicationEditionService implements OnDestroy {
|
|||||||
});
|
});
|
||||||
this.subscriptions.push(afterDialogCloseSubscription);
|
this.subscriptions.push(afterDialogCloseSubscription);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
save(): void {
|
||||||
|
const publication = this._publication;
|
||||||
|
|
||||||
|
this.isSavingSubject.next(true);
|
||||||
|
setTimeout(() => this.isSavingSubject.next(false), 1500);
|
||||||
|
// this.publicationRestService.update(publication)
|
||||||
|
// .then(() => {
|
||||||
|
// this.snackBar.open('Publication updated succesfully!', 'Close', { duration: 5000 });
|
||||||
|
// this.router.navigate(['/home']);
|
||||||
|
// })
|
||||||
|
// .catch(error => {
|
||||||
|
// const errorMessage = 'An error occured while saving publication modifications.';
|
||||||
|
// console.error(errorMessage, error);
|
||||||
|
// this.snackBar.open(errorMessage, 'Close', { duration: 5000 });
|
||||||
|
// })
|
||||||
|
// .finally(() => this.isSavingSubject.next(false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user