Add project files.
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { UserProfileComponent } from './user-profile/user-profile.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'signin',
|
||||
pathMatch: 'full',
|
||||
},
|
||||
{
|
||||
path: 'user/profile',
|
||||
component: UserProfileComponent,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class PagesRoutingModule {
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { PagesRoutingModule } from './pages-routing.module';
|
||||
import { UserProfileComponent } from './user-profile/user-profile.component';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { ComponentsModule } from '../shared/components/components.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
UserProfileComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
ComponentsModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
PagesRoutingModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatIconModule,
|
||||
MatButtonModule,
|
||||
],
|
||||
})
|
||||
export class PagesModule {
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
<section class="content">
|
||||
<div class="content-block">
|
||||
<div class="block-header">
|
||||
<!-- breadcrumb -->
|
||||
<app-breadcrumb [title]="'Profile'" [items]="['User']" [active_item]="'Profile'">
|
||||
</app-breadcrumb>
|
||||
</div>
|
||||
<div class="row clearfix">
|
||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<h2><strong>User</strong> Profile</h2>
|
||||
</div>
|
||||
<div class="body">
|
||||
<form class="m-4" [formGroup]="form!" (ngSubmit)="onSubmit()">
|
||||
<div class="row">
|
||||
<div class="col-xl-12 col-lg-12 col-md-12 col-sm-12 mb-2">
|
||||
<mat-form-field class="example-full-width" appearance="fill">
|
||||
<mat-label>Email</mat-label>
|
||||
<input matInput formControlName="email" autocomplete="off">
|
||||
<mat-icon class="material-icons-two-tone color-icon p-3" matSuffix>email</mat-icon>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 mb-2">
|
||||
<mat-form-field class="example-full-width" appearance="fill">
|
||||
<mat-label>First name</mat-label>
|
||||
<input matInput formControlName="firstName" autocomplete="off">
|
||||
<mat-icon class="material-icons-two-tone color-icon p-3" matSuffix>face</mat-icon>
|
||||
<mat-error *ngIf="form.get('firstName')?.hasError('pattern')">
|
||||
Only characters or numbers allowed
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 mb-2">
|
||||
<mat-form-field class="example-full-width" appearance="fill">
|
||||
<mat-label>Last name</mat-label>
|
||||
<input matInput formControlName="lastName" autocomplete="off">
|
||||
<mat-icon class="material-icons-two-tone color-icon p-3" matSuffix>face</mat-icon>
|
||||
<mat-error *ngIf="form.get('lastName')?.hasError('pattern')">
|
||||
Only characters or numbers allowed
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-12 col-lg-12 col-md-12 col-sm-12 mb-2">
|
||||
<mat-form-field class="example-full-width" appearance="fill">
|
||||
<mat-label>Full name</mat-label>
|
||||
<input matInput formControlName="fullName" autocomplete="off">
|
||||
<mat-icon class="material-icons-two-tone color-icon p-3" matSuffix>face</mat-icon>
|
||||
<mat-error *ngIf="form.get('fullName')?.hasError('whitespace')">
|
||||
Full name is empty
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-12 col-lg-12 col-md-12 col-sm-12 mb-2">
|
||||
<button class="btn-space" [disabled]="!form.valid" mat-raised-button color="primary">
|
||||
Submit
|
||||
</button>
|
||||
<button type="button" mat-button>Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
|
||||
<div class="card">
|
||||
<div class="header">
|
||||
<h2>
|
||||
<strong>Providers</strong>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="row" *ngFor="let provider of providers">
|
||||
<div class="col-md-1">
|
||||
<mat-icon class="material-icons-two-tone color-icon" color="secondary" *ngIf="!provider.isConnected" matSuffix>link</mat-icon>
|
||||
<mat-icon class="material-icons-two-tone color-icon" color="accent" *ngIf="provider.isConnected && !provider.isEmailConfirmed" title="Email not confirmed" matSuffix>email</mat-icon>
|
||||
<mat-icon class="material-icons-two-tone color-icon" color="primary" *ngIf="provider.isConnected && provider.isEmailConfirmed" title="Email confirmed" matSuffix>mark_email_read</mat-icon>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
{{provider.name}}
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<span class="fl-r" *ngIf="!provider.isConnected">
|
||||
<button color="accent" class="btn-block mw-100-imp"
|
||||
mat-raised-button
|
||||
(click)="connect(provider)"
|
||||
[disabled]="provider.isLoading"
|
||||
[class.spinner]="provider.isLoading"
|
||||
type="button">
|
||||
Connect
|
||||
</button>
|
||||
</span>
|
||||
<span class="fl-r" *ngIf="provider.isConnected">
|
||||
<button color="warn" class="btn-block mw-100-imp"
|
||||
mat-raised-button
|
||||
(click)="disconnect(provider.provider)"
|
||||
[disabled]="provider.isLoading"
|
||||
[class.spinner]="provider.isLoading"
|
||||
type="button">
|
||||
Disconect
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { UserProfileComponent } from './user-profile.component';
|
||||
|
||||
describe('UserProfileComponent',
|
||||
() => {
|
||||
let component: UserProfileComponent;
|
||||
let fixture: ComponentFixture<UserProfileComponent>;
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [UserProfileComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
);
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(UserProfileComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('should create',
|
||||
() => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,139 @@
|
||||
// angular
|
||||
import { Component } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { UntypedFormBuilder } from '@angular/forms';
|
||||
import { UntypedFormGroup } from '@angular/forms';
|
||||
import { Validators } from '@angular/forms';
|
||||
import { FormControl } from '@angular/forms';
|
||||
|
||||
// libs
|
||||
import Swal from 'sweetalert2';
|
||||
|
||||
// app
|
||||
import { ApiRoutes } from '../../api-routes';
|
||||
import { ExternalLoginConfig } from '../../config.external-login';
|
||||
import { AuthService } from '../../core/service/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-blank',
|
||||
templateUrl: './user-profile.component.html',
|
||||
styleUrls: ['./user-profile.component.scss'],
|
||||
})
|
||||
export class UserProfileComponent {
|
||||
form: UntypedFormGroup;
|
||||
providers?: ProviderModel[];
|
||||
|
||||
constructor(
|
||||
private fb: UntypedFormBuilder,
|
||||
private httpClient: HttpClient,
|
||||
private authService: AuthService,
|
||||
) {
|
||||
this.form = this.fb.group({
|
||||
firstName: ['', [Validators.pattern('[a-zA-Z0-9]+')]],
|
||||
lastName: ['', [Validators.pattern('[a-zA-Z0-9]+')]],
|
||||
fullName: ['', [Validators.pattern('[a-zA-Z0-9 ]+')]],
|
||||
email: [{ value: '', disabled: true }, []],
|
||||
});
|
||||
|
||||
this.providers = ExternalLoginConfig
|
||||
.getConfiguredProviders()
|
||||
.map(x => <ProviderModel>{
|
||||
provider: x.provider,
|
||||
name: x.name,
|
||||
isConnected: false,
|
||||
isEmailConfirmed: false
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.load();
|
||||
}
|
||||
|
||||
private updateForm(data: ProfileModel) {
|
||||
this.form.patchValue(data);
|
||||
}
|
||||
|
||||
private load() {
|
||||
this.httpClient.get<ProfileModel>(ApiRoutes.UserProfile).subscribe(data => {
|
||||
this.updateForm(data);
|
||||
|
||||
this.providers?.map((provider) => {
|
||||
var attached = data.providers?.find(x => x.provider === provider.provider);
|
||||
provider.isConnected = false;
|
||||
provider.isEmailConfirmed = false;
|
||||
if (attached) {
|
||||
provider.isConnected = true;
|
||||
provider.isEmailConfirmed = attached.isEmailConfirmed;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
if (this.form.invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.httpClient.post<ProfileModel>(ApiRoutes.UserProfile, this.form.value).subscribe(data => {
|
||||
this.updateForm(data);
|
||||
console.log(this.form);
|
||||
});
|
||||
}
|
||||
|
||||
connect(provider: ProviderModel) {
|
||||
provider.error = undefined;
|
||||
provider.isLoading = true;
|
||||
|
||||
if (provider.provider === ExternalLoginConfig.GOOGLE) {
|
||||
this.authService.attachGoogle().subscribe(r => {
|
||||
this.load();
|
||||
provider.isLoading = false;
|
||||
});
|
||||
}
|
||||
if (provider.provider === ExternalLoginConfig.AUTH0) {
|
||||
this.authService.attachAuth0().subscribe(resp => {
|
||||
provider.error = '';
|
||||
if (!resp.success) {
|
||||
provider.error = resp.data || 'Connect failed';
|
||||
}
|
||||
this.load();
|
||||
provider.isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
disconnect(provider: string) {
|
||||
Swal.fire({
|
||||
title: 'Are you sure to disconnect ' + provider + ' ?',
|
||||
text: 'Some providers can be connected only with login!',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#3085d6',
|
||||
cancelButtonColor: '#d33',
|
||||
confirmButtonText: 'Yes, disconnect it!',
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
this.authService.deattach(provider).subscribe(x => {
|
||||
this.load();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
interface ProviderModel {
|
||||
provider: string;
|
||||
name: string;
|
||||
isConnected: boolean;
|
||||
isEmailConfirmed: boolean;
|
||||
error?: string;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
interface ProfileModel {
|
||||
firstName?: string,
|
||||
lastName?: string,
|
||||
fullName?: string,
|
||||
isEmailConfirmed?: boolean,
|
||||
providers?: ProviderModel[],
|
||||
}
|
||||
Reference in New Issue
Block a user