diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 00d9521..c46cf5b 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -5,7 +5,11 @@ const routes: Routes = [ { path: 'pages', loadChildren: './pages/pages.module#PagesModule' - } + }, + { + path: 'auth', + loadChildren: './pages/accounts/accounts.module#AccountsModule' + }, ]; @NgModule({ diff --git a/src/app/app.theme.scss b/src/app/app.theme.scss index 643a382..edbae8a 100644 --- a/src/app/app.theme.scss +++ b/src/app/app.theme.scss @@ -82,11 +82,14 @@ $typography: mat-typography-config( @import 'src/app/layout/components/chat-panel/chat-panel.theme'; @import 'src/app/layout/components/toolbar/toolbar.theme'; +@import 'src/app/pages/accounts/component/authentication/authentication.theme'; + // Define a mixin for easier access @mixin components-theme($theme) { // Layout components @include chat-panel-theme($theme); @include toolbar-theme($theme); + @include login-theme($theme); } // ----------------------------------------------------------------------------------------------------- diff --git a/src/app/pages/accounts/accounts-routing.page.module.ts b/src/app/pages/accounts/accounts-routing.page.module.ts new file mode 100644 index 0000000..f31ad93 --- /dev/null +++ b/src/app/pages/accounts/accounts-routing.page.module.ts @@ -0,0 +1,37 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; + +// import { AccountsLayoutComponent } from '../../layout/component/accounts/accounts.layout.component'; + +// import { AuthenticationPageComponent } from './component/authentication/authentication.page.component'; +// import { WelcomePageComponent } from './component/welcome/welcome.page.component'; +// import { AuthenticationCallbackPageComponent } from './component/authentication/authentication-callback.page.component'; + +import { AuthenticationComponent } from './component/authentication/authentication.component'; + +const routes: Routes = [ + { + path: 'signin', + component: AuthenticationComponent, + // children: [ + // { + // path: 'authentication', + // component: AuthenticationPageComponent + // }, + // { + // path: 'authentication/callback', + // component: AuthenticationCallbackPageComponent + // }, + // { + // path: 'welcome', + // component: WelcomePageComponent + // } + // ] + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class AccountsRoutingPageModule { } diff --git a/src/app/pages/accounts/accounts-store.module.ts b/src/app/pages/accounts/accounts-store.module.ts new file mode 100755 index 0000000..ad310ca --- /dev/null +++ b/src/app/pages/accounts/accounts-store.module.ts @@ -0,0 +1,17 @@ +import { NgModule, ModuleWithProviders } from '@angular/core'; +import { StoreModule } from '@ngrx/store'; +import { EffectsModule } from '@ngrx/effects'; + +import { + REDUCERS, + EFFECTS, +} from './store'; + +@NgModule({ + imports: [ + StoreModule.forFeature('accounts', REDUCERS), + EffectsModule.forFeature(EFFECTS), + ], +}) +export class AccountsStoreModule { +} diff --git a/src/app/pages/accounts/accounts.module.ts b/src/app/pages/accounts/accounts.module.ts new file mode 100755 index 0000000..a108993 --- /dev/null +++ b/src/app/pages/accounts/accounts.module.ts @@ -0,0 +1,62 @@ +import { NgModule, ModuleWithProviders } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { TranslateModule } from '@ngx-translate/core'; + +import { COMPONENTS } from './component'; +import { AccountsService } from './service/accounts.service'; +import { AccountsStoreModule } from './accounts-store.module'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; + +import { FuseSharedModule } from 'src/@fuse/shared.module'; +import { AccountsRoutingPageModule } from './accounts-routing.page.module'; + +// import { SharedModule } from '../../../common/shared/shared.module'; + +@NgModule({ + imports: [ + CommonModule, + TranslateModule, + AccountsRoutingPageModule, + + MatButtonModule, + MatCheckboxModule, + MatFormFieldModule, + MatIconModule, + MatInputModule, + + FuseSharedModule + + // SharedModule, + + ], + declarations: [ + ...COMPONENTS, + ], + exports: [ + ...COMPONENTS, + ], +}) +export class AccountsModule { + public static forRoot(): ModuleWithProviders { + return { + ngModule: AccountsRootModule, + providers: [ + AccountsService, + ], + }; + } +} + +@NgModule({ + imports: [ + AccountsStoreModule, + ], + exports: [ + ] +}) +export class AccountsRootModule { +} diff --git a/src/app/pages/accounts/component/authentication/authentication.component.html b/src/app/pages/accounts/component/authentication/authentication.component.html new file mode 100644 index 0000000..283ff39 --- /dev/null +++ b/src/app/pages/accounts/component/authentication/authentication.component.html @@ -0,0 +1,75 @@ +
+ +
+ +
+ + + +
LOGIN TO YOUR ACCOUNT
+ +
+ + + username + + mail + + username is required + + + Please enter a valid username + + + + + Password + + vpn_key + + Password is required + + + +
+ + Remember Me + + + + Forgot Password? + +
+ + + +
+ + +
+ +
+ +
\ No newline at end of file diff --git a/src/app/pages/accounts/component/authentication/authentication.component.scss b/src/app/pages/accounts/component/authentication/authentication.component.scss new file mode 100644 index 0000000..de1c002 --- /dev/null +++ b/src/app/pages/accounts/component/authentication/authentication.component.scss @@ -0,0 +1,147 @@ +@import "src/@fuse/scss/fuse.scss"; + +authentication { + + #login { + width: 100%; + background: url('/assets/images/backgrounds/dark-material-bg.jpg') no-repeat; + background-size: cover; + + #login-form-wrapper { + flex: 1 0 auto; + padding: 32px; + + @include media-breakpoint('xs') { + padding: 16px; + } + + #login-form { + width: 384px; + max-width: 384px; + padding: 32px; + text-align: center; + @include mat-elevation(16); + + @include media-breakpoint('xs') { + padding: 24px; + width: 100%; + } + + .logo { + width: 128px; + margin: 32px auto; + } + + .title { + font-size: 20px; + margin: 16px 0 32px 0; + } + + form { + width: 100%; + text-align: left; + + mat-form-field { + width: 100%; + } + + mat-checkbox { + margin: 0; + } + + .remember-forgot-password { + font-size: 13px; + margin-top: 8px; + + .remember-me { + margin-bottom: 16px + } + + .forgot-password { + font-size: 13px; + font-weight: 600; + margin-bottom: 16px + } + } + + .submit-button { + width: 220px; + margin: 16px auto; + display: block; + + @include media-breakpoint('xs') { + width: 90%; + } + } + } + + .register { + margin: 32px auto 24px auto; + font-weight: 600; + + .text { + margin-right: 8px; + } + } + + .separator { + font-size: 15px; + font-weight: 600; + margin: 24px auto; + position: relative; + overflow: hidden; + width: 100px; + + .text { + display: inline-flex; + position: relative; + padding: 0 8px; + z-index: 9999; + + &:before, + &:after { + content: ''; + display: block; + width: 30px; + position: absolute; + top: 10px; + border-top: 1px solid; + } + + &:before { + right: 100%; + } + + &:after { + left: 100%; + } + } + } + + button { + + &.google, + &.facebook { + width: 192px; + text-transform: none; + color: #FFFFFF; + font-size: 13px; + } + + @include media-breakpoint('xs') { + width: 80%; + } + + &.google { + background-color: #D73D32; + margin-bottom: 8px; + } + + &.facebook { + background-color: rgb(63, 92, 154); + } + } + } + } + } +} diff --git a/src/app/pages/accounts/component/authentication/authentication.component.ts b/src/app/pages/accounts/component/authentication/authentication.component.ts new file mode 100644 index 0000000..eb24315 --- /dev/null +++ b/src/app/pages/accounts/component/authentication/authentication.component.ts @@ -0,0 +1,68 @@ +import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; + +import { FuseConfigService } from 'src/@fuse/services/config.service'; +import { fuseAnimations } from 'src/@fuse/animations'; + +@Component({ + selector: 'authentication', + templateUrl: './authentication.component.html', + styleUrls: ['./authentication.component.scss'], + encapsulation: ViewEncapsulation.None, + animations: fuseAnimations +}) +export class AuthenticationComponent implements OnInit { + + loginForm: FormGroup; + + /** + * Constructor + * + * @param {FuseConfigService} _fuseConfigService + * @param {FormBuilder} _formBuilder + */ + constructor( + private _fuseConfigService: FuseConfigService, + private _formBuilder: FormBuilder + ) { + // Configure the layout + this._fuseConfigService.config = { + layout: { + navbar: { + hidden: true + }, + toolbar: { + hidden: true + }, + footer: { + hidden: true + }, + sidepanel: { + hidden: true + } + } + }; + } + + // ----------------------------------------------------------------------------------------------------- + // @ Lifecycle hooks + // ----------------------------------------------------------------------------------------------------- + + /** + * On init + */ + ngOnInit(): void { + this.loginForm = this._formBuilder.group({ + username: ['', [Validators.required]], + password: ['', Validators.required] + }); + } + + onSignin(): void { + let userName = this.loginForm.get('username').value; + let password = this.loginForm.get('password').value; + + + } + +} diff --git a/src/app/pages/accounts/component/authentication/authentication.theme.scss b/src/app/pages/accounts/component/authentication/authentication.theme.scss new file mode 100644 index 0000000..8c7b817 --- /dev/null +++ b/src/app/pages/accounts/component/authentication/authentication.theme.scss @@ -0,0 +1,35 @@ +@mixin login-theme($theme) { + + $background: map-get($theme, background); + $foreground: map-get($theme, foreground); + $is-dark: map-get($theme, is-dark); + + authentication { + + #login { + + #login-form-wrapper { + + #login-form { + @if ($is-dark) { + background: mat-color($fuse-navy, 600); + } @else { + background: map-get($background, card); + } + + .separator { + color: map-get($foreground, divider); + + .text { + + &:before, + &:after { + border-top-color: map-get($foreground, divider); + } + } + } + } + } + } + } +} diff --git a/src/app/pages/accounts/component/index.ts b/src/app/pages/accounts/component/index.ts new file mode 100644 index 0000000..7efa8e5 --- /dev/null +++ b/src/app/pages/accounts/component/index.ts @@ -0,0 +1,5 @@ +import { AuthenticationComponent } from './authentication/authentication.component'; + +export const COMPONENTS = [ + AuthenticationComponent, +]; diff --git a/src/app/pages/accounts/service/accounts.service.ts b/src/app/pages/accounts/service/accounts.service.ts new file mode 100644 index 0000000..1bf40a6 --- /dev/null +++ b/src/app/pages/accounts/service/accounts.service.ts @@ -0,0 +1,68 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; + +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { CookieService } from 'ngx-cookie-service'; +import { environment } from '../../../../environments/environment'; +import { User } from '../../../../shared/user/model/user.model'; + +@Injectable() +export class AccountsService { + public constructor( + private httpClient: HttpClient, + private cookieService: CookieService, + ) { + } + + // public authenticate(oauthType: string, params?: Map) { + // const query = this.getQueryParams(params); + + // const externalUrl = new URL(`${environment.oauth[oauthType].authenticateURL}?${query}`); + + // window.open(externalUrl.href, '_self'); + // } + + // public authenticateOneall(oauthType: string, params?: Map) { + // const callbackUri = `${environment.oauth.oneall.callbackURL}?${this.getQueryParams(params)}`; + + // let query = ''; + // query += `service=social_login`; + // query += `&callback_uri=${callbackUri}`; + + // const externalUrl = new URL(`${environment.oauth.oneall.authenticateURL}/${oauthType}/?${query}`); + + // window.open(externalUrl.href, '_self'); + // } + + protected getQueryParams(params?: Map): string { + let query = ''; + + if (!params || 0 === params.size) { + model + return query; + } + + params.forEach((value, key) => { + if ('' !== query) { + query += '&'; + } + query += `${key}=${value}`; + }); + + return query; + } + + public login(username: string, password: string): Observable { + const model = { + username, + password + }; + + return this.httpClient.post( + `${this.apiEntryPoint}/auth/signin`, + model + ); + } +} diff --git a/src/app/pages/accounts/store/auth/auth.action.ts b/src/app/pages/accounts/store/auth/auth.action.ts new file mode 100755 index 0000000..d454c32 --- /dev/null +++ b/src/app/pages/accounts/store/auth/auth.action.ts @@ -0,0 +1,48 @@ +import { Action } from '@ngrx/store'; +import { User } from '../../../../../shared/user/model/user.model'; + +export enum ActionType { + LoginProcessing = '[account.auth] LoginProcessing', + + LoginSuccess = '[account.auth] LoginSuccess', + LogoutSuccess = '[account.auth] LogoutSuccess', + ChangeUser = '[account.auth] ChangeUser', + LoginRequired = '[account.auth] LoginRequired', +} + +export class LoginProcessing implements Action { + readonly type = ActionType.LoginProcessing; + + constructor(public payload: { processing: boolean }) { } +} + +export class LoginSuccess implements Action { + readonly type = ActionType.LoginSuccess; + + constructor(public payload: { user: User, returnURL: string }) { } +} + +export class ChangeUser implements Action { + readonly type = ActionType.ChangeUser; + + constructor(public payload: { user: User }) { } +} + +export class LogoutSuccess implements Action { + readonly type = ActionType.LogoutSuccess; +} + +export class LoginRequired implements Action { + readonly type = ActionType.LoginRequired; + + constructor() { } +} + + +export type Actions = + | LoginProcessing + | LoginSuccess + | ChangeUser + | LogoutSuccess + | LoginRequired + ; diff --git a/src/app/pages/accounts/store/auth/auth.reducer.ts b/src/app/pages/accounts/store/auth/auth.reducer.ts new file mode 100755 index 0000000..4b88eb4 --- /dev/null +++ b/src/app/pages/accounts/store/auth/auth.reducer.ts @@ -0,0 +1,43 @@ +import { Actions, ActionType } from './auth.action'; + +import { State, initialState } from './auth.state'; + +export function reducer(state: State = initialState, action: Actions): State { + switch (action.type) { + case ActionType.LoginProcessing: { + return { + ...state, + processing: action.payload.processing + }; + } + + case ActionType.LoginSuccess: { + return { + ...state, + processing: false, + logined: true, + user: action.payload.user + }; + } + + case ActionType.ChangeUser: { + return { + ...state, + user: action.payload.user + }; + } + + case ActionType.LogoutSuccess: { + return { + ...state, + processing: false, + logined: false, + user: null + }; + } + + default: { + return state; + } + } +} diff --git a/src/app/pages/accounts/store/auth/auth.state.ts b/src/app/pages/accounts/store/auth/auth.state.ts new file mode 100755 index 0000000..060d153 --- /dev/null +++ b/src/app/pages/accounts/store/auth/auth.state.ts @@ -0,0 +1,22 @@ +import { Selector, createSelector } from '@ngrx/store'; +import { User } from '../../../../../shared/user/model/user.model'; + +export interface State { + processing: boolean; + logined: boolean; + user: User | null; +} + +export const initialState: State = { + processing: false, + logined: false, + user: null, +}; + +export function getSelectors(selector: Selector) { + return { + selectProcessing: createSelector(selector, (state: State) => state.processing), + selectLogined: createSelector(selector, (state: State) => state.logined), + selectUser: createSelector(selector, (state: State) => state.user), + }; +} diff --git a/src/app/pages/accounts/store/auth/index.ts b/src/app/pages/accounts/store/auth/index.ts new file mode 100644 index 0000000..92047bf --- /dev/null +++ b/src/app/pages/accounts/store/auth/index.ts @@ -0,0 +1,3 @@ +export * from './auth.action'; +export * from './auth.reducer'; +export * from './auth.state'; diff --git a/src/app/pages/accounts/store/index.ts b/src/app/pages/accounts/store/index.ts new file mode 100644 index 0000000..67cf06e --- /dev/null +++ b/src/app/pages/accounts/store/index.ts @@ -0,0 +1,24 @@ +import { + createSelector, + createFeatureSelector, +} from '@ngrx/store'; + +import * as AuthStore from './auth'; + +export interface State { + auth: AuthStore.State; +} + +export const REDUCERS = { + auth: AuthStore.reducer, +}; + +export const EFFECTS = [ +]; + +export const selectState = createFeatureSelector('accounts'); + +export const AuthSelector = AuthStore.getSelectors(createSelector( + selectState, + (state: State) => state.auth, +)); diff --git a/src/app/pages/pages.module.ts b/src/app/pages/pages.module.ts index 82718f0..d1ff6c9 100644 --- a/src/app/pages/pages.module.ts +++ b/src/app/pages/pages.module.ts @@ -4,6 +4,7 @@ import { FuseSharedModule } from 'src/@fuse/shared.module'; import { PagesRoutingModule } from './pages-routing.module'; @NgModule({ - imports: [FuseSharedModule, PagesRoutingModule] + imports: [FuseSharedModule, PagesRoutingModule], + declarations: [] }) -export class PagesModule {} +export class PagesModule { } diff --git a/src/app/pages/users/user/component/user-list/user-list.component.ts b/src/app/pages/users/user/component/user-list/user-list.component.ts index e1a758a..f471e92 100644 --- a/src/app/pages/users/user/component/user-list/user-list.component.ts +++ b/src/app/pages/users/user/component/user-list/user-list.component.ts @@ -48,6 +48,9 @@ export class UserListComponent implements OnInit, OnDestroy { 'recentConnectIp' ]; + total: number; + userList: User[]; + @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;