0527 sync
This commit is contained in:
parent
23bbfc4b63
commit
92da6c71ce
|
@ -55,8 +55,8 @@
|
|||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
"maximumWarning": "30kb",
|
||||
"maximumError": "50kb"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -86,8 +86,8 @@
|
|||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
"maximumWarning": "30kb",
|
||||
"maximumError": "50kb"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
18
package.json
18
package.json
|
@ -32,12 +32,12 @@
|
|||
"@ngrx/router-store": "^9.0.0",
|
||||
"@ngrx/store": "^9.0.0",
|
||||
"@ucap/api": "~0.0.2",
|
||||
"@ucap/api-common": "~0.0.3",
|
||||
"@ucap/api-common": "~0.0.5",
|
||||
"@ucap/api-external": "~0.0.5",
|
||||
"@ucap/api-message": "~0.0.3",
|
||||
"@ucap/api-prompt": "~0.0.3",
|
||||
"@ucap/api-public": "~0.0.4",
|
||||
"@ucap/core": "~0.0.7",
|
||||
"@ucap/core": "~0.0.10",
|
||||
"@ucap/logger": "~0.0.12",
|
||||
"@ucap/native": "~0.0.6",
|
||||
"@ucap/native-browser": "~0.0.5",
|
||||
|
@ -46,7 +46,7 @@
|
|||
"@ucap/ng-api-message": "~0.0.1",
|
||||
"@ucap/ng-api-prompt": "~0.0.1",
|
||||
"@ucap/ng-api-public": "~0.0.1",
|
||||
"@ucap/ng-core": "~0.0.1",
|
||||
"@ucap/ng-core": "~0.0.7",
|
||||
"@ucap/ng-logger": "~0.0.2",
|
||||
"@ucap/ng-i18n": "~0.0.6",
|
||||
"@ucap/ng-native": "~0.0.1",
|
||||
|
@ -69,16 +69,16 @@
|
|||
"@ucap/ng-protocol-sync": "~0.0.3",
|
||||
"@ucap/ng-protocol-umg": "~0.0.3",
|
||||
"@ucap/ng-store-authentication": "~0.0.11",
|
||||
"@ucap/ng-store-chat": "~0.0.13",
|
||||
"@ucap/ng-store-chat": "~0.0.16",
|
||||
"@ucap/ng-store-group": "~0.0.14",
|
||||
"@ucap/ng-store-organization": "~0.0.8",
|
||||
"@ucap/ng-web-socket": "~0.0.2",
|
||||
"@ucap/ng-web-storage": "~0.0.3",
|
||||
"@ucap/ng-ui": "~0.0.19",
|
||||
"@ucap/ng-ui-organization": "~0.0.55",
|
||||
"@ucap/ng-ui-authentication": "~0.0.24",
|
||||
"@ucap/ng-ui-organization": "~0.0.83",
|
||||
"@ucap/ng-ui-authentication": "~0.0.25",
|
||||
"@ucap/ng-ui-group": "~0.0.33",
|
||||
"@ucap/ng-ui-chat": "~0.0.9",
|
||||
"@ucap/ng-ui-chat": "~0.0.12",
|
||||
"@ucap/ng-ui-material": "~0.0.4",
|
||||
"@ucap/ng-ui-skin-default": "~0.0.1",
|
||||
"@ucap/pi": "~0.0.5",
|
||||
|
@ -93,12 +93,12 @@
|
|||
"@ucap/protocol-option": "~0.0.7",
|
||||
"@ucap/protocol-ping": "~0.0.6",
|
||||
"@ucap/protocol-query": "~0.0.5",
|
||||
"@ucap/protocol-room": "~0.0.5",
|
||||
"@ucap/protocol-room": "~0.0.6",
|
||||
"@ucap/protocol-service": "~0.0.4",
|
||||
"@ucap/protocol-status": "~0.0.5",
|
||||
"@ucap/protocol-sync": "~0.0.4",
|
||||
"@ucap/protocol-umg": "~0.0.5",
|
||||
"@ucap/ui-scss": "~0.0.4",
|
||||
"@ucap/ui-scss": "~0.0.5",
|
||||
"@ucap/web-socket": "~0.0.10",
|
||||
"@ucap/web-storage": "~0.0.9",
|
||||
"autolinker": "^3.13.0",
|
||||
|
|
|
@ -15,6 +15,7 @@ import { AppAuthenticationService } from './services/app-authentication.service'
|
|||
import { AppNativeService } from './services/app-native.service';
|
||||
import { AppService } from './services/app.service';
|
||||
import { AppChatService } from './services/app-chat.service';
|
||||
import { AppFileService } from './services/app-file.service';
|
||||
|
||||
const GUARDS = [AppAuthenticationGuard];
|
||||
const RESOLVERS = [AppSessionResolver];
|
||||
|
@ -22,6 +23,7 @@ const SERVICES = [
|
|||
AppService,
|
||||
AppAuthenticationService,
|
||||
AppNativeService,
|
||||
AppFileService,
|
||||
AppChatService
|
||||
];
|
||||
|
||||
|
|
|
@ -22,11 +22,11 @@ $typography: mat-typography-config(
|
|||
// 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);
|
||||
$lgRed-app-primary: mat-palette($ucap-color-primary);
|
||||
$lgRed-app-accent: mat-palette($ucap-color-accent, 700);
|
||||
|
||||
// The warn palette is optional (defaults to red).
|
||||
$lgRed-app-warn: mat-palette($lg-red);
|
||||
$lgRed-app-warn: mat-palette($ucap-color-warn, 500);
|
||||
|
||||
// Create the theme object (a Sass map containing all of the palettes).
|
||||
$lgRed-app-theme: mat-light-theme(
|
||||
|
|
|
@ -11,6 +11,7 @@ import { MatSidenav } from '@angular/material/sidenav';
|
|||
import { LogService } from '@ucap/ng-logger';
|
||||
|
||||
import { AppSelector } from '@app/store/state';
|
||||
import { AppChatService } from '@app/services/app-chat.service';
|
||||
|
||||
const NAVS = ['group', 'chat', 'organization', 'message'];
|
||||
|
||||
|
@ -37,6 +38,7 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
|
|||
constructor(
|
||||
private router: Router,
|
||||
private store: Store<any>,
|
||||
private appChatService: AppChatService,
|
||||
private logService: LogService
|
||||
) {}
|
||||
|
||||
|
@ -175,7 +177,7 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
|
|||
break;
|
||||
case 'CAHT_NEW_ADD':
|
||||
{
|
||||
this.logService.debug('CAHT_NEW_ADD');
|
||||
this.appChatService.newOpenRoomDialog();
|
||||
}
|
||||
break;
|
||||
case 'CHAT_NEW_TIMER_ADD':
|
||||
|
|
|
@ -17,6 +17,9 @@ import { UiModule } from '@ucap/ng-ui';
|
|||
|
||||
import { COMPONENTS } from './components';
|
||||
import { DIALOGS } from './dialogs';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -30,13 +33,21 @@ import { DIALOGS } from './dialogs';
|
|||
MatSidenavModule,
|
||||
MatTabsModule,
|
||||
MatToolbarModule,
|
||||
MatSelectModule,
|
||||
|
||||
PerfectScrollbarModule,
|
||||
|
||||
I18nModule,
|
||||
UiModule
|
||||
],
|
||||
exports: [...COMPONENTS, ...DIALOGS],
|
||||
declarations: [...COMPONENTS, ...DIALOGS],
|
||||
entryComponents: [...DIALOGS]
|
||||
entryComponents: [...DIALOGS],
|
||||
providers: [
|
||||
{
|
||||
provide: UCAP_I18N_NAMESPACE,
|
||||
useValue: ['chat', 'common']
|
||||
}
|
||||
]
|
||||
})
|
||||
export class AppLayoutsModule {}
|
||||
|
|
|
@ -7,9 +7,11 @@ $login-bg-h: 100/1080;
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
// box-sizing: border-box;
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
background-color: $bg-gray;
|
||||
background-image: url(../../../../assets/images/bg/bg_login_circle_square01.svg),
|
||||
|
|
|
@ -15,6 +15,7 @@ import { AppChatRoutingPageModule } from './chat-routing.page.module';
|
|||
|
||||
import { UiModule } from '@ucap/ng-ui';
|
||||
import { COMPONENTS } from './components';
|
||||
import { UCAP_I18N_NAMESPACE, I18nModule } from '@ucap/ng-i18n';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -30,9 +31,16 @@ import { COMPONENTS } from './components';
|
|||
AppChatSectionModule,
|
||||
AppChatRoutingPageModule,
|
||||
|
||||
I18nModule,
|
||||
UiModule
|
||||
],
|
||||
declarations: [...COMPONENTS],
|
||||
entryComponents: []
|
||||
entryComponents: [],
|
||||
providers: [
|
||||
{
|
||||
provide: UCAP_I18N_NAMESPACE,
|
||||
useValue: ['chat', 'common']
|
||||
}
|
||||
]
|
||||
})
|
||||
export class AppChatPageModule {}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<div fxFlexFill class="sidenav-container">
|
||||
<div class="chat-header">
|
||||
<h3>대화</h3>
|
||||
<h3>{{ 'label.chat' | ucapI18n }}</h3>
|
||||
<div class="chat-menu-btn">
|
||||
<button
|
||||
mat-icon-button
|
||||
|
|
|
@ -44,6 +44,9 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
|
|||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject<boolean>();
|
||||
|
||||
// language setting
|
||||
this.i18nService.setDefaultNamespace('chat');
|
||||
|
||||
this.store
|
||||
.pipe(takeUntil(this.ngOnDestroySubject), select(RoomSelector.rooms))
|
||||
.subscribe((rooms) => {
|
||||
|
@ -67,8 +70,8 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
|
|||
ConfirmDialogResult
|
||||
>(ConfirmDialogComponent, {
|
||||
data: {
|
||||
title: this.i18nService.t('room.dialog.titleExitFromRoom'),
|
||||
html: this.i18nService.t('room.dialog.confirmExitFromRoom')
|
||||
title: this.i18nService.t('dialog.title.exitFromRoom'),
|
||||
html: this.i18nService.t('dialog.confirmExitFromRoom')
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="extra-box" fxFlex="0 0 50px">
|
||||
<app-organization-search-for-tenant (changed)="onKeyDownSearch($event)">
|
||||
<app-organization-search-for-tenant [(searchData)]="companySearchData">
|
||||
</app-organization-search-for-tenant>
|
||||
</div>
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
|||
<app-sections-group-list
|
||||
#sectionGroupList
|
||||
fxFlexFill
|
||||
[searchObj]="searchObj"
|
||||
[searchData]="companySearchData"
|
||||
[showType]="showType"
|
||||
></app-sections-group-list>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Subscription, of } from 'rxjs';
|
||||
import { take, map, catchError } from 'rxjs/operators';
|
||||
import { of, Subject } from 'rxjs';
|
||||
import { take, map, catchError, takeUntil } from 'rxjs/operators';
|
||||
|
||||
import {
|
||||
Component,
|
||||
|
@ -14,14 +14,14 @@ import { Store } from '@ngrx/store';
|
|||
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
|
||||
import { ParamsUtil } from '@ucap/ng-core';
|
||||
import { LogService } from '@ucap/ng-logger';
|
||||
import { GroupActions } from '@ucap/ng-store-group';
|
||||
|
||||
import { SelectUserDialogType } from '@app/types';
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
|
||||
import { CreateDialogComponent } from '@app/sections/group/dialogs/create.dialog.component';
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
import { ListSectionComponent } from '@app/sections/group/components/list.section.component';
|
||||
import { SearchData } from '@app/ucap/organization/models/search-data';
|
||||
import { QueryParams } from '@app/pages/organization/types/params.type';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pages-group-sidenav',
|
||||
|
@ -32,14 +32,19 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
|
|||
@ViewChild('sectionGroupList', { static: false })
|
||||
sectionGroupList: ListSectionComponent;
|
||||
|
||||
searchObj: any = {
|
||||
isShowSearch: false,
|
||||
companyCode: '',
|
||||
searchWord: ''
|
||||
};
|
||||
set companySearchData(searchData: SearchData) {
|
||||
this._companySearchData = searchData;
|
||||
}
|
||||
get companySearchData() {
|
||||
return this._companySearchData;
|
||||
}
|
||||
// tslint:disable-next-line: variable-name
|
||||
_companySearchData: SearchData;
|
||||
|
||||
showType: string;
|
||||
|
||||
private ngOnDestroySubject: Subject<boolean>;
|
||||
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private router: Router,
|
||||
|
@ -53,33 +58,14 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject<boolean>();
|
||||
|
||||
this.showType = 'ALL';
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {}
|
||||
|
||||
onClickFab(event: MouseEvent) {}
|
||||
onKeyDownSearch(params: {
|
||||
isShowSearch: boolean;
|
||||
companyCode: string;
|
||||
searchWord: string;
|
||||
}) {
|
||||
this.searchObj = {
|
||||
isShowSearch: params.isShowSearch,
|
||||
companyCode: params.companyCode,
|
||||
searchWord: params.searchWord
|
||||
};
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
onClickCancel() {
|
||||
this.searchObj = {
|
||||
isShowSearch: false,
|
||||
companyCode: '',
|
||||
searchWord: ''
|
||||
};
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
onClickGroupMenu(menuType: string) {
|
||||
switch (menuType) {
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
<div fxFlexFill>
|
||||
<app-sections-organization-member-list
|
||||
[searchData]="_searchData"
|
||||
></app-sections-organization-member-list>
|
||||
<div class="index-page-container" fxLayout="column">
|
||||
<!-- search start-->
|
||||
<div fxFlex="0 0 50px">
|
||||
<app-organization-search-for-tenant [(searchData)]="companySearchData">
|
||||
</app-organization-search-for-tenant>
|
||||
</div>
|
||||
<!-- search end-->
|
||||
<div class="member-list-body" fxFlex="1 1 auto">
|
||||
<app-sections-organization-member-list [searchData]="deptSearchData">
|
||||
</app-sections-organization-member-list>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
.index-page-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
|
@ -2,10 +2,14 @@ import { Subject } from 'rxjs';
|
|||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
import { Component, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { ActivatedRoute, Router, Params } from '@angular/router';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { ParamsUtil } from '@ucap/ng-core';
|
||||
|
||||
import { SearchData } from '@app/ucap/organization/models/search-data';
|
||||
|
||||
import { QueryParams } from '../types/params.type';
|
||||
|
||||
@Component({
|
||||
|
@ -14,12 +18,17 @@ import { QueryParams } from '../types/params.type';
|
|||
styleUrls: ['./index.page.component.scss']
|
||||
})
|
||||
export class IndexPageComponent implements OnInit, OnDestroy {
|
||||
set companySearchData(searchData: SearchData) {
|
||||
this._companySearchData = searchData;
|
||||
this.onChangedCompanySearch();
|
||||
}
|
||||
get companySearchData() {
|
||||
return this._companySearchData;
|
||||
}
|
||||
// tslint:disable-next-line: variable-name
|
||||
_searchData: {
|
||||
companyCode: string;
|
||||
searchWord: string;
|
||||
isSearch: boolean;
|
||||
};
|
||||
_companySearchData: SearchData;
|
||||
|
||||
deptSearchData: SearchData;
|
||||
|
||||
deptSeq: string;
|
||||
|
||||
|
@ -27,6 +36,7 @@ export class IndexPageComponent implements OnInit, OnDestroy {
|
|||
|
||||
constructor(
|
||||
private store: Store<any>,
|
||||
private router: Router,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private changeDetectorRef: ChangeDetectorRef
|
||||
) {}
|
||||
|
@ -37,14 +47,25 @@ export class IndexPageComponent implements OnInit, OnDestroy {
|
|||
this.activatedRoute.queryParams
|
||||
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||
.subscribe((params) => {
|
||||
console.log('activatedRoute.queryParams');
|
||||
if (!!params) {
|
||||
const companyCode = params[QueryParams.DEPT_SEQ];
|
||||
console.log('activatedRoute.queryParams', companyCode);
|
||||
this._searchData = {
|
||||
companyCode,
|
||||
searchWord: '',
|
||||
isSearch: false
|
||||
const deptSeq = params[QueryParams.DEPT_SEQ];
|
||||
const companyCode = params[QueryParams.COMPANY_CODE];
|
||||
const searchWord = params[QueryParams.SEARCH_WORD];
|
||||
const bySearch = ParamsUtil.getParameter<boolean>(
|
||||
params,
|
||||
QueryParams.BY_SEARCH,
|
||||
false
|
||||
);
|
||||
|
||||
this.deptSearchData = {
|
||||
deptSeq: bySearch ? undefined : deptSeq,
|
||||
companyCode: bySearch ? companyCode : undefined,
|
||||
searchWord: bySearch ? searchWord : undefined,
|
||||
bySearch
|
||||
};
|
||||
|
||||
this._companySearchData = {
|
||||
...this.deptSearchData
|
||||
};
|
||||
}
|
||||
});
|
||||
|
@ -55,4 +76,23 @@ export class IndexPageComponent implements OnInit, OnDestroy {
|
|||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
||||
onChangedCompanySearch() {
|
||||
const queryParams: Params = {};
|
||||
queryParams[QueryParams.COMPANY_CODE] = this._companySearchData.companyCode;
|
||||
queryParams[QueryParams.SEARCH_WORD] = this._companySearchData.searchWord;
|
||||
queryParams[QueryParams.BY_SEARCH] = String(true);
|
||||
|
||||
this.router.navigate(
|
||||
[
|
||||
'organization',
|
||||
{
|
||||
outlets: { content: 'index' }
|
||||
}
|
||||
],
|
||||
{
|
||||
queryParams
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ export class SidenavPageComponent implements OnInit, OnDestroy {
|
|||
onClickedTree(node: DeptInfo) {
|
||||
const queryParams: Params = {};
|
||||
queryParams[QueryParams.DEPT_SEQ] = String(node.seq);
|
||||
queryParams[QueryParams.BY_SEARCH] = String(false);
|
||||
|
||||
this.router.navigate(
|
||||
[
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
export enum QueryParams {
|
||||
DEPT_SEQ = 'dept_seq'
|
||||
DEPT_SEQ = 'dept_seq',
|
||||
COMPANY_CODE = 'company_code',
|
||||
SEARCH_WORD = 'search_word',
|
||||
BY_SEARCH = 'by_search'
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
|
||||
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
|
||||
|
@ -20,6 +20,7 @@ import { MatSelectModule } from '@angular/material/select';
|
|||
import { MatTreeModule } from '@angular/material/tree';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { MatStepperModule } from '@angular/material/stepper';
|
||||
|
||||
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
||||
|
||||
|
@ -27,12 +28,16 @@ import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
|
|||
import { UiModule } from '@ucap/ng-ui';
|
||||
import { ChatUiModule } from '@ucap/ng-ui-chat';
|
||||
import { AppChatModule } from '@app/ucap/chat/chat.module';
|
||||
import { AppLayoutsModule } from '@app/layouts/layouts.module';
|
||||
import { AppGroupSectionModule } from '../group/group.section.module';
|
||||
|
||||
import { COMPONENTS } from './components';
|
||||
import { DIALOGS } from './dialogs';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
FlexLayoutModule,
|
||||
|
||||
|
@ -50,6 +55,7 @@ import { COMPONENTS } from './components';
|
|||
MatRippleModule,
|
||||
MatTreeModule,
|
||||
MatTooltipModule,
|
||||
MatStepperModule,
|
||||
|
||||
PerfectScrollbarModule,
|
||||
ScrollingModule,
|
||||
|
@ -57,12 +63,14 @@ import { COMPONENTS } from './components';
|
|||
I18nModule,
|
||||
UiModule,
|
||||
|
||||
AppLayoutsModule,
|
||||
AppGroupSectionModule,
|
||||
ChatUiModule,
|
||||
AppChatModule
|
||||
],
|
||||
exports: [...COMPONENTS],
|
||||
declarations: [...COMPONENTS],
|
||||
entryComponents: [],
|
||||
exports: [...COMPONENTS, ...DIALOGS],
|
||||
declarations: [...COMPONENTS, ...DIALOGS],
|
||||
entryComponents: [...DIALOGS],
|
||||
providers: [
|
||||
{
|
||||
provide: UCAP_I18N_NAMESPACE,
|
||||
|
|
|
@ -1,16 +1,23 @@
|
|||
<ng-container [ngSwitch]="selectorType">
|
||||
<app-chat-selector-sticker *ngSwitchCase="'STICKER'">
|
||||
<app-chat-selector-sticker
|
||||
*ngSwitchCase="SelectorType.STICKER"
|
||||
(selectedSticker)="onSelectedSticker($event)"
|
||||
(closeSticker)="selectorType = SelectorType.EMPTY"
|
||||
>
|
||||
</app-chat-selector-sticker>
|
||||
|
||||
<app-chat-selector-translation
|
||||
*ngSwitchCase="'TRANSLATION'"
|
||||
*ngSwitchCase="SelectorType.TRANSLATION"
|
||||
></app-chat-selector-translation>
|
||||
|
||||
<app-chat-selector-file-upload *ngSwitchCase="'FILEUPLOAD'">
|
||||
<app-chat-selector-file-upload
|
||||
#fileUploadSelector
|
||||
*ngSwitchCase="SelectorType.FILEUPLOAD"
|
||||
>
|
||||
</app-chat-selector-file-upload>
|
||||
|
||||
<app-chat-selector-email-send
|
||||
*ngSwitchCase="'EMAILSENDER'"
|
||||
*ngSwitchCase="SelectorType.EMAILSENDER"
|
||||
></app-chat-selector-email-send>
|
||||
</ng-container>
|
||||
|
||||
|
@ -23,13 +30,22 @@
|
|||
>
|
||||
<textarea
|
||||
matInput
|
||||
#replyInput
|
||||
#messageInput
|
||||
placeholder=""
|
||||
name="message"
|
||||
[matTextareaAutosize]="true"
|
||||
[matAutosizeMaxRows]="20"
|
||||
(keydown)="onKeydown($event)"
|
||||
></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
<input
|
||||
type="file"
|
||||
#fileInput
|
||||
style="display: none;"
|
||||
multiple
|
||||
(change)="onChangeFileInput()"
|
||||
/>
|
||||
</div>
|
||||
<div class="button-area">
|
||||
<button
|
||||
|
@ -37,8 +53,7 @@
|
|||
aria-label="attachFile"
|
||||
matTooltipPosition="above"
|
||||
matTooltip="{{ 'label.attachFile' | ucapI18n }}"
|
||||
matTool
|
||||
(click)="onOpenSelector('FILEUPLOAD')"
|
||||
(click)="clearSelector(); fileInput.click()"
|
||||
>
|
||||
첨부파일
|
||||
</button>
|
||||
|
@ -47,7 +62,7 @@
|
|||
aria-label="attachImage"
|
||||
matTooltipPosition="above"
|
||||
matTooltip="{{ 'label.attachImage' | ucapI18n }}"
|
||||
(click)="onOpenSelector('')"
|
||||
(click)="onOpenSelector(SelectorType.EMPTY)"
|
||||
>
|
||||
이미지
|
||||
</button>
|
||||
|
@ -56,7 +71,7 @@
|
|||
aria-label="screenshot"
|
||||
matTooltipPosition="above"
|
||||
matTooltip="{{ 'label.screenshot' | ucapI18n }}"
|
||||
(click)="onOpenSelector('')"
|
||||
(click)="onOpenSelector(SelectorType.EMPTY)"
|
||||
>
|
||||
캡쳐 화면 전송
|
||||
</button>
|
||||
|
@ -65,7 +80,7 @@
|
|||
aria-label="imoticon"
|
||||
matTooltipPosition="above"
|
||||
matTooltip="{{ 'label.imoticon' | ucapI18n }}"
|
||||
(click)="onOpenSelector('STICKER')"
|
||||
(click)="onOpenSelector(SelectorType.STICKER)"
|
||||
>
|
||||
이모티콘
|
||||
</button>
|
||||
|
@ -74,7 +89,7 @@
|
|||
aria-label="emailSend"
|
||||
matTooltipPosition="above"
|
||||
matTooltip="{{ 'label.emailSend' | ucapI18n }}"
|
||||
(click)="onOpenSelector('EMAILSENDER')"
|
||||
(click)="onOpenSelector(SelectorType.EMAILSENDER)"
|
||||
>
|
||||
대화내용 메일 전송
|
||||
</button>
|
||||
|
@ -83,7 +98,7 @@
|
|||
aria-label="translation"
|
||||
matTooltipPosition="above"
|
||||
matTooltip="{{ 'label.translation' | ucapI18n }}"
|
||||
(click)="onOpenSelector('TRANSLATION')"
|
||||
(click)="onOpenSelector(SelectorType.TRANSLATION)"
|
||||
>
|
||||
대화내용 번역
|
||||
</button>
|
||||
|
@ -92,7 +107,7 @@
|
|||
aria-label="gams"
|
||||
matTooltipPosition="above"
|
||||
matTooltip="{{ 'label.gams' | ucapI18n }}"
|
||||
(click)="onOpenSelector('')"
|
||||
(click)="onOpenSelector(SelectorType.EMPTY)"
|
||||
>
|
||||
+GAMS
|
||||
</button>
|
||||
|
@ -101,7 +116,7 @@
|
|||
color="accent"
|
||||
aria-label="send"
|
||||
matTooltip="{{ 'label.send' | ucapI18n }}"
|
||||
(click)="onOpenSelector('')"
|
||||
(click)="send()"
|
||||
>
|
||||
<mat-icon>send</mat-icon>
|
||||
</button>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { Subject, of, Observable, forkJoin } from 'rxjs';
|
||||
import { takeUntil, map, catchError, take } from 'rxjs/operators';
|
||||
|
||||
import {
|
||||
Component,
|
||||
|
@ -7,12 +7,61 @@ import {
|
|||
OnDestroy,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Input
|
||||
Input,
|
||||
ViewChild,
|
||||
ElementRef
|
||||
} from '@angular/core';
|
||||
import { Store, select } from '@ngrx/store';
|
||||
import { RoomInfo } from '@ucap/protocol-room';
|
||||
import { Dictionary } from '@ngrx/entity';
|
||||
|
||||
import { RoomInfo } from '@ucap/protocol-room';
|
||||
import {
|
||||
SendRequest as SendEventRequest,
|
||||
EventType
|
||||
} from '@ucap/protocol-event';
|
||||
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||
|
||||
import { ChattingActions } from '@ucap/ng-store-chat';
|
||||
import {
|
||||
LoginSelector,
|
||||
ConfigurationSelector
|
||||
} from '@ucap/ng-store-authentication';
|
||||
import { StickerFilesInfo, KEY_STICKER_HISTORY } from '@ucap/ng-core';
|
||||
import {
|
||||
AlertDialogComponent,
|
||||
AlertDialogData,
|
||||
AlertDialogResult
|
||||
} from '@ucap/ng-ui';
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
import { LogService } from '@ucap/ng-logger';
|
||||
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import {
|
||||
TranslationSaveResponse,
|
||||
MassTalkSaveRequest,
|
||||
FileTalkSaveResponse,
|
||||
FileTalkSaveRequest
|
||||
} from '@ucap/api-common';
|
||||
import { environment } from '@environments';
|
||||
import { LocalStorageService } from '@ucap/ng-web-storage';
|
||||
import { CommonApiService } from '@ucap/ng-api-common';
|
||||
import { LoginSession } from '@app/models/login-session';
|
||||
import { AppAuthenticationService } from '@app/services/app-authentication.service';
|
||||
import { StatusCode, FileUploadItem } from '@ucap/api';
|
||||
import { AppFileService } from '@app/services/app-file.service';
|
||||
import { VersionInfo2Response } from '@ucap/api-public';
|
||||
import { FileUploadSelectorComponent } from '@app/ucap/chat/components/file-upload.selector.component';
|
||||
import { FileUtil } from '@ucap/core';
|
||||
import { AppChatService } from '@app/services/app-chat.service';
|
||||
|
||||
export enum SelectorType {
|
||||
EMPTY = '',
|
||||
STICKER = 'STICKER',
|
||||
TRANSLATION = 'TRANSLATION',
|
||||
FILEUPLOAD = 'FILEUPLOAD',
|
||||
EMAILSENDER = 'EMAILSENDER'
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-sections-chat-form',
|
||||
templateUrl: './form.section.component.html',
|
||||
|
@ -30,19 +79,70 @@ export class FormSectionComponent implements OnInit, OnDestroy {
|
|||
// tslint:disable-next-line: variable-name
|
||||
_roomId: string;
|
||||
|
||||
versionInfo2Res: VersionInfo2Response;
|
||||
loginSession: LoginSession;
|
||||
loginRes: LoginResponse;
|
||||
|
||||
currentRoomInfo: RoomInfo;
|
||||
|
||||
selectorType = '';
|
||||
selectorType: SelectorType = SelectorType.EMPTY;
|
||||
|
||||
/** About Sticker */
|
||||
selectedSticker: StickerFilesInfo;
|
||||
|
||||
/** About Translation */
|
||||
translationSimpleview = false;
|
||||
translationPreview = false;
|
||||
destLocale = 'en'; // default English :: en
|
||||
translationPreviewInfo: {
|
||||
previewInfo: TranslationSaveResponse | null;
|
||||
translationType: EventType.Translation | EventType.MassTranslation;
|
||||
};
|
||||
|
||||
@ViewChild('messageInput', { static: false })
|
||||
messageInput: ElementRef<HTMLTextAreaElement>;
|
||||
@ViewChild('fileInput', { static: false })
|
||||
fileInput: ElementRef<HTMLInputElement>;
|
||||
@ViewChild('fileUploadSelector', { static: false })
|
||||
fileUploadSelector: FileUploadSelectorComponent;
|
||||
|
||||
SelectorType = SelectorType;
|
||||
|
||||
private ngOnDestroySubject: Subject<boolean>;
|
||||
constructor(
|
||||
private appFileService: AppFileService,
|
||||
private appChatService: AppChatService,
|
||||
private store: Store<any>,
|
||||
private i18nService: I18nService,
|
||||
private dialog: MatDialog,
|
||||
private localStorageService: LocalStorageService,
|
||||
private logService: LogService,
|
||||
private appAuthenticationService: AppAuthenticationService,
|
||||
private commonApiService: CommonApiService,
|
||||
private changeDetectorRef: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject<boolean>();
|
||||
|
||||
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.appAuthenticationService
|
||||
.getLoginSession$()
|
||||
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||
.subscribe((loginSession) => (this.loginSession = loginSession));
|
||||
|
||||
this.store
|
||||
.pipe(
|
||||
takeUntil(this.ngOnDestroySubject),
|
||||
|
@ -65,7 +165,176 @@ export class FormSectionComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
onOpenSelector(type: string): void {
|
||||
/** About Selector */
|
||||
onOpenSelector(type: SelectorType): void {
|
||||
this.selectorType = type;
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
clearSelector(): void {
|
||||
this.selectorType = SelectorType.EMPTY;
|
||||
this.selectedSticker = null;
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
/** Element Handling */
|
||||
focus(clearField: boolean = true): void {
|
||||
if (!!this.messageInput) {
|
||||
if (!!clearField) {
|
||||
this.messageInput.nativeElement.value = '';
|
||||
|
||||
this.clearSelector();
|
||||
}
|
||||
this.messageInput.nativeElement.focus();
|
||||
}
|
||||
}
|
||||
|
||||
onChangeFileInput(): void {
|
||||
const self = this;
|
||||
const fileList = this.fileInput.nativeElement.files;
|
||||
|
||||
this.appFileService
|
||||
.validUploadFile(fileList, this.versionInfo2Res?.fileAllowSize)
|
||||
.then((result) => {
|
||||
if (!result) {
|
||||
self.fileInput.nativeElement.value = '';
|
||||
return;
|
||||
} else {
|
||||
// selector open
|
||||
self.onOpenSelector(SelectorType.FILEUPLOAD);
|
||||
|
||||
// FileuploadItem Init. & FileSelector Init.
|
||||
const fileUploadItems = FileUploadItem.fromFiles(fileList);
|
||||
if (!!self.fileUploadSelector) {
|
||||
self.fileUploadSelector.onFileSelected(fileUploadItems);
|
||||
}
|
||||
self.fileInput.nativeElement.value = '';
|
||||
|
||||
// File Upload..
|
||||
self.appChatService
|
||||
.sendMessageOfAttachFile(
|
||||
self.loginRes,
|
||||
self.loginSession.deviceType,
|
||||
self.currentRoomInfo.roomId,
|
||||
fileUploadItems
|
||||
)
|
||||
.then((success) => {
|
||||
if (!!success) {
|
||||
self.clearSelector();
|
||||
if (!!self.fileUploadSelector) {
|
||||
self.fileUploadSelector.onUploadComplete();
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
alert(err);
|
||||
if (!!self.fileUploadSelector) {
|
||||
self.fileUploadSelector.onUploadComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
self.fileInput.nativeElement.value = '';
|
||||
self.logService.error(`validUploadFile ${err}`);
|
||||
});
|
||||
}
|
||||
|
||||
onKeydown(event: KeyboardEvent) {
|
||||
if (event.key === 'PageUp' || event.key === 'PageDown') {
|
||||
event.preventDefault();
|
||||
return false;
|
||||
} else if (event.key === 'Enter' && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
this.send();
|
||||
}
|
||||
}
|
||||
|
||||
onSelectedSticker(stickerInfo: StickerFilesInfo) {
|
||||
this.selectedSticker = stickerInfo;
|
||||
this.focus(false);
|
||||
}
|
||||
|
||||
async send() {
|
||||
const roomId = this.currentRoomInfo.roomId;
|
||||
const userSeq = this.loginRes.userSeq;
|
||||
let message = this.messageInput.nativeElement.value;
|
||||
|
||||
if (!!message || message.trim().length > 0) {
|
||||
message = message.replace(/\t/g, ' ');
|
||||
}
|
||||
|
||||
// Empty Check.
|
||||
if (!this.selectedSticker) {
|
||||
try {
|
||||
if (!message || message.trim().length === 0) {
|
||||
const result = await this.dialog.open<
|
||||
AlertDialogComponent,
|
||||
AlertDialogData,
|
||||
AlertDialogResult
|
||||
>(AlertDialogComponent, {
|
||||
data: {
|
||||
title: this.i18nService.t('errors.label'),
|
||||
message: this.i18nService.t('errors.inputChatMessage')
|
||||
},
|
||||
panelClass: ''
|
||||
});
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
this.logService.debug(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
this.selectorType === SelectorType.TRANSLATION &&
|
||||
this.destLocale.trim().length > 0
|
||||
) {
|
||||
/** CASE : Translation */
|
||||
// 번역할 대화 없이 스티커만 전송할 경우.
|
||||
if (!message || message.trim().length === 0) {
|
||||
this.appChatService.sendMessageOfSticker(
|
||||
userSeq,
|
||||
roomId,
|
||||
this.selectedSticker,
|
||||
message
|
||||
);
|
||||
|
||||
this.clearSelector();
|
||||
} else {
|
||||
this.appChatService.sendMessageOfTranslate(
|
||||
this.loginRes,
|
||||
this.loginSession.deviceType,
|
||||
this.destLocale,
|
||||
roomId,
|
||||
message,
|
||||
this.selectedSticker
|
||||
);
|
||||
}
|
||||
} else if (!!this.selectedSticker) {
|
||||
/** CASE : Sticker */
|
||||
this.appChatService.sendMessageOfSticker(
|
||||
userSeq,
|
||||
roomId,
|
||||
this.selectedSticker,
|
||||
message
|
||||
);
|
||||
|
||||
this.clearSelector();
|
||||
} else if (
|
||||
message.trim().length > environment.productConfig.chat.masstextLength
|
||||
) {
|
||||
/** CASE : MASS TEXT */
|
||||
this.appChatService.sendMessageOfMassText(
|
||||
this.loginRes,
|
||||
this.loginSession.deviceType,
|
||||
roomId,
|
||||
message
|
||||
);
|
||||
} else {
|
||||
/** CASE : Normal Text */
|
||||
this.appChatService.sendMessageOfNormal(userSeq, roomId, message);
|
||||
}
|
||||
|
||||
this.focus();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -144,6 +144,14 @@ export class ListSectionComponent implements OnInit, OnDestroy {
|
|||
private ngZone: NgZone,
|
||||
private logService: LogService
|
||||
) {
|
||||
// default image setting
|
||||
this.defaultProfileImage = this.appChatService.defaultProfileImage;
|
||||
this.defaultProfileImageMulti = this.appChatService.defaultProfileImage;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject<boolean>();
|
||||
|
||||
// language setting
|
||||
this.translateService.setDefaultLang(this.i18nService.currentLng);
|
||||
this.translateService.use(this.i18nService.currentLng);
|
||||
|
@ -153,14 +161,6 @@ export class ListSectionComponent implements OnInit, OnDestroy {
|
|||
);
|
||||
this.i18nService.setDefaultNamespace('chat');
|
||||
|
||||
// default image setting
|
||||
this.defaultProfileImage = this.appChatService.defaultProfileImage;
|
||||
this.defaultProfileImageMulti = this.appChatService.defaultProfileImage;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject<boolean>();
|
||||
|
||||
this.store
|
||||
.pipe(
|
||||
takeUntil(this.ngOnDestroySubject),
|
||||
|
@ -176,15 +176,20 @@ export class ListSectionComponent implements OnInit, OnDestroy {
|
|||
this.loginRes = loginRes;
|
||||
});
|
||||
|
||||
this.store
|
||||
.pipe(takeUntil(this.ngOnDestroySubject), select(RoomSelector.rooms))
|
||||
.subscribe((rooms) => {
|
||||
rooms = (rooms || []).filter((info) => info.isJoinRoom);
|
||||
combineLatest([
|
||||
this.store.pipe(select(RoomSelector.rooms)),
|
||||
this.store.pipe(select(RoomSelector.standbyRooms))
|
||||
])
|
||||
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||
.subscribe(([rooms, standbyRooms]) => {
|
||||
rooms = (rooms || []).filter((info) => {
|
||||
return (
|
||||
info.isJoinRoom &&
|
||||
!standbyRooms.find((standbyRoom) => standbyRoom === info.roomId)
|
||||
);
|
||||
});
|
||||
this.roomList = rooms;
|
||||
|
||||
// groupping.
|
||||
this.initGroup();
|
||||
|
||||
this.changeDetectorRef.detectChanges();
|
||||
});
|
||||
|
||||
|
@ -218,41 +223,6 @@ export class ListSectionComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
initGroup() {
|
||||
this.roomGroup = [];
|
||||
|
||||
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.roomGroup.findIndex(
|
||||
(info) => info.division === division
|
||||
);
|
||||
if (index > -1) {
|
||||
this.roomGroup[index] = {
|
||||
...this.roomGroup[index],
|
||||
roomList: [...this.roomGroup[index].roomList, roomInfo]
|
||||
};
|
||||
} else {
|
||||
this.roomGroup.push({
|
||||
division,
|
||||
roomList: [roomInfo]
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getRoomName(roomInfo: RoomInfo): string {
|
||||
if (!roomInfo) {
|
||||
return '';
|
||||
|
@ -392,8 +362,8 @@ export class ListSectionComponent implements OnInit, OnDestroy {
|
|||
ConfirmDialogResult
|
||||
>(ConfirmDialogComponent, {
|
||||
data: {
|
||||
title: this.i18nService.t('room.dialog.titleExitFromRoom'),
|
||||
html: this.i18nService.t('room.dialog.confirmExitFromRoom')
|
||||
title: this.i18nService.t('dialog.title.exitFromRoom'),
|
||||
html: this.i18nService.t('dialog.confirmExitFromRoom')
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
<app-chat-message-box
|
||||
*ngFor="let event of eventList"
|
||||
[message]="event"
|
||||
[roomInfo]="currentRoomInfo"
|
||||
[isMe]="event.senderSeq + '' === loginRes?.userSeq + ''"
|
||||
[senderInfo]="getSenderInfo(event.senderSeq)"
|
||||
[defaultProfileImage]="defaultProfileImage"
|
||||
|
|
|
@ -21,7 +21,8 @@ import { Chatting } from '@ucap/ng-store-chat/lib/store/chatting/state';
|
|||
import { ChattingSelector, RoomSelector } from '@ucap/ng-store-chat';
|
||||
import {
|
||||
UserInfo as RoomUserInfo,
|
||||
UserInfoShort as RoomUserInfoShort
|
||||
UserInfoShort as RoomUserInfoShort,
|
||||
RoomInfo
|
||||
} from '@ucap/protocol-room';
|
||||
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||
import {
|
||||
|
@ -31,6 +32,7 @@ import {
|
|||
import { AppChatService } from '@app/services/app-chat.service';
|
||||
import { VersionInfo2Response } from '@ucap/api-public';
|
||||
import moment from 'moment';
|
||||
import { Dictionary } from '@ngrx/entity';
|
||||
|
||||
@Component({
|
||||
selector: 'app-sections-chat-message',
|
||||
|
@ -59,6 +61,7 @@ export class MessageSectionComponent implements OnInit, OnDestroy {
|
|||
|
||||
roomIdSubject = new Subject<string>();
|
||||
|
||||
currentRoomInfo: RoomInfo;
|
||||
chatting$: Observable<Chatting>;
|
||||
roomUsers: RoomUserInfoShort[] = [];
|
||||
// eventList$: Observable<Info<EventJson>[]>;
|
||||
|
@ -91,6 +94,19 @@ export class MessageSectionComponent implements OnInit, OnDestroy {
|
|||
.subscribe((loginRes) => {
|
||||
this.loginRes = loginRes;
|
||||
});
|
||||
|
||||
this.store
|
||||
.pipe(
|
||||
takeUntil(this.ngOnDestroySubject),
|
||||
select(
|
||||
(state: any) => state.chat.room.rooms.entities as Dictionary<RoomInfo>
|
||||
)
|
||||
)
|
||||
.subscribe((rooms) => {
|
||||
if (!!this.roomId) {
|
||||
this.currentRoomInfo = rooms[this.roomId];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initializeRoomData() {
|
||||
|
|
86
src/app/sections/chat/dialogs/create.dialog.component.html
Normal file
86
src/app/sections/chat/dialogs/create.dialog.component.html
Normal file
|
@ -0,0 +1,86 @@
|
|||
<div class="dialog-container">
|
||||
<app-layouts-default-dialog
|
||||
[disableClose]="false"
|
||||
(closed)="onClosed($event)"
|
||||
>
|
||||
<div appLayoutsDefaultDialog="header">
|
||||
{{ 'dialog.title.newChatRoom' | ucapI18n }}
|
||||
</div>
|
||||
<div class="dialog-body" appLayoutsDefaultDialog="body">
|
||||
<mat-horizontal-stepper
|
||||
[linear]="true"
|
||||
#stepper
|
||||
[selectedIndex]="currentStep"
|
||||
>
|
||||
<mat-step label="Select room type">
|
||||
<div
|
||||
class="normal-room"
|
||||
[ngClass]="
|
||||
isTimer !== undefined && isTimer === false ? 'checked' : ''
|
||||
"
|
||||
(click)="isTimer = false"
|
||||
>
|
||||
<span class="title">{{ 'dialog.normalRoom' | ucapI18n }}</span>
|
||||
<img />
|
||||
<div
|
||||
class="description"
|
||||
[innerHTML]="
|
||||
'dialog.normalRoomDescription'
|
||||
| ucapI18n: { maxCount: maxChatRoomUser }
|
||||
"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="normal-room"
|
||||
[ngClass]="
|
||||
isTimer !== undefined && isTimer === true ? 'checked' : ''
|
||||
"
|
||||
(click)="isTimer = true"
|
||||
>
|
||||
<span class="title">{{ 'dialog.timerRoom' | ucapI18n }}</span>
|
||||
<img />
|
||||
<div
|
||||
class="description"
|
||||
[innerHTML]="
|
||||
'dialog.timerRoomDescription'
|
||||
| ucapI18n: { maxCount: maxChatRoomUser }
|
||||
"
|
||||
></div>
|
||||
</div>
|
||||
</mat-step>
|
||||
<mat-step label="Select users">
|
||||
<app-group-select-user
|
||||
[isDialog]="true"
|
||||
[checkable]="true"
|
||||
(changeUserList)="onChangeUserList($event)"
|
||||
></app-group-select-user>
|
||||
</mat-step>
|
||||
</mat-horizontal-stepper>
|
||||
</div>
|
||||
<div appLayoutsDefaultDialog="action">
|
||||
<button mat-button (click)="onCancel(stepper)">
|
||||
{{
|
||||
(stepper.selectedIndex === 0
|
||||
? 'dialog.button.cancel'
|
||||
: 'dialog.button.previous'
|
||||
) | ucapI18n
|
||||
}}
|
||||
</button>
|
||||
<button
|
||||
mat-button
|
||||
*ngIf="stepper.selectedIndex === 0"
|
||||
(click)="onConfirm(stepper)"
|
||||
>
|
||||
{{ 'dialog.button.selectRoomUser' | ucapI18n }}
|
||||
</button>
|
||||
<button
|
||||
mat-button
|
||||
*ngIf="stepper.selectedIndex === 1"
|
||||
(click)="onOpenRoom(stepper)"
|
||||
>
|
||||
{{ 'dialog.button.openRoom' | ucapI18n }}
|
||||
</button>
|
||||
</div>
|
||||
</app-layouts-default-dialog>
|
||||
</div>
|
|
@ -0,0 +1,9 @@
|
|||
.dialog-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.dialog-body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { DebugElement } from '@angular/core';
|
||||
|
||||
import { CreateDialogComponent } from './create.dialog.component';
|
||||
|
||||
describe('ucap::ui-organization::CreateChatDialogComponent', () => {
|
||||
let component: CreateDialogComponent;
|
||||
let fixture: ComponentFixture<CreateDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [CreateDialogComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CreateDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
132
src/app/sections/chat/dialogs/create.dialog.component.ts
Normal file
132
src/app/sections/chat/dialogs/create.dialog.component.ts
Normal file
|
@ -0,0 +1,132 @@
|
|||
import { Subject } from 'rxjs';
|
||||
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Inject,
|
||||
Input
|
||||
} from '@angular/core';
|
||||
|
||||
import {
|
||||
MatDialogRef,
|
||||
MAT_DIALOG_DATA,
|
||||
MatDialog
|
||||
} from '@angular/material/dialog';
|
||||
|
||||
import { UserInfo } from '@ucap/protocol-sync';
|
||||
import { UserInfoSS, UserInfoF, UserInfoDN } from '@ucap/protocol-query';
|
||||
import { UserInfo as RoomUserInfo } from '@ucap/protocol-room';
|
||||
import { MatStepper } from '@angular/material/stepper';
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
import { GroupActions } from '@ucap/ng-store-group';
|
||||
import {
|
||||
AlertDialogComponent,
|
||||
AlertDialogData,
|
||||
AlertDialogResult
|
||||
} from '@ucap/ng-ui';
|
||||
import { environment } from '@environments';
|
||||
import { AppChatService } from '@app/services/app-chat.service';
|
||||
|
||||
export type UserInfoTypes =
|
||||
| UserInfo
|
||||
| UserInfoSS
|
||||
| UserInfoF
|
||||
| UserInfoDN
|
||||
| RoomUserInfo;
|
||||
|
||||
export interface CreateDialogData {}
|
||||
export interface CreateDialogResult {
|
||||
userSeqs: string[];
|
||||
isTimer: boolean | undefined;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-dialog-chat-create',
|
||||
templateUrl: './create.dialog.component.html',
|
||||
styleUrls: ['./create.dialog.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class CreateDialogComponent implements OnInit, OnDestroy {
|
||||
currentStep = 0;
|
||||
maxChatRoomUser: number;
|
||||
|
||||
isTimer: boolean | undefined;
|
||||
selectedUserList: UserInfoTypes[] = [];
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<CreateDialogData, CreateDialogResult>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: CreateDialogData,
|
||||
private i18nService: I18nService,
|
||||
public dialog: MatDialog,
|
||||
private changeDetectorRef: ChangeDetectorRef
|
||||
) {
|
||||
this.maxChatRoomUser = environment.productConfig.chat.maxChatRoomUser;
|
||||
}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
ngOnDestroy(): void {}
|
||||
|
||||
onClosed(event: MouseEvent): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
onCancel(stepper: MatStepper) {
|
||||
if (stepper.selectedIndex > 0) {
|
||||
stepper.previous();
|
||||
return;
|
||||
}
|
||||
this.dialogRef.close();
|
||||
}
|
||||
onConfirm(stepper: MatStepper) {
|
||||
// validation.
|
||||
if (this.isTimer === undefined) {
|
||||
this.dialog.open<
|
||||
AlertDialogComponent,
|
||||
AlertDialogData,
|
||||
AlertDialogResult
|
||||
>(AlertDialogComponent, {
|
||||
data: {
|
||||
title: this.i18nService.t('errors.label'),
|
||||
html: this.i18nService.t('errors.emptyOpenRoomType', {
|
||||
maxCount: this.maxChatRoomUser
|
||||
})
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
stepper.next();
|
||||
}
|
||||
|
||||
onOpenRoom(stepper: MatStepper) {
|
||||
const userSeqs: string[] = [];
|
||||
|
||||
this.selectedUserList.map((user) => userSeqs.push(user.seq.toString()));
|
||||
|
||||
if (this.selectedUserList.length >= this.maxChatRoomUser) {
|
||||
this.dialog.open<
|
||||
AlertDialogComponent,
|
||||
AlertDialogData,
|
||||
AlertDialogResult
|
||||
>(AlertDialogComponent, {
|
||||
data: {
|
||||
title: this.i18nService.t('errors.label'),
|
||||
html: this.i18nService.t('errors.maxCountOfRoomMemberWith', {
|
||||
maxCount: this.maxChatRoomUser
|
||||
})
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Open Room.
|
||||
this.dialogRef.close({ userSeqs, isTimer: this.isTimer });
|
||||
}
|
||||
|
||||
onChangeUserList(selectedUserList: UserInfoTypes[]) {
|
||||
this.selectedUserList = selectedUserList;
|
||||
}
|
||||
}
|
3
src/app/sections/chat/dialogs/index.ts
Normal file
3
src/app/sections/chat/dialogs/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
import { CreateDialogComponent } from './create.dialog.component';
|
||||
|
||||
export const DIALOGS = [CreateDialogComponent];
|
|
@ -3,11 +3,12 @@ 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';
|
||||
|
||||
import { SelectGroupSectionComponent } from './select-group.section.component';
|
||||
export const COMPONENTS = [
|
||||
ListSectionComponent,
|
||||
SearchSectionComponent,
|
||||
ProfileSectionComponent,
|
||||
InfoSectionComponent,
|
||||
SelectUserSectionComponent
|
||||
SelectUserSectionComponent,
|
||||
SelectGroupSectionComponent
|
||||
];
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<div
|
||||
*ngIf="!!searchObj && !searchObj.isShowSearch"
|
||||
*ngIf="!!searchData && !searchData.bySearch"
|
||||
fxFlexFill
|
||||
class="list-container"
|
||||
>
|
||||
|
@ -8,20 +8,13 @@
|
|||
[showType]="showType"
|
||||
(clicked)="onClickUser($event)"
|
||||
(selectGroupMenu)="onSelectGroupMenu($event)"
|
||||
(selectProfileMenu)="onSelectProfileMenu($event)"
|
||||
(profileMenu)="onProfileMenu($event)"
|
||||
></app-group-expansion>
|
||||
</div>
|
||||
<div *ngIf="!!searchObj && searchObj.isShowSearch" class="search-wrpper">
|
||||
<perfect-scrollbar fxFlex="1 1 auto">
|
||||
<app-group-profile-list-item
|
||||
*ngFor="let userInfo of searchUserInfos"
|
||||
[userInfo]="userInfo"
|
||||
[checkable]="checkable"
|
||||
defaultProfileImage="assets/images/img_nophoto_50.png"
|
||||
(checkUser)="onCheckUser($event)"
|
||||
(click)="onClickSearchUser($event, userInfo)"
|
||||
[presence]="getStatusBulkInfo(userInfo) | async"
|
||||
>
|
||||
</app-group-profile-list-item>
|
||||
</perfect-scrollbar>
|
||||
<div *ngIf="!!searchData && searchData.bySearch" class="search-wrpper">
|
||||
<app-group-profile-list
|
||||
[searchData]="_searchData"
|
||||
[checkable]="checkable"
|
||||
></app-group-profile-list>
|
||||
</div>
|
||||
|
|
|
@ -29,7 +29,7 @@ import { LogService } from '@ucap/ng-logger';
|
|||
import { ExpansionComponent as AppExpansionComponent } from '@app/ucap/group/components/expansion.component';
|
||||
import { SessionStorageService } from '@ucap/ng-web-storage';
|
||||
import { LoginSelector } from '@ucap/ng-store-authentication';
|
||||
import { GroupActions } from '@ucap/ng-store-group';
|
||||
import { GroupActions, BuddyActions } from '@ucap/ng-store-group';
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
|
||||
import { AppAuthenticationService } from '@app/services/app-authentication.service';
|
||||
|
@ -52,7 +52,22 @@ import {
|
|||
AlertDialogData,
|
||||
AlertDialogResult
|
||||
} from '@ucap/ng-ui';
|
||||
import {
|
||||
ManageDialogComponent,
|
||||
ManageDialogData,
|
||||
ManageDialogResult
|
||||
} from '../dialogs/manage.dialog.component';
|
||||
|
||||
import { PresenceActions, PresenceSelector } from '@ucap/ng-store-organization';
|
||||
import { GroupUserDialaogType } from '@app/types';
|
||||
import { EditInlineInputDialogComponent } from '../dialogs/edit-inline-input.dialog.component';
|
||||
import {
|
||||
EditUserDialogComponent,
|
||||
EditUserDialogData,
|
||||
EditUserDialogResult
|
||||
} from '../dialogs/edit-user.dialog.component';
|
||||
import { SearchData } from '@app/ucap/organization/models/search-data';
|
||||
import { UserStore } from '@app/models/user-store';
|
||||
|
||||
export type UserInfoTypes =
|
||||
| UserInfo
|
||||
|
@ -81,24 +96,34 @@ export class GroupVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy {
|
|||
})
|
||||
export class ListSectionComponent implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
set searchObj(obj: {
|
||||
isShowSearch: boolean;
|
||||
companyCode: string;
|
||||
searchWord: string;
|
||||
}) {
|
||||
this._searchObj = obj;
|
||||
if (obj.isShowSearch && obj.searchWord.localeCompare('') !== 0) {
|
||||
this.onOrganizationTenantSearch(obj);
|
||||
set searchData(searchData: SearchData) {
|
||||
this._searchData = searchData;
|
||||
if (!searchData) {
|
||||
this._searchData = {
|
||||
companyCode: this.userStore.companyCode
|
||||
};
|
||||
} else {
|
||||
this._searchObj.isShowSearch = false;
|
||||
this.searchUserInfos = [];
|
||||
if (!this._searchData.companyCode) {
|
||||
this._searchData.companyCode = this.userStore.companyCode;
|
||||
}
|
||||
|
||||
if (
|
||||
searchData.bySearch &&
|
||||
searchData.searchWord.localeCompare('') !== 0
|
||||
) {
|
||||
this.onOrganizationTenantSearch(searchData);
|
||||
} else {
|
||||
this._searchData.isShowSearch = false;
|
||||
this.searchUserInfos = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get searchObj() {
|
||||
return this._searchObj;
|
||||
get searchData() {
|
||||
return this._searchData;
|
||||
}
|
||||
_searchObj: any;
|
||||
// tslint:disable-next-line: variable-name
|
||||
_searchData: any;
|
||||
|
||||
@Input()
|
||||
checkable = false;
|
||||
|
@ -120,6 +145,7 @@ export class ListSectionComponent implements OnInit, OnDestroy {
|
|||
searchUserInfos: UserInfoSS[] = [];
|
||||
|
||||
private ngOnDestroySubject = new Subject<boolean>();
|
||||
private userStore: UserStore;
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
|
@ -132,6 +158,7 @@ export class ListSectionComponent implements OnInit, OnDestroy {
|
|||
private queryProtocolService: QueryProtocolService,
|
||||
public dialog: MatDialog
|
||||
) {
|
||||
this.userStore = this.appAuthenticationService.getUserStore();
|
||||
this.i18nService.setDefaultNamespace('group');
|
||||
}
|
||||
|
||||
|
@ -151,19 +178,15 @@ export class ListSectionComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
onOrganizationTenantSearch(obj: {
|
||||
isShowSearch: boolean;
|
||||
companyCode: string;
|
||||
searchWord: string;
|
||||
}) {
|
||||
onOrganizationTenantSearch(searchData: SearchData) {
|
||||
const searchUserInfos: UserInfoSS[] = [];
|
||||
|
||||
this.queryProtocolService
|
||||
.deptUser({
|
||||
divCd: 'GRP',
|
||||
companyCode: this._searchObj.companyCode,
|
||||
companyCode: searchData.companyCode,
|
||||
searchRange: DeptSearchType.All,
|
||||
search: this._searchObj.searchWord,
|
||||
search: searchData.searchWord,
|
||||
senderCompanyCode: this.loginRes.userInfo.companyCode,
|
||||
senderEmployeeType: this.loginRes.userInfo.employeeType
|
||||
})
|
||||
|
@ -234,7 +257,11 @@ export class ListSectionComponent implements OnInit, OnDestroy {
|
|||
onProfileMenu(event) {
|
||||
console.log(event);
|
||||
}
|
||||
onSelectGroupMenu(params: { menuType: string; group: GroupDetailData }) {
|
||||
onSelectGroupMenu(params: {
|
||||
menuType: string;
|
||||
groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] };
|
||||
rect: any;
|
||||
}) {
|
||||
switch (params.menuType) {
|
||||
case 'CHAT':
|
||||
{
|
||||
|
@ -266,10 +293,31 @@ export class ListSectionComponent implements OnInit, OnDestroy {
|
|||
break;
|
||||
case 'RENAME':
|
||||
{
|
||||
this.renameGroup(params);
|
||||
}
|
||||
break;
|
||||
case 'MANAGE_MEMBER':
|
||||
{
|
||||
const dialogRef = this.dialog.open<
|
||||
ManageDialogComponent,
|
||||
ManageDialogData,
|
||||
ManageDialogResult
|
||||
>(ManageDialogComponent, {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
data: {
|
||||
title: '그룹 멤버 관리',
|
||||
groupBuddyList: params.groupBuddyList
|
||||
}
|
||||
});
|
||||
dialogRef
|
||||
.afterClosed()
|
||||
.pipe(take(1))
|
||||
.subscribe((result) => {
|
||||
if (!!result && !!result.type) {
|
||||
this.manageGroup(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'DELETE':
|
||||
|
@ -289,7 +337,7 @@ export class ListSectionComponent implements OnInit, OnDestroy {
|
|||
.pipe(take(1))
|
||||
.subscribe((result) => {
|
||||
if (!!result && !!result.choice) {
|
||||
this.store.dispatch(GroupActions.del({ group: params.group }));
|
||||
GroupActions.del({ group: params.groupBuddyList.group });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -298,41 +346,168 @@ export class ListSectionComponent implements OnInit, OnDestroy {
|
|||
break;
|
||||
}
|
||||
}
|
||||
// onEditGroupName(params: { editName: string; group: GroupDetailData }) {
|
||||
// if (params.editName.localeCompare('') === 0) {
|
||||
// const dialogRef = this.dialog.open<
|
||||
// AlertDialogComponent,
|
||||
// AlertDialogData,
|
||||
// AlertDialogResult
|
||||
// >(AlertDialogComponent, {
|
||||
// data: {
|
||||
// title: this.i18nService.t('moreMenu.error.label'),
|
||||
// html: this.i18nService.t('moreMenu.error.requireName')
|
||||
// }
|
||||
// });
|
||||
// dialogRef
|
||||
// .afterClosed()
|
||||
// .pipe(
|
||||
// take(1),
|
||||
// map((result) => {}),
|
||||
// catchError((err) => {
|
||||
// return of(err);
|
||||
// })
|
||||
// )
|
||||
// .subscribe();
|
||||
// return;
|
||||
// }
|
||||
// this.store.dispatch(
|
||||
// GroupActions.update({
|
||||
// req: {
|
||||
// groupSeq: params.group.seq,
|
||||
// groupName: params.editName,
|
||||
// userSeqs: params.group.userSeqs
|
||||
// }
|
||||
// })
|
||||
// );
|
||||
// console.log(params.editName);
|
||||
// }
|
||||
|
||||
onSelectProfileMenu(params: {
|
||||
menuType: string;
|
||||
userInfo: UserInfoF;
|
||||
group: GroupDetailData;
|
||||
rect: any;
|
||||
}) {
|
||||
switch (params.menuType) {
|
||||
case 'REGISTER_FAVORITE':
|
||||
this.store.dispatch(
|
||||
BuddyActions.update({
|
||||
req: {
|
||||
seq: Number(params.userInfo.seq),
|
||||
isFavorit: !params.userInfo.isFavorit
|
||||
}
|
||||
})
|
||||
);
|
||||
break;
|
||||
case 'NICKNAME':
|
||||
{
|
||||
this.editNickname(params.userInfo, params.rect);
|
||||
}
|
||||
break;
|
||||
case 'COPY_BUDDY':
|
||||
this.editUserDialog('COPY_BUDDY', params.group, params.userInfo);
|
||||
break;
|
||||
case 'MOVE_BUDDY':
|
||||
this.editUserDialog('MOVE_BUDDY', params.group, params.userInfo);
|
||||
break;
|
||||
case 'REMOVE_BUDDY':
|
||||
{
|
||||
this.removeBuddy(params.userInfo, params.group);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private renameGroup(params: {
|
||||
menuType: string;
|
||||
groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] };
|
||||
rect: any;
|
||||
}) {
|
||||
const paramGroup = params.groupBuddyList.group;
|
||||
|
||||
const dialogRef = this.dialog.open(EditInlineInputDialogComponent, {
|
||||
width: params.rect.width,
|
||||
height: params.rect.height,
|
||||
panelClass: 'ucap-edit-group-name-dialog',
|
||||
data: {
|
||||
curValue: paramGroup.name,
|
||||
placeholder: '그룹명을 입력하세요.',
|
||||
left: params.rect.left,
|
||||
top: params.rect.top
|
||||
}
|
||||
});
|
||||
|
||||
dialogRef
|
||||
.afterClosed()
|
||||
.pipe(
|
||||
take(1),
|
||||
map((result) => {
|
||||
if (
|
||||
!!result &&
|
||||
result.choice &&
|
||||
result.curValue.localeCompare(paramGroup.name) !== 0
|
||||
) {
|
||||
this.store.dispatch(
|
||||
GroupActions.update({
|
||||
req: {
|
||||
groupSeq: paramGroup.seq,
|
||||
groupName: result.curValue,
|
||||
userSeqs: paramGroup.userSeqs
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}),
|
||||
catchError((err) => {
|
||||
return of(err);
|
||||
})
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
private manageGroup(result: ManageDialogResult) {
|
||||
let targetGroup: GroupDetailData;
|
||||
let targetUserSeqs: string[];
|
||||
|
||||
if (result.type === GroupUserDialaogType.Add) {
|
||||
targetGroup = result.group;
|
||||
targetUserSeqs = [];
|
||||
result.selelctUserList.forEach((userInfo) => {
|
||||
targetUserSeqs.push(userInfo.seq + '');
|
||||
});
|
||||
|
||||
this.store.dispatch(
|
||||
GroupActions.updateMember({ targetGroup, targetUserSeqs })
|
||||
);
|
||||
} else if (result.type === GroupUserDialaogType.Copy) {
|
||||
if (!!result.selectGroupList && result.selectGroupList.length > 0) {
|
||||
result.selectGroupList.forEach((g) => {
|
||||
targetGroup = g;
|
||||
targetUserSeqs = [];
|
||||
|
||||
g.userSeqs.map((seq) => {
|
||||
targetUserSeqs.push(seq);
|
||||
});
|
||||
|
||||
if (targetUserSeqs.length === 0) {
|
||||
result.selelctUserList.forEach((user) => {
|
||||
targetUserSeqs.push(user.seq as any);
|
||||
});
|
||||
} else {
|
||||
result.selelctUserList.forEach((user) => {
|
||||
const find = targetUserSeqs.indexOf(user.seq as any);
|
||||
if (find < 0) {
|
||||
targetUserSeqs.push(user.seq as any);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.store.dispatch(
|
||||
GroupActions.updateMember({ targetGroup, targetUserSeqs })
|
||||
);
|
||||
});
|
||||
}
|
||||
} else if (result.type === GroupUserDialaogType.Move) {
|
||||
const fromGroup = result.group;
|
||||
let toGroup: GroupDetailData;
|
||||
targetUserSeqs = [];
|
||||
|
||||
if (!!result.selectGroupList && result.selectGroupList.length > 0) {
|
||||
result.selectGroupList.forEach((g) => {
|
||||
toGroup = g;
|
||||
targetUserSeqs = [];
|
||||
|
||||
result.selelctUserList.forEach((user) => {
|
||||
targetUserSeqs.push(user.seq as any);
|
||||
});
|
||||
|
||||
this.store.dispatch(
|
||||
GroupActions.moveMember({
|
||||
fromGroup,
|
||||
toGroup,
|
||||
targetUserSeq: targetUserSeqs
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
} else if (result.type === GroupUserDialaogType.Create) {
|
||||
targetUserSeqs = [];
|
||||
result.selelctUserList.forEach((userInfo) => {
|
||||
targetUserSeqs.push(userInfo.seq + '');
|
||||
});
|
||||
|
||||
this.store.dispatch(
|
||||
GroupActions.create({
|
||||
groupName: result.groupName,
|
||||
targetUserSeqs
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
getStatusBulkInfo(buddy: UserInfoTypes) {
|
||||
return this.store.pipe(
|
||||
|
@ -342,4 +517,188 @@ export class ListSectionComponent implements OnInit, OnDestroy {
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
private editUserDialog(
|
||||
type: string,
|
||||
group: GroupDetailData,
|
||||
userInfo: UserInfoTypes
|
||||
) {
|
||||
let title = '';
|
||||
let dialogType: GroupUserDialaogType;
|
||||
if (type === 'COPY_BUDDY') {
|
||||
title = '멤버 복사';
|
||||
dialogType = GroupUserDialaogType.Copy;
|
||||
} else {
|
||||
title = '멤버 이동';
|
||||
dialogType = GroupUserDialaogType.Move;
|
||||
}
|
||||
const dialogRef = this.dialog.open<
|
||||
EditUserDialogComponent,
|
||||
EditUserDialogData,
|
||||
EditUserDialogResult
|
||||
>(EditUserDialogComponent, {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
data: {
|
||||
title,
|
||||
type: dialogType,
|
||||
group,
|
||||
userInfo
|
||||
}
|
||||
});
|
||||
dialogRef
|
||||
.afterClosed()
|
||||
.pipe(
|
||||
take(1),
|
||||
map((result: EditUserDialogResult) => {
|
||||
let targetGroup: GroupDetailData;
|
||||
let targetUserSeqs: string[];
|
||||
if (result.type === GroupUserDialaogType.Add) {
|
||||
targetGroup = result.group;
|
||||
targetUserSeqs = [];
|
||||
result.selelctUserList.forEach((u) => {
|
||||
targetUserSeqs.push(u.seq + '');
|
||||
});
|
||||
this.store.dispatch(
|
||||
GroupActions.updateMember({ targetGroup, targetUserSeqs })
|
||||
);
|
||||
} else if (result.type === GroupUserDialaogType.Copy) {
|
||||
if (!!result.selectGroupList && result.selectGroupList.length > 0) {
|
||||
result.selectGroupList.forEach((g) => {
|
||||
targetGroup = g;
|
||||
targetUserSeqs = [];
|
||||
g.userSeqs.map((seq) => {
|
||||
targetUserSeqs.push(seq);
|
||||
});
|
||||
if (targetUserSeqs.length === 0) {
|
||||
result.selelctUserList.forEach((user) => {
|
||||
targetUserSeqs.push(user.seq as any);
|
||||
});
|
||||
} else {
|
||||
result.selelctUserList.forEach((user) => {
|
||||
const find = targetUserSeqs.indexOf(user.seq as any);
|
||||
if (find < 0) {
|
||||
targetUserSeqs.push(user.seq as any);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.store.dispatch(
|
||||
GroupActions.updateMember({ targetGroup, targetUserSeqs })
|
||||
);
|
||||
});
|
||||
}
|
||||
} else if (result.type === GroupUserDialaogType.Move) {
|
||||
const fromGroup = result.group;
|
||||
let toGroup: GroupDetailData;
|
||||
targetUserSeqs = [];
|
||||
if (!!result.selectGroupList && result.selectGroupList.length > 0) {
|
||||
result.selectGroupList.forEach((g) => {
|
||||
toGroup = g;
|
||||
targetUserSeqs = [];
|
||||
result.selelctUserList.forEach((user) => {
|
||||
targetUserSeqs.push(user.seq as any);
|
||||
});
|
||||
this.store.dispatch(
|
||||
GroupActions.moveMember({
|
||||
fromGroup,
|
||||
toGroup,
|
||||
targetUserSeq: targetUserSeqs
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
} else if (result.type === GroupUserDialaogType.Create) {
|
||||
targetUserSeqs = [];
|
||||
result.selelctUserList.forEach((u) => {
|
||||
targetUserSeqs.push(u.seq + '');
|
||||
});
|
||||
this.store.dispatch(
|
||||
GroupActions.create({
|
||||
groupName: result.groupName,
|
||||
targetUserSeqs
|
||||
})
|
||||
);
|
||||
}
|
||||
}),
|
||||
catchError((err) => {
|
||||
return of(err);
|
||||
})
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
private removeBuddy(userInfo: UserInfoF, group: GroupDetailData) {
|
||||
const dialogRef = this.dialog.open<
|
||||
ConfirmDialogComponent,
|
||||
ConfirmDialogData,
|
||||
ConfirmDialogResult
|
||||
>(ConfirmDialogComponent, {
|
||||
data: {
|
||||
title: '',
|
||||
html: this.i18nService.t('label.confirmRemoveBuddy')
|
||||
}
|
||||
});
|
||||
dialogRef
|
||||
.afterClosed()
|
||||
.pipe(
|
||||
take(1),
|
||||
map((result) => {
|
||||
if (!!result && result.choice) {
|
||||
const trgtUserSeq = group.userSeqs.filter(
|
||||
(user) => user + '' !== userInfo.seq + ''
|
||||
);
|
||||
|
||||
this.store.dispatch(
|
||||
GroupActions.updateMember({
|
||||
targetGroup: group,
|
||||
targetUserSeqs: trgtUserSeq
|
||||
})
|
||||
);
|
||||
}
|
||||
}),
|
||||
catchError((err) => {
|
||||
return of(err);
|
||||
})
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
private editNickname(userInfo: UserInfoF, rect: any) {
|
||||
const dialogRef = this.dialog.open(EditInlineInputDialogComponent, {
|
||||
width: rect.width - 30 + '',
|
||||
height: rect.height,
|
||||
panelClass: 'ucap-edit-group-name-dialog',
|
||||
data: {
|
||||
curValue: userInfo.nickName,
|
||||
placeholder: '닉네임을 설정하세요.',
|
||||
left: rect.left + 70,
|
||||
top: rect.top
|
||||
}
|
||||
});
|
||||
|
||||
dialogRef
|
||||
.afterClosed()
|
||||
.pipe(
|
||||
take(1),
|
||||
map((result) => {
|
||||
if (
|
||||
!!result &&
|
||||
result.choice &&
|
||||
result.curValue.localeCompare(userInfo.nickName) !== 0
|
||||
) {
|
||||
this.store.dispatch(
|
||||
BuddyActions.nickname({
|
||||
req: {
|
||||
userSeq: Number(userInfo.seq),
|
||||
nickname: result.curValue
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}),
|
||||
catchError((err) => {
|
||||
return of(err);
|
||||
})
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
<div fxFlexFill>
|
||||
<div fxFlex class="container">
|
||||
<!-- search start-->
|
||||
<div>
|
||||
<app-organization-search-for-tenant
|
||||
placeholder="이름 부서명, 전화번호, 이메일"
|
||||
[(searchData)]="companySearchData"
|
||||
(canceled)="onCanceled()"
|
||||
>
|
||||
</app-organization-search-for-tenant>
|
||||
</div>
|
||||
<!-- search end-->
|
||||
|
||||
<div *ngIf="!isSearch">
|
||||
<form name="inputForm" [formGroup]="inputForm" novalidate>
|
||||
<mat-form-field
|
||||
hintLabel="금지단어[-,_]"
|
||||
style="display: block; margin-bottom: 10px;"
|
||||
>
|
||||
<input
|
||||
matInput
|
||||
#input
|
||||
maxlength="20"
|
||||
placeholder="그룹명을 입력하세요."
|
||||
formControlName="groupName"
|
||||
(keyup)="onKeyupGroupName()"
|
||||
/>
|
||||
<mat-hint align="end">{{ input.value?.length || 0 }}/20</mat-hint>
|
||||
<!-- <mat-error *ngIf="inputForm.get('groupName').hasError('groupNameBanned')"> -->
|
||||
<!-- {{
|
||||
'group.errors.bannedWords'
|
||||
| translate: { bannedWords: appService.bannedGroupNames.join(',') }
|
||||
}} -->
|
||||
<!-- 금지단어[-,_] -->
|
||||
<!-- </mat-error> -->
|
||||
<!-- <mat-error *ngIf="inputForm.get('groupName').hasError('groupNameSamed')"> -->
|
||||
<!-- {{ 'group.errors.sameNameExist' | translate }} -->
|
||||
<!-- 이미 존재하는 그룹명입니다. -->
|
||||
<!-- </mat-error> -->
|
||||
</mat-form-field>
|
||||
</form>
|
||||
|
||||
<div *ngIf="!!groupList && groupList.length > 0">
|
||||
<span>기존 그룹 지정</span>
|
||||
<div fxFlexFill class="group-list-container">
|
||||
<div *ngFor="let group of groupList">
|
||||
<div *ngIf="checkVisible(group)">
|
||||
<span>{{ group.name }}</span>
|
||||
<div>
|
||||
<mat-checkbox
|
||||
#checkbox
|
||||
[checked]="groupChecked"
|
||||
(change)="onCheckForGroup($event.source, group)"
|
||||
></mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="!!isSearch" class="search-wrpper" style="height: 400px;">
|
||||
<app-group-profile-list
|
||||
[searchData]="companySearchData"
|
||||
[checkable]="checkable"
|
||||
[isDialog]="isDialog"
|
||||
(toggleCheck)="onToggleCheck($event)"
|
||||
></app-group-profile-list>
|
||||
</div>
|
||||
</div>
|
||||
</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 { SelectGroupSectionComponent } from './select-group.section.component';
|
||||
|
||||
describe('app::sections::group::SelectGroupSectionComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [RouterTestingModule],
|
||||
declarations: [SelectGroupSectionComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(SelectGroupSectionComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'ucap-lg-web'`, () => {
|
||||
const fixture = TestBed.createComponent(SelectGroupSectionComponent);
|
||||
const app = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(SelectGroupSectionComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.nativeElement;
|
||||
expect(compiled.querySelector('.content span').textContent).toContain(
|
||||
'ucap-lg-web app is running!'
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,226 @@
|
|||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Input,
|
||||
Output,
|
||||
EventEmitter
|
||||
} from '@angular/core';
|
||||
|
||||
import { Subject, of } from 'rxjs';
|
||||
import { Store, select } from '@ngrx/store';
|
||||
import { takeUntil, take, map, catchError } from 'rxjs/operators';
|
||||
import {
|
||||
LoginSelector,
|
||||
AuthorizationSelector
|
||||
} from '@ucap/ng-store-authentication';
|
||||
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||
import { QueryProtocolService } from '@ucap/ng-protocol-query';
|
||||
import {
|
||||
UserInfoSS,
|
||||
AuthResponse,
|
||||
DeptSearchType,
|
||||
UserInfoF,
|
||||
UserInfoDN
|
||||
} from '@ucap/protocol-query';
|
||||
import { GroupSelector } from '@ucap/ng-store-group';
|
||||
import { GroupDetailData, UserInfo } from '@ucap/protocol-sync';
|
||||
import { PresenceActions } from '@ucap/ng-store-organization';
|
||||
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
||||
import { UserInfo as RoomUserInfo } from '@ucap/protocol-room';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import {
|
||||
AlertDialogComponent,
|
||||
AlertDialogData,
|
||||
AlertDialogResult
|
||||
} from '@ucap/ng-ui';
|
||||
import { MatCheckbox } from '@angular/material/checkbox';
|
||||
import { SearchData } from '@app/ucap/organization/models/search-data';
|
||||
|
||||
export type UserInfoTypes =
|
||||
| UserInfo
|
||||
| UserInfoSS
|
||||
| UserInfoF
|
||||
| UserInfoDN
|
||||
| RoomUserInfo;
|
||||
|
||||
@Component({
|
||||
selector: 'app-sections-select-group',
|
||||
templateUrl: './select-group.section.component.html',
|
||||
styleUrls: ['./select-group.section.component.scss'],
|
||||
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class SelectGroupSectionComponent implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
isMemberMove: boolean;
|
||||
|
||||
@Input()
|
||||
isDialog = false;
|
||||
|
||||
@Input()
|
||||
checkable = false;
|
||||
|
||||
@Input()
|
||||
curGroup: GroupDetailData;
|
||||
|
||||
@Output()
|
||||
changeUserList: EventEmitter<{
|
||||
checked: boolean;
|
||||
userInfo: UserInfoSS;
|
||||
}> = new EventEmitter();
|
||||
|
||||
@Output()
|
||||
changeGroupList: EventEmitter<GroupDetailData[]> = new EventEmitter();
|
||||
|
||||
@Output()
|
||||
changeGroupName: EventEmitter<string> = new EventEmitter();
|
||||
|
||||
set companySearchData(searchData: SearchData) {
|
||||
this._companySearchData = searchData;
|
||||
this.onChangedCompanySearch();
|
||||
}
|
||||
get companySearchData() {
|
||||
return this._companySearchData;
|
||||
}
|
||||
// tslint:disable-next-line: variable-name
|
||||
_companySearchData: SearchData;
|
||||
|
||||
private ngOnDestroySubject = new Subject<boolean>();
|
||||
|
||||
constructor(
|
||||
private store: Store<any>,
|
||||
private queryProtocolService: QueryProtocolService,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private formBuilder: FormBuilder,
|
||||
public dialog: MatDialog
|
||||
) {}
|
||||
|
||||
loginRes: LoginResponse;
|
||||
isSearch = false;
|
||||
searchWord: string;
|
||||
searchUserInfos: UserInfoSS[] = [];
|
||||
groupList: GroupDetailData[];
|
||||
selectedUserList: UserInfoTypes[] = [];
|
||||
selectedGroupList: GroupDetailData[] = [];
|
||||
inputForm: FormGroup;
|
||||
groupChecked = false;
|
||||
|
||||
groupName: string;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.store
|
||||
.pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
|
||||
.subscribe((loginRes) => {
|
||||
this.loginRes = loginRes;
|
||||
});
|
||||
|
||||
this.store
|
||||
.pipe(takeUntil(this.ngOnDestroySubject), select(GroupSelector.groups))
|
||||
.subscribe((groups) => {
|
||||
this.groupList = groups;
|
||||
});
|
||||
|
||||
this.inputForm = this.formBuilder.group({
|
||||
groupName: [
|
||||
this.groupName,
|
||||
[
|
||||
// Validators.required
|
||||
// StringUtil.includes(, CharactorType.Special),
|
||||
// this.checkBanWords(),
|
||||
// this.checkSameName()
|
||||
]
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
||||
onKeyupGroupName() {
|
||||
this.inputForm.get('groupName').markAsTouched();
|
||||
this.changeGroupName.emit(this.inputForm.get('groupName').value);
|
||||
}
|
||||
|
||||
onCheckForGroup(checbox: MatCheckbox, group: GroupDetailData) {
|
||||
if (
|
||||
this.isMemberMove &&
|
||||
!!this.selectedGroupList &&
|
||||
this.selectedGroupList.length > 0 &&
|
||||
this.selectedGroupList[0].seq !== group.seq
|
||||
) {
|
||||
this.dialog.open<
|
||||
AlertDialogComponent,
|
||||
AlertDialogData,
|
||||
AlertDialogResult
|
||||
>(AlertDialogComponent, {
|
||||
data: {
|
||||
title: '멤버이동',
|
||||
html: '멤버이동은 그룹 여러개를 선택할 수 없습니다.'
|
||||
}
|
||||
});
|
||||
|
||||
checbox.checked = false;
|
||||
return;
|
||||
}
|
||||
if (
|
||||
this.selectedGroupList.filter((g) => g.seq === group.seq).length === 0
|
||||
) {
|
||||
this.selectedGroupList = [...this.selectedGroupList, group];
|
||||
} else {
|
||||
this.selectedGroupList = this.selectedGroupList.filter(
|
||||
(g) => g.seq !== group.seq
|
||||
);
|
||||
}
|
||||
|
||||
this.changeGroupList.emit(this.selectedGroupList);
|
||||
}
|
||||
|
||||
// onCheckForUser(params: { isChecked: boolean; userInfo: UserInfoTypes }) {
|
||||
// if (
|
||||
// this.selectedUserList.filter((user) => user.seq === params.userInfo.seq)
|
||||
// .length === 0
|
||||
// ) {
|
||||
// this.selectedUserList = [...this.selectedUserList, params.userInfo];
|
||||
// } else {
|
||||
// this.selectedUserList = this.selectedUserList.filter(
|
||||
// (item) => item.seq !== params.userInfo.seq
|
||||
// );
|
||||
// }
|
||||
|
||||
// this.changeUserList.emit(this.selectedUserList);
|
||||
// }
|
||||
|
||||
onToggleCheck(data: { checked: boolean; userInfo: UserInfoSS }) {
|
||||
this.changeUserList.emit(data);
|
||||
}
|
||||
|
||||
onChangedCompanySearch() {
|
||||
this.isSearch = true;
|
||||
}
|
||||
|
||||
onCanceled() {
|
||||
this.isSearch = false;
|
||||
}
|
||||
|
||||
getCheckedUser(userInfo: UserInfoTypes) {
|
||||
if (!!this.selectedUserList && this.selectedUserList.length > 0) {
|
||||
return (
|
||||
this.selectedUserList.filter((item) => item.seq === userInfo.seq)
|
||||
.length > 0
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
checkVisible(group: GroupDetailData): boolean {
|
||||
if (!!this.curGroup && this.curGroup.seq === group.seq) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
<div>
|
||||
<app-organization-search-for-tenant
|
||||
placeholder="이름 부서명, 전화번호, 이메일"
|
||||
(changed)="onChanged($event)"
|
||||
[(searchData)]="companySearchData"
|
||||
(canceled)="onCanceled()"
|
||||
>
|
||||
</app-organization-search-for-tenant>
|
||||
|
@ -24,12 +24,13 @@
|
|||
<div fxFlexFill>
|
||||
<div class="organization-tree">
|
||||
<app-group-expansion
|
||||
style="height: 400px;"
|
||||
fxFlexFill
|
||||
[isDialog]="isDialog"
|
||||
[selectedUserList]="selectedUserList"
|
||||
[checkable]="true"
|
||||
(checked)="onCheckUser($event)"
|
||||
(checkGroup)="onCheckGroup($event)"
|
||||
[checkable]="checkable"
|
||||
(toggleCheckUser)="onToggleCheckUser($event)"
|
||||
(toggleCheckGroup)="onToggleCheckGroup($event)"
|
||||
></app-group-expansion>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -59,73 +60,14 @@
|
|||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</div>
|
||||
<div *ngIf="!!isSearch" class="search-wrpper">
|
||||
<perfect-scrollbar fxFlex="1 1 auto">
|
||||
<app-group-profile-list-item
|
||||
*ngFor="let userInfo of searchUserInfos"
|
||||
[userInfo]="userInfo"
|
||||
[checkable]="checkable"
|
||||
[isChecked]="getCheckedUser(userInfo)"
|
||||
defaultProfileImage="assets/images/img_nophoto_50.png"
|
||||
(checked)="onCheckUser($event)"
|
||||
>
|
||||
</app-group-profile-list-item>
|
||||
</perfect-scrollbar>
|
||||
<div *ngIf="!!isSearch" class="search-wrpper" style="height: 400px;">
|
||||
<app-group-profile-list
|
||||
[searchData]="companySearchData"
|
||||
[selectedUser]="selectedUserList"
|
||||
[checkable]="checkable"
|
||||
[isDialog]="isDialog"
|
||||
(toggleCheck)="onToggleCheckUser($event)"
|
||||
></app-group-profile-list>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<ng-template [ngTemplateOutlet]="selectedUserListTemplate"></ng-template>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
|
|
|
@ -27,6 +27,7 @@ import {
|
|||
import { UserInfo as RoomUserInfo } from '@ucap/protocol-room';
|
||||
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||
import { LoginSelector } from '@ucap/ng-store-authentication';
|
||||
import { SearchData } from '@app/ucap/organization/models/search-data';
|
||||
|
||||
export type UserInfoTypes =
|
||||
| UserInfo
|
||||
|
@ -61,16 +62,22 @@ export class SelectUserSectionComponent implements OnInit, OnDestroy {
|
|||
existUsers: UserInfoTypes[];
|
||||
|
||||
@Input()
|
||||
set checkable(check: boolean) {
|
||||
this._checkable = check;
|
||||
}
|
||||
get checkable(): boolean {
|
||||
return this._checkable;
|
||||
}
|
||||
_checkable = false;
|
||||
isSelectionOff = true;
|
||||
|
||||
@Input()
|
||||
checkable = false;
|
||||
|
||||
@Output()
|
||||
changeUserList = new EventEmitter<UserInfoTypes[]>();
|
||||
toggleCheckUser: EventEmitter<{
|
||||
checked: boolean;
|
||||
userInfo: UserInfoSS;
|
||||
}> = new EventEmitter();
|
||||
|
||||
@Output()
|
||||
toggleCheckGroup: EventEmitter<{
|
||||
isChecked: boolean;
|
||||
groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] };
|
||||
}> = new EventEmitter();
|
||||
|
||||
@Output()
|
||||
cancel = new EventEmitter();
|
||||
|
@ -78,11 +85,23 @@ export class SelectUserSectionComponent implements OnInit, OnDestroy {
|
|||
@Output()
|
||||
confirm = new EventEmitter();
|
||||
|
||||
set companySearchData(searchData: SearchData) {
|
||||
this._companySearchData = searchData;
|
||||
|
||||
this.onChangedCompanySearch();
|
||||
}
|
||||
get companySearchData() {
|
||||
return this._companySearchData;
|
||||
}
|
||||
// tslint:disable-next-line: variable-name
|
||||
_companySearchData: SearchData;
|
||||
|
||||
isSearch = false;
|
||||
searchWord: string;
|
||||
|
||||
private ngOnDestroySubject = new Subject<boolean>();
|
||||
|
||||
@Input()
|
||||
selectedUserList: UserInfoTypes[] = [];
|
||||
searchUserInfos: UserInfoSS[] = [];
|
||||
|
||||
|
@ -113,20 +132,15 @@ export class SelectUserSectionComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
onCheckUser(params: { isChecked: boolean; userInfo: UserInfoTypes }) {
|
||||
console.log(params);
|
||||
if (
|
||||
this.selectedUserList.filter((user) => user.seq === params.userInfo.seq)
|
||||
.length === 0
|
||||
) {
|
||||
this.selectedUserList = [...this.selectedUserList, params.userInfo];
|
||||
} else {
|
||||
this.selectedUserList = this.selectedUserList.filter(
|
||||
(item) => item.seq !== params.userInfo.seq
|
||||
);
|
||||
}
|
||||
onToggleCheckGroup(params: {
|
||||
isChecked: boolean;
|
||||
groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] };
|
||||
}) {
|
||||
this.toggleCheckGroup.emit(params);
|
||||
}
|
||||
|
||||
this.changeUserList.emit(this.selectedUserList);
|
||||
onToggleCheckUser(data: { checked: boolean; userInfo: UserInfoSS }) {
|
||||
this.toggleCheckUser.emit(data);
|
||||
}
|
||||
|
||||
getCheckedUser(userInfo: UserInfoTypes) {
|
||||
|
@ -139,96 +153,8 @@ export class SelectUserSectionComponent implements OnInit, OnDestroy {
|
|||
return false;
|
||||
}
|
||||
|
||||
onCheckGroup(params: {
|
||||
isChecked: boolean;
|
||||
groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] };
|
||||
}) {
|
||||
if (params.isChecked) {
|
||||
params.groupBuddyList.buddyList.forEach((item) => {
|
||||
if (
|
||||
this.selectedUserList.filter((user) => user.seq === item.seq)
|
||||
.length === 0
|
||||
) {
|
||||
this.selectedUserList = [...this.selectedUserList, item];
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.selectedUserList = this.selectedUserList.filter(
|
||||
(item) =>
|
||||
params.groupBuddyList.buddyList.filter((del) => del.seq === item.seq)
|
||||
.length === 0
|
||||
);
|
||||
}
|
||||
this.changeDetectorRef.markForCheck();
|
||||
}
|
||||
getSelectedUserList(): any[] {
|
||||
return this.selectedUserList;
|
||||
}
|
||||
|
||||
getChipsRemoveYn(userInfo: UserInfoTypes) {
|
||||
if (!!this.existUsers && this.existUsers.length > 0) {
|
||||
return !(
|
||||
this.existUsers.filter((user) => user.seq === userInfo.seq).length > 0
|
||||
);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
onClickDeleteUser(userInfo: UserInfoTypes) {
|
||||
this.selectedUserList = this.selectedUserList.filter(
|
||||
(item) => item.seq !== userInfo.seq
|
||||
);
|
||||
this.changeDetectorRef.markForCheck();
|
||||
}
|
||||
|
||||
onChanged(data: { companyCode: string; searchWord: string }) {
|
||||
onChangedCompanySearch() {
|
||||
this.isSearch = true;
|
||||
this.searchWord = data.searchWord;
|
||||
|
||||
const searchUserInfos: UserInfoSS[] = [];
|
||||
|
||||
this.queryProtocolService
|
||||
.deptUser({
|
||||
divCd: 'GRP',
|
||||
companyCode: data.companyCode,
|
||||
searchRange: DeptSearchType.All,
|
||||
search: data.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();
|
||||
}
|
||||
|
||||
onCanceled() {
|
||||
|
|
|
@ -43,12 +43,25 @@
|
|||
</mat-step>
|
||||
<mat-step label="Step 1">
|
||||
<app-group-select-user
|
||||
style="height: 500px;"
|
||||
[isDialog]="true"
|
||||
[checkable]="true"
|
||||
(changeUserList)="onChangeUserList($event)"
|
||||
[selectedUserList]="selectedUserList"
|
||||
(toggleCheckUser)="onChangeUserList($event)"
|
||||
(toggleCheckGroup)="onChangeGroupList($event)"
|
||||
></app-group-select-user>
|
||||
</mat-step>
|
||||
</mat-horizontal-stepper>
|
||||
|
||||
<div>
|
||||
<ucap-organization-profile-selection-01
|
||||
[userInfoList]="selectedUserList"
|
||||
[removableFor]="removableForSelection"
|
||||
[colorFor]="colorForSelection"
|
||||
(removed)="onRemovedProfileSelection($event)"
|
||||
>
|
||||
</ucap-organization-profile-selection-01>
|
||||
</div>
|
||||
</div>
|
||||
<div appLayoutsDefaultDialog="action">
|
||||
<button mat-button (click)="onCancel(stepper)">취소</button>
|
||||
|
|
|
@ -147,7 +147,63 @@ export class CreateDialogComponent implements OnInit, OnDestroy {
|
|||
this.inputForm.get('groupName').markAsTouched();
|
||||
}
|
||||
|
||||
onChangeUserList(selectedUserList: UserInfoTypes[]) {
|
||||
this.selectedUserList = selectedUserList;
|
||||
onChangeUserList(data: { checked: boolean; userInfo: UserInfoSS }) {
|
||||
const i = this.selectedUserList.findIndex(
|
||||
(u) => u.seq === data.userInfo.seq
|
||||
);
|
||||
|
||||
if (data.checked) {
|
||||
if (-1 === i) {
|
||||
this.selectedUserList = [...this.selectedUserList, data.userInfo];
|
||||
}
|
||||
} else {
|
||||
if (-1 < i) {
|
||||
this.selectedUserList = this.selectedUserList.filter(
|
||||
(u) => u.seq !== data.userInfo.seq
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onChangeGroupList(params: {
|
||||
isChecked: boolean;
|
||||
groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] };
|
||||
}) {
|
||||
if (params.isChecked) {
|
||||
params.groupBuddyList.buddyList.forEach((item) => {
|
||||
if (
|
||||
this.selectedUserList.filter((user) => user.seq === item.seq)
|
||||
.length === 0
|
||||
) {
|
||||
this.selectedUserList = [...this.selectedUserList, item];
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.selectedUserList = this.selectedUserList.filter(
|
||||
(item) =>
|
||||
params.groupBuddyList.buddyList.filter((del) => del.seq === item.seq)
|
||||
.length === 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
onRemovedProfileSelection(userInfo: UserInfo) {
|
||||
const i = this.selectedUserList.findIndex(
|
||||
(u) => (u.seq as any) === (userInfo.seq as any)
|
||||
);
|
||||
|
||||
if (-1 < i) {
|
||||
this.selectedUserList = this.selectedUserList.filter(
|
||||
(u) => (u.seq as any) !== (userInfo.seq as any)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
removableForSelection = (userInfo: UserInfo) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
colorForSelection = (userInfo: UserInfo) => {
|
||||
return 'accent';
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<div class="dialog-container">
|
||||
<app-layouts-default-dialog
|
||||
[disableClose]="false"
|
||||
(closed)="onClosed($event)"
|
||||
>
|
||||
<div appLayoutsDefaultDialog="header">
|
||||
{{ data.title }}
|
||||
</div>
|
||||
<div class="dialog-body" appLayoutsDefaultDialog="body">
|
||||
<app-sections-select-group
|
||||
[isMemberMove]="data.type"
|
||||
[isDialog]="true"
|
||||
[checkable]="true"
|
||||
[curGroup]="data.group"
|
||||
(changeUserList)="onChangeUserList($event)"
|
||||
(changeGroupList)="onChangeGroupList($event)"
|
||||
(changeGroupName)="onChangeGroupName($event)"
|
||||
></app-sections-select-group>
|
||||
<div>
|
||||
<ucap-organization-profile-selection-01
|
||||
[userInfoList]="selectedUserList"
|
||||
[removableFor]="removableForSelection"
|
||||
[colorFor]="colorForSelection"
|
||||
(removed)="onRemovedProfileSelection($event)"
|
||||
>
|
||||
</ucap-organization-profile-selection-01>
|
||||
</div>
|
||||
</div>
|
||||
<div appLayoutsDefaultDialog="action">
|
||||
<button mat-button (click)="onClosed($event)">취소</button>
|
||||
<button mat-button (click)="onConfirm()">
|
||||
완료
|
||||
</button>
|
||||
</div>
|
||||
</app-layouts-default-dialog>
|
||||
</div>
|
|
@ -0,0 +1,9 @@
|
|||
.dialog-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.dialog-body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { DebugElement } from '@angular/core';
|
||||
|
||||
import { EditUserDialogComponent } from './edit-user.dialog.component';
|
||||
|
||||
describe('ucap::ui-organization::EditUserDialogComponent', () => {
|
||||
let component: EditUserDialogComponent;
|
||||
let fixture: ComponentFixture<EditUserDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [EditUserDialogComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(EditUserDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
149
src/app/sections/group/dialogs/edit-user.dialog.component.ts
Normal file
149
src/app/sections/group/dialogs/edit-user.dialog.component.ts
Normal file
|
@ -0,0 +1,149 @@
|
|||
import { Subject } from 'rxjs';
|
||||
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Inject,
|
||||
Input
|
||||
} from '@angular/core';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import {
|
||||
MatDialogRef,
|
||||
MAT_DIALOG_DATA,
|
||||
MatDialog
|
||||
} from '@angular/material/dialog';
|
||||
|
||||
import { UserInfo, GroupDetailData } from '@ucap/protocol-sync';
|
||||
import { UserInfoSS, UserInfoF, UserInfoDN } from '@ucap/protocol-query';
|
||||
import { UserInfo as RoomUserInfo } from '@ucap/protocol-room';
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
|
||||
import { SelectUserDialogType, GroupUserDialaogType } from '@app/types';
|
||||
|
||||
export type UserInfoTypes =
|
||||
| UserInfo
|
||||
| UserInfoSS
|
||||
| UserInfoF
|
||||
| UserInfoDN
|
||||
| RoomUserInfo;
|
||||
|
||||
export interface EditUserDialogData {
|
||||
title: string;
|
||||
type: GroupUserDialaogType;
|
||||
group: GroupDetailData;
|
||||
userInfo: UserInfoTypes;
|
||||
}
|
||||
export interface EditUserDialogResult {
|
||||
type: GroupUserDialaogType;
|
||||
groupName: string;
|
||||
group: GroupDetailData;
|
||||
selelctUserList?: UserInfoTypes[];
|
||||
selectGroupList?: GroupDetailData[];
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-dialog-edit-user',
|
||||
templateUrl: './edit-user.dialog.component.html',
|
||||
styleUrls: ['./edit-user.dialog.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class EditUserDialogComponent implements OnInit, OnDestroy {
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<EditUserDialogData, EditUserDialogResult>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: EditUserDialogData,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private store: Store<any>,
|
||||
private i18nService: I18nService,
|
||||
public dialog: MatDialog
|
||||
) {}
|
||||
|
||||
private ngOnDestroySubject: Subject<void>;
|
||||
|
||||
currentType: GroupUserDialaogType;
|
||||
selectedUserList: UserInfoTypes[] = [];
|
||||
selectedGroupList: GroupDetailData[] = [];
|
||||
groupName = '';
|
||||
SelectUserDialogType = SelectUserDialogType;
|
||||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject();
|
||||
|
||||
this.selectedUserList.push(this.data.userInfo);
|
||||
this.currentType = this.data.type;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
||||
onClosed(event: MouseEvent): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
onConfirm() {
|
||||
if (!!this.groupName && this.groupName.trim().localeCompare('') !== 0) {
|
||||
this.currentType = GroupUserDialaogType.Create;
|
||||
} else {
|
||||
this.groupName = undefined;
|
||||
}
|
||||
|
||||
this.dialogRef.close({
|
||||
type: this.currentType,
|
||||
groupName: this.groupName,
|
||||
group: this.data.group,
|
||||
selelctUserList: this.selectedUserList,
|
||||
selectGroupList: this.selectedGroupList
|
||||
});
|
||||
}
|
||||
onChangeUserList(data: { checked: boolean; userInfo: UserInfoSS }) {
|
||||
const i = this.selectedUserList.findIndex(
|
||||
(u) => u.seq === data.userInfo.seq
|
||||
);
|
||||
|
||||
if (data.checked) {
|
||||
if (-1 === i) {
|
||||
this.selectedUserList = [...this.selectedUserList, data.userInfo];
|
||||
}
|
||||
} else {
|
||||
if (-1 < i) {
|
||||
this.selectedUserList = this.selectedUserList.filter(
|
||||
(u) => u.seq !== data.userInfo.seq
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onChangeGroupList(selectedGroupList: GroupDetailData[]) {
|
||||
this.selectedGroupList = selectedGroupList;
|
||||
}
|
||||
|
||||
onChangeGroupName(name: string) {
|
||||
this.groupName = name;
|
||||
}
|
||||
|
||||
onRemovedProfileSelection(userInfo: UserInfo) {
|
||||
const i = this.selectedUserList.findIndex(
|
||||
(u) => (u.seq as any) === (userInfo.seq as any)
|
||||
);
|
||||
|
||||
if (-1 < i) {
|
||||
this.selectedUserList = this.selectedUserList.filter(
|
||||
(u) => (u.seq as any) !== (userInfo.seq as any)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
removableForSelection = (userInfo: UserInfo) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
colorForSelection = (userInfo: UserInfo) => {
|
||||
return 'accent';
|
||||
};
|
||||
}
|
|
@ -1,4 +1,11 @@
|
|||
import { CreateDialogComponent } from './create.dialog.component';
|
||||
import { EditInlineInputDialogComponent } from './edit-inline-input.dialog.component';
|
||||
import { ManageDialogComponent } from './manage.dialog.component';
|
||||
import { EditUserDialogComponent } from './edit-user.dialog.component';
|
||||
|
||||
export const DIALOGS = [CreateDialogComponent, EditInlineInputDialogComponent];
|
||||
export const DIALOGS = [
|
||||
CreateDialogComponent,
|
||||
EditInlineInputDialogComponent,
|
||||
ManageDialogComponent,
|
||||
EditUserDialogComponent
|
||||
];
|
||||
|
|
|
@ -12,28 +12,68 @@
|
|||
#stepper
|
||||
[selectedIndex]="currentStep"
|
||||
>
|
||||
<mat-step label="Step 1"> </mat-step>
|
||||
<mat-step label="Step 1">
|
||||
<app-group-select-user
|
||||
[isDialog]="true"
|
||||
[checkable]="true"
|
||||
(changeUserList)="onChangeUserList($event)"
|
||||
></app-group-select-user>
|
||||
<div>
|
||||
<div>
|
||||
{{ data.groupBuddyList.group.name }}
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
mat-button
|
||||
(click)="onAdd(stepper)"
|
||||
aria-label="대화상대 추가"
|
||||
>
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<app-group-profile-list-item-02
|
||||
*ngFor="let userInfo of this.data.groupBuddyList.buddyList"
|
||||
[userInfo]="userInfo"
|
||||
[isDialog]="true"
|
||||
[checked]="getCheckedUser(userInfo)"
|
||||
[checkable]="true"
|
||||
(changeCheckUser)="onToggleCheck($event)"
|
||||
></app-group-profile-list-item-02>
|
||||
</div>
|
||||
</mat-step>
|
||||
<mat-step label="Step 2">
|
||||
<ng-template #dialogContainer></ng-template>
|
||||
</mat-step>
|
||||
</mat-horizontal-stepper>
|
||||
<div>
|
||||
<ucap-organization-profile-selection-01
|
||||
[userInfoList]="selectedUserList"
|
||||
[removableFor]="removableForSelection"
|
||||
[colorFor]="colorForSelection"
|
||||
(removed)="onRemovedProfileSelection($event)"
|
||||
>
|
||||
</ucap-organization-profile-selection-01>
|
||||
</div>
|
||||
</div>
|
||||
<div appLayoutsDefaultDialog="action">
|
||||
<button mat-button (click)="onDelete(stepper)">삭제</button>
|
||||
<button mat-button (click)="onConfirm(stepper)">
|
||||
완료
|
||||
</button>
|
||||
<button
|
||||
*ngIf="currentStep < 1"
|
||||
mat-button
|
||||
(click)="onCompleteConfirm(stepper)"
|
||||
>
|
||||
그룹지정후 완료
|
||||
</button>
|
||||
<div *ngIf="currentStep === 0">
|
||||
<button mat-button (click)="onDelete(stepper)">삭제</button>
|
||||
<button
|
||||
mat-button
|
||||
(click)="onUpdateMember(stepper, GroupUserDialaogType.Copy)"
|
||||
>
|
||||
그룹복사
|
||||
</button>
|
||||
<button
|
||||
mat-button
|
||||
(click)="onUpdateMember(stepper, GroupUserDialaogType.Move)"
|
||||
>
|
||||
그룹이동
|
||||
</button>
|
||||
</div>
|
||||
<div *ngIf="currentStep > 0">
|
||||
<button mat-button (click)="onCnacel(stepper)">취소</button>
|
||||
<button mat-button (click)="onConfirm(stepper)">
|
||||
완료
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</app-layouts-default-dialog>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Subject } from 'rxjs';
|
||||
import { Subject, of } from 'rxjs';
|
||||
|
||||
import {
|
||||
Component,
|
||||
|
@ -7,7 +7,11 @@ import {
|
|||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Inject,
|
||||
Input
|
||||
Input,
|
||||
ComponentFactoryResolver,
|
||||
ViewChild,
|
||||
ViewContainerRef,
|
||||
ComponentRef
|
||||
} from '@angular/core';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
|
@ -28,8 +32,15 @@ import { GroupActions } from '@ucap/ng-store-group';
|
|||
import {
|
||||
AlertDialogComponent,
|
||||
AlertDialogData,
|
||||
AlertDialogResult
|
||||
AlertDialogResult,
|
||||
ConfirmDialogComponent,
|
||||
ConfirmDialogResult,
|
||||
ConfirmDialogData
|
||||
} from '@ucap/ng-ui';
|
||||
import { SelectUserSectionComponent } from '../components/select-user.section.component';
|
||||
import { take, map, catchError } from 'rxjs/operators';
|
||||
import { SelectGroupSectionComponent } from '../components/select-group.section.component';
|
||||
import { SelectUserDialogType, GroupUserDialaogType } from '@app/types';
|
||||
|
||||
export type UserInfoTypes =
|
||||
| UserInfo
|
||||
|
@ -40,8 +51,15 @@ export type UserInfoTypes =
|
|||
|
||||
export interface ManageDialogData {
|
||||
title: string;
|
||||
groupBuddyList?: { group: GroupDetailData; buddyList: UserInfo[] };
|
||||
}
|
||||
export interface ManageDialogResult {
|
||||
type: GroupUserDialaogType;
|
||||
groupName: string;
|
||||
group?: GroupDetailData;
|
||||
selelctUserList?: UserInfoTypes[];
|
||||
selectGroupList?: GroupDetailData[];
|
||||
}
|
||||
export interface ManageDialogResult {}
|
||||
|
||||
@Component({
|
||||
selector: 'app-dialog-group-manage',
|
||||
|
@ -57,16 +75,29 @@ export class ManageDialogComponent implements OnInit, OnDestroy {
|
|||
private store: Store<any>,
|
||||
private formBuilder: FormBuilder,
|
||||
private i18nService: I18nService,
|
||||
public dialog: MatDialog
|
||||
public dialog: MatDialog,
|
||||
private cfResolver: ComponentFactoryResolver
|
||||
) {}
|
||||
|
||||
private ngOnDestroySubject: Subject<void>;
|
||||
currentStep = 0;
|
||||
@ViewChild('dialogContainer', { static: true, read: ViewContainerRef })
|
||||
dialogContainer: ViewContainerRef;
|
||||
|
||||
componentRef: ComponentRef<any>;
|
||||
private ngOnDestroySubject: Subject<void>;
|
||||
currentType: GroupUserDialaogType;
|
||||
SelectUserDialogType = SelectUserDialogType;
|
||||
GroupUserDialaogType = GroupUserDialaogType;
|
||||
|
||||
currentStep = 0;
|
||||
groupName = '';
|
||||
|
||||
delteUserList: UserInfoTypes[] = [];
|
||||
selectedUserList: UserInfoTypes[] = [];
|
||||
selectedGroupList: GroupDetailData[] = [];
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject();
|
||||
// this.selectedUserList = this.data.groupBuddyList.buddyList;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
|
@ -80,14 +111,111 @@ export class ManageDialogComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
onDelete(stepper: MatStepper) {
|
||||
this.dialogRef.close();
|
||||
if (
|
||||
!!this.delteUserList &&
|
||||
this.delteUserList.length > 0 &&
|
||||
this.currentStep === 0
|
||||
) {
|
||||
let titleStr = '';
|
||||
this.delteUserList.forEach((user, idx) => {
|
||||
let userTitle = user.name + ' ' + user.grade;
|
||||
if (idx < this.delteUserList.length) {
|
||||
userTitle = userTitle + ', ';
|
||||
}
|
||||
titleStr = titleStr.concat('', userTitle);
|
||||
});
|
||||
const dialogRef = this.dialog.open<
|
||||
ConfirmDialogComponent,
|
||||
ConfirmDialogData,
|
||||
ConfirmDialogResult
|
||||
>(ConfirmDialogComponent, {
|
||||
data: {
|
||||
title: '동료 삭제',
|
||||
html: titleStr + '을 삭제하시겠습니까?'
|
||||
}
|
||||
});
|
||||
dialogRef
|
||||
.afterClosed()
|
||||
.pipe(
|
||||
take(1),
|
||||
map((result) => {
|
||||
if (!!result && result.choice) {
|
||||
const trgtUserSeq: string[] = [];
|
||||
|
||||
this.delteUserList.forEach((userIfno) => {
|
||||
const tempSeq = this.data.groupBuddyList.group.userSeqs.filter(
|
||||
(seq) => seq === userIfno.seq
|
||||
);
|
||||
|
||||
trgtUserSeq.push(tempSeq[0]);
|
||||
});
|
||||
|
||||
console.log(trgtUserSeq);
|
||||
|
||||
this.store.dispatch(
|
||||
GroupActions.updateMember({
|
||||
targetGroup: this.data.groupBuddyList.group,
|
||||
targetUserSeqs: trgtUserSeq
|
||||
})
|
||||
);
|
||||
this.dialogRef.close();
|
||||
}
|
||||
}),
|
||||
catchError((err) => {
|
||||
return of(err);
|
||||
})
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
onCopy(stepper: MatStepper) {
|
||||
this.dialogRef.close();
|
||||
onUpdateMember(stepper: MatStepper, type: GroupUserDialaogType) {
|
||||
this.dialogContainer.clear();
|
||||
this.currentType = type;
|
||||
const isMemberMove = type === GroupUserDialaogType.Copy ? false : true;
|
||||
// const title = type === GroupUserDialaogType.Copy ? '멤버 복사' : '멤버 이동';
|
||||
|
||||
const factory = this.cfResolver.resolveComponentFactory(
|
||||
SelectGroupSectionComponent
|
||||
);
|
||||
|
||||
this.componentRef = this.dialogContainer.createComponent(factory);
|
||||
const cpInstance = this.componentRef.instance;
|
||||
// cpInstance.title = title;
|
||||
cpInstance.isMemberMove = isMemberMove;
|
||||
cpInstance.isDialog = true;
|
||||
cpInstance.checkable = true;
|
||||
cpInstance.curGroup = this.data.groupBuddyList.group;
|
||||
cpInstance.changeUserList.subscribe((val) => {
|
||||
this.selectedUserList = val;
|
||||
});
|
||||
cpInstance.changeGroupList.subscribe((groupList) => {
|
||||
this.selectedGroupList = groupList;
|
||||
});
|
||||
cpInstance.changeGroupName.subscribe((groupName) => {
|
||||
this.groupName = groupName;
|
||||
});
|
||||
this.currentStep++;
|
||||
stepper.next();
|
||||
}
|
||||
onMove(stepper: MatStepper) {}
|
||||
|
||||
onAdd(stepper: MatStepper) {
|
||||
this.dialogContainer.clear();
|
||||
this.currentType = GroupUserDialaogType.Add;
|
||||
// this.selectedUserList = this.data.groupBuddyList.buddyList;
|
||||
|
||||
const factory = this.cfResolver.resolveComponentFactory(
|
||||
SelectUserSectionComponent
|
||||
);
|
||||
|
||||
this.componentRef = this.dialogContainer.createComponent(factory);
|
||||
const cpInstance = this.componentRef.instance;
|
||||
cpInstance.isDialog = true;
|
||||
cpInstance.checkable = true;
|
||||
cpInstance.selectedUserList = this.data.groupBuddyList.buddyList;
|
||||
cpInstance.isSelectionOff = false;
|
||||
cpInstance.changeUserList.subscribe((val) => {
|
||||
this.selectedUserList = val;
|
||||
});
|
||||
this.currentStep++;
|
||||
stepper.next();
|
||||
}
|
||||
|
@ -95,4 +223,119 @@ export class ManageDialogComponent implements OnInit, OnDestroy {
|
|||
onChangeUserList(selectedUserList: UserInfoTypes[]) {
|
||||
this.selectedUserList = selectedUserList;
|
||||
}
|
||||
|
||||
onCnacel(stepper: MatStepper) {
|
||||
if (!!this.selectedUserList && this.selectedUserList.length > 0) {
|
||||
this.selectedUserList = [];
|
||||
}
|
||||
this.currentStep--;
|
||||
stepper.previous();
|
||||
}
|
||||
onConfirm(stepper: MatStepper) {
|
||||
switch (this.currentType) {
|
||||
case GroupUserDialaogType.Add:
|
||||
{
|
||||
if (!!this.selectedUserList && this.selectedUserList.length > 0) {
|
||||
this.doAction();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GroupUserDialaogType.Copy:
|
||||
case GroupUserDialaogType.Move:
|
||||
{
|
||||
if (!!this.selectedUserList && this.selectedUserList.length === 0) {
|
||||
this.dialog.open<
|
||||
AlertDialogComponent,
|
||||
AlertDialogData,
|
||||
AlertDialogResult
|
||||
>(AlertDialogComponent, {
|
||||
data: {
|
||||
title: 'Error',
|
||||
html: '선택된 유저가 없습니다.'
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.doAction();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
doAction() {
|
||||
this.dialogContainer.clear();
|
||||
if (!!this.groupName && this.groupName.trim().localeCompare('') !== 0) {
|
||||
this.currentType = GroupUserDialaogType.Create;
|
||||
} else {
|
||||
this.groupName = undefined;
|
||||
}
|
||||
|
||||
this.dialogRef.close({
|
||||
type: this.currentType,
|
||||
groupName: this.groupName,
|
||||
group: this.data.groupBuddyList.group,
|
||||
selelctUserList: this.selectedUserList,
|
||||
selectGroupList: this.selectedGroupList
|
||||
});
|
||||
}
|
||||
|
||||
/** 개별 체크여부 */
|
||||
getCheckedUser(userInfo: UserInfoSS) {
|
||||
if (!!this.selectedUserList && this.selectedUserList.length > 0) {
|
||||
return (
|
||||
this.selectedUserList.filter(
|
||||
(item) => (item.seq as any) === (userInfo.seq as any)
|
||||
).length > 0
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
onToggleCheckForDelete(data: { checked: boolean; userInfo: UserInfoSS }) {
|
||||
if (data.checked) {
|
||||
this.delteUserList.push(data.userInfo);
|
||||
} else {
|
||||
const index = this.delteUserList.findIndex(
|
||||
(userInfo) => userInfo.seq === data.userInfo.seq
|
||||
);
|
||||
if (index > -1) {
|
||||
this.delteUserList.splice(index, 1);
|
||||
}
|
||||
}
|
||||
this.onToggleCheck(data);
|
||||
}
|
||||
onToggleCheck(data: { checked: boolean; userInfo: UserInfoSS }) {
|
||||
if (data.checked) {
|
||||
this.selectedUserList.push(data.userInfo);
|
||||
} else {
|
||||
const index = this.selectedUserList.findIndex(
|
||||
(userInfo) => userInfo.seq === data.userInfo.seq
|
||||
);
|
||||
if (index > -1) {
|
||||
this.selectedUserList.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onRemovedProfileSelection(userInfo: UserInfo) {
|
||||
const i = this.selectedUserList.findIndex(
|
||||
(u) => (u.seq as any) === (userInfo.seq as any)
|
||||
);
|
||||
|
||||
if (-1 < i) {
|
||||
this.selectedUserList = this.selectedUserList.filter(
|
||||
(u) => (u.seq as any) !== (userInfo.seq as any)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
removableForSelection = (userInfo: UserInfo) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
colorForSelection = (userInfo: UserInfo) => {
|
||||
return 'accent';
|
||||
};
|
||||
}
|
||||
|
|
|
@ -43,8 +43,6 @@ import { DIALOGS } from './dialogs';
|
|||
MatIconModule,
|
||||
MatCardModule,
|
||||
MatCheckboxModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatSelectModule,
|
||||
MatTabsModule,
|
||||
MatChipsModule,
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
<div class="title">
|
||||
{{ selectedDeptInfo | ucapOrganizationTranslate: 'name'
|
||||
}}<strong>{{
|
||||
!!searchData.isSearch ? searchUserList.length : departmentUserList.length
|
||||
!!searchObj.isShowSearch
|
||||
? searchUserList.length
|
||||
: departmentUserList.length
|
||||
}}</strong
|
||||
>{{ 'common.units.persons' | ucapI18n }}
|
||||
</div>
|
||||
|
@ -27,9 +29,28 @@
|
|||
</mat-checkbox>
|
||||
</div>
|
||||
<div class="table-body" fxFlexFill>
|
||||
<app-organization-profile-list
|
||||
[searchData]="searchData"
|
||||
></app-organization-profile-list>
|
||||
<cdk-virtual-scroll-viewport #cvsvList perfectScrollbar fxFlexFill>
|
||||
<ucap-organization-profile-list-item-01
|
||||
*cdkVirtualFor="
|
||||
let userInfo of !!searchObj.isShowSearch
|
||||
? searchUserList
|
||||
: departmentUserList
|
||||
"
|
||||
[userInfo]="userInfo"
|
||||
[checkable]="loginRes?.userSeq !== userInfo.seq"
|
||||
[checked]="getCheckedUser(userInfo)"
|
||||
(openProfile)="onOpenProfile($event)"
|
||||
(changeCheck)="onToggleUser($event)"
|
||||
>
|
||||
<ucap-organization-profile-image-01
|
||||
ucapOrganizationProfileListItem01="profileImage"
|
||||
[userInfo]="userInfo"
|
||||
[profileImageRoot]="versionInfo2Res?.profileRoot"
|
||||
defaultProfileImage="assets/images/ico/img_nophoto.svg"
|
||||
>
|
||||
</ucap-organization-profile-image-01>
|
||||
</ucap-organization-profile-list-item-01>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
</div>
|
||||
<div class="selected-users">
|
||||
<mat-accordion class="organization-accordion">
|
||||
|
|
|
@ -59,7 +59,26 @@ export class DepartmentUserVirtualScrollStrategy extends FixedSizeVirtualScrollS
|
|||
})
|
||||
export class DetailTableComponent implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
searchData: any;
|
||||
set searchObj(obj: {
|
||||
isShowSearch: boolean;
|
||||
companyCode: string;
|
||||
searchWord: string;
|
||||
}) {
|
||||
this._searchObj = obj;
|
||||
if (obj.isShowSearch && obj.searchWord.localeCompare('') !== 0) {
|
||||
this.onOrganizationTenantSearch(obj);
|
||||
} else {
|
||||
this._searchObj.isShowSearch = false;
|
||||
this.searchUserList = [];
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
get searchObj() {
|
||||
return this._searchObj;
|
||||
}
|
||||
// tslint:disable-next-line: variable-name
|
||||
_searchObj: any;
|
||||
|
||||
@Input()
|
||||
set deptSeq(deptSeq: string) {
|
||||
|
@ -198,13 +217,59 @@ export class DetailTableComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
onOrganizationTenantSearch(obj: {
|
||||
isShowSearch: boolean;
|
||||
companyCode: string;
|
||||
searchWord: string;
|
||||
}) {
|
||||
this.departmentUserListProcessing = true;
|
||||
|
||||
this.queryProtocolService
|
||||
.deptUser({
|
||||
divCd: 'ORGS',
|
||||
companyCode: this._searchObj.companyCode,
|
||||
searchRange: DeptSearchType.All,
|
||||
search: this._searchObj.searchWord,
|
||||
senderCompanyCode: this.loginRes.userInfo.companyCode,
|
||||
senderEmployeeType: this.loginRes.userInfo.employeeType
|
||||
})
|
||||
.pipe(
|
||||
map((resObj) => {
|
||||
// 검색 결과 처리.
|
||||
this.searchUserList = resObj.userInfos.sort((a, b) =>
|
||||
a.name < b.name ? -1 : a.name > b.name ? 1 : 0
|
||||
);
|
||||
this.changeDetectorRef.detectChanges();
|
||||
this.departmentUserListProcessing = false;
|
||||
|
||||
// 검색 결과에 따른 프레즌스 조회.
|
||||
const userSeqList: string[] = [];
|
||||
this.searchUserList.map((user) => userSeqList.push(user.seq));
|
||||
|
||||
if (userSeqList.length > 0) {
|
||||
this.store.dispatch(
|
||||
PresenceActions.bulkInfo({
|
||||
divCd: 'orgSrch',
|
||||
userSeqs: userSeqList
|
||||
})
|
||||
);
|
||||
}
|
||||
}),
|
||||
catchError((error) => {
|
||||
this.departmentUserListProcessing = false;
|
||||
return of(error);
|
||||
})
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
/** 전체선택 체크여부 */
|
||||
getCheckedAllUser() {
|
||||
if (!this.loginRes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const compareList: UserInfoSS[] = !!this.searchData.isSearch
|
||||
const compareList: UserInfoSS[] = !!this.searchObj.isShowSearch
|
||||
? this.searchUserList
|
||||
: this.departmentUserList;
|
||||
if (
|
||||
|
@ -233,7 +298,7 @@ export class DetailTableComponent implements OnInit, OnDestroy {
|
|||
return false;
|
||||
}
|
||||
|
||||
const trgtUserList = !!this.searchData.isSearch
|
||||
const trgtUserList = !!this.searchObj.isShowSearch
|
||||
? this.searchUserList
|
||||
: this.departmentUserList;
|
||||
|
||||
|
|
|
@ -1,34 +1,99 @@
|
|||
<div class="member-list-container" fxLayout="column">
|
||||
<!-- search start-->
|
||||
<div fxFlex="0 0 50px">
|
||||
<app-organization-search-for-tenant (changed)="onChangedSearch($event)">
|
||||
</app-organization-search-for-tenant>
|
||||
</div>
|
||||
<!-- search end-->
|
||||
|
||||
<!-- content table start-->
|
||||
<div class="member-list-body" fxFlex="1 1 auto" fxLayout="column">
|
||||
<div class="list-header" fxFlex="0 0 48px" fxLayout="row">
|
||||
<div class="list-header-title" fxFlex="1 1 auto">
|
||||
<h5>아키텍처솔루션팀 <strong>20</strong>명</h5>
|
||||
</div>
|
||||
<div class="list-header-toolbox" fxFlex="0 0 100px">
|
||||
이름
|
||||
<mat-icon matSuffix class="selet-ico-obj" matRipple unbounded="true">
|
||||
format_line_spacing
|
||||
</mat-icon>
|
||||
<mat-checkbox
|
||||
class="subtitle-chk-box"
|
||||
#checkboxAll
|
||||
(click)="$event.stopPropagation()"
|
||||
>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
<div class="list-header" fxFlex="0 0 48px" fxLayout="row">
|
||||
<div class="list-header-title" fxFlex="1 1 auto">
|
||||
<h5>
|
||||
<ng-container *ngIf="!!selectedDeptInfo">
|
||||
{{ selectedDeptInfo | ucapOrganizationTranslate: 'name' }}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!!selectedCompanyInfo">
|
||||
{{ selectedCompanyInfo.companyName }}
|
||||
</ng-container>
|
||||
<strong>{{ searchedProfileLength }}</strong
|
||||
>명
|
||||
</h5>
|
||||
</div>
|
||||
<div class="list-header-toolbox" fxFlex="0 0 100px">
|
||||
이름
|
||||
<button mat-icon-button aria-label="icon" (click)="onClickToggleSort()">
|
||||
<mat-icon>format_line_spacing</mat-icon>
|
||||
</button>
|
||||
|
||||
<mat-checkbox
|
||||
class="subtitle-chk-box"
|
||||
#checkboxAll
|
||||
(change)="onChangeSelectAll($event)"
|
||||
>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
<div fxFlex="1 1 auto" fxLayout="column">
|
||||
<div fxFlex="1 1 auto">
|
||||
<app-organization-profile-list [searchData]="_searchData">
|
||||
<app-organization-profile-list
|
||||
#profileList
|
||||
[searchData]="_searchData"
|
||||
[selectedUser]="selectedUserInfos"
|
||||
[sortOrder]="sortOrderForProfileList"
|
||||
(searched)="onSearchedProfileList($event)"
|
||||
(changedCheck)="onChangedCheckProfileList($event)"
|
||||
>
|
||||
</app-organization-profile-list>
|
||||
</div>
|
||||
<div
|
||||
[style.display]="
|
||||
selectedUserInfos && 0 < selectedUserInfos.length ? '' : 'none'
|
||||
"
|
||||
class="selected-users"
|
||||
[fxFlex]="isExpanded ? '0 0 200px' : '0 0 40px'"
|
||||
>
|
||||
<mat-accordion class="organization-accordion">
|
||||
<mat-expansion-panel
|
||||
(opened)="onOpenedSelection()"
|
||||
(closed)="onClosedSelection()"
|
||||
>
|
||||
<mat-expansion-panel-header
|
||||
collapsedHeight="40px"
|
||||
expandedHeight="40px"
|
||||
class="organization-accordion-head"
|
||||
>
|
||||
<mat-panel-title class="select-user-title">
|
||||
선택한 대화상대
|
||||
<strong>{{
|
||||
!!selectedUserInfos ? selectedUserInfos.length : 0
|
||||
}}</strong>
|
||||
</mat-panel-title>
|
||||
<mat-panel-description> </mat-panel-description>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
<div class="selected-user-list">
|
||||
<ucap-organization-profile-selection-01
|
||||
[userInfoList]="selectedUserInfos"
|
||||
[removableFor]="removableForSelection"
|
||||
[colorFor]="colorForSelection"
|
||||
(removed)="onRemovedProfileSelection($event)"
|
||||
>
|
||||
</ucap-organization-profile-selection-01>
|
||||
</div>
|
||||
<div class="btn-box">
|
||||
<button mat-stroked-button class="mat-basic">
|
||||
그룹추가
|
||||
</button>
|
||||
<button mat-flat-button class="bg-primary-darkest">
|
||||
대화
|
||||
</button>
|
||||
<button mat-flat-button class="bg-primary-dark">
|
||||
쪽지
|
||||
</button>
|
||||
<button mat-flat-button class="bg-primary-dark" disabled>
|
||||
전화
|
||||
</button>
|
||||
<button mat-flat-button class="bg-primary-dark">
|
||||
화상
|
||||
</button>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
</div>
|
||||
</div>
|
||||
<!-- content table end-->
|
||||
</div>
|
||||
|
|
|
@ -1,34 +1,59 @@
|
|||
@import '~@ucap/lg-scss/mixins';
|
||||
|
||||
.member-list-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.member-list-body {
|
||||
align-content: space-between;
|
||||
padding: 0 30px;
|
||||
background-color: white;
|
||||
align-content: space-between;
|
||||
padding: 0 30px;
|
||||
background-color: white;
|
||||
|
||||
.list-header {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 2px solid #999999;
|
||||
padding: 12px 0 13px;
|
||||
.list-header {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 2px solid #999999;
|
||||
padding: 12px 0 13px;
|
||||
|
||||
.list-header-title {
|
||||
h5 {
|
||||
font-size: 13px;
|
||||
align-items: center;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
flex-grow: 1;
|
||||
strong {
|
||||
color: #e42f66;
|
||||
}
|
||||
.list-header-title {
|
||||
h5 {
|
||||
font-size: 13px;
|
||||
align-items: center;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
flex-grow: 1;
|
||||
strong {
|
||||
color: #e42f66;
|
||||
}
|
||||
}
|
||||
.list-header-toolbox {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
}
|
||||
.list-header-toolbox {
|
||||
right: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.selected-users {
|
||||
flex-grow: 0.8;
|
||||
.organization-accordion-head {
|
||||
background-color: #f1f2f6;
|
||||
}
|
||||
.select-user-title {
|
||||
strong {
|
||||
color: $lipstick;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.selected-user-list {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.btn-box {
|
||||
margin-top: 10px;
|
||||
padding-right: 8px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-content: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,89 +1,83 @@
|
|||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
import {
|
||||
Component,
|
||||
ViewChild,
|
||||
OnInit,
|
||||
ChangeDetectorRef,
|
||||
Input,
|
||||
OnDestroy,
|
||||
ChangeDetectionStrategy
|
||||
ChangeDetectionStrategy,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { SelectionModel } from '@angular/cdk/collections';
|
||||
import { MatSort } from '@angular/material/sort';
|
||||
import { Subject, of } from 'rxjs';
|
||||
import { map, takeUntil, take, catchError } from 'rxjs/operators';
|
||||
import {
|
||||
DeptInfo,
|
||||
DeptSearchType,
|
||||
DeptUserRequest,
|
||||
UserInfoSS,
|
||||
AuthResponse
|
||||
} from '@ucap/protocol-query';
|
||||
|
||||
import { select, Store } from '@ngrx/store';
|
||||
|
||||
import { SortOrder } from '@ucap/core';
|
||||
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||
import { DeptInfo, UserInfoSS } from '@ucap/protocol-query';
|
||||
import { UserInfo } from '@ucap/protocol-sync';
|
||||
|
||||
import { LoginSelector } from '@ucap/ng-store-authentication';
|
||||
import {
|
||||
DepartmentSelector,
|
||||
PresenceActions
|
||||
CompanySelector
|
||||
} from '@ucap/ng-store-organization';
|
||||
import { select, Store } from '@ngrx/store';
|
||||
import { QueryProtocolService } from '@ucap/ng-protocol-query';
|
||||
import {
|
||||
LoginSelector,
|
||||
AuthorizationSelector,
|
||||
ConfigurationSelector
|
||||
} from '@ucap/ng-store-authentication';
|
||||
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||
import {
|
||||
FixedSizeVirtualScrollStrategy,
|
||||
VIRTUAL_SCROLL_STRATEGY,
|
||||
CdkVirtualScrollViewport
|
||||
} from '@angular/cdk/scrolling';
|
||||
import { VersionInfo2Response } from '@ucap/api-public';
|
||||
import { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
|
||||
|
||||
export class MemberVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy {
|
||||
constructor() {
|
||||
super(184, 150, 200);
|
||||
}
|
||||
}
|
||||
import { SearchData } from '@app/ucap/organization/models/search-data';
|
||||
import { Company } from '@ucap/api-external';
|
||||
import { MatCheckboxChange } from '@angular/material/checkbox';
|
||||
import { ProfileListComponent as AppProfileListComponent } from '@app/ucap/organization/components/profile-list.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-sections-organization-member-list',
|
||||
templateUrl: './member-list.component.html',
|
||||
styleUrls: ['./member-list.component.scss'],
|
||||
providers: [
|
||||
{
|
||||
provide: VIRTUAL_SCROLL_STRATEGY,
|
||||
useClass: MemberVirtualScrollStrategy
|
||||
}
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class MemberListComponent implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
set searchData(data: {
|
||||
companyCode: string;
|
||||
searchWord: string;
|
||||
isSearch: boolean;
|
||||
}) {
|
||||
this._searchData = data;
|
||||
set searchData(searchData: SearchData) {
|
||||
this._searchData = searchData;
|
||||
|
||||
if (searchData.bySearch) {
|
||||
this.setCompanyInfo(searchData.companyCode);
|
||||
} else {
|
||||
this.setDeptInfo(searchData.deptSeq);
|
||||
}
|
||||
}
|
||||
|
||||
@ViewChild('profileList', { static: false })
|
||||
profileList: AppProfileListComponent;
|
||||
|
||||
// tslint:disable-next-line: variable-name
|
||||
_searchData: {
|
||||
companyCode: string;
|
||||
searchWord: string;
|
||||
isSearch: boolean;
|
||||
_searchData: SearchData;
|
||||
loginRes: LoginResponse;
|
||||
selectedDeptInfo: DeptInfo;
|
||||
selectedCompanyInfo: Company;
|
||||
searchedProfileLength: number;
|
||||
selectedUserInfos: UserInfoSS[] = [];
|
||||
isExpanded = false;
|
||||
sortOrderForProfileList: SortOrder = {
|
||||
property: 'name',
|
||||
ascending: true
|
||||
};
|
||||
|
||||
private ngOnDestroySubject: Subject<void>;
|
||||
|
||||
constructor(
|
||||
private store: Store<any>,
|
||||
private queryProtocolService: QueryProtocolService,
|
||||
private changeDetectorRef: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.ngOnDestroySubject = new Subject();
|
||||
|
||||
this.store
|
||||
.pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
|
||||
.subscribe((loginRes) => {
|
||||
this.loginRes = loginRes;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
|
@ -92,11 +86,131 @@ export class MemberListComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
onChangedSearch(data: {
|
||||
isSearch: boolean;
|
||||
companyCode: string;
|
||||
searchWord: string;
|
||||
}) {
|
||||
this._searchData = data;
|
||||
onChangedSearch(data: { deptSeq: string; searchWord: string }) {
|
||||
this._searchData = {
|
||||
bySearch: true,
|
||||
...data
|
||||
};
|
||||
this.setDeptInfo(data.deptSeq);
|
||||
}
|
||||
|
||||
onSearchedProfileList(userInfos: UserInfoSS[]) {
|
||||
this.searchedProfileLength = !!userInfos ? userInfos.length : 0;
|
||||
}
|
||||
|
||||
onChangedCheckProfileList(
|
||||
datas: { checked: boolean; userInfo: UserInfoSS }[]
|
||||
) {
|
||||
if (!datas || 0 === datas.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pushs: UserInfoSS[] = [];
|
||||
const pops: UserInfoSS[] = [];
|
||||
|
||||
datas.forEach((d) => {
|
||||
const i = this.selectedUserInfos.findIndex(
|
||||
(u) => u.seq === d.userInfo.seq
|
||||
);
|
||||
if (d.checked) {
|
||||
if (-1 === i) {
|
||||
pushs.push(d.userInfo);
|
||||
}
|
||||
} else {
|
||||
if (-1 < i) {
|
||||
pops.push(d.userInfo);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (0 < pushs.length) {
|
||||
this.selectedUserInfos = [...this.selectedUserInfos, ...pushs];
|
||||
}
|
||||
|
||||
if (0 < pops.length) {
|
||||
this.selectedUserInfos = this.selectedUserInfos.filter(
|
||||
(u) => -1 === pops.findIndex((p) => p.seq === u.seq)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
onRemovedProfileSelection(userInfo: UserInfo) {
|
||||
const i = this.selectedUserInfos.findIndex(
|
||||
(u) => u.seq === String(userInfo.seq)
|
||||
);
|
||||
|
||||
if (-1 < i) {
|
||||
this.selectedUserInfos = this.selectedUserInfos.filter(
|
||||
(u) => u.seq !== String(userInfo.seq)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
removableForSelection = (userInfo: UserInfo) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
colorForSelection = (userInfo: UserInfo) => {
|
||||
return 'accent';
|
||||
};
|
||||
|
||||
onOpenedSelection() {
|
||||
this.isExpanded = true;
|
||||
}
|
||||
|
||||
onClosedSelection() {
|
||||
this.isExpanded = false;
|
||||
}
|
||||
|
||||
onClickToggleSort() {
|
||||
this.sortOrderForProfileList = {
|
||||
...this.sortOrderForProfileList,
|
||||
ascending: !this.sortOrderForProfileList.ascending
|
||||
};
|
||||
}
|
||||
|
||||
onChangeSelectAll(event: MatCheckboxChange) {
|
||||
if (event.checked) {
|
||||
this.profileList.checkAll();
|
||||
} else {
|
||||
this.selectedUserInfos = [];
|
||||
}
|
||||
}
|
||||
|
||||
private setCompanyInfo(companyCode: string) {
|
||||
const destroySubject: Subject<void> = new Subject();
|
||||
this.store
|
||||
.pipe(takeUntil(destroySubject), select(CompanySelector.companyList))
|
||||
.subscribe((companyList) => {
|
||||
if (!companyList) {
|
||||
return;
|
||||
}
|
||||
this.selectedCompanyInfo = companyList.find(
|
||||
(c) => c.companyCode === companyCode
|
||||
);
|
||||
this.changeDetectorRef.markForCheck();
|
||||
destroySubject.next();
|
||||
destroySubject.complete();
|
||||
});
|
||||
}
|
||||
|
||||
private setDeptInfo(seq: string) {
|
||||
const destroySubject: Subject<void> = new Subject();
|
||||
this.store
|
||||
.pipe(
|
||||
takeUntil(destroySubject),
|
||||
select(DepartmentSelector.departmentInfoList)
|
||||
)
|
||||
.subscribe((departmentInfoList) => {
|
||||
if (!departmentInfoList) {
|
||||
return;
|
||||
}
|
||||
this.selectedDeptInfo = departmentInfoList.find(
|
||||
(d) => String(d.seq) === seq
|
||||
);
|
||||
this.changeDetectorRef.markForCheck();
|
||||
destroySubject.next();
|
||||
destroySubject.complete();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import { MatChipsModule } from '@angular/material/chips';
|
|||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
|
||||
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
||||
|
||||
|
@ -42,6 +43,7 @@ import { COMPONENTS } from './components';
|
|||
MatTableModule,
|
||||
MatButtonModule,
|
||||
MatExpansionModule,
|
||||
MatSidenavModule,
|
||||
|
||||
PerfectScrollbarModule,
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { Observable } from 'rxjs';
|
||||
import { take, concatMap, map } from 'rxjs/operators';
|
||||
import { Observable, of, forkJoin } from 'rxjs';
|
||||
import { take, concatMap, map, catchError } from 'rxjs/operators';
|
||||
|
||||
import { Injectable, Inject, ChangeDetectorRef } from '@angular/core';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { LocaleCode } from '@ucap/core';
|
||||
import { LocaleCode, DeviceType, FileUtil } from '@ucap/core';
|
||||
|
||||
import { PasswordUtil } from '@ucap/pi';
|
||||
import { LoginResponse, SSOMode } from '@ucap/protocol-authentication';
|
||||
|
@ -26,7 +26,12 @@ import { UserStore } from '@app/models/user-store';
|
|||
import { AppKey } from '@app/types/app-key.type';
|
||||
|
||||
import { environment } from '@environments';
|
||||
import { RoomInfo, RoomType } from '@ucap/protocol-room';
|
||||
import {
|
||||
RoomInfo,
|
||||
RoomType,
|
||||
OpenRequest,
|
||||
Open3Request
|
||||
} from '@ucap/protocol-room';
|
||||
import { Dictionary } from '@ngrx/entity';
|
||||
import {
|
||||
RoomUserMap,
|
||||
|
@ -38,6 +43,31 @@ import {
|
|||
TranslateService
|
||||
} from '@ucap/ng-ui-organization';
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
import { ChattingActions, RoomActions } from '@ucap/ng-store-chat';
|
||||
import {
|
||||
SendRequest as SendEventRequest,
|
||||
EventType
|
||||
} from '@ucap/protocol-event';
|
||||
import {
|
||||
MassTalkSaveRequest,
|
||||
FileTalkSaveResponse,
|
||||
FileTalkSaveRequest
|
||||
} from '@ucap/api-common';
|
||||
import { CommonApiService } from '@ucap/ng-api-common';
|
||||
import { StatusCode, FileUploadItem } from '@ucap/api';
|
||||
import { LogService } from '@ucap/ng-logger';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import {
|
||||
AlertDialogComponent,
|
||||
AlertDialogData,
|
||||
AlertDialogResult
|
||||
} from '@ucap/ng-ui';
|
||||
import { StickerFilesInfo, KEY_STICKER_HISTORY } from '@ucap/ng-core';
|
||||
import {
|
||||
CreateDialogComponent,
|
||||
CreateDialogData,
|
||||
CreateDialogResult
|
||||
} from '@app/sections/chat/dialogs/create.dialog.component';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
@ -46,8 +76,21 @@ export class AppChatService {
|
|||
defaultProfileImage = 'assets/images/ico/img_nophoto.svg';
|
||||
defaultProfileImageMulti = 'assets/images/ico/img_nophoto.svg';
|
||||
|
||||
constructor(private i18nService: I18nService) {}
|
||||
constructor(
|
||||
private i18nService: I18nService,
|
||||
private dialog: MatDialog,
|
||||
private localStorageService: LocalStorageService,
|
||||
private store: Store<any>,
|
||||
private commonApiService: CommonApiService,
|
||||
private logService: LogService
|
||||
) {
|
||||
this.i18nService.setDefaultNamespace('chat');
|
||||
}
|
||||
|
||||
/**
|
||||
* 방이름 생성.
|
||||
* cf) 방이름이 지정되어 있다면 방이름 리턴, 지정되어 있지 않으면 방참여인원의 이름 조합.
|
||||
*/
|
||||
getRoomName(
|
||||
organizationTranslate: OrganizationTranslate,
|
||||
loginRes: LoginResponse,
|
||||
|
@ -109,6 +152,12 @@ export class AppChatService {
|
|||
return roomName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 방 프로필 이미지 생성.
|
||||
* cf) 방 참여인원의 프로필을 리턴.
|
||||
* 없으면, defaultProfileImage, defaultProfileImageMulti,
|
||||
* 멀티룸은 기본 defaultProfileImageMulti
|
||||
*/
|
||||
getRoomProfileImage(
|
||||
roomInfo: RoomInfo,
|
||||
loginRes: LoginResponse,
|
||||
|
@ -164,6 +213,11 @@ export class AppChatService {
|
|||
return roomImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 방 인원 정보 수집.
|
||||
* cf) roomUserShort, roomUser 데이터가 쌍을 이루는데 수집방법에 따라 short 만 존재할 수 있어, 수집할 수 있는 방인원을 리턴.
|
||||
* roomUser 가 detail 정보라 우선함.
|
||||
*/
|
||||
getRoomUserList(
|
||||
loginRes: LoginResponse,
|
||||
roomId: string,
|
||||
|
@ -197,4 +251,431 @@ export class AppChatService {
|
|||
users
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 스티커 히스토리 관리 in localstorage.
|
||||
* cf) 스티커 전송 성공분만 처리.
|
||||
*/
|
||||
setStickerHistory(sticker: StickerFilesInfo) {
|
||||
const history = this.localStorageService.get<string[]>(KEY_STICKER_HISTORY);
|
||||
|
||||
if (!!history && history.length > 0) {
|
||||
const stickers: string[] = [];
|
||||
[sticker.index, ...history.filter((hist) => hist !== sticker.index)].map(
|
||||
(s, i) => {
|
||||
if (i < 10) {
|
||||
stickers.push(s);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.localStorageService.set<string[]>(KEY_STICKER_HISTORY, stickers);
|
||||
} else {
|
||||
this.localStorageService.set<string[]>(KEY_STICKER_HISTORY, [
|
||||
sticker.index
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event Send Protocol Service.
|
||||
*/
|
||||
protected sendEvent(
|
||||
senderSeq: string,
|
||||
roomId: string,
|
||||
eventType: EventType,
|
||||
sentMessage: string
|
||||
) {
|
||||
this.store.dispatch(
|
||||
ChattingActions.send({
|
||||
senderSeq,
|
||||
req: {
|
||||
roomId,
|
||||
eventType,
|
||||
sentMessage
|
||||
} as SendEventRequest
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/** Send Normal message */
|
||||
sendMessageOfNormal(senderSeq: string, roomId: string, sentMessage: string) {
|
||||
this.sendEvent(senderSeq, roomId, EventType.Character, sentMessage);
|
||||
}
|
||||
|
||||
/** Send Masstext message */
|
||||
sendMessageOfMassText(
|
||||
loginRes: LoginResponse,
|
||||
deviceType: DeviceType,
|
||||
roomId: string,
|
||||
sentMessage: string
|
||||
) {
|
||||
const req: MassTalkSaveRequest = {
|
||||
userSeq: loginRes.userSeq,
|
||||
deviceType,
|
||||
token: loginRes.tokenString,
|
||||
content: sentMessage.replace(/"/g, '\\"'),
|
||||
roomId
|
||||
};
|
||||
|
||||
this.commonApiService
|
||||
.massTalkSave(req)
|
||||
.pipe(
|
||||
take(1),
|
||||
map((res) => {
|
||||
if (res.statusCode === StatusCode.Success) {
|
||||
this.sendEvent(
|
||||
loginRes.userSeq,
|
||||
roomId,
|
||||
EventType.MassText,
|
||||
res.returnJson
|
||||
);
|
||||
} else {
|
||||
this.logService.error(
|
||||
`commonApiService] massTalkSave ${res?.errorMessage}`
|
||||
);
|
||||
}
|
||||
}),
|
||||
catchError((error) => of({ error }))
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
async sendMessageOfSticker(
|
||||
senderSeq: string,
|
||||
roomId: string,
|
||||
selectedSticker: StickerFilesInfo,
|
||||
sentMessage: string
|
||||
) {
|
||||
// Validation
|
||||
if (
|
||||
!!sentMessage &&
|
||||
sentMessage.trim().length > environment.productConfig.chat.masstextLength
|
||||
) {
|
||||
const result = await this.dialog.open<
|
||||
AlertDialogComponent,
|
||||
AlertDialogData,
|
||||
AlertDialogResult
|
||||
>(AlertDialogComponent, {
|
||||
panelClass: 'miniSize-dialog',
|
||||
data: {
|
||||
title: this.i18nService.t('errors.label'),
|
||||
message: this.i18nService.t('errors.maxLengthOfMassText', {
|
||||
maxLength: environment.productConfig.chat.masstextLength
|
||||
})
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Send
|
||||
this.sendEvent(
|
||||
senderSeq,
|
||||
roomId,
|
||||
EventType.Sticker,
|
||||
JSON.stringify({
|
||||
name: '스티커',
|
||||
file: selectedSticker.index,
|
||||
chat: !!sentMessage ? sentMessage.trim() : ''
|
||||
})
|
||||
);
|
||||
|
||||
// set sticker's history in localstorage
|
||||
this.setStickerHistory(selectedSticker);
|
||||
}
|
||||
|
||||
/** Send Translation message */
|
||||
sendMessageOfTranslate(
|
||||
loginRes: LoginResponse,
|
||||
deviceType: DeviceType,
|
||||
destLocale: string,
|
||||
roomId: string,
|
||||
sentMessage: string,
|
||||
selectedSticker?: StickerFilesInfo
|
||||
) {
|
||||
// const destLocale = this.destLocale;
|
||||
// const original = message;
|
||||
// const roomSeq = this.roomInfoSubject.value.roomSeq;
|
||||
// if (!!this.isTranslationProcess) {
|
||||
// return;
|
||||
// }
|
||||
// this.isTranslationProcess = true;
|
||||
// this.commonApiService
|
||||
// .translationSave({
|
||||
// userSeq: this.loginResSubject.value.userSeq,
|
||||
// deviceType: this.environmentsInfo.deviceType,
|
||||
// token: this.loginResSubject.value.tokenString,
|
||||
// roomSeq,
|
||||
// original,
|
||||
// srcLocale: '',
|
||||
// destLocale
|
||||
// } as TranslationSaveRequest)
|
||||
// .pipe(
|
||||
// take(1),
|
||||
// map((res) => {
|
||||
// if (res.statusCode === StatusCode.Success) {
|
||||
// let sentMessage = '';
|
||||
// let eventType = EventType.Translation;
|
||||
// let previewObject: TranslationEventJson | MassTranslationEventJson;
|
||||
// if (res.translationSeq > 0) {
|
||||
// // Mass Text Translation
|
||||
// previewObject = res;
|
||||
// sentMessage = res.returnJson;
|
||||
// eventType = EventType.MassTranslation;
|
||||
// } else {
|
||||
// // Normal Text Translation
|
||||
// previewObject = {
|
||||
// locale: destLocale,
|
||||
// original,
|
||||
// translation: res.translation,
|
||||
// stickername: '',
|
||||
// stickerfile: !!this.selectedSticker
|
||||
// ? this.selectedSticker.index
|
||||
// : ''
|
||||
// };
|
||||
// sentMessage = JSON.stringify(previewObject);
|
||||
// eventType = EventType.Translation;
|
||||
// }
|
||||
// if (!!this.translationPreview) {
|
||||
// // preview
|
||||
// this.translationPreviewInfo = {
|
||||
// previewInfo: res,
|
||||
// translationType: eventType
|
||||
// };
|
||||
// this.changeDetectorRef.detectChanges();
|
||||
// } else {
|
||||
// // direct send
|
||||
// this.store.dispatch(
|
||||
// EventStore.send({
|
||||
// senderSeq: this.loginResSubject.value.userSeq,
|
||||
// req: {
|
||||
// roomSeq,
|
||||
// eventType,
|
||||
// sentMessage
|
||||
// }
|
||||
// })
|
||||
// );
|
||||
// if (!!this.translationPreviewInfo) {
|
||||
// this.translationPreviewInfo = null;
|
||||
// }
|
||||
// }
|
||||
// if (!!this.selectedSticker) {
|
||||
// this.isShowStickerSelector = false;
|
||||
// this.setStickerHistory(this.selectedSticker);
|
||||
// this.selectedSticker = null;
|
||||
// }
|
||||
// } else {
|
||||
// this.isTranslationProcess = false;
|
||||
// this.dialogService.open<
|
||||
// AlertDialogComponent,
|
||||
// AlertDialogData,
|
||||
// AlertDialogResult
|
||||
// >(AlertDialogComponent, {
|
||||
// panelClass: 'miniSize-dialog',
|
||||
// data: {
|
||||
// title: '',
|
||||
// message: this.translateService.instant(
|
||||
// 'chat.error.translateServerError'
|
||||
// )
|
||||
// }
|
||||
// });
|
||||
// this.logger.error('res', res);
|
||||
// }
|
||||
// }),
|
||||
// catchError((error) => {
|
||||
// this.isTranslationProcess = false;
|
||||
// this.dialogService.open<
|
||||
// AlertDialogComponent,
|
||||
// AlertDialogData,
|
||||
// AlertDialogResult
|
||||
// >(AlertDialogComponent, {
|
||||
// panelClass: 'miniSize-dialog',
|
||||
// data: {
|
||||
// title: '',
|
||||
// message: this.translateService.instant(
|
||||
// 'chat.error.translateServerError'
|
||||
// )
|
||||
// }
|
||||
// });
|
||||
// return of(this.logger.error('error', error));
|
||||
// })
|
||||
// )
|
||||
// .subscribe(() => {
|
||||
// this.isTranslationProcess = false;
|
||||
// });
|
||||
}
|
||||
|
||||
/** Send AttachFile message */
|
||||
sendMessageOfAttachFile(
|
||||
loginRes: LoginResponse,
|
||||
deviceType: DeviceType,
|
||||
roomId: string,
|
||||
fileUploadItems: FileUploadItem[]
|
||||
): Promise<boolean> {
|
||||
const executor = async (
|
||||
resolve: (value?: boolean | PromiseLike<boolean>) => void,
|
||||
reject: (reason?: any) => void
|
||||
) => {
|
||||
const allObservables: Observable<FileTalkSaveResponse>[] = [];
|
||||
|
||||
for (const fileUploadItem of fileUploadItems) {
|
||||
let thumbnail: File;
|
||||
if (
|
||||
-1 !==
|
||||
[
|
||||
'3gp',
|
||||
'avi',
|
||||
'm4v',
|
||||
'mkv',
|
||||
'mov',
|
||||
'mp4',
|
||||
'mpeg',
|
||||
'mpg',
|
||||
'rv',
|
||||
'ts',
|
||||
'webm',
|
||||
'wmv'
|
||||
].indexOf(FileUtil.getExtension(fileUploadItem.file.name))
|
||||
) {
|
||||
try {
|
||||
thumbnail = await FileUtil.thumbnail(fileUploadItem.file);
|
||||
} catch (err) {
|
||||
this.logService.error('video thumbnail error.', err);
|
||||
}
|
||||
this.logService.debug('thumbnail', thumbnail);
|
||||
}
|
||||
|
||||
const req: FileTalkSaveRequest = {
|
||||
userSeq: loginRes.userSeq,
|
||||
deviceType,
|
||||
token: loginRes.tokenString,
|
||||
roomId,
|
||||
file: fileUploadItem.file,
|
||||
fileName: fileUploadItem.file.name,
|
||||
thumb: thumbnail,
|
||||
fileUploadItem
|
||||
};
|
||||
|
||||
allObservables.push(
|
||||
this.commonApiService.fileTalkSave(req).pipe(
|
||||
map((res) => {
|
||||
if (!res) {
|
||||
return;
|
||||
}
|
||||
if (StatusCode.Success === res.statusCode) {
|
||||
return res;
|
||||
} else {
|
||||
throw res;
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
forkJoin(allObservables)
|
||||
.pipe(take(1))
|
||||
.subscribe(
|
||||
(resList) => {
|
||||
for (const res of resList) {
|
||||
this.store.dispatch(
|
||||
ChattingActions.send({
|
||||
senderSeq: loginRes.userSeq,
|
||||
req: {
|
||||
roomId,
|
||||
eventType: EventType.File,
|
||||
sentMessage: JSON.stringify(res.returnJson)
|
||||
} as SendEventRequest
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
this.logService.debug('onFileSelected error', error);
|
||||
const msg = this.i18nService.t('common.file.errors.failToUpload');
|
||||
alert(msg);
|
||||
|
||||
reject(msg);
|
||||
},
|
||||
() => {
|
||||
resolve(true);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return new Promise<boolean>(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open Dialog for 'New Room Open'.
|
||||
*/
|
||||
newOpenRoomDialog(): void {
|
||||
const dialogRef = this.dialog.open<
|
||||
CreateDialogComponent,
|
||||
CreateDialogData,
|
||||
CreateDialogResult
|
||||
>(CreateDialogComponent, {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
data: {}
|
||||
});
|
||||
|
||||
dialogRef
|
||||
.afterClosed()
|
||||
.pipe(
|
||||
take(1),
|
||||
map((result) => {
|
||||
if (!!result && !!result.userSeqs && result.userSeqs.length > 0) {
|
||||
this.newOpenRoom(result.userSeqs, result.isTimer);
|
||||
}
|
||||
}),
|
||||
catchError((err) => {
|
||||
return of(err);
|
||||
})
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
newOpenRoom(
|
||||
userSeqs: string[],
|
||||
isTimerRoom: boolean,
|
||||
loginRes?: LoginResponse
|
||||
) {
|
||||
if (!userSeqs || userSeqs.length === 0) {
|
||||
return;
|
||||
}
|
||||
isTimerRoom = isTimerRoom || false;
|
||||
|
||||
if (!!isTimerRoom) {
|
||||
/** Timer Room Open. */
|
||||
const req: Open3Request = {
|
||||
divCd: 'OPROOMT',
|
||||
roomName: '',
|
||||
isTimerRoom,
|
||||
timerRoomInterval:
|
||||
environment.productConfig.chat.timerRoomDefaultInterval,
|
||||
userSeqs
|
||||
};
|
||||
this.store.dispatch(RoomActions.createTimer({ req }));
|
||||
} else {
|
||||
/** Normal Room Open */
|
||||
let req: OpenRequest;
|
||||
if (
|
||||
userSeqs.length === 1 &&
|
||||
!!loginRes &&
|
||||
userSeqs[0] === loginRes.userSeq
|
||||
) {
|
||||
// MyTalk Open.
|
||||
req = {
|
||||
divCd: 'OPMYTALK',
|
||||
userSeqs: [loginRes.talkWithMeBotSeq + '']
|
||||
};
|
||||
} else {
|
||||
req = {
|
||||
divCd: 'OPROOM',
|
||||
userSeqs
|
||||
};
|
||||
}
|
||||
this.store.dispatch(RoomActions.create({ req }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
86
src/app/services/app-file.service.ts
Normal file
86
src/app/services/app-file.service.ts
Normal file
|
@ -0,0 +1,86 @@
|
|||
import { Injectable, Inject } from '@angular/core';
|
||||
|
||||
import { FileUtil } from '@ucap/core';
|
||||
import { I18nService, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
|
||||
import { CommonApiService } from '@ucap/ng-api-common';
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AppFileService {
|
||||
constructor(
|
||||
private store: Store<any>,
|
||||
private i18nService: I18nService,
|
||||
private commonApiService: CommonApiService
|
||||
) {
|
||||
this.i18nService.setDefaultNamespace('common');
|
||||
}
|
||||
|
||||
async validUploadFile(
|
||||
fileList: FileList,
|
||||
fileAllowSize: number = 50
|
||||
): Promise<boolean> {
|
||||
let valid = true;
|
||||
|
||||
// File size check.
|
||||
// tslint:disable-next-line: prefer-for-of
|
||||
for (let i = 0; i < fileList.length; i++) {
|
||||
const file = fileList[i];
|
||||
if (file.size > fileAllowSize * 1024 * 1024) {
|
||||
const msg = this.i18nService.t('common.file.errors.oversize', {
|
||||
maxSize: fileAllowSize
|
||||
});
|
||||
alert(msg);
|
||||
|
||||
valid = false;
|
||||
return valid;
|
||||
}
|
||||
}
|
||||
|
||||
// Extention check.
|
||||
const checkExt = this.commonApiService.acceptableExtensionForFileTalk(
|
||||
FileUtil.getExtensions(fileList)
|
||||
);
|
||||
if (!!checkExt) {
|
||||
const msg = this.i18nService.t('common.file.errors.notSupporedType', {
|
||||
supporedType: checkExt.join(',')
|
||||
});
|
||||
alert(msg);
|
||||
// this.snackBarService.openFromComponent<
|
||||
// AlertSnackbarComponent,
|
||||
// AlertSnackbarData
|
||||
// >(AlertSnackbarComponent, {
|
||||
// duration: 1000,
|
||||
// verticalPosition: 'bottom',
|
||||
// horizontalPosition: 'center',
|
||||
// data: {
|
||||
// html: this.translateService.instant(
|
||||
// 'common.file.errors.notSupporedType',
|
||||
// {
|
||||
// supporedType: checkExt.join(',')
|
||||
// }
|
||||
// )
|
||||
// }
|
||||
// });
|
||||
valid = false;
|
||||
return valid;
|
||||
}
|
||||
|
||||
// Fake media file check.
|
||||
const fakeMedia = await this.commonApiService.checkInvalidMediaMimeForFileTalkForFileList(
|
||||
fileList
|
||||
);
|
||||
if (!!fakeMedia) {
|
||||
const msg = this.i18nService.t('common.file.errors.notAcceptableMime', {
|
||||
supporedType: fakeMedia.join(',')
|
||||
});
|
||||
alert(msg);
|
||||
|
||||
valid = false;
|
||||
return valid;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
}
|
6
src/app/types/group-user.dialog.type.ts
Normal file
6
src/app/types/group-user.dialog.type.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
export enum GroupUserDialaogType {
|
||||
Create = 'CREATE_GROUP',
|
||||
Add = 'ADD_GROUP',
|
||||
Copy = 'COPY_GROUP',
|
||||
Move = 'MOVE_GROUP'
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
export * from './app-key.type';
|
||||
export * from './select-user.dialog.type';
|
||||
export * from './tokens';
|
||||
export * from './group-user.dialog.type';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="login-container">
|
||||
<div class="login-container ucap-mat-input-container">
|
||||
<ucap-authentication-login
|
||||
[companyList]="companyList"
|
||||
[fixedCompanyCode]="fixedCompanyCode"
|
||||
|
@ -9,7 +9,7 @@
|
|||
[loginTry]="loginTry"
|
||||
(login)="onLogin($event)"
|
||||
>
|
||||
<div ucapAuthenticationLogin="header">
|
||||
<div ucapAuthenticationLogin="header" class="header">
|
||||
<div class="logo-img">
|
||||
<img src="../../../assets/images/logo_140.png" alt="" />
|
||||
</div>
|
||||
|
@ -47,7 +47,7 @@
|
|||
</div>
|
||||
<div class="login-pass-info">
|
||||
<ul>
|
||||
<li>
|
||||
<li matRipple>
|
||||
<a href="">{{ 'login.labels.forgotPassword' | ucapI18n }}</a>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -58,7 +58,7 @@
|
|||
</ul>
|
||||
</div>
|
||||
<div class="login-button-area">
|
||||
<button type="button">
|
||||
<button mat-flat-button>
|
||||
{{ 'login.labels.notesOnUse' | ucapI18n }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -1,32 +1,55 @@
|
|||
@import '~@ucap/lg-scss/mixins';
|
||||
|
||||
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-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
padding: 0 0 45px;
|
||||
width: 420px;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
flex-basis: auto;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
.header {
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
h1 {
|
||||
@include font-family($font-light);
|
||||
font-size: 1.714em;
|
||||
text-align: center;
|
||||
color: $txt-color01;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
@include screen(mid) {
|
||||
font-size: 1.357em;
|
||||
}
|
||||
@include screen(xs) {
|
||||
font-size: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.login-chk-area {
|
||||
margin-top: 6px;
|
||||
font-size: 13px;
|
||||
margin-top: 7px;
|
||||
font-size: 0.929em;
|
||||
text-align: left;
|
||||
@include screen(xs) {
|
||||
font-size: 12px;
|
||||
font-size: 0.857em;
|
||||
}
|
||||
}
|
||||
.login-pass-info {
|
||||
|
@ -62,14 +85,14 @@ h1 {
|
|||
}
|
||||
a {
|
||||
line-height: 24px;
|
||||
font-size: 12px;
|
||||
font-size: 0.857em;
|
||||
color: $gray-re4a;
|
||||
padding-left: 34px;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
&::before {
|
||||
font-family: 'material Icons';
|
||||
font-size: 18px;
|
||||
font-size: 1.286em;
|
||||
text-align: center;
|
||||
content: 'search';
|
||||
color: $white;
|
||||
|
@ -104,7 +127,7 @@ h1 {
|
|||
height: 46px;
|
||||
border-radius: 4px;
|
||||
background-color: #e0e3e7;
|
||||
font-size: 12px;
|
||||
font-size: 0.857em;
|
||||
color: $gray-re4a;
|
||||
cursor: pointer;
|
||||
@include screen(mid) {
|
||||
|
|
|
@ -7,8 +7,24 @@
|
|||
</div>
|
||||
|
||||
<div appLayoutsSelector="body">
|
||||
<div class="file-item" *ngFor="let item of fileList">
|
||||
<span class="file-name">{{ item.name }}</span>
|
||||
<div class="file-item" *ngFor="let fileUploadItem of fileUploadItems">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="butt"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path
|
||||
d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"
|
||||
></path>
|
||||
</svg>
|
||||
|
||||
<div class="file-upload-name">{{ fileUploadItem.file.name }}</div>
|
||||
<button
|
||||
mat-icon-button
|
||||
matSuffix
|
||||
|
@ -18,8 +34,13 @@
|
|||
>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
<div class="progress-area" style="width: 100%;" *ngIf="item.progress">
|
||||
<mat-progress-bar mode="determinate" value="75"> </mat-progress-bar>
|
||||
|
||||
<div class="progress-area" style="width: 100%;">
|
||||
<mat-progress-bar
|
||||
mode="determinate"
|
||||
[value]="fileUploadItem.uploadingProgress$ | async"
|
||||
>
|
||||
</mat-progress-bar>
|
||||
<button
|
||||
mat-icon-button
|
||||
matSuffix
|
||||
|
@ -34,11 +55,11 @@
|
|||
</div>
|
||||
|
||||
<div appLayoutsSelector="footer">
|
||||
<button mat-button aria-label="모두에게 보내기">
|
||||
모두에게 보내기
|
||||
<button mat-button aria-label="취소">
|
||||
취소
|
||||
</button>
|
||||
<button mat-button aria-label="나에게 보내기">
|
||||
나에게 보내기
|
||||
<button mat-button aria-label="완료">
|
||||
완료
|
||||
</button>
|
||||
</div>
|
||||
</app-layout-selector>
|
||||
|
|
|
@ -1,35 +1,103 @@
|
|||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
Input,
|
||||
ElementRef,
|
||||
AfterViewInit,
|
||||
Inject
|
||||
ChangeDetectorRef,
|
||||
ChangeDetectionStrategy
|
||||
} from '@angular/core';
|
||||
import { FileUploadItem } from '@ucap/api';
|
||||
|
||||
@Component({
|
||||
selector: 'app-chat-selector-file-upload',
|
||||
templateUrl: './file-upload.selector.component.html',
|
||||
styleUrls: ['./file-upload.selector.component.scss']
|
||||
styleUrls: ['./file-upload.selector.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class FileUploadSelectorComponent implements OnInit, AfterViewInit {
|
||||
fileList: { name: string; progress: boolean }[];
|
||||
fileUploadItems: FileUploadItem[];
|
||||
uploadItems: DataTransferItem[];
|
||||
|
||||
constructor() {}
|
||||
constructor(
|
||||
private elementRef: ElementRef<HTMLElement>,
|
||||
private changeDetectorRef: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.fileList = [
|
||||
{ name: 'UCAP 메신저 UX 가이드_V0.1_0309.pptx', progress: false },
|
||||
{ name: 'UCAP 메신저 UX .1_0309.pptx', progress: false },
|
||||
{ name: 'UCAP 메신저 가이드_V0.1_0309.pptx', progress: true },
|
||||
{ name: 'UCAP 메신저 UX 가이드_V.1_0309.pptx', progress: true },
|
||||
{ name: 'UCAP 메신저 UX 가이드_V0.1_0309.pptx', progress: false },
|
||||
{ name: 'UCAP 메신저 UX 가이드_V0..pptx', progress: false },
|
||||
{ name: 'UCAP 메신저 UX 가이드_V0.1_0309.pptx', progress: false },
|
||||
{ name: ' 메신저 UX 가이드_V0.1_0309.pptx', progress: false },
|
||||
{ name: 'UCAP 메신저 UX 가이드_V0.1_0309.pptx', progress: false }
|
||||
];
|
||||
}
|
||||
ngOnInit() {}
|
||||
|
||||
ngAfterViewInit(): void {}
|
||||
|
||||
onDragEnter(items: DataTransferItemList): void {
|
||||
if (!items || 0 === items.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uploadItems: DataTransferItem[] = [];
|
||||
// tslint:disable-next-line: prefer-for-of
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
uploadItems.push(items[i]);
|
||||
}
|
||||
this.uploadItems = [...uploadItems];
|
||||
this.changeStyleDisplay(true);
|
||||
}
|
||||
|
||||
onDragLeave(): void {
|
||||
this.changeStyleDisplay(false);
|
||||
}
|
||||
|
||||
onDrop(fileUploadItems: FileUploadItem[]) {
|
||||
if (!fileUploadItems || 0 === fileUploadItems.length) {
|
||||
return;
|
||||
}
|
||||
this.fileUploadItems = fileUploadItems;
|
||||
this.uploadItems = undefined;
|
||||
}
|
||||
|
||||
onFileSelected(fileUploadItems: FileUploadItem[]): void {
|
||||
if (!fileUploadItems || 0 === fileUploadItems.length) {
|
||||
return;
|
||||
}
|
||||
this.fileUploadItems = fileUploadItems;
|
||||
this.uploadItems = undefined;
|
||||
this.changeStyleDisplay(true);
|
||||
}
|
||||
|
||||
onUploadComplete(): void {
|
||||
setTimeout(() => {
|
||||
this.fileUploadItems = undefined;
|
||||
this.changeStyleDisplay(false);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
isEventInElement(event: DragEvent): boolean {
|
||||
const rect = this.elementRef.nativeElement.getBoundingClientRect();
|
||||
// const rect: DOMRect = this.elementRef.nativeElement.getBoundingClientRect();
|
||||
|
||||
if (
|
||||
event.pageX >= rect.left &&
|
||||
event.pageX <= rect.left + rect.width &&
|
||||
event.pageY >= rect.top &&
|
||||
event.pageY <= rect.top + rect.height
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private changeStyleDisplay(show: boolean): void {
|
||||
// if (show || (!!this.fileUploadItems && 0 < this.fileUploadItems.length)) {
|
||||
// if (this.dropZoneIncludeParent) {
|
||||
// this.elementRef.nativeElement.parentElement.style.display = '';
|
||||
// } else {
|
||||
// this.elementRef.nativeElement.style.display = '';
|
||||
// }
|
||||
// } else {
|
||||
// if (this.dropZoneIncludeParent) {
|
||||
// this.elementRef.nativeElement.parentElement.style.display = 'none';
|
||||
// } else {
|
||||
// this.elementRef.nativeElement.style.display = 'none';
|
||||
// }
|
||||
// }
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
highlight: highlight
|
||||
}"
|
||||
>
|
||||
isMe : {{ isMe }}
|
||||
<ucap-chat-message-box-read-here
|
||||
id="message-box-readhere"
|
||||
*ngIf="existReadToHere"
|
||||
|
@ -19,14 +18,13 @@
|
|||
</ucap-chat-message-box-date-splitter>
|
||||
|
||||
<div #mbChatRow class="chat-row">
|
||||
<div *ngIf="isInformation; then information; else contents"></div>
|
||||
<div *ngIf="isInformation(message); then information; else contents"></div>
|
||||
<ng-template #information>
|
||||
<ng-container class="bubble" [ngSwitch]="message.type">
|
||||
Information...
|
||||
<ucap-chat-message-box-information
|
||||
*ngSwitchCase="EventType.Join"
|
||||
[message]="message"
|
||||
></ucap-chat-message-box-information>
|
||||
-->
|
||||
|
||||
<ucap-chat-message-box-information
|
||||
*ngSwitchCase="EventType.Exit"
|
||||
|
@ -65,7 +63,7 @@
|
|||
<img
|
||||
ucapImage
|
||||
[base]="profileImageRoot"
|
||||
[path]="senderInfo.profileImageFile"
|
||||
[path]="senderInfo?.profileImageFile"
|
||||
[default]="defaultProfileImage"
|
||||
/>
|
||||
</div>
|
||||
|
@ -91,33 +89,40 @@
|
|||
<div class="contents">
|
||||
<ng-container
|
||||
class="bubble"
|
||||
*ngIf="
|
||||
messageType !== EventType.NotificationForTimerRoom &&
|
||||
!isInformation
|
||||
"
|
||||
*ngIf="!isInformation(message)"
|
||||
[ngSwitch]="message.type"
|
||||
>
|
||||
<ucap-chat-message-box-text
|
||||
*ngSwitchCase="EventType.Character"
|
||||
[message]="message"
|
||||
(openLink)="onOpenLink($event)"
|
||||
>
|
||||
</ucap-chat-message-box-text>
|
||||
|
||||
<ucap-chat-message-box-file
|
||||
*ngSwitchCase="EventType.File"
|
||||
[message]="message"
|
||||
[roomInfo]="roomInfo"
|
||||
>
|
||||
</ucap-chat-message-box-file>
|
||||
|
||||
<ucap-chat-message-box-sticker
|
||||
*ngSwitchCase="EventType.Sticker"
|
||||
[message]="message"
|
||||
(openLink)="onOpenLink($event)"
|
||||
>
|
||||
<img
|
||||
ucapUiChatStickerComponent="stickerImage"
|
||||
[src]="getStickerImage(message)"
|
||||
onerror="this.src='assets/sticker/sticker_default.png'"
|
||||
/>
|
||||
</ucap-chat-message-box-sticker>
|
||||
|
||||
<ucap-chat-message-box-mass
|
||||
*ngSwitchCase="EventType.MassText"
|
||||
[message]="message"
|
||||
(openLink)="onOpenLink($event)"
|
||||
(massDetail)="onOpenMassDetail($event)"
|
||||
>
|
||||
</ucap-chat-message-box-mass>
|
||||
|
||||
|
@ -136,11 +141,13 @@
|
|||
<ucap-chat-message-box-translation
|
||||
*ngSwitchCase="EventType.Translation"
|
||||
[message]="message"
|
||||
(openLink)="onOpenLink($event)"
|
||||
></ucap-chat-message-box-translation>
|
||||
|
||||
<ucap-chat-message-box-mass-translation
|
||||
*ngSwitchCase="EventType.MassTranslation"
|
||||
[message]="message"
|
||||
(openLink)="onOpenLink($event)"
|
||||
>
|
||||
</ucap-chat-message-box-mass-translation>
|
||||
|
||||
|
|
|
@ -7,22 +7,52 @@ import {
|
|||
AfterViewInit,
|
||||
ElementRef,
|
||||
ViewChild,
|
||||
ChangeDetectorRef
|
||||
ChangeDetectorRef,
|
||||
OnDestroy
|
||||
} from '@angular/core';
|
||||
import { Info, EventJson, EventType, FileType } from '@ucap/protocol-event';
|
||||
|
||||
import { Store, select } from '@ngrx/store';
|
||||
import { map, catchError, takeUntil, take } from 'rxjs/operators';
|
||||
import { of, Subject } from 'rxjs';
|
||||
|
||||
import {
|
||||
Info,
|
||||
EventJson,
|
||||
EventType,
|
||||
FileType,
|
||||
StickerEventJson
|
||||
} from '@ucap/protocol-event';
|
||||
import {
|
||||
UserInfo as RoomUserInfo,
|
||||
UserInfoShort as RoomUserInfoShort
|
||||
UserInfoShort as RoomUserInfoShort,
|
||||
RoomInfo
|
||||
} from '@ucap/protocol-room';
|
||||
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||
import { LoginSession } from '@ucap/core';
|
||||
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
import { LocalStorageService } from '@ucap/ng-web-storage';
|
||||
import { LogService } from '@ucap/ng-logger';
|
||||
import { CommonApiService } from '@ucap/ng-api-common';
|
||||
|
||||
import { AppAuthenticationService } from '@app/services/app-authentication.service';
|
||||
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { LoginSelector } from '@ucap/ng-store-authentication';
|
||||
import { MassTalkDownloadRequest } from '@ucap/api-common';
|
||||
import { StatusCode } from '@ucap/api';
|
||||
import { Dictionary } from '@ngrx/entity';
|
||||
|
||||
@Component({
|
||||
selector: 'app-chat-message-box',
|
||||
templateUrl: './message-box.component.html',
|
||||
styleUrls: ['./message-box.component.scss']
|
||||
})
|
||||
export class MessageBoxComponent implements OnInit, AfterViewInit {
|
||||
export class MessageBoxComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
@Input()
|
||||
message: Info<EventJson>;
|
||||
@Input()
|
||||
roomInfo: RoomInfo;
|
||||
|
||||
@Input()
|
||||
isMe = false;
|
||||
|
@ -38,23 +68,129 @@ export class MessageBoxComponent implements OnInit, AfterViewInit {
|
|||
@Input()
|
||||
unreadCount: number;
|
||||
|
||||
loginSession: LoginSession;
|
||||
loginRes: LoginResponse;
|
||||
|
||||
EventType = EventType;
|
||||
|
||||
//////////////////////////////////
|
||||
@Input()
|
||||
messageType: string;
|
||||
@Input()
|
||||
isInformation = false;
|
||||
@Input()
|
||||
highlight = false;
|
||||
@Input()
|
||||
existReadToHere = false;
|
||||
@Input()
|
||||
fileType: FileType;
|
||||
|
||||
constructor() {}
|
||||
private ngOnDestroySubject: Subject<boolean>;
|
||||
constructor(
|
||||
private store: Store<any>,
|
||||
private i18nService: I18nService,
|
||||
private dialog: MatDialog,
|
||||
private localStorageService: LocalStorageService,
|
||||
private logService: LogService,
|
||||
private appAuthenticationService: AppAuthenticationService,
|
||||
private commonApiService: CommonApiService,
|
||||
private changeDetectorRef: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
ngOnInit() {}
|
||||
ngOnInit() {
|
||||
this.ngOnDestroySubject = new Subject<boolean>();
|
||||
this.store
|
||||
.pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
|
||||
.subscribe((loginRes) => {
|
||||
this.loginRes = loginRes;
|
||||
});
|
||||
this.appAuthenticationService
|
||||
.getLoginSession$()
|
||||
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||
.subscribe((loginSession) => (this.loginSession = loginSession));
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {}
|
||||
|
||||
/**
|
||||
* 정보성 Event 인지 판단.
|
||||
* @description 정보성 event 일 경우 프로필, 일시 를 표현하지 않는다.
|
||||
* Edit with reducers.ts / sync / updateRoomForNewEventMessage
|
||||
*/
|
||||
isInformation(info: Info<EventJson>) {
|
||||
if (
|
||||
info.type === EventType.Join ||
|
||||
info.type === EventType.Exit ||
|
||||
info.type === EventType.ForcedExit ||
|
||||
info.type === EventType.RenameRoom ||
|
||||
info.type === EventType.NotificationForiOSCapture ||
|
||||
info.type === EventType.NotificationForTimerRoom ||
|
||||
info.type === EventType.GuideForRoomTimerChanged
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Sticker image.
|
||||
*/
|
||||
getStickerImage(message: Info<EventJson>): string {
|
||||
if (!!message.sentMessageJson) {
|
||||
return `assets/sticker/sticker_s_${
|
||||
(message.sentMessageJson as StickerEventJson).file
|
||||
}.png`;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Url link Open Event.
|
||||
*/
|
||||
onOpenLink(url: string): void {
|
||||
console.log('onOpenLink', url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detail view > Mass text.
|
||||
*/
|
||||
onOpenMassDetail(eventMassSeq: number): void {
|
||||
const req = {
|
||||
userSeq: this.loginRes.userSeq,
|
||||
deviceType: this.loginSession.deviceType,
|
||||
eventMassSeq: Number(eventMassSeq),
|
||||
token: this.loginRes.tokenString
|
||||
} as MassTalkDownloadRequest;
|
||||
|
||||
this.commonApiService
|
||||
.massTalkDownload(req)
|
||||
.pipe(
|
||||
take(1),
|
||||
map((res) => {
|
||||
if (res.statusCode === StatusCode.Success) {
|
||||
console.log('massTalkDownload', res.content);
|
||||
// const result = this.dialog.open<
|
||||
// MassDetailComponent,
|
||||
// MassDetailDialogData
|
||||
// >(MassDetailComponent, {
|
||||
// data: {
|
||||
// title: this.i18nService.t('chat.detailView'),
|
||||
// contents: res.content
|
||||
// }
|
||||
// });
|
||||
} else {
|
||||
this.logService.error(
|
||||
`commonApiService] massTalkDownload ${res?.errorMessage}`
|
||||
);
|
||||
}
|
||||
}),
|
||||
catchError((error) => of({ error }))
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,10 @@
|
|||
|
||||
<ng-template ucapChatRoomExpansionHeader let-node>
|
||||
<span class="header-buddy">
|
||||
<span>{{ node.nodeType }} {{ node.nodeType | ucapDate: 'dddd' }}</span>
|
||||
<span
|
||||
>{{ node.nodeType | ucapDate: 'LL' }}
|
||||
{{ node.nodeType | ucapDate: 'dddd' }}</span
|
||||
>
|
||||
<span *ngIf="isToday(node.nodeType)">
|
||||
({{ 'room.today' | ucapI18n }})
|
||||
</span>
|
||||
|
|
|
@ -151,10 +151,18 @@ export class RoomExpansionComponent implements OnInit, OnDestroy {
|
|||
this.loginRes = loginRes;
|
||||
});
|
||||
|
||||
this.store
|
||||
.pipe(takeUntil(this.ngOnDestroySubject), select(RoomSelector.rooms))
|
||||
.subscribe((rooms) => {
|
||||
rooms = (rooms || []).filter((info) => info.isJoinRoom);
|
||||
combineLatest([
|
||||
this.store.pipe(select(RoomSelector.rooms)),
|
||||
this.store.pipe(select(RoomSelector.standbyRooms))
|
||||
])
|
||||
.pipe(takeUntil(this.ngOnDestroySubject))
|
||||
.subscribe(([rooms, standbyRooms]) => {
|
||||
rooms = (rooms || []).filter((info) => {
|
||||
return (
|
||||
info.isJoinRoom &&
|
||||
!standbyRooms.find((standbyRoom) => standbyRoom === info.roomId)
|
||||
);
|
||||
});
|
||||
this.roomList = rooms;
|
||||
|
||||
// groupping.
|
||||
|
@ -201,7 +209,7 @@ export class RoomExpansionComponent implements OnInit, OnDestroy {
|
|||
const date = roomInfo.finalEventDate;
|
||||
let division = '';
|
||||
try {
|
||||
const value = this.dateService.get(date, 'LL');
|
||||
const value = this.dateService.get(date, 'YYYYMMDD');
|
||||
|
||||
if (value === 'Invalid date') {
|
||||
division = date;
|
||||
|
@ -295,8 +303,8 @@ export class RoomExpansionComponent implements OnInit, OnDestroy {
|
|||
ConfirmDialogResult
|
||||
>(ConfirmDialogComponent, {
|
||||
data: {
|
||||
title: this.i18nService.t('room.dialog.titleExitFromRoom'),
|
||||
html: this.i18nService.t('room.dialog.confirmExitFromRoom')
|
||||
title: this.i18nService.t('dialog.title.exitFromRoom'),
|
||||
html: this.i18nService.t('dialog.confirmExitFromRoom')
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -4,8 +4,19 @@ import {
|
|||
Input,
|
||||
ElementRef,
|
||||
AfterViewInit,
|
||||
Inject
|
||||
Inject,
|
||||
Output,
|
||||
EventEmitter
|
||||
} from '@angular/core';
|
||||
import {
|
||||
StickerInfo,
|
||||
StickerFilesInfo,
|
||||
StickerUtil,
|
||||
StickerMap,
|
||||
ActiveAndOrdering,
|
||||
KEY_STICKER_HISTORY
|
||||
} from '@ucap/ng-core';
|
||||
import { LocalStorageService } from '@ucap/ng-web-storage';
|
||||
|
||||
@Component({
|
||||
selector: 'app-chat-selector-sticker',
|
||||
|
@ -13,6 +24,12 @@ import {
|
|||
styleUrls: ['./sticker.selector.component.scss']
|
||||
})
|
||||
export class StickerSelectorComponent implements OnInit, AfterViewInit {
|
||||
@Output()
|
||||
selectedSticker = new EventEmitter<StickerFilesInfo>();
|
||||
|
||||
@Output()
|
||||
closeSticker = new EventEmitter();
|
||||
|
||||
stickerHistory: string[] = [];
|
||||
|
||||
stickerBasePath = '../../../../assets/sticker/';
|
||||
|
@ -22,10 +39,20 @@ export class StickerSelectorComponent implements OnInit, AfterViewInit {
|
|||
|
||||
currentTabIndex: number;
|
||||
|
||||
constructor() {}
|
||||
customStickerMap: StickerInfo[] = [...StickerMap];
|
||||
activeAndOrdering: string[] = [...ActiveAndOrdering];
|
||||
|
||||
constructor(private localStorageService: LocalStorageService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.stickerInfoList = StickerUtil.getStickerInfoList();
|
||||
this.stickerHistory = this.localStorageService.get<string[]>(
|
||||
KEY_STICKER_HISTORY
|
||||
);
|
||||
|
||||
this.stickerInfoList = StickerUtil.getStickerInfoList(
|
||||
this.customStickerMap,
|
||||
this.activeAndOrdering
|
||||
);
|
||||
this.currentTabIndex = 0;
|
||||
}
|
||||
|
||||
|
@ -59,7 +86,7 @@ export class StickerSelectorComponent implements OnInit, AfterViewInit {
|
|||
this.stickerHistory.forEach((sticker) => {
|
||||
const arr: string[] = sticker.split('_');
|
||||
if (arr.length === 2) {
|
||||
const sInfo: StickerInfo[] = StickerMap.filter(
|
||||
const sInfo: StickerInfo[] = this.customStickerMap.filter(
|
||||
(s) => s.index === arr[0]
|
||||
);
|
||||
if (!!sInfo && sInfo.length > 0) {
|
||||
|
@ -84,180 +111,10 @@ export class StickerSelectorComponent implements OnInit, AfterViewInit {
|
|||
|
||||
onClickSelectSticker(sticker: StickerFilesInfo) {
|
||||
this.currentSticker = sticker;
|
||||
this.selectedSticker.emit(sticker);
|
||||
}
|
||||
|
||||
onClickClose() {}
|
||||
}
|
||||
|
||||
export interface StickerInfo {
|
||||
index: string;
|
||||
title: string;
|
||||
iconPath: string;
|
||||
iconPathOn: string;
|
||||
useYn: boolean;
|
||||
fileInfos: StickerFilesInfo[];
|
||||
}
|
||||
export interface StickerFilesInfo {
|
||||
index: string;
|
||||
path: string;
|
||||
}
|
||||
export const StickerMap: StickerInfo[] = [
|
||||
{
|
||||
index: '00',
|
||||
title: 'History',
|
||||
iconPath: 'sticker_cate00.png',
|
||||
iconPathOn: 'sticker_cate00_f.png',
|
||||
useYn: true,
|
||||
fileInfos: []
|
||||
},
|
||||
{
|
||||
index: '01',
|
||||
title: '꼼므',
|
||||
iconPath: 'sticker_cate01.png',
|
||||
iconPathOn: 'sticker_cate01_f.png',
|
||||
useYn: true,
|
||||
fileInfos: [
|
||||
{ index: '01_01', path: 'sticker_s_01_01.png' },
|
||||
{ index: '01_02', path: 'sticker_s_01_02.png' },
|
||||
{ index: '01_03', path: 'sticker_s_01_03.png' },
|
||||
{ index: '01_04', path: 'sticker_s_01_04.png' },
|
||||
{ index: '01_05', path: 'sticker_s_01_05.png' },
|
||||
{ index: '01_06', path: 'sticker_s_01_06.png' },
|
||||
{ index: '01_07', path: 'sticker_s_01_07.png' },
|
||||
{ index: '01_08', path: 'sticker_s_01_08.png' }
|
||||
]
|
||||
},
|
||||
{
|
||||
index: '02',
|
||||
title: '까미',
|
||||
iconPath: 'sticker_cate02.png',
|
||||
iconPathOn: 'sticker_cate02_f.png',
|
||||
useYn: true,
|
||||
fileInfos: [
|
||||
{ index: '02_02', path: 'sticker_s_02_02.png' },
|
||||
{ index: '02_03', path: 'sticker_s_02_03.png' },
|
||||
{ index: '02_04', path: 'sticker_s_02_04.png' },
|
||||
{ index: '02_05', path: 'sticker_s_02_05.png' },
|
||||
{ index: '02_06', path: 'sticker_s_02_06.png' },
|
||||
{ index: '02_07', path: 'sticker_s_02_07.png' },
|
||||
{ index: '02_08', path: 'sticker_s_02_08.png' }
|
||||
]
|
||||
},
|
||||
{
|
||||
index: '03',
|
||||
title: '왈도',
|
||||
iconPath: 'sticker_cate03.png',
|
||||
iconPathOn: 'sticker_cate03_f.png',
|
||||
useYn: true,
|
||||
fileInfos: [
|
||||
{ index: '03_01', path: 'sticker_s_03_01.png' },
|
||||
{ index: '03_02', path: 'sticker_s_03_02.png' },
|
||||
{ index: '03_03', path: 'sticker_s_03_03.png' },
|
||||
{ index: '03_04', path: 'sticker_s_03_04.png' },
|
||||
{ index: '03_05', path: 'sticker_s_03_05.png' },
|
||||
{ index: '03_06', path: 'sticker_s_03_06.png' },
|
||||
{ index: '03_07', path: 'sticker_s_03_07.png' },
|
||||
{ index: '03_08', path: 'sticker_s_03_08.png' }
|
||||
]
|
||||
},
|
||||
{
|
||||
index: '04',
|
||||
title: '웅쓰',
|
||||
iconPath: 'sticker_cate04.png',
|
||||
iconPathOn: 'sticker_cate04_f.png',
|
||||
useYn: true,
|
||||
fileInfos: [
|
||||
{ index: '04_01', path: 'sticker_s_04_01.png' },
|
||||
{ index: '04_02', path: 'sticker_s_04_02.png' },
|
||||
{ index: '04_03', path: 'sticker_s_04_03.png' },
|
||||
{ index: '04_04', path: 'sticker_s_04_04.png' },
|
||||
{ index: '04_05', path: 'sticker_s_04_05.png' },
|
||||
{ index: '04_06', path: 'sticker_s_04_06.png' },
|
||||
{ index: '04_07', path: 'sticker_s_04_07.png' },
|
||||
{ index: '04_08', path: 'sticker_s_04_08.png' }
|
||||
]
|
||||
},
|
||||
{
|
||||
index: '05',
|
||||
title: '말풍선',
|
||||
iconPath: 'sticker_cate05.png',
|
||||
iconPathOn: 'sticker_cate05_f.png',
|
||||
useYn: true,
|
||||
fileInfos: [
|
||||
{ index: '05_01', path: 'sticker_s_05_01.png' },
|
||||
{ index: '05_02', path: 'sticker_s_05_02.png' },
|
||||
{ index: '05_03', path: 'sticker_s_05_03.png' },
|
||||
{ index: '05_04', path: 'sticker_s_05_04.png' },
|
||||
{ index: '05_05', path: 'sticker_s_05_05.png' },
|
||||
{ index: '05_06', path: 'sticker_s_05_06.png' },
|
||||
{ index: '05_07', path: 'sticker_s_05_07.png' },
|
||||
{ index: '05_08', path: 'sticker_s_05_08.png' },
|
||||
{ index: '05_09', path: 'sticker_s_05_09.png' },
|
||||
{ index: '05_10', path: 'sticker_s_05_10.png' },
|
||||
{ index: '05_11', path: 'sticker_s_05_11.png' },
|
||||
{ index: '05_12', path: 'sticker_s_05_12.png' },
|
||||
{ index: '05_13', path: 'sticker_s_05_13.png' },
|
||||
{ index: '05_14', path: 'sticker_s_05_14.png' },
|
||||
{ index: '05_15', path: 'sticker_s_05_15.png' },
|
||||
{ index: '05_16', path: 'sticker_s_05_16.png' }
|
||||
]
|
||||
},
|
||||
{
|
||||
index: '12',
|
||||
title: '황소',
|
||||
iconPath: 'sticker_cate12.png',
|
||||
iconPathOn: 'sticker_cate12_f.png',
|
||||
useYn: true,
|
||||
fileInfos: [
|
||||
{ index: '12_01', path: 'sticker_s_12_01.gif' },
|
||||
{ index: '12_02', path: 'sticker_s_12_02.gif' },
|
||||
{ index: '12_03', path: 'sticker_s_12_03.gif' },
|
||||
{ index: '12_04', path: 'sticker_s_12_04.gif' },
|
||||
{ index: '12_05', path: 'sticker_s_12_05.gif' },
|
||||
{ index: '12_06', path: 'sticker_s_12_06.gif' },
|
||||
{ index: '12_07', path: 'sticker_s_12_07.gif' },
|
||||
{ index: '12_08', path: 'sticker_s_12_08.gif' },
|
||||
{ index: '12_09', path: 'sticker_s_12_09.gif' },
|
||||
{ index: '12_10', path: 'sticker_s_12_10.gif' },
|
||||
{ index: '12_11', path: 'sticker_s_12_11.gif' },
|
||||
{ index: '12_12', path: 'sticker_s_12_12.gif' },
|
||||
{ index: '12_13', path: 'sticker_s_12_13.gif' },
|
||||
{ index: '12_14', path: 'sticker_s_12_14.gif' },
|
||||
{ index: '12_15', path: 'sticker_s_12_15.gif' },
|
||||
{ index: '12_16', path: 'sticker_s_12_16.gif' }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const ActiveAndOrdering: string[] = ['00', '01', '02', '03', '04', '05'];
|
||||
|
||||
export class StickerUtil {
|
||||
static getStickerInfoList(): StickerInfo[] {
|
||||
const rtnStickerList: StickerInfo[] = [];
|
||||
|
||||
ActiveAndOrdering.forEach((idx) => {
|
||||
const stickerInfos: StickerInfo[] = StickerMap.filter(
|
||||
(sticker) => sticker.index === idx && sticker.useYn
|
||||
);
|
||||
|
||||
if (!!stickerInfos && stickerInfos.length > 0) {
|
||||
rtnStickerList.push(stickerInfos[0]);
|
||||
}
|
||||
});
|
||||
|
||||
return rtnStickerList;
|
||||
}
|
||||
|
||||
static getStickerInfoSub(index: string): StickerFilesInfo[] {
|
||||
const stickerFilesList: StickerFilesInfo[] = [];
|
||||
const stickerInfos: StickerInfo[] = StickerMap.filter(
|
||||
(sticker) => sticker.index === index && sticker.useYn
|
||||
);
|
||||
|
||||
if (!!stickerInfos && stickerInfos.length > 0) {
|
||||
stickerFilesList.concat(stickerInfos[0].fileInfos);
|
||||
}
|
||||
|
||||
return stickerFilesList;
|
||||
onClickClose() {
|
||||
this.closeSticker.emit();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,22 +8,18 @@
|
|||
[selectedUserList]="selectedUserList"
|
||||
[checkable]="checkable"
|
||||
(clickMoreMenu)="!isDialog ? onClickMoreMenu($event) : null"
|
||||
(checkGroup)="onCheckGroup($event)"
|
||||
(checkGroup)="onToggleCheckGroup($event)"
|
||||
>
|
||||
<ng-template ucapGroupExpansionNode let-node>
|
||||
<app-group-profile-list-item
|
||||
<app-group-profile-list-item-02
|
||||
[userInfo]="node.userInfo"
|
||||
[isMe]="loginRes?.userSeq === node.userInfo.seq"
|
||||
[group]="node.groupDetail"
|
||||
defaultProfileImage="assets/images/img_nophoto_50.png"
|
||||
[profileImageRoot]="versionInfo2Res?.profileRoot"
|
||||
[checkable]="checkable"
|
||||
[presence]="getStatusBulkInfo(node.userInfo) | async"
|
||||
[isChecked]="getCheckedUser(node.userInfo)"
|
||||
(checked)="onCheckedUser($event)"
|
||||
(click)="onClickUser($event, node.userInfo)"
|
||||
[isDialog]="isDialog"
|
||||
[checked]="getCheckedUser(node.userInfo)"
|
||||
[isMe]="loginRes?.userSeq === node.userInfo.seq"
|
||||
(moreMenu)="onClikeMoreProfile($event)"
|
||||
></app-group-profile-list-item>
|
||||
(changeCheckUser)="onChangeCheckUser($event)"
|
||||
></app-group-profile-list-item-02>
|
||||
</ng-template>
|
||||
|
||||
<ng-template ucapGroupExpansionFavoriteHeader let-node>
|
||||
|
@ -109,7 +105,7 @@
|
|||
#profileContextMenuTrigger="matMenuTrigger"
|
||||
[matMenuTriggerFor]="profileContextMenu"
|
||||
></div>
|
||||
<mat-menu #profileContextMenu="matMenu">
|
||||
<mat-menu #profileContextMenu="matMenu" (closed)="onProfileMenuClose($event)">
|
||||
<ng-template
|
||||
matMenuContent
|
||||
let-userInfo="userInfo"
|
||||
|
@ -177,46 +173,5 @@
|
|||
>
|
||||
{{ 'moreMenu.profile.removeBuddy' | ucapI18n }}
|
||||
</button>
|
||||
|
||||
<!-- <button
|
||||
mat-menu-item
|
||||
*ngIf="getShowProfileContextMenu('REGISTER_NICKNAME', userInfo, group)"
|
||||
(click)="onClickProfileContextMenu('REGISTER_NICKNAME', userInfo)"
|
||||
>
|
||||
닉네임 설정
|
||||
</button> -->
|
||||
</ng-template>
|
||||
</mat-menu>
|
||||
|
||||
<!-- <div
|
||||
style="visibility: hidden; position: fixed;"
|
||||
[style.left]="editGroupNamePosition.x"
|
||||
[style.top]="editGroupNamePosition.y"
|
||||
#editGroupNameTrigger="matMenuTrigger"
|
||||
[matMenuTriggerFor]="groupNameMenu"
|
||||
></div>
|
||||
|
||||
<mat-menu #groupNameMenu="matMenu">
|
||||
<ng-template matMenuContent let-group="group">
|
||||
<ucap-inline-edit-input
|
||||
[initialMode]="true"
|
||||
(apply)="
|
||||
$event.stopPropagation();
|
||||
onApplyEditGroupName(editGroupInput.value, group)
|
||||
"
|
||||
(edit)="$event.stopPropagation()"
|
||||
(cancel)="$event.stopPropagation(); editGroupInput.value = group.name"
|
||||
class="form-eidt"
|
||||
>
|
||||
<span ucapInlineEditInput="view">{{ group.name }}</span>
|
||||
<span ucapInlineEditInput="edit"
|
||||
><input
|
||||
matInput
|
||||
#editGroupInput
|
||||
type="text"
|
||||
[value]="group.name"
|
||||
(click)="$event.stopPropagation()"
|
||||
/></span>
|
||||
</ucap-inline-edit-input>
|
||||
</ng-template>
|
||||
</mat-menu> -->
|
||||
|
|
|
@ -52,16 +52,9 @@ import { environment } from '@environments';
|
|||
import { PresenceSelector } from '@ucap/ng-store-organization';
|
||||
import { StatusCode } from '@ucap/core';
|
||||
|
||||
import { EditInlineInputDialogComponent } from '@app/sections/group/dialogs/edit-inline-input.dialog.component';
|
||||
import {
|
||||
AlertDialogComponent,
|
||||
AlertDialogData,
|
||||
AlertDialogResult,
|
||||
ConfirmDialogComponent,
|
||||
ConfirmDialogData,
|
||||
ConfirmDialogResult
|
||||
} from '@ucap/ng-ui';
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
import { ProfileListItem01Component } from '@ucap/ng-ui-organization';
|
||||
import { ProfileListItem02Component } from './profile-list-item-02.component';
|
||||
|
||||
export type UserInfoTypes =
|
||||
| UserInfo
|
||||
|
@ -104,12 +97,6 @@ export class ExpansionComponent implements OnInit, OnDestroy {
|
|||
@Input()
|
||||
showType: string;
|
||||
|
||||
@Output()
|
||||
checked = new EventEmitter<{
|
||||
isChecked: boolean;
|
||||
userInfo: UserInfoTypes;
|
||||
}>();
|
||||
|
||||
@Output()
|
||||
profileMenu: EventEmitter<any> = new EventEmitter();
|
||||
|
||||
|
@ -119,17 +106,26 @@ export class ExpansionComponent implements OnInit, OnDestroy {
|
|||
@Output()
|
||||
selectGroupMenu = new EventEmitter<{
|
||||
menuType: string;
|
||||
group: GroupDetailData;
|
||||
groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] };
|
||||
rect: any;
|
||||
}>();
|
||||
|
||||
// @Output()
|
||||
// editGroupName = new EventEmitter<{
|
||||
// editName: string;
|
||||
// group: GroupDetailData;
|
||||
// }>();
|
||||
@Output()
|
||||
selectProfileMenu: EventEmitter<{
|
||||
menuType: string;
|
||||
userInfo: UserInfoF;
|
||||
group: GroupDetailData;
|
||||
rect: any;
|
||||
}> = new EventEmitter();
|
||||
|
||||
@Output()
|
||||
checkGroup = new EventEmitter<{
|
||||
toggleCheckUser: EventEmitter<{
|
||||
checked: boolean;
|
||||
userInfo: UserInfoSS;
|
||||
}> = new EventEmitter();
|
||||
|
||||
@Output()
|
||||
toggleCheckGroup = new EventEmitter<{
|
||||
isChecked: boolean;
|
||||
groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] };
|
||||
}>();
|
||||
|
@ -178,7 +174,6 @@ export class ExpansionComponent implements OnInit, OnDestroy {
|
|||
private store: Store<any>,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private logService: LogService,
|
||||
private dialog: MatDialog,
|
||||
private i18nService: I18nService,
|
||||
@Self() private elementRef: ElementRef
|
||||
) {}
|
||||
|
@ -366,10 +361,6 @@ export class ExpansionComponent implements OnInit, OnDestroy {
|
|||
this.profileMenu.emit({ event, userInfo, group, isSearchData });
|
||||
}
|
||||
|
||||
onCheckedUser(params: { isChecked: boolean; userInfo: UserInfoTypes }) {
|
||||
this.checked.emit(params);
|
||||
}
|
||||
|
||||
onClickUser(event: MouseEvent, userInfo: UserInfo) {
|
||||
this.clicked.emit({ event, userInfo });
|
||||
}
|
||||
|
@ -463,94 +454,59 @@ export class ExpansionComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
onSelectGroupMenu(menuType: string, group: GroupDetailData) {
|
||||
let rect: any;
|
||||
|
||||
if (menuType.localeCompare('RENAME') === 0) {
|
||||
// this.editablGroup = group;
|
||||
// this.onShowEditGroup(group);
|
||||
const target = this.elementRef.nativeElement.querySelector(
|
||||
'.mat-tree-node'
|
||||
);
|
||||
const rect = target.getBoundingClientRect();
|
||||
|
||||
const clickEventY = this.groupMenuEvent.clientY;
|
||||
const tartgetY = Math.floor((clickEventY - 150) * 0.1) * 10;
|
||||
const clientRect = target.getBoundingClientRect();
|
||||
|
||||
const dialogRef = this.dialog.open(EditInlineInputDialogComponent, {
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
panelClass: 'ucap-edit-group-name-dialog',
|
||||
data: {
|
||||
curValue: group.name,
|
||||
placeholder: '그룹명을 입력하세요.',
|
||||
left: rect.left,
|
||||
top: clickEventY - 100 + rect.height
|
||||
}
|
||||
});
|
||||
|
||||
dialogRef
|
||||
.afterClosed()
|
||||
.pipe(
|
||||
take(1),
|
||||
map((result) => {
|
||||
if (
|
||||
!!result &&
|
||||
result.choice &&
|
||||
result.curValue.localeCompare(group.name) !== 0
|
||||
) {
|
||||
this.store.dispatch(
|
||||
GroupActions.update({
|
||||
req: {
|
||||
groupSeq: group.seq,
|
||||
groupName: result.curValue,
|
||||
userSeqs: group.userSeqs
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}),
|
||||
catchError((err) => {
|
||||
return of(err);
|
||||
})
|
||||
)
|
||||
.subscribe();
|
||||
rect = {
|
||||
width: clientRect.width,
|
||||
height: clientRect.height,
|
||||
top: clickEventY - 100 + clientRect.height,
|
||||
left: clientRect.left,
|
||||
bottom: clientRect.bottom,
|
||||
right: clientRect.right
|
||||
};
|
||||
}
|
||||
this.selectGroupMenu.emit({ menuType, group });
|
||||
}
|
||||
|
||||
// onApplyEditGroupName(editName: string, group: GroupDetailData) {
|
||||
// // this.editablGroup = null;
|
||||
// this.editGroupName.emit({
|
||||
// editName,
|
||||
// group
|
||||
// });
|
||||
// }
|
||||
|
||||
onCheckGroup(params: {
|
||||
isChecked: boolean;
|
||||
groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] };
|
||||
}) {
|
||||
this.checkGroup.emit({
|
||||
isChecked: params.isChecked,
|
||||
groupBuddyList: params.groupBuddyList
|
||||
const groupBuddyList = this.groupBuddies.filter(
|
||||
(g) => g.group.seq === group.seq
|
||||
);
|
||||
this.selectGroupMenu.emit({
|
||||
menuType,
|
||||
groupBuddyList: groupBuddyList[0],
|
||||
rect
|
||||
});
|
||||
}
|
||||
|
||||
/** 개별선택(토글) 이벤트 */
|
||||
onChangeCheckUser(param: { checked: boolean; userInfo: UserInfoSS }) {
|
||||
this.toggleCheckUser.emit(param);
|
||||
}
|
||||
|
||||
/** 개별 체크여부 */
|
||||
getCheckedUser(userInfo: UserInfoTypes) {
|
||||
if (!!this.selectedUserList && this.selectedUserList.length > 0) {
|
||||
return (
|
||||
this.selectedUserList.filter((item) => item.seq === userInfo.seq)
|
||||
.length > 0
|
||||
this.selectedUserList.filter(
|
||||
(item) => (item.seq as any) === userInfo.seq
|
||||
).length > 0
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
getStatusBulkInfo(buddy: UserInfoTypes) {
|
||||
return this.store.pipe(
|
||||
select(PresenceSelector.selectEntitiesStatusBulkInfo),
|
||||
map((statusBulkInfo) =>
|
||||
!!statusBulkInfo ? statusBulkInfo[buddy.seq] : undefined
|
||||
)
|
||||
);
|
||||
onToggleCheckGroup(params: {
|
||||
isChecked: boolean;
|
||||
groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] };
|
||||
}) {
|
||||
this.toggleCheckGroup.emit({
|
||||
isChecked: params.isChecked,
|
||||
groupBuddyList: params.groupBuddyList
|
||||
});
|
||||
}
|
||||
|
||||
getBuddiesForShowType(): { group: GroupDetailData; buddyList: UserInfo[] }[] {
|
||||
|
@ -592,114 +548,30 @@ export class ExpansionComponent implements OnInit, OnDestroy {
|
|||
) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
switch (menuType) {
|
||||
case 'VIEW_PROFILE':
|
||||
this.onClickUser(event, userInfo as UserInfo);
|
||||
break;
|
||||
case 'REGISTER_FAVORITE':
|
||||
this.store.dispatch(
|
||||
BuddyActions.update({
|
||||
req: {
|
||||
seq: Number(userInfo.seq),
|
||||
isFavorit: !userInfo.isFavorit
|
||||
}
|
||||
})
|
||||
);
|
||||
break;
|
||||
case 'NICKNAME':
|
||||
{
|
||||
this.editNickname(event, userInfo, rect);
|
||||
}
|
||||
break;
|
||||
case 'COPY_BUDDY':
|
||||
break;
|
||||
case 'MOVE_BUDDY':
|
||||
break;
|
||||
case 'REMOVE_BUDDY':
|
||||
{
|
||||
this.removeBuddy(userInfo, group);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private removeBuddy(userInfo: UserInfoF, group: GroupDetailData) {
|
||||
const dialogRef = this.dialog.open<
|
||||
ConfirmDialogComponent,
|
||||
ConfirmDialogData,
|
||||
ConfirmDialogResult
|
||||
>(ConfirmDialogComponent, {
|
||||
data: {
|
||||
title: '',
|
||||
html: this.i18nService.t('label.confirmRemoveBuddy')
|
||||
}
|
||||
});
|
||||
dialogRef
|
||||
.afterClosed()
|
||||
.pipe(
|
||||
take(1),
|
||||
map((result) => {
|
||||
if (!!result && result.choice) {
|
||||
const trgtUserSeq = group.userSeqs.filter(
|
||||
(user) => user + '' !== userInfo.seq + ''
|
||||
);
|
||||
|
||||
this.store.dispatch(
|
||||
GroupActions.updateMember({
|
||||
targetGroup: group,
|
||||
targetUserSeqs: trgtUserSeq
|
||||
})
|
||||
);
|
||||
}
|
||||
}),
|
||||
catchError((err) => {
|
||||
return of(err);
|
||||
})
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
private editNickname(event: MouseEvent, userInfo: UserInfoF, rect: any) {
|
||||
const clickEventY = event.clientY;
|
||||
|
||||
const dialogRef = this.dialog.open(EditInlineInputDialogComponent, {
|
||||
width: rect.width - 30 + '',
|
||||
const clientRect = {
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
panelClass: 'ucap-edit-group-name-dialog',
|
||||
data: {
|
||||
curValue: userInfo.nickName,
|
||||
placeholder: '닉네임을 설정하세요.',
|
||||
left: rect.left + 70,
|
||||
top: rect.top
|
||||
}
|
||||
});
|
||||
top: rect.top,
|
||||
left: rect.left,
|
||||
right: rect.right,
|
||||
bottom: rect.bottom
|
||||
};
|
||||
|
||||
dialogRef
|
||||
.afterClosed()
|
||||
.pipe(
|
||||
take(1),
|
||||
map((result) => {
|
||||
if (
|
||||
!!result &&
|
||||
result.choice &&
|
||||
result.curValue.localeCompare(userInfo.nickName) !== 0
|
||||
) {
|
||||
this.store.dispatch(
|
||||
BuddyActions.nickname({
|
||||
req: {
|
||||
userSeq: Number(userInfo.seq),
|
||||
nickname: result.curValue
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}),
|
||||
catchError((err) => {
|
||||
return of(err);
|
||||
})
|
||||
)
|
||||
.subscribe();
|
||||
if (menuType === 'VIEW_PROFILE') {
|
||||
this.onClickUser(event, userInfo as UserInfo);
|
||||
}
|
||||
|
||||
this.selectProfileMenu.emit({
|
||||
menuType,
|
||||
userInfo,
|
||||
group,
|
||||
rect: clientRect
|
||||
});
|
||||
}
|
||||
|
||||
onProfileMenuClose(event: MouseEvent) {}
|
||||
|
||||
getShowProfileContextMenu(menuType: string, group: GroupDetailData) {
|
||||
return true;
|
||||
if (!!this.isSearchData) {
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
import { ExpansionComponent } from './expansion.component';
|
||||
import { ProfileListItemComponent } from './profile-list-item.component';
|
||||
|
||||
export const COMPONENTS = [ExpansionComponent, ProfileListItemComponent];
|
||||
import { ProfileListItem02Component } from './profile-list-item-02.component';
|
||||
import { ProfileListComponent } from './profile-list.component';
|
||||
|
||||
export const COMPONENTS = [
|
||||
ExpansionComponent,
|
||||
ProfileListItem02Component,
|
||||
ProfileListComponent
|
||||
];
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
<div
|
||||
class="profile-list-container"
|
||||
(mouseover)="onMouseover($event)"
|
||||
(mouseleave)="onMouseleave($event)"
|
||||
>
|
||||
<ucap-organization-profile-list-item-02
|
||||
[userInfo]="userInfo"
|
||||
[checkable]="checkable"
|
||||
[checked]="checked"
|
||||
(openProfile)="onOpenProfile($event)"
|
||||
(changeCheck)="onChangeCheckUser($event)"
|
||||
>
|
||||
<ucap-organization-profile-image-01
|
||||
ucapOrganizationProfileListItem01="profileImage"
|
||||
[userInfo]="userInfo"
|
||||
[profileImageRoot]="versionInfo2Res?.profileRoot"
|
||||
defaultProfileImage="assets/images/ico/img_nophoto.svg"
|
||||
>
|
||||
</ucap-organization-profile-image-01>
|
||||
|
||||
<div class="info-content" appProfileListItemUserInfo="info" *ngIf="!isShow">
|
||||
<div
|
||||
*ngIf="
|
||||
!!userInfo && userInfo.nickName !== '';
|
||||
then nicknameBlock;
|
||||
else introBlock
|
||||
"
|
||||
></div>
|
||||
<ng-template #nicknameBlock>{{ userInfo.nickName }}</ng-template>
|
||||
<ng-template #introBlock>{{ userInfo.intro }}</ng-template>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="action-content"
|
||||
appProfileListItemUserAction="action"
|
||||
*ngIf="isShow && !isDialog"
|
||||
>
|
||||
<ng-template
|
||||
[ngTemplateOutlet]="profileMoreContextTemplate"
|
||||
></ng-template>
|
||||
</div>
|
||||
</ucap-organization-profile-list-item-02>
|
||||
</div>
|
||||
|
||||
<ng-template #profileMoreContextTemplate>
|
||||
<button
|
||||
mat-icon-button
|
||||
aria-label="chat"
|
||||
(click)="onClickProfileContextMenu($event, 'CHAT')"
|
||||
>
|
||||
<img src="../../../assets/images/ico/btn_lise_chat_a24.svg" alt="" />
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
aria-label="message"
|
||||
(click)="
|
||||
$event.stopPropagation(); onClickProfileContextMenu($event, 'CHAT')
|
||||
"
|
||||
>
|
||||
<img src="../../../assets/images/ico/btn_list_message_a24.svg" alt="" />
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
aria-label="mobile"
|
||||
(click)="onClickProfileContextMenu($event, 'CHAT')"
|
||||
>
|
||||
<img src="../../../assets/images/ico/btn_list_mobile_a24.svg" alt="" />
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
aria-label="call"
|
||||
(click)="onClickProfileContextMenu($event, 'CHAT')"
|
||||
>
|
||||
<img src="../../../assets/images/ico/btn_list_call_a24.svg" alt="" />
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
aria-label="vc"
|
||||
(click)="onClickProfileContextMenu($event, 'CHAT')"
|
||||
>
|
||||
<img src="../../../assets/images/ico/btn_list_vc-a24.svg" alt="" />
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
aria-label="more"
|
||||
*ngIf="true"
|
||||
(click)="onClickMore($event)"
|
||||
>
|
||||
<mat-icon>more_horiz</mat-icon>
|
||||
</button>
|
||||
</ng-template>
|
|
@ -0,0 +1,139 @@
|
|||
@import '~@ucap/ng-ui-material/material';
|
||||
|
||||
/// var
|
||||
/// --ucap-organization-profile-list-item-01-size: 70px
|
||||
|
||||
.ucap-group-profile-list-item-01-container {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: space-between;
|
||||
padding: 0 16px;
|
||||
height: 70px;
|
||||
align-items: center;
|
||||
&.line-top {
|
||||
// border-top: 1px solid $gray-rec;
|
||||
}
|
||||
.user-profile-info {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
flex-grow: 2.3;
|
||||
// .user-profile-thumb {
|
||||
// @include profile-avatar-default(
|
||||
// 0 5px 5px 0,
|
||||
// 8,
|
||||
// $green,
|
||||
// 18px
|
||||
// ); //오른 아래 공간, 모바일 온라인 아이콘 크기, 모바일 아이콘 색, 모바일 아이콘 bg크기
|
||||
// .presence {
|
||||
// //PC 상태
|
||||
// @include presence-state(8px); //원크기
|
||||
// }
|
||||
// .profile-image {
|
||||
// @include avatar-img(36px, 2px); //아바타 크기, 왼쪽공간
|
||||
// }
|
||||
// }
|
||||
.user-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
padding-left: 16px;
|
||||
.user-n-g {
|
||||
display: flex;
|
||||
flex-flow: row-reverse nowrap;
|
||||
align-items: flex-end;
|
||||
height: 22px;
|
||||
.user-name {
|
||||
@include ellipsis-column(1);
|
||||
height: 22px;
|
||||
font: {
|
||||
size: 16px;
|
||||
weight: 600;
|
||||
}
|
||||
// color: $gray-re21;
|
||||
order: 1;
|
||||
-ms-flex-order: 1;
|
||||
}
|
||||
.user-grade {
|
||||
@include ellipsis(1);
|
||||
align-self: stretch;
|
||||
font: {
|
||||
size: 13px;
|
||||
}
|
||||
// color: $gray-re70;
|
||||
margin-left: 4px;
|
||||
order: 0;
|
||||
-ms-flex-order: 0;
|
||||
}
|
||||
}
|
||||
.dept-name {
|
||||
@include ellipsis(1);
|
||||
font-size: 12px;
|
||||
// color: $gray-re6;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.intro {
|
||||
display: inline-flex;
|
||||
flex-flow: row nowrap;
|
||||
flex-basis: 35%;
|
||||
flex-grow: 0;
|
||||
align-items: baseline;
|
||||
p {
|
||||
font-size: 11px;
|
||||
line-height: 1.4;
|
||||
@include ellipsis(2);
|
||||
height: 30px;
|
||||
}
|
||||
&:before {
|
||||
content: 'chat';
|
||||
// @include font-family-ico($font-ico-default, 12, center, $lipstick);
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
line-height: 12px;
|
||||
margin-right: 4.8px;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
}
|
||||
.btn-partner-set {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 25px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.8);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.8);
|
||||
height: 20px;
|
||||
margin-top: 20px;
|
||||
img {
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
.intro-name {
|
||||
display: inline-flex;
|
||||
flex-flow: row nowrap;
|
||||
flex-basis: 35%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
span {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
// color: $gray-re70;
|
||||
font-size: 11px;
|
||||
padding: 0 10px;
|
||||
border-radius: 30px;
|
||||
// border: solid 1px $warm-pink;
|
||||
background-color: #ffffff;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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::ucap::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();
|
||||
});
|
||||
});
|
157
src/app/ucap/group/components/profile-list-item-02.component.ts
Normal file
157
src/app/ucap/group/components/profile-list-item-02.component.ts
Normal file
|
@ -0,0 +1,157 @@
|
|||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Input,
|
||||
EventEmitter,
|
||||
Output,
|
||||
ElementRef,
|
||||
Self
|
||||
} from '@angular/core';
|
||||
import { GroupDetailData } from '@ucap/protocol-sync';
|
||||
import { UserInfoSS, UserInfoF } from '@ucap/protocol-query';
|
||||
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
import { ucapAnimations } from '@ucap/ng-ui';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { select, Store } from '@ngrx/store';
|
||||
import {
|
||||
ConfigurationSelector,
|
||||
LoginSelector
|
||||
} from '@ucap/ng-store-authentication';
|
||||
import { VersionInfo2Response } from '@ucap/api-public';
|
||||
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||
|
||||
@Component({
|
||||
selector: 'app-group-profile-list-item-02',
|
||||
templateUrl: './profile-list-item-02.component.html',
|
||||
styleUrls: ['./profile-list-item-02.component.scss'],
|
||||
animations: ucapAnimations,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ProfileListItem02Component implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
userInfo: UserInfoF;
|
||||
|
||||
@Input()
|
||||
group: GroupDetailData;
|
||||
|
||||
@Input()
|
||||
checkable = false;
|
||||
|
||||
@Input()
|
||||
isMe = false;
|
||||
|
||||
@Input()
|
||||
checked = false;
|
||||
|
||||
@Output()
|
||||
moreMenu: EventEmitter<{
|
||||
event: MouseEvent;
|
||||
userInfo: UserInfoF;
|
||||
group: GroupDetailData;
|
||||
rect: any;
|
||||
}> = new EventEmitter();
|
||||
|
||||
@Output()
|
||||
changeCheckUser: EventEmitter<{
|
||||
checked: boolean;
|
||||
userInfo: UserInfoF;
|
||||
}> = new EventEmitter();
|
||||
|
||||
@Input()
|
||||
isShow = false;
|
||||
@Input()
|
||||
isDialog = false;
|
||||
@Input()
|
||||
isClickMore = false;
|
||||
|
||||
private ngOnDestroySubject: Subject<void>;
|
||||
|
||||
loginRes: LoginResponse;
|
||||
versionInfo2Res: VersionInfo2Response;
|
||||
|
||||
tempRect: any;
|
||||
|
||||
constructor(
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private i18nService: I18nService,
|
||||
@Self() private elementRef: ElementRef,
|
||||
private store: Store<any>
|
||||
) {
|
||||
this.i18nService.setDefaultNamespace('organization');
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject();
|
||||
|
||||
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.tempRect = this.elementRef.nativeElement.getBoundingClientRect();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.next();
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
||||
onOpenProfile(userInfo: UserInfoSS): void {
|
||||
alert('Open Profile');
|
||||
}
|
||||
|
||||
onChangeCheckUser(param: { checked: boolean; userInfo: UserInfoF }) {
|
||||
this.changeCheckUser.emit(param);
|
||||
}
|
||||
|
||||
onMouseover(event: MouseEvent): void {
|
||||
if (!this.isMe && !this.isDialog) {
|
||||
this.isShow = true;
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
onMouseleave(event: MouseEvent): void {
|
||||
if (!this.isMe && !this.isDialog) {
|
||||
this.isShow = false;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
onClickProfileContextMenu(event: MouseEvent, type: string) {}
|
||||
|
||||
onClickMore(event: MouseEvent) {
|
||||
this.isClickMore = true;
|
||||
|
||||
const rect = this.elementRef.nativeElement.getBoundingClientRect();
|
||||
|
||||
this.moreMenu.emit({
|
||||
event,
|
||||
userInfo: this.userInfo,
|
||||
group: this.group,
|
||||
rect
|
||||
});
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
21
src/app/ucap/group/components/profile-list.component.html
Normal file
21
src/app/ucap/group/components/profile-list.component.html
Normal file
|
@ -0,0 +1,21 @@
|
|||
<div class="profile-list-container">
|
||||
<ucap-organization-profile-list [userInfos]="userInfos">
|
||||
<ng-template ucapOrganizationProfileListNode let-userInfo>
|
||||
<app-group-profile-list-item-02
|
||||
[userInfo]="userInfo"
|
||||
[checkable]="checkable"
|
||||
[isDialog]="isDialog"
|
||||
[checked]="getCheckedUser(userInfo)"
|
||||
(openProfile)="onOpenProfile($event)"
|
||||
(changeCheckUser)="onChangeCheckUser($event)"
|
||||
>
|
||||
<ucap-organization-profile-image-01
|
||||
ucapOrganizationProfileListItem01="profileImage"
|
||||
[userInfo]="userInfo"
|
||||
[profileImageRoot]="versionInfo2Res?.profileRoot"
|
||||
defaultProfileImage="assets/images/ico/img_nophoto.svg"
|
||||
></ucap-organization-profile-image-01>
|
||||
</app-group-profile-list-item-02>
|
||||
</ng-template>
|
||||
</ucap-organization-profile-list>
|
||||
</div>
|
|
@ -0,0 +1,4 @@
|
|||
.profile-list-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
32
src/app/ucap/group/components/profile-list.component.spec.ts
Normal file
32
src/app/ucap/group/components/profile-list.component.spec.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { TestBed, async } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { Profile01Component } from './profile-01.component';
|
||||
|
||||
describe('app::ucap::organization::Profile01Component', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [RouterTestingModule],
|
||||
declarations: [Profile01Component]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(Profile01Component);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'ucap-lg-web'`, () => {
|
||||
const fixture = TestBed.createComponent(Profile01Component);
|
||||
const app = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(Profile01Component);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.nativeElement;
|
||||
expect(compiled.querySelector('.content span').textContent).toContain(
|
||||
'ucap-lg-web app is running!'
|
||||
);
|
||||
});
|
||||
});
|
207
src/app/ucap/group/components/profile-list.component.ts
Normal file
207
src/app/ucap/group/components/profile-list.component.ts
Normal file
|
@ -0,0 +1,207 @@
|
|||
import { Subject, of } from 'rxjs';
|
||||
import { takeUntil, take, map, catchError } from 'rxjs/operators';
|
||||
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Input,
|
||||
Output,
|
||||
EventEmitter
|
||||
} from '@angular/core';
|
||||
|
||||
import { Store, select } from '@ngrx/store';
|
||||
|
||||
import { VersionInfo2Response } from '@ucap/api-public';
|
||||
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||
import {
|
||||
AuthResponse,
|
||||
UserInfoSS,
|
||||
DeptSearchType,
|
||||
DeptUserRequest
|
||||
} from '@ucap/protocol-query';
|
||||
|
||||
import { LogService } from '@ucap/ng-logger';
|
||||
import {
|
||||
LoginSelector,
|
||||
AuthorizationSelector,
|
||||
ConfigurationSelector
|
||||
} from '@ucap/ng-store-authentication';
|
||||
import { QueryProtocolService } from '@ucap/ng-protocol-query';
|
||||
import {
|
||||
DepartmentSelector,
|
||||
PresenceActions
|
||||
} from '@ucap/ng-store-organization';
|
||||
import {
|
||||
FixedSizeVirtualScrollStrategy,
|
||||
VIRTUAL_SCROLL_STRATEGY
|
||||
} from '@angular/cdk/scrolling';
|
||||
|
||||
import { SearchData } from '@app/ucap/organization/models/search-data';
|
||||
|
||||
export class ProfileListVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy {
|
||||
constructor() {
|
||||
super(70, 250, 500); // (itemSize, minBufferPx, maxBufferPx)
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-group-profile-list',
|
||||
templateUrl: './profile-list.component.html',
|
||||
styleUrls: ['./profile-list.component.scss'],
|
||||
providers: [
|
||||
{
|
||||
provide: VIRTUAL_SCROLL_STRATEGY,
|
||||
useClass: ProfileListVirtualScrollStrategy
|
||||
}
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ProfileListComponent implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
checkable = false;
|
||||
|
||||
@Input()
|
||||
isDialog = false;
|
||||
|
||||
@Input()
|
||||
set searchData(data: SearchData) {
|
||||
if (!this.loginRes) {
|
||||
this._searchData = data;
|
||||
return;
|
||||
}
|
||||
this.searchMember(data);
|
||||
}
|
||||
// tslint:disable-next-line: variable-name
|
||||
private _searchData: SearchData;
|
||||
|
||||
@Input() selectedUser: UserInfoSS[];
|
||||
|
||||
@Output()
|
||||
searched: EventEmitter<UserInfoSS[]> = new EventEmitter();
|
||||
|
||||
@Output()
|
||||
toggleCheck: EventEmitter<{
|
||||
checked: boolean;
|
||||
userInfo: UserInfoSS;
|
||||
}> = new EventEmitter();
|
||||
|
||||
set userInfos(userInfos: UserInfoSS[]) {
|
||||
this._userInfos = userInfos;
|
||||
this.searched.emit(userInfos);
|
||||
}
|
||||
get userInfos() {
|
||||
return this._userInfos;
|
||||
}
|
||||
// tslint:disable-next-line: variable-name
|
||||
_userInfos: UserInfoSS[] = [];
|
||||
|
||||
loginRes: LoginResponse;
|
||||
versionInfo2Res: VersionInfo2Response;
|
||||
|
||||
processing = false;
|
||||
|
||||
private ngOnDestroySubject: Subject<void>;
|
||||
|
||||
constructor(
|
||||
private queryProtocolService: QueryProtocolService,
|
||||
private store: Store<any>,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private logService: LogService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject();
|
||||
|
||||
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;
|
||||
if (!!this._searchData) {
|
||||
this.searchMember(this._searchData);
|
||||
this._searchData = undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
||||
/** 개별 체크여부 */
|
||||
getCheckedUser(userInfo: UserInfoSS) {
|
||||
if (!!this.selectedUser && this.selectedUser.length > 0) {
|
||||
return (
|
||||
this.selectedUser.filter((item) => item.seq === userInfo.seq).length > 0
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 개별선택(토글) 이벤트 */
|
||||
onChangeCheckUser(param: { checked: boolean; userInfo: UserInfoSS }) {
|
||||
this.toggleCheck.emit(param);
|
||||
}
|
||||
|
||||
onOpenProfile(userInfo: UserInfoSS): void {
|
||||
alert('Open Profile');
|
||||
}
|
||||
|
||||
private searchMember(searchData: SearchData) {
|
||||
if (!searchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const req = {
|
||||
divCd: 'ORGS',
|
||||
companyCode: searchData.companyCode,
|
||||
searchRange: DeptSearchType.All,
|
||||
search: searchData.searchWord,
|
||||
senderCompanyCode: this.loginRes.userInfo.companyCode,
|
||||
senderEmployeeType: this.loginRes.userInfo.employeeType
|
||||
} as DeptUserRequest;
|
||||
|
||||
this.processing = true;
|
||||
this.queryProtocolService
|
||||
.deptUser(req)
|
||||
.pipe(take(1))
|
||||
.subscribe(
|
||||
(data) => {
|
||||
this.userInfos = data.userInfos.sort((a, b) =>
|
||||
a.name < b.name ? -1 : a.name > b.name ? 1 : 0
|
||||
);
|
||||
this.changeDetectorRef.detectChanges();
|
||||
|
||||
// 검색 결과에 따른 프레즌스 조회.
|
||||
const userSeqList: string[] = [];
|
||||
this.userInfos.map((user) => userSeqList.push(user.seq));
|
||||
|
||||
if (userSeqList.length > 0) {
|
||||
this.store.dispatch(
|
||||
PresenceActions.bulkInfo({
|
||||
divCd: 'orgSrch',
|
||||
userSeqs: userSeqList
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
(error) => {},
|
||||
() => {
|
||||
this.processing = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
[checkable]="loginRes?.userSeq !== userInfo.seq"
|
||||
[checked]="getCheckedUser(userInfo)"
|
||||
(openProfile)="onOpenProfile($event)"
|
||||
(changeCheck)="onToggleUser($event)"
|
||||
(changeCheck)="onChangeCheckUser($event)"
|
||||
>
|
||||
<ucap-organization-profile-image-01
|
||||
ucapOrganizationProfileListItem01="profileImage"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Subject, of } from 'rxjs';
|
||||
import { takeUntil, take, map, catchError } from 'rxjs/operators';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil, take } from 'rxjs/operators';
|
||||
|
||||
import {
|
||||
Component,
|
||||
|
@ -7,91 +7,115 @@ import {
|
|||
OnDestroy,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Input
|
||||
Input,
|
||||
Output,
|
||||
EventEmitter
|
||||
} from '@angular/core';
|
||||
|
||||
import { Store, select } from '@ngrx/store';
|
||||
|
||||
import { VersionInfo2Response } from '@ucap/api-public';
|
||||
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||
import { AuthResponse, UserInfoSS, DeptSearchType } from '@ucap/protocol-query';
|
||||
import {
|
||||
UserInfoSS,
|
||||
DeptSearchType,
|
||||
DeptUserRequest
|
||||
} from '@ucap/protocol-query';
|
||||
|
||||
import { LogService } from '@ucap/ng-logger';
|
||||
import {
|
||||
LoginSelector,
|
||||
AuthorizationSelector,
|
||||
ConfigurationSelector
|
||||
} from '@ucap/ng-store-authentication';
|
||||
import { QueryProtocolService } from '@ucap/ng-protocol-query';
|
||||
import {
|
||||
DepartmentActions,
|
||||
DepartmentSelector,
|
||||
PresenceActions
|
||||
} from '@ucap/ng-store-organization';
|
||||
import {
|
||||
FixedSizeVirtualScrollStrategy,
|
||||
VIRTUAL_SCROLL_STRATEGY
|
||||
} from '@angular/cdk/scrolling';
|
||||
|
||||
import { SearchData } from '../models/search-data';
|
||||
import { SortOrder } from '@ucap/core';
|
||||
|
||||
export class ProfileListVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy {
|
||||
constructor() {
|
||||
super(70, 250, 500); // (itemSize, minBufferPx, maxBufferPx)
|
||||
}
|
||||
}
|
||||
|
||||
interface CheckedInfo {
|
||||
checked: boolean;
|
||||
userInfo: UserInfoSS;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-organization-profile-list',
|
||||
templateUrl: './profile-list.component.html',
|
||||
styleUrls: ['./profile-list.component.scss'],
|
||||
providers: [
|
||||
{
|
||||
provide: VIRTUAL_SCROLL_STRATEGY,
|
||||
useClass: ProfileListVirtualScrollStrategy
|
||||
}
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ProfileListComponent implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
set searchData(data: {
|
||||
companyCode: string;
|
||||
searchWord: string;
|
||||
isSearch: boolean;
|
||||
}) {
|
||||
set searchData(data: SearchData) {
|
||||
if (!this.loginRes) {
|
||||
this._searchData = data;
|
||||
return;
|
||||
}
|
||||
this.processing = true;
|
||||
|
||||
this.queryProtocolService
|
||||
.deptUser({
|
||||
divCd: 'ORGS',
|
||||
companyCode: data.companyCode,
|
||||
searchRange: DeptSearchType.All,
|
||||
search: data.searchWord,
|
||||
senderCompanyCode: this.loginRes.userInfo.companyCode,
|
||||
senderEmployeeType: this.loginRes.userInfo.employeeType
|
||||
})
|
||||
.pipe(take(1))
|
||||
.subscribe(
|
||||
(res) => {
|
||||
// 검색 결과 처리.
|
||||
this.userInfos = res.userInfos.sort((a, b) =>
|
||||
a.name < b.name ? -1 : a.name > b.name ? 1 : 0
|
||||
);
|
||||
this.changeDetectorRef.detectChanges();
|
||||
|
||||
// 검색 결과에 따른 프레즌스 조회.
|
||||
const userSeqList: string[] = [];
|
||||
this.userInfos.map((user) => userSeqList.push(user.seq));
|
||||
|
||||
if (userSeqList.length > 0) {
|
||||
this.store.dispatch(
|
||||
PresenceActions.bulkInfo({
|
||||
divCd: 'orgSrch',
|
||||
userSeqs: userSeqList
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
(error) => {},
|
||||
() => {
|
||||
this.processing = false;
|
||||
}
|
||||
);
|
||||
this.searchMember(data);
|
||||
}
|
||||
// tslint:disable-next-line: variable-name
|
||||
private _searchData: SearchData;
|
||||
|
||||
@Input()
|
||||
selectedUser: UserInfoSS[];
|
||||
|
||||
@Input()
|
||||
set sortOrder(value: SortOrder) {
|
||||
this._sortOrder = value;
|
||||
this.userInfos = this.sort(this.userInfos);
|
||||
}
|
||||
get sortOrder() {
|
||||
return this._sortOrder;
|
||||
}
|
||||
// tslint:disable-next-line: variable-name
|
||||
_sortOrder: SortOrder = {
|
||||
property: 'name',
|
||||
ascending: true
|
||||
};
|
||||
|
||||
@Output()
|
||||
searched: EventEmitter<UserInfoSS[]> = new EventEmitter();
|
||||
|
||||
@Output()
|
||||
changedCheck: EventEmitter<CheckedInfo[]> = new EventEmitter();
|
||||
|
||||
set userInfos(userInfos: UserInfoSS[]) {
|
||||
this._userInfos = userInfos;
|
||||
this.searched.emit(userInfos);
|
||||
}
|
||||
get userInfos() {
|
||||
return this._userInfos;
|
||||
}
|
||||
// tslint:disable-next-line: variable-name
|
||||
_userInfos: UserInfoSS[] = [];
|
||||
|
||||
loginRes: LoginResponse;
|
||||
versionInfo2Res: VersionInfo2Response;
|
||||
|
||||
userInfos: UserInfoSS[] = [];
|
||||
selectedUserInfos: UserInfoSS[] = [];
|
||||
processing = false;
|
||||
|
||||
private ngOnDestroySubject: Subject<void>;
|
||||
private myDeptDestroySubject: Subject<void>;
|
||||
|
||||
constructor(
|
||||
private queryProtocolService: QueryProtocolService,
|
||||
|
@ -116,6 +140,10 @@ export class ProfileListComponent implements OnInit, OnDestroy {
|
|||
.pipe(takeUntil(this.ngOnDestroySubject), select(LoginSelector.loginRes))
|
||||
.subscribe((loginRes) => {
|
||||
this.loginRes = loginRes;
|
||||
if (!!this._searchData) {
|
||||
this.searchMember(this._searchData);
|
||||
this._searchData = undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -125,36 +153,156 @@ export class ProfileListComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
checkAll() {
|
||||
if (!this.userInfos || 0 === this.userInfos.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const checked: CheckedInfo[] = [];
|
||||
this.userInfos.forEach((u) => {
|
||||
checked.push({
|
||||
checked: true,
|
||||
userInfo: u
|
||||
});
|
||||
});
|
||||
this.changedCheck.emit(checked);
|
||||
}
|
||||
|
||||
/** 개별 체크여부 */
|
||||
getCheckedUser(userInfo: UserInfoSS) {
|
||||
if (!!this.selectedUserInfos && this.selectedUserInfos.length > 0) {
|
||||
if (!!this.selectedUser && this.selectedUser.length > 0) {
|
||||
return (
|
||||
this.selectedUserInfos.filter((item) => item.seq === userInfo.seq)
|
||||
.length > 0
|
||||
this.selectedUser.filter((item) => item.seq === userInfo.seq).length > 0
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 개별선택(토글) 이벤트 */
|
||||
onToggleUser(param: { isChecked: boolean; userInfo: UserInfoSS }) {
|
||||
if (!this.loginRes || param.userInfo.seq === this.loginRes.userSeq) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!this.selectedUserInfos.some((user) => user.seq === param.userInfo.seq)
|
||||
) {
|
||||
this.selectedUserInfos = [...this.selectedUserInfos, param.userInfo];
|
||||
} else {
|
||||
this.selectedUserInfos = this.selectedUserInfos.filter(
|
||||
(item) => item.seq !== param.userInfo.seq
|
||||
);
|
||||
}
|
||||
this.changeDetectorRef.detectChanges();
|
||||
onChangeCheckUser(param: { checked: boolean; userInfo: UserInfoSS }) {
|
||||
this.changedCheck.emit([param]);
|
||||
}
|
||||
|
||||
onOpenProfile(userInfo: UserInfoSS): void {
|
||||
alert('Open Profile');
|
||||
}
|
||||
|
||||
private getMyDeptMember() {
|
||||
this.myDeptDestroySubject = new Subject();
|
||||
|
||||
const req: DeptUserRequest = {
|
||||
divCd: 'ORG',
|
||||
companyCode: this.loginRes.companyCode,
|
||||
seq: this.loginRes.departmentCode,
|
||||
search: '',
|
||||
searchRange: DeptSearchType.All,
|
||||
senderCompanyCode: this.loginRes.companyCode,
|
||||
senderEmployeeType: this.loginRes.userInfo.employeeType
|
||||
};
|
||||
|
||||
this.store
|
||||
.pipe(
|
||||
takeUntil(this.ngOnDestroySubject),
|
||||
select(DepartmentSelector.myDepartmentUserInfoList)
|
||||
)
|
||||
.subscribe(
|
||||
(myDepartmentUserInfoList) => {
|
||||
if (!myDepartmentUserInfoList) {
|
||||
this.store.dispatch(DepartmentActions.myDeptUser({ req }));
|
||||
return;
|
||||
}
|
||||
this.userInfos = this.sort(myDepartmentUserInfoList);
|
||||
|
||||
this.myDeptDestroySubject.next();
|
||||
this.myDeptDestroySubject.complete();
|
||||
this.myDeptDestroySubject = undefined;
|
||||
},
|
||||
(error) => {},
|
||||
() => {}
|
||||
);
|
||||
}
|
||||
|
||||
private sort(userInfos: UserInfoSS[]): UserInfoSS[] {
|
||||
if (!userInfos || 0 === userInfos.length) {
|
||||
return userInfos;
|
||||
}
|
||||
|
||||
const property = this.sortOrder.property;
|
||||
const ascending = this.sortOrder.ascending;
|
||||
|
||||
return userInfos.slice().sort((a, b) => {
|
||||
let c: any;
|
||||
let d: any;
|
||||
try {
|
||||
c = ascending ? a[property] : b[property];
|
||||
d = ascending ? b[property] : a[property];
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
return c < d ? -1 : c > d ? 1 : 0;
|
||||
});
|
||||
}
|
||||
|
||||
private searchMember(searchData: SearchData) {
|
||||
if (!searchData || (!searchData.companyCode && !searchData.deptSeq)) {
|
||||
this.getMyDeptMember();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!!this.myDeptDestroySubject) {
|
||||
this.myDeptDestroySubject.next();
|
||||
this.myDeptDestroySubject.complete();
|
||||
this.myDeptDestroySubject = undefined;
|
||||
}
|
||||
|
||||
let req: DeptUserRequest;
|
||||
if (searchData.bySearch) {
|
||||
req = {
|
||||
divCd: 'ORGS',
|
||||
companyCode: searchData.companyCode,
|
||||
searchRange: DeptSearchType.All,
|
||||
search: searchData.searchWord,
|
||||
senderCompanyCode: this.loginRes.userInfo.companyCode,
|
||||
senderEmployeeType: this.loginRes.userInfo.employeeType
|
||||
};
|
||||
} else {
|
||||
req = {
|
||||
divCd: 'ORG',
|
||||
companyCode: this.loginRes.companyCode,
|
||||
seq: Number(searchData.deptSeq),
|
||||
search: '',
|
||||
searchRange: DeptSearchType.All,
|
||||
senderCompanyCode: this.loginRes.companyCode,
|
||||
senderEmployeeType: this.loginRes.userInfo.employeeType
|
||||
};
|
||||
}
|
||||
|
||||
this.processing = true;
|
||||
this.queryProtocolService
|
||||
.deptUser(req)
|
||||
.pipe(take(1))
|
||||
.subscribe(
|
||||
(data) => {
|
||||
this.userInfos = this.sort(data.userInfos);
|
||||
this.changeDetectorRef.detectChanges();
|
||||
|
||||
// 검색 결과에 따른 프레즌스 조회.
|
||||
const userSeqList: string[] = [];
|
||||
this.userInfos.map((user) => userSeqList.push(user.seq));
|
||||
|
||||
if (userSeqList.length > 0) {
|
||||
this.store.dispatch(
|
||||
PresenceActions.bulkInfo({
|
||||
divCd: 'orgSrch',
|
||||
userSeqs: userSeqList
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
(error) => {},
|
||||
() => {
|
||||
this.processing = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<div class="search-container">
|
||||
<ucap-organization-search-for-tenant
|
||||
[companyList]="companyList"
|
||||
[defaultCompany]="defaultCompany"
|
||||
[defaultCompany]="searchData.companyCode"
|
||||
[defaultSearchWord]="searchData.searchWord"
|
||||
placeholder="이름 부서명, 전화번호, 이메일"
|
||||
(changed)="onChanged($event)"
|
||||
(canceled)="onCanceled()"
|
||||
|
|
|
@ -8,7 +8,8 @@ import {
|
|||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Output,
|
||||
EventEmitter
|
||||
EventEmitter,
|
||||
Input
|
||||
} from '@angular/core';
|
||||
|
||||
import { Store, select } from '@ngrx/store';
|
||||
|
@ -19,6 +20,8 @@ import { LogService } from '@ucap/ng-logger';
|
|||
import { CompanySelector } from '@ucap/ng-store-organization';
|
||||
|
||||
import { AppAuthenticationService } from '@app/services/app-authentication.service';
|
||||
import { SearchData } from '../models/search-data';
|
||||
import { UserStore } from '@app/models/user-store';
|
||||
|
||||
@Component({
|
||||
selector: 'app-organization-search-for-tenant',
|
||||
|
@ -27,34 +30,45 @@ import { AppAuthenticationService } from '@app/services/app-authentication.servi
|
|||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class SearchForTenantComponent implements OnInit, OnDestroy {
|
||||
@Output()
|
||||
changed: EventEmitter<{
|
||||
isShowSearch: boolean;
|
||||
companyCode: string;
|
||||
searchWord: string;
|
||||
}> = new EventEmitter();
|
||||
@Output() searchDataChange: EventEmitter<SearchData> = new EventEmitter();
|
||||
@Input() set searchData(value: SearchData) {
|
||||
this._searchData = value;
|
||||
if (!this._searchData) {
|
||||
this._searchData = {
|
||||
companyCode: this.userStore.companyCode
|
||||
};
|
||||
} else {
|
||||
if (!this._searchData.companyCode) {
|
||||
this._searchData.companyCode = this.userStore.companyCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
get searchData() {
|
||||
return this._searchData;
|
||||
}
|
||||
// tslint:disable-next-line: variable-name
|
||||
_searchData: SearchData;
|
||||
|
||||
@Output()
|
||||
canceled: EventEmitter<void> = new EventEmitter();
|
||||
|
||||
companyList: Company[];
|
||||
defaultCompany: string;
|
||||
|
||||
private ngOnDestroySubject = new Subject<boolean>();
|
||||
private userStore: UserStore;
|
||||
|
||||
constructor(
|
||||
private appAuthenticationService: AppAuthenticationService,
|
||||
private store: Store<any>,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private logService: LogService
|
||||
) {}
|
||||
) {
|
||||
this.userStore = this.appAuthenticationService.getUserStore();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject<boolean>();
|
||||
|
||||
const userStore = this.appAuthenticationService.getUserStore();
|
||||
this.defaultCompany = userStore.companyCode;
|
||||
|
||||
this.store
|
||||
.pipe(
|
||||
takeUntil(this.ngOnDestroySubject),
|
||||
|
@ -72,15 +86,14 @@ export class SearchForTenantComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
onChanged(data: { companyCode: string; searchWord: string }): void {
|
||||
this.changed.emit({
|
||||
...data,
|
||||
isShowSearch: true
|
||||
this.searchDataChange.emit({
|
||||
companyCode: data.companyCode,
|
||||
searchWord: data.searchWord
|
||||
});
|
||||
}
|
||||
|
||||
onCanceled(): void {
|
||||
this.changed.emit({
|
||||
isShowSearch: false,
|
||||
this.searchDataChange.emit({
|
||||
companyCode: '',
|
||||
searchWord: ''
|
||||
});
|
||||
|
|
|
@ -53,7 +53,7 @@ export class TreeComponent implements OnInit, OnDestroy {
|
|||
@Input()
|
||||
set initialExpanded(seq: number) {
|
||||
if (!!this.treeData) {
|
||||
this.treeList.expand(seq);
|
||||
this.expand(seq);
|
||||
return;
|
||||
}
|
||||
this._initialExpanded = seq;
|
||||
|
|
6
src/app/ucap/organization/models/search-data.ts
Normal file
6
src/app/ucap/organization/models/search-data.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
export interface SearchData {
|
||||
deptSeq?: string;
|
||||
companyCode?: string;
|
||||
searchWord?: string;
|
||||
bySearch?: boolean;
|
||||
}
|
|
@ -6,13 +6,10 @@
|
|||
"openRoom": "Open room",
|
||||
"turnOnRoomAlert": "Turn on room alert",
|
||||
"turnOffRoomAlert": "Turn off room alert",
|
||||
"exitFromRoom": "Exit room",
|
||||
"dialog": {
|
||||
"titleExitFromRoom": "Exit room",
|
||||
"confirmExitFromRoom": "Do you want to exit the chat room?<br/>Exiting will delete your chat history and chat room information."
|
||||
}
|
||||
"exitFromRoom": "Exit room"
|
||||
},
|
||||
"label": {
|
||||
"chat": "Chat",
|
||||
"menu": "Menu",
|
||||
"search": "Search",
|
||||
"notificationIsOn": "Notification is on",
|
||||
|
@ -26,5 +23,51 @@
|
|||
"emailSend": "Send email for chat.",
|
||||
"translation": "Trnaslation",
|
||||
"gams": "+GAMS"
|
||||
},
|
||||
"event": {
|
||||
"inviteToRoomWith": "{{owner}} invited {{inviter}}.",
|
||||
"exitFromRoomWith": "{{exitor}} has left.",
|
||||
"ejectedFromRoomWith": "{{requester}} has eject {{ejected}}.",
|
||||
"renamedRoomWith": "{{requester}} has changed their chat room name to '{{roomName}}'.",
|
||||
"setTimerWith": "{{requester}} set a timer ({{timer}})",
|
||||
"iosCapture": "{{requester}} captured a conversation",
|
||||
"showMassTranslationOfOriginal": "Show original",
|
||||
"showMassTranslationOfTranslated": "Show translated",
|
||||
"showMassDetail": "Show detail",
|
||||
"readToHere": "Read to here",
|
||||
"recalled": "Recalled",
|
||||
"scheduleTypeNew": "[Event] Registered",
|
||||
"scheduleTypeUpdate": "[Event] Modified",
|
||||
"scheduleTypeDelete": "[Event] Deleted",
|
||||
"scheduleTypeDefault": "[Event] Processing..",
|
||||
"scheduleTypePrefix": "[Event] ",
|
||||
"scheduleTypeSurfixLeft": " left",
|
||||
"showPreviousEvents": "Show previous",
|
||||
"moreUnreadEventsWith": "There is unread messages<span class=\"text-warn-color\">({{countOfUnread}})</span>"
|
||||
},
|
||||
"errors": {
|
||||
"label": "Chat erros",
|
||||
"inputChatMessage": "Please enter a chat message",
|
||||
"maxLengthOfMassText": "If you include a sticker, you can't send more than {{maxLength}} characters.",
|
||||
"maxCountOfRoomMemberWith": "you can't open room with more than {{maxCount}} peoples.",
|
||||
"emptyOpenRoomType": "Pleas select type of room",
|
||||
"translateServerError": "Failed to translate"
|
||||
},
|
||||
"dialog": {
|
||||
"title": {
|
||||
"exitFromRoom": "Exit room",
|
||||
"newChatRoom": "Add new chat room"
|
||||
},
|
||||
"confirmExitFromRoom": "Do you want to exit the chat room?<br/>Exiting will delete your chat history and chat room information.",
|
||||
"normalRoom": "Basic Room",
|
||||
"timerRoom": "Timer Room",
|
||||
"normalRoomDescription": "<strong>Up to {{maxCount}} peoples</strong><br/>can join.",
|
||||
"timerRoomDescription": "<strong>When setting the timer,</strong><br/>the conversation is automatically deleted.",
|
||||
"button": {
|
||||
"cancel": "Cancel",
|
||||
"previous": "Previous",
|
||||
"selectRoomUser": "Choice user",
|
||||
"openRoom": "Open chat room"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
"common": {
|
||||
"messages": {
|
||||
"no": "No",
|
||||
"yes": "Yes"
|
||||
"yes": "Yes",
|
||||
"confirm": "Confirm"
|
||||
},
|
||||
"units": {
|
||||
"date": "Date",
|
||||
|
@ -17,6 +18,14 @@
|
|||
"tomorrowAfternoon": "Tomorrow afternoon",
|
||||
"weekLaterWith": "(A) {{week}} week(s) later",
|
||||
"monthLaterWith": "(A) {{month}} month(s) later"
|
||||
},
|
||||
"file": {
|
||||
"errors": {
|
||||
"failToUpload": "File upload failed.",
|
||||
"notSupporedType": "File format is not supported. <br/> ({{supporedType}})",
|
||||
"notAcceptableMime": "File type is invalid. <br/> ({{supporedType}})",
|
||||
"oversize": "You cannot upload files larger than {{size}} megabytes."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,13 +6,10 @@
|
|||
"openRoom": "대화방 열기",
|
||||
"turnOnRoomAlert": "대화방 알람 켜기",
|
||||
"turnOffRoomAlert": "대화방 알람 끄기",
|
||||
"exitFromRoom": "대화방 나가기",
|
||||
"dialog": {
|
||||
"titleExitFromRoom": "대화방 나가기",
|
||||
"confirmExitFromRoom": "대화방을 나가시겠습니까?<br/>나가기를 하면 대화내용 및 대화방 정보가 삭제됩니다."
|
||||
}
|
||||
"exitFromRoom": "대화방 나가기"
|
||||
},
|
||||
"label": {
|
||||
"chat": "대화",
|
||||
"menu": "메뉴",
|
||||
"search": "검색",
|
||||
"notificationIsOn": "알림 켜짐",
|
||||
|
@ -26,5 +23,51 @@
|
|||
"emailSend": "대화내용 메일전송",
|
||||
"translation": "대화내용 번역",
|
||||
"gams": "+GAMS"
|
||||
},
|
||||
"event": {
|
||||
"inviteToRoomWith": "{{owner}}님이 {{inviter}}님을 초대했습니다.",
|
||||
"exitFromRoomWith": "{{exitor}}님이 퇴장하셨습니다.",
|
||||
"ejectedFromRoomWith": "{{requester}}님이 {{ejected}}님을 퇴장 시키셨습니다.",
|
||||
"renamedRoomWith": "{{requester}}님이 대화방명을 '{{roomName}}'으로 변경하셨습니다.",
|
||||
"setTimerWith": "{{requester}}님이 타이머를 설정하였습니다. ({{timer}})",
|
||||
"iosCapture": "{{requester}}님이 대화내용을 캡쳐하였습니다.",
|
||||
"showMassTranslationOfOriginal": "원본 보기",
|
||||
"showMassTranslationOfTranslated": "번역 보기",
|
||||
"showMassDetail": "전체 보기",
|
||||
"readToHere": "여기까지 읽었습니다.",
|
||||
"recalled": "회수된 메시지",
|
||||
"scheduleTypeNew": "[이벤트] 등록",
|
||||
"scheduleTypeUpdate": "[이벤트] 수정",
|
||||
"scheduleTypeDelete": "[이벤트] 삭제",
|
||||
"scheduleTypeDefault": "[이벤트] 조회중..",
|
||||
"scheduleTypePrefix": "[이벤트] ",
|
||||
"scheduleTypeSurfixLeft": " 전 알림",
|
||||
"showPreviousEvents": "이전 대화 보기",
|
||||
"moreUnreadEventsWith": "안읽은 메시지가 <span class=\"text-warn-color\">({{countOfUnread}})</span>개 더 있습니다."
|
||||
},
|
||||
"errors": {
|
||||
"label": "대화 에러",
|
||||
"inputChatMessage": "대화 내용을 입력해 주세요.",
|
||||
"maxLengthOfMassText": "스티커를 포함할 경우 {{maxLength}}자 이상 보낼 수 없습니다.",
|
||||
"maxCountOfRoomMemberWith": "{{maxCount}}명 이상 대화할 수 없습니다.",
|
||||
"emptyOpenRoomType": "대화방 타입을 선택해 주세요.",
|
||||
"translateServerError": "번역하지 못했습니다."
|
||||
},
|
||||
"dialog": {
|
||||
"title": {
|
||||
"exitFromRoom": "대화방 나가기",
|
||||
"newChatRoom": "새로운 대화방 추가"
|
||||
},
|
||||
"confirmExitFromRoom": "대화방을 나가시겠습니까?<br/>나가기를 하면 대화내용 및 대화방 정보가 삭제됩니다.",
|
||||
"normalRoom": "일반 대화방",
|
||||
"timerRoom": "타이머 대화방",
|
||||
"normalRoomDescription": "<strong>{{maxCount}}명 까지</strong><br/>참여가 가능합니다.",
|
||||
"timerRoomDescription": "<strong>타이머 설정시</strong><br/>대화내용이 자동으로 삭제됩니다.",
|
||||
"button": {
|
||||
"cancel": "취소",
|
||||
"previous": "이전",
|
||||
"selectRoomUser": "대화방 멤버 선택",
|
||||
"openRoom": "대화방 생성"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
"common": {
|
||||
"messages": {
|
||||
"no": "아니요",
|
||||
"yes": "예"
|
||||
"yes": "예",
|
||||
"confirm": "확인"
|
||||
},
|
||||
"units": {
|
||||
"date": "날짜",
|
||||
|
@ -17,6 +18,14 @@
|
|||
"tomorrowAfternoon": "내일 오후",
|
||||
"weekLaterWith": "{{week}}주일 뒤",
|
||||
"monthLaterWith": "{{month}}달 뒤"
|
||||
},
|
||||
"file": {
|
||||
"errors": {
|
||||
"failToUpload": "파일 업로드에 실패하였습니다.",
|
||||
"notSupporedType": "지원하지 않는 파일형식입니다. <br/> ({{supporedType}})",
|
||||
"notAcceptableMime": "유효하지 않은 파일 타입입니다. <br/> ({{supporedType}})",
|
||||
"oversize": "{{maxSize}}MB 이상 파일을 업로드 할 수 없습니다."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
// Material theming tools
|
||||
@import '~@angular/material/theming';
|
||||
@import 'mixins';
|
||||
|
||||
// Include core Angular Material styles
|
||||
@include mat-core();
|
||||
|
||||
//creative
|
||||
@import 'global/default';
|
||||
@import 'global/ucap-ui';
|
||||
@import 'global/material-ui';
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
// Material theming tools
|
||||
@import '~@angular/material/theming';
|
||||
|
||||
@import '~@ucap/ng-ui-material/material';
|
||||
|
||||
@import 'setting/variables';
|
||||
|
||||
@import '~@ucap/ng-ui-material/material';
|
||||
|
||||
@import 'mixins/font';
|
||||
@import 'mixins/dom';
|
||||
|
||||
|
|
|
@ -139,7 +139,7 @@
|
|||
.mat-tab-label[aria-selected='true'] {
|
||||
.mat-tab-label-content {
|
||||
.icon-item {
|
||||
background: mat-color($accent, 300);
|
||||
background: mat-color($accent, 700);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -212,6 +212,7 @@
|
|||
.mat-form-field-infix {
|
||||
align-self: center;
|
||||
width: inherit;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -499,10 +500,58 @@
|
|||
.color-white {
|
||||
color: $white;
|
||||
}
|
||||
//mat chip
|
||||
.mat-chip {
|
||||
&.mat-standard-chip {
|
||||
background-color: mat-color($accent, A400);
|
||||
color: mat-color($primary, default-contrast);
|
||||
.mat-chip-remove {
|
||||
//color: rgba(0, 0, 0, 0.87);
|
||||
color: mat-color($primary, default-contrast);
|
||||
}
|
||||
}
|
||||
}
|
||||
.mat-chip.mat-standard-chip.mat-chip-selected.mat-primary {
|
||||
background-color: mat-color($accent, 700);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
//mat checkbox
|
||||
.mat-checkbox-inner-container {
|
||||
width: 18px !important;
|
||||
height: 18px !important;
|
||||
background-color: #fff;
|
||||
}
|
||||
.mat-checkbox-checked {
|
||||
&.mat-accent {
|
||||
.mat-checkbox-background {
|
||||
border: 2px solid mat-color($accent, 700);
|
||||
background-color: #fff;
|
||||
border-radius: 2px;
|
||||
.mat-checkbox-checkmark-path {
|
||||
stroke: mat-color($accent, 700) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//mat dadge
|
||||
.mat-badge-accent .mat-badge-content,
|
||||
.weblink .mat-badge-content {
|
||||
background-color: mat-color($warn, 400);
|
||||
background-color: mat-color($warn, 500);
|
||||
border: 1px solid mat-color($warn, 500);
|
||||
}
|
||||
.noti-sum {
|
||||
.mat-badge-content {
|
||||
right: 16px !important;
|
||||
bottom: 12px !important;
|
||||
min-width: 22px;
|
||||
width: auto;
|
||||
line-height: 20px;
|
||||
align-items: center;
|
||||
padding: 0 5px;
|
||||
border-radius: 11px;
|
||||
}
|
||||
}
|
||||
//mat-card
|
||||
.allim-card {
|
||||
|
@ -616,6 +665,43 @@
|
|||
}
|
||||
}
|
||||
|
||||
//mat slide toggle
|
||||
.mat-slide-toggle {
|
||||
.mat-slide-toggle-bar {
|
||||
width: 32px;
|
||||
height: 18px;
|
||||
border-radius: 9px;
|
||||
border: 1px solid #707070;
|
||||
background-color: #fff;
|
||||
.mat-slide-toggle-thumb-container {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
.mat-slide-toggle-thumb {
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.2),
|
||||
0px 1px 1px 0px rgba(0, 0, 0, 0.14),
|
||||
0px 1px 3px 0px rgba(0, 0, 0, 0.12);
|
||||
background-color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.mat-checked {
|
||||
.mat-slide-toggle-bar {
|
||||
background-color: #fff;
|
||||
border: 1px solid #fd578a;
|
||||
.mat-slide-toggle-thumb-container {
|
||||
transform: translate3d(14px, 0, 0);
|
||||
.mat-slide-toggle-thumb {
|
||||
background-color: #e42f66;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Btn Float
|
||||
.btn-main-float {
|
||||
right: 22px !important;
|
||||
|
|
6
src/assets/scss/global/_ucap-ui.scss
Normal file
6
src/assets/scss/global/_ucap-ui.scss
Normal file
|
@ -0,0 +1,6 @@
|
|||
@import 'ucap/organization';
|
||||
@import 'ucap/authentication';
|
||||
@import 'ucap/group';
|
||||
@import 'ucap/call';
|
||||
@import 'ucap/message';
|
||||
@import 'ucap/call';
|
205
src/assets/scss/global/ucap/_authentication.scss
Normal file
205
src/assets/scss/global/ucap/_authentication.scss
Normal file
|
@ -0,0 +1,205 @@
|
|||
.ucap-authentication-login-container {
|
||||
background-color: transparent !important;
|
||||
@include screen(mid) {
|
||||
width: 350px !important;
|
||||
}
|
||||
@include screen(xs) {
|
||||
width: 300px !important;
|
||||
}
|
||||
.ucap-authentication-login-input-field {
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 2px;
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
min-width: 150px;
|
||||
height: 60px;
|
||||
padding: 0 !important;
|
||||
background-color: $white !important;
|
||||
margin-top: 10px !important;
|
||||
display: flex !important;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
.ucap-authentication-login-input-icon {
|
||||
align-self: center;
|
||||
flex-basis: 45px;
|
||||
margin-right: 16px !important;
|
||||
margin-left: 16px;
|
||||
height: 24px !important;
|
||||
width: 24px !important;
|
||||
@include screen(mid) {
|
||||
height: 20px !important;
|
||||
width: 20px !important;
|
||||
margin-right: 10px !important;
|
||||
}
|
||||
@include screen(xs) {
|
||||
height: 20px !important;
|
||||
width: 20px !important;
|
||||
margin-right: 10px !important;
|
||||
}
|
||||
}
|
||||
.mat-form-field {
|
||||
.mat-form-field-wrapper {
|
||||
.mat-form-field-underline {
|
||||
height: 0 !important;
|
||||
}
|
||||
}
|
||||
&.mat-form-field-appearance-legacy {
|
||||
.mat-form-field-ripple {
|
||||
height: 0 !important;
|
||||
}
|
||||
&.mat-form-field-can-float {
|
||||
.mat-form-field-autofill-control {
|
||||
&:-webkit-autofill + .mat-form-field-label-wrapper {
|
||||
.mat-form-field-label {
|
||||
width: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&:nth-child(1) {
|
||||
margin-top: 30px !important;
|
||||
width: 420px;
|
||||
.mat-form-field {
|
||||
@include ucapMatSelect(60px, 0 16px);
|
||||
}
|
||||
@include screen(mid) {
|
||||
margin-top: 23px !important;
|
||||
width: 350px;
|
||||
.mat-form-field {
|
||||
@include ucapMatSelect(50px, 0 16px);
|
||||
}
|
||||
}
|
||||
@include screen(xs) {
|
||||
margin-top: 23px !important;
|
||||
width: 300px;
|
||||
.mat-form-field {
|
||||
@include ucapMatSelect(42px, 0 16px);
|
||||
}
|
||||
}
|
||||
}
|
||||
&:nth-child(2) {
|
||||
.mat-form-field {
|
||||
@include ucapMatFormField(0, 0, auto, 360px, 360px, 60px, 60px, white);
|
||||
}
|
||||
@include screen(mid) {
|
||||
.mat-form-field {
|
||||
@include ucapMatFormField(
|
||||
0,
|
||||
0,
|
||||
auto,
|
||||
300px,
|
||||
300px,
|
||||
50px,
|
||||
50px,
|
||||
white
|
||||
);
|
||||
}
|
||||
}
|
||||
@include screen(xs) {
|
||||
.mat-form-field {
|
||||
@include ucapMatFormField(
|
||||
0,
|
||||
0,
|
||||
auto,
|
||||
250px,
|
||||
250px,
|
||||
42px,
|
||||
42px,
|
||||
white
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
&:nth-child(3) {
|
||||
.mat-form-field {
|
||||
@include ucapMatFormField(0, 0, 360px, 360px, 360px, 60px, 60px, white);
|
||||
}
|
||||
@include screen(mid) {
|
||||
.mat-form-field {
|
||||
@include ucapMatFormField(
|
||||
0,
|
||||
0,
|
||||
auto,
|
||||
300px,
|
||||
300px,
|
||||
50px,
|
||||
50px,
|
||||
white
|
||||
);
|
||||
}
|
||||
}
|
||||
@include screen(xs) {
|
||||
.mat-form-field {
|
||||
@include ucapMatFormField(
|
||||
0,
|
||||
0,
|
||||
auto,
|
||||
250px,
|
||||
250px,
|
||||
42px,
|
||||
42px,
|
||||
white
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@include screen(mid) {
|
||||
width: 350px;
|
||||
height: 50px;
|
||||
}
|
||||
@include screen(xs) {
|
||||
width: 300px;
|
||||
height: 42px;
|
||||
}
|
||||
}
|
||||
.ucap-authentication-login-error-container {
|
||||
position: absolute;
|
||||
width: 300px;
|
||||
height: 80px;
|
||||
//background-color: gold;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
bottom: 135px;
|
||||
left: calc(50% - 150px);
|
||||
font-size: 0.929em !important;
|
||||
font-weight: 600;
|
||||
color: $lipstick !important;
|
||||
.mat-error {
|
||||
font-size: 0.929em !important;
|
||||
line-height: 1.7 !important;
|
||||
color: $lipstick !important;
|
||||
@include screen(xs) {
|
||||
font-size: 0.857em !important;
|
||||
line-height: 1.5 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ucap-authentication-login-button-submit {
|
||||
//transform: all 0.3s;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
background-color: $black;
|
||||
border-radius: 2px !important;
|
||||
color: $white;
|
||||
font-size: 1.429em !important;
|
||||
@include font-family($font-semibold);
|
||||
border: 0;
|
||||
margin: 12px 0 0 !important;
|
||||
font-weight: 600 !important;
|
||||
cursor: pointer;
|
||||
@include screen(mid) {
|
||||
margin-top: 8px !important;
|
||||
font-size: 1.143em !important;
|
||||
height: 50px;
|
||||
}
|
||||
@include screen(xs) {
|
||||
font-size: 1em !important;
|
||||
height: 42px;
|
||||
}
|
||||
}
|
||||
}
|
0
src/assets/scss/global/ucap/_call.scss
Normal file
0
src/assets/scss/global/ucap/_call.scss
Normal file
0
src/assets/scss/global/ucap/_chat.scss
Normal file
0
src/assets/scss/global/ucap/_chat.scss
Normal file
0
src/assets/scss/global/ucap/_group.scss
Normal file
0
src/assets/scss/global/ucap/_group.scss
Normal file
0
src/assets/scss/global/ucap/_message.scss
Normal file
0
src/assets/scss/global/ucap/_message.scss
Normal file
7
src/assets/scss/global/ucap/_organization.scss
Normal file
7
src/assets/scss/global/ucap/_organization.scss
Normal file
|
@ -0,0 +1,7 @@
|
|||
.ucap-organization-profile-image-01-image-container {
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
margin-left: 2px;
|
||||
}
|
|
@ -1,15 +1,8 @@
|
|||
:root {
|
||||
--ucap-screen-max-xs: 575;
|
||||
--ucap-screen-min-sm: 576;
|
||||
--ucap-screen-max-sm: 767;
|
||||
--ucap-screen-min-md: 768;
|
||||
--ucap-screen-max-md: 991;
|
||||
--ucap-screen-min-lg: 992;
|
||||
--ucap-screen-max-lg: 1199;
|
||||
--ucap-screen-min-xl: 1200;
|
||||
|
||||
--ucap-organization-search-for-tenant-border-width: 1px;
|
||||
--ucap-organization-search-for-tenant-border-color: #e42f66;
|
||||
|
||||
--ucap-organization-profile-list-item-01-size: 70px;
|
||||
}
|
||||
|
||||
// Fonts Family
|
||||
|
@ -96,26 +89,22 @@ $bg-linear-gradient: linear-gradient(57deg, #ffe1ba 13%, #f5878c 87%);
|
|||
height: 100vh;
|
||||
}
|
||||
|
||||
$lg-red: (
|
||||
50: #ffffff,
|
||||
100: #fff9fc,
|
||||
200: #f1e1e5,
|
||||
300: #ef4c73,
|
||||
400: #ffbf2a,
|
||||
//#ec407a
|
||||
500: #ed097e,
|
||||
600: #d81b60,
|
||||
700: #c2185b,
|
||||
800: #ad1457,
|
||||
900: #5f2a41,
|
||||
A100: #ff80ab,
|
||||
A200: #ff4081,
|
||||
A400: #ff3399,
|
||||
A700: #c51162,
|
||||
B100: #4f4f4f,
|
||||
B200: #67545b,
|
||||
G100: #ef4c73,
|
||||
G900: #352a37,
|
||||
// Theming color set/////////////////////////////
|
||||
$ucap-color-primary: (
|
||||
50: #f0f0f0,
|
||||
100: #e2e2e2,
|
||||
200: #d4d4d4,
|
||||
300: #cccccc,
|
||||
400: #aaa4a6,
|
||||
500: #81757a,
|
||||
600: #584f52,
|
||||
700: #474244,
|
||||
800: #3b3637,
|
||||
900: #2d2a2c,
|
||||
A100: #f7f8fa,
|
||||
A200: #f1f2f6,
|
||||
A400: #999999,
|
||||
A700: #333333,
|
||||
contrast: (
|
||||
50: $dark-primary-text,
|
||||
100: $dark-primary-text,
|
||||
|
@ -128,11 +117,84 @@ $lg-red: (
|
|||
800: $light-primary-text,
|
||||
900: $light-primary-text,
|
||||
A100: $dark-primary-text,
|
||||
A200: $light-primary-text,
|
||||
A200: $dark-primary-text,
|
||||
A400: $light-primary-text,
|
||||
A700: $light-primary-text,
|
||||
B100: $light-primary-text,
|
||||
G100: $dark-primary-text,
|
||||
G900: $light-primary-text
|
||||
A700: $light-primary-text
|
||||
)
|
||||
);
|
||||
|
||||
$ucap-color-accent: (
|
||||
50: #fef1f4,
|
||||
100: #fce7ed,
|
||||
200: #ffd9e5,
|
||||
300: #ffb5cc,
|
||||
400: #fd78a1,
|
||||
500: #fd578a,
|
||||
600: #f83772,
|
||||
700: #e42f66,
|
||||
800: #dc225b,
|
||||
900: #c21e50,
|
||||
A100: #fff2f3,
|
||||
A200: #ffc5ca,
|
||||
A400: #f67f8a,
|
||||
A700: #ec5361,
|
||||
contrast: (
|
||||
50: $dark-primary-text,
|
||||
100: $dark-primary-text,
|
||||
200: $dark-primary-text,
|
||||
300: $light-primary-text,
|
||||
400: $light-primary-text,
|
||||
500: $light-primary-text,
|
||||
600: $light-primary-text,
|
||||
700: $light-primary-text,
|
||||
800: $light-primary-text,
|
||||
900: $light-primary-text,
|
||||
A100: $dark-primary-text,
|
||||
A200: $dark-primary-text,
|
||||
A400: $light-primary-text,
|
||||
A700: $light-primary-text
|
||||
)
|
||||
);
|
||||
|
||||
$ucap-color-warn: (
|
||||
50: #fefaf6,
|
||||
100: #fef6ec,
|
||||
200: #ffe8cb,
|
||||
300: #ffda9a,
|
||||
400: #fbca69,
|
||||
500: #fcb954,
|
||||
600: #faa640,
|
||||
700: #f69532,
|
||||
800: #e68118,
|
||||
900: #d8750f,
|
||||
A100: #fff3ef,
|
||||
A200: #ffeadf,
|
||||
A400: #ffbf9e,
|
||||
A700: #ff6c20,
|
||||
contrast: (
|
||||
50: $dark-primary-text,
|
||||
100: $dark-primary-text,
|
||||
200: $dark-primary-text,
|
||||
300: $dark-primary-text,
|
||||
400: $light-primary-text,
|
||||
500: $light-primary-text,
|
||||
600: $light-primary-text,
|
||||
700: $light-primary-text,
|
||||
800: $light-primary-text,
|
||||
900: $light-primary-text,
|
||||
A100: $dark-primary-text,
|
||||
A200: $dark-primary-text,
|
||||
A400: $light-primary-text,
|
||||
A700: $light-primary-text
|
||||
)
|
||||
);
|
||||
/////////////////////////////Theming color set //
|
||||
|
||||
$ucap-screen-max-xs: 575;
|
||||
$ucap-screen-min-sm: 576;
|
||||
$ucap-screen-max-sm: 767;
|
||||
$ucap-screen-min-md: 768;
|
||||
$ucap-screen-max-md: 991;
|
||||
$ucap-screen-min-lg: 992;
|
||||
$ucap-screen-max-lg: 1199;
|
||||
$ucap-screen-min-xl: 1200;
|
||||
|
|
|
@ -103,7 +103,7 @@ export const environment: Environment = {
|
|||
commonApiModuleConfig: {
|
||||
hostConfig: {
|
||||
protocol: 'http',
|
||||
domain: 'lftalk2.lfcorp.com',
|
||||
domain: '13.124.88.127',
|
||||
port: 8033
|
||||
},
|
||||
urls: commonApiUrls,
|
||||
|
@ -115,8 +115,8 @@ export const environment: Environment = {
|
|||
publicApiModuleConfig: {
|
||||
hostConfig: {
|
||||
protocol: 'http',
|
||||
domain: 'lftalk2.lfcorp.com',
|
||||
port: 8033
|
||||
domain: '13.124.88.127',
|
||||
port: 8011
|
||||
},
|
||||
urls: publicApiUrls
|
||||
},
|
||||
|
@ -124,7 +124,7 @@ export const environment: Environment = {
|
|||
externalApiModuleConfig: {
|
||||
hostConfig: {
|
||||
protocol: 'http',
|
||||
domain: 'lftalk2.lfcorp.com',
|
||||
domain: '13.124.88.127',
|
||||
port: 8011
|
||||
},
|
||||
urls: externalApiUrls
|
||||
|
@ -133,7 +133,7 @@ export const environment: Environment = {
|
|||
messageApiModuleConfig: {
|
||||
hostConfig: {
|
||||
protocol: 'http',
|
||||
domain: 'lftalk2.lfcorp.com',
|
||||
domain: '13.124.88.127',
|
||||
port: 9097
|
||||
},
|
||||
urls: messageApiUrls
|
||||
|
@ -142,7 +142,7 @@ export const environment: Environment = {
|
|||
promptApiModuleConfig: {
|
||||
hostConfig: {
|
||||
protocol: 'http',
|
||||
domain: 'lftalk2.lfcorp.com',
|
||||
domain: '13.124.88.127',
|
||||
port: 9097
|
||||
},
|
||||
urls: promptUrls
|
||||
|
@ -151,7 +151,7 @@ export const environment: Environment = {
|
|||
piModuleConfig: {
|
||||
hostConfig: {
|
||||
protocol: 'http',
|
||||
domain: 'lftalk2.lfcorp.com',
|
||||
domain: '13.124.88.127',
|
||||
port: 9097
|
||||
},
|
||||
urls: piUrls
|
||||
|
@ -166,7 +166,7 @@ export const environment: Environment = {
|
|||
protocolModuleConfig: {
|
||||
hostConfig: {
|
||||
protocol: 'ws',
|
||||
domain: 'lftalk2.lfcorp.com',
|
||||
domain: '13.124.88.127',
|
||||
port: 8080
|
||||
},
|
||||
urls: protocolUrls,
|
||||
|
@ -181,6 +181,6 @@ export const environment: Environment = {
|
|||
},
|
||||
pingProtocolModuleConfig: {
|
||||
statusCode: 'O',
|
||||
interval: 5
|
||||
interval: 20
|
||||
}
|
||||
};
|
||||
|
|
|
@ -107,7 +107,7 @@ export const environment: Environment = {
|
|||
hostConfig: {
|
||||
protocol: 'http',
|
||||
domain: '13.124.88.127',
|
||||
port: 8033
|
||||
port: 8011
|
||||
},
|
||||
urls: commonApiUrls,
|
||||
acceptableFileExtensions: commonApiacceptableFileExtensions,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user