This commit is contained in:
병준 박 2019-11-15 10:13:38 +09:00
commit 5f5269c15b
37 changed files with 1748 additions and 294 deletions

View File

@ -0,0 +1,31 @@
import {
APIRequest,
APIResponse,
APIDecoder,
APIJsonEncoder,
MessageAPIResponse
} from '@ucap-webmessenger/api';
import { DeviceType } from '@ucap-webmessenger/core';
import { MessageType } from '../types/message.type';
export interface DelRequest extends APIRequest {
userSeq: number;
deviceType: DeviceType;
tokenKey: string;
type: MessageType;
msgList: { msgId: number }[];
}
export interface DelResponse extends MessageAPIResponse {}
export const encodeDel: APIJsonEncoder<DelRequest> = (req: DelRequest) => {
return JSON.stringify(req);
};
export const decodeDel: APIDecoder<DelResponse> = (res: any) => {
return {
responseCode: res.responseCode,
responseMsg: res.responseMsg
} as DelResponse;
};

View File

@ -0,0 +1,182 @@
import {
APIRequest,
MessageAPIResponse,
APIDecoder,
APIJsonEncoder
} from '@ucap-webmessenger/api';
import { DeviceType } from '@ucap-webmessenger/core';
import { MessageType } from '../types/message.type';
import { CategoryType } from '../types/category.type';
export interface DetailRequest extends APIRequest {
userSeq: number;
deviceType: DeviceType;
tokenKey: string;
type: MessageType;
msgId: number;
}
export interface DetailResponse extends MessageAPIResponse {
msgInfo: {
msgId: number;
category: CategoryType;
title: string;
titleYn: boolean;
};
content: {
type: MessageType;
sendUserSeq: number;
sendUserName: string;
reservationTime: string | null;
sendYn: boolean;
regDate: string;
attachmentYn: boolean;
smsYn: boolean;
fileAllow: string;
}[];
recvList: {
userSeq: number;
userName: string;
cancelYn: boolean;
readDate: string;
readYn: boolean;
}[];
}
export const encodeDetail: APIJsonEncoder<DetailRequest> = (
req: DetailRequest
) => {
return JSON.stringify(req);
};
export const decodeDetail: APIDecoder<DetailResponse> = (res: any) => {
return {
responseCode: res.responseCode,
responseMsg: res.responseMsg,
msgInfo: null,
content: [],
recvList: []
} as DetailResponse;
};
export interface ReadRequest extends APIRequest {
userSeq: number;
deviceType: DeviceType;
tokenKey: string;
type: MessageType;
msgId: number;
}
export interface ReadResponse extends MessageAPIResponse {}
export const encodeRead: APIJsonEncoder<ReadRequest> = (req: ReadRequest) => {
return JSON.stringify(req);
};
export const decodeRead: APIDecoder<ReadResponse> = (res: any) => {
return {
responseCode: res.responseCode,
responseMsg: res.responseMsg
} as ReadResponse;
};
export interface ReadCheckResponse extends MessageAPIResponse {
recvList: {
userSeq: number;
userName: string;
cancelYn: boolean;
readDate: string;
readYn: boolean;
}[];
}
export const decodeReadCheck: APIDecoder<ReadCheckResponse> = (res: any) => {
return {
responseCode: res.responseCode,
responseMsg: res.responseMsg,
recvList: []
} as ReadCheckResponse;
};
export interface CancelRequest extends APIRequest {
userSeq: number;
deviceType: DeviceType;
tokenKey: string;
type: MessageType;
msgId: number;
recvUserList: { userSeq: number }[];
}
export interface CancelResponse extends MessageAPIResponse {}
export const encodeCancel: APIJsonEncoder<CancelRequest> = (
req: CancelRequest
) => {
return JSON.stringify(req);
};
export const decodeCancel: APIDecoder<CancelResponse> = (res: any) => {
return {
responseCode: res.responseCode,
responseMsg: res.responseMsg
} as CancelResponse;
};
export interface CancelReservationRequest extends APIRequest {
userSeq: number;
deviceType: DeviceType;
tokenKey: string;
type: MessageType;
msgId: number;
}
export interface CancelReservationResponse extends MessageAPIResponse {}
export const encodeCancelReservation: APIJsonEncoder<CancelReservationRequest> = (
req: CancelReservationRequest
) => {
return JSON.stringify(req);
};
export const decodeCancelReservation: APIDecoder<CancelReservationResponse> = (
res: any
) => {
return {
responseCode: res.responseCode,
responseMsg: res.responseMsg
} as CancelReservationResponse;
};
export interface RetrieveResourceFileRequest extends APIRequest {
userSeq: number;
deviceType: DeviceType;
tokenKey: string;
type: MessageType;
msgId: number;
resUrl: string;
}
export interface RetrieveResourceFileResponse extends MessageAPIResponse {}
export const encodeRetrieveResourceFile: APIJsonEncoder<RetrieveResourceFileRequest> = (
req: RetrieveResourceFileRequest
) => {
return JSON.stringify(req);
};
export const decodeRetrieveResourceFile: APIDecoder<RetrieveResourceFileResponse> = (
res: any
) => {
return {} as RetrieveResourceFileResponse;
};

View File

@ -0,0 +1,42 @@
import {
APIRequest,
MessageAPIResponse,
APIJsonEncoder,
APIDecoder
} from '@ucap-webmessenger/api';
import { DeviceType } from '@ucap-webmessenger/core';
import { CategoryType } from '../types/category.type';
import { ContentType } from '../types/content.type';
export interface EditReservationRequest extends APIRequest {
userSeq: number;
deviceType: DeviceType;
tokenKey: string;
msgId: number;
category: CategoryType;
title: string;
titleYn: boolean;
listOrder: ContentType[];
reservationTime: string;
smsYn: boolean;
textContent: { text: string }[];
recvUserList: { userSeq: number; userName: string }[];
}
export interface EditReservationResponse extends MessageAPIResponse {}
export const encodeEditReservation: APIJsonEncoder<EditReservationRequest> = (
req: EditReservationRequest
) => {
return JSON.stringify(req);
};
export const decodeEditReservation: APIDecoder<EditReservationResponse> = (
res: any
) => {
return {
responseCode: res.responseCode,
responseMsg: res.responseMsg
} as EditReservationResponse;
};

View File

@ -0,0 +1,104 @@
import {
APIRequest,
MessageAPIResponse,
APIJsonEncoder,
APIDecoder
} from '@ucap-webmessenger/api';
import { DeviceType } from '@ucap-webmessenger/core';
export interface SaveMyRequest extends APIRequest {
userSeq: number;
deviceType: DeviceType;
tokenKey: string;
userName: string;
message: string;
}
export interface SaveMyResponse extends MessageAPIResponse {}
export const encodeSaveMy: APIJsonEncoder<SaveMyRequest> = (
req: SaveMyRequest
) => {
return JSON.stringify(req);
};
export const decodeSaveMy: APIDecoder<SaveMyResponse> = (res: any) => {
return {
responseCode: res.responseCode,
responseMsg: res.responseMsg
} as SaveMyResponse;
};
export interface RetrieveMyRequest extends APIRequest {
userSeq: number;
deviceType: DeviceType;
tokenKey: string;
pageSize: number;
pageCount: number;
}
export interface RetrieveMyResponse extends MessageAPIResponse {
msgList: {
msgId: number;
message: string;
}[];
}
export const encodeRetrieveMy: APIJsonEncoder<RetrieveMyRequest> = (
req: RetrieveMyRequest
) => {
return JSON.stringify(req);
};
export const decodeRetrieveMy: APIDecoder<RetrieveMyResponse> = (res: any) => {
return {
responseCode: res.responseCode,
responseMsg: res.responseMsg,
msgList: []
} as RetrieveMyResponse;
};
export interface DelMyRequest extends APIRequest {
userSeq: number;
deviceType: DeviceType;
tokenKey: string;
}
export interface DelMyResponse extends MessageAPIResponse {}
export const encodeDelMy: APIJsonEncoder<DelMyRequest> = (
req: DelMyRequest
) => {
return JSON.stringify(req);
};
export const decodeDelMy: APIDecoder<DelMyResponse> = (res: any) => {
return {
responseCode: res.responseCode,
responseMsg: res.responseMsg
} as DelMyResponse;
};
export interface EditMyRequest extends APIRequest {
userSeq: number;
deviceType: DeviceType;
tokenKey: string;
msgId: number;
message: string;
}
export interface EditMyResponse extends MessageAPIResponse {}
export const encodeEditMy: APIJsonEncoder<EditMyRequest> = (
req: EditMyRequest
) => {
return JSON.stringify(req);
};
export const decodeEditMy: APIDecoder<EditMyResponse> = (res: any) => {
return {
responseCode: res.responseCode,
responseMsg: res.responseMsg
} as EditMyResponse;
};

View File

@ -1,49 +0,0 @@
import {
APIRequest,
APIResponse,
APIEncoder,
APIDecoder,
ParameterUtil
} from '@ucap-webmessenger/api';
import { DeviceType } from '@ucap-webmessenger/core';
import { MessageType } from '../types/message.type';
import { MessageList } from '../models/message-list';
export interface RetrieveRequest extends APIRequest {
userSeq: number;
deviceType: DeviceType;
tokenKey: string;
type: MessageType;
pageSize: number;
pageCount: number;
}
export interface RetrieveSendResponse extends APIResponse {
responseCode: string;
responseMsg: string;
totalCount: number;
messageList: MessageList[];
}
const RetrieveSendEncodeMap = {};
export const encodeRetrieve: APIEncoder<RetrieveRequest> = (
req: RetrieveRequest
) => {
return ParameterUtil.encode(RetrieveSendEncodeMap, req);
};
export const decodeRetrieveSend: APIDecoder<RetrieveSendResponse> = (
res: any
) => {
return {
statusCode: res.StatusCode,
errorMessage: res.ErrorMessage,
responseCode: res.responseCode,
responseMsg: res.responseMsg,
totalCount: res.totalCount,
messageList: []
} as RetrieveSendResponse;
};

View File

@ -0,0 +1,157 @@
import {
APIRequest,
MessageAPIResponse,
APIDecoder,
APIJsonEncoder
} from '@ucap-webmessenger/api';
import { DeviceType } from '@ucap-webmessenger/core';
import { MessageType } from '../types/message.type';
import { MessageList } from '../models/message-list';
import { CategoryType } from '../types/category.type';
import { ContentType } from '../types/content.type';
export interface RetrieveRequest extends APIRequest {
userSeq: number;
deviceType: DeviceType;
tokenKey: string;
type: MessageType;
pageSize: number;
pageCount: number;
}
export interface RetrieveSearchRequest extends RetrieveRequest {
searchTitle: string;
searchName: string;
searchContent: string;
}
export interface RetrieveResponse extends MessageAPIResponse {
pageCount: number;
pageSize: number;
totalCount: number;
messageList: MessageList[];
}
export const encodeRetrieve: APIJsonEncoder<RetrieveRequest> = (
req: RetrieveRequest
) => {
return JSON.stringify(req);
};
export const encodeRetrieveSearch: APIJsonEncoder<RetrieveResponse> = (
req: RetrieveResponse
) => {
return JSON.stringify(req);
};
export const decodeRetrieveSend: APIDecoder<RetrieveResponse> = (res: any) => {
const messageList: MessageList[] = [];
if (!!res.msgList && res.msgList.length > 0) {
for (const msgList of res.msgList) {
messageList.push({
...msgList,
userCount:
!msgList.userCount || msgList.userCount === null
? 0
: msgList.userCount,
userReadCount:
!msgList.userReadCount || msgList.userReadCount === null
? 0
: msgList.userReadCount,
titleYn: msgList.titleYn === 'Y' ? true : false,
attachmentYn: msgList.attachmentYn === 'Y' ? true : false
} as MessageList);
}
}
return {
responseCode: res.responseCode,
responseMsg: res.responseMsg,
pageCount: res.pageCount,
pageSize: res.pageSize,
totalCount: res.totalCount,
messageList
} as RetrieveResponse;
};
export const decodeRetrieveReceive: APIDecoder<RetrieveResponse> = (
res: any
) => {
const messageList: MessageList[] = [];
if (!!res.msgList && res.msgList.length > 0) {
for (const msgList of res.msgList) {
messageList.push({
...msgList,
userCount:
!msgList.userCount || msgList.userCount === null
? 0
: msgList.userCount,
userReadCount:
!msgList.userReadCount || msgList.userReadCount === null
? 0
: msgList.userReadCount,
titleYn: msgList.titleYn === 'Y' ? true : false,
attachmentYn: msgList.attachmentYn === 'Y' ? true : false,
readYn: msgList.readYn === 'Y' ? true : false
} as MessageList);
}
}
return {
responseCode: res.responseCode,
responseMsg: res.responseMsg,
pageCount: res.pageCount,
pageSize: res.pageSize,
totalCount: res.totalCount,
messageList
} as RetrieveResponse;
};
export const decodeRetrieveReservation: APIDecoder<RetrieveResponse> = (
res: any
) => {
const messageList: MessageList[] = [];
if (!!res.msgList && res.msgList.length > 0) {
for (const msgList of res.msgList) {
messageList.push({
...msgList,
userCount:
!msgList.userCount || msgList.userCount === null
? 0
: msgList.userCount,
userReadCount:
!msgList.userReadCount || msgList.userReadCount === null
? 0
: msgList.userReadCount,
titleYn: msgList.titleYn === 'Y' ? true : false,
attachmentYn: msgList.attachmentYn === 'Y' ? true : false
} as MessageList);
}
}
return {
responseCode: res.responseCode,
responseMsg: res.responseMsg,
pageCount: res.pageCount,
pageSize: res.pageSize,
totalCount: res.totalCount,
messageList
} as RetrieveResponse;
};
export const decodeRetrieveSearch: APIDecoder<RetrieveResponse> = (
res: any
) => {
return {
responseCode: res.responseCode,
responseMsg: res.responseMsg,
totalCount: res.totalCount,
messageList: []
} as RetrieveResponse;
};

View File

@ -0,0 +1,41 @@
import {
APIRequest,
MessageAPIResponse,
APIJsonEncoder,
APIDecoder
} from '@ucap-webmessenger/api';
import { DeviceType } from '@ucap-webmessenger/core';
import { CategoryType } from '../types/category.type';
import { ContentType } from '../types/content.type';
export interface SendCopyRequest extends APIRequest {
userSeq: number;
deviceType: DeviceType;
tokenKey: string;
userName: string;
msgId: number;
category: CategoryType;
title: string;
titleYn: boolean;
listOrder: ContentType[];
reservationTime: string;
smsYn: boolean;
textContent: { text: string }[];
recvUserList: { userSeq: number; userName: string }[];
}
export interface SendCopyResponse extends MessageAPIResponse {}
export const encodeSendCopy: APIJsonEncoder<SendCopyRequest> = (
req: SendCopyRequest
) => {
return JSON.stringify(req);
};
export const decodeSendCopy: APIDecoder<SendCopyResponse> = (res: any) => {
return {
responseCode: res.responseCode,
responseMsg: res.responseMsg
} as SendCopyResponse;
};

View File

@ -0,0 +1,41 @@
import {
APIRequest,
MessageAPIResponse,
APIJsonEncoder,
APIDecoder
} from '@ucap-webmessenger/api';
import { DeviceType } from '@ucap-webmessenger/core';
import { MessageType } from '../types/message.type';
import { CategoryType } from '../types/category.type';
import { ContentType } from '../types/content.type';
export interface SendRequest extends APIRequest {
userSeq: number;
deviceType: DeviceType;
tokenKey: string;
type: MessageType;
userName: string;
category: CategoryType;
title: string;
titleYn: boolean;
listOrder: ContentType[];
reservationTime: string;
smsYn: boolean;
textContent: { text: string }[];
recvUserList: { userSeq: number; userName: string }[];
}
export interface SendResponse extends MessageAPIResponse {}
export const encodeSend: APIJsonEncoder<SendRequest> = (req: SendRequest) => {
return JSON.stringify(req);
};
export const decodeSend: APIDecoder<SendResponse> = (res: any) => {
return {
responseCode: res.responseCode,
responseMsg: res.responseMsg
} as SendResponse;
};

View File

@ -0,0 +1,33 @@
import {
APIRequest,
MessageAPIResponse,
APIJsonEncoder,
APIDecoder
} from '@ucap-webmessenger/api';
import { DeviceType } from '@ucap-webmessenger/core';
export interface UnreadCountRequest extends APIRequest {
userSeq: number;
deviceType: DeviceType;
tokenKey: string;
}
export interface UnreadCountResponse extends MessageAPIResponse {
unreadCount: number;
}
export const encodeUnreadCount: APIJsonEncoder<UnreadCountRequest> = (
req: UnreadCountRequest
) => {
return JSON.stringify(req);
};
export const decodeUnreadCount: APIDecoder<UnreadCountResponse> = (
res: any
) => {
return {
responseCode: res.responseCode,
responseMsg: res.responseMsg,
unreadCount: res.unreadCount
} as UnreadCountResponse;
};

View File

@ -14,17 +14,21 @@ export interface MessageList {
/** 메시지 TYPE */
type: MessageType;
/** 수신자의 시퀀스 */
userSeq: number;
userSeq?: number;
/** 수신자 */
userName: string;
/** 수신자 수 */
userCount: number;
userCount?: number;
/** 읽은 사람 수 */
userReadCount: number;
userReadCount?: number;
/** 예약시간 :: DATE 형식은 "yyyy-MM-dd HH:mm:ss" (단 초는 00고정. 예약메일은 분단위까지만) */
reservationTime?: string;
/** 발신시간 */
regDate: Date;
regDate: string;
/** CONTENT 타입 */
resType: ContentType;
/** 첨부파일 존재여부 */
attachmentYn: boolean;
/** 읽음여부 */
readYn?: boolean;
}

View File

@ -1,5 +1,5 @@
import { Injectable, Inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@ -10,10 +10,81 @@ import { UrlConfig } from '@ucap-webmessenger/core';
import { Urls } from '../config/urls';
import {
RetrieveRequest,
RetrieveSendResponse,
RetrieveResponse,
encodeRetrieve,
decodeRetrieveSend
} from '../apis/retrieve-send';
decodeRetrieveSend,
decodeRetrieveReceive,
decodeRetrieveReservation,
RetrieveSearchRequest,
decodeRetrieveSearch
} from '../apis/retrieve';
import {
SendRequest,
SendResponse,
encodeSend,
decodeSend
} from '../apis/send';
import {
DetailRequest,
DetailResponse,
encodeDetail,
decodeDetail,
ReadRequest,
ReadResponse,
encodeRead,
decodeRead,
ReadCheckResponse,
decodeReadCheck,
CancelRequest,
CancelResponse,
encodeCancel,
decodeCancel,
CancelReservationRequest,
CancelReservationResponse,
encodeCancelReservation,
decodeCancelReservation,
RetrieveResourceFileRequest,
RetrieveResourceFileResponse,
encodeRetrieveResourceFile,
decodeRetrieveResourceFile
} from '../apis/detail';
import { DelRequest, DelResponse, decodeDel, encodeDel } from '../apis/del';
import {
SaveMyRequest,
SaveMyResponse,
encodeSaveMy,
decodeSaveMy,
RetrieveMyRequest,
RetrieveMyResponse,
encodeRetrieveMy,
decodeRetrieveMy,
DelMyRequest,
DelMyResponse,
encodeDelMy,
decodeDelMy,
EditMyRequest,
EditMyResponse,
encodeEditMy,
decodeEditMy
} from '../apis/my-message';
import {
EditReservationRequest,
EditReservationResponse,
encodeEditReservation,
decodeEditReservation
} from '../apis/edit-reservation-ex';
import {
SendCopyRequest,
SendCopyResponse,
encodeSendCopy,
decodeSendCopy
} from '../apis/send-copy';
import {
UnreadCountRequest,
UnreadCountResponse,
encodeUnreadCount,
decodeUnreadCount
} from '../apis/unread-count';
@Injectable({
providedIn: 'root'
@ -21,6 +92,8 @@ import {
export class MessageApiService {
readonly urls: Urls;
headers: HttpHeaders;
constructor(
@Inject(_MODULE_CONFIG) private moduleConfig: ModuleConfig,
private httpClient: HttpClient
@ -29,19 +102,192 @@ export class MessageApiService {
this.moduleConfig.hostConfig,
this.moduleConfig.urls
);
this.headers = new HttpHeaders({
'Content-Type': 'application/json; charset=utf-8'
});
}
/** retrieve */
public retrieveSendMessage(
req: RetrieveRequest
): Observable<RetrieveSendResponse> {
): Observable<RetrieveResponse> {
return this.httpClient
.post<any>(
this.urls.retrieveSendMessageList,
{},
{
params: encodeRetrieve(req)
}
)
.post<any>(this.urls.retrieveSendMessageList, encodeRetrieve(req), {
headers: this.headers
})
.pipe(map(res => decodeRetrieveSend(res)));
}
public retrieveReceiveMessage(
req: RetrieveRequest
): Observable<RetrieveResponse> {
return this.httpClient
.post<any>(this.urls.retrieveRecvMessageList, encodeRetrieve(req), {
headers: this.headers
})
.pipe(map(res => decodeRetrieveReceive(res)));
}
public retrieveReservationMessage(
req: RetrieveRequest
): Observable<RetrieveResponse> {
return this.httpClient
.post<any>(
this.urls.retrieveReservationMessageList,
encodeRetrieve(req),
{
headers: this.headers
}
)
.pipe(map(res => decodeRetrieveReservation(res)));
}
public retrieveSearchMessage(
req: RetrieveSearchRequest
): Observable<RetrieveResponse> {
return this.httpClient
.post<any>(this.urls.retrieveSearchMessage, encodeRetrieve(req), {
headers: this.headers
})
.pipe(map(res => decodeRetrieveSearch(res)));
}
/** send */
public sendMessage(req: SendRequest): Observable<SendResponse> {
return this.httpClient
.post<any>(this.urls.sendNewMessage, encodeSend(req), {
headers: this.headers
})
.pipe(map(res => decodeSend(res)));
}
/** detail */
public detailMessage(req: DetailRequest): Observable<DetailResponse> {
return this.httpClient
.post<any>(this.urls.retrieveMessageDetail, encodeDetail(req), {
headers: this.headers
})
.pipe(map(res => decodeDetail(res)));
}
public readMessage(req: ReadRequest): Observable<ReadResponse> {
return this.httpClient
.post<any>(this.urls.readMessage, encodeRead(req), {
headers: this.headers
})
.pipe(map(res => decodeRead(res)));
}
public retrieveReadCheck(req: ReadRequest): Observable<ReadCheckResponse> {
return this.httpClient
.post<any>(this.urls.retrieveReadCheck, encodeRead(req), {
headers: this.headers
})
.pipe(map(res => decodeReadCheck(res)));
}
public cancelMessage(req: CancelRequest): Observable<CancelResponse> {
return this.httpClient
.post<any>(this.urls.cancelMessage, encodeCancel(req), {
headers: this.headers
})
.pipe(map(res => decodeCancel(res)));
}
public cancelReservationMessage(
req: CancelReservationRequest
): Observable<CancelReservationResponse> {
return this.httpClient
.post<any>(
this.urls.cancelReservationMessage,
encodeCancelReservation(req),
{
headers: this.headers
}
)
.pipe(map(res => decodeCancelReservation(res)));
}
public retrieveResourceFile(
req: RetrieveResourceFileRequest
): Observable<RetrieveResourceFileResponse> {
return this.httpClient
.post<any>(
this.urls.retrieveResourceFile,
encodeRetrieveResourceFile(req),
{
headers: this.headers
}
)
.pipe(map(res => decodeRetrieveResourceFile(res)));
}
/** del */
public deleteMessage(req: DelRequest): Observable<DelResponse> {
return this.httpClient
.post<any>(this.urls.deleteMessage, encodeDel(req), {
headers: this.headers
})
.pipe(map(res => decodeDel(res)));
}
/** save my message */
public saveMyMessage(req: SaveMyRequest): Observable<SaveMyResponse> {
return this.httpClient
.post<any>(this.urls.saveMyMessage, encodeSaveMy(req), {
headers: this.headers
})
.pipe(map(res => decodeSaveMy(res)));
}
public retrieveMyMessage(
req: RetrieveMyRequest
): Observable<RetrieveMyResponse> {
return this.httpClient
.post<any>(this.urls.retrieveMyMessage, encodeRetrieveMy(req), {
headers: this.headers
})
.pipe(map(res => decodeRetrieveMy(res)));
}
public deleteMyMessage(req: DelMyRequest): Observable<DelMyResponse> {
return this.httpClient
.post<any>(this.urls.deleteMyMessage, encodeDelMy(req), {
headers: this.headers
})
.pipe(map(res => decodeDelMy(res)));
}
public editMyMessage(req: EditMyRequest): Observable<EditMyResponse> {
return this.httpClient
.post<any>(this.urls.editMyMessage, encodeEditMy(req), {
headers: this.headers
})
.pipe(map(res => decodeEditMy(res)));
}
/** edit reservation */
public editReservationMessageEx(
req: EditReservationRequest
): Observable<EditReservationResponse> {
return this.httpClient
.post<any>(
this.urls.editReservationMessageEx,
encodeEditReservation(req),
{
headers: this.headers
}
)
.pipe(map(res => decodeEditReservation(res)));
}
/** send-copy(forward) */
public sendCopyMessage(req: SendCopyRequest): Observable<SendCopyResponse> {
return this.httpClient
.post<any>(this.urls.sendCopyMessage, encodeSendCopy(req), {
headers: this.headers
})
.pipe(map(res => decodeSendCopy(res)));
}
/** unread count */
public retrieveUnreadCount(
req: UnreadCountRequest
): Observable<UnreadCountResponse> {
return this.httpClient
.post<any>(this.urls.retrieveUnreadCount, encodeUnreadCount(req), {
headers: this.headers
})
.pipe(map(res => decodeUnreadCount(res)));
}
}

View File

@ -1,7 +1,13 @@
/*
* Public API Surface of ucap-webmessenger-api-message
*/
export * from './lib/apis/retrieve-send';
export * from './lib/apis/del';
export * from './lib/apis/detail';
export * from './lib/apis/edit-reservation-ex';
export * from './lib/apis/my-message';
export * from './lib/apis/retrieve';
export * from './lib/apis/send-copy';
export * from './lib/apis/send';
export * from './lib/services/message-api.service';

View File

@ -1,6 +1,7 @@
import { HttpParams } from '@angular/common/http';
import { StatusCode } from '../types/status-code.type';
import { MessageStatusCode } from '../types/message-status-code.type';
export interface APIRequest {
_id?: string;
@ -11,9 +12,16 @@ export interface APIResponse {
statusCode: StatusCode;
errorMessage: string;
}
export interface MessageAPIResponse {
_id?: string;
responseCode: MessageStatusCode;
responseMsg: string;
}
export type APIEncoder<REQ> = (req: REQ) => HttpParams;
export type APIJsonEncoder<REQ> = (req: REQ) => string;
export type APIFormDataEncoder<REQ> = (req: REQ) => FormData;
export type APIDecoder<RES> = (res: any) => RES;

View File

@ -0,0 +1,28 @@
export enum MessageStatusCode {
/** 성공 */
Success = '00',
/** 사용자 토큰이 일치하지 않을 경우 발생 (인증 실패) */
Fail_Auth = '10',
/** 메시지 타입이 일치하지 않을 경우 발생 */
Fail_Msg_Type = '20',
/** 메시지 아이디가 NULL일 경우 발생 */
Fail_Msg_Id_Null = '21',
/** 예약 메시지 발송 시, 예약 시간이 지정되어있지 않을 경우 발생 */
Fail_Msg_Reservation_Time_Null = '22',
/** 메시지 발송 시, 제목이 최대 길이보다 길 경우 발생 */
Fail_Msg_Title_length = '23',
/** 예약 메시지 발송 시, 현재 시간 10분 전일 경우 발생 */
Fail_Msg_Reservation_Time_Less = '24',
/** 예약 메시지 수정 시, 발송 시간 10분 전일 경우 발생 */
Fail_Msg_Edit_Reservation_Time_Less = '25',
/** 예약 메시지 수정 및 발송 취소 시, 이미 발송된 메시지일 경우 발생 */
Fail_Msg_Edit_Cancel_Reservation_Sended = '26',
/** 파일 업로드 시, 경로가 올바르지 않을 경우 발생 */
Fail_File_Path = '40',
/** 파일 업로드 시, 파일 크기가 최대 크기보다 클 경우 발생 */
Fail_File_Size = '41',
/** 파일 업로드 시, 업로드할 수 없는 파일 형식일 경우 발생 */
Fail_File_Ext = '42',
/** Exception 에러 발생 */
Fail = '99'
}

View File

@ -4,6 +4,7 @@
export * from './lib/apis/api';
export * from './lib/types/message-status-code.type';
export * from './lib/types/status-code.type';
export * from './lib/utils/json.util';

View File

@ -95,12 +95,9 @@
.file-drop-zone {
position: absolute;
background-color: rgb(180, 180, 180);
top: calc(100% - 200px);
left: 20%;
width: 60%;
height: 200px;
padding:10px 10px 0 10px;
background-color: rgb(54, 54, 54, 0.8);
bottom:0
}
}
}

View File

@ -52,28 +52,24 @@
</td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Name</th>
<td mat-cell *matCellDef="let element">{{ element.info.name }}</td>
</ng-container>
<ng-container matColumnDef="size">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Size</th>
<th mat-header-cell *matHeaderCellDef mat-sort-header class="infos">
Name
</th>
<td mat-cell *matCellDef="let element">
<div>
{{ element.info.name }}
</div>
<div>
{{ element.info.size | ucapBytes }}
</div>
</td>
</ng-container>
<ng-container matColumnDef="sendDate">
<ng-container matColumnDef="sendDate" class="date">
<th mat-header-cell *matHeaderCellDef mat-sort-header>sendDate</th>
<td mat-cell *matCellDef="let element">
{{ element.info.sendDate | dateToStringFormat: 'YYYY.MM.DD' }}
</td>
</ng-container>
<ng-container matColumnDef="receivedUserCount">
<th mat-header-cell *matHeaderCellDef mat-sort-header>receivedUser</th>
<td mat-cell *matCellDef="let element">
{{ element.info.receivedUserCount }}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr
mat-row

View File

@ -3,7 +3,7 @@ import { MatPaginator, MatTableDataSource, MatSort } from '@angular/material';
import {
FileInfo,
FileDownloadInfo,
FileType,
FileType
} from '@ucap-webmessenger/protocol-file';
import { Subscription, combineLatest } from 'rxjs';
import { Store, select } from '@ngrx/store';
@ -24,16 +24,10 @@ export interface FileInfoTotal {
@Component({
selector: 'app-layout-chat-right-drawer-file-box',
templateUrl: './file-box.component.html',
styleUrls: ['./file-box.component.scss'],
styleUrls: ['./file-box.component.scss']
})
export class FileBoxComponent implements OnInit, OnDestroy {
displayedColumns: string[] = [
'check',
'name',
'size',
'sendDate',
'receivedUserCount',
];
displayedColumns: string[] = ['check', 'name', 'sendDate'];
dataSource = new MatTableDataSource<FileInfoTotal>();
fileInfoTotal: FileInfoTotal[];
@ -69,7 +63,7 @@ export class FileBoxComponent implements OnInit, OnDestroy {
select(
AppStore.MessengerSelector.EventSelector.selectAllFileInfoCheckList
)
),
)
])
.pipe(
tap(() => (this.fileInfoTotal = [])),
@ -91,7 +85,7 @@ export class FileBoxComponent implements OnInit, OnDestroy {
info: fileInfo,
checkInfo: fileInfoCheckList.filter(
checkInfo => checkInfo.seq === fileInfo.seq
),
)
});
});
@ -108,10 +102,8 @@ export class FileBoxComponent implements OnInit, OnDestroy {
return item.info.size;
case 'sendDate':
return item.info.sendDate;
case 'receivedUserCount':
return item.info.receivedUserCount;
default:
return item[property];
return item.info[property];
}
};
this.dataSource.sort = this.sort;

View File

@ -1,3 +1,109 @@
<div class="message-box container">
<p>message-box works!</p>
<div>
search Area.
</div>
<div class="container">
<mat-tab-group
mat-stretch-tabs
animationDuration="0ms"
(selectedIndexChange)="onSelectedIndexChange($event)"
>
<mat-tab>
<ng-template mat-tab-label>
수신
</ng-template>
<div *ngFor="let message of messageList">
<dl>
<dt>
<mat-icon
*ngIf="
!!message.resType && message.resType === ContentType.Image
"
>image</mat-icon
>
<mat-icon
*ngIf="
!!message.resType &&
message.resType === ContentType.AttachFile
"
>attach_file</mat-icon
>
<ul>
<li>{{ message.userName }}</li>
<li>{{ message.title }}</li>
</ul>
</dt>
<dd>
{{ message.regDate | dateToStringFormat: 'MM:DD' }}
</dd>
</dl>
</div>
</mat-tab>
<mat-tab>
<ng-template mat-tab-label>
발신
</ng-template>
<div *ngFor="let message of messageList">
<dl>
<dt>
<mat-icon
*ngIf="
!!message.resType && message.resType === ContentType.Image
"
>image</mat-icon
>
<mat-icon
*ngIf="
!!message.resType &&
message.resType === ContentType.AttachFile
"
>attach_file</mat-icon
>
<ul>
<li>{{ message.userName }}</li>
<li>{{ message.title }}</li>
</ul>
</dt>
<dd>
{{ message.regDate | dateToStringFormat: 'MM:DD' }}
</dd>
</dl>
</div>
</mat-tab>
<mat-tab>
<ng-template mat-tab-label>
예약
</ng-template>
<div *ngFor="let message of messageList">
<dl>
<dt>
<mat-icon
*ngIf="
!!message.resType && message.resType === ContentType.Image
"
>image</mat-icon
>
<mat-icon
*ngIf="
!!message.resType &&
message.resType === ContentType.AttachFile
"
>attach_file</mat-icon
>
<ul>
<li>{{ message.userName }}</li>
<li>{{ message.title }}</li>
</ul>
</dt>
<dd>
{{ message.regDate | dateToStringFormat: 'MM:DD' }}
</dd>
</dl>
</div>
</mat-tab>
</mat-tab-group>
</div>
</div>

View File

@ -10,35 +10,23 @@ import { Store, select } from '@ngrx/store';
import { tap, map, catchError } from 'rxjs/operators';
import * as AppStore from '@app/store';
import * as SyncStore from '@app/store/messenger/sync';
import * as RoomStore from '@app/store/messenger/room';
import { UserInfo } from '@ucap-webmessenger/protocol-room';
import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
import { KEY_VER_INFO } from '@app/types/ver-info.type';
import { DialogService } from '@ucap-webmessenger/ui';
import {
SelectGroupDialogComponent,
SelectGroupDialogResult,
SelectGroupDialogData
} from '../../dialogs/group/select-group.dialog.component';
import { GroupDetailData } from '@ucap-webmessenger/protocol-sync';
import {
CreateChatDialogComponent,
CreateChatDialogResult,
CreateChatDialogData
} from '../../dialogs/chat/create-chat.dialog.component';
import { UserSelectDialogType } from '@app/types';
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type';
import {
MessageApiService,
MessageType,
RetrieveRequest,
MessageType
MessageList
} from '@ucap-webmessenger/api-message';
import { DeviceType } from '@ucap-webmessenger/core';
import { StatusCode } from '@ucap-webmessenger/api';
import { MessageStatusCode } from '@ucap-webmessenger/api';
import { ContentType } from '@ucap-webmessenger/api-message';
@Component({
selector: 'app-layout-chat-right-drawer-message-box',
@ -52,10 +40,23 @@ export class MessageBoxComponent implements OnInit, OnDestroy {
loginRes: LoginResponse;
sessionVerinfo: VersionInfo2Response;
messageSendListSubscription: Subscription;
messageList: MessageList[] = [];
pageSize = 100; // default
currentPage = 0; // start index is 0.
messageRecieveListSubscription: Subscription;
messageSendListSubscription: Subscription;
messageReservationListSubscription: Subscription;
messageSearchListSubscription: Subscription;
defaultPageSize = 100; // default
recieveCurrentPage = 0; // start index is 0.
sendCurrentPage = 0; // start index is 0.
reservationCurrentPage = 0; // start index is 0.
searchCurrentPage = 0; // start index is 0.
currentTotalCount = 0;
currentPage = 0;
ContentType = ContentType;
constructor(
private store: Store<any>,
@ -81,19 +82,75 @@ export class MessageBoxComponent implements OnInit, OnDestroy {
)
.subscribe();
// 초기 검색은 수신함.
this.getRetrieveMessage(MessageType.Receive, this.recieveCurrentPage);
}
ngOnDestroy(): void {
if (!!this.userInfoListSubscription) {
this.userInfoListSubscription.unsubscribe();
}
if (!!this.messageRecieveListSubscription) {
this.messageRecieveListSubscription.unsubscribe();
}
if (!!this.messageSendListSubscription) {
this.messageSendListSubscription.unsubscribe();
}
if (!!this.messageReservationListSubscription) {
this.messageReservationListSubscription.unsubscribe();
}
if (!!this.messageSearchListSubscription) {
this.messageSearchListSubscription.unsubscribe();
}
}
onSelectedIndexChange(value: number) {
switch (value) {
case 0:
{
// Recieve
this.getRetrieveMessage(MessageType.Receive, this.recieveCurrentPage);
}
break;
case 1:
{
// Send
this.getRetrieveMessage(MessageType.Send, this.sendCurrentPage);
}
break;
case 2:
{
// Reservation
this.getRetrieveMessage(
MessageType.Reservation,
this.reservationCurrentPage
);
}
break;
}
}
getRetrieveMessage(type: MessageType, trgtPageIndex: number) {
switch (type) {
case MessageType.Receive:
{
this.messageSendListSubscription = this.messageApiService
.retrieveSendMessage({
.retrieveReceiveMessage({
userSeq: this.loginRes.userSeq,
deviceType: DeviceType.PC,
tokenKey: this.loginRes.tokenString,
type: MessageType.Send,
pageSize: this.pageSize,
pageCount: this.currentPage
type: MessageType.Receive,
pageSize: this.defaultPageSize,
pageCount: this.recieveCurrentPage
} as RetrieveRequest)
.pipe(
map(res => {
console.log(res);
if (res.statusCode === StatusCode.Success) {
if (res.responseCode === MessageStatusCode.Success) {
this.currentTotalCount = res.totalCount;
this.currentPage = res.pageCount;
this.recieveCurrentPage = res.pageCount;
this.messageList = res.messageList;
} else {
}
}),
@ -101,13 +158,61 @@ export class MessageBoxComponent implements OnInit, OnDestroy {
)
.subscribe();
}
ngOnDestroy(): void {
if (!!this.userInfoListSubscription) {
this.userInfoListSubscription.unsubscribe();
break;
case MessageType.Send:
{
this.messageSendListSubscription = this.messageApiService
.retrieveSendMessage({
userSeq: this.loginRes.userSeq,
deviceType: DeviceType.PC,
tokenKey: this.loginRes.tokenString,
type: MessageType.Send,
pageSize: this.defaultPageSize,
pageCount: this.sendCurrentPage
} as RetrieveRequest)
.pipe(
map(res => {
console.log(res);
if (res.responseCode === MessageStatusCode.Success) {
this.currentTotalCount = res.totalCount;
this.currentPage = res.pageCount;
this.sendCurrentPage = res.pageCount;
this.messageList = res.messageList;
} else {
}
if (!!this.messageSendListSubscription) {
this.messageSendListSubscription.unsubscribe();
}),
catchError(error => of(console.log(error)))
)
.subscribe();
}
break;
case MessageType.Reservation:
{
this.messageSendListSubscription = this.messageApiService
.retrieveReservationMessage({
userSeq: this.loginRes.userSeq,
deviceType: DeviceType.PC,
tokenKey: this.loginRes.tokenString,
type: MessageType.Reservation,
pageSize: this.defaultPageSize,
pageCount: this.reservationCurrentPage
} as RetrieveRequest)
.pipe(
map(res => {
console.log(res);
if (res.responseCode === MessageStatusCode.Success) {
this.currentTotalCount = res.totalCount;
this.currentPage = res.pageCount;
this.reservationCurrentPage = res.pageCount;
this.messageList = res.messageList;
} else {
}
}),
catchError(error => of(console.log(error)))
)
.subscribe();
}
break;
}
}
}

View File

@ -2,6 +2,8 @@
<div class="login-wrapper" fxLayout="column" fxLayoutAlign="center center">
<ucap-account-login
[companyList]="companyList$ | async"
[loginBtnEnable]="loginBtnEnable"
[loginBtnText]="loginBtnText"
(login)="onLogin($event)"
>
</ucap-account-login>

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Store, select } from '@ngrx/store';
@ -7,20 +7,45 @@ import { Company } from '@ucap-webmessenger/api-external';
import * as AppStore from '@app/store';
import * as AuthenticationStore from '@app/store/account/authentication';
import * as CompanyStore from '@app/store/setting/company';
import { Observable } from 'rxjs';
import { Observable, Subscription } from 'rxjs';
import { Router } from '@angular/router';
import { tap, map } from 'rxjs/operators';
import {
DialogService,
AlertDialogComponent,
AlertDialogData,
AlertDialogResult
} from '@ucap-webmessenger/ui';
import { StringUtil } from '@ucap-webmessenger/core';
@Component({
selector: 'app-page-account-login',
templateUrl: './login.page.component.html',
styleUrls: ['./login.page.component.scss']
})
export class LoginPageComponent implements OnInit {
export class LoginPageComponent implements OnInit, OnDestroy {
companyList$: Observable<Company[]>;
constructor(private store: Store<any>, private router: Router) {}
loginFailureCount: Subscription;
defatulLoginBtnText: string;
loginBtnText: string;
loginBtnEnable: boolean;
timeChecker: any;
defatulWaitingTime: number;
waitingTime: number;
constructor(
private store: Store<any>,
private router: Router,
private dialogService: DialogService
) {}
ngOnInit(): void {
this.defatulLoginBtnText = 'LOGIN';
this.defatulWaitingTime = 5 * 60; // sec
this.store.dispatch(
CompanyStore.companyList({
companyGroupCode: 'LG'
@ -30,6 +55,80 @@ export class LoginPageComponent implements OnInit {
this.companyList$ = this.store.pipe(
select(AppStore.SettingSelector.CompanySelector.companyList)
);
this.loginFailureCount = this.store
.pipe(
select(AppStore.AccountSelector.AuthenticationSelector.loginFailCount),
map(count => {
if (count > 0) {
if (count < 5) {
this.dialogService.open<
AlertDialogComponent,
AlertDialogData,
AlertDialogResult
>(AlertDialogComponent, {
width: '360px',
data: {
title: 'Alert',
html: `아이디 또는 패스워드가<br/>일치하지 않습니다.`
}
});
}
if (count === 5) {
this.dialogService.open<
AlertDialogComponent,
AlertDialogData,
AlertDialogResult
>(AlertDialogComponent, {
width: '360px',
data: {
title: 'Alert',
html: `비밀번호 오류 횟수 초과입니다.<br/>비밀번호를 확인하신 후<br/>잠시 후 다시 시작해 주세요.`
}
});
this.timeChecker = setInterval(() => this.getCheckTime(), 1000);
}
return;
} else {
if (!!this.timeChecker) {
clearInterval(this.timeChecker);
}
this.waitingTime = this.defatulWaitingTime;
this.loginBtnText = this.defatulLoginBtnText;
this.loginBtnEnable = true;
}
})
)
.subscribe();
}
ngOnDestroy(): void {
if (!!this.loginFailureCount) {
this.loginFailureCount.unsubscribe();
}
}
getCheckTime() {
if (this.waitingTime <= 0) {
// reset.
if (!!this.timeChecker) {
clearInterval(this.timeChecker);
}
this.waitingTime = this.defatulWaitingTime;
this.loginBtnText = this.defatulLoginBtnText;
this.loginBtnEnable = true;
} else {
// wait.
this.waitingTime = this.waitingTime - 1;
this.loginBtnText = `${StringUtil.zeroFill(
Math.floor(this.waitingTime / 60),
2
)}:${StringUtil.zeroFill(this.waitingTime % 60, 2)}`;
this.loginBtnEnable = false;
}
}
onLogin(value: {

View File

@ -6,7 +6,7 @@ import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
import { LoginInfo } from '@app/types';
import {
UserPasswordSetRequest,
UserPasswordSetResponse,
UserPasswordSetResponse
} from '@ucap-webmessenger/protocol-service';
export const webLogin = createAction(
@ -43,6 +43,15 @@ export const loginFailure = createAction(
props<{ error: any }>()
);
export const increaseLoginFailCount = createAction(
'[Account::Authentication] Increase Login Failure Count',
props()
);
export const initialLoginFailCount = createAction(
'[Account::Authentication] Initialize Login Failure Count',
props()
);
export const loginRedirect = createAction(
'[Account::Authentication] Login Redirect'
);

View File

@ -9,14 +9,14 @@ import { Actions, ofType, createEffect } from '@ngrx/effects';
import {
PiService,
Login2Response,
UserTermsActionResponse,
UserTermsActionResponse
} from '@ucap-webmessenger/pi';
import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native';
import {
DialogService,
ConfirmDialogComponent,
ConfirmDialogData,
ConfirmDialogResult,
ConfirmDialogResult
} from '@ucap-webmessenger/ui';
import {
@ -35,12 +35,14 @@ import {
changePassword,
changePasswordFailure,
changePasswordSuccess,
increaseLoginFailCount,
initialLoginFailCount
} from './actions';
import {
LoginInfo,
KEY_LOGIN_INFO,
EnvironmentsInfo,
KEY_ENVIRONMENTS_INFO,
KEY_ENVIRONMENTS_INFO
} from '@app/types';
import { AppAuthenticationService } from '@app/services/authentication.service';
import { NGXLogger } from 'ngx-logger';
@ -48,7 +50,7 @@ import { Store } from '@ngrx/store';
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
import {
ServiceProtocolService,
UserPasswordSetResponse,
UserPasswordSetResponse
} from '@ucap-webmessenger/protocol-service';
@Injectable()
@ -62,17 +64,19 @@ export class Effects {
.login2({
loginId: params.loginInfo.loginId,
loginPw: params.loginInfo.loginPw,
companyCode: params.loginInfo.companyCode,
companyCode: params.loginInfo.companyCode
})
.pipe(
map((res: Login2Response) => {
if ('success' !== res.status.toLowerCase()) {
this.store.dispatch(increaseLoginFailCount({}));
return webLoginFailure({ error: 'Failed' });
} else {
this.store.dispatch(initialLoginFailCount({}));
return webLoginSuccess({
loginInfo: params.loginInfo,
rememberMe: params.rememberMe,
login2Response: res,
login2Response: res
});
}
}),
@ -147,8 +151,8 @@ export class Effects {
width: '220px',
data: {
title: 'Logout',
message: 'Logout ?',
},
message: 'Logout ?'
}
});
return result.choice;
@ -177,7 +181,7 @@ export class Effects {
token: loginRes.tokenString,
deviceType: environmentsInfo.deviceType,
localeCode: loginInfo.localeCode,
textOnly: 'true',
textOnly: 'true'
});
const result = await this.dialogService.open<
@ -189,9 +193,9 @@ export class Effects {
height: '500px',
disableClose: true,
data: {
title: '개인정보 동의',
title: '개인정보 동의'
// html: `<iframe id="ifm_privacy" src="${privacyTotalUrl}" style="width: 100%;height: 300px;" />`
},
}
});
if (result.choice) {
@ -213,8 +217,8 @@ export class Effects {
disableClose: true,
data: {
title: '패스워드 만료',
message: '',
},
message: ''
}
});
if (result.choice) {
@ -241,7 +245,7 @@ export class Effects {
return {
loginInfo,
environmentsInfo,
loginResponse: action.loginRes,
loginResponse: action.loginRes
};
}),
exhaustMap(params =>
@ -249,7 +253,7 @@ export class Effects {
.userTermsAction({
userSeq: params.loginResponse.userSeq,
token: params.loginResponse.tokenString,
deviceType: params.environmentsInfo.deviceType,
deviceType: params.environmentsInfo.deviceType
})
.pipe(
map((res: UserTermsActionResponse) => {
@ -273,7 +277,7 @@ export class Effects {
this.serviceProtocolService.userPasswordSet(req).pipe(
map((res: UserPasswordSetResponse) => {
return changePasswordSuccess({
res,
res
});
}),
catchError(error => of(changePasswordFailure({ error })))

View File

@ -1,13 +1,30 @@
import { Action, combineReducers, createReducer, on } from '@ngrx/store';
import { State, initialState } from './state';
import { loginSuccess } from './actions';
import {
loginSuccess,
increaseLoginFailCount,
initialLoginFailCount
} from './actions';
export const reducer = createReducer(
initialState,
on(loginSuccess, (state, action) => {
return {
...state,
loginRes: action.loginRes,
loginRes: action.loginRes
};
}),
on(increaseLoginFailCount, (state, action) => {
return {
...state,
loginFailCount: state.loginFailCount + 1
};
}),
on(initialLoginFailCount, (state, action) => {
return {
...state,
loginFailCount: 0
};
})
);

View File

@ -4,14 +4,20 @@ import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
// tslint:disable-next-line: no-empty-interface
export interface State {
loginRes: LoginResponse | null;
loginFailCount: number;
}
export const initialState: State = {
loginRes: null,
loginFailCount: 0
};
export function selectors<S>(selector: Selector<any, State>) {
return {
loginRes: createSelector(selector, (state: State) => state.loginRes),
loginFailCount: createSelector(
selector,
(state: State) => state.loginFailCount
)
};
}

View File

@ -1 +1,18 @@
export class StringUtil {}
export class StringUtil {
/**
* prefix zero fill
* @param str target string
* @param len fill in length
*/
public static zeroFill(str: any, len: number): string {
if (typeof str === 'string') {
let fillin = '';
for (let i = 0; i < len - str.length; i++) {
fillin += '0';
}
return fillin + str;
} else if (typeof str === 'number') {
return StringUtil.zeroFill(str.toString(), len);
}
}
}

View File

@ -2,7 +2,7 @@
<div class="mat-title">LOGIN TO YOUR ACCOUNT</div>
<form name="loginForm" [formGroup]="loginForm" novalidate>
<mat-form-field >
<mat-form-field>
<mat-label>Company</mat-label>
<mat-select formControlName="companyCode" required>
<mat-option
@ -27,7 +27,7 @@
</mat-error>
</mat-form-field>
<mat-form-field >
<mat-form-field>
<mat-label>Password</mat-label>
<input matInput type="password" formControlName="loginPw" #loginPw />
<mat-error>
@ -35,8 +35,17 @@
</mat-error>
</mat-form-field>
<div class="remember-forgot-password" fxLayout="row" fxLayout.xs="column" fxLayoutAlign="space-between center">
<mat-checkbox class="remember-me" formControlName="remember" aria-label="Remember Me">
<div
class="remember-forgot-password"
fxLayout="row"
fxLayout.xs="column"
fxLayoutAlign="space-between center"
>
<mat-checkbox
class="remember-me"
formControlName="remember"
aria-label="Remember Me"
>
Remember Me
</mat-checkbox>
@ -49,10 +58,10 @@
mat-raised-button
class="submit-button bg-accent-dark"
aria-label="LOG IN"
[disabled]="loginForm.invalid"
[disabled]="loginForm.invalid || !loginBtnEnable"
(click)="onClickLogin()"
>
LOGIN
{{ loginBtnText }}
</button>
</form>

View File

@ -20,6 +20,10 @@ import { LoginInfo, KEY_LOGIN_INFO } from '@app/types';
export class LoginComponent implements OnInit {
@Input()
companyList?: Company[];
@Input()
loginBtnText?: string;
@Input()
loginBtnEnable: boolean;
@Output()
login = new EventEmitter<{
@ -57,6 +61,10 @@ export class LoginComponent implements OnInit {
loginPw: ['', Validators.required],
remember: [remember]
});
if (!this.loginBtnText || this.loginBtnText.trim().length === 0) {
this.loginBtnText = 'LOGIN';
}
}
onClickLogin() {

View File

@ -1,7 +1,5 @@
<div class="bubble-main">
<!--파일명에 따라 doc exe hwp ppt xls zip 으로 추가되고 나머지 파일 명은 file로 기간이 만료된 파일은 그뒤에 disable도 추가-->
<!-- <div class="file-img" [ngClass]="fileInfo.FileExt"></div> -->
<div>
<div class="file-thumbimg">
<img *ngIf="!!fileInfo.thumbUrl" [src]="fileInfo.thumbUrl" />
</div>
<div

View File

@ -1,18 +1,13 @@
.bubble-main {
flex-direction: column;
display:flex;
flex-direction: row;
padding: 14px;
.file-img {
display: inline-flex;
width: 50px;
height: 50px;
float: left;
margin-right: 14px;
.file-thumbimg{
display:inline-flex;
img {
width:200px;
padding-right: 20px;
background-repeat: no-repeat;
&.video {
background-image: url(/assets/images/file/icon_talk_video.png);
&.disable {
background-image: url(/assets/images/file/icon_talk_video_d.png);
}
}
}
.file-info {
@ -20,7 +15,7 @@
flex-direction: column;
text-align: left;
line-height: 1.6em;
width:100%;
min-width:100px;
margin-top:10px;
.file-name {
font-size: 14px;

View File

@ -1,4 +1,4 @@
<mat-card class="example-card">
<mat-card class="example-card mat-elevation-z">
<mat-card-header>
<mat-card-title>
<span>{{ userInfo.name }}</span>
@ -6,70 +6,124 @@
</mat-card-title>
<mat-card-subtitle>{{ userInfo.deptName }}</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<div class="profile-img">
<img ucapImage [base]="profileImageRoot" [path]="userInfo.profileImageFile"
[default]="'assets/images/img_nophoto_50.png'" />
</div>
<!-- 내프로필의 경우<div class="profile-option">이 없음 -->
<div class="profile-option">
<span class="btn-favorite">
<!--즐겨찾기 추가버튼 추가된 상태: svg에 class="on"-->
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="1.5" stroke-linecap="butt" stroke-linejoin="round">
<polygon
points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2">
</polygon>
</svg>
</span>
<span class="btn-groupadd">
<!--친구 추가 버튼-->
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="1.5" stroke-linecap="butt" stroke-linejoin="round" class="on">
<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
<circle cx="8.5" cy="7" r="4"></circle>
<line x1="20" y1="8" x2="20" y2="14"></line>
<line x1="23" y1="11" x2="17" y2="11"></line>
</svg>
<!--친구 삭제 버튼-->
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="1.5" stroke-linecap="butt" stroke-linejoin="round">
<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
<circle cx="8.5" cy="7" r="4"></circle>
<line x1="23" y1="11" x2="17" y2="11"></line>
</svg>
</span>
</div>
<ul>
<li>
<img
ucapImage
[base]="profileImageRoot"
[path]="userInfo.profileImageFile"
[default]="'assets/images/img_nophoto_50.png'"
/>
</li>
<li>
<mat-icon>chat</mat-icon>
<!--<mat-icon>chat</mat-icon>-->
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="1.5" stroke-linecap="butt" stroke-linejoin="round">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
</svg>
{{ userInfo.intro }}
</li>
<li>
<mat-icon>email</mat-icon>
<!--<mat-icon>email</mat-icon>-->
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="1.5" stroke-linecap="butt" stroke-linejoin="round">
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
<polyline points="22,6 12,13 2,6"></polyline>
</svg>
{{ userInfo.email }}
</li>
<li>
<mat-icon>local_phone</mat-icon>
<!--<mat-icon>local_phone</mat-icon>-->
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="1.5" stroke-linecap="butt" stroke-linejoin="round">
<path
d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z">
</path>
</svg>
{{ userInfo.lineNumber }}
</li>
<li>
<mat-icon>phone_android</mat-icon>
<!--<mat-icon>phone_android</mat-icon>-->
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="1.5" stroke-linecap="butt" stroke-linejoin="round">
<rect x="5" y="2" width="14" height="20" rx="2" ry="2"></rect>
<line x1="12" y1="18" x2="12" y2="18"></line>
</svg>
{{ userInfo.hpNumber }}
</li>
</ul>
</mat-card-content>
<mat-card-actions>
<div fxFlex fxLayout="row" fxLayoutAlign="space-around center">
<button
mat-mini-fab
[matTooltip]="isMe ? 'MyTalk' : '1:1 대화'"
matTooltipPosition="above"
(click)="onClickOpenChat()"
>
<mat-icon>chat</mat-icon>
<button mat-mini-fab class="mat-elevation-z" [matTooltip]="isMe ? 'MyTalk' : '1:1 대화'" matTooltipPosition="above"
(click)="onClickOpenChat()">
<!--<mat-icon>chat</mat-icon>-->
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="butt" stroke-linejoin="round">
<path
d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z">
</path>
</svg>
</button>
<button
mat-mini-fab
*ngIf="!isMe"
matTooltip="전화"
matTooltipPosition="above"
(click)="onClickCall()"
>
<mat-icon>call</mat-icon>
<button mat-mini-fab class="mat-elevation-z" *ngIf="!isMe" matTooltip="전화" matTooltipPosition="above"
(click)="onClickCall()">
<!--<mat-icon>call</mat-icon>-->
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="butt" stroke-linejoin="round">
<path
d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z">
</path>
</svg>
</button>
<button
mat-mini-fab
*ngIf="!isMe"
matTooltip="화상회의"
matTooltipPosition="above"
(click)="onClickVideoConference()"
>
<mat-icon>videocam</mat-icon>
<button mat-mini-fab class="mat-elevation-z" *ngIf="!isMe" matTooltip="화상회의" matTooltipPosition="above"
(click)="onClickVideoConference()">
<!--<mat-icon>videocam</mat-icon>-->
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="butt" stroke-linejoin="round">
<path
d="M15.6 11.6L22 7v10l-6.4-4.5v-1zM4 5h9a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V7c0-1.1.9-2 2-2z" />
</svg>
</button>
<button
mat-mini-fab
*ngIf="!isMe"
matTooltip="쪽지"
matTooltipPosition="above"
(click)="onClickMessage()"
>
<mat-icon>sms</mat-icon>
<button mat-mini-fab class="mat-elevation-z" *ngIf="!isMe" matTooltip="쪽지" matTooltipPosition="above"
(click)="onClickMessage()">
<!--<mat-icon>sms</mat-icon>-->
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="butt" stroke-linejoin="round">
<line class="st0" x1="18" y1="2" x2="9.2" y2="10.8" />
<polygon class="st0" points="18,2 12.4,18 9.2,10.8 2,7.6 " />
</svg>
</button>
</div>
</mat-card-actions>

View File

@ -1,3 +1,104 @@
@mixin ellipsis($row) {
overflow: hidden;
text-overflow: ellipsis;
@if $row == 1 {
display: block;
white-space: nowrap;
word-wrap: normal;
} @else if $row >= 2 {
display: -webkit-box;
-webkit-line-clamp: $row;
-webkit-box-orient: vertical;
word-wrap: break-word;
}
}
::ng-deep .mat-card-header-text{
width:100%;
.mat-card-subtitle{
color: rgb(256, 256, 256, 0.7) !important;
text-align:center;
margin-top:10px !important;
}
}
.example-card {
width: 400px;
padding: 0 0 20px;
position: relative;
.mat-card-header{
justify-content: center;
padding-bottom: 40px;
background: #ce395d;
background: linear-gradient(to right, #345385, #ef4c73);
color: #ffffff;
padding-top: 20px;
width:100%;
.mat-card-title{
margin-bottom: 12px;
max-width: 100%;
justify-content: center;
display: flex;
margin:0 20px;
span{
@include ellipsis(1);
}
}
}
.mat-card-content{
margin-top:-40px;
.profile-img{
display:flex;
height:80px;
justify-content: center;
margin-bottom:20px;
img{
widows: 80px;
height: 80px;
border-radius: 50%;
background-color:#efefef;
border:2px solid #ffffff;
}
}
.profile-option{
display:flex;
padding:0 20px;
color:#ffffff;
margin-top: -100px;
height: 120px;
.btn-favorite{
cursor: pointer;
.on{
fill:yellow;
}
}
.btn-groupadd{
margin-left:auto;
cursor: pointer;
svg{
display:none;
&.on{
display:block;
}
}
}
}
ul{
padding:0 20px;
display:flex;
flex-flow: column;
margin-top:-20px;
li{
display:inline-flex;
height:30px;
align-items: center;
flex-flow:row;
margin-bottom:20px;
svg{
margin-right:10px;
color:#777777;
}
}
}
}
}

View File

@ -1,25 +1,23 @@
<div fxLayout="row wrap" fxFlex="100" class="ucap-file-upload-queue-container">
<div
fxLayout="column"
fxFlex="100"
fxFlex.gt-xs="100"
fxFlex.gt-md="25"
*ngFor="let fileUploadItem of fileUploadItems"
>
<div fxLayout="row">
<div>
<mat-icon>image</mat-icon>
</div>
<div>{{ fileUploadItem.file.name }}</div>
<div *ngFor="let fileUploadItem of fileUploadItems" class="file-upload-item">
<div fxLayout="row" class="file-upload-info">
<!--<mat-icon>image</mat-icon>-->
<!--파일이미지 svg-->
<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>
<!-- <div (click)="onClickClear(fileUploadItem)">
<mat-icon>clear</mat-icon>
</div> -->
</div>
<div fxLayout="row">
<mat-progress-bar
mode="determinate"
[value]="fileUploadItem.uploadingProgress$ | async"
>
<div fxLayout="row" class="file-upload-progress">
<mat-progress-bar mode="determinate" [value]="fileUploadItem.uploadingProgress$ | async">
</mat-progress-bar>
</div>
</div>

View File

@ -1,4 +1,48 @@
@mixin ellipsis($row) {
overflow: hidden;
text-overflow: ellipsis;
@if $row == 1 {
display: block;
white-space: nowrap;
word-wrap: normal;
} @else if $row >= 2 {
display: -webkit-box;
-webkit-line-clamp: $row;
-webkit-box-orient: vertical;
word-wrap: break-word;
}
}
.ucap-file-upload-queue-container {
width: 100%;
height: 100%;
.file-upload-item {
background-color: #eeeeee;
min-width: 200px;
margin: 0 0.5%;
margin-bottom: 10px;
width: 32%;
border-radius: 3px;
.file-upload-info {
padding: 10px;
background-color:#ffffff;
border-bottom: 1px solid #dddddd;
svg {
margin-right: 6px;
}
.file-upload-name {
height: 40px;
@include ellipsis(2);
}
}
.file-upload-progress {
padding: 6px 10px;
}
&:nth-child(3n + 1) {
margin-left: 1%;
}
&:nth-child(3n + 0) {
margin-right: 1%;
}
}
}

View File

@ -1,55 +1,45 @@
<div class="ucap-binary-viewer-container">
<mat-toolbar color="accent" class="ucap-binary-viewer-header">
<mat-icon class="ucap-binary-viewer-icon">attachment</mat-icon>
<mat-toolbar class="ucap-binary-viewer-header">
<!--<mat-icon class="ucap-binary-viewer-icon">attachment</mat-icon>-->
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="butt" stroke-linejoin="round" class="ucap-binary-viewer-icon">
<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>
<span class="ucap-binary-viewer-title">{{ fileInfo.fileName }}</span>
<span class="ucap-binary-viewer-spacer"></span>
<button
mat-icon-button
class="ucap-binary-viewer-action"
matTooltip="다운로드"
matTooltipPosition="below"
aria-label=""
(click)="onClickDownload()"
>
<mat-icon>get_app</mat-icon>
<button mat-icon-button class="ucap-binary-viewer-action" matTooltip="다운로드" matTooltipPosition="below" aria-label=""
(click)="onClickDownload()">
<svg xmlns="http://www.w3.org/2000/svg" width="21" height="21" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="butt" stroke-linejoin="round">
<path d="M3 15v4c0 1.1.9 2 2 2h14a2 2 0 0 0 2-2v-4M17 9l-5 5-5-5M12 12.8V2.5" />
</svg>
</button>
<button
mat-raised-button
color="primary"
class="ucap-binary-viewer-action"
(click)="onClickClose()"
>
Close
<span class="stroke-bar"></span>
<button mat-icon-button color="warn" class="ucap-binary-viewer-action" (click)="onClickClose()">
<svg xmlns="http://www.w3.org/2000/svg" width="21" height="21" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="butt" stroke-linejoin="round">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</mat-toolbar>
<mat-progress-bar
*ngIf="fileDownloadItem && fileDownloadItem.downloadingProgress$"
mode="determinate"
[value]="fileDownloadItem.downloadingProgress$ | async"
></mat-progress-bar>
<div class="ucap-binary-viewer-body">
<div
class="ucap-binary-viewer-content-wrapper"
fxLayout="column"
fxLayout.xs="row"
fxFlexFill
fxLayoutAlign="center center"
>
<div class="ucap-binary-viewer-content-wrapper" fxLayout="column" fxLayout.xs="row" fxFlexFill
fxLayoutAlign="center center">
<div class="circle-box">
<div [ngClass]="['mime-icon', 'light', 'ico-' + fileInfo.fileExt]">
<div class="ico"></div>
</div>
<div>
</div>
<div class="guide-msg">
미리보기를 지원하지 않는 파일입니다.
</div>
<div>
<button
color="warn"
mat-raised-button
aria-label=""
(click)="onClickDownload()"
>
<button colori mat-raised-button aria-label="" (click)="onClickDownload()">
Download
</button>
</div>

View File

@ -4,29 +4,61 @@
.ucap-binary-viewer-header {
width: 100%;
height: 50px;
height: 60px;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.6);
background-color: #333333;
color:#ffffff;
.ucap-binary-viewer-icon {
margin-right: 10px;
}
.ucap-binary-viewer-title {
font-size: 16px;
}
.ucap-binary-viewer-spacer {
flex: 1 1 auto;
}
.stroke-bar {
width: 1px;
height: 30px;
background-color: rgba(256, 256, 256, 0.3);
margin: 0 10px;
}
.ucap-binary-viewer-action {
margin-left: auto;
&:hover {
opacity: 0.7;
}
}
}
.ucap-binary-viewer-body {
position: relative;
width: 100%;
height: 100%;
background-color: white;
height: calc(100% - 60px);
.ucap-binary-viewer-content-wrapper {
.ucap-image-viewer-image-wrapper {
height: 100%;
padding: 20px;
background-color: rgba(0, 0, 0, 0.9);
}
.circle-box{
display:flex;
width:140px;
height:140px;
border-radius:50%;
justify-content: center;
align-items: center;
border:2px solid #ffffff;
background-color:rgba(256, 256, 256, 0.6);
}
.guide-msg{
font-size:16px;
margin:30px;
color:#ffffff;
}
}
}