조직도 detail > 그룹에 추가, 대화, 화상회의 연결.

This commit is contained in:
leejinho 2020-03-05 14:53:42 +09:00
parent e7356b3ad3
commit bf351bf690
8 changed files with 304 additions and 72 deletions

View File

@ -15,6 +15,7 @@
<app-layout-messenger-organization <app-layout-messenger-organization
(openProfile)="onClickOpenProfile($event)" (openProfile)="onClickOpenProfile($event)"
(createConference)="onClickConferenceCreate($event)"
[style.display]=" [style.display]="
MainMenu.Organization === (this.gnbMenuIndex$ | async) ? 'block' : 'none' MainMenu.Organization === (this.gnbMenuIndex$ | async) ? 'block' : 'none'
" "

View File

@ -1,6 +1,5 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { OpenProfileOptions } from '@ucap-webmessenger/protocol-buddy';
import * as AppStore from '@app/store'; import * as AppStore from '@app/store';
import { select, Store } from '@ngrx/store'; import { select, Store } from '@ngrx/store';
@ -24,6 +23,9 @@ export class MainContentsComponent implements OnInit {
@Output() @Output()
closeRightDrawer = new EventEmitter(); closeRightDrawer = new EventEmitter();
@Output()
createConference = new EventEmitter<number[]>();
MainMenu = MainMenu; MainMenu = MainMenu;
gnbMenuIndex$: Observable<string>; gnbMenuIndex$: Observable<string>;
@ -42,4 +44,8 @@ export class MainContentsComponent implements OnInit {
onCloseRightDrawer() { onCloseRightDrawer() {
this.closeRightDrawer.emit(); this.closeRightDrawer.emit();
} }
onClickConferenceCreate(userSeqs: number[]) {
this.createConference.emit(userSeqs);
}
} }

View File

@ -24,7 +24,7 @@
<ng-container *ngIf="!!(isSearch | async)"> <ng-container *ngIf="!!(isSearch | async)">
{{ 'common.searchResult' | translate {{ 'common.searchResult' | translate
}}<span class="text-accent-color" }}<span class="text-accent-color"
>({{ selectedDepartmentUserInfoList.length }} >({{ departmentUserInfoList.length }}
{{ 'common.units.persons' | translate }})</span {{ 'common.units.persons' | translate }})</span
> >
</ng-container> </ng-container>
@ -49,10 +49,14 @@
<div fxFlex="auto" class="organization-content"> <div fxFlex="auto" class="organization-content">
<div fxFlex="0 0 auto" class="table-box"> <div fxFlex="0 0 auto" class="table-box">
<ucap-organization-detail-table <ucap-organization-detail-table
[loginRes]="loginRes"
[presence]="presence$ | async" [presence]="presence$ | async"
[selectedDepartmentUserInfoList]="selectedDepartmentUserInfoList" [departmentUserInfoList]="departmentUserInfoList"
[profileImageRoot]="profileImageRoot" [profileImageRoot]="profileImageRoot"
[selectedUserList]="selectedUserList"
(openProfile)="onClickOpenProfile($event)" (openProfile)="onClickOpenProfile($event)"
(toggleAllUser)="onToggleAllUser($event)"
(toggleUser)="onToggleUser($event)"
class="detail-table" class="detail-table"
></ucap-organization-detail-table> ></ucap-organization-detail-table>
</div> </div>
@ -63,22 +67,60 @@
<mat-expansion-panel> <mat-expansion-panel>
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title> <mat-panel-title>
Personal data {{ 'organization.selectedUser' | translate }}
<span *ngIf="selectedUserList.length > 0">
({{ selectedUserList.length }})
{{ 'common.units.persons' | translate }}
</span>
</mat-panel-title> </mat-panel-title>
<mat-panel-description> <mat-panel-description> </mat-panel-description>
Type your name and age
</mat-panel-description>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<mat-form-field> <div class="list-chip">
<mat-label>First name</mat-label> <mat-chip-list aria-label="User selection">
<input matInput /> <mat-chip
</mat-form-field> *ngFor="let userInfo of selectedUserList"
(removed)="onClickDeleteUser(userInfo)"
<mat-form-field> >
<mat-label>Age</mat-label> {{ userInfo.name }}
<input matInput type="number" min="1" /> <mat-icon matChipRemove>clear</mat-icon>
</mat-form-field> </mat-chip>
</mat-chip-list>
</div>
<div class="btn-box">
<ul>
<li>
<button
mat-flat-button
[disabled]="selectedUserList.length > 0 ? 'false' : 'true'"
(click)="onClickAddGroup()"
class="mat-primary"
>
{{ 'organization.addToGroup' | translate }}
</button>
</li>
<li>
<button
mat-flat-button
[disabled]="selectedUserList.length > 0 ? 'false' : 'true'"
(click)="onClickChatOpen()"
class="mat-primary"
>
{{ 'organization.startChat' | translate }}
</button>
</li>
<li *ngIf="!!authInfo && authInfo.canVideoConference">
<button
mat-flat-button
[disabled]="selectedUserList.length > 0 ? 'false' : 'true'"
(click)="onClickConference()"
class="mat-primary"
>
{{ 'organization.startVideoConference' | translate }}
</button>
</li>
</ul>
</div>
</mat-expansion-panel> </mat-expansion-panel>
</mat-accordion> </mat-accordion>
</div> </div>

View File

@ -103,3 +103,36 @@
width: 100%; width: 100%;
} }
} }
.btn-box {
padding: 10px;
button {
width: 100%;
@include ellipsis(1);
span {
vertical-align: baseline;
}
}
ul {
display: flex;
flex-flow: row;
align-content: space-between;
margin-top: 4px;
li {
display: inline-flex;
align-items: center;
flex-grow: 1;
width: 33%;
margin-right: 4px;
&:last-child {
margin-right: 0;
}
button {
text-align: center;
width: 100%;
height: 100%;
padding: 0 6px;
}
}
}
}

View File

@ -6,7 +6,11 @@ import {
Output, Output,
EventEmitter EventEmitter
} from '@angular/core'; } from '@angular/core';
import { SelectedDept, UserInfoSS } from '@ucap-webmessenger/protocol-query'; import {
SelectedDept,
UserInfoSS,
AuthResponse
} from '@ucap-webmessenger/protocol-query';
import { NGXLogger } from 'ngx-logger'; import { NGXLogger } from 'ngx-logger';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { DialogService } from '@ucap-webmessenger/ui'; import { DialogService } from '@ucap-webmessenger/ui';
@ -25,8 +29,16 @@ import {
WorkStatusType WorkStatusType
} from '@ucap-webmessenger/protocol-status'; } from '@ucap-webmessenger/protocol-status';
import { VersionInfo2Response } from '@ucap-webmessenger/api-public'; import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
import { KEY_VER_INFO } from '@app/types'; import { KEY_VER_INFO, KEY_AUTH_INFO } from '@app/types';
import { Sort } from '@angular/material/sort'; import { Sort } from '@angular/material/sort';
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
import { tap } from 'rxjs/operators';
import {
SelectGroupDialogComponent,
SelectGroupDialogData,
SelectGroupDialogResult
} from '../../dialogs/group/select-group.dialog.component';
import { GroupDetailData } from '@ucap-webmessenger/protocol-sync';
@Component({ @Component({
selector: 'app-layout-messenger-organization', selector: 'app-layout-messenger-organization',
@ -36,8 +48,13 @@ import { Sort } from '@angular/material/sort';
export class OrganizationComponent implements OnInit, OnDestroy { export class OrganizationComponent implements OnInit, OnDestroy {
@Output() @Output()
openProfile = new EventEmitter<number>(); openProfile = new EventEmitter<number>();
@Output()
createConference = new EventEmitter<number[]>();
loginRes: LoginResponse;
loginResSubscription: Subscription;
sessionVerinfo: VersionInfo2Response; sessionVerinfo: VersionInfo2Response;
authInfo: AuthResponse;
isSearch: boolean; isSearch: boolean;
isSearchSubscription: Subscription; isSearchSubscription: Subscription;
@ -47,7 +64,9 @@ export class OrganizationComponent implements OnInit, OnDestroy {
departmentUserInfoListSubscription: Subscription; departmentUserInfoListSubscription: Subscription;
searchDepartmentUserInfoListSubscription: Subscription; searchDepartmentUserInfoListSubscription: Subscription;
selectedDepartmentUserInfoList: UserInfoSS[] = []; departmentUserInfoList: UserInfoSS[] = [];
selectedUserList: UserInfoSS[] = []; // selected user in departmentUserList detail
profileImageRoot: string; profileImageRoot: string;
presence$: Observable<StatusBulkInfo[]>; presence$: Observable<StatusBulkInfo[]>;
@ -64,9 +83,19 @@ export class OrganizationComponent implements OnInit, OnDestroy {
this.sessionVerinfo = this.sessionStorageService.get<VersionInfo2Response>( this.sessionVerinfo = this.sessionStorageService.get<VersionInfo2Response>(
KEY_VER_INFO KEY_VER_INFO
); );
this.authInfo = this.sessionStorageService.get<AuthResponse>(KEY_AUTH_INFO);
} }
ngOnInit() { ngOnInit() {
this.loginResSubscription = this.store
.pipe(
select(AppStore.AccountSelector.AuthenticationSelector.loginRes),
tap(loginRes => {
this.loginRes = loginRes;
})
)
.subscribe();
this.isSearchSubscription = this.store this.isSearchSubscription = this.store
.pipe(select(AppStore.MessengerSelector.QuerySelector.isSearch)) .pipe(select(AppStore.MessengerSelector.QuerySelector.isSearch))
.subscribe(isSearch => { .subscribe(isSearch => {
@ -89,7 +118,7 @@ export class OrganizationComponent implements OnInit, OnDestroy {
) )
.subscribe(list => { .subscribe(list => {
if (!this.isSearch) { if (!this.isSearch) {
this.selectedDepartmentUserInfoList = list; this.departmentUserInfoList = list;
} }
}); });
@ -101,7 +130,7 @@ export class OrganizationComponent implements OnInit, OnDestroy {
) )
.subscribe(list => { .subscribe(list => {
if (!!this.isSearch) { if (!!this.isSearch) {
this.selectedDepartmentUserInfoList = list; this.departmentUserInfoList = list;
} }
}); });
@ -113,6 +142,9 @@ export class OrganizationComponent implements OnInit, OnDestroy {
} }
ngOnDestroy(): void { ngOnDestroy(): void {
if (!!this.loginResSubscription) {
this.loginResSubscription.unsubscribe();
}
if (!!this.isSearchSubscription) { if (!!this.isSearchSubscription) {
this.isSearchSubscription.unsubscribe(); this.isSearchSubscription.unsubscribe();
} }
@ -124,6 +156,97 @@ export class OrganizationComponent implements OnInit, OnDestroy {
} }
} }
/** Selected User Handling */
onToggleAllUser(params: { isChecked: boolean; userInfos: UserInfoSS[] }) {
params.userInfos.forEach(userInfo => {
if (params.isChecked) {
if (
this.selectedUserList.filter(user => user.seq === userInfo.seq)
.length === 0
) {
this.selectedUserList = [...this.selectedUserList, userInfo];
}
} else {
this.selectedUserList = this.selectedUserList.filter(
user => user.seq !== userInfo.seq
);
}
});
}
onToggleUser(userInfo: UserInfoSS) {
if (userInfo.seq === this.loginRes.userSeq) {
return;
}
if (
this.selectedUserList.filter(user => user.seq === userInfo.seq).length ===
0
) {
this.selectedUserList = [...this.selectedUserList, userInfo];
} else {
this.selectedUserList = this.selectedUserList.filter(
item => item.seq !== userInfo.seq
);
}
this.changeDetectorRef.detectChanges();
}
/** Handling chipset for selectedUserList */
/** 선택된 사용자 취소 */
onClickDeleteUser(userInfo: UserInfoSS) {
this.selectedUserList = this.selectedUserList.filter(
item => item.seq !== userInfo.seq
);
this.changeDetectorRef.detectChanges();
}
/** Handling Button */
async onClickAddGroup() {
this.logger.debug('onClickAddGroup', this.selectedUserList);
const result = await this.dialogService.open<
SelectGroupDialogComponent,
SelectGroupDialogData,
SelectGroupDialogResult
>(SelectGroupDialogComponent, {
width: '600px',
data: {
title: this.translateService.instant('group.selectTargetGroup')
}
});
if (!!result && !!result.choice && result.choice) {
if (!!result.group) {
const oldGroup: GroupDetailData = result.group;
const trgtUserSeq: number[] = [];
result.group.userSeqs.map(seq => trgtUserSeq.push(seq));
this.selectedUserList
.filter(v => result.group.userSeqs.indexOf(v.seq) < 0)
.forEach(user => {
trgtUserSeq.push(user.seq);
});
this.store.dispatch(
SyncStore.updateGroupMember({
oldGroup,
trgtUserSeq
})
);
}
}
}
onClickChatOpen() {
if (!!this.selectedUserList && this.selectedUserList.length > 0) {
const seq: number[] = [];
this.selectedUserList.map(user => seq.push(user.seq));
this.store.dispatch(ChatStore.openRoom({ userSeqList: seq }));
}
}
onClickConference() {
const targetUserSeqs = this.selectedUserList.map(userInfo => userInfo.seq);
this.createConference.emit(targetUserSeqs);
}
onClickOpenProfile(userSeq: number) { onClickOpenProfile(userSeq: number) {
this.openProfile.emit(userSeq); this.openProfile.emit(userSeq);
} }

View File

@ -29,6 +29,7 @@
[selectedChat]="this.selectedChat$ | async" [selectedChat]="this.selectedChat$ | async"
(openProfile)="onClickOpenProfile($event)" (openProfile)="onClickOpenProfile($event)"
(closeRightDrawer)="onCloseRightDrawer()" (closeRightDrawer)="onCloseRightDrawer()"
(createConference)="conferenceCreate($event)"
> >
</app-layout-messenger-main-contents> </app-layout-messenger-main-contents>
<button <button

View File

@ -138,8 +138,9 @@
<td mat-cell *matCellDef="let element"> <td mat-cell *matCellDef="let element">
<mat-checkbox <mat-checkbox
#checkbox #checkbox
[checked]="getCheckedAllUser()" *ngIf="loginRes.userSeq !== element.seq"
(change)="onCheckAllUser(checkbox.checked)" [checked]="getCheckedUser(element)"
(change)="onToggleUser(checkbox.checked, element)"
(click)="$event.stopPropagation()" (click)="$event.stopPropagation()"
> >
</mat-checkbox> </mat-checkbox>

View File

@ -1,4 +1,11 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; import {
Component,
OnInit,
Input,
Output,
EventEmitter,
ChangeDetectorRef
} from '@angular/core';
import { NGXLogger } from 'ngx-logger'; import { NGXLogger } from 'ngx-logger';
import { UserInfoSS } from '@ucap-webmessenger/protocol-query'; import { UserInfoSS } from '@ucap-webmessenger/protocol-query';
import { PresenceType, StatusCode } from '@ucap-webmessenger/core'; import { PresenceType, StatusCode } from '@ucap-webmessenger/core';
@ -7,6 +14,7 @@ import {
StatusBulkInfo StatusBulkInfo
} from '@ucap-webmessenger/protocol-status'; } from '@ucap-webmessenger/protocol-status';
import { Sort } from '@angular/material/sort'; import { Sort } from '@angular/material/sort';
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
@Component({ @Component({
selector: 'ucap-organization-detail-table', selector: 'ucap-organization-detail-table',
@ -14,12 +22,13 @@ import { Sort } from '@angular/material/sort';
styleUrls: ['./detail-table.component.scss'] styleUrls: ['./detail-table.component.scss']
}) })
export class DetailTableComponent implements OnInit { export class DetailTableComponent implements OnInit {
@Input('selectedDepartmentUserInfoList') @Input('departmentUserInfoList')
set userInfoListIn(userInfo: UserInfoSS[]) { set userInfoListIn(userInfo: UserInfoSS[]) {
this.selectedDepartmentUserInfoList = userInfo; this.departmentUserInfoList = userInfo;
this.sortedData = userInfo; this.sortedData = userInfo;
} }
@Input()
loginRes: LoginResponse;
@Input() @Input()
presence: StatusBulkInfo[]; presence: StatusBulkInfo[];
@Input() @Input()
@ -30,19 +39,14 @@ export class DetailTableComponent implements OnInit {
@Output() @Output()
openProfile = new EventEmitter<number>(); openProfile = new EventEmitter<number>();
@Output() @Output()
checkUser = new EventEmitter<{ toggleAllUser = new EventEmitter<{
isChecked: boolean;
userInfo: UserInfoSS;
}>();
@Output()
toggleUser = new EventEmitter<UserInfoSS>();
@Output()
checkAllUser = new EventEmitter<{
isChecked: boolean; isChecked: boolean;
userInfos: UserInfoSS[]; userInfos: UserInfoSS[];
}>(); }>();
@Output()
toggleUser = new EventEmitter<UserInfoSS>();
selectedDepartmentUserInfoList: UserInfoSS[]; departmentUserInfoList: UserInfoSS[];
sortedData: UserInfoSS[] = []; sortedData: UserInfoSS[] = [];
PresenceType = PresenceType; PresenceType = PresenceType;
@ -55,7 +59,10 @@ export class DetailTableComponent implements OnInit {
'checkable' 'checkable'
]; ];
constructor(private logger: NGXLogger) {} constructor(
private changeDetectorRef: ChangeDetectorRef,
private logger: NGXLogger
) {}
ngOnInit() {} ngOnInit() {}
@ -158,7 +165,7 @@ export class DetailTableComponent implements OnInit {
} }
sortData(sort: Sort) { sortData(sort: Sort) {
const data = this.selectedDepartmentUserInfoList.slice(); const data = this.departmentUserInfoList.slice();
if (!sort.active || sort.direction === '') { if (!sort.active || sort.direction === '') {
this.sortedData = data; this.sortedData = data;
return; return;
@ -196,43 +203,61 @@ export class DetailTableComponent implements OnInit {
/** 전체 체크여부 */ /** 전체 체크여부 */
getCheckedAllUser() { getCheckedAllUser() {
// if (!this.loginRes) { if (!this.loginRes) {
// return false; return false;
// } }
// const compareList: UserInfoSS[] = this.isShowSearch
// ? this.searchUserInfos const compareList: UserInfoSS[] = this.departmentUserInfoList;
// : this.selectedDepartmentUserInfoList; if (
// if ( !compareList ||
// !compareList || compareList.length === 0 ||
// compareList.length === 0 || compareList
// compareList .filter(item => item.seq !== this.loginRes.userSeq)
// .filter(item => item.seq !== this.loginRes.userSeq) .filter(
// .filter( item =>
// item => !(
// !( this.selectedUserList.filter(user => user.seq === item.seq)
// this.selectedUserList.filter(user => user.seq === item.seq) .length > 0
// .length > 0 )
// ) ).length > 0
// ).length > 0 ) {
// ) { return false;
// return false; } else {
// } else { return true;
// return true; }
// }
} }
/** 전체선택 이벤트 */ /** 전체선택 이벤트 */
onCheckAllUser(value: boolean) { onCheckAllUser(value: boolean) {
// if (!this.loginRes) { if (!this.loginRes) {
// return false; return false;
// } }
// this.checkAllUser.emit({
// isChecked: value, this.toggleAllUser.emit({
// userInfos: (this.isShowSearch isChecked: value,
// ? this.searchUserInfos userInfos: this.departmentUserInfoList.filter(
// : this.selectedDepartmentUserInfoList user => user.seq !== this.loginRes.userSeq
// ).filter(user => user.seq !== this.loginRes.userSeq) )
// }); });
// this.changeDetectorRef.detectChanges(); this.changeDetectorRef.detectChanges();
}
/** 개별 체크여부 */
getCheckedUser(userInfo: UserInfoSS) {
if (!!this.selectedUserList && this.selectedUserList.length > 0) {
return (
this.selectedUserList.filter(item => item.seq === userInfo.seq).length >
0
);
}
return false;
}
/** 개별선택(토글) 이벤트 */
onToggleUser(isChecked: boolean, userInfo: UserInfoSS) {
console.log(isChecked, userInfo);
if (!this.loginRes || userInfo.seq === this.loginRes.userSeq) {
return;
}
this.toggleUser.emit(userInfo);
} }
onClickOpenProfile(event: MouseEvent, userSeq: number) { onClickOpenProfile(event: MouseEvent, userSeq: number) {