Initial Implementation of Help and My Profile Popup

This commit is contained in:
2023-08-26 05:48:08 -03:00
parent 1328ca7c4c
commit bd8719654c
18 changed files with 550 additions and 51 deletions

View File

@@ -0,0 +1,7 @@
.help-container {
max-width: 400px;
}
p {
color: #555555;
}

View File

@@ -0,0 +1,28 @@
<app-popup [state]="state"
(stateChange)="onStateChange($event)"
[ignoreClickOutside]="ignoreClickOutside">
<div class="help-container container m-0 overflow-hidden">
<p>
This is a simple example project to demonstrate
User Authentication and Authorization using
<a href="https://spring.io/projects/spring-security" target="_blank">Spring Security</a>
and
<a href="https://docs.spring.io/spring-security/reference/servlet/oauth2/" target="_blank">OAuth2</a>.
<br/><br/>
The only data stored is your email address, username and name.
This data is stored in a database and is used to authenticate you
and will not be used for any other purpose.
<br/><br/>
All data can be deleted by clicking the "Delete Account" button
on the "My Profile" option.
</p>
</div>
</app-popup>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HelpComponent } from './help.component';
describe('HelpComponent', () => {
let component: HelpComponent;
let fixture: ComponentFixture<HelpComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ HelpComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(HelpComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,24 @@
import {Component, EventEmitter, Input, Output} from '@angular/core';
@Component({
selector: 'app-help',
templateUrl: './help.component.html',
styleUrls: ['./help.component.css']
})
export class HelpComponent {
@Input()
state: boolean = false;
@Input()
ignoreClickOutside!: HTMLDivElement[];
@Output()
stateChange = new EventEmitter<boolean>();
constructor() { }
onStateChange(state: boolean) {
this.stateChange.emit(state);
}
}

View File

@@ -0,0 +1,98 @@
* {
margin: 0;
}
.profile-container .row {
display: flex;
flex-direction: column;
justify-content: center;
}
.profile-container {
justify-content: space-around;
display: flex !important;
flex-direction: column;
align-content: center;
align-items: center;
height: 200px;
max-width: 400px;
}
.separator-line {
justify-content: center;
align-content: center;
align-items: center;
margin: 30px 0;
display: flex;
width: 100%;
}
.line {
width: 100%;
border-bottom: 2px solid #80808076;
border-radius: 50px;
}
.profile-container button {
text-decoration: none;
border-radius: 8px;
color: #ffffff;
font-weight: 500;
font-size: 16px;
border: none;
height: 50px;
width: 150px;
}
.picture-btn {
background-color: rgba(118, 118, 118, 0.7) !important;
}
.delete-btn {
background-color: rgba(216, 41, 28, 0.7) !important;
}
@media (min-width:767px) {
.profile-container {
all: unset;
justify-content: space-around;
width: 600px;
align-items: center;
display: flex;
height: 200px;
}
.profile-container .row {
all: unset;
display: flex;
height: 200px;
}
.btn-container {
justify-content: center;
align-content: center;
align-items: center;
display: flex;
}
.separator-line {
all: unset;
justify-content: center;
align-content: center;
align-items: center;
margin: 0px 60px;
display: flex;
height: 100%;
}
.line {
all: unset;
border-right: 2px solid #80808076;
border-radius: 50px;
height: 100%;
}
}

View File

@@ -0,0 +1,42 @@
<app-popup [state]="state"
(stateChange)="onStateChange($event)"
[ignoreClickOutside]="ignoreClickOutside">
<div class="container m-0 overflow-hidden"
[@resizeContainerForErrorMessage]="hideErrorMessage()">
<app-error-box [errorMessage]="errorMessage"
[@showErrorMessage]="showErrorMessage()">
</app-error-box>
<div class="container profile-container"
[@hideAuthContainer]="hideErrorMessage()"
(@hideAuthContainer.done)="hideAuthContainer($event)">
<div class="row">
<div class="btn-container">
<button class="picture-btn"
(click)="onAddProfilePicture()">
Add Profile Picture
</button>
</div>
<div class="separator-line">
<div class="line"></div>
</div>
<div class="btn-container">
<button class="delete-btn"
(click)="onDeleteAccount()">
Delete Account
</button>
</div>
</div>
</div>
</div>
</app-popup>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MyProfileComponent } from './my-profile.component';
describe('MyProfileComponent', () => {
let component: MyProfileComponent;
let fixture: ComponentFixture<MyProfileComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ MyProfileComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(MyProfileComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,162 @@
import {ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {AuthService} from "../../../shared/auth/auth.service";
import {User} from "../../../shared/model/user/user.model";
import {animate, animateChild, group, query, state, style, transition, trigger} from "@angular/animations";
import {MatIconRegistry} from "@angular/material/icon";
import {DomSanitizer} from "@angular/platform-browser";
import {FormControl, FormGroup, Validators} from "@angular/forms";
import {ValidateNotEmptyValidator} from "../../../shared/validators/validate-not-empty.validator";
import {ValidatePasswordValidator} from "../../../shared/validators/validate-password.validator";
import {first, take} from "rxjs";
import UserChecker from "../../../shared/model/user/user.checker";
import HttpErrorChecker from "../../../shared/model/httpError/httpErrorChecker";
import {HttpError} from "../../../shared/model/httpError/httpError.model";
@Component({
selector: 'app-my-profile',
templateUrl: './my-profile.component.html',
styleUrls: ['./my-profile.component.css'],
animations: [
trigger('resizeContainerForErrorMessage', [
state('hide',
style({
height: '100px',
width: '320px',
})
),
transition(
'show => hide',
group([
query(
"@*",
animateChild(),
{ optional: true }
),
animate('1s ease')
])
)
]),
trigger('showErrorMessage', [
state('show',
style({
opacity: 1,
height: '100px',
width: '320px',
})
),
state('hide',
style({
opacity: 0,
height: '0px',
width: '0px',
})
),
transition(
'* => show',
animate(
'500ms ease-in'
)
),
]),
trigger('hideAuthContainer', [
state('hide',
style({
opacity: 0,
})
),
transition(
'show => hide',
group([
query(
"@*",
animateChild(),
{ optional: true }
),
animate(
'250ms ease-out'
)
])
)
]),
]
})
export class MyProfileComponent implements OnInit {
@Input()
state: boolean = false;
@Input()
user!: User | null;
@Input()
ignoreClickOutside!: HTMLDivElement[];
@Output()
stateChange = new EventEmitter<boolean>();
alterForm!: FormGroup;
errorMessage!: string | null;
isShowErrorMessage = false;
constructor(private authService: AuthService) {
}
ngOnInit(): void {
this.alterForm = new FormGroup({
'username': new FormControl(null, [Validators.required, ValidateNotEmptyValidator]),
'password': new FormControl(null, [Validators.required, ValidatePasswordValidator])
});
this.errorMessage = null;
}
onStateChange(state: boolean) {
this.stateChange.emit(state);
}
public showErrorMessage(): string {
if (this.isShowErrorMessage) {
return "show";
}
return "hide";
}
public hideErrorMessage(): string {
if (!!this.errorMessage) {
return "hide";
}
return "show";
}
public onDeleteAccount() {
this.authService.deleteAccount().subscribe({
next: (res) => {
if (res && UserChecker.test(res)) {
this.closePopup()
} if (HttpErrorChecker.test(res)) {
this.errorMessage = (<HttpError>res).details;
}
}
})
// this.authService.logout()
// this.onStateChange(false);
}
public onAddProfilePicture() {
this.authService.addProfilePicture()
}
hideAuthContainer(event: any) {
if (event.toState === "hide") {
event.element.style.display = "none";
this.isShowErrorMessage = true;
}
}
private closePopup() {
this.onStateChange(false);
}
}