ing
This commit is contained in:
parent
0fc503db87
commit
550fff3849
4342
package-lock.json
generated
4342
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
|
@ -69,15 +69,15 @@
|
||||||
"@ucap/ng-protocol-sync": "~0.0.3",
|
"@ucap/ng-protocol-sync": "~0.0.3",
|
||||||
"@ucap/ng-protocol-umg": "~0.0.3",
|
"@ucap/ng-protocol-umg": "~0.0.3",
|
||||||
"@ucap/ng-store-authentication": "~0.0.10",
|
"@ucap/ng-store-authentication": "~0.0.10",
|
||||||
"@ucap/ng-store-chat": "~0.0.5",
|
"@ucap/ng-store-chat": "~0.0.6",
|
||||||
"@ucap/ng-store-group": "~0.0.6",
|
"@ucap/ng-store-group": "~0.0.7",
|
||||||
"@ucap/ng-store-organization": "~0.0.4",
|
"@ucap/ng-store-organization": "~0.0.4",
|
||||||
"@ucap/ng-web-socket": "~0.0.2",
|
"@ucap/ng-web-socket": "~0.0.2",
|
||||||
"@ucap/ng-web-storage": "~0.0.3",
|
"@ucap/ng-web-storage": "~0.0.3",
|
||||||
"@ucap/ng-ui": "~0.0.4",
|
"@ucap/ng-ui": "~0.0.7",
|
||||||
"@ucap/ng-ui-organization": "~0.0.2",
|
"@ucap/ng-ui-organization": "~0.0.15",
|
||||||
"@ucap/ng-ui-authentication": "~0.0.16",
|
"@ucap/ng-ui-authentication": "~0.0.19",
|
||||||
"@ucap/ng-ui-group": "~0.0.3",
|
"@ucap/ng-ui-group": "~0.0.28",
|
||||||
"@ucap/ng-ui-skin-default": "~0.0.1",
|
"@ucap/ng-ui-skin-default": "~0.0.1",
|
||||||
"@ucap/pi": "~0.0.5",
|
"@ucap/pi": "~0.0.5",
|
||||||
"@ucap/protocol": "~0.0.17",
|
"@ucap/protocol": "~0.0.17",
|
||||||
|
@ -97,7 +97,7 @@
|
||||||
"@ucap/protocol-sync": "~0.0.4",
|
"@ucap/protocol-sync": "~0.0.4",
|
||||||
"@ucap/protocol-umg": "~0.0.5",
|
"@ucap/protocol-umg": "~0.0.5",
|
||||||
"@ucap/web-socket": "~0.0.10",
|
"@ucap/web-socket": "~0.0.10",
|
||||||
"@ucap/web-storage": "~0.0.5",
|
"@ucap/web-storage": "~0.0.9",
|
||||||
"autolinker": "^3.13.0",
|
"autolinker": "^3.13.0",
|
||||||
"axios": "^0.19.2",
|
"axios": "^0.19.2",
|
||||||
"classlist.js": "^1.1.20150312",
|
"classlist.js": "^1.1.20150312",
|
||||||
|
|
|
@ -14,10 +14,16 @@ import { AppSessionResolver } from './resolvers/app-session.resolver';
|
||||||
import { AppAuthenticationService } from './services/app-authentication.service';
|
import { AppAuthenticationService } from './services/app-authentication.service';
|
||||||
import { AppNativeService } from './services/app-native.service';
|
import { AppNativeService } from './services/app-native.service';
|
||||||
import { AppService } from './services/app.service';
|
import { AppService } from './services/app.service';
|
||||||
|
import { AppChatService } from './services/app-chat.service';
|
||||||
|
|
||||||
const GUARDS = [AppAuthenticationGuard];
|
const GUARDS = [AppAuthenticationGuard];
|
||||||
const RESOLVERS = [AppSessionResolver];
|
const RESOLVERS = [AppSessionResolver];
|
||||||
const SERVICES = [AppService, AppAuthenticationService, AppNativeService];
|
const SERVICES = [
|
||||||
|
AppService,
|
||||||
|
AppAuthenticationService,
|
||||||
|
AppNativeService,
|
||||||
|
AppChatService
|
||||||
|
];
|
||||||
|
|
||||||
const axiosFactory = () => {
|
const axiosFactory = () => {
|
||||||
const i = axios.create();
|
const i = axios.create();
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
<app-layouts-top-bar></app-layouts-top-bar>
|
<div class="app-container">
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
:host {
|
:host {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto !important;
|
height: 100%;
|
||||||
|
|
||||||
|
.app-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,9 @@ import { debounce } from 'rxjs/operators';
|
||||||
styleUrls: ['./app.component.scss']
|
styleUrls: ['./app.component.scss']
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnInit, OnDestroy {
|
export class AppComponent implements OnInit, OnDestroy {
|
||||||
|
showTopbar = true;
|
||||||
|
showFooter = false;
|
||||||
|
|
||||||
private resizeWindowSubscription: Subscription;
|
private resizeWindowSubscription: Subscription;
|
||||||
|
|
||||||
constructor(private store: Store<any>) {
|
constructor(private store: Store<any>) {
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
|
||||||
import { NgModule } from '@angular/core';
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
|
|
||||||
import { StoreModule } from '@ngrx/store';
|
import { StoreModule } from '@ngrx/store';
|
||||||
import { EffectsModule } from '@ngrx/effects';
|
import { EffectsModule } from '@ngrx/effects';
|
||||||
|
@ -65,6 +67,8 @@ import { environment } from '@environments';
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
|
|
||||||
|
FlexLayoutModule,
|
||||||
|
|
||||||
LoggerModule.forRoot({}),
|
LoggerModule.forRoot({}),
|
||||||
|
|
||||||
CommonApiModule.forRoot(environment.commonApiModuleConfig),
|
CommonApiModule.forRoot(environment.commonApiModuleConfig),
|
||||||
|
|
|
@ -16,164 +16,32 @@ $typography: mat-typography-config(
|
||||||
// Setup the typography
|
// Setup the typography
|
||||||
@include angular-material-typography($typography);
|
@include angular-material-typography($typography);
|
||||||
|
|
||||||
@mixin components-theme($theme) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------------
|
||||||
// @ Define the default theme
|
// @ Define theme --LG RED
|
||||||
// -----------------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// Define the palettes for your theme using the Material Design palettes available in palette.scss
|
||||||
|
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
|
||||||
|
// hue. Available color palettes: https://material.io/design/color/
|
||||||
|
$lgRed-app-primary: mat-palette($lg-red);
|
||||||
|
$lgRed-app-accent: mat-palette($lg-red, A200, A100, A400);
|
||||||
|
|
||||||
// Define the primary, accent and warn palettes
|
// The warn palette is optional (defaults to red).
|
||||||
$default-primary-palette: mat-palette($mat-indigo);
|
$lgRed-app-warn: mat-palette($lg-red);
|
||||||
$default-accent-palette: mat-palette($mat-light-blue, 600, 400, 700);
|
|
||||||
$default-warn-palette: mat-palette($mat-red);
|
|
||||||
|
|
||||||
// Create the Material theme object
|
// Create the theme object (a Sass map containing all of the palettes).
|
||||||
$theme: mat-light-theme(
|
$lgRed-app-theme: mat-light-theme(
|
||||||
$default-primary-palette,
|
$lgRed-app-primary,
|
||||||
$default-accent-palette,
|
$lgRed-app-accent,
|
||||||
$default-warn-palette
|
$lgRed-app-warn
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add ".theme-default" class to the body to activate this theme.
|
// Include theme styles for core and each component used in your app.
|
||||||
// Class name must start with "theme-" !!!
|
// Alternatively, you can import and @include the theme mixins for each component
|
||||||
/*body.theme-default {
|
// that you are using.
|
||||||
// Create an Angular Material theme from the $theme map
|
@include angular-material-theme($lgRed-app-theme);
|
||||||
@include angular-material-theme($theme);
|
|
||||||
|
|
||||||
// Apply the theme to the user components
|
// Apply the theme to the user components
|
||||||
@include components-theme($theme);
|
/*
|
||||||
@include ucap-material-theme($theme);
|
@include components-theme($lgRed-app-theme);
|
||||||
}*/
|
*/
|
||||||
|
@include ucap-material-theme($lgRed-app-theme);
|
||||||
// -----------------------------------------------------------------------------------------------------
|
|
||||||
// @ Define a blue-gray dark theme
|
|
||||||
// -----------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Define the primary, accent and warn palettes
|
|
||||||
$blue-gray-dark-theme-primary-palette: mat-palette($mat-blue);
|
|
||||||
$blue-gray-dark-theme-accent-palette: mat-palette($mat-blue-gray);
|
|
||||||
$blue-gray-dark-theme-warn-palette: mat-palette($mat-red);
|
|
||||||
|
|
||||||
// Create the Material theme object
|
|
||||||
$blue-gray-dark-theme: mat-dark-theme(
|
|
||||||
$blue-gray-dark-theme-primary-palette,
|
|
||||||
$blue-gray-dark-theme-accent-palette,
|
|
||||||
$blue-gray-dark-theme-warn-palette
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add ".theme-blue-gray-dark" class to the body to activate this theme.
|
|
||||||
// Class name must start with "theme-" !!!
|
|
||||||
body.theme-blue-gray-dark {
|
|
||||||
// Generate the Angular Material theme
|
|
||||||
@include angular-material-theme($blue-gray-dark-theme);
|
|
||||||
|
|
||||||
// Apply the theme to the user components
|
|
||||||
@include components-theme($blue-gray-dark-theme);
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------------
|
|
||||||
// @ Define a pink dark theme
|
|
||||||
// -----------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Define the primary, accent and warn palettes
|
|
||||||
$pink-dark-theme-primary-palette: mat-palette($mat-pink);
|
|
||||||
$pink-dark-theme-accent-palette: mat-palette($mat-pink);
|
|
||||||
$pink-dark-theme-warn-palette: mat-palette($mat-red);
|
|
||||||
|
|
||||||
// Create the Material theme object
|
|
||||||
$pink-dark-theme: mat-dark-theme(
|
|
||||||
$pink-dark-theme-primary-palette,
|
|
||||||
$pink-dark-theme-accent-palette,
|
|
||||||
$pink-dark-theme-warn-palette
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add ".theme-pink-dark" class to the body to activate this theme.
|
|
||||||
// Class name must start with "theme-" !!!
|
|
||||||
body.theme-pink-dark {
|
|
||||||
// Generate the Angular Material theme
|
|
||||||
@include angular-material-theme($pink-dark-theme);
|
|
||||||
|
|
||||||
// Apply the theme to the user components
|
|
||||||
@include components-theme($pink-dark-theme);
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------------
|
|
||||||
// @ Define a pink light theme --LG RED 변경 예정(샘플링)
|
|
||||||
// -----------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Define the primary, accent and warn palettes
|
|
||||||
$lgRed-light-theme-primary-palette: mat-palette($mat-grey, 800);
|
|
||||||
$lgRed-light-theme-accent-palette: mat-palette($lg-red, 400);
|
|
||||||
$lgRed-light-theme-warn-palette: mat-palette($mat-cyan);
|
|
||||||
|
|
||||||
// Create the Material theme object
|
|
||||||
$lgRed-light-theme: mat-light-theme(
|
|
||||||
$lgRed-light-theme-primary-palette,
|
|
||||||
$lgRed-light-theme-accent-palette,
|
|
||||||
$lgRed-light-theme-warn-palette
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add ".theme-pink-dark" class to the body to activate this theme.
|
|
||||||
// Class name must start with "theme-" !!!
|
|
||||||
body.theme-lgRed {
|
|
||||||
// Generate the Angular Material theme
|
|
||||||
@include angular-material-theme($lgRed-light-theme);
|
|
||||||
|
|
||||||
// Apply the theme to the user components
|
|
||||||
@include components-theme($lgRed-light-theme);
|
|
||||||
@include ucap-material-theme($lgRed-light-theme);
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------------
|
|
||||||
//aqua-blue-daesang
|
|
||||||
// -----------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
$aquaBlue-light-theme-primary-palette: mat-palette($daesang-grey, 900);
|
|
||||||
$aquaBlue-theme-accent-palette: mat-palette($aquaBlue-daesang);
|
|
||||||
$aquaBlue-theme-warn-palette: mat-palette($mat-orange);
|
|
||||||
|
|
||||||
// Create the Material theme object
|
|
||||||
$aquaBlue-light-theme: mat-light-theme(
|
|
||||||
$aquaBlue-light-theme-primary-palette,
|
|
||||||
$aquaBlue-theme-accent-palette,
|
|
||||||
$aquaBlue-theme-warn-palette
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add ".theme-pink-dark" class to the body to activate this theme.
|
|
||||||
// Class name must start with "theme-" !!!
|
|
||||||
body.theme-default {
|
|
||||||
// Generate the Angular Material theme
|
|
||||||
@include angular-material-theme($aquaBlue-light-theme);
|
|
||||||
|
|
||||||
// Apply the theme to the user components
|
|
||||||
@include components-theme($aquaBlue-light-theme);
|
|
||||||
@include ucap-material-theme($aquaBlue-light-theme);
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------------
|
|
||||||
// @ Define a red theme --LF 변경 예정(샘플링)
|
|
||||||
// -----------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Define the primary, accent and warn palettes
|
|
||||||
$lfRed-light-theme-primary-palette: mat-palette($lf-blue-grey, 800);
|
|
||||||
$lfRed-light-theme-accent-palette: mat-palette($lf-red, 400);
|
|
||||||
$lfRed-light-theme-warn-palette: mat-palette($lf-amber);
|
|
||||||
|
|
||||||
// Create the Material theme object
|
|
||||||
$lfRed-light-theme: mat-light-theme(
|
|
||||||
$lfRed-light-theme-primary-palette,
|
|
||||||
$lfRed-light-theme-accent-palette,
|
|
||||||
$lfRed-light-theme-warn-palette
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add ".theme-pink-dark" class to the body to activate this theme.
|
|
||||||
// Class name must start with "theme-" !!!
|
|
||||||
body.theme-lfRed {
|
|
||||||
// Generate the Angular Material theme
|
|
||||||
@include angular-material-theme($lfRed-light-theme);
|
|
||||||
|
|
||||||
// Apply the theme to the user components
|
|
||||||
@include components-theme($lfRed-light-theme);
|
|
||||||
@include ucap-material-theme($lfRed-light-theme);
|
|
||||||
}
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
|
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
|
|
||||||
|
import { LogService } from '@ucap/ng-logger';
|
||||||
import { PiService } from '@ucap/ng-pi';
|
import { PiService } from '@ucap/ng-pi';
|
||||||
|
|
||||||
import { LoginActions } from '@ucap/ng-store-authentication';
|
import { LoginActions } from '@ucap/ng-store-authentication';
|
||||||
|
@ -26,7 +27,8 @@ export class AppAuthenticationGuard implements CanActivate {
|
||||||
private piService: PiService,
|
private piService: PiService,
|
||||||
private appAuthenticationService: AppAuthenticationService,
|
private appAuthenticationService: AppAuthenticationService,
|
||||||
private store: Store<any>,
|
private store: Store<any>,
|
||||||
private router: Router
|
private router: Router,
|
||||||
|
private logService: LogService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
canActivate(
|
canActivate(
|
||||||
|
@ -38,11 +40,19 @@ export class AppAuthenticationGuard implements CanActivate {
|
||||||
| Observable<boolean | UrlTree>
|
| Observable<boolean | UrlTree>
|
||||||
| Promise<boolean | UrlTree> {
|
| Promise<boolean | UrlTree> {
|
||||||
return new Promise<boolean | UrlTree>((resolve, reject) => {
|
return new Promise<boolean | UrlTree>((resolve, reject) => {
|
||||||
if (this.appAuthenticationService.loggedIn()) {
|
const loggedIn = this.appAuthenticationService.loggedIn();
|
||||||
|
if (loggedIn) {
|
||||||
resolve(true);
|
resolve(true);
|
||||||
} else {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const userStore = this.appAuthenticationService.useAutoLogin();
|
const userStore = this.appAuthenticationService.useAutoLogin();
|
||||||
if (!!userStore) {
|
if (!userStore) {
|
||||||
|
this.router.navigateByUrl('/account/login');
|
||||||
|
resolve(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const loginSession = this.appAuthenticationService.getLoginSession();
|
const loginSession = this.appAuthenticationService.getLoginSession();
|
||||||
|
|
||||||
const onWebLoginFailure = (error: any) => {
|
const onWebLoginFailure = (error: any) => {
|
||||||
|
@ -83,14 +93,8 @@ export class AppAuthenticationGuard implements CanActivate {
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
onWebLoginFailure(error);
|
onWebLoginFailure(error);
|
||||||
},
|
}
|
||||||
() => {}
|
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
this.router.navigateByUrl('/account/login');
|
|
||||||
resolve(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div fxFlexFill class="layout-container">
|
<div class="layout-container" fxLayout="row">
|
||||||
<div class="navi-container">
|
<div class="navitab-page" fxFlex="60px">
|
||||||
<mat-tab-group
|
<mat-tab-group
|
||||||
#navTabGroup
|
#navTabGroup
|
||||||
vertical
|
vertical
|
||||||
|
@ -93,15 +93,45 @@
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
</mat-tab-group>
|
</mat-tab-group>
|
||||||
|
<ucap-float-action-button
|
||||||
|
*ngIf="fabButtonShow"
|
||||||
|
[buttons]="fabButtons"
|
||||||
|
(buttonClick)="onClickFab($event)"
|
||||||
|
>
|
||||||
|
</ucap-float-action-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-container" fxFlexFill>
|
<div class="content-page" fxFlex="1 1 auto">
|
||||||
<mat-sidenav-container autosize="true" fxFlexFill>
|
<mat-sidenav-container autosize="true">
|
||||||
<mat-sidenav #leftSidenav class="left-sidenav" mode="side" opened="true">
|
<mat-sidenav #leftSidenav class="left-sidenav" mode="side" opened="true">
|
||||||
<router-outlet></router-outlet>
|
<div class="left-sidenav-container" fxLayout="column">
|
||||||
</mat-sidenav>
|
<div class="top-bar" fxFlex="40px">
|
||||||
|
M-Messenger
|
||||||
|
</div>
|
||||||
<div fxFlex="1 1 auto">
|
<div fxFlex="1 1 auto">
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-sidenav>
|
||||||
|
<mat-sidenav
|
||||||
|
#rightSidenav
|
||||||
|
class="right-sidenav"
|
||||||
|
mode="side"
|
||||||
|
opened="true"
|
||||||
|
position="end"
|
||||||
|
>
|
||||||
|
Right
|
||||||
|
</mat-sidenav>
|
||||||
|
|
||||||
|
<mat-sidenav-content>
|
||||||
|
<div class="content-sidenav-container" fxLayout="column">
|
||||||
|
<div class="content-sidenav-top-bar" fxFlex="40px">
|
||||||
|
<app-layouts-top-bar></app-layouts-top-bar>
|
||||||
|
</div>
|
||||||
|
<div class="content-sidenav-body" fxFlex="1 1 auto">
|
||||||
<router-outlet name="content"></router-outlet>
|
<router-outlet name="content"></router-outlet>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-sidenav-content>
|
||||||
</mat-sidenav-container>
|
</mat-sidenav-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,22 +1,47 @@
|
||||||
.layout-container {
|
:host {
|
||||||
display: flex;
|
width: 100%;
|
||||||
|
|
||||||
.navi-container {
|
|
||||||
width: 70px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-container {
|
|
||||||
.left-sidenav {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 370px;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
max-width: 90%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-drawer {
|
.layout-container {
|
||||||
flex: 0 0 auto;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.navitab-page {
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-page {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
mat-sidenav-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.left-sidenav {
|
||||||
|
width: 370px;
|
||||||
|
max-width: 90%;
|
||||||
|
|
||||||
|
.left-sidenav-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-sidenav {
|
||||||
|
width: 370px;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-sidenav-content {
|
||||||
|
.content-sidenav-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.content-sidenav-body {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,12 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
|
||||||
@ViewChild('leftSidenav', { static: true })
|
@ViewChild('leftSidenav', { static: true })
|
||||||
leftSidenav: MatSidenav;
|
leftSidenav: MatSidenav;
|
||||||
|
|
||||||
|
showFooter = false;
|
||||||
|
|
||||||
|
/** FAB */
|
||||||
|
fabButtonShow = true;
|
||||||
|
fabButtons: { icon: string; tooltip?: string; divisionType?: string }[];
|
||||||
|
|
||||||
private windowSizeSubscription: Subscription;
|
private windowSizeSubscription: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -50,6 +56,8 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setTabGroup(this.router.url);
|
this.setTabGroup(this.router.url);
|
||||||
|
|
||||||
|
this.setFabInitial(NAVS[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
|
@ -70,6 +78,7 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
|
||||||
NAVS[event.index],
|
NAVS[event.index],
|
||||||
{ outlets: { content: 'index' } }
|
{ outlets: { content: 'index' } }
|
||||||
]);
|
]);
|
||||||
|
this.setFabInitial(NAVS[event.index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
onClickToggleLeftSidenav() {
|
onClickToggleLeftSidenav() {
|
||||||
|
@ -85,4 +94,101 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
|
||||||
url.startsWith(`/${v}`)
|
url.startsWith(`/${v}`)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setFabInitial(type: string) {
|
||||||
|
switch (type) {
|
||||||
|
case 'group':
|
||||||
|
{
|
||||||
|
this.fabButtonShow = true;
|
||||||
|
this.fabButtons = [
|
||||||
|
{
|
||||||
|
icon: 'add',
|
||||||
|
tooltip: '그룹 추가',
|
||||||
|
divisionType: 'GROUP_NEW_ADD'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'chat':
|
||||||
|
{
|
||||||
|
this.fabButtonShow = true;
|
||||||
|
this.fabButtons = [
|
||||||
|
{
|
||||||
|
icon: 'chat',
|
||||||
|
tooltip: '대화 추가',
|
||||||
|
divisionType: 'CAHT_NEW_ADD'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// if (environment.productConfig.CommonSetting.useTimerRoom) {
|
||||||
|
// this.fabButtons.push({
|
||||||
|
// icon: 'timer',
|
||||||
|
// tooltip: this.translateService.instant('chat.newTimerChat'),
|
||||||
|
// divisionType: 'CHAT_NEW_TIMER_ADD'
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'organization':
|
||||||
|
{
|
||||||
|
this.fabButtonShow = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'message':
|
||||||
|
{
|
||||||
|
this.fabButtonShow = true;
|
||||||
|
this.fabButtons = [
|
||||||
|
{
|
||||||
|
icon: 'add',
|
||||||
|
tooltip: '쪽지 추가',
|
||||||
|
divisionType: 'MESSAGE_NEW'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// case MainMenu.Call:
|
||||||
|
// {
|
||||||
|
// this.fabButtonShow = false;
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
this.fabButtonShow = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickFab(params: { btn: any }) {
|
||||||
|
const btn = params.btn as {
|
||||||
|
icon: string;
|
||||||
|
tooltip?: string;
|
||||||
|
divisionType?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (btn.divisionType) {
|
||||||
|
case 'GROUP_NEW_ADD':
|
||||||
|
{
|
||||||
|
this.logService.debug('GROUP_NEW_ADD');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'CAHT_NEW_ADD':
|
||||||
|
{
|
||||||
|
this.logService.debug('CAHT_NEW_ADD');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'CHAT_NEW_TIMER_ADD':
|
||||||
|
{
|
||||||
|
// if (environment.productConfig.CommonSetting.useTimerRoom) {
|
||||||
|
// this.onClickNewChat('TIMER');
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'MESSAGE_NEW':
|
||||||
|
{
|
||||||
|
this.logService.debug('MESSAGE_NEW');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,8 @@
|
||||||
|
<div class="layout-container" fxFlexFill fxLayout="column">
|
||||||
|
<div *ngIf="showTopbar" class="top-bar" fxFlex="50px">
|
||||||
|
<app-layouts-top-bar></app-layouts-top-bar>
|
||||||
|
</div>
|
||||||
|
<div class="layout-content" fxFlex="1 1 auto">
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
.layout-container {
|
||||||
|
}
|
|
@ -6,5 +6,7 @@ import { Component } from '@angular/core';
|
||||||
styleUrls: ['./no-navi.layout.component.scss']
|
styleUrls: ['./no-navi.layout.component.scss']
|
||||||
})
|
})
|
||||||
export class NoNaviLayoutComponent {
|
export class NoNaviLayoutComponent {
|
||||||
|
showTopbar = true;
|
||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<mat-toolbar class=".title-bar">
|
<div class="title-bar">
|
||||||
<ucap-title-bar
|
<ucap-title-bar
|
||||||
[platform]="platform"
|
[platform]="platform"
|
||||||
[native]="native"
|
[native]="native"
|
||||||
|
@ -7,4 +7,4 @@
|
||||||
(minimized)="onMinimizedTitleBar()"
|
(minimized)="onMinimizedTitleBar()"
|
||||||
>
|
>
|
||||||
</ucap-title-bar>
|
</ucap-title-bar>
|
||||||
</mat-toolbar>
|
</div>
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
.title-bar {
|
.title-bar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 50px;
|
height: 100%;
|
||||||
-webkit-user-select: none;
|
|
||||||
-webkit-app-region: drag;
|
|
||||||
position: fixed;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
display: flex;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
cursor: pointer;
|
background-color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
<div class="login-page-container" fxLayout="row">
|
<div class="login-container">
|
||||||
<div fxFlex="1 1 auto">Login</div>
|
|
||||||
<div class="login-section-container">
|
|
||||||
<app-sections-account-login
|
<app-sections-account-login
|
||||||
[companyGroupCode]="companyGroupCode"
|
[companyGroupCode]="companyGroupCode"
|
||||||
[fixedCompanyCode]="fixedCompanyCode"
|
[fixedCompanyCode]="fixedCompanyCode"
|
||||||
|
@ -9,4 +7,3 @@
|
||||||
[useAutoLogin]="useAutoLogin"
|
[useAutoLogin]="useAutoLogin"
|
||||||
></app-sections-account-login>
|
></app-sections-account-login>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,6 +1,62 @@
|
||||||
.login-page-container {
|
@import '../../../../assets/scss/components';
|
||||||
.login-section-container {
|
|
||||||
width: 400px;
|
.login-container {
|
||||||
margin: 40px;
|
width: 100%;
|
||||||
}
|
overflow: auto;
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: $bg-gray;
|
||||||
|
background-image: url(../../../../assets/images/bg/bg_login_circle_square01.svg),
|
||||||
|
url(../../../../assets/images/bg/bg_login_circle_stroke01.svg),
|
||||||
|
url(../../../../assets/images/bg/bg_login_circle01.svg),
|
||||||
|
url(../../../../assets/images/bg/bg_login_circle03.svg),
|
||||||
|
url(../../../../assets/images/bg/bg_login_circle_diagonal01.svg),
|
||||||
|
url(../../../../assets/images/bg/bg_login_circle_square02.svg),
|
||||||
|
url(../../../../assets/images/bg/bg_login_circle04.svg),
|
||||||
|
url(../../../../assets/images/bg/bg_login_circle05.svg),
|
||||||
|
url(../../../../assets/images/bg/bg_login_circle06.svg),
|
||||||
|
url(../../../../assets/images/bg/bg_login_circle07.svg),
|
||||||
|
url(../../../../assets/images/bg/bg_login_circle08.svg),
|
||||||
|
url(../../../../assets/images/bg/bg_login_circle_diagonal02.svg),
|
||||||
|
url(../../../../assets/images/bg/bg_login_circle_diagonal03.svg),
|
||||||
|
url(../../../../assets/images/bg/bg_login_circle_stroke02.svg),
|
||||||
|
url(../../../../assets/images/bg/bg_login_circle_stroke03.svg),
|
||||||
|
url(../../../../assets/images/bg/bg_login_circle_stroke04.svg),
|
||||||
|
url(../../../../assets/images/bg/bg_login_circle_stroke05.svg),
|
||||||
|
url(../../../../assets/images/bg/bg_login_polygon01.svg),
|
||||||
|
url(../../../../assets/images/bg/bg_login_polygon02.svg);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: unquote($login-bg-w * 323 + '%')
|
||||||
|
unquote($login-bg-h * 179 + '%'),
|
||||||
|
unquote($login-bg-w * -147 + '%') unquote($login-bg-h * 18 + '%'),
|
||||||
|
unquote($login-bg-w * 285 + '%') unquote($login-bg-h * 226 + '%'),
|
||||||
|
unquote($login-bg-w * 1235 + '%') unquote($login-bg-h * -101 + '%'),
|
||||||
|
unquote($login-bg-w * 1397 + '%') unquote($login-bg-h * 163 + '%'),
|
||||||
|
unquote($login-bg-w * 1569 + '%') unquote($login-bg-h * 580 + '%'),
|
||||||
|
unquote($login-bg-w * 426 + '%') unquote($login-bg-h * 293 + '%'),
|
||||||
|
unquote($login-bg-w * 1531 + '%') unquote($login-bg-h * 250 + '%'),
|
||||||
|
unquote($login-bg-w * 1774 + '%') unquote($login-bg-h * 166 + '%'),
|
||||||
|
unquote($login-bg-w * 1362 + '%') unquote($login-bg-h * 673 + '%'),
|
||||||
|
unquote($login-bg-w * 152 + '%') unquote($login-bg-h * 730 + '%'),
|
||||||
|
unquote($login-bg-w * 286 + '%') unquote($login-bg-h * 719 + '%'),
|
||||||
|
unquote($login-bg-w * 683 + '%') unquote($login-bg-h * 593 + '%'),
|
||||||
|
unquote($login-bg-w * 498 + '%') unquote($login-bg-h * 453 + '%'),
|
||||||
|
unquote($login-bg-w * 1709 + '%') unquote($login-bg-h * 599 + '%'),
|
||||||
|
unquote($login-bg-w * 1395 + '%') unquote($login-bg-h * 989 + '%'),
|
||||||
|
unquote(100 + $login-bg-w * 89 + '%') unquote(100 + $login-bg-h * 137 + '%'),
|
||||||
|
unquote($login-bg-w * 90 + '%') unquote($login-bg-h * 463 + '%'),
|
||||||
|
unquote($login-bg-w * 549 + '%') unquote($login-bg-h * 874 + '%');
|
||||||
|
background-size: unquote($login-bg-w * 79 + '%'),
|
||||||
|
unquote($login-bg-w * 333 + '%'), unquote($login-bg-w * 84 + '%'),
|
||||||
|
unquote($login-bg-w * 172 + '%'), unquote($login-bg-w * 210 + '%'),
|
||||||
|
unquote($login-bg-w * 94 + '%'), unquote($login-bg-w * 44 + '%'),
|
||||||
|
unquote($login-bg-w * 118 + '%'), unquote($login-bg-w * 52 + '%'),
|
||||||
|
unquote($login-bg-w * 70 + '%'), unquote($login-bg-w * 172 + '%'),
|
||||||
|
unquote($login-bg-w * 82 + '%'), unquote($login-bg-w * 135 + '%'),
|
||||||
|
unquote($login-bg-w * 102 + '%'), unquote($login-bg-w * 130 + '%'),
|
||||||
|
unquote($login-bg-w * 184 + '%'), unquote($login-bg-w * 370 + '%'),
|
||||||
|
unquote($login-bg-w * 122 + '%'), unquote($login-bg-w * 75 + '%');
|
||||||
|
padding-top: 5%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import { environment } from '@environments';
|
||||||
|
|
||||||
import { UserStore } from '@app/models/user-store';
|
import { UserStore } from '@app/models/user-store';
|
||||||
import { AppKey } from '@app/types/app-key.type';
|
import { AppKey } from '@app/types/app-key.type';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-pages-account-login',
|
selector: 'app-pages-account-login',
|
||||||
|
@ -23,14 +25,25 @@ export class LoginPageComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
readonly fixedCompanyCode = environment.companyConfig.fixedCompanyCode;
|
readonly fixedCompanyCode = environment.companyConfig.fixedCompanyCode;
|
||||||
|
|
||||||
|
private ngOnDestroySubject = new Subject<boolean>();
|
||||||
|
|
||||||
constructor(private localStorageService: LocalStorageService) {}
|
constructor(private localStorageService: LocalStorageService) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.userStore = this.localStorageService.encGet<UserStore>(
|
this.ngOnDestroySubject = new Subject<boolean>();
|
||||||
|
|
||||||
|
this.localStorageService
|
||||||
|
.encGet$<UserStore>(
|
||||||
AppKey.UserStore,
|
AppKey.UserStore,
|
||||||
environment.productConfig.localEncriptionKey
|
environment.productConfig.localEncriptionKey
|
||||||
);
|
)
|
||||||
|
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||||
|
.subscribe((userStore) => (this.userStore = userStore));
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {}
|
ngOnDestroy(): void {
|
||||||
|
if (!!this.ngOnDestroySubject) {
|
||||||
|
this.ngOnDestroySubject.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,17 +3,32 @@ import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
|
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
|
|
||||||
|
import { AppChatSectionModule } from '@app/sections/chat/chat.section.module';
|
||||||
|
|
||||||
import { AppChatRoutingPageModule } from './chat-routing.page.module';
|
import { AppChatRoutingPageModule } from './chat-routing.page.module';
|
||||||
|
|
||||||
import { IndexPageComponent } from './components/index.page.component';
|
import { IndexPageComponent } from './components/index.page.component';
|
||||||
import { SidenavPageComponent } from './components/sidenav.page.component';
|
import { SidenavPageComponent } from './components/sidenav.page.component';
|
||||||
|
|
||||||
|
import { UiModule } from '@ucap/ng-ui';
|
||||||
|
|
||||||
export const COMPONENTS = [IndexPageComponent, SidenavPageComponent];
|
export const COMPONENTS = [IndexPageComponent, SidenavPageComponent];
|
||||||
|
|
||||||
export { IndexPageComponent, SidenavPageComponent };
|
export { IndexPageComponent, SidenavPageComponent };
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, FlexLayoutModule, AppChatRoutingPageModule],
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
FlexLayoutModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatMenuModule,
|
||||||
|
AppChatSectionModule,
|
||||||
|
AppChatRoutingPageModule,
|
||||||
|
UiModule
|
||||||
|
],
|
||||||
declarations: [...COMPONENTS],
|
declarations: [...COMPONENTS],
|
||||||
entryComponents: []
|
entryComponents: []
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,3 +1,27 @@
|
||||||
<div fxFlexFill>
|
<div fxFlexFill class="sidenav-container">
|
||||||
sidenav page of chat is works!
|
<div class="group-header">
|
||||||
|
<h3>대화</h3>
|
||||||
|
<div class="group-menu-btn">
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
[matMenuTriggerFor]="groupMenu"
|
||||||
|
aria-label="group menu"
|
||||||
|
>
|
||||||
|
<mat-icon>more_vert</mat-icon>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<app-sections-chat-search
|
||||||
|
(keyDownEnter)="onKeyDownSearch($event)"
|
||||||
|
(searchCancel)="onClickCancel()"
|
||||||
|
></app-sections-chat-search>
|
||||||
|
<app-sections-chat-list
|
||||||
|
fxFlexFill
|
||||||
|
[searchObj]="searchObj"
|
||||||
|
></app-sections-chat-list>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-menu #groupMenu="matMenu">
|
||||||
|
<button mat-menu-item>Item 1</button>
|
||||||
|
<button mat-menu-item>Item 2</button>
|
||||||
|
</mat-menu>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject, ChangeDetectorRef } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import { LogService } from '@ucap/ng-logger';
|
import { LogService } from '@ucap/ng-logger';
|
||||||
|
@ -9,5 +9,29 @@ import { LogService } from '@ucap/ng-logger';
|
||||||
styleUrls: ['./sidenav.page.component.scss']
|
styleUrls: ['./sidenav.page.component.scss']
|
||||||
})
|
})
|
||||||
export class SidenavPageComponent {
|
export class SidenavPageComponent {
|
||||||
constructor(private logService: LogService) {}
|
searchObj: any = {
|
||||||
|
isShowSearch: false,
|
||||||
|
searchWord: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private logService: LogService,
|
||||||
|
private changeDetectorRef: ChangeDetectorRef
|
||||||
|
) {}
|
||||||
|
|
||||||
|
onKeyDownSearch(params: { companyCode: string; searchWord: string }) {
|
||||||
|
this.searchObj = {
|
||||||
|
isShowSearch: true,
|
||||||
|
searchWord: params.searchWord
|
||||||
|
};
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickCancel() {
|
||||||
|
this.searchObj = {
|
||||||
|
isShowSearch: false,
|
||||||
|
searchWord: ''
|
||||||
|
};
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
<div>
|
<div fxLayout="column">
|
||||||
index of group
|
<div class="subtitle" fxFlex="30px">Welcome to M-Messenger</div>
|
||||||
|
<div class="content-container" fxFlex="1 1 auto" fxLayout="row">
|
||||||
|
<div class="profile-container" fxFlex="44%">
|
||||||
|
<app-sections-group-profile [userSeq]="userSeq">
|
||||||
|
</app-sections-group-profile>
|
||||||
|
</div>
|
||||||
|
<div class="group-info-container" fxFlex="1 1 auto">
|
||||||
|
<app-sections-group-info [userSeq]="userSeq"></app-sections-group-info>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
.profile-container {
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-info-container {
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
|
@ -21,10 +21,13 @@ export class IndexPageComponent implements OnInit, OnDestroy {
|
||||||
private logService: LogService
|
private logService: LogService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
userSeq: string;
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.paramsSubscription = this.activatedRoute.queryParams.subscribe(
|
this.paramsSubscription = this.activatedRoute.queryParams.subscribe(
|
||||||
(params: Params) => {
|
(params: Params) => {
|
||||||
console.log('IndexPageComponent', params[QueryParams.ID]);
|
const seqParam = params[QueryParams.ID];
|
||||||
|
this.userSeq = !!seqParam ? seqParam : undefined;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,50 @@
|
||||||
<div fxFlexFill class="sidenav-container">
|
<div class="sidenav-container" fxFlexFill fxLayout="column">
|
||||||
<app-sections-group-search></app-sections-group-search>
|
<div class="sub-header" fxFlex="50px" fxLayout="row">
|
||||||
<app-sections-group-list fxFlexFill></app-sections-group-list>
|
<h3 fxFlex="1 1 auto">그룹</h3>
|
||||||
|
<div class="menu-btn" fxFlex="70px">
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
[matMenuTriggerFor]="groupViewMenu"
|
||||||
|
aria-label="group view menu"
|
||||||
|
>
|
||||||
|
<mat-icon>refresh</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
[matMenuTriggerFor]="groupMenu"
|
||||||
|
aria-label="group menu"
|
||||||
|
>
|
||||||
|
<mat-icon>more_vert</mat-icon>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="extra-box" fxFlex="50px">
|
||||||
|
<app-sections-group-search
|
||||||
|
(keyDownEnter)="onKeyDownSearch($event)"
|
||||||
|
(searchCancel)="onClickCancel()"
|
||||||
|
>
|
||||||
|
</app-sections-group-search>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlex="1 1 auto">
|
||||||
|
<app-sections-group-list
|
||||||
|
fxFlexFill
|
||||||
|
[searchObj]="searchObj"
|
||||||
|
></app-sections-group-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-menu #groupMenu="matMenu">
|
||||||
|
<button mat-menu-item (click)="onClickGroupMenu('GROUP_NEW')">
|
||||||
|
새그룹 추가
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item>그룹전체 열기</button>
|
||||||
|
<button mat-menu-item>그룹전체 닫기</button>
|
||||||
|
<button mat-menu-item>그룹순서 바꾸기</button>
|
||||||
|
</mat-menu>
|
||||||
|
|
||||||
|
<mat-menu #groupViewMenu="matMenu">
|
||||||
|
<button mat-menu-item>전체보기</button>
|
||||||
|
<button mat-menu-item>접속한 동료만 보기</button>
|
||||||
|
<button mat-menu-item>온/오프라인 보기</button>
|
||||||
|
</mat-menu>
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
import { Component, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
|
||||||
import { ActivatedRoute, Router, Params } from '@angular/router';
|
import { ActivatedRoute, Router, Params } from '@angular/router';
|
||||||
|
|
||||||
import { LogService } from '@ucap/ng-logger';
|
import { LogService } from '@ucap/ng-logger';
|
||||||
|
|
||||||
import { QueryParams } from '../types/params.type';
|
import { QueryParams } from '../types/params.type';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { CreateChatDialogComponent } from '@app/sections/group/components/component-ui/dialogs/create-chat.dialog.component';
|
||||||
|
import { SelectUserDialogType } from '@app/types';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-pages-group-sidenav',
|
selector: 'app-pages-group-sidenav',
|
||||||
|
@ -13,25 +16,57 @@ import { QueryParams } from '../types/params.type';
|
||||||
styleUrls: ['./sidenav.page.component.scss']
|
styleUrls: ['./sidenav.page.component.scss']
|
||||||
})
|
})
|
||||||
export class SidenavPageComponent implements OnInit, OnDestroy {
|
export class SidenavPageComponent implements OnInit, OnDestroy {
|
||||||
private queryParamsSubscription: Subscription;
|
searchObj: any = {
|
||||||
|
isShowSearch: false,
|
||||||
|
companyCode: '',
|
||||||
|
searchWord: ''
|
||||||
|
};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private activatedRoute: ActivatedRoute,
|
private activatedRoute: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private logService: LogService
|
private logService: LogService,
|
||||||
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
|
public dialog: MatDialog
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {}
|
||||||
this.queryParamsSubscription = this.activatedRoute.queryParams.subscribe(
|
|
||||||
(params: Params) => {
|
ngOnDestroy(): void {}
|
||||||
console.log('SidenavPageComponent', params[QueryParams.ID]);
|
|
||||||
}
|
onClickFab(event: MouseEvent) {}
|
||||||
);
|
onKeyDownSearch(params: { companyCode: string; searchWord: string }) {
|
||||||
|
this.searchObj = {
|
||||||
|
isShowSearch: true,
|
||||||
|
companyCode: params.companyCode,
|
||||||
|
searchWord: params.searchWord
|
||||||
|
};
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
onClickCancel() {
|
||||||
if (!!this.queryParamsSubscription) {
|
this.searchObj = {
|
||||||
this.queryParamsSubscription.unsubscribe();
|
isShowSearch: false,
|
||||||
|
companyCode: '',
|
||||||
|
searchWord: ''
|
||||||
|
};
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickGroupMenu(menuType: string) {
|
||||||
|
switch (menuType) {
|
||||||
|
case 'GROUP_NEW':
|
||||||
|
{
|
||||||
|
this.dialog.open(CreateChatDialogComponent, {
|
||||||
|
width: '850px',
|
||||||
|
height: '600px',
|
||||||
|
data: {
|
||||||
|
type: SelectUserDialogType.NewGroup,
|
||||||
|
title: '새 그룹 추가'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,9 @@ import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
|
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
|
|
||||||
import { AppGroupSectionModule } from '@app/sections/group/group.section.module';
|
import { AppGroupSectionModule } from '@app/sections/group/group.section.module';
|
||||||
|
|
||||||
import { AppGroupRoutingPageModule } from './group-routing.page.module';
|
import { AppGroupRoutingPageModule } from './group-routing.page.module';
|
||||||
|
@ -10,6 +13,8 @@ import { AppGroupRoutingPageModule } from './group-routing.page.module';
|
||||||
import { IndexPageComponent } from './components/index.page.component';
|
import { IndexPageComponent } from './components/index.page.component';
|
||||||
import { SidenavPageComponent } from './components/sidenav.page.component';
|
import { SidenavPageComponent } from './components/sidenav.page.component';
|
||||||
|
|
||||||
|
import { UiModule } from '@ucap/ng-ui';
|
||||||
|
|
||||||
export const COMPONENTS = [IndexPageComponent, SidenavPageComponent];
|
export const COMPONENTS = [IndexPageComponent, SidenavPageComponent];
|
||||||
|
|
||||||
export { IndexPageComponent, SidenavPageComponent };
|
export { IndexPageComponent, SidenavPageComponent };
|
||||||
|
@ -18,8 +23,11 @@ export { IndexPageComponent, SidenavPageComponent };
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
FlexLayoutModule,
|
FlexLayoutModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatMenuModule,
|
||||||
AppGroupSectionModule,
|
AppGroupSectionModule,
|
||||||
AppGroupRoutingPageModule
|
AppGroupRoutingPageModule,
|
||||||
|
UiModule
|
||||||
],
|
],
|
||||||
declarations: [...COMPONENTS],
|
declarations: [...COMPONENTS],
|
||||||
entryComponents: []
|
entryComponents: []
|
||||||
|
|
|
@ -1,3 +1,25 @@
|
||||||
<div fxFlexFill>
|
<div class="sidenav-container" fxFlexFill fxLayout="column">
|
||||||
sidenav page of ogranization is works!
|
<div class="sub-header" fxFlex="50px" fxLayout="row">
|
||||||
|
<h3 fxFlex="1 1 auto">조직도</h3>
|
||||||
|
<div class="menu-btn" fxFlex="70px">
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
[matMenuTriggerFor]="organizationMenu"
|
||||||
|
aria-label="organization menu"
|
||||||
|
>
|
||||||
|
<mat-icon>more_vert</mat-icon>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="extra-box" fxFlex="50px">
|
||||||
|
LG CNS
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxFlex="1 1 auto">
|
||||||
|
<app-sections-organization-tree></app-sections-organization-tree>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<mat-menu #organizationMenu="matMenu">
|
||||||
|
<button mat-menu-item>Item 1</button>
|
||||||
|
<button mat-menu-item>Item 2</button>
|
||||||
|
</mat-menu>
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
.sidenav-container {
|
||||||
|
padding-bottom: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.organization-header {
|
||||||
|
}
|
||||||
|
|
||||||
|
.organization-tree {
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,12 +3,24 @@ import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
|
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
|
|
||||||
|
import { AppOrganizationSectionModule } from '@app/sections/organization/organization.section.module';
|
||||||
|
|
||||||
import { AppOrganizationRoutingPageModule } from './organization-routing.page.module';
|
import { AppOrganizationRoutingPageModule } from './organization-routing.page.module';
|
||||||
|
|
||||||
import { COMPONENTS } from './components';
|
import { COMPONENTS } from './components';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, FlexLayoutModule, AppOrganizationRoutingPageModule],
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
FlexLayoutModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatMenuModule,
|
||||||
|
AppOrganizationSectionModule,
|
||||||
|
AppOrganizationRoutingPageModule
|
||||||
|
],
|
||||||
declarations: [...COMPONENTS],
|
declarations: [...COMPONENTS],
|
||||||
entryComponents: []
|
entryComponents: []
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Observable, forkJoin, Subject } from 'rxjs';
|
import { Observable, forkJoin, Subject } from 'rxjs';
|
||||||
import { take, filter, map, takeUntil } from 'rxjs/operators';
|
import { take, takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
|
@ -13,7 +13,6 @@ import { Store } from '@ngrx/store';
|
||||||
import { StatusCode } from '@ucap/api';
|
import { StatusCode } from '@ucap/api';
|
||||||
|
|
||||||
import { LogService } from '@ucap/ng-logger';
|
import { LogService } from '@ucap/ng-logger';
|
||||||
import { SessionStorageService } from '@ucap/ng-web-storage';
|
|
||||||
import { PublicApiService } from '@ucap/ng-api-public';
|
import { PublicApiService } from '@ucap/ng-api-public';
|
||||||
import { ExternalApiService } from '@ucap/ng-api-external';
|
import { ExternalApiService } from '@ucap/ng-api-external';
|
||||||
import { ProtocolService } from '@ucap/ng-protocol';
|
import { ProtocolService } from '@ucap/ng-protocol';
|
||||||
|
@ -22,7 +21,6 @@ import { CompanyActions } from '@ucap/ng-store-organization';
|
||||||
import { ConfigurationActions } from '@ucap/ng-store-authentication';
|
import { ConfigurationActions } from '@ucap/ng-store-authentication';
|
||||||
|
|
||||||
import { AppAuthenticationService } from '@app/services/app-authentication.service';
|
import { AppAuthenticationService } from '@app/services/app-authentication.service';
|
||||||
import { AppKey } from '@app/types/app-key.type';
|
|
||||||
import { LoginSession } from '@app/models/login-session';
|
import { LoginSession } from '@app/models/login-session';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -32,7 +30,6 @@ export class AppSessionResolver implements Resolve<void> {
|
||||||
private externalApiService: ExternalApiService,
|
private externalApiService: ExternalApiService,
|
||||||
private protocolService: ProtocolService,
|
private protocolService: ProtocolService,
|
||||||
private store: Store<any>,
|
private store: Store<any>,
|
||||||
private sessionStorageService: SessionStorageService,
|
|
||||||
private appAuthenticationService: AppAuthenticationService,
|
private appAuthenticationService: AppAuthenticationService,
|
||||||
private logService: LogService
|
private logService: LogService
|
||||||
) {}
|
) {}
|
||||||
|
@ -44,7 +41,6 @@ export class AppSessionResolver implements Resolve<void> {
|
||||||
return new Promise<void>(async (resolve, reject) => {
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
const loginSession = this.appAuthenticationService.getLoginSession();
|
const loginSession = this.appAuthenticationService.getLoginSession();
|
||||||
|
|
||||||
if (loginSession.alive) {
|
if (loginSession.alive) {
|
||||||
resolve();
|
resolve();
|
||||||
return;
|
return;
|
||||||
|
@ -115,18 +111,15 @@ export class AppSessionResolver implements Resolve<void> {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const destroy$ = new Subject<boolean>();
|
const destroySubject = new Subject<boolean>();
|
||||||
this.sessionStorageService.changed$
|
this.appAuthenticationService
|
||||||
.pipe(
|
.getLoginSession$()
|
||||||
takeUntil(destroy$),
|
.pipe(takeUntil(destroySubject))
|
||||||
filter((param) => AppKey.LoginSession === param.key),
|
|
||||||
map((param) => param.value)
|
|
||||||
)
|
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(v) => {
|
(v) => {
|
||||||
if ((v as LoginSession).alive) {
|
if ((v as LoginSession).alive) {
|
||||||
destroy$.next(true);
|
destroySubject.next(true);
|
||||||
destroy$.unsubscribe();
|
destroySubject.complete();
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,6 +10,24 @@ import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
|
||||||
import { AuthenticationUiModule } from '@ucap/ng-ui-authentication';
|
import { AuthenticationUiModule } from '@ucap/ng-ui-authentication';
|
||||||
|
|
||||||
import { COMPONENTS } from './components';
|
import { COMPONENTS } from './components';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||||
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
|
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||||
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||||
|
import { MatSliderModule } from '@angular/material/slider';
|
||||||
|
import { MatTabsModule } from '@angular/material/tabs';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -17,7 +35,28 @@ import { COMPONENTS } from './components';
|
||||||
FlexLayoutModule,
|
FlexLayoutModule,
|
||||||
MatCheckboxModule,
|
MatCheckboxModule,
|
||||||
I18nModule,
|
I18nModule,
|
||||||
AuthenticationUiModule
|
AuthenticationUiModule,
|
||||||
|
|
||||||
|
ReactiveFormsModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatButtonToggleModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatDatepickerModule,
|
||||||
|
MatDialogModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatMenuModule,
|
||||||
|
MatProgressBarModule,
|
||||||
|
MatProgressSpinnerModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
MatSelectModule,
|
||||||
|
MatSidenavModule,
|
||||||
|
MatSliderModule,
|
||||||
|
MatTabsModule,
|
||||||
|
MatTooltipModule,
|
||||||
|
MatToolbarModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatSelectModule
|
||||||
],
|
],
|
||||||
exports: [...COMPONENTS],
|
exports: [...COMPONENTS],
|
||||||
declarations: [...COMPONENTS],
|
declarations: [...COMPONENTS],
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
<div class="login-box">
|
||||||
|
<ng-content select="[ucapAuthenticationLogin='header']"></ng-content>
|
||||||
|
|
||||||
|
<div class="login-content">
|
||||||
|
<form name="loginForm" [formGroup]="loginForm" novalidate>
|
||||||
|
<mat-form-field
|
||||||
|
[style.display]="!!fixedCompanyCode ? 'none' : 'block'"
|
||||||
|
class="login-company"
|
||||||
|
appearance="none"
|
||||||
|
>
|
||||||
|
<mat-select
|
||||||
|
[formControl]="companyCodeFormControl"
|
||||||
|
[value]="companyCode || fixedCompanyCode"
|
||||||
|
placeholder="{{ 'login.labels.selectCompany' | ucapI18n }}"
|
||||||
|
class="login-input-area login-select-form"
|
||||||
|
>
|
||||||
|
<mat-option
|
||||||
|
*ngFor="let company of companyList"
|
||||||
|
[value]="company.companyCode"
|
||||||
|
>{{ company.companyName }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div class="login-input-area idpass-type">
|
||||||
|
<mat-form-field
|
||||||
|
class="login-idpass-txt"
|
||||||
|
appearance="none"
|
||||||
|
floatLabel=""
|
||||||
|
>
|
||||||
|
<!-- <mat-label>{{ 'login.fields.loginId' | ucapI18n }}</mat-label> -->
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
[formControl]="loginIdFormControl"
|
||||||
|
placeholder="{{ 'login.fields.loginId' | ucapI18n }}"
|
||||||
|
/>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="login-input-area idpass-type pass-type">
|
||||||
|
<mat-form-field class="login-idpass-txt" appearance="none">
|
||||||
|
<!-- <mat-label>{{ 'login.fields.loginPw' | ucapI18n }}</mat-label> -->
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
type="password"
|
||||||
|
[formControl]="loginPwFormControl"
|
||||||
|
placeholder="{{ 'login.fields.loginPw' | ucapI18n }}"
|
||||||
|
#loginPw
|
||||||
|
/>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="error-container">
|
||||||
|
<mat-error
|
||||||
|
*ngIf="
|
||||||
|
companyCodeFormControl.dirty &&
|
||||||
|
companyCodeFormControl.invalid &&
|
||||||
|
companyCodeFormControl.hasError('required')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ 'login.errors.requireCompany' | ucapI18n }}
|
||||||
|
</mat-error>
|
||||||
|
<mat-error
|
||||||
|
*ngIf="
|
||||||
|
loginIdFormControl.dirty &&
|
||||||
|
loginIdFormControl.invalid &&
|
||||||
|
loginIdFormControl.hasError('required')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ 'login.errors.requireLoginId' | ucapI18n }}
|
||||||
|
</mat-error>
|
||||||
|
<mat-error
|
||||||
|
*ngIf="
|
||||||
|
loginPwFormControl.dirty &&
|
||||||
|
loginPwFormControl.invalid &&
|
||||||
|
loginPwFormControl.hasError('required')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ 'login.errors.requireLoginPw' | ucapI18n }}
|
||||||
|
</mat-error>
|
||||||
|
<mat-error *ngIf="loginFailed">
|
||||||
|
{{ 'login.errors.failed' | ucapI18n }}
|
||||||
|
</mat-error>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
mat-raised-button
|
||||||
|
class="login-input-submit"
|
||||||
|
aria-label="LOG IN"
|
||||||
|
[disabled]="
|
||||||
|
loginForm.invalid ||
|
||||||
|
disable ||
|
||||||
|
(!!loginTry && !!loginTry.remainTimeForNextTry)
|
||||||
|
"
|
||||||
|
(click)="onClickLogin()"
|
||||||
|
>
|
||||||
|
<ng-container
|
||||||
|
*ngIf="!processing && (!loginTry || !loginTry.remainTimeForNextTry)"
|
||||||
|
>
|
||||||
|
{{ 'login.labels.doLogin' | ucapI18n }}
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="!!loginTry && !!loginTry.remainTimeForNextTry">
|
||||||
|
{{ 'login.errors.attemptsExceeded' | ucapI18n }}
|
||||||
|
(
|
||||||
|
{{
|
||||||
|
moment
|
||||||
|
.utc(
|
||||||
|
moment
|
||||||
|
.duration(loginTry.remainTimeForNextTry, 'seconds')
|
||||||
|
.asMilliseconds()
|
||||||
|
)
|
||||||
|
.format('mm:ss')
|
||||||
|
}}
|
||||||
|
)
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<mat-spinner
|
||||||
|
*ngIf="processing && (!loginTry || !loginTry.remainTimeForNextTry)"
|
||||||
|
>
|
||||||
|
</mat-spinner>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<ng-content select="[ucapAuthenticationLogin='footer']"></ng-content>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,160 @@
|
||||||
|
@import '../../../../../assets/scss/components';
|
||||||
|
|
||||||
|
.login-box {
|
||||||
|
@extend %clearfix;
|
||||||
|
padding: 0 0 45px;
|
||||||
|
width: 420px;
|
||||||
|
margin: auto;
|
||||||
|
text-align: center;
|
||||||
|
flex-basis: auto;
|
||||||
|
align-items: center;
|
||||||
|
.logo-img {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
img {
|
||||||
|
margin-bottom: 7px;
|
||||||
|
vertical-align: top;
|
||||||
|
@include screen(mid) {
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
@include screen(xs) {
|
||||||
|
width: 100px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@extend %guideline;
|
||||||
|
|
||||||
|
.login-content {
|
||||||
|
@extend %guideline2; //Guide Line2
|
||||||
|
margin: 30px auto 0;
|
||||||
|
.login-input-area {
|
||||||
|
border: 1px solid #cccccc;
|
||||||
|
border-radius: 2px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 420px;
|
||||||
|
min-width: 150px;
|
||||||
|
height: 60px;
|
||||||
|
background-color: $white;
|
||||||
|
margin-top: 10px;
|
||||||
|
&.login-select-form {
|
||||||
|
height: 60px;
|
||||||
|
line-height: 60px;
|
||||||
|
padding: 0 16px;
|
||||||
|
@include screen(mid) {
|
||||||
|
height: 50px;
|
||||||
|
line-height: 50px;
|
||||||
|
}
|
||||||
|
@include screen(xs) {
|
||||||
|
height: 42px;
|
||||||
|
line-height: 42px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:first-of-type {
|
||||||
|
margin-top: 0px;
|
||||||
|
}
|
||||||
|
&.idpass-type {
|
||||||
|
padding-left: 50px;
|
||||||
|
position: relative;
|
||||||
|
&::before {
|
||||||
|
font-family: 'material Icons';
|
||||||
|
font-size: 24px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 60px;
|
||||||
|
content: 'perm_identity';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 16px;
|
||||||
|
@include screen(mid) {
|
||||||
|
line-height: 50px;
|
||||||
|
}
|
||||||
|
@include screen(xs) {
|
||||||
|
line-height: 42px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.pass-type {
|
||||||
|
&::before {
|
||||||
|
content: 'https';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.login-idpass-txt {
|
||||||
|
width: 368px;
|
||||||
|
height: 60px;
|
||||||
|
line-height: 60px;
|
||||||
|
font-size: 14px;
|
||||||
|
@include screen(mid) {
|
||||||
|
width: 358 - 60 + px;
|
||||||
|
height: 50px;
|
||||||
|
line-height: 50px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
@include screen(xs) {
|
||||||
|
width: 308 - 60 + px;
|
||||||
|
font-size: 14px;
|
||||||
|
height: 42px;
|
||||||
|
line-height: 42px;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 58px;
|
||||||
|
margin-top: 0;
|
||||||
|
vertical-align: top;
|
||||||
|
background-color: $white;
|
||||||
|
padding: 0 10px 0 5px;
|
||||||
|
@include screen(mid) {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 48px;
|
||||||
|
}
|
||||||
|
@include screen(xs) {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@include screen(mid) {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.login-input-submit {
|
||||||
|
width: 100%;
|
||||||
|
height: 60px;
|
||||||
|
background-color: $black;
|
||||||
|
border-radius: 2px;
|
||||||
|
color: $white;
|
||||||
|
font-size: 20px;
|
||||||
|
@include font-family($font-semibold);
|
||||||
|
border: 0;
|
||||||
|
margin-top: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
@include screen(mid) {
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
@include screen(xs) {
|
||||||
|
font-size: 14px;
|
||||||
|
height: 42px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@include screen(mid) {
|
||||||
|
margin-top: 23px;
|
||||||
|
width: 350px;
|
||||||
|
.login-input-area {
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@include screen(xs) {
|
||||||
|
margin-top: 23px;
|
||||||
|
width: 300px;
|
||||||
|
.login-input-area {
|
||||||
|
height: 42px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.login-company {
|
||||||
|
width: 100%;
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { LoginComponent } from './login.component';
|
||||||
|
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { ChangeDetectorRef } from '@angular/core';
|
||||||
|
import { I18nService, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
|
||||||
|
import { AuthenticationUiModule } from '../authentication-ui.module';
|
||||||
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
import { Company } from '@ucap/api-external';
|
||||||
|
import { LogService } from '@ucap/ng-logger';
|
||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
|
|
||||||
|
describe('ui::authentication::LoginComponent', () => {
|
||||||
|
let component: LoginComponent;
|
||||||
|
let fixture: ComponentFixture<LoginComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
BrowserModule,
|
||||||
|
BrowserAnimationsModule,
|
||||||
|
|
||||||
|
CommonModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
|
||||||
|
MatButtonModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatProgressSpinnerModule,
|
||||||
|
MatSelectModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
AuthenticationUiModule,
|
||||||
|
// { provide: FormBuilder, useValue: new FormBuilder() },
|
||||||
|
// { provide: ChangeDetectorRef, useValue: ChangeDetectorRef },
|
||||||
|
{ provide: I18nService, useValue: new I18nService(new LogService({})) },
|
||||||
|
{
|
||||||
|
provide: UCAP_I18N_NAMESPACE,
|
||||||
|
useValue: 'authentication'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(LoginComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
component.companyList = [
|
||||||
|
{ companyName: 'LG CNS', companyCode: 'GUC100' },
|
||||||
|
{ companyName: 'LG UCAP', companyCode: 'GUC101' }
|
||||||
|
] as Company[];
|
||||||
|
component.loginId = 'test';
|
||||||
|
component.companyCode = 'GUC100';
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
component.ngOnInit();
|
||||||
|
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('login', (done) => {
|
||||||
|
component.companyList = [
|
||||||
|
{ companyName: 'LG CNS', companyCode: 'GUC100' },
|
||||||
|
{ companyName: 'LG UCAP', companyCode: 'GUC101' }
|
||||||
|
] as Company[];
|
||||||
|
component.loginId = 'test';
|
||||||
|
component.companyCode = 'GUC100';
|
||||||
|
|
||||||
|
component.ngOnInit();
|
||||||
|
|
||||||
|
component.login.subscribe((value) => {
|
||||||
|
console.log(value);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
component.onClickLogin();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,110 @@
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
EventEmitter,
|
||||||
|
ViewChild,
|
||||||
|
ElementRef,
|
||||||
|
ChangeDetectorRef
|
||||||
|
} from '@angular/core';
|
||||||
|
import {
|
||||||
|
FormGroup,
|
||||||
|
FormBuilder,
|
||||||
|
Validators,
|
||||||
|
FormControl,
|
||||||
|
ValidatorFn
|
||||||
|
} from '@angular/forms';
|
||||||
|
import { Company } from '@ucap/api-external';
|
||||||
|
import { LoginTry } from '@ucap/pi';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-authentication-login-local',
|
||||||
|
templateUrl: './login.component.html',
|
||||||
|
styleUrls: ['./login.component.scss']
|
||||||
|
})
|
||||||
|
export class LoginComponent implements OnInit {
|
||||||
|
@Input()
|
||||||
|
companyList: Company[];
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
fixedCompanyCode: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
companyCode: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
loginId: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
disable = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
processing = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
loginTry: LoginTry;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
login = new EventEmitter<{
|
||||||
|
companyCode: string;
|
||||||
|
loginId: string;
|
||||||
|
loginPw: string;
|
||||||
|
notValid: () => void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
@ViewChild('loginPw', { static: true }) loginPwElementRef: ElementRef;
|
||||||
|
|
||||||
|
loginForm: FormGroup;
|
||||||
|
companyCodeFormControl = new FormControl('');
|
||||||
|
loginIdFormControl = new FormControl('');
|
||||||
|
loginPwFormControl = new FormControl('');
|
||||||
|
loginFailed = false;
|
||||||
|
|
||||||
|
moment = moment;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private changeDetectorRef: ChangeDetectorRef
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
const companyCodeValidators: ValidatorFn[] = [Validators.required];
|
||||||
|
this.companyCodeFormControl.setValidators(companyCodeValidators);
|
||||||
|
if (!!this.fixedCompanyCode) {
|
||||||
|
this.companyCodeFormControl.setValue(this.fixedCompanyCode);
|
||||||
|
}
|
||||||
|
if (!!this.companyCode) {
|
||||||
|
this.companyCodeFormControl.setValue(this.companyCode);
|
||||||
|
}
|
||||||
|
const loginIdValidators: ValidatorFn[] = [Validators.required];
|
||||||
|
this.loginIdFormControl.setValidators(loginIdValidators);
|
||||||
|
if (!!this.loginId) {
|
||||||
|
this.loginIdFormControl.setValue(this.loginId);
|
||||||
|
}
|
||||||
|
const loginPwValidators: ValidatorFn[] = [Validators.required];
|
||||||
|
this.loginPwFormControl.setValidators(loginPwValidators);
|
||||||
|
|
||||||
|
this.loginForm = this.formBuilder.group({
|
||||||
|
companyCodeFormControl: this.companyCodeFormControl,
|
||||||
|
loginIdFormControl: this.loginIdFormControl,
|
||||||
|
loginPwFormControl: this.loginPwFormControl
|
||||||
|
});
|
||||||
|
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickLogin() {
|
||||||
|
this.login.emit({
|
||||||
|
companyCode: this.loginForm.get('companyCodeFormControl').value,
|
||||||
|
loginId: this.loginForm.get('loginIdFormControl').value,
|
||||||
|
loginPw: this.loginForm.get('loginPwFormControl').value,
|
||||||
|
notValid: () => {
|
||||||
|
this.loginFailed = true;
|
||||||
|
this.loginPwElementRef.nativeElement.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
import { LoginSectionComponent } from './login.section.component';
|
import { LoginSectionComponent } from './login.section.component';
|
||||||
|
import { LoginComponent } from './component-ui/login.component';
|
||||||
|
|
||||||
export const COMPONENTS = [LoginSectionComponent];
|
export const COMPONENTS = [LoginSectionComponent, LoginComponent];
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<ucap-authentication-login
|
<div class="login-section-container">
|
||||||
|
<ucap-authentication-login-local
|
||||||
[companyList]="companyList"
|
[companyList]="companyList"
|
||||||
[fixedCompanyCode]="fixedCompanyCode"
|
[fixedCompanyCode]="fixedCompanyCode"
|
||||||
[companyCode]="userStore?.companyCode"
|
[companyCode]="userStore?.companyCode"
|
||||||
|
@ -8,33 +9,30 @@
|
||||||
[loginTry]="loginTry"
|
[loginTry]="loginTry"
|
||||||
(login)="onLogin($event)"
|
(login)="onLogin($event)"
|
||||||
>
|
>
|
||||||
<div
|
<div ucapAuthenticationLogin="header">
|
||||||
ucapAuthenticationLogin="header"
|
<div class="logo-img">
|
||||||
style="background-image: url(./assets/images/logo/bg_logo_login.png);"
|
<img src="../../../assets/images/logo_140.png" alt="" />
|
||||||
>
|
|
||||||
{{ 'login.labels.instructionsOfLogin' | ucapI18n }}
|
|
||||||
</div>
|
</div>
|
||||||
|
<h1>Welcome to Messenger</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div ucapAuthenticationLogin="footer">
|
<div ucapAuthenticationLogin="footer">
|
||||||
<div
|
<div class="login-chk-area">
|
||||||
class="remember-forgot-password"
|
<div>
|
||||||
fxLayout="row"
|
|
||||||
fxLayout.xs="column"
|
|
||||||
fxLayoutAlign="space-between center"
|
|
||||||
>
|
|
||||||
<mat-checkbox
|
<mat-checkbox
|
||||||
#chkUseRememberMe
|
#chkUseRememberMe
|
||||||
*ngIf="useRememberMe"
|
*ngIf="useRememberMe"
|
||||||
class="remember-me"
|
|
||||||
aria-label="Remember Me"
|
aria-label="Remember Me"
|
||||||
[checked]="!!userStore && userStore.rememberMe"
|
[checked]="!!userStore && userStore.rememberMe"
|
||||||
>
|
>
|
||||||
{{ 'login.labels.rememberMe' | ucapI18n }}
|
{{ 'login.labels.rememberMe' | ucapI18n }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
<mat-checkbox
|
<mat-checkbox
|
||||||
#chkUseAutoLogin
|
#chkUseAutoLogin
|
||||||
*ngIf="useAutoLogin"
|
*ngIf="useAutoLogin"
|
||||||
class="auto-login"
|
|
||||||
aria-label="Auto Login"
|
aria-label="Auto Login"
|
||||||
[checked]="
|
[checked]="
|
||||||
!!userStore &&
|
!!userStore &&
|
||||||
|
@ -46,23 +44,24 @@
|
||||||
{{ 'login.labels.autoLogin' | ucapI18n }}
|
{{ 'login.labels.autoLogin' | ucapI18n }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
<div class="register" fxLayout="column" fxLayoutAlign="center center">
|
</div>
|
||||||
<button
|
<div class="login-pass-info">
|
||||||
class="link btn-login-forgot"
|
<ul>
|
||||||
(click)="onClickForgotPassword('ko')"
|
<li>
|
||||||
>
|
<a href="">{{ 'login.labels.forgotPassword' | ucapI18n }}</a>
|
||||||
Forgot Password? KO
|
</li>
|
||||||
</button>
|
<li>
|
||||||
<button
|
<a href="" class="fir-pass">{{
|
||||||
class="link btn-login-forgot"
|
'login.labels.resetPassword' | ucapI18n
|
||||||
(click)="onClickForgotPassword('en')"
|
}}</a>
|
||||||
>
|
</li>
|
||||||
Forgot Password? EN
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="login-button-area">
|
||||||
|
<button type="button">
|
||||||
|
{{ 'login.labels.notesOnUse' | ucapI18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="policy bg-primary-light">
|
|
||||||
<a class="link">개인정보 처리방침</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
</ucap-authentication-login-local>
|
||||||
</div>
|
</div>
|
||||||
</ucap-authentication-login>
|
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
@import '../../../../assets/scss/components';
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
@include font-family($font-light);
|
||||||
|
font-size: 24px;
|
||||||
|
text-align: center;
|
||||||
|
color: $txt-color01;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.2;
|
||||||
|
@include screen(mid) {
|
||||||
|
font-size: 19px;
|
||||||
|
}
|
||||||
|
@include screen(xs) {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-section-container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-chk-area {
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: left;
|
||||||
|
@include screen(xs) {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.login-pass-info {
|
||||||
|
overflow: hidden;
|
||||||
|
margin-top: 83px;
|
||||||
|
ul {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
li {
|
||||||
|
height: 24px;
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 12% 0 8%;
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
height: 11px;
|
||||||
|
width: 1px;
|
||||||
|
display: flex;
|
||||||
|
background-color: $gray-re4a;
|
||||||
|
position: absolute;
|
||||||
|
top: 6.5px;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
&:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
&::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
line-height: 24px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: $gray-re4a;
|
||||||
|
padding-left: 34px;
|
||||||
|
position: relative;
|
||||||
|
white-space: nowrap;
|
||||||
|
&::before {
|
||||||
|
font-family: 'material Icons';
|
||||||
|
font-size: 18px;
|
||||||
|
text-align: center;
|
||||||
|
content: 'search';
|
||||||
|
color: $white;
|
||||||
|
display: block;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: $black;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
&.fir-pass {
|
||||||
|
&::before {
|
||||||
|
content: 'sync';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button-area {
|
||||||
|
margin-top: 14px;
|
||||||
|
@include screen(xs) {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 46px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #e0e3e7;
|
||||||
|
font-size: 12px;
|
||||||
|
color: $gray-re4a;
|
||||||
|
cursor: pointer;
|
||||||
|
@include screen(mid) {
|
||||||
|
height: 38px;
|
||||||
|
}
|
||||||
|
@include screen(xs) {
|
||||||
|
height: 34px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import { Subscription } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { take, filter } from 'rxjs/operators';
|
import { take, takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
import { Component, OnInit, OnDestroy, Input, ViewChild } from '@angular/core';
|
import { Component, OnInit, OnDestroy, Input, ViewChild } from '@angular/core';
|
||||||
|
|
||||||
|
@ -56,8 +56,7 @@ export class LoginSectionComponent implements OnInit, OnDestroy {
|
||||||
loginProcessing = false;
|
loginProcessing = false;
|
||||||
loginTry: LoginTry;
|
loginTry: LoginTry;
|
||||||
|
|
||||||
private companyListSubscription: Subscription;
|
private ngOnDestroySubject = new Subject<boolean>();
|
||||||
private loginTrySubscription: Subscription;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private piService: PiService,
|
private piService: PiService,
|
||||||
|
@ -70,9 +69,17 @@ export class LoginSectionComponent implements OnInit, OnDestroy {
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.loginSession = this.appAuthenticationService.getLoginSession();
|
this.ngOnDestroySubject = new Subject<boolean>();
|
||||||
|
|
||||||
this.loginTry = this.sessionStorageService.get<LoginTry>(AppKey.LoginTry);
|
this.appAuthenticationService
|
||||||
|
.getLoginSession$()
|
||||||
|
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||||
|
.subscribe((loginSession) => (this.loginSession = loginSession));
|
||||||
|
|
||||||
|
this.sessionStorageService
|
||||||
|
.get$<LoginTry>(AppKey.LoginTry)
|
||||||
|
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||||
|
.subscribe((loginTry) => (this.loginTry = loginTry));
|
||||||
|
|
||||||
this.protocolService.disconnect();
|
this.protocolService.disconnect();
|
||||||
|
|
||||||
|
@ -82,25 +89,19 @@ export class LoginSectionComponent implements OnInit, OnDestroy {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.companyListSubscription = this.store
|
this.store
|
||||||
.pipe(select(CompanySelector.companyList))
|
.pipe(
|
||||||
|
takeUntil(this.ngOnDestroySubject),
|
||||||
|
select(CompanySelector.companyList)
|
||||||
|
)
|
||||||
.subscribe((companyList) => {
|
.subscribe((companyList) => {
|
||||||
this.companyList = companyList;
|
this.companyList = companyList;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.loginTrySubscription = this.sessionStorageService.changed$
|
|
||||||
.pipe(filter((param) => AppKey.LoginTry === param.key))
|
|
||||||
.subscribe((param) => {
|
|
||||||
this.loginTry = param.value as LoginTry;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
if (!!this.companyListSubscription) {
|
if (!!this.ngOnDestroySubject) {
|
||||||
this.companyListSubscription.unsubscribe();
|
this.ngOnDestroySubject.complete();
|
||||||
}
|
|
||||||
if (!!this.loginTrySubscription) {
|
|
||||||
this.loginTrySubscription.unsubscribe();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
71
src/app/sections/chat/chat.section.module.ts
Normal file
71
src/app/sections/chat/chat.section.module.ts
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
|
|
||||||
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
|
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
import { MatBadgeModule } from '@angular/material/badge';
|
||||||
|
|
||||||
|
import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
|
||||||
|
|
||||||
|
import { UiModule } from '@ucap/ng-ui';
|
||||||
|
import { OrganizationUiModule } from '@ucap/ng-ui-organization';
|
||||||
|
// import { GroupUiModule } from '@ucap/ng-ui-group';
|
||||||
|
|
||||||
|
import { COMPONENTS, DIRECTIVES } from './components';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatRippleModule } from '@angular/material/core';
|
||||||
|
import { MatTreeModule } from '@angular/material/tree';
|
||||||
|
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
||||||
|
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FlexLayoutModule,
|
||||||
|
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatAutocompleteModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatSelectModule,
|
||||||
|
MatBadgeModule,
|
||||||
|
|
||||||
|
MatButtonModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatRippleModule,
|
||||||
|
MatTreeModule,
|
||||||
|
|
||||||
|
PerfectScrollbarModule,
|
||||||
|
ScrollingModule,
|
||||||
|
|
||||||
|
I18nModule,
|
||||||
|
UiModule,
|
||||||
|
OrganizationUiModule
|
||||||
|
// GroupUiModule
|
||||||
|
],
|
||||||
|
exports: [...COMPONENTS, ...DIRECTIVES],
|
||||||
|
declarations: [...COMPONENTS, ...DIRECTIVES],
|
||||||
|
entryComponents: [],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: UCAP_I18N_NAMESPACE,
|
||||||
|
useValue: ['chat']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class AppChatSectionModule {}
|
|
@ -0,0 +1,21 @@
|
||||||
|
<div class="chat-list-item">
|
||||||
|
<div class="profileImage">{{ defaultProfileImage }}</div>
|
||||||
|
<div class="roomName">
|
||||||
|
{{ roomName }}
|
||||||
|
<strong *ngIf="roomInfo.roomType === RoomType.Multi"
|
||||||
|
>({{ roomInfo.joinUserCount }})</strong
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="lastMessage">{{ roomInfo.finalEventMessage }}</div>
|
||||||
|
<div class="date">{{ roomInfo.finalEventDate | ucapDate: 'LT' }}</div>
|
||||||
|
|
||||||
|
<span
|
||||||
|
class="noti-sum"
|
||||||
|
*ngIf="!!roomInfo.noReadCnt && roomInfo.noReadCnt > 0"
|
||||||
|
[matBadgeHidden]="roomInfo.noReadCnt === 0"
|
||||||
|
[matBadge]="roomInfo.noReadCnt"
|
||||||
|
matBadgeOverlap="true"
|
||||||
|
matBadgeColor="accent"
|
||||||
|
matBadgePosition="below after"
|
||||||
|
></span>
|
||||||
|
</div>
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ChatListItemComponent } from './chat-list-item.component';
|
||||||
|
|
||||||
|
describe('ChatListItemComponent', () => {
|
||||||
|
let component: ChatListItemComponent;
|
||||||
|
let fixture: ComponentFixture<ChatListItemComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ ChatListItemComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ChatListItemComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,41 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Input,
|
||||||
|
OnDestroy
|
||||||
|
} from '@angular/core';
|
||||||
|
import { RoomInfo, RoomType } from '@ucap/protocol-room';
|
||||||
|
import {
|
||||||
|
RoomUserMap,
|
||||||
|
RoomUserShortMap
|
||||||
|
} from '@ucap/ng-store-chat/lib/store/room/state';
|
||||||
|
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-chat-list-item',
|
||||||
|
templateUrl: './chat-list-item.component.html',
|
||||||
|
styleUrls: ['./chat-list-item.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class ChatListItemComponent implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
roomInfo: RoomInfo;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
defaultProfileImage: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
profileImage: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
roomName: string;
|
||||||
|
|
||||||
|
RoomType = RoomType;
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
ngOnInit(): void {}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
<div class="ucap-group-expansion-container" fxFlexFill>
|
||||||
|
<cdk-virtual-scroll-viewport #cvsvList perfectScrollbar fxFlexFill>
|
||||||
|
<ng-container
|
||||||
|
*cdkVirtualFor="
|
||||||
|
let node of dataSource.expandedData$;
|
||||||
|
templateCacheSize: 0
|
||||||
|
"
|
||||||
|
></ng-container>
|
||||||
|
|
||||||
|
<mat-tree #treeList [dataSource]="dataSource" [treeControl]="treeControl">
|
||||||
|
<mat-tree-node
|
||||||
|
*matTreeNodeDef="let node"
|
||||||
|
[attr.node-type]="node?.nodeType"
|
||||||
|
matRipple
|
||||||
|
>
|
||||||
|
<li>
|
||||||
|
<div>
|
||||||
|
<ng-container
|
||||||
|
[ngTemplateOutlet]="nodeTemplate"
|
||||||
|
[ngTemplateOutletContext]="{ $implicit: node?.node }"
|
||||||
|
></ng-container>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</mat-tree-node>
|
||||||
|
|
||||||
|
<mat-tree-node
|
||||||
|
*matTreeNodeDef="let node; when: isHeader"
|
||||||
|
class="tree-node-frame ucap-clickable"
|
||||||
|
[attr.node-type]="node?.nodeType"
|
||||||
|
matRipple
|
||||||
|
>
|
||||||
|
<li class="tree-node-header" matTreeNodeToggle>
|
||||||
|
<div class="path">
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
[attr.aria-label]="'toggle '"
|
||||||
|
class="btn-toggle"
|
||||||
|
*ngIf="!checkable"
|
||||||
|
>
|
||||||
|
<mat-icon class="mat-icon-rtl-mirror">
|
||||||
|
{{
|
||||||
|
treeControl.isExpanded(node) ? 'expand_less' : 'expand_more'
|
||||||
|
}}
|
||||||
|
</mat-icon>
|
||||||
|
</button>
|
||||||
|
<div class="group-info">
|
||||||
|
<ng-container
|
||||||
|
[ngTemplateOutlet]="headerTemplate"
|
||||||
|
[ngTemplateOutletContext]="{ $implicit: node?.node }"
|
||||||
|
>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-checkbox
|
||||||
|
*ngIf="checkable"
|
||||||
|
#checkbox
|
||||||
|
[checked]="isCheckedGroup(node)"
|
||||||
|
[disabled]="!isCheckableGroup(node)"
|
||||||
|
(change)="onChangeCheckGroup(checkbox.checked, node)"
|
||||||
|
(click)="$event.stopPropagation()"
|
||||||
|
class="group-check"
|
||||||
|
>
|
||||||
|
</mat-checkbox>
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
aria-label="group-header-menu"
|
||||||
|
*ngIf="!checkable"
|
||||||
|
(click)="
|
||||||
|
$event.stopPropagation(); onClickHeaderMenu($event, node)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<mat-icon>more_vert</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<ul [class.group-tree-node-invisible]="!treeControl.isExpanded(node)">
|
||||||
|
<div *ngIf="treeControl.isExpanded(node)" class="boxnone">
|
||||||
|
<div class="vertical-line"></div>
|
||||||
|
<ng-container matTreeNodeOutlet></ng-container>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</mat-tree-node>
|
||||||
|
</mat-tree>
|
||||||
|
</cdk-virtual-scroll-viewport>
|
||||||
|
</div>
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { ExpansionComponent } from './expansion.component';
|
||||||
|
|
||||||
|
describe('ucap::ui-group::ExpansionComponent', () => {
|
||||||
|
let component: ExpansionComponent;
|
||||||
|
let fixture: ComponentFixture<ExpansionComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ExpansionComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ExpansionComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,248 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
EventEmitter,
|
||||||
|
ViewChild,
|
||||||
|
ContentChild,
|
||||||
|
TemplateRef,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Directive
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||||
|
import { FlatTreeControl } from '@angular/cdk/tree';
|
||||||
|
|
||||||
|
import { MatTreeFlattener, MatTree } from '@angular/material/tree';
|
||||||
|
|
||||||
|
import { UserInfo, GroupDetailData } from '@ucap/protocol-sync';
|
||||||
|
|
||||||
|
import { VirtualScrollTreeFlatDataSource } from '@ucap/ng-ui';
|
||||||
|
import { UserInfoSS, UserInfoF, UserInfoDN } from '@ucap/protocol-query';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
import { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
|
||||||
|
import { RoomInfo } from '@ucap/protocol-room';
|
||||||
|
|
||||||
|
export interface ChatGroupNode {
|
||||||
|
nodeType: string;
|
||||||
|
roomInfo?: RoomInfo;
|
||||||
|
children?: ChatGroupNode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FlatNode {
|
||||||
|
expandable: boolean;
|
||||||
|
level: number;
|
||||||
|
node: ChatGroupNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[ucapChatExpansionNode]'
|
||||||
|
})
|
||||||
|
export class ExpansionNodeDirective {}
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[ucapChatExpansionHeader]'
|
||||||
|
})
|
||||||
|
export class ExpansionHeaderDirective {}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-chat-expansion',
|
||||||
|
templateUrl: './expansion.component.html',
|
||||||
|
styleUrls: ['./expansion.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class ExpansionComponent implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
set chatGroup(list: { division: string; roomList: RoomInfo[] }[]) {
|
||||||
|
if (!list || 0 === list.length) {
|
||||||
|
} else {
|
||||||
|
list.sort((a, b) =>
|
||||||
|
a.division < b.division ? 1 : a.division > b.division ? -1 : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const item of list) {
|
||||||
|
const nodeType = item.division;
|
||||||
|
const node: ChatGroupNode = {
|
||||||
|
nodeType,
|
||||||
|
children: []
|
||||||
|
};
|
||||||
|
|
||||||
|
item.roomList.sort((a, b) =>
|
||||||
|
a.finalEventDate < b.finalEventDate
|
||||||
|
? 1
|
||||||
|
: a.finalEventDate > b.finalEventDate
|
||||||
|
? -1
|
||||||
|
: 0
|
||||||
|
);
|
||||||
|
|
||||||
|
item.roomList.forEach((roomInfo) => {
|
||||||
|
node.children.push({
|
||||||
|
nodeType,
|
||||||
|
roomInfo
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!!this.nodeMap.get(item.division)) {
|
||||||
|
this.nodeMap[item.division].push(node);
|
||||||
|
} else {
|
||||||
|
this.nodeMap.set(item.division, [node]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.refreshNodes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
checkable = false;
|
||||||
|
|
||||||
|
// @Input()
|
||||||
|
// selectedUserList?: (UserInfo | UserInfoSS | UserInfoF | UserInfoDN)[] = [];
|
||||||
|
|
||||||
|
// @Input()
|
||||||
|
// unselectableUserList?: (
|
||||||
|
// | UserInfo
|
||||||
|
// | UserInfoSS
|
||||||
|
// | UserInfoF
|
||||||
|
// | UserInfoDN
|
||||||
|
// )[] = [];
|
||||||
|
|
||||||
|
@ViewChild('treeList', { static: false })
|
||||||
|
treeList: MatTree<FlatNode>;
|
||||||
|
|
||||||
|
@ViewChild('cvsvList', { static: false })
|
||||||
|
cvsvList: CdkVirtualScrollViewport;
|
||||||
|
|
||||||
|
@ViewChild(PerfectScrollbarDirective, { static: false })
|
||||||
|
psDirectiveRef?: PerfectScrollbarDirective;
|
||||||
|
|
||||||
|
@ContentChild(ExpansionNodeDirective, {
|
||||||
|
read: TemplateRef,
|
||||||
|
static: false
|
||||||
|
})
|
||||||
|
nodeTemplate: TemplateRef<ExpansionNodeDirective>;
|
||||||
|
|
||||||
|
@ContentChild(ExpansionHeaderDirective, {
|
||||||
|
read: TemplateRef,
|
||||||
|
static: false
|
||||||
|
})
|
||||||
|
headerTemplate: TemplateRef<ExpansionHeaderDirective>;
|
||||||
|
|
||||||
|
treeControl: FlatTreeControl<FlatNode>;
|
||||||
|
treeFlattener: MatTreeFlattener<ChatGroupNode, FlatNode>;
|
||||||
|
dataSource: VirtualScrollTreeFlatDataSource<ChatGroupNode, FlatNode>;
|
||||||
|
|
||||||
|
private nodeMap: Map<string, ChatGroupNode[]> = new Map();
|
||||||
|
// tslint:disable-next-line: variable-name
|
||||||
|
private _ngOnDestroySubject: Subject<void>;
|
||||||
|
|
||||||
|
constructor(private changeDetectorRef: ChangeDetectorRef) {
|
||||||
|
this.treeControl = new FlatTreeControl<FlatNode>(
|
||||||
|
(node) => node.level,
|
||||||
|
(node) => node.expandable
|
||||||
|
);
|
||||||
|
|
||||||
|
this.treeFlattener = new MatTreeFlattener<ChatGroupNode, FlatNode>(
|
||||||
|
(node: ChatGroupNode, level: number) => {
|
||||||
|
return {
|
||||||
|
expandable: !!node.children && node.children.length > 0,
|
||||||
|
level,
|
||||||
|
nodeType: node.nodeType,
|
||||||
|
node
|
||||||
|
};
|
||||||
|
},
|
||||||
|
(node) => node.level,
|
||||||
|
(node) => node.expandable,
|
||||||
|
(node) => node.children
|
||||||
|
);
|
||||||
|
|
||||||
|
this.dataSource = new VirtualScrollTreeFlatDataSource<
|
||||||
|
ChatGroupNode,
|
||||||
|
FlatNode
|
||||||
|
>(this.treeControl, this.treeFlattener);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this._ngOnDestroySubject = new Subject();
|
||||||
|
|
||||||
|
this.dataSource.cdkVirtualScrollViewport = this.cvsvList;
|
||||||
|
this.treeControl.expansionModel.changed
|
||||||
|
.pipe(takeUntil(this._ngOnDestroySubject))
|
||||||
|
.subscribe(() => {
|
||||||
|
this.cvsvList.checkViewportSize();
|
||||||
|
this.psDirectiveRef.update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (!!this._ngOnDestroySubject) {
|
||||||
|
this._ngOnDestroySubject.next();
|
||||||
|
this._ngOnDestroySubject.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickHeaderMenu(event: MouseEvent, node: FlatNode) {}
|
||||||
|
|
||||||
|
isCheckedGroup(node: FlatNode): boolean {
|
||||||
|
// const groupDetail = node.node.groupDetail;
|
||||||
|
|
||||||
|
// if (!groupDetail || groupDetail === undefined) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (groupDetail.userSeqs.length === 0) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!!this.selectedUserList && this.selectedUserList.length > 0) {
|
||||||
|
// let allExist = true;
|
||||||
|
// groupDetail.userSeqs.some((seq) => {
|
||||||
|
// if (
|
||||||
|
// this.selectedUserList.filter((item) => item.seq === seq).length === 0
|
||||||
|
// ) {
|
||||||
|
// allExist = false;
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// return allExist;
|
||||||
|
// }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isCheckableGroup(node: FlatNode): boolean {
|
||||||
|
// if (!!this.unselectableUserList && this.unselectableUserList.length > 0) {
|
||||||
|
// const groupDetail = node.node.groupDetail;
|
||||||
|
// let allExist = true;
|
||||||
|
// groupDetail.userSeqs.some((seq) => {
|
||||||
|
// if (
|
||||||
|
// this.unselectableUserList.filter((item) => item.seq === seq)
|
||||||
|
// .length === 0
|
||||||
|
// ) {
|
||||||
|
// allExist = false;
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// if (allExist) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeCheckGroup(value: boolean, node: FlatNode) {}
|
||||||
|
|
||||||
|
isHeader = (_: number, node: FlatNode) => 0 === node.level;
|
||||||
|
|
||||||
|
private refreshNodes() {
|
||||||
|
const rootNode: ChatGroupNode[] = [];
|
||||||
|
this.nodeMap.forEach((node) => rootNode.push(...node));
|
||||||
|
|
||||||
|
this.dataSource.data = rootNode;
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
}
|
||||||
|
}
|
10
src/app/sections/chat/components/component-ui/index.ts
Normal file
10
src/app/sections/chat/components/component-ui/index.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { ChatListItemComponent } from './chat-list-item.component';
|
||||||
|
import {
|
||||||
|
ExpansionComponent,
|
||||||
|
ExpansionNodeDirective,
|
||||||
|
ExpansionHeaderDirective
|
||||||
|
} from './expansion.component';
|
||||||
|
|
||||||
|
export const COMPONENTS = [ChatListItemComponent, ExpansionComponent];
|
||||||
|
|
||||||
|
export const DIRECTIVES = [ExpansionNodeDirective, ExpansionHeaderDirective];
|
15
src/app/sections/chat/components/index.ts
Normal file
15
src/app/sections/chat/components/index.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { SearchSectionComponent } from './search.section.component';
|
||||||
|
import { ListSectionComponent } from './list.section.component';
|
||||||
|
|
||||||
|
import {
|
||||||
|
COMPONENTS as COMPONENTS_UI,
|
||||||
|
DIRECTIVES as DIRECTIVES_UI
|
||||||
|
} from './component-ui';
|
||||||
|
|
||||||
|
export const COMPONENTS = [
|
||||||
|
...COMPONENTS_UI,
|
||||||
|
SearchSectionComponent,
|
||||||
|
ListSectionComponent
|
||||||
|
];
|
||||||
|
|
||||||
|
export const DIRECTIVES = [...DIRECTIVES_UI];
|
42
src/app/sections/chat/components/list.section.component.html
Normal file
42
src/app/sections/chat/components/list.section.component.html
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<div
|
||||||
|
*ngIf="!!searchObj && !searchObj.isShowSearch"
|
||||||
|
fxFlexFill
|
||||||
|
class="list-container"
|
||||||
|
>
|
||||||
|
<!-- <app-chat-list-item
|
||||||
|
*ngFor="let roomInfo of roomList"
|
||||||
|
[roomInfo]="roomInfo"
|
||||||
|
[roomName]="getRoomName(roomInfo)"
|
||||||
|
defaultProfileImage="assets/images/img_nophoto_50.png"
|
||||||
|
profileImage=""
|
||||||
|
></app-chat-list-item> -->
|
||||||
|
|
||||||
|
<ucap-chat-expansion [chatGroup]="chatGroup">
|
||||||
|
<ng-template ucapChatExpansionNode let-node>
|
||||||
|
<app-chat-list-item
|
||||||
|
[roomInfo]="node.roomInfo"
|
||||||
|
[roomName]="getRoomName(node.roomInfo)"
|
||||||
|
defaultProfileImage="assets/images/img_nophoto_50.png"
|
||||||
|
profileImage=""
|
||||||
|
></app-chat-list-item>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template ucapChatExpansionHeader let-node>
|
||||||
|
<span class="header-buddy">
|
||||||
|
<span>{{ node.nodeType }} {{ node.nodeType | ucapDate: 'dddd' }}</span>
|
||||||
|
<span *ngIf="isToday(node.nodeType)">
|
||||||
|
({{ 'room.today' | ucapI18n }})
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</ng-template>
|
||||||
|
</ucap-chat-expansion>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="!!searchObj && searchObj.isShowSearch">
|
||||||
|
<app-chat-list-item
|
||||||
|
*ngFor="let roomInfo of searchRoomList"
|
||||||
|
[roomInfo]="roomInfo"
|
||||||
|
[roomName]="getRoomName(roomInfo)"
|
||||||
|
defaultProfileImage="assets/images/img_nophoto_50.png"
|
||||||
|
profileImage=""
|
||||||
|
></app-chat-list-item>
|
||||||
|
</div>
|
|
@ -0,0 +1,2 @@
|
||||||
|
.list-container {
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { TestBed, async } from '@angular/core/testing';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { ListSectionComponent } from './list.section.component';
|
||||||
|
|
||||||
|
describe('app::sections::group::ListSectionComponent', () => {
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [RouterTestingModule],
|
||||||
|
declarations: [ListSectionComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create the app', () => {
|
||||||
|
const fixture = TestBed.createComponent(ListSectionComponent);
|
||||||
|
const app = fixture.componentInstance;
|
||||||
|
expect(app).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should have as title 'ucap-lg-web'`, () => {
|
||||||
|
const fixture = TestBed.createComponent(ListSectionComponent);
|
||||||
|
const app = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render title', () => {
|
||||||
|
const fixture = TestBed.createComponent(ListSectionComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement;
|
||||||
|
expect(compiled.querySelector('.content span').textContent).toContain(
|
||||||
|
'ucap-lg-web app is running!'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
300
src/app/sections/chat/components/list.section.component.ts
Normal file
300
src/app/sections/chat/components/list.section.component.ts
Normal file
|
@ -0,0 +1,300 @@
|
||||||
|
import { Observable, Subject, combineLatest, of } from 'rxjs';
|
||||||
|
import { filter, takeUntil, take, map, catchError } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Input,
|
||||||
|
ViewChild
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { Store, select, State } from '@ngrx/store';
|
||||||
|
|
||||||
|
import {
|
||||||
|
VirtualScrollStrategy,
|
||||||
|
FixedSizeVirtualScrollStrategy,
|
||||||
|
VIRTUAL_SCROLL_STRATEGY,
|
||||||
|
CdkVirtualScrollViewport
|
||||||
|
} from '@angular/cdk/scrolling';
|
||||||
|
|
||||||
|
import { VersionInfo2Response } from '@ucap/api-public';
|
||||||
|
import { Company } from '@ucap/api-external';
|
||||||
|
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||||
|
import { UserInfo, GroupDetailData } from '@ucap/protocol-sync';
|
||||||
|
|
||||||
|
import { LogService } from '@ucap/ng-logger';
|
||||||
|
import { NodeType } from '@ucap/ng-ui-group';
|
||||||
|
import { SessionStorageService } from '@ucap/ng-web-storage';
|
||||||
|
import {
|
||||||
|
LoginSelector,
|
||||||
|
ConfigurationSelector
|
||||||
|
} from '@ucap/ng-store-authentication';
|
||||||
|
import { CompanySelector } from '@ucap/ng-store-organization';
|
||||||
|
import { BuddySelector, GroupSelector } from '@ucap/ng-store-group';
|
||||||
|
|
||||||
|
import { AppAuthenticationService } from '@app/services/app-authentication.service';
|
||||||
|
import { AppKey } from '@app/types/app-key.type';
|
||||||
|
import { LoginSession } from '@app/models/login-session';
|
||||||
|
import { QueryProtocolService } from '@ucap/ng-protocol-query';
|
||||||
|
import {
|
||||||
|
UserInfoSS,
|
||||||
|
DeptSearchType,
|
||||||
|
SSVC_TYPE_QUERY_DEPT_USER_DATA,
|
||||||
|
DeptUserData,
|
||||||
|
SSVC_TYPE_QUERY_DEPT_USER_RES
|
||||||
|
} from '@ucap/protocol-query';
|
||||||
|
import { RoomSelector } from '@ucap/ng-store-chat';
|
||||||
|
import {
|
||||||
|
RoomInfo,
|
||||||
|
UserInfo as RoomUserInfo,
|
||||||
|
UserInfoShort as RoomUserInfoShort,
|
||||||
|
RoomType
|
||||||
|
} from '@ucap/protocol-room';
|
||||||
|
import {
|
||||||
|
RoomUserMap,
|
||||||
|
RoomUserShortMap
|
||||||
|
} from '@ucap/ng-store-chat/lib/store/room/state';
|
||||||
|
import { Dictionary } from '@ngrx/entity';
|
||||||
|
import {
|
||||||
|
TranslatePipe as OrganizationTranslate,
|
||||||
|
TranslateService
|
||||||
|
} from '@ucap/ng-ui-organization';
|
||||||
|
import { I18nService } from '@ucap/ng-i18n';
|
||||||
|
import { AppChatService } from '@app/services/app-chat.service';
|
||||||
|
import { DateService } from '@ucap/ng-ui';
|
||||||
|
|
||||||
|
import moment from 'moment';
|
||||||
|
import 'moment-timezone';
|
||||||
|
|
||||||
|
export class ChatVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy {
|
||||||
|
constructor() {
|
||||||
|
super(60, 150, 200); // (itemSize, minBufferPx, maxBufferPx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-sections-chat-list',
|
||||||
|
templateUrl: './list.section.component.html',
|
||||||
|
styleUrls: ['./list.section.component.scss'],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: VIRTUAL_SCROLL_STRATEGY,
|
||||||
|
useClass: ChatVirtualScrollStrategy
|
||||||
|
}
|
||||||
|
],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class ListSectionComponent implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
set searchObj(obj: { isShowSearch: boolean; searchWord: string }) {
|
||||||
|
this._searchObj = obj;
|
||||||
|
console.log(this._searchObj);
|
||||||
|
|
||||||
|
if (obj.isShowSearch && obj.searchWord.localeCompare('') !== 0) {
|
||||||
|
this.onRoomSearch(obj);
|
||||||
|
} else {
|
||||||
|
this._searchObj.isShowSearch = false;
|
||||||
|
this.searchRoomList = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get searchObj() {
|
||||||
|
return this._searchObj;
|
||||||
|
}
|
||||||
|
// tslint:disable-next-line: variable-name
|
||||||
|
_searchObj: any;
|
||||||
|
|
||||||
|
loginRes: LoginResponse;
|
||||||
|
|
||||||
|
roomList: RoomInfo[];
|
||||||
|
roomUsersDictionary: Dictionary<RoomUserMap>;
|
||||||
|
roomUsersShortDictionary: Dictionary<RoomUserShortMap>;
|
||||||
|
|
||||||
|
searchRoomList: RoomInfo[];
|
||||||
|
|
||||||
|
chatGroup: { division: string; roomList: RoomInfo[] }[];
|
||||||
|
|
||||||
|
organizationTranslate: OrganizationTranslate;
|
||||||
|
|
||||||
|
private ngOnDestroySubject = new Subject<boolean>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private appChatService: AppChatService,
|
||||||
|
private dateService: DateService,
|
||||||
|
private sessionStorageService: SessionStorageService,
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private translateService: TranslateService,
|
||||||
|
private store: Store<any>,
|
||||||
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
|
private logService: LogService
|
||||||
|
) {
|
||||||
|
this.translateService.setDefaultLang(this.i18nService.currentLng);
|
||||||
|
this.translateService.use(this.i18nService.currentLng);
|
||||||
|
this.organizationTranslate = new OrganizationTranslate(
|
||||||
|
this.translateService,
|
||||||
|
this.changeDetectorRef
|
||||||
|
);
|
||||||
|
|
||||||
|
this.i18nService.setDefaultNamespace('chat');
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.ngOnDestroySubject = new Subject<boolean>();
|
||||||
|
|
||||||
|
this.store
|
||||||
|
.pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
|
||||||
|
.subscribe((loginRes) => {
|
||||||
|
this.loginRes = loginRes;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.store
|
||||||
|
.pipe(takeUntil(this.ngOnDestroySubject), select(RoomSelector.rooms))
|
||||||
|
.subscribe((rooms) => {
|
||||||
|
rooms = (rooms || []).filter((info) => info.isJoinRoom);
|
||||||
|
this.roomList = rooms;
|
||||||
|
|
||||||
|
// groupping.
|
||||||
|
this.initGroup();
|
||||||
|
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.store
|
||||||
|
.pipe(
|
||||||
|
takeUntil(this.ngOnDestroySubject),
|
||||||
|
select(
|
||||||
|
(state: any) =>
|
||||||
|
state.chat.room.roomUsers.entities as Dictionary<RoomUserMap>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.subscribe((roomUsers) => {
|
||||||
|
this.roomUsersDictionary = roomUsers;
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.store
|
||||||
|
.pipe(
|
||||||
|
takeUntil(this.ngOnDestroySubject),
|
||||||
|
select(
|
||||||
|
(state: any) =>
|
||||||
|
state.chat.room.roomUsersShort.entities as Dictionary<
|
||||||
|
RoomUserShortMap
|
||||||
|
>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.subscribe((roomUsersShort) => {
|
||||||
|
this.roomUsersShortDictionary = roomUsersShort;
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (!!this.ngOnDestroySubject) {
|
||||||
|
this.ngOnDestroySubject.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initGroup() {
|
||||||
|
this.chatGroup = [];
|
||||||
|
|
||||||
|
this.roomList.forEach((roomInfo) => {
|
||||||
|
const date = roomInfo.finalEventDate;
|
||||||
|
let division = '';
|
||||||
|
try {
|
||||||
|
const value = this.dateService.get(date, 'LL');
|
||||||
|
|
||||||
|
if (value === 'Invalid date') {
|
||||||
|
division = date;
|
||||||
|
} else {
|
||||||
|
division = value;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
division = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = this.chatGroup.findIndex(
|
||||||
|
(info) => info.division === division
|
||||||
|
);
|
||||||
|
if (index > -1) {
|
||||||
|
this.chatGroup[index] = {
|
||||||
|
...this.chatGroup[index],
|
||||||
|
roomList: [...this.chatGroup[index].roomList, roomInfo]
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this.chatGroup.push({
|
||||||
|
division,
|
||||||
|
roomList: [roomInfo]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getRoomName(roomInfo: RoomInfo): string {
|
||||||
|
if (!roomInfo) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const roomName = this.appChatService.getRoomName(
|
||||||
|
this.organizationTranslate,
|
||||||
|
this.loginRes,
|
||||||
|
roomInfo,
|
||||||
|
this.roomUsersDictionary,
|
||||||
|
this.roomUsersShortDictionary
|
||||||
|
);
|
||||||
|
|
||||||
|
return roomName;
|
||||||
|
}
|
||||||
|
|
||||||
|
isToday(date: any) {
|
||||||
|
return this.dateService.isToday(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
onRoomSearch(obj: { isShowSearch: boolean; searchWord: string }) {
|
||||||
|
const searchRoomList: RoomInfo[] = [];
|
||||||
|
|
||||||
|
this.roomList.forEach((roomInfo) => {
|
||||||
|
if (roomInfo.roomName.indexOf(obj.searchWord) > -1) {
|
||||||
|
searchRoomList.push(roomInfo);
|
||||||
|
} else {
|
||||||
|
const roomId = roomInfo.roomId;
|
||||||
|
const roomUsers = !!this.roomUsersDictionary
|
||||||
|
? this.roomUsersDictionary[roomId]
|
||||||
|
: undefined;
|
||||||
|
const roomUsersShort = !!this.roomUsersShortDictionary
|
||||||
|
? this.roomUsersShortDictionary[roomId]
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
let users = [];
|
||||||
|
let existUsers = false;
|
||||||
|
if (!!roomUsers && roomUsers.userInfos.length > 0) {
|
||||||
|
existUsers = true;
|
||||||
|
users = roomUsers.userInfos.filter(
|
||||||
|
(userInfo) => userInfo.seq !== Number(this.loginRes.userSeq)
|
||||||
|
);
|
||||||
|
} else if (!!roomUsersShort && roomUsersShort.userInfos.length > 0) {
|
||||||
|
existUsers = true;
|
||||||
|
users = roomUsersShort.userInfos.filter(
|
||||||
|
(userInfo) => userInfo.seq !== Number(this.loginRes.userSeq)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
existUsers &&
|
||||||
|
users.filter(
|
||||||
|
(userInfo) =>
|
||||||
|
userInfo.name.indexOf(obj.searchWord) > -1 ||
|
||||||
|
userInfo.nameEn.indexOf(obj.searchWord) > -1 ||
|
||||||
|
userInfo.nameCn.indexOf(obj.searchWord) > -1
|
||||||
|
).length > 0
|
||||||
|
) {
|
||||||
|
searchRoomList.push(roomInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.searchRoomList = searchRoomList;
|
||||||
|
}
|
||||||
|
}
|
31
src/app/sections/chat/components/list.section.strategy.ts
Normal file
31
src/app/sections/chat/components/list.section.strategy.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { Observable, Subject } from 'rxjs';
|
||||||
|
|
||||||
|
import {
|
||||||
|
VirtualScrollStrategy,
|
||||||
|
CdkVirtualScrollViewport
|
||||||
|
} from '@angular/cdk/scrolling';
|
||||||
|
import { distinctUntilChanged } from 'rxjs/operators';
|
||||||
|
|
||||||
|
export class ChatGroupVirtualScrollStrategy implements VirtualScrollStrategy {
|
||||||
|
scrolledIndexChange: Observable<number>;
|
||||||
|
|
||||||
|
private indexSubject = new Subject<number>();
|
||||||
|
private viewport: CdkVirtualScrollViewport | null = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.scrolledIndexChange = this.indexSubject.pipe(distinctUntilChanged());
|
||||||
|
}
|
||||||
|
|
||||||
|
attach(viewport: CdkVirtualScrollViewport): void {
|
||||||
|
this.viewport = viewport;
|
||||||
|
}
|
||||||
|
detach(): void {
|
||||||
|
this.indexSubject.complete();
|
||||||
|
this.viewport = null;
|
||||||
|
}
|
||||||
|
onContentScrolled(): void {}
|
||||||
|
onDataLengthChanged(): void {}
|
||||||
|
onContentRendered(): void {}
|
||||||
|
onRenderedOffsetChanged(): void {}
|
||||||
|
scrollToIndex(index: number, behavior: ScrollBehavior): void {}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
<div class="search-container">
|
||||||
|
<div class="searchbox">
|
||||||
|
<form [formGroup]="fgSearch">
|
||||||
|
<mat-form-field floatLabel="never">
|
||||||
|
<mat-label>{{ 'room.searchRoomByName' | ucapI18n }}</mat-label>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
#searchWordInput
|
||||||
|
type="text"
|
||||||
|
maxlength="20"
|
||||||
|
placeholder="{{ 'room.searchRoomByName' | ucapI18n }}"
|
||||||
|
formControlName="searchInput"
|
||||||
|
[matAutocomplete]="auto"
|
||||||
|
(keydown.enter)="onKeyDownEnter($event, searchWordInput.value)"
|
||||||
|
/>
|
||||||
|
<mat-autocomplete #auto="matAutocomplete">
|
||||||
|
<mat-option
|
||||||
|
*ngFor="let filteredRecommendedWord of filteredRecommendedWordList"
|
||||||
|
[value]="filteredRecommendedWord"
|
||||||
|
>
|
||||||
|
{{ filteredRecommendedWord }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-autocomplete>
|
||||||
|
<button
|
||||||
|
mat-button
|
||||||
|
matSuffix
|
||||||
|
mat-icon-button
|
||||||
|
aria-label="Clear"
|
||||||
|
(click)="searchWordInput.value = ''; onClickCancel()"
|
||||||
|
>
|
||||||
|
<mat-icon>close</mat-icon>
|
||||||
|
</button>
|
||||||
|
<mat-icon matSuffix>search</mat-icon>
|
||||||
|
</mat-form-field>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { TestBed, async } from '@angular/core/testing';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { SearchSectionComponent } from './search.section.component';
|
||||||
|
|
||||||
|
describe('app::sections::group::SearchSectionComponent', () => {
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [RouterTestingModule],
|
||||||
|
declarations: [SearchSectionComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create the app', () => {
|
||||||
|
const fixture = TestBed.createComponent(SearchSectionComponent);
|
||||||
|
const app = fixture.componentInstance;
|
||||||
|
expect(app).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should have as title 'ucap-lg-web'`, () => {
|
||||||
|
const fixture = TestBed.createComponent(SearchSectionComponent);
|
||||||
|
const app = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render title', () => {
|
||||||
|
const fixture = TestBed.createComponent(SearchSectionComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement;
|
||||||
|
expect(compiled.querySelector('.content span').textContent).toContain(
|
||||||
|
'ucap-lg-web app is running!'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
147
src/app/sections/chat/components/search.section.component.ts
Normal file
147
src/app/sections/chat/components/search.section.component.ts
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
Output,
|
||||||
|
EventEmitter,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ViewChild
|
||||||
|
} from '@angular/core';
|
||||||
|
import { FormGroup, FormBuilder } from '@angular/forms';
|
||||||
|
|
||||||
|
import { Store, select } from '@ngrx/store';
|
||||||
|
|
||||||
|
import { LogService } from '@ucap/ng-logger';
|
||||||
|
import { takeUntil, debounceTime } from 'rxjs/operators';
|
||||||
|
import { Subject, combineLatest } from 'rxjs';
|
||||||
|
|
||||||
|
import { RoomSelector } from '@ucap/ng-store-chat';
|
||||||
|
import { LoginSelector } from '@ucap/ng-store-authentication';
|
||||||
|
|
||||||
|
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||||
|
import { MatAutocompleteTrigger } from '@angular/material/autocomplete';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-sections-chat-search',
|
||||||
|
templateUrl: './search.section.component.html',
|
||||||
|
styleUrls: ['./search.section.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class SearchSectionComponent implements OnInit, OnDestroy {
|
||||||
|
@Output()
|
||||||
|
keyDownEnter = new EventEmitter<{
|
||||||
|
searchWord: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
searchCancel = new EventEmitter<any>();
|
||||||
|
|
||||||
|
loginRes: LoginResponse;
|
||||||
|
|
||||||
|
fgSearch: FormGroup;
|
||||||
|
recommendedWordList: string[];
|
||||||
|
filteredRecommendedWordList: string[];
|
||||||
|
|
||||||
|
private ngOnDestroySubject = new Subject<boolean>();
|
||||||
|
|
||||||
|
@ViewChild(MatAutocompleteTrigger) autocomplete: MatAutocompleteTrigger;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private store: Store<any>,
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
|
private logService: LogService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.ngOnDestroySubject = new Subject<boolean>();
|
||||||
|
|
||||||
|
this.fgSearch = this.formBuilder.group({
|
||||||
|
searchInput: null
|
||||||
|
});
|
||||||
|
|
||||||
|
this.fgSearch
|
||||||
|
.get('searchInput')
|
||||||
|
.valueChanges.pipe(debounceTime(100))
|
||||||
|
.subscribe((value) => {
|
||||||
|
if (value !== null && value.length > 0) {
|
||||||
|
this.filteredRecommendedWordList = this.recommendedWordList.filter(
|
||||||
|
(v) => {
|
||||||
|
return v.includes(value);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.filteredRecommendedWordList = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.store
|
||||||
|
.pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
|
||||||
|
.subscribe((loginRes) => {
|
||||||
|
this.loginRes = loginRes;
|
||||||
|
});
|
||||||
|
|
||||||
|
combineLatest([
|
||||||
|
this.store.pipe(select(RoomSelector.rooms)),
|
||||||
|
this.store.pipe(select(RoomSelector.roomUsers)),
|
||||||
|
this.store.pipe(select(RoomSelector.roomUsersShort))
|
||||||
|
])
|
||||||
|
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||||
|
.subscribe(([rooms, roomUsers, roomUsersShort]) => {
|
||||||
|
rooms = rooms || [];
|
||||||
|
roomUsers = roomUsers || [];
|
||||||
|
roomUsersShort = roomUsersShort || [];
|
||||||
|
|
||||||
|
const recommendedWordList = [];
|
||||||
|
for (const r of rooms) {
|
||||||
|
if (!!r.roomName && '' !== r.roomName.trim()) {
|
||||||
|
recommendedWordList.push(r.roomName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const ru of roomUsers) {
|
||||||
|
for (const u of ru.userInfos) {
|
||||||
|
if (!!this.loginRes && u.seq !== Number(this.loginRes.userSeq)) {
|
||||||
|
if (!!u.name && '' !== u.name.trim() && u.isJoinRoom) {
|
||||||
|
recommendedWordList.push(u.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const ru of roomUsersShort) {
|
||||||
|
for (const u of ru.userInfos) {
|
||||||
|
if (!!this.loginRes && u.seq !== Number(this.loginRes.userSeq)) {
|
||||||
|
if (!!u.name && '' !== u.name.trim() && u.isJoinRoom) {
|
||||||
|
recommendedWordList.push(u.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.recommendedWordList = [
|
||||||
|
...recommendedWordList.filter(
|
||||||
|
(item, index) => recommendedWordList.indexOf(item) === index
|
||||||
|
)
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (!!this.ngOnDestroySubject) {
|
||||||
|
this.ngOnDestroySubject.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyDownEnter(event: KeyboardEvent, searchWord: string) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
this.autocomplete.closePanel();
|
||||||
|
|
||||||
|
this.keyDownEnter.emit({ searchWord });
|
||||||
|
}
|
||||||
|
onClickCancel() {
|
||||||
|
this.searchCancel.emit();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
<mat-card class="confirm-card mat-elevation-z dialog-creat-chat">
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title
|
||||||
|
cdkDrag
|
||||||
|
cdkDragRootElement=".cdk-overlay-pane"
|
||||||
|
cdkDragHandle
|
||||||
|
>{{ data.title }}</mat-card-title
|
||||||
|
>
|
||||||
|
<button class="icon-button btn-dialog-close" (click)="onClickChoice(false)">
|
||||||
|
<i class="mdi mdi-window-close"></i>
|
||||||
|
</button>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<div>
|
||||||
|
<mat-horizontal-stepper [linear]="isLinear" #stepper>
|
||||||
|
<mat-step label="Step 1" state="phone">
|
||||||
|
<ucap-local-organization-select-user
|
||||||
|
(changeUserList)="onChangeSelectedUserList($event)"
|
||||||
|
></ucap-local-organization-select-user>
|
||||||
|
<ng-template
|
||||||
|
[ngTemplateOutlet]="selectedUserListTemplate"
|
||||||
|
></ng-template>
|
||||||
|
<div>
|
||||||
|
<button mat-button>취소</button>
|
||||||
|
<button mat-button matStepperNext>
|
||||||
|
완료
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</mat-step>
|
||||||
|
<mat-step label="Step 2" state="chat">
|
||||||
|
<div>
|
||||||
|
<mat-label>새 그룹 추가</mat-label>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
#searchWordInput
|
||||||
|
placeholder="그룹 이름을 입력해주세요."
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
mat-button
|
||||||
|
matSuffix
|
||||||
|
mat-icon-button
|
||||||
|
aria-label="Clear"
|
||||||
|
(click)="searchWordInput.value = ''; onClickCancel()"
|
||||||
|
>
|
||||||
|
<mat-icon>close</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<ng-template
|
||||||
|
[ngTemplateOutlet]="selectedUserListTemplate"
|
||||||
|
></ng-template>
|
||||||
|
<div>
|
||||||
|
<button mat-button matStepperPrevious>Back</button>
|
||||||
|
<button mat-button (click)="onClickComplete(searchWordInput.value)">
|
||||||
|
Next
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</mat-step>
|
||||||
|
</mat-horizontal-stepper>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <mat-card-actions class="button-form flex-row">
|
||||||
|
<button
|
||||||
|
mat-stroked-button
|
||||||
|
(click)="onClickChoice(false)"
|
||||||
|
class="mat-primary"
|
||||||
|
>
|
||||||
|
{{ 'common.messages.no' | translate }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
mat-flat-button
|
||||||
|
[disabled]="getBtnValid()"
|
||||||
|
(click)="onClickChoice(true)"
|
||||||
|
class="mat-primary"
|
||||||
|
>
|
||||||
|
{{ 'common.messages.yes' | translate }}
|
||||||
|
</button>
|
||||||
|
</mat-card-actions> -->
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
<ng-template #selectedUserListTemplate>
|
||||||
|
<div class="list-chip">
|
||||||
|
<mat-chip-list aria-label="User selection">
|
||||||
|
<mat-chip
|
||||||
|
*ngFor="let userInfo of selectedUserList"
|
||||||
|
[selected]="getChipsRemoveYn(userInfo)"
|
||||||
|
(removed)="onClickDeleteUser(userInfo)"
|
||||||
|
>
|
||||||
|
<!-- {{ userInfo | ucapTranslate: 'name' }} -->
|
||||||
|
{{ userInfo.name }}
|
||||||
|
<mat-icon matChipRemove *ngIf="getChipsRemoveYn(userInfo)"
|
||||||
|
>clear</mat-icon
|
||||||
|
>
|
||||||
|
</mat-chip>
|
||||||
|
</mat-chip-list>
|
||||||
|
</div>
|
||||||
|
<ng-container
|
||||||
|
*ngIf="
|
||||||
|
SelectUserDialogType.NewChat === SelectUserDialogType.NewChat;
|
||||||
|
then newchatcount;
|
||||||
|
else defaultcount
|
||||||
|
"
|
||||||
|
></ng-container>
|
||||||
|
<ng-template #newchatcount>
|
||||||
|
<span [ngClass]="selectedUserList.length >= 300 ? 'text-warn-color' : ''">
|
||||||
|
{{ selectedUserList.length }} / 300
|
||||||
|
<!-- {{ environment.productConfig.CommonSetting.maxChatRoomUser - 1 }} -->
|
||||||
|
<!-- {{ 'common.units.persons' | translate }} -->
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="text-warn-color"
|
||||||
|
style="float: right;"
|
||||||
|
*ngIf="selectedUserList.length >= 300"
|
||||||
|
>
|
||||||
|
<!-- ({{
|
||||||
|
'chat.errors.maxCountOfRoomMemberWith'
|
||||||
|
| translate
|
||||||
|
: {
|
||||||
|
maxCount:
|
||||||
|
environment.productConfig.CommonSetting.maxChatRoomUser - 1
|
||||||
|
}
|
||||||
|
}}) -->
|
||||||
|
</span>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template #defaultcount>
|
||||||
|
<span>
|
||||||
|
{{ selectedUserList.length }}
|
||||||
|
<!-- {{ 'common.units.persons' | translate }} -->
|
||||||
|
</span>
|
||||||
|
</ng-template>
|
||||||
|
</ng-template>
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { CreateChatDialogComponent } from './create-chat.dialog.component';
|
||||||
|
|
||||||
|
describe('ucap::ui-organization::CreateChatDialogComponent', () => {
|
||||||
|
let component: CreateChatDialogComponent;
|
||||||
|
let fixture: ComponentFixture<CreateChatDialogComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [CreateChatDialogComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CreateChatDialogComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,137 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Inject,
|
||||||
|
ViewChild
|
||||||
|
} from '@angular/core';
|
||||||
|
import { UserInfo, GroupDetailData } from '@ucap/protocol-sync';
|
||||||
|
import {
|
||||||
|
UserInfoSS,
|
||||||
|
UserInfoF,
|
||||||
|
UserInfoDN,
|
||||||
|
DeptInfo
|
||||||
|
} from '@ucap/protocol-query';
|
||||||
|
|
||||||
|
import { Store, select } from '@ngrx/store';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
import {
|
||||||
|
CompanySelector,
|
||||||
|
DepartmentSelector
|
||||||
|
} from '@ucap/ng-store-organization';
|
||||||
|
import { Subject, combineLatest } from 'rxjs';
|
||||||
|
import { AppAuthenticationService } from '@app/services/app-authentication.service';
|
||||||
|
import { SelectUserDialogType } from '@app/types';
|
||||||
|
import { RoomInfo, UserInfo as RoomUserInfo } from '@ucap/protocol-room';
|
||||||
|
import { LoginSelector } from '@ucap/ng-store-authentication';
|
||||||
|
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||||
|
import { environment } from '@environments';
|
||||||
|
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
||||||
|
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||||
|
import { SelectUserSectionComponent } from '../../select-user.section.component';
|
||||||
|
import { GroupActions } from '@ucap/ng-store-group';
|
||||||
|
import { UserInfoTypes } from '../profile-list-item.component';
|
||||||
|
|
||||||
|
export interface CreateChatDialogData {
|
||||||
|
type?: SelectUserDialogType;
|
||||||
|
title: string;
|
||||||
|
/** CASE :: EditMember */
|
||||||
|
group?: GroupDetailData;
|
||||||
|
/** CASE :: EventForward */
|
||||||
|
ignoreRoom?: RoomInfo[];
|
||||||
|
/** CASE :: EditChatMember */
|
||||||
|
curRoomUser?: (
|
||||||
|
| UserInfo
|
||||||
|
| UserInfoSS
|
||||||
|
| UserInfoF
|
||||||
|
| UserInfoDN
|
||||||
|
| RoomUserInfo
|
||||||
|
)[];
|
||||||
|
}
|
||||||
|
export interface CreateChatDialogResult {}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-local-organization-create-chat.dialog',
|
||||||
|
templateUrl: './create-chat.dialog.component.html',
|
||||||
|
styleUrls: ['./create-chat.dialog.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class CreateChatDialogComponent implements OnInit, OnDestroy {
|
||||||
|
private ngOnDestroySubject = new Subject<boolean>();
|
||||||
|
isLinear = false;
|
||||||
|
firstFormGroup: FormGroup;
|
||||||
|
secondFormGroup: FormGroup;
|
||||||
|
selectedUserList: UserInfoTypes[] = [];
|
||||||
|
SelectUserDialogType = SelectUserDialogType;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public dialogRef: MatDialogRef<
|
||||||
|
CreateChatDialogData,
|
||||||
|
CreateChatDialogResult
|
||||||
|
>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: CreateChatDialogData,
|
||||||
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
|
private store: Store<any>,
|
||||||
|
private appAuthenticationService: AppAuthenticationService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@ViewChild('selectBoxUserComponent', { static: false })
|
||||||
|
selectBoxUserComponent: SelectUserSectionComponent;
|
||||||
|
|
||||||
|
ngOnInit(): void {}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (!!this.ngOnDestroySubject) {
|
||||||
|
this.ngOnDestroySubject.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickCancel() {}
|
||||||
|
onClickChoice(s: boolean) {}
|
||||||
|
getBtnValid() {}
|
||||||
|
getChipsRemoveYn(userInfo: UserInfo) {}
|
||||||
|
onClickDeleteUser(userInfo: UserInfo) {}
|
||||||
|
|
||||||
|
onChangeSelectedUserList(userList: UserInfoTypes[]) {
|
||||||
|
this.selectedUserList = userList;
|
||||||
|
this.changeDetectorRef.markForCheck();
|
||||||
|
}
|
||||||
|
onClickComplete(groupName: string) {
|
||||||
|
switch (this.data.type) {
|
||||||
|
case SelectUserDialogType.NewGroup:
|
||||||
|
{
|
||||||
|
const userSeqs: string[] = [];
|
||||||
|
this.selectedUserList.map((user) =>
|
||||||
|
userSeqs.push(user.seq.toString())
|
||||||
|
);
|
||||||
|
this.store.dispatch(
|
||||||
|
GroupActions.create({
|
||||||
|
groupName,
|
||||||
|
targetUserSeqs: userSeqs
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SelectUserDialogType.NewChat:
|
||||||
|
{
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SelectUserDialogType.EditChatMember:
|
||||||
|
{
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SelectUserDialogType.EditMember:
|
||||||
|
{
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SelectUserDialogType.MessageForward:
|
||||||
|
{
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { CreateChatDialogComponent } from './create-chat.dialog.component';
|
||||||
|
|
||||||
|
export const DIALOGS = [CreateChatDialogComponent];
|
9
src/app/sections/group/components/component-ui/index.ts
Normal file
9
src/app/sections/group/components/component-ui/index.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { ProfileListItemComponent } from './profile-list-item.component';
|
||||||
|
import { ProfileComponent } from './profile.component';
|
||||||
|
import { TenantSearchComponent } from './tenant-search.component';
|
||||||
|
|
||||||
|
export const COMPONENTS = [
|
||||||
|
ProfileListItemComponent,
|
||||||
|
ProfileComponent,
|
||||||
|
TenantSearchComponent
|
||||||
|
];
|
|
@ -0,0 +1,35 @@
|
||||||
|
<div class="ucap-organization-profile-list-item-container">
|
||||||
|
<span class="ucap-organization-profile-list-item-presence">bullet</span>
|
||||||
|
<div class="ucap-organization-profile-list-item-profile-image">
|
||||||
|
<img
|
||||||
|
class="thumbnail"
|
||||||
|
ucapImage
|
||||||
|
[base]="profileImageRoot"
|
||||||
|
[path]="userInfo.profileImageFile"
|
||||||
|
[default]="defaultProfileImage"
|
||||||
|
(click)="onClickProfileImage($event, userInfo)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="user-info">
|
||||||
|
<div>
|
||||||
|
<div class="user-name">
|
||||||
|
{{ userInfo | ucapOrganizationTranslate: 'name' }}
|
||||||
|
</div>
|
||||||
|
<div class="user-grade">
|
||||||
|
{{ userInfo | ucapOrganizationTranslate: 'grade' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dept-name">
|
||||||
|
{{ userInfo | ucapOrganizationTranslate: 'deptName' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="intro">
|
||||||
|
{{ userInfo.intro }}
|
||||||
|
</div>
|
||||||
|
<div *ngIf="checkable">
|
||||||
|
<mat-checkbox
|
||||||
|
#checkbox
|
||||||
|
(change)="onChangeCheck(checkbox.checked, userInfo)"
|
||||||
|
></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { ProfileListItemComponent } from './profile-list-item.component';
|
||||||
|
|
||||||
|
describe('ucap::ui-organization::ProfileListItemComponent', () => {
|
||||||
|
let component: ProfileListItemComponent;
|
||||||
|
let fixture: ComponentFixture<ProfileListItemComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ProfileListItemComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ProfileListItemComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,64 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Input,
|
||||||
|
EventEmitter,
|
||||||
|
Output
|
||||||
|
} from '@angular/core';
|
||||||
|
import { UserInfo } from '@ucap/protocol-sync';
|
||||||
|
import { UserInfoSS, UserInfoF, UserInfoDN } from '@ucap/protocol-query';
|
||||||
|
|
||||||
|
export type UserInfoTypes = UserInfo | UserInfoSS | UserInfoF | UserInfoDN;
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-local-organization-profile-list-item',
|
||||||
|
templateUrl: './profile-list-item.component.html',
|
||||||
|
styleUrls: ['./profile-list-item.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class ProfileListItemComponent implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
set userInfo(user: UserInfoTypes) {
|
||||||
|
this._userInfo = user;
|
||||||
|
}
|
||||||
|
get userInfo(): UserInfoTypes {
|
||||||
|
return this._userInfo;
|
||||||
|
}
|
||||||
|
_userInfo: UserInfoTypes;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
defaultProfileImage: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
profileImageRoot: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
checkable = false;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
checkUser = new EventEmitter<{
|
||||||
|
isChecked: boolean;
|
||||||
|
userInfo: UserInfoTypes;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
constructor(private changeDetectorRef: ChangeDetectorRef) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {}
|
||||||
|
|
||||||
|
onClickProfileImage(event: Event, userInfo: UserInfoTypes): void {}
|
||||||
|
|
||||||
|
onChangeCheck(
|
||||||
|
value: boolean,
|
||||||
|
userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN
|
||||||
|
) {
|
||||||
|
this.checkUser.emit({
|
||||||
|
isChecked: value,
|
||||||
|
userInfo
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
<div class="mainProfile">
|
||||||
|
<mat-card class="example-card">
|
||||||
|
<mat-card-header>
|
||||||
|
<div mat-card-avatar class="profileImage" style="background-size: cover;">
|
||||||
|
<img
|
||||||
|
src="https://material.angular.io/assets/img/examples/shiba2.jpg"
|
||||||
|
style="width: 50px; height: auto;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<mat-card-title class="name"
|
||||||
|
>{{ userInfo.name }}
|
||||||
|
<span class="grade">{{ userInfo.grade }}</span></mat-card-title
|
||||||
|
>
|
||||||
|
<mat-card-subtitle>({{ userInfo.nameEn }})</mat-card-subtitle>
|
||||||
|
<mat-card-subtitle><span>O</span>온라인</mat-card-subtitle>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<ng-container *ngIf="isMe; then isMe; else other"></ng-container>
|
||||||
|
|
||||||
|
<ng-template #isMe>
|
||||||
|
<div class="intro">
|
||||||
|
<mat-form-field class="example-full-width">
|
||||||
|
<mat-label>이름 부서명, 전화번호, 이메일</mat-label>
|
||||||
|
<input matInput placeholder="인트로" value="" />
|
||||||
|
<mat-icon matSuffix>search</mat-icon>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template #other>
|
||||||
|
<mat-card-actions>
|
||||||
|
<button mat-button class="info" aria-label="메세지">메세지</button>
|
||||||
|
<button mat-button class="theme" aria-label="쪽지">쪽지</button>
|
||||||
|
<button mat-button class="theme" aria-label="휴대폰">휴대폰</button>
|
||||||
|
<button mat-button class="theme" aria-label="콜">콜</button>
|
||||||
|
<button mat-button class="theme" aria-label="화상회의">
|
||||||
|
화상회의
|
||||||
|
</button>
|
||||||
|
</mat-card-actions>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li class="company">
|
||||||
|
<label>{{ 'profile.labels.company' | ucapI18n }}</label
|
||||||
|
>{{ userInfo.companyName }}
|
||||||
|
</li>
|
||||||
|
<li class="dept">
|
||||||
|
<label>{{ 'profile.labels.deptartment' | ucapI18n }}</label
|
||||||
|
>{{ userInfo.deptName }}
|
||||||
|
</li>
|
||||||
|
<li class="email">
|
||||||
|
<label>{{ 'profile.labels.email' | ucapI18n }}</label
|
||||||
|
>{{ userInfo.email }}
|
||||||
|
</li>
|
||||||
|
<li class="office">
|
||||||
|
<label>{{ 'profile.labels.officePhoneNumber' | ucapI18n }}</label
|
||||||
|
>{{ userInfo.lineNumber }}
|
||||||
|
</li>
|
||||||
|
<li class="mobile">
|
||||||
|
<label>{{ 'profile.labels.handphone' | ucapI18n }}</label
|
||||||
|
>{{ userInfo.hpNumber }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</mat-card-content>
|
||||||
|
<mat-card-actions>
|
||||||
|
<button mat-button class="info">info</button>
|
||||||
|
<button mat-button class="theme">theme1</button>
|
||||||
|
<button mat-button class="theme">theme2</button>
|
||||||
|
<button mat-button class="theme checked">theme3</button>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
|
@ -0,0 +1,2 @@
|
||||||
|
.profile-container {
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { TestBed, async } from '@angular/core/testing';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { ProfileComponent } from './profile.component';
|
||||||
|
|
||||||
|
describe('app::sections::group::ProfileComponent', () => {
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [RouterTestingModule],
|
||||||
|
declarations: [ProfileComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create the app', () => {
|
||||||
|
const fixture = TestBed.createComponent(ProfileComponent);
|
||||||
|
const app = fixture.componentInstance;
|
||||||
|
expect(app).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should have as title 'ucap-lg-web'`, () => {
|
||||||
|
const fixture = TestBed.createComponent(ProfileComponent);
|
||||||
|
const app = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render title', () => {
|
||||||
|
const fixture = TestBed.createComponent(ProfileComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement;
|
||||||
|
expect(compiled.querySelector('.content span').textContent).toContain(
|
||||||
|
'ucap-lg-web app is running!'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,280 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
EventEmitter,
|
||||||
|
ViewChild,
|
||||||
|
ElementRef
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { AppKey } from '@app/types/app-key.type';
|
||||||
|
import { LoginSession } from '@app/models/login-session';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { UserInfoSS, AuthResponse } from '@ucap/protocol-query';
|
||||||
|
import { OpenProfileOptions } from '@ucap/protocol-buddy';
|
||||||
|
import { FileUploadItem } from '@ucap/api';
|
||||||
|
import { FormControl } from '@angular/forms';
|
||||||
|
import { WorkStatusType } from '@ucap/protocol';
|
||||||
|
import { I18nService } from '@ucap/ng-i18n';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-group-profile',
|
||||||
|
templateUrl: './profile.component.html',
|
||||||
|
styleUrls: ['./profile.component.scss'],
|
||||||
|
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class ProfileComponent implements OnInit, OnDestroy {
|
||||||
|
private ngOnDestroySubject = new Subject<boolean>();
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
profileImageRoot: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
isMe: boolean;
|
||||||
|
@Input()
|
||||||
|
isBuddy: boolean;
|
||||||
|
@Input()
|
||||||
|
isFavorit: boolean;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set userInfo(u: UserInfoSS) {
|
||||||
|
this._userInfo = u;
|
||||||
|
}
|
||||||
|
get userInfo(): UserInfoSS {
|
||||||
|
return this._userInfo;
|
||||||
|
}
|
||||||
|
_userInfo: UserInfoSS;
|
||||||
|
@Input()
|
||||||
|
myMadn?: string;
|
||||||
|
@Input()
|
||||||
|
openProfileOptions?: OpenProfileOptions;
|
||||||
|
@Input()
|
||||||
|
useBuddyToggleButton: boolean;
|
||||||
|
@Input()
|
||||||
|
authInfo: AuthResponse;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
profileImageView = new EventEmitter<void>();
|
||||||
|
@Output()
|
||||||
|
openChat = new EventEmitter<UserInfoSS>();
|
||||||
|
@Output()
|
||||||
|
sendMessage = new EventEmitter<UserInfoSS>();
|
||||||
|
@Output()
|
||||||
|
sendCall = new EventEmitter<string>();
|
||||||
|
@Output()
|
||||||
|
sendSms = new EventEmitter<string>();
|
||||||
|
@Output()
|
||||||
|
createConference = new EventEmitter<number>();
|
||||||
|
@Output()
|
||||||
|
toggleFavorit = new EventEmitter<{
|
||||||
|
userInfo: UserInfoSS;
|
||||||
|
isFavorit: boolean;
|
||||||
|
}>();
|
||||||
|
@Output()
|
||||||
|
toggleBuddy = new EventEmitter<{
|
||||||
|
userInfo: UserInfoSS;
|
||||||
|
isBuddy: boolean;
|
||||||
|
}>();
|
||||||
|
@Output()
|
||||||
|
uploadProfileImage = new EventEmitter<FileUploadItem>();
|
||||||
|
@Output()
|
||||||
|
updateIntro = new EventEmitter<string>();
|
||||||
|
|
||||||
|
@ViewChild('profileImageFileInput', { static: false })
|
||||||
|
profileImageFileInput: ElementRef<HTMLInputElement>;
|
||||||
|
|
||||||
|
userIntroFormControl = new FormControl('');
|
||||||
|
profileImageFileUploadItem: FileUploadItem;
|
||||||
|
|
||||||
|
constructor(private i18nService: I18nService) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (!!this.ngOnDestroySubject) {
|
||||||
|
this.ngOnDestroySubject.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickProfileImageView() {
|
||||||
|
this.profileImageView.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickOpenChat() {
|
||||||
|
this.openChat.emit(this.userInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickCall(type: string) {
|
||||||
|
let calleeNumber = '';
|
||||||
|
|
||||||
|
if (type === 'LINE') {
|
||||||
|
calleeNumber = this.userInfo.lineNumber;
|
||||||
|
} else {
|
||||||
|
calleeNumber = this.userInfo.hpNumber;
|
||||||
|
}
|
||||||
|
this.sendCall.emit(calleeNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickSMS() {
|
||||||
|
this.sendSms.emit(this.userInfo.employeeNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickVideoConference() {
|
||||||
|
this.createConference.emit(Number(this.userInfo.seq));
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickMessage() {
|
||||||
|
this.sendMessage.emit(this.userInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
onToggleFavorit() {
|
||||||
|
this.isFavorit = !this.isFavorit;
|
||||||
|
|
||||||
|
this.toggleFavorit.emit({
|
||||||
|
userInfo: this.userInfo,
|
||||||
|
isFavorit: this.isFavorit
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickAddBuddy() {
|
||||||
|
this.toggleBuddy.emit({
|
||||||
|
userInfo: this.userInfo,
|
||||||
|
isBuddy: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickDelBuddy() {
|
||||||
|
this.toggleBuddy.emit({
|
||||||
|
userInfo: this.userInfo,
|
||||||
|
isBuddy: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
onApplyIntroMessage(intro: string) {
|
||||||
|
if (intro.trim().length < 1) {
|
||||||
|
this.updateIntro.emit(' ');
|
||||||
|
} else {
|
||||||
|
this.updateIntro.emit(intro);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeFileInput() {
|
||||||
|
this.profileImageFileUploadItem = FileUploadItem.fromFiles(
|
||||||
|
this.profileImageFileInput.nativeElement.files
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
this.uploadProfileImage.emit(this.profileImageFileUploadItem);
|
||||||
|
|
||||||
|
this.profileImageFileInput.nativeElement.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
getWorkstatus(userInfo: UserInfoSS): string {
|
||||||
|
let workstatus = '';
|
||||||
|
if (!!userInfo && !!userInfo.workstatus) {
|
||||||
|
switch (userInfo.workstatus) {
|
||||||
|
case WorkStatusType.VacationAM:
|
||||||
|
workstatus = '오전';
|
||||||
|
break;
|
||||||
|
case WorkStatusType.VacationPM:
|
||||||
|
workstatus = '오후';
|
||||||
|
break;
|
||||||
|
case WorkStatusType.VacationAll:
|
||||||
|
workstatus = '휴가';
|
||||||
|
break;
|
||||||
|
case WorkStatusType.LeaveOfAbsence:
|
||||||
|
workstatus = '휴직';
|
||||||
|
break;
|
||||||
|
case WorkStatusType.LongtermRefresh:
|
||||||
|
workstatus = '장기';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return workstatus;
|
||||||
|
}
|
||||||
|
getWorkstatusStyle(userInfo: UserInfoSS): string {
|
||||||
|
// morning-off: 오전 afternoon-off: 오후 day-off: 휴가 long-time: 장기 leave-of-absence: 휴직
|
||||||
|
let style = '';
|
||||||
|
if (!!userInfo && !!userInfo.workstatus) {
|
||||||
|
switch (userInfo.workstatus) {
|
||||||
|
case WorkStatusType.VacationAM:
|
||||||
|
style = 'morning-off';
|
||||||
|
break;
|
||||||
|
case WorkStatusType.VacationPM:
|
||||||
|
style = 'afternoon-off';
|
||||||
|
break;
|
||||||
|
case WorkStatusType.VacationAll:
|
||||||
|
style = 'day-off';
|
||||||
|
break;
|
||||||
|
case WorkStatusType.LeaveOfAbsence:
|
||||||
|
style = 'leave-of-absence';
|
||||||
|
break;
|
||||||
|
case WorkStatusType.LongtermRefresh:
|
||||||
|
style = 'long-time';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDisabledBtn(type: string): boolean {
|
||||||
|
if (!this.myMadn || this.myMadn.trim().length === 0) {
|
||||||
|
if (type === 'LINE' || type === 'MOBILE') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'LINE') {
|
||||||
|
if (
|
||||||
|
!!this.userInfo &&
|
||||||
|
!!this.userInfo.lineNumber &&
|
||||||
|
this.userInfo.lineNumber.trim().length > 0
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (type === 'MOBILE') {
|
||||||
|
if (
|
||||||
|
!!this.userInfo &&
|
||||||
|
!!this.userInfo.hpNumber &&
|
||||||
|
this.userInfo.hpNumber.trim().length > 0
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (type === 'SMS') {
|
||||||
|
// const smsUtils = new SmsUtils(
|
||||||
|
// this.sessionStorageService,
|
||||||
|
// this.nativeService
|
||||||
|
// );
|
||||||
|
// return !smsUtils.getAuthSms();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getShowBuddyToggleBtn(type: 'DEL' | 'ADD'): boolean {
|
||||||
|
let rtn = false;
|
||||||
|
if (!this.useBuddyToggleButton) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'ADD') {
|
||||||
|
if (!this.isBuddy) {
|
||||||
|
rtn = true;
|
||||||
|
}
|
||||||
|
} else if (type === 'DEL') {
|
||||||
|
if (!!this.isBuddy) {
|
||||||
|
rtn = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rtn;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
<div class="search-container">
|
||||||
|
<div class="selectbox">
|
||||||
|
<mat-select [(value)]="companyCode" disableOptionCentering>
|
||||||
|
<mat-option
|
||||||
|
*ngFor="let company of companyList"
|
||||||
|
[value]="company.companyCode"
|
||||||
|
>{{ company.companyName }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</div>
|
||||||
|
<div class="searchbox">
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label>이름 부서명, 전화번호, 이메일</mat-label>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
#searchWordInput
|
||||||
|
placeholder="이름 부서명, 전화번호, 이메일"
|
||||||
|
(keydown.enter)="onKeyDownEnter(searchWordInput.value)"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
mat-button
|
||||||
|
matSuffix
|
||||||
|
mat-icon-button
|
||||||
|
aria-label="Clear"
|
||||||
|
(click)="searchWordInput.value = ''; onClickCancel()"
|
||||||
|
>
|
||||||
|
<mat-icon>close</mat-icon>
|
||||||
|
</button>
|
||||||
|
<mat-icon matSuffix>search</mat-icon>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { TenantSearchComponent } from './tenant-search.component';
|
||||||
|
|
||||||
|
describe('ucap::ui-organization::TenantSearchComponent', () => {
|
||||||
|
let component: TenantSearchComponent;
|
||||||
|
let fixture: ComponentFixture<TenantSearchComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [TenantSearchComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(TenantSearchComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,73 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Input,
|
||||||
|
EventEmitter,
|
||||||
|
Output
|
||||||
|
} from '@angular/core';
|
||||||
|
import { UserInfo } from '@ucap/protocol-sync';
|
||||||
|
import { UserInfoSS, UserInfoF, UserInfoDN } from '@ucap/protocol-query';
|
||||||
|
|
||||||
|
import { Store, select } from '@ngrx/store';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
import { CompanySelector } from '@ucap/ng-store-organization';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { Company } from '@ucap/api-external';
|
||||||
|
import { AppAuthenticationService } from '@app/services/app-authentication.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-local-organization-tenant-search',
|
||||||
|
templateUrl: './tenant-search.component.html',
|
||||||
|
styleUrls: ['./tenant-search.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class TenantSearchComponent implements OnInit, OnDestroy {
|
||||||
|
companyList: Company[];
|
||||||
|
companyCode: string;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
keyDownEnter = new EventEmitter<{
|
||||||
|
companyCode: string;
|
||||||
|
searchWord: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
searchCancel = new EventEmitter<any>();
|
||||||
|
|
||||||
|
private ngOnDestroySubject = new Subject<boolean>();
|
||||||
|
constructor(
|
||||||
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
|
private store: Store<any>,
|
||||||
|
private appAuthenticationService: AppAuthenticationService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
const userStore = this.appAuthenticationService.getUserStore();
|
||||||
|
this.companyCode = userStore.companyCode;
|
||||||
|
|
||||||
|
this.store
|
||||||
|
.pipe(
|
||||||
|
takeUntil(this.ngOnDestroySubject),
|
||||||
|
select(CompanySelector.companyList)
|
||||||
|
)
|
||||||
|
.subscribe((companyList) => {
|
||||||
|
this.companyList = companyList;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (!!this.ngOnDestroySubject) {
|
||||||
|
this.ngOnDestroySubject.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyDownEnter(searchWord: string) {
|
||||||
|
this.keyDownEnter.emit({ companyCode: this.companyCode, searchWord });
|
||||||
|
}
|
||||||
|
onClickCancel() {
|
||||||
|
this.searchCancel.emit();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,19 @@
|
||||||
import { ListSectionComponent } from './list.section.component';
|
import { ListSectionComponent } from './list.section.component';
|
||||||
import { SearchSectionComponent } from './search.section.component';
|
import { SearchSectionComponent } from './search.section.component';
|
||||||
|
import { ProfileSectionComponent } from './profile.section.component';
|
||||||
|
import { InfoSectionComponent } from './info.section.component';
|
||||||
|
import { SelectUserSectionComponent } from './select-user.section.component';
|
||||||
|
|
||||||
export const COMPONENTS = [ListSectionComponent, SearchSectionComponent];
|
import { COMPONENTS as COMPONENTS_UI } from './component-ui';
|
||||||
|
import { DIALOGS as DIALOGS_UI } from './component-ui/dialogs';
|
||||||
|
|
||||||
|
export const COMPONENTS = [
|
||||||
|
...COMPONENTS_UI,
|
||||||
|
ListSectionComponent,
|
||||||
|
SearchSectionComponent,
|
||||||
|
ProfileSectionComponent,
|
||||||
|
InfoSectionComponent,
|
||||||
|
SelectUserSectionComponent
|
||||||
|
];
|
||||||
|
|
||||||
|
export const DIALOGS = [...DIALOGS_UI];
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
<div class="info">
|
||||||
|
<ng-container *ngIf="!!isMe; then myInfo; else otherInfo"> </ng-container>
|
||||||
|
<ng-template #myInfo>
|
||||||
|
<div class="bookmark">
|
||||||
|
<div class="subtitle">Bookmark</div>
|
||||||
|
<div class="chatlist">
|
||||||
|
<!-- loop > component > 대화 리스트 공용 -->
|
||||||
|
<div>
|
||||||
|
<div class="profileImage">
|
||||||
|
<img
|
||||||
|
src="https://material.angular.io/assets/img/examples/shiba2.jpg"
|
||||||
|
style="width: 50px; height: 50px;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="info">
|
||||||
|
<div class="roomName">UCAP 프로젝트방</div>
|
||||||
|
<div class="lastMessage">
|
||||||
|
대화방의 마지막대화내용이 들어갈껍니다.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="subInfo">
|
||||||
|
<div class="lastDate" matBadge="4">2020.04.05</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--// loop > component > 대화 리스트 공용 -->
|
||||||
|
<div>
|
||||||
|
<div class="profileImage">
|
||||||
|
<img
|
||||||
|
src="https://material.angular.io/assets/img/examples/shiba2.jpg"
|
||||||
|
style="width: 50px; height: 50px;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="info">
|
||||||
|
<div class="roomName">UCAP 프로젝트방</div>
|
||||||
|
<div class="lastMessage">
|
||||||
|
대화방의 마지막대화내용이 들어갈껍니다.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="subInfo">
|
||||||
|
<div class="lastDate">2020.04.05</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="allim">
|
||||||
|
<div class="subtitle">알림봇</div>
|
||||||
|
<div class="allimList">
|
||||||
|
<mat-card class="allim-card">
|
||||||
|
<mat-card-header>
|
||||||
|
<div
|
||||||
|
mat-card-avatar
|
||||||
|
class="profileImage"
|
||||||
|
style="background-size: cover;"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="https://material.angular.io/assets/img/examples/shiba2.jpg"
|
||||||
|
style="width: 50px; height: auto;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<mat-card-title>화상회의</mat-card-title>
|
||||||
|
<mat-card-subtitle>2020.04.05</mat-card-subtitle>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="title">화상회의 개설</div>
|
||||||
|
<div class="contents">화상회의가 개설되었습니다.</div>
|
||||||
|
</mat-card-content>
|
||||||
|
<mat-card-actions>
|
||||||
|
<button mat-button class="more">더보기</button>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
||||||
|
<mat-card class="allim-card">
|
||||||
|
<mat-card-header>
|
||||||
|
<div
|
||||||
|
mat-card-avatar
|
||||||
|
class="profileImage"
|
||||||
|
style="background-size: cover;"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="https://material.angular.io/assets/img/examples/shiba2.jpg"
|
||||||
|
style="width: 50px; height: auto;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<mat-card-title>화상회의</mat-card-title>
|
||||||
|
<mat-card-subtitle>2020.04.05</mat-card-subtitle>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<div class="title">화상회의 개설</div>
|
||||||
|
<div class="contents">화상회의가 개설되었습니다.</div>
|
||||||
|
</mat-card-content>
|
||||||
|
<mat-card-actions>
|
||||||
|
<button mat-button class="more">더보기</button>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template #otherInfo> </ng-template>
|
||||||
|
<div class="banner">배너</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,2 @@
|
||||||
|
.info-container {
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { TestBed, async } from '@angular/core/testing';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { InfoSectionComponent } from './info.section.component';
|
||||||
|
|
||||||
|
describe('app::sections::group::InfoSectionComponent', () => {
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [RouterTestingModule],
|
||||||
|
declarations: [InfoSectionComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create the app', () => {
|
||||||
|
const fixture = TestBed.createComponent(InfoSectionComponent);
|
||||||
|
const app = fixture.componentInstance;
|
||||||
|
expect(app).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should have as title 'ucap-lg-web'`, () => {
|
||||||
|
const fixture = TestBed.createComponent(InfoSectionComponent);
|
||||||
|
const app = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render title', () => {
|
||||||
|
const fixture = TestBed.createComponent(InfoSectionComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement;
|
||||||
|
expect(compiled.querySelector('.content span').textContent).toContain(
|
||||||
|
'ucap-lg-web app is running!'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
58
src/app/sections/group/components/info.section.component.ts
Normal file
58
src/app/sections/group/components/info.section.component.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Input
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { AppKey } from '@app/types/app-key.type';
|
||||||
|
import { LoginSession } from '@app/models/login-session';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { Store, select } from '@ngrx/store';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
import { LoginSelector } from '@ucap/ng-store-authentication';
|
||||||
|
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-sections-group-info',
|
||||||
|
templateUrl: './info.section.component.html',
|
||||||
|
styleUrls: ['./info.section.component.scss'],
|
||||||
|
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class InfoSectionComponent implements OnInit, OnDestroy {
|
||||||
|
private ngOnDestroySubject = new Subject<boolean>();
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
userSeq: string;
|
||||||
|
@Input()
|
||||||
|
isMe: boolean;
|
||||||
|
|
||||||
|
loginRes: LoginResponse;
|
||||||
|
|
||||||
|
constructor(private store: Store<any>) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.store
|
||||||
|
.pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
|
||||||
|
.subscribe((loginRes) => {
|
||||||
|
this.loginRes = loginRes;
|
||||||
|
|
||||||
|
if (
|
||||||
|
(!!this.userSeq &&
|
||||||
|
this.userSeq.localeCompare(loginRes.userSeq) === 0) ||
|
||||||
|
!this.userSeq
|
||||||
|
) {
|
||||||
|
this.isMe = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (!!this.ngOnDestroySubject) {
|
||||||
|
this.ngOnDestroySubject.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,59 @@
|
||||||
<div fxFlexFill class="list-container">
|
<div
|
||||||
<ucap-group-expansion-list></ucap-group-expansion-list>
|
*ngIf="!!searchObj && !searchObj.isShowSearch"
|
||||||
|
fxFlexFill
|
||||||
|
class="list-container"
|
||||||
|
>
|
||||||
|
<ucap-group-expansion
|
||||||
|
[displayOrder]="displayOrder"
|
||||||
|
[profile]="loginRes?.userInfo"
|
||||||
|
[favorites]="favorites"
|
||||||
|
[groupBuddies]="groupBuddies"
|
||||||
|
>
|
||||||
|
<ng-template ucapGroupExpansionNode let-node>
|
||||||
|
<ucap-local-organization-profile-list-item
|
||||||
|
[userInfo]="node.userInfo"
|
||||||
|
defaultProfileImage="assets/images/img_nophoto_50.png"
|
||||||
|
[profileImageRoot]="versionInfo2Res?.profileRoot"
|
||||||
|
[checkable]="checkable"
|
||||||
|
(checkUser)="onCheckUser($event)"
|
||||||
|
></ucap-local-organization-profile-list-item>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template ucapGroupExpansionFavoriteHeader let-node>
|
||||||
|
<span class="header-favorite">
|
||||||
|
<span>
|
||||||
|
{{ 'category.favorite' | ucapI18n }}
|
||||||
|
</span>
|
||||||
|
<span>{{ node.children?.length }}</span>
|
||||||
|
</span>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template ucapGroupExpansionBuddyHeader let-node>
|
||||||
|
<span class="header-buddy">
|
||||||
|
<span>{{ node.groupDetail.name }}</span>
|
||||||
|
<span>
|
||||||
|
{{ node.children?.length }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template ucapGroupExpansionDefaultHeader let-node>
|
||||||
|
<span class="header-default">
|
||||||
|
<span>
|
||||||
|
{{ 'category.default' | ucapI18n }}
|
||||||
|
</span>
|
||||||
|
<span>{{ node.children?.length }}</span>
|
||||||
|
</span>
|
||||||
|
</ng-template>
|
||||||
|
</ucap-group-expansion>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="!!searchObj && searchObj.isShowSearch" class="search-wrpper">
|
||||||
|
<perfect-scrollbar fxFlex="1 1 auto">
|
||||||
|
<ucap-local-organization-profile-list-item
|
||||||
|
*ngFor="let userInfo of searchUserInfos"
|
||||||
|
[userInfo]="userInfo"
|
||||||
|
[checkable]="checkable"
|
||||||
|
defaultProfileImage="assets/images/img_nophoto_50.png"
|
||||||
|
(checkUser)="onCheckUser($event)"
|
||||||
|
>
|
||||||
|
</ucap-local-organization-profile-list-item>
|
||||||
|
</perfect-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,2 +1,7 @@
|
||||||
.list-container {
|
.list-container {
|
||||||
}
|
}
|
||||||
|
.search-wrpper {
|
||||||
|
overflow: auto;
|
||||||
|
position: relative;
|
||||||
|
height: 350px;
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,20 @@
|
||||||
import { Observable } from 'rxjs';
|
import { Observable, Subject, combineLatest, of } from 'rxjs';
|
||||||
|
import { filter, takeUntil, take, map, catchError } from 'rxjs/operators';
|
||||||
|
|
||||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Input,
|
||||||
|
ViewChild,
|
||||||
|
EventEmitter,
|
||||||
|
Output
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { Store, select } from '@ngrx/store';
|
||||||
|
|
||||||
import { LogService } from '@ucap/ng-logger';
|
|
||||||
import {
|
import {
|
||||||
VirtualScrollStrategy,
|
VirtualScrollStrategy,
|
||||||
FixedSizeVirtualScrollStrategy,
|
FixedSizeVirtualScrollStrategy,
|
||||||
|
@ -10,6 +22,34 @@ import {
|
||||||
CdkVirtualScrollViewport
|
CdkVirtualScrollViewport
|
||||||
} from '@angular/cdk/scrolling';
|
} from '@angular/cdk/scrolling';
|
||||||
|
|
||||||
|
import { VersionInfo2Response } from '@ucap/api-public';
|
||||||
|
import { Company } from '@ucap/api-external';
|
||||||
|
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||||
|
import { UserInfo, GroupDetailData } from '@ucap/protocol-sync';
|
||||||
|
|
||||||
|
import { LogService } from '@ucap/ng-logger';
|
||||||
|
import { NodeType } from '@ucap/ng-ui-group';
|
||||||
|
import { SessionStorageService } from '@ucap/ng-web-storage';
|
||||||
|
import {
|
||||||
|
LoginSelector,
|
||||||
|
ConfigurationSelector
|
||||||
|
} from '@ucap/ng-store-authentication';
|
||||||
|
import { CompanySelector } from '@ucap/ng-store-organization';
|
||||||
|
import { BuddySelector, GroupSelector } from '@ucap/ng-store-group';
|
||||||
|
|
||||||
|
import { AppAuthenticationService } from '@app/services/app-authentication.service';
|
||||||
|
import { AppKey } from '@app/types/app-key.type';
|
||||||
|
import { LoginSession } from '@app/models/login-session';
|
||||||
|
import { QueryProtocolService } from '@ucap/ng-protocol-query';
|
||||||
|
import {
|
||||||
|
UserInfoSS,
|
||||||
|
DeptSearchType,
|
||||||
|
SSVC_TYPE_QUERY_DEPT_USER_DATA,
|
||||||
|
DeptUserData,
|
||||||
|
SSVC_TYPE_QUERY_DEPT_USER_RES
|
||||||
|
} from '@ucap/protocol-query';
|
||||||
|
import { UserInfoTypes } from './component-ui/profile-list-item.component';
|
||||||
|
|
||||||
export class GroupVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy {
|
export class GroupVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(60, 150, 200); // (itemSize, minBufferPx, maxBufferPx)
|
super(60, 150, 200); // (itemSize, minBufferPx, maxBufferPx)
|
||||||
|
@ -25,12 +65,220 @@ export class GroupVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy {
|
||||||
provide: VIRTUAL_SCROLL_STRATEGY,
|
provide: VIRTUAL_SCROLL_STRATEGY,
|
||||||
useClass: GroupVirtualScrollStrategy
|
useClass: GroupVirtualScrollStrategy
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
})
|
})
|
||||||
export class ListSectionComponent implements OnInit, OnDestroy {
|
export class ListSectionComponent implements OnInit, OnDestroy {
|
||||||
constructor(private logService: LogService) {}
|
@Input()
|
||||||
|
set searchObj(obj: {
|
||||||
ngOnInit(): void {}
|
isShowSearch: boolean;
|
||||||
|
companyCode: string;
|
||||||
ngOnDestroy(): void {}
|
searchWord: string;
|
||||||
|
}) {
|
||||||
|
this._searchObj = obj;
|
||||||
|
if (obj.isShowSearch && obj.searchWord.localeCompare('') !== 0) {
|
||||||
|
this.onOrganizationTenantSearch(obj);
|
||||||
|
} else {
|
||||||
|
this._searchObj.isShowSearch = false;
|
||||||
|
this.searchUserInfos = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get searchObj() {
|
||||||
|
return this._searchObj;
|
||||||
|
}
|
||||||
|
_searchObj: any;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set checkable(check: boolean) {
|
||||||
|
console.log(check);
|
||||||
|
this._checkable = check;
|
||||||
|
}
|
||||||
|
get checkable(): boolean {
|
||||||
|
return this._checkable;
|
||||||
|
}
|
||||||
|
_checkable = false;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
checkUser = new EventEmitter<{
|
||||||
|
isChecked: boolean;
|
||||||
|
userInfo: UserInfoTypes;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
loginSession: LoginSession;
|
||||||
|
versionInfo2Res: VersionInfo2Response;
|
||||||
|
loginRes: LoginResponse;
|
||||||
|
companyList: Company[];
|
||||||
|
|
||||||
|
searchUserInfos: UserInfoSS[] = [];
|
||||||
|
|
||||||
|
displayOrder: NodeType[] = [
|
||||||
|
NodeType.Profile,
|
||||||
|
NodeType.Favorite,
|
||||||
|
NodeType.Buddy,
|
||||||
|
NodeType.Default
|
||||||
|
];
|
||||||
|
|
||||||
|
profile: UserInfo;
|
||||||
|
favorites: UserInfo[];
|
||||||
|
groupBuddies: { group: GroupDetailData; buddyList: UserInfo[] }[];
|
||||||
|
|
||||||
|
private ngOnDestroySubject = new Subject<boolean>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private appAuthenticationService: AppAuthenticationService,
|
||||||
|
private sessionStorageService: SessionStorageService,
|
||||||
|
private store: Store<any>,
|
||||||
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
|
private logService: LogService,
|
||||||
|
private queryProtocolService: QueryProtocolService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.ngOnDestroySubject = new Subject<boolean>();
|
||||||
|
|
||||||
|
this.appAuthenticationService
|
||||||
|
.getLoginSession$()
|
||||||
|
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||||
|
.subscribe((loginSession) => (this.loginSession = loginSession));
|
||||||
|
|
||||||
|
this.store
|
||||||
|
.pipe(
|
||||||
|
takeUntil(this.ngOnDestroySubject),
|
||||||
|
select(ConfigurationSelector.versionInfo2Response)
|
||||||
|
)
|
||||||
|
.subscribe((versionInfo2Res) => {
|
||||||
|
this.versionInfo2Res = versionInfo2Res;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.store
|
||||||
|
.pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
|
||||||
|
.subscribe((loginRes) => {
|
||||||
|
this.loginRes = loginRes;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.store
|
||||||
|
.pipe(
|
||||||
|
takeUntil(this.ngOnDestroySubject),
|
||||||
|
select(CompanySelector.companyList)
|
||||||
|
)
|
||||||
|
.subscribe((companyList) => {
|
||||||
|
this.companyList = companyList;
|
||||||
|
});
|
||||||
|
|
||||||
|
combineLatest([
|
||||||
|
this.store.pipe(select(BuddySelector.buddies)),
|
||||||
|
this.store.pipe(select(GroupSelector.groups))
|
||||||
|
])
|
||||||
|
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||||
|
.subscribe(([buddies, groups]) => {
|
||||||
|
buddies = buddies || [];
|
||||||
|
groups = groups || [];
|
||||||
|
|
||||||
|
const favorites = buddies
|
||||||
|
.filter((buddy) => buddy.isFavorit)
|
||||||
|
.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
||||||
|
|
||||||
|
if (!!favorites && 0 < favorites.length) {
|
||||||
|
this.favorites = favorites;
|
||||||
|
this.changeDetectorRef.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
const tempOrder: GroupDetailData[] = [];
|
||||||
|
let defaultGroup: GroupDetailData;
|
||||||
|
const buddyGroup: GroupDetailData[] = [];
|
||||||
|
groups.forEach((group) => {
|
||||||
|
if (0 === group.seq) {
|
||||||
|
defaultGroup = group;
|
||||||
|
} else {
|
||||||
|
buddyGroup.push(group);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tempOrder.push(
|
||||||
|
...buddyGroup.sort((a, b) =>
|
||||||
|
a.name < b.name ? -1 : a.name > b.name ? 1 : 0
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!!defaultGroup) {
|
||||||
|
tempOrder.push(defaultGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
groups = tempOrder;
|
||||||
|
|
||||||
|
if (!!groups && 0 < groups.length) {
|
||||||
|
this.groupBuddies = [];
|
||||||
|
for (const group of groups) {
|
||||||
|
this.groupBuddies.push({
|
||||||
|
group,
|
||||||
|
buddyList: buddies.filter((buddy) => {
|
||||||
|
return -1 < group.userSeqs.indexOf(String(buddy.seq));
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.changeDetectorRef.markForCheck();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (!!this.ngOnDestroySubject) {
|
||||||
|
this.ngOnDestroySubject.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onOrganizationTenantSearch(obj: {
|
||||||
|
isShowSearch: boolean;
|
||||||
|
companyCode: string;
|
||||||
|
searchWord: string;
|
||||||
|
}) {
|
||||||
|
const searchUserInfos: UserInfoSS[] = [];
|
||||||
|
|
||||||
|
this.queryProtocolService
|
||||||
|
.deptUser({
|
||||||
|
divCd: 'GRP',
|
||||||
|
companyCode: this._searchObj.companyCode,
|
||||||
|
searchRange: DeptSearchType.All,
|
||||||
|
search: this._searchObj.searchWord,
|
||||||
|
senderCompanyCode: this.loginRes.userInfo.companyCode,
|
||||||
|
senderEmployeeType: this.loginRes.userInfo.employeeType
|
||||||
|
})
|
||||||
|
.pipe(
|
||||||
|
map((resObj) => {
|
||||||
|
const userInfos = resObj.userInfos;
|
||||||
|
|
||||||
|
searchUserInfos.push(...userInfos);
|
||||||
|
// 검색 결과 처리.
|
||||||
|
this.searchUserInfos = searchUserInfos.sort((a, b) =>
|
||||||
|
a.name < b.name ? -1 : a.name > b.name ? 1 : 0
|
||||||
|
);
|
||||||
|
// this.searchProcessing = false;
|
||||||
|
|
||||||
|
// 검색 결과에 따른 프레즌스 조회.
|
||||||
|
const userSeqList: number[] = [];
|
||||||
|
this.searchUserInfos.map((user) =>
|
||||||
|
userSeqList.push(Number(user.seq))
|
||||||
|
);
|
||||||
|
this.changeDetectorRef.markForCheck();
|
||||||
|
if (userSeqList.length > 0) {
|
||||||
|
// this.store.dispatch(
|
||||||
|
// StatusStore.bulkInfo({
|
||||||
|
// divCd: 'groupSrch',
|
||||||
|
// userSeqs: userSeqList
|
||||||
|
// })
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
catchError((error) => {
|
||||||
|
// this.searchProcessing = false;
|
||||||
|
return of(error);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
onCheckUser(params: { isChecked: boolean; userInfo: UserInfoTypes }) {
|
||||||
|
this.checkUser.emit(params);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<div fxFlexFill class="profile-container">
|
||||||
|
<app-group-profile
|
||||||
|
[isMe]="isMe"
|
||||||
|
[userInfo]="loginRes?.userInfo"
|
||||||
|
></app-group-profile>
|
||||||
|
</div>
|
|
@ -0,0 +1,2 @@
|
||||||
|
.profile-container {
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { TestBed, async } from '@angular/core/testing';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { ProfileSectionComponent } from './profile.section.component';
|
||||||
|
|
||||||
|
describe('app::sections::group::ProfileSectionComponent', () => {
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [RouterTestingModule],
|
||||||
|
declarations: [ProfileSectionComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create the app', () => {
|
||||||
|
const fixture = TestBed.createComponent(ProfileSectionComponent);
|
||||||
|
const app = fixture.componentInstance;
|
||||||
|
expect(app).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should have as title 'ucap-lg-web'`, () => {
|
||||||
|
const fixture = TestBed.createComponent(ProfileSectionComponent);
|
||||||
|
const app = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render title', () => {
|
||||||
|
const fixture = TestBed.createComponent(ProfileSectionComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement;
|
||||||
|
expect(compiled.querySelector('.content span').textContent).toContain(
|
||||||
|
'ucap-lg-web app is running!'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,55 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Input
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { AppKey } from '@app/types/app-key.type';
|
||||||
|
import { LoginSession } from '@app/models/login-session';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { Store, select } from '@ngrx/store';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
import { LoginSelector } from '@ucap/ng-store-authentication';
|
||||||
|
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-sections-group-profile',
|
||||||
|
templateUrl: './profile.section.component.html',
|
||||||
|
styleUrls: ['./profile.section.component.scss'],
|
||||||
|
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class ProfileSectionComponent implements OnInit, OnDestroy {
|
||||||
|
private ngOnDestroySubject = new Subject<boolean>();
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
userSeq: string;
|
||||||
|
loginRes: LoginResponse;
|
||||||
|
isMe = false;
|
||||||
|
|
||||||
|
constructor(private store: Store<any>) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.store
|
||||||
|
.pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
|
||||||
|
.subscribe((loginRes) => {
|
||||||
|
this.loginRes = loginRes;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!!this.userSeq &&
|
||||||
|
this.userSeq.localeCompare(loginRes.userSeq) === 0
|
||||||
|
) {
|
||||||
|
this.isMe = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (!!this.ngOnDestroySubject) {
|
||||||
|
this.ngOnDestroySubject.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
<div>
|
<ucap-local-organization-tenant-search
|
||||||
search section of group
|
(keyDownEnter)="onKeyDownEnter($event)"
|
||||||
</div>
|
(searchCancel)="onClickCancel()"
|
||||||
|
>
|
||||||
|
</ucap-local-organization-tenant-search>
|
||||||
|
|
|
@ -1,16 +1,44 @@
|
||||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
Output,
|
||||||
|
EventEmitter,
|
||||||
|
ChangeDetectionStrategy
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
import { LogService } from '@ucap/ng-logger';
|
import { LogService } from '@ucap/ng-logger';
|
||||||
|
import { I18nService } from '@ucap/ng-i18n';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-sections-group-search',
|
selector: 'app-sections-group-search',
|
||||||
templateUrl: './search.section.component.html',
|
templateUrl: './search.section.component.html',
|
||||||
styleUrls: ['./search.section.component.scss']
|
styleUrls: ['./search.section.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
})
|
})
|
||||||
export class SearchSectionComponent implements OnInit, OnDestroy {
|
export class SearchSectionComponent implements OnInit, OnDestroy {
|
||||||
constructor(private logService: LogService) {}
|
@Output()
|
||||||
|
keyDownEnter = new EventEmitter<{
|
||||||
|
companyCode: string;
|
||||||
|
searchWord: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
searchCancel = new EventEmitter<any>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private logService: LogService,
|
||||||
|
private i18nService: I18nService
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {}
|
ngOnInit(): void {}
|
||||||
|
|
||||||
ngOnDestroy(): void {}
|
ngOnDestroy(): void {}
|
||||||
|
|
||||||
|
onKeyDownEnter(params: { companyCode: string; searchWord: string }) {
|
||||||
|
this.keyDownEnter.emit(params);
|
||||||
|
}
|
||||||
|
onClickCancel() {
|
||||||
|
this.searchCancel.emit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
<div fxFlexFill>
|
||||||
|
<div fxFlex class="container">
|
||||||
|
<!-- search start-->
|
||||||
|
<ucap-local-organization-tenant-search
|
||||||
|
(keyDownEnter)="onKeyDownEnter($event)"
|
||||||
|
(searchCancel)="onClickCancel()"
|
||||||
|
></ucap-local-organization-tenant-search>
|
||||||
|
<!-- search end-->
|
||||||
|
|
||||||
|
<mat-tab-group mat-stretch-tabs class="tap-container">
|
||||||
|
<!--그룹-->
|
||||||
|
<mat-tab>
|
||||||
|
<ng-template mat-tab-label>
|
||||||
|
<!-- <button class="icon-button">
|
||||||
|
<i class="mid mid-24 mdi-account-multiple"></i>
|
||||||
|
</button> -->
|
||||||
|
<p>그룹</p>
|
||||||
|
</ng-template>
|
||||||
|
<div fxFlexFill>
|
||||||
|
<div class="mat-tab-frame dialog-tab-grouplist">
|
||||||
|
<app-sections-group-list
|
||||||
|
fxFlexFill
|
||||||
|
[searchObj]="searchObj"
|
||||||
|
[checkable]="true"
|
||||||
|
(checkUser)="onCheckUser($event)"
|
||||||
|
></app-sections-group-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-tab>
|
||||||
|
<!--조직-->
|
||||||
|
<mat-tab>
|
||||||
|
<ng-template mat-tab-label>
|
||||||
|
<!-- <button class="icon-button">
|
||||||
|
<i class="mid mid-24 mdi-account-multiple"></i>
|
||||||
|
</button> -->
|
||||||
|
<p>조직도</p>
|
||||||
|
</ng-template>
|
||||||
|
<div fxFlexFill>
|
||||||
|
<!-- <div class="mat-tab-frame dialog-tab-grouplist">
|
||||||
|
<app-sections-organization-tree></app-sections-organization-tree>
|
||||||
|
</div> -->
|
||||||
|
<div>
|
||||||
|
<span *ngFor="let breadcrumb of breadcrumbs">
|
||||||
|
<button mat-raised-button>{{ breadcrumb.label }}</button>
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="organization-tree">
|
||||||
|
<app-sections-organization-tree></app-sections-organization-tree>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,12 @@
|
||||||
|
.container {
|
||||||
|
overflow: hidden;
|
||||||
|
.tap-container {
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.organization-tree {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100% - 30px);
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { SelectUserSectionComponent } from './select-user.section.component';
|
||||||
|
|
||||||
|
describe('ucap::ui-organization::SelectUserSectionComponent', () => {
|
||||||
|
let component: SelectUserSectionComponent;
|
||||||
|
let fixture: ComponentFixture<SelectUserSectionComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [SelectUserSectionComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SelectUserSectionComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,107 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Output,
|
||||||
|
EventEmitter
|
||||||
|
} from '@angular/core';
|
||||||
|
import { UserInfo, GroupDetailData } from '@ucap/protocol-sync';
|
||||||
|
import {
|
||||||
|
UserInfoSS,
|
||||||
|
UserInfoF,
|
||||||
|
UserInfoDN,
|
||||||
|
DeptInfo
|
||||||
|
} from '@ucap/protocol-query';
|
||||||
|
|
||||||
|
import { Store, select } from '@ngrx/store';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
import {
|
||||||
|
CompanySelector,
|
||||||
|
DepartmentSelector
|
||||||
|
} from '@ucap/ng-store-organization';
|
||||||
|
import { Subject, combineLatest } from 'rxjs';
|
||||||
|
import { AppAuthenticationService } from '@app/services/app-authentication.service';
|
||||||
|
import { SelectUserDialogType } from '@app/types';
|
||||||
|
import { RoomInfo, UserInfo as RoomUserInfo } from '@ucap/protocol-room';
|
||||||
|
import { LoginSelector } from '@ucap/ng-store-authentication';
|
||||||
|
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||||
|
import { environment } from '@environments';
|
||||||
|
import { UserInfoTypes } from './component-ui/profile-list-item.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-local-organization-select-user',
|
||||||
|
templateUrl: './select-user.section.component.html',
|
||||||
|
styleUrls: ['./select-user.section.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class SelectUserSectionComponent implements OnInit, OnDestroy {
|
||||||
|
breadcrumbs: any = [
|
||||||
|
{
|
||||||
|
label: 'LGCNS'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'IT Helpdesk'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Issue Log'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
changeUserList = new EventEmitter<UserInfoTypes[]>();
|
||||||
|
|
||||||
|
searchObj: any = {
|
||||||
|
isShowSearch: false,
|
||||||
|
companyCode: '',
|
||||||
|
searchWord: ''
|
||||||
|
};
|
||||||
|
SelectUserDialogType = SelectUserDialogType;
|
||||||
|
|
||||||
|
selectedUserList: UserInfoTypes[] = [];
|
||||||
|
|
||||||
|
private ngOnDestroySubject = new Subject<boolean>();
|
||||||
|
constructor(
|
||||||
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
|
private store: Store<any>,
|
||||||
|
private appAuthenticationService: AppAuthenticationService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.ngOnDestroySubject = new Subject<boolean>();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (!!this.ngOnDestroySubject) {
|
||||||
|
this.ngOnDestroySubject.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyDownEnter(params: { companyCode: string; searchWord: string }) {
|
||||||
|
this.searchObj = {
|
||||||
|
isShowSearch: true,
|
||||||
|
companyCode: params.companyCode,
|
||||||
|
searchWord: params.searchWord
|
||||||
|
};
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
}
|
||||||
|
onClickCancel() {
|
||||||
|
this.searchObj = {
|
||||||
|
isShowSearch: false,
|
||||||
|
companyCode: '',
|
||||||
|
searchWord: ''
|
||||||
|
};
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
onCheckUser(params: { isChecked: boolean; userInfo: UserInfoTypes }) {
|
||||||
|
console.log(params);
|
||||||
|
this.selectedUserList = [...this.selectedUserList, params.userInfo];
|
||||||
|
this.changeUserList.emit(this.selectedUserList);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectedUserList(): UserInfoTypes[] {
|
||||||
|
return this.selectedUserList;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,27 +1,60 @@
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { FormsModule, ReactiveFormsModule, FormControl } from '@angular/forms';
|
||||||
|
|
||||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
|
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
|
import { MatTabsModule } from '@angular/material/tabs';
|
||||||
|
import { MatChipsModule } from '@angular/material/chips';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatStepperModule } from '@angular/material/stepper';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
|
||||||
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
|
||||||
import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
|
import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
|
||||||
|
|
||||||
|
import { UiModule } from '@ucap/ng-ui';
|
||||||
import { GroupUiModule } from '@ucap/ng-ui-group';
|
import { GroupUiModule } from '@ucap/ng-ui-group';
|
||||||
|
|
||||||
import { COMPONENTS } from './components';
|
import { COMPONENTS, DIALOGS } from './components';
|
||||||
|
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
||||||
|
import { AppOrganizationSectionModule } from '@app/sections/organization/organization.section.module';
|
||||||
|
import { OrganizationUiModule } from '@ucap/ng-ui-organization';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
FlexLayoutModule,
|
FlexLayoutModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatCardModule,
|
||||||
MatCheckboxModule,
|
MatCheckboxModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatSelectModule,
|
||||||
|
MatTabsModule,
|
||||||
|
MatChipsModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatStepperModule,
|
||||||
|
PerfectScrollbarModule,
|
||||||
I18nModule,
|
I18nModule,
|
||||||
|
UiModule,
|
||||||
|
AppOrganizationSectionModule,
|
||||||
|
OrganizationUiModule,
|
||||||
GroupUiModule
|
GroupUiModule
|
||||||
],
|
],
|
||||||
exports: [...COMPONENTS],
|
exports: [...COMPONENTS, ...DIALOGS],
|
||||||
declarations: [...COMPONENTS],
|
declarations: [...COMPONENTS, ...DIALOGS],
|
||||||
entryComponents: [],
|
entryComponents: [...DIALOGS],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
provide: UCAP_I18N_NAMESPACE,
|
provide: UCAP_I18N_NAMESPACE,
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
export const COMPONENTS = [];
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user