implement part 1
CureNetMulti/pipeline/head This commit looks good Details
CureNetMulti/pipeline/pr-dev This commit looks good Details

This commit is contained in:
Sieciech 2021-08-04 19:11:43 +02:00
parent f52e611c67
commit 0b4d1afb97
30 changed files with 492 additions and 67 deletions

View File

@ -35,7 +35,7 @@ export const appRoutes: AppRoute[] = [
},
{
path: 'profile',
component: NotFoundComponent,
loadChildren: () => import('./modules/profile/profile.module').then(m => m.ProfileModule),
menuEntries: [
{
label: 'MENU.PROFILE',

View File

@ -14,40 +14,42 @@ import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { TokenInterceptor } from './modules/shared/interceptors/token/token.interceptor';
import { AuthTokenService } from './modules/shared/services/auth/auth-token.service';
import { AuthService } from './modules/auth/services/auth/auth.service';
import { CommonModule } from '@angular/common';
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
return new TranslateHttpLoader(http, '/assets/lang/', '.json');
return new TranslateHttpLoader(http, '/assets/lang/', '.json');
}
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
MaterialModule,
FormsModule,
ReactiveFormsModule,
HttpClientModule,
TranslateModule.forRoot({
defaultLanguage: 'en',
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
}),
],
providers: [
AppService,
ThemeService,
BrowserStorageService,
AuthTokenService,
AuthService,
{ provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true }
],
bootstrap: [AppComponent]
declarations: [
AppComponent,
],
imports: [
CommonModule,
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
MaterialModule,
FormsModule,
ReactiveFormsModule,
HttpClientModule,
TranslateModule.forRoot({
defaultLanguage: 'en',
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
}),
],
providers: [
AppService,
ThemeService,
BrowserStorageService,
AuthTokenService,
AuthService,
{ provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true }
],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@ -8,16 +8,16 @@ import { AdminUserService } from './services/admin-user/admin-user.service';
@NgModule({
declarations: [
UsersComponent
],
imports: [
CommonModule,
AdminRoutingModule,
SharedModule,
],
providers: [
AdminUserService,
],
declarations: [
UsersComponent
],
imports: [
CommonModule,
AdminRoutingModule,
SharedModule,
],
providers: [
AdminUserService,
],
})
export class AdminModule { }

View File

@ -8,6 +8,9 @@ import { MaterialModule } from '../material/material.module';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AuthService } from './services/auth/auth.service';
import { NgStackFormsModule } from '@ng-stack/forms';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { HttpLoaderFactory } from 'src/app/app.module';
import { HttpClient } from '@angular/common/http';
@NgModule({
declarations: [
@ -21,6 +24,14 @@ import { NgStackFormsModule } from '@ng-stack/forms';
FormsModule,
ReactiveFormsModule,
NgStackFormsModule,
TranslateModule.forChild({
defaultLanguage: 'en',
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
}),
],
providers: [
AuthService,

View File

@ -3,61 +3,61 @@
<mat-card class="px-0">
<mat-tab-group mat-align-tabs="center" [selectedIndex]="selectedIndex" (selectedIndexChange)="selectedIndexChange($event)">
<mat-tab label="Logowanie">
<mat-tab [label]="'AUTH.SIGN_IN' | translate">
<div [formGroup]="loginForm" class="row align-items-center p-4 m-0">
<div class="col-12 col-md-4" required>Email</div>
<div class="col-12 col-md-4" required>{{ 'AUTH.EMAIL_SHORT' | translate }}</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Adres email</mat-label>
<mat-label>{{ 'AUTH.EMAIL' | translate }}</mat-label>
<input formControlName="email" matInput>
</mat-form-field>
<div class="col-12 col-md-4" required>Hasło</div>
<div class="col-12 col-md-4" required>{{ 'AUTH.PASSWORD' | translate }}</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Hasło</mat-label>
<mat-label>{{ 'AUTH.PASSWORD' | translate }}</mat-label>
<input formControlName="password" type="password" matInput>
</mat-form-field>
<div class="text-end">
<button mat-button (click)="selectedIndexChange(getSelectedIndex(AuthTabEnum.RestorePassword))">Odzyskaj hasło</button>
<button mat-flat-button color="primary" (click)="login()">Zaloguj</button>
<button mat-button (click)="selectedIndexChange(getSelectedIndex(AuthTabEnum.RestorePassword))">{{ 'AUTH.RESTORE_ACCOUNT' | translate }}</button>
<button mat-flat-button color="primary" (click)="login()">{{ 'AUTH.SIGN_IN' | translate }}</button>
</div>
</div>
</mat-tab>
<mat-tab label="Rejestracja">
<mat-tab [label]="'AUTH.SIGN_UP' | translate">
<div [formGroup]="registerForm" class="row align-items-center p-4 m-0">
<div class="col-12 col-md-4" required>Imię</div>
<div class="col-12 col-md-4" required>{{ 'AUTH.NAME' | translate }}</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Imię</mat-label>
<mat-label>{{ 'AUTH.NAME' | translate }}</mat-label>
<input formControlName="name" matInput>
</mat-form-field>
<div class="col-12 col-md-4">Nazwisko</div>
<div class="col-12 col-md-4">{{ 'AUTH.SURNAME' | translate }}</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Nazwisko</mat-label>
<mat-label>{{ 'AUTH.SURNAME' | translate }}</mat-label>
<input formControlName="surname" matInput>
</mat-form-field>
<div class="col-12 col-md-4" required>Email</div>
<div class="col-12 col-md-4" required>{{ 'AUTH.EMAIL_SHORT' | translate }}</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Adres email</mat-label>
<mat-label>{{ 'AUTH.EMAIL' | translate }}</mat-label>
<input formControlName="email" matInput>
</mat-form-field>
<div class="col-12 col-md-4" required>Hasło</div>
<div class="col-12 col-md-4" required>{{ 'AUTH.PASSWORD' | translate }}</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Hasło</mat-label>
<mat-label>{{ 'AUTH.PASSWORD' | translate }}</mat-label>
<input formControlName="password" type="password" matInput>
</mat-form-field>
<div class="text-end">
<button mat-flat-button color="primary" (click)="register()">Zarejestruj</button>
<button mat-flat-button color="primary" (click)="register()">{{ 'AUTH.SIGN_UP' | translate }}</button>
</div>
</div>
</mat-tab>
<mat-tab label="Odzyskiwanie hasła" *ngIf="selectedIndex === 2">
<mat-tab [label]="'AUTH.RESTORE_ACCOUNT' | translate" *ngIf="selectedIndex === 2">
<div [formGroup]="restoreForm" class="row align-items-center p-4 m-0">
<div class="col-12 col-md-4" required>Email</div>
<div class="col-12 col-md-4" required>{{ 'AUTH.EMAIL_SHORT' | translate }}</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Adres email</mat-label>
<mat-label>{{ 'AUTH.EMAIL' | translate }}</mat-label>
<input formControlName="email" matInput>
</mat-form-field>
<div class="text-end">
<button mat-button (click)="selectedIndexChange(getSelectedIndex(AuthTabEnum.Login))">Anuluj</button>
<button mat-flat-button color="primary">Odzyskaj hasło</button>
<button mat-button (click)="selectedIndexChange(getSelectedIndex(AuthTabEnum.Login))">{{ 'AUTH.CANCEL' | translate }}</button>
<button mat-flat-button color="primary">{{ 'AUTH.SEND_NEW_PASSWORD' | translate }}</button>
</div>
</div>
</mat-tab>

View File

@ -12,8 +12,8 @@ import { Router } from '@angular/router';
export class AuthService {
private static updateAgent: any;
private user: UserModel;
public userChange = new Subject<UserModel>();
private user: UserModel | null = null;
public userChange = new Subject<UserModel | null>();
constructor(
private http: HttpClient,
@ -26,7 +26,7 @@ export class AuthService {
});
}
getUser(): UserModel {
getUser(): UserModel | null {
return this.user;
}
@ -63,6 +63,7 @@ export class AuthService {
logout(): void {
this.authTokenService.removeToken();
this.userChange.next(null);
this.router.navigate(['/auth']);
}
}

View File

@ -0,0 +1 @@
<p>profile-edit-avatar works!</p>

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-profile-edit-avatar',
templateUrl: './profile-edit-avatar.component.html',
styleUrls: ['./profile-edit-avatar.component.scss']
})
export class ProfileEditAvatarComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}

View File

@ -0,0 +1 @@
<p>profile-edit-basics works!</p>

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-profile-edit-basics',
templateUrl: './profile-edit-basics.component.html',
styleUrls: ['./profile-edit-basics.component.scss']
})
export class ProfileEditBasicsComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}

View File

@ -0,0 +1 @@
<p>profile-edit-contact works!</p>

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-profile-edit-contact',
templateUrl: './profile-edit-contact.component.html',
styleUrls: ['./profile-edit-contact.component.scss']
})
export class ProfileEditContactComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}

View File

@ -0,0 +1 @@
<p>profile-edit-credentials works!</p>

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-profile-edit-credentials',
templateUrl: './profile-edit-credentials.component.html',
styleUrls: ['./profile-edit-credentials.component.scss']
})
export class ProfileEditCredentialsComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}

View File

@ -0,0 +1 @@
<p>profile-edit-settings works!</p>

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-profile-edit-settings',
templateUrl: './profile-edit-settings.component.html',
styleUrls: ['./profile-edit-settings.component.scss']
})
export class ProfileEditSettingsComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}

View File

@ -0,0 +1,36 @@
<div class="toolbar-background user-cover"></div>
<div class="user-header-container p-4">
<mat-card *ngIf="user === null" class="loading-card">
loading
</mat-card>
<mat-card *ngIf="user !== null">
<div class="user-header">
<div>
<div class="user-avatar" [style.background-image]="url(user.avatar)"></div>
</div>
<div class="d-flex align-items-end flex-grow-1">
<h3>{{ user.name }} {{ user.surname }}</h3>
</div>
</div>
</mat-card>
</div>
<ng-container *ngIf="user !== null">
<div class="p-3 d-none d-md-block">
<h6>{{ currentTabName | translate }}</h6>
</div>
<div class="tab-container">
<mat-tab-group [selectedIndex]="selectedIndex" (selectedIndexChange)="selectedIndexChange($event)">
<ng-container *ngFor="let tab of tabs">
<mat-tab>
<ng-template mat-tab-label>
<i *ngIf="tab.icon" class="me-0 me-md-3" [ngClass]="tab.icon"></i>
<span class="d-none d-md-inline-block">{{ tab.label | translate }}</span>
</ng-template>
</mat-tab>
</ng-container>
</mat-tab-group>
</div>
<div class="p-3">
<router-outlet></router-outlet>
</div>
</ng-container>

View File

@ -0,0 +1,28 @@
.user-cover{
height: 100px;
background-color: var(--toolbar-background);
}
.user-header-container{
margin-top: -100px;
position: relative;
z-index: 20;
}
.user-header{
display: flex;
flex-direction: row-reverse;
}
.user-avatar{
width: 128px;
height: 128px;
background-color: rgba(0,0,0, 0.25);
background-position: center;
background-repeat: no-repeat;
background-size: cover;
border-radius: 27px;
}
.tab-container{
height: 50px;
}
.loading-card{
margin-top: 50px;
}

View File

@ -0,0 +1,114 @@
import { Component, OnInit } from '@angular/core';
import { DomSanitizer, SafeStyle } from '@angular/platform-browser';
import { ActivatedRoute, ActivatedRouteSnapshot, ActivationStart, Data, Router, RoutesRecognized } from '@angular/router';
import { UserModel } from 'src/app/modules/auth/models/user.model';
import { AuthService } from 'src/app/modules/auth/services/auth/auth.service';
import { ProfileTabEnum } from '../../enums/profile-tab.enum';
import { profileTabRoutes } from '../../profile-routing';
interface ProfileTab {
tab: ProfileTabEnum;
label: string;
icon?: string;
}
@Component({
selector: 'app-profile-edit',
templateUrl: './profile-edit.component.html',
styleUrls: ['./profile-edit.component.scss']
})
export class ProfileEditComponent implements OnInit {
profileTabRoutes = profileTabRoutes;
currentTabName: string;
user: UserModel | null = null;
selectedIndex = 0;
defaultProfileTab = ProfileTabEnum.Basics;
tabs: ProfileTab[] = [
{
tab: ProfileTabEnum.Basics,
label: 'PROFILE.BASICS',
icon: 'fa fa-id-badge',
},
{
tab: ProfileTabEnum.Contact,
label: 'PROFILE.CONTACT',
icon: 'fa fa-address-book'
},
{
tab: ProfileTabEnum.Credentials,
label: 'PROFILE.PASSWORD',
icon: 'fa fa-unlock-alt',
},
{
tab: ProfileTabEnum.Avatar,
label: 'PROFILE.AVATAR',
icon: 'fa fa-picture-o',
},
{
tab: ProfileTabEnum.Settings,
label: 'PROFILE.SITE_SETTINGS',
icon: 'fa fa-sliders',
},
];
constructor(
private sanitizer: DomSanitizer,
authService: AuthService,
private activatedRoute: ActivatedRoute,
private router: Router,
) {
this.user = authService.getUser();
authService.userChange.subscribe(user => {
this.user = user;
});
this.onRouteChange(activatedRoute.snapshot.firstChild?.data);
this.router.events.subscribe(event => {
if (event instanceof RoutesRecognized) {
console.log({event});
}
});
}
onRouteChange(data: Data): void {
this.currentTabName = undefined;
if (data?.tab) {
const tab = data.tab as ProfileTabEnum;
const tabIndex = this.tabs.findIndex(i => i.tab === tab);
console.log({tab, tabIndex});
if (tabIndex !== -1) {
this.selectedIndex = tabIndex;
this.currentTabName = this.tabs[tabIndex].label;
}
} else {
const index = this.tabs.findIndex(i => i.tab === this.defaultProfileTab);
if (index !== -1) {
this.selectedIndexChange(index);
}
}
}
selectedIndexChange(index: number): void {
console.log({index});
this.selectedIndex = index;
this.currentTabName = undefined;
const tab = this.tabs[index];
if (tab) {
const route = this.profileTabRoutes.find(i => i.data.tab === tab.tab);
this.currentTabName = tab.label;
if (route) {
this.router.navigate([route.path], {
relativeTo: this.activatedRoute,
});
}
}
}
ngOnInit(): void {
}
url(url: string): SafeStyle {
return this.sanitizer.bypassSecurityTrustStyle(`url('${url}')`);
}
}

View File

@ -0,0 +1,7 @@
export enum ProfileTabEnum {
Basics,
Contact,
Credentials,
Avatar,
Settings,
}

View File

@ -0,0 +1,11 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { profileRoutes } from './profile-routing';
@NgModule({
imports: [RouterModule.forChild(profileRoutes)],
exports: [RouterModule]
})
export class ProfileRoutingModule { }

View File

@ -0,0 +1,55 @@
import { Route } from '@angular/router';
import { NotFoundComponent } from '../shared/components/not-found/not-found.component';
import { ProfileEditAvatarComponent } from './components/profile-edit-avatar/profile-edit-avatar.component';
import { ProfileEditBasicsComponent } from './components/profile-edit-basics/profile-edit-basics.component';
import { ProfileEditContactComponent } from './components/profile-edit-contact/profile-edit-contact.component';
import { ProfileEditCredentialsComponent } from './components/profile-edit-credentials/profile-edit-credentials.component';
import { ProfileEditSettingsComponent } from './components/profile-edit-settings/profile-edit-settings.component';
import { ProfileEditComponent } from './components/profile-edit/profile-edit.component';
import { ProfileTabEnum } from './enums/profile-tab.enum';
export const profileTabRoutes: Route[] = [
{
path: 'basics',
component: ProfileEditBasicsComponent,
data: {
tab: ProfileTabEnum.Basics,
},
},
{
path: 'contact',
component: ProfileEditContactComponent,
data: {
tab: ProfileTabEnum.Contact,
},
},
{
path: 'credentials',
component: ProfileEditCredentialsComponent,
data: {
tab: ProfileTabEnum.Credentials,
},
},
{
path: 'avatar',
component: ProfileEditAvatarComponent,
data: {
tab: ProfileTabEnum.Avatar,
},
},
{
path: 'settings',
component: ProfileEditSettingsComponent,
data: {
tab: ProfileTabEnum.Settings,
},
},
];
export const profileRoutes: Route[] = [
{
path: '',
component: ProfileEditComponent,
children: profileTabRoutes,
},
];

View File

@ -0,0 +1,42 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ProfileRoutingModule } from './profile-routing.module';
import { ProfileEditComponent } from './components/profile-edit/profile-edit.component';
import { MaterialModule } from '../material/material.module';
import { AuthService } from '../auth/services/auth/auth.service';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { HttpLoaderFactory } from 'src/app/app.module';
import { HttpClient } from '@angular/common/http';
import { ProfileEditBasicsComponent } from './components/profile-edit-basics/profile-edit-basics.component';
import { ProfileEditContactComponent } from './components/profile-edit-contact/profile-edit-contact.component';
import { ProfileEditCredentialsComponent } from './components/profile-edit-credentials/profile-edit-credentials.component';
import { ProfileEditAvatarComponent } from './components/profile-edit-avatar/profile-edit-avatar.component';
import { ProfileEditSettingsComponent } from './components/profile-edit-settings/profile-edit-settings.component';
@NgModule({
declarations: [
ProfileEditComponent,
ProfileEditBasicsComponent,
ProfileEditContactComponent,
ProfileEditCredentialsComponent,
ProfileEditAvatarComponent,
ProfileEditSettingsComponent,
],
imports: [
CommonModule,
ProfileRoutingModule,
MaterialModule,
TranslateModule.forChild({
defaultLanguage: 'en',
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
}),
],
providers: [
AuthService,
],
})
export class ProfileModule { }

View File

@ -26,5 +26,24 @@
"RESEARCH": "Research",
"ADMIN": "Admin",
"USERS": "Users"
},
"AUTH": {
"EMAIL_SHORT": "Email",
"EMAIL": "Address email",
"PASSWORD": "Password",
"SIGN_IN": "Sign in",
"NAME": "Name",
"SURNAME": "Surname",
"SIGN_UP": "Sign up",
"CANCEL": "Cancel",
"SEND_NEW_PASSWORD": "Send new password",
"RESTORE_ACCOUNT": "Restore account"
},
"PROFILE": {
"BASICS": "Basic information",
"CONTACT": "Contact information",
"AVATAR": "Avatar",
"PASSWORD": "Password",
"SITE_SETTINGS": "Site settings"
}
}

View File

@ -26,5 +26,24 @@
"RESEARCH": "Badania naukowe",
"ADMIN": "Panel administratora",
"USERS": "Użytkownicy"
},
"AUTH": {
"EMAIL_SHORT": "Email",
"EMAIL": "Adres email",
"PASSWORD": "Hasło",
"SIGN_IN": "Zaloguj",
"NAME": "Imię",
"SURNAME": "Nazwisko",
"SIGN_UP": "Zarejestruj",
"CANCEL": "Anuluj",
"SEND_NEW_PASSWORD": "Wyślij nowe hasło",
"RESTORE_ACCOUNT": "Odzyskaj konto"
},
"PROFILE": {
"BASICS": "Podstawowe informacje",
"CONTACT": "Dane kontaktowe",
"AVATAR": "Zdjęcie profilowe",
"PASSWORD": "Hasło",
"SITE_SETTINGS": "Ustawienia strony"
}
}