Merge pull request #566 from Alfresco/dev-mromano-538

Dev mromano 538
This commit is contained in:
Mario Romano
2016-08-13 20:06:16 +01:00
committed by GitHub
45 changed files with 2680 additions and 124 deletions

View File

@@ -11,16 +11,16 @@
<section class="mdl-layout__tab-panel is-active" id="scroll-tab-1">
<div class="page-content">
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--2-col task-column">
<div class="mdl-cell mdl-cell--2-col task-column mdl-shadow--2dp">
<span>Task Filters</span>
<activiti-filters (filterClick)="onFilterClick($event)"></activiti-filters>
</div>
<div class="mdl-cell mdl-cell--3-col task-column">
<div class="mdl-cell mdl-cell--3-col task-column mdl-shadow--2dp">
<span>Task List</span>
<activiti-tasklist *ngIf="isTaskListSelected()" [taskFilter]="taskFilter" [schemaColumn]="schemaColumn"
(rowClick)="onRowClick($event)" #activititasklist></activiti-tasklist>
</div>
<div class="mdl-cell mdl-cell--7-col task-column">
<div class="mdl-cell mdl-cell--7-col task-column mdl-shadow--2dp">
<span>Task Details</span>
<activiti-task-details [taskId]="currentTaskId" #activitidetails></activiti-task-details>
</div>

View File

@@ -0,0 +1,49 @@
.upload-border {
position: relative;
height: 200px;
width: 200px;
padding: 5px 5px
}
.drag-area {
width: 200px;
height: 100px;
border: 1px solid #888888;
}
.image-cell{
width: 100px;
height: 100px;
border-radius: 50px;
text-align: center;
position: absolute;
top: 0;
margin-left: -50px;
border: 3px #1c597f solid;
z-index: 99;
margin-top: 45px;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);
}
.container-title-details{
background-color: #1c597f;
color: #ffffff;
}
.title-details {
margin: auto;
text-align: center;
}
.user-image-wrap{
background-color: #1c597f;
text-align: center;
height: 60px;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);
}
.upload-photo-button{
text-align: center;
height: 50px;
margin-top: 35px;
}

View File

@@ -0,0 +1,20 @@
<div class="mdl-card__title title container-title-details">
<h2 class="mdl-card__title-text title-details">Create Patient</h2>
</div>
<div class="user-image-wrap">
<img class="image-cell"
src="{{imgSrc}}">
</div>
<div class="upload-photo-button">
<alfresco-upload-button [showUdoNotificationBar]="true"
[uploaddirectory]="'Shared'"
[currentFolderPath]="'Shared'"
[uploadFolders]="false"
[multipleFiles]="false"
[acceptedFilesType]="'.jpg,.gif,.png,.svg'"
[staticTitle]="'Upload Picture'"
(onSuccess)="fileUploaded($event)">
</alfresco-upload-button>
</div>
<activiti-form [formName]="'Health_User_Form'" (saveOption)='saveMetadata($event)' [data]="metadata" [showTitle]="false"
[showRefreshButton]="false" ></activiti-form>

View File

@@ -0,0 +1,101 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component } from '@angular/core';
import { AlfrescoAuthenticationService, AlfrescoSettingsService } from 'ng2-alfresco-core';
import { FormService, ActivitiForm } from 'ng2-activiti-form';
import { Router } from '@angular/router';
import { NotificationService } from '../../services/notification.service';
import { ALFRESCO_ULPOAD_COMPONENTS } from 'ng2-alfresco-upload';
declare let __moduleName: string;
declare let AlfrescoApi: any;
@Component({
moduleId: __moduleName,
selector: 'createpatient-component',
templateUrl: './createpatient.component.html',
styleUrls: ['./createpatient.component.css'],
providers: [FormService],
directives: [ALFRESCO_ULPOAD_COMPONENTS, ActivitiForm]
})
export class CreatePatientComponent {
currentPath: string = '/Sites/swsdp/documentLibrary';
metadata: any = {};
photoNode: string = "";
imgSrc: string = "app/img/anonymous.gif";
constructor(private authService: AlfrescoAuthenticationService, private router: Router,
private notificationService: NotificationService,
private alfrescoSettingsService: AlfrescoSettingsService) {
}
public fileUploaded(data) {
if (data && data.value) {
this.photoNode = data.value.entry.id;
this.imgSrc = this.alfrescoSettingsService.ecmHost + '/alfresco/api/-default-/public/alfresco/versions/1/nodes/' + data.value.entry.id + '/content?attachment=false';
console.log(this.photoNode);
}
}
saveMetadata(data: any) {
let name = '';
if (!this.photoNode) {
name = this.generateUuid();
} else {
name = this.photoNode;
}
let body = {
name: name,
nodeType: 'hc:patientFolder',
properties: {},
relativePath: this.currentPath
};
for (var key in data) {
if (data[key]) {
body.properties['hc:' + key] = data[key];
}
}
let opts = {};
let self = this;
this.authService.getAlfrescoApi().nodes.addNode('-root-', body, opts).then(
(data) => {
console.log('The folder created', data);
self.router.navigate(['/patients']);
this.notificationService.sendNotification('User Created');
},
(err) => {
window.alert('See console output for error details');
console.log(err);
}
);
}
private generateUuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
}

View File

@@ -0,0 +1,23 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export class PatientModel {
folderName: string;
firstName: string;
lastName: string;
doctor: string;
}

View File

@@ -0,0 +1,30 @@
.image-cell{
width: 100px;
height: 100px;
border-radius: 50px;
text-align: center;
position: absolute;
top: 0;
margin-left: -50px;
border: 3px #1c597f solid;
z-index: 99;
margin-top: 45px;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);
}
.container-title-details{
background-color: #1c597f;
color: #ffffff;
}
.title-details {
margin: auto;
text-align: center;
}
.user-image-wrap{
background-color: #1c597f;
text-align: center;
height: 60px;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);
}

View File

@@ -0,0 +1,14 @@
<div class="mdl-card__title title container-title-details">
<h2 class="mdl-card__title-text title-details">Patient Details</h2>
</div>
<div class="user-image-wrap">
<img class="image-cell"
src="{{alfrescoSettingsService.ecmHost}}/alfresco/api/-default-/public/alfresco/versions/1/nodes/{{photoNodeId}}/content?attachment=false&alf_ticket={{ticket}}">
</div>
<activiti-form
[readOnly]="true"
[formName]="'Health_User_Form'"
[data]="metadata"
[showTitle]="false"
[showRefreshButton]="false">
</activiti-form>

View File

@@ -0,0 +1,82 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, OnInit, OnDestroy } from '@angular/core';
import { AlfrescoAuthenticationService, AlfrescoSettingsService } from 'ng2-alfresco-core';
import { FormService, ActivitiForm } from 'ng2-activiti-form';
import { ActivatedRoute, Router } from '@angular/router';
import { Subscription } from 'rxjs/Rx';
declare let __moduleName: string;
declare let AlfrescoApi: any;
@Component({
moduleId: __moduleName,
selector: 'patient-details',
templateUrl: './patientdetails.component.html',
styleUrls: ['./patientdetails.component.css'],
providers: [FormService],
directives: [ActivitiForm]
})
export class PatientDetailsComponent implements OnInit, OnDestroy {
private sub: Subscription;
currentPath: string = '/Sites/swsdp/documentLibrary';
metadata: any = {};
nodeId: string;
photoNodeId: string;
ticket: string = localStorage.getItem('ticket-ECM');
constructor(private route: ActivatedRoute,
private router: Router,
private authService: AlfrescoAuthenticationService,
public alfrescoSettingsService: AlfrescoSettingsService) {
}
ngOnInit() {
this.sub = this.route.params.subscribe(params => {
this.retriveNodeMetadataFromEcm(params['id']);
});
}
ngOnDestroy() {
if (this.sub) {
this.sub.unsubscribe();
}
}
private retriveNodeMetadataFromEcm(nodeId: string): void{
var self = this;
this.nodeId = nodeId;
this.authService.getAlfrescoApi().nodes.getNodeInfo(this.nodeId).then(function (data) {
console.log(data.properties);
self.photoNodeId = data.name;
for (var key in data.properties) {
console.log(key + ' => ' + data[key]);
self.metadata [key.replace('hc:','')] = data.properties[key];
}
}, function (error) {
console.log('This node does not exist');
});
}
}

View File

@@ -0,0 +1,72 @@
.container {
/*margin: 10px;*/
}
@media only screen and (max-width: 640px) {
.container {
margin: 0;
}
}
.patients-debug-container {
padding: 10px;
}
.patients-debug-container .debug-toggle-text {
padding-left: 15px;
cursor: pointer;
}
.patients-debug-container .debug-toggle-text:hover {
font-weight: bold;
}
alfresco-document-list >>> .image-cell {
width: 50px;
height: 50px;
cursor: default;
border: 2px solid #aaaaaa;
border-radius: 30px;
}
alfresco-document-list >>> .mdl-data-table {
border: 0px;
}
.image-cell{
background-color: #ffffff;
width: 100px;
height: 100px;
border-radius: 50px;
text-align: center;
position: absolute;
top: 17px;
margin-left: -50px;
border: 3px #1c597f solid;
z-index: 99;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);
}
.container-title-details{
background-color: #1c597f;
color: #ffffff;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);
}
.title-details {
margin: auto;
text-align: center;
}
.user-image-wrap{
background-color: #1c597f;
text-align: center;
height: 64px;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);
}
@media only screen and (max-width: 840px) {
#tags {
width: 100%;
}
}

View File

@@ -0,0 +1,218 @@
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--2-col mdl-shadow--2dp" id="tags">
<div class="mdl-card__title title container-title-details">
<h2 class="mdl-card__title-text title-details">Tags</h2>
</div>
<ul class="mdl-list">
<li *ngFor="let filter of tagFilters" class="mdl-list__item">
<span class="mdl-list__item-primary-content">
<label [attr.for]="'chk-' + filter.id"
class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect"
[class.is-checked]="filter.isSelected">
<input type="checkbox"
[attr.id]="'chk-' + filter.id"
class="mdl-checkbox__input"
[(ngModel)]="filter.isSelected"
(change)="onFilterChanged($event)">
<span class="mdl-checkbox__label">{{filter.tag}}</span>
</label>
</span>
<!--<span class="mdl-list__item-secondary-action">(0)</span>-->
</li>
</ul>
</div>
<div class="mdl-cell mdl-shadow--2dp"
[class.mdl-cell--10-col]="!selectedNodeProperties"
[class.mdl-cell--8-col]="selectedNodeProperties">
<div style="padding-bottom: 4px;">
<button
class="mdl-button mdl-js-button mdl-button--icon"
(click)="documentList.currentFolderPath = '/Sites/swsdp/documentLibrary';">
<i class="material-icons">home</i>
</button>
</div>
<div class="container">
<alfresco-upload-drag-area
[showUploadDialog]="true"
[currentFolderPath]="currentPath"
[uploaddirectory]=""
(onSuccess)="documentList.reload()">
<alfresco-document-list
#documentList
[currentFolderPath]="currentPath"
[contextMenuActions]="true"
[contentActions]="true"
[rowFilter]="tagFilter"
[imageResolver]="folderImageResolver"
(preview)="showFile($event)"
(folderChange)="onFolderChanged($event)"
(nodeClick)="onNodeClicked($event)">
<empty-folder-content>
<template>
<div style="text-align: center">
<h2>No patient data available.</h2>
<p>
<img src="/app/img/users.png" style="width: 200px;">
</p>
</div>
</template>
</empty-folder-content>
<!--
<content-columns>
<content-column key="name" type="image"></content-column>
<content-column
title="First Name"
key="properties.hc:firstName"
sortable="true"
class="desktop-only">
</content-column>
<content-column
title="Last Name"
key="properties.hc:lastName"
sortable="true"
class="desktop-only">
</content-column>
<content-column
title="Doctor"
key="properties.hc:doctor"
sortable="true"
class="desktop-only">
</content-column>
<content-column
title="{{'DOCUMENT_LIST.COLUMNS.CREATED_ON' | translate}}"
key="createdAt"
type="date"
format="shortDate"
sortable="true"
class="desktop-only">
</content-column>
</content-columns>
-->
<content-actions>
<!-- folder actions -->
<content-action
target="folder"
title="View details"
(execute)="patientDetails($event)">
</content-action>
<content-action *ngIf="isAdmin()"
target="folder"
title="Schedule an appointment"
(execute)="scheduleAppointment($event)">
</content-action>
<content-action target="folder"
title="Add tag"
(execute)="addTag($event)">
</content-action>
<content-action *ngIf="isAdmin()"
target="folder"
title="{{'DOCUMENT_LIST.ACTIONS.FOLDER.DELETE' | translate}}"
handler="delete">
</content-action>
<!-- document actions -->
<content-action
target="document"
title="{{'DOCUMENT_LIST.ACTIONS.DOCUMENT.DOWNLOAD' | translate}}"
handler="download">
</content-action>
<content-action *ngIf="isAdmin()"
target="document"
title="{{'DOCUMENT_LIST.ACTIONS.DOCUMENT.DELETE' | translate}}"
handler="delete">
</content-action>
</content-actions>
</alfresco-document-list>
<alfresco-pagination
[provider]="documentList.data"
[supportedPageSizes]="[5, 10, 15, 20]">
</alfresco-pagination>
</alfresco-upload-drag-area>
</div>
</div>
<div *ngIf="selectedNodeProperties" class="mdl-cell mdl-cell--2-col mdl-shadow--2dp">
<div class="user-image-wrap">
<img _ngcontent-ksd-12="" class="image-cell" onerror="this.onerror=null;this.src='app/img/anonymous.gif';"
[src]="detailsAvatarImage">
</div>
<ul class="mdl-list">
<li *ngFor="let prop of selectedNodeProperties" class="mdl-list__item mdl-list__item--two-line">
<span class="mdl-list__item-primary-content">
<span [title]="prop.fullName">{{prop.name}}</span>
<span class="mdl-list__item-sub-title">{{prop.value}}</span>
</span>
</li>
</ul>
</div>
</div>
<!-- Example for creating a custom folder type with properties -->
<!--
<div class="mdl-grid">
<div class="md-cell mdl-cell--12-col">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" id="patient-folderName" [(ngModel)]="newPatient.folderName">
<label class="mdl-textfield__label" for="patient-folderName">Folder Name</label>
</div>
</div>
<div class="md-cell mdl-cell--12-col">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" id="patient-firstName" [(ngModel)]="newPatient.firstName">
<label class="mdl-textfield__label" for="patient-firstName">First Name</label>
</div>
</div>
<div class="md-cell mdl-cell--12-col">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" id="patient-lastName" [(ngModel)]="newPatient.lastName">
<label class="mdl-textfield__label" for="patient-lastName">Last Name</label>
</div>
</div>
<div class="md-cell mdl-cell--12-col">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" id="patient-doctor" [(ngModel)]="newPatient.doctor">
<label class="mdl-textfield__label" for="patient-doctor">Doctor</label>
</div>
</div>
<div class="mdl-cell mdl-cell--12-col">
<button
class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect"
(click)="onCreateNewPatientClick()">
Create Patient
</button>
</div>
</div>
-->
<dialog class="mdl-dialog">
<h4 class="mdl-dialog__title">Set Tags</h4>
<div class="mdl-dialog__content">
<div class="md-cell mdl-cell--12-col">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" id="node-tags" >
<label class="mdl-textfield__label" for="node-tags">Tags</label>
</div>
</div>
</div>
<div class="mdl-dialog__actions">
<button type="button" class="mdl-button save">Save</button>
<button type="button" class="mdl-button close">Close</button>
</div>
</dialog>
<!--
For debugging and data visualisation purposes,
will be removed during future revisions
-->
<context-menu-holder></context-menu-holder>
<alfresco-viewer [(showViewer)]="fileShowed"
[urlFile]="urlFile"
[fileName]="fileName"
[mimeType]="mimeType"
[overlayMode]="true">
<div class="mdl-spinner mdl-js-spinner is-active"></div>
</alfresco-viewer>
<file-uploading-dialog #fileDialog></file-uploading-dialog>

View File

@@ -0,0 +1,402 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, OnInit, ViewChild, ChildNode } from '@angular/core';
import { Router } from '@angular/router';
import {
PaginationComponent,
DataColumn,
DataRow,
ObjectDataColumn
} from 'ng2-alfresco-datatable';
import {
DOCUMENT_LIST_DIRECTIVES,
DOCUMENT_LIST_PROVIDERS,
DocumentList,
ShareDataRow,
RowFilter,
MinimalNodeEntity,
ImageResolver
} from 'ng2-alfresco-documentlist';
import {
MDL,
AlfrescoContentService,
CONTEXT_MENU_DIRECTIVES,
AlfrescoPipeTranslate,
AlfrescoSettingsService
} from 'ng2-alfresco-core';
import { ALFRESCO_ULPOAD_COMPONENTS } from 'ng2-alfresco-upload';
import { VIEWERCOMPONENT } from 'ng2-alfresco-viewer';
import { FormService } from 'ng2-activiti-form';
import { PatientModel } from './patient.model';
import { TagModel, TagCache, TagFilter } from './tag.model';
import { TagService } from './tag.service';
declare let __moduleName: string;
@Component({
moduleId: __moduleName,
selector: 'patients-component',
templateUrl: './patients.component.html',
styleUrls: ['./patients.component.css'],
directives: [
DOCUMENT_LIST_DIRECTIVES,
MDL,
ALFRESCO_ULPOAD_COMPONENTS,
VIEWERCOMPONENT,
CONTEXT_MENU_DIRECTIVES,
PaginationComponent
],
providers: [DOCUMENT_LIST_PROVIDERS, FormService, TagService],
pipes: [AlfrescoPipeTranslate]
})
export class PatientsComponent implements OnInit {
private DEFAULT_PATH: string = '/Sites/swsdp/documentLibrary';
currentPath: string = this.DEFAULT_PATH;
urlFile: string;
fileName: string;
mimeType: string;
fileShowed: boolean = false;
acceptedFilesType: string = '.jpg,.pdf,.js';
@ViewChild(DocumentList)
documentList: DocumentList;
newPatient: PatientModel;
debugMode: boolean = false;
tags: TagCache = {};
tagFilters: TagFilter[] = [];
selectedNode: MinimalNodeEntity;
selectedNodeProperties: NodePropertyModel[] = [];
selectedNodePropertiesName: string;
tagFilter: RowFilter;
folderImageResolver: ImageResolver;
ticket: string = localStorage.getItem('ticket-ECM');
ecmHost: string;
detailsAvatarImage: string;
isVisitFolder: boolean = false;
private patientLayout: DataColumn[] = [];
private fileLayout: DataColumn[] = [];
emptyimgSrc: string = "app/img/anonymous.gif";
constructor(private contentService: AlfrescoContentService,
private router: Router,
private tagService: TagService,
private alfrescoSettingsService: AlfrescoSettingsService) {
this.newPatient = new PatientModel();
this.ecmHost = alfrescoSettingsService.ecmHost;
this.tagFilter = (row: ShareDataRow) => {
let selectedTags = this.tagFilters
.filter(f => f.isSelected)
.map(f => f.id);
if (selectedTags.length > 0) {
let properties = row.node.entry.properties;
if (properties) {
let tags = properties['cm:taggable'];
if (tags && tags instanceof Array && tags.length > 0) {
let result = false;
for (let i = 0; i < selectedTags.length; i++) {
if (tags.indexOf(selectedTags[i]) > -1) {
result = true;
break;
}
}
return result;
}
}
return false;
}
return true;
};
this.folderImageResolver = (row: DataRow, col: DataColumn) => {
let isFolder = <boolean> row.getValue('isFolder');
if (isFolder) {
let value = row.getValue(col.key);
return this.alfrescoSettingsService.ecmHost + `/alfresco/api/-default-/public/alfresco/versions/1/nodes/` +
value + '/content?attachment=false&alf_ticket=' + this.ticket;
}
return null;
};
this.patientLayout = this.getPatientLayout();
this.fileLayout = this.getFileLayout();
}
isAdmin() {
if (localStorage.getItem(`username`) === 'admin') {
return true;
} else {
return false;
}
}
resetFilters() {
if (this.tagFilters && this.tagFilters.length > 0) {
this.tagFilters.map(f => f.isSelected = false);
this.documentList.reload();
}
}
patientDetails(event: any) {
this.router.navigate(['/patientdetails', event.value.entry.id]);
}
showFile(event) {
if (event.value.entry.isFile) {
this.fileName = event.value.entry.name;
this.mimeType = event.value.entry.content.mimeType;
this.urlFile = this.contentService.getContentUrl(event.value);
this.fileShowed = true;
} else {
this.fileShowed = false;
}
}
onFolderChanged(event?: any) {
if (event) {
this.selectedNode = null;
this.selectedNodeProperties = null;
this.currentPath = event.path;
this.loadTags();
if (this.currentPath === this.DEFAULT_PATH) {
this.folderImageResolver = (row: DataRow, col: DataColumn) => {
let isFolder = <boolean> row.getValue('isFolder');
if (isFolder) {
let value = row.getValue(col.key);
return this.alfrescoSettingsService.ecmHost + `/alfresco/api/-default-/public/alfresco/versions/1/nodes/` +
value + '/content?attachment=false&alf_ticket=' + this.ticket;
}
return null;
};
this.documentList.data.setColumns(this.patientLayout);
this.isVisitFolder = false;
} else {
this.documentList.data.setColumns(this.fileLayout);
this.folderImageResolver = (row: DataRow, col: DataColumn) => {
return 'app/img/checklist.svg';
};
this.isVisitFolder = true;
}
}
}
ngOnInit() {
// this.loadTags();
}
onNodeClicked(event?: any) {
console.log(event);
if (event && event.value) {
this.selectedNodeProperties = null;
this.selectedNode = <MinimalNodeEntity> event.value;
this.selectedNodePropertiesName = event.value.entry.name;
if(this.isVisitFolder){
this.detailsAvatarImage = 'app/img/checklist.svg';
}else{
this.detailsAvatarImage = this.ecmHost + '/alfresco/api/-default-/public/alfresco/versions/1/nodes/' + this.selectedNodePropertiesName + '/content?attachment=false&alf_ticket=' + this.ticket;
}
if (this.selectedNode) {
this.selectedNodeProperties = this.getNodeProperties(this.selectedNode);
console.log(this.selectedNodeProperties);
}
}
}
onFilterChanged(event) {
setTimeout(() => {
this.documentList.reload();
}, 500);
}
addTag(event){
let self = this;
let nodeId = event.value.entry.id;
let dialog = document.querySelector('dialog');
if (! dialog.showModal) {
dialogPolyfill.registerDialog(dialog);
}
dialog.showModal();
dialog.querySelector('.close').addEventListener('click', function() {
dialog.close();
});
dialog.querySelector('.save').addEventListener('click', function() {
self.setNodeTags(nodeId, dialog.querySelector('#node-tags').value);
dialog.close();
});
}
setNodeTags(nodeId: string, value: string) {
if (nodeId && value) {
let tags = value.split(',').map(val => {
return {
tag: val.trim()
}
});
this.tagService.addTags(nodeId, tags).then(
data => {
console.log(data);
// TODO: share seems to have issues with returning newly created tags
// it sometimes takes several seconds for changes to global tags to propagate
// and become visible
/*
this.getTags().then(
res => {
console.log('after tags updated');
console.log(res);
this.tags = res || [];
this.documentList.reload();
},
this.handleError
);
*/
},
this.handleError
);
}
}
scheduleAppointment(event?: any) {
this.router.navigate(['/startvisit', event.value.entry.id]);
}
private getNodeProperties(node: MinimalNodeEntity): NodePropertyModel[] {
let result = [];
if (node && node.entry && node.entry.properties) {
let props = node.entry.properties;
Object.keys(props).forEach(key => {
result.push(new NodePropertyModel(key, props[key]));
});
}
return result;
}
private loadTags() {
this.tagService.getTags().then(
(tags: TagModel[]) => {
this.tagFilters = tags.map((tag) => new TagFilter(tag.id, tag.tag));
tags.forEach(tag => this.tags[tag.id] = tag);
},
this.handleError
);
}
private handleError(err) {
console.log(err);
}
private getPatientLayout(): DataColumn[] {
return [
new ObjectDataColumn({
key: 'name',
type: 'image'
}),
new ObjectDataColumn({
title: 'First Name',
key: 'properties.hc:firstName',
sortable: true,
cssClass: 'desktop-only'
}),
new ObjectDataColumn({
title: 'Last Name',
key: 'properties.hc:lastName',
sortable: true,
cssClass: 'desktop-only'
}),
new ObjectDataColumn({
title: 'Doctor',
key: 'properties.hc:doctor',
sortable: true,
cssClass: 'desktop-only'
}),
new ObjectDataColumn({
title: 'Created On',
key: 'createdAt',
type: 'date',
format: 'shortDate',
sortable: true,
cssClass: 'desktop-only'
})
];
}
private getFileLayout(): DataColumn[] {
return [
new ObjectDataColumn({
key: '$thumbnail',
type: 'image'
}),
new ObjectDataColumn({
title: 'Display Name',
key: 'name',
sortable: true,
cssClass: 'full-width ellipsis-cell'
}),
new ObjectDataColumn({
title: 'Created By',
key: 'createdByUser.displayName',
sortable: true,
cssClass: 'desktop-only'
}),
new ObjectDataColumn({
title: 'Created On',
key: 'createdAt',
type: 'date',
format: 'shortDate',
sortable: true,
cssClass: 'desktop-only'
})
];
}
}
class NodePropertyModel {
prefix: string;
name: string;
value: string;
fullName: string;
constructor(name: string, value: string) {
this.fullName = name;
this.name = name;
if (name) {
let idx = name.indexOf(':');
if (idx > -1) {
this.prefix = name.substring(0, idx);
this.name = name.substring(idx + 1);
}
}
this.value = value;
}
}

View File

@@ -0,0 +1,39 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export class TagModel {
id: string;
tag: string;
constructor(id: string, tag: string) {
this.id = id;
this.tag = tag;
}
}
export interface TagCache {
[key: string]: TagModel;
}
export class TagFilter extends TagModel {
count: number = 0;
isSelected: boolean = false;
constructor(id: string, tag: string) {
super(id, tag);
}
}

View File

@@ -0,0 +1,49 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { AlfrescoAuthenticationService } from 'ng2-alfresco-core';
import { TagModel } from './tag.model';
@Injectable()
export class TagService {
constructor(private authService: AlfrescoAuthenticationService) {}
getTags(): Promise<TagModel[]> {
return new Promise<TagModel[]>((resolve, reject) => {
this.authService.getAlfrescoApi().core.tagsApi.getTags({}).then(
data => {
let entries = data.list.entries || [];
let tags = entries.map(obj => <TagModel> obj.entry);
resolve(tags);
},
err => reject(err)
);
});
}
addTags(nodeId: string, tags: { tag: string }[]): Promise<any> {
return new Promise<any>((resolve, reject) => {
this.authService.getAlfrescoApi().core.tagsApi.addTag(nodeId, tags).then(
data => resolve(data),
err => reject(err)
);
});
}
}

View File

@@ -0,0 +1,12 @@
export interface Process {
category: string;
deploymentId: string;
description: string;
hasStartForm: boolean;
id: string;
key: string;
name: string;
tenantId: string;
version: number;
metaDataValues: any[];
}

View File

@@ -0,0 +1,121 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { Response, Http, Headers, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { AlfrescoAuthenticationService, AlfrescoSettingsService } from 'ng2-alfresco-core';
import { Process } from './process.data';
@Injectable()
export class ProcessService {
constructor(private http: Http,
private authService: AlfrescoAuthenticationService,
private alfrescoSettingsService: AlfrescoSettingsService) {
}
getDeployedApplications(name: string): Observable<Process[]> {
let url = `${this.alfrescoSettingsService.bpmHost}/activiti-app/api/enterprise/runtime-app-definitions`;
let options = this.getRequestOptions();
return this.http
.get(url, options)
.map((response: Response) => response.json().data.find(p => p.name === name))
.do(data => console.log('Application: ' + JSON.stringify(data)))
.catch(this.handleError);
}
getProcessDefinitions(): Observable<Process[]> {
let url = `${this.alfrescoSettingsService.bpmHost}/activiti-app/api/enterprise/process-definitions`;
let options = this.getRequestOptions();
return this.http
.get(url, options)
.map((response: Response) => <Process[]> response.json())
.do(data => console.log('All: ' + JSON.stringify(data)))
.catch(this.handleError);
}
getProcessDefinitionByApplication(application: any): Observable<Process> {
return this.getProcessDefinitions()
.map((processes: Process[]) => <Process> processes.data.find(p => p.deploymentId === application.deploymentId));
}
getStartFormForProcess(processDefinitionId: string): Observable<any> {
let url = `${this.alfrescoSettingsService.bpmHost}/activiti-app/api/enterprise/process-definitions/${processDefinitionId}/start-form`;
let options = this.getRequestOptions();
return this.http
.get(url, options)
.map((response: Response) => response.json())
.catch(this.handleError);
}
startProcessByID(processDefinitionId: string, processName: string): void {
let url = `${this.alfrescoSettingsService.bpmHost}/activiti-app/api/enterprise/process-instances`;
let options = this.getRequestOptions();
let body = JSON.stringify({processDefinitionId: processDefinitionId, name: processName});
console.log(body);
return this.http
.post(url, body, options)
.map(this.toJson)
.catch(this.handleError);
}
getTaskIdFromProcessID(processDefinitionId: string, appDefinitionId: string, processInstanceId: string): Observable<any> {
let url = `${this.alfrescoSettingsService.bpmHost}/activiti-app/api/enterprise/tasks/query`;
let options = this.getRequestOptions();
let body = JSON.stringify({
processDefinitionId: processDefinitionId,
appDefinitionId: appDefinitionId,
processInstanceId: processInstanceId
});
console.log(body);
return this.http
.post(url, body, options)
.map(this.toJson)
.catch(this.handleError);
}
private getHeaders(): Headers {
return new Headers({
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': this.authService.getTicket('BPM')
});
}
private getRequestOptions(): RequestOptions {
let headers = this.getHeaders();
return new RequestOptions({headers: headers});
}
private toJson(res: Response) {
let body = res.json();
return body || {};
}
private handleError(error: any) {
console.log("ERROR");
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg);
return Observable.throw(errMsg);
}
}

View File

@@ -0,0 +1,39 @@
.image-cell{
width: 100px;
height: 100px;
border-radius: 50px;
text-align: center;
position: absolute;
top: 0;
margin-left: -50px;
border: 3px #1c597f solid;
z-index: 99;
margin-top: 45px;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);
}
.container-title-details{
background-color: #1c597f;
color: #ffffff;
}
.title-details {
margin: auto;
text-align: center;
}
.user-image-wrap{
background-color: #1c597f;
text-align: center;
height: 60px;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);
}
.upload-photo-button{
text-align: center;
height: 50px;
margin-top: 35px;
}
.wrap-form{
margin-top: 35px;
}

View File

@@ -0,0 +1,10 @@
<div class="mdl-card__title title container-title-details">
<h2 class="mdl-card__title-text title-details">Schedule Appointment</h2>
</div>
<div class="user-image-wrap">
<img class="image-cell"
src="app/img/createvisit.jpg">
</div>
<div class="wrap-form ">
<activiti-form [taskId]="taskId" [data]="metadata" [showRefreshButton]="false" (completeOption)='saveData($event)' [showTitle]="false"></activiti-form>
</div>

View File

@@ -0,0 +1,134 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component } from '@angular/core';
import { AlfrescoAuthenticationService } from 'ng2-alfresco-core';
import { ActivatedRoute, Router } from '@angular/router';
import { Subscription } from 'rxjs/Rx';
import { ProcessService } from './process.service';
import { Process } from './process.data';
import { FormService, ActivitiForm } from 'ng2-activiti-form';
import { NotificationService } from '../../services/notification.service';
declare let __moduleName: string;
declare let AlfrescoApi: any;
@Component({
moduleId: __moduleName,
selector: 'start-visit-component',
templateUrl: './start-visit.component.html',
styleUrls: ['./start-visit.component.css'],
providers: [ProcessService, FormService],
directives: [ActivitiForm]
})
export class StartVisitComponent {
private sub: Subscription;
currentPath: string = '/Sites/swsdp/documentLibrary';
metadata: any = {};
nodeId: string;
nodeName: string;
errorMessage: string;
processName: string = "TEST";
process: Process;
taskId: string;
constructor(private route: ActivatedRoute,
private router: Router,
private processService: ProcessService,
private authService: AlfrescoAuthenticationService,
private notificationService: NotificationService) {
}
ngOnInit() {
this.sub = this.route.params.subscribe(params => {
this.retriveNodeMetadataFromEcm(params['id']);
});
let self = this;
this.processService.getDeployedApplications("Visit").subscribe(
application => {
console.log("I'm the application hello", application);
this.processService.getProcessDefinitionByApplication(application).subscribe(
process => {
console.log("this is the process", process);
self.processService.startProcessByID(process.id, process.name).subscribe(
startedProcess => {
console.log(startedProcess);
this.processService.getTaskIdFromProcessID(process.id, application.id, startedProcess.id).subscribe(
response => {
console.log(response.data[0].id);
self.taskId = response.data[0].id;
},
error => {
console.log(error)
}
);
},
error => {
console.log(error);
}
);
},
error => this.errorMessage = <any>error
);
console.log(application);
},
error => this.errorMessage = <any>error
);
}
public saveData(){
this.router.navigate(['/patients']);
this.notificationService.sendNotification('New Visit Created');
}
private retriveNodeMetadataFromEcm(nodeId: string): void {
var self = this;
this.nodeId = nodeId;
this.authService.getAlfrescoApi().nodes.getNodeInfo(this.nodeId).then(function (data) {
console.log(data.properties);
self.nodeName = data.name;
for (var key in data.properties) {
console.log(key + ' => ' + data[key]);
self.metadata [key.replace('hc:', '')] = data.properties[key];
}
self.metadata.nodeId = self.nodeName;
}, function (error) {
console.log('This node does not exist');
});
}
private generateUuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 KiB

View File

@@ -0,0 +1,782 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="110px"
height="110px" viewBox="0 0 110 110" style="enable-background:new 0 0 110 110;" xml:space="preserve">
<g id="Artboard" style="display:none;">
<rect x="-443" y="-79" style="display:inline;fill:#8C8C8C;" width="1412" height="743"/>
</g>
<g id="R-Multicolor" style="display:none;">
<circle style="display:inline;fill:#E04F5F;" cx="55" cy="55" r="55"/>
<g style="display:inline;">
<path style="fill:#FFFFFF;" d="M23,26.84c0-2.121,1.719-3.84,3.84-3.84H55l16,13.44v46.72c0,2.121-1.719,3.84-3.84,3.84H26.84
C24.719,87,23,85.281,23,83.16V26.84z"/>
<path style="fill:#F0F1F1;" d="M55,23v9.6c0,2.121,1.719,3.84,3.84,3.84H71L55,23z"/>
<polygon style="fill:#F4D0A1;" points="77,41.5 82,31 87,41.5 87,80 77,80 "/>
<polygon style="fill:#EDBC7C;" points="87,47 87,41.5 82,31 82,47 "/>
<path style="fill:#F79392;" d="M87,84c0,1.657-1.343,3-3,3h-4c-1.657,0-3-1.343-3-3v-2h10V84z"/>
<path style="fill:#E2E4E5;" d="M62,45c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15C61.55,44,62,44.45,62,45
L62,45z"/>
<path style="fill:#E2E4E5;" d="M56,50c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,49,56,49.45,56,50
L56,50z"/>
<path style="fill:#E2E4E5;" d="M62,58c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15C61.55,57,62,57.45,62,58
L62,58z"/>
<path style="fill:#E2E4E5;" d="M56,63c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,62,56,62.45,56,63
L56,63z"/>
<path style="fill:#E2E4E5;" d="M62,71c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15C61.55,70,62,70.45,62,71
L62,71z"/>
<path style="fill:#E2E4E5;" d="M56,76c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,75,56,75.45,56,76
L56,76z"/>
<g>
<path style="fill:#17B198;" d="M35.067,52l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.024-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.425,0.984-0.482,1.408-0.128c0.424,0.354,0.482,0.984,0.128,1.408L35.067,52z"/>
</g>
<g>
<path style="fill:#17B198;" d="M35.067,65l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.024-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.425,0.984-0.482,1.408-0.128c0.424,0.354,0.482,0.984,0.128,1.408L35.067,65z"/>
</g>
<g>
<path style="fill:#17B198;" d="M35.067,78l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.024-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.425,0.984-0.482,1.408-0.128c0.424,0.354,0.482,0.984,0.128,1.408L35.067,78z"/>
</g>
<rect x="77" y="79" style="fill:#FACB1B;" width="10" height="3"/>
<rect x="77" y="80" style="fill:#FBE158;" width="5" height="2"/>
<path style="fill:#3E3E3F;" d="M82.013,30.984l-1.857,3.899c0.512,0.678,1.152,1.101,1.857,1.101s1.345-0.422,1.857-1.101
L82.013,30.984z"/>
<path style="fill:#0484AB;" d="M84.5,43c-1.154,0-2.126-0.782-2.413-1.845c-0.026-0.097-0.147-0.097-0.173,0
C81.626,42.218,80.654,43,79.5,43c-1.052,0-1.953-0.65-2.322-1.571C77.139,41.332,77,41.359,77,41.464V80h10V41.464
c0-0.105-0.139-0.131-0.178-0.034C86.453,42.35,85.552,43,84.5,43z"/>
<path style="fill:#21B2D1;" d="M77,80V41.493c0-0.111,0.147-0.139,0.19-0.036C77.565,42.363,78.458,43,79.5,43
c1.159,0,2.134-0.789,2.417-1.859C81.931,41.089,82,41.096,82,41.15V80H77z"/>
<path style="fill:#F7B4B4;" d="M77,82v2c0,1.657,1.343,3,3,3h2v-5H77z"/>
</g>
</g>
<g id="Multicolor">
<g>
<path style="fill:#FFFFFF;" d="M23,26.84c0-2.121,1.719-3.84,3.84-3.84H55l16,13.44v46.72c0,2.121-1.719,3.84-3.84,3.84H26.84
C24.719,87,23,85.281,23,83.16V26.84z"/>
<path style="fill:#F0F1F1;" d="M55,23v9.6c0,2.121,1.719,3.84,3.84,3.84H71L55,23z"/>
<polygon style="fill:#F4D0A1;" points="77,41.5 82,31 87,41.5 87,80 77,80 "/>
<polygon style="fill:#EDBC7C;" points="87,47 87,41.5 82,31 82,47 "/>
<path style="fill:#F79392;" d="M87,84c0,1.657-1.343,3-3,3h-4c-1.657,0-3-1.343-3-3v-2h10V84z"/>
<path style="fill:#E2E4E5;" d="M62,45c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15C61.55,44,62,44.45,62,45
L62,45z"/>
<path style="fill:#E2E4E5;" d="M56,50c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,49,56,49.45,56,50
L56,50z"/>
<path style="fill:#E2E4E5;" d="M62,58c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15C61.55,57,62,57.45,62,58
L62,58z"/>
<path style="fill:#E2E4E5;" d="M56,63c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,62,56,62.45,56,63
L56,63z"/>
<path style="fill:#E2E4E5;" d="M62,71c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15C61.55,70,62,70.45,62,71
L62,71z"/>
<path style="fill:#E2E4E5;" d="M56,76c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,75,56,75.45,56,76
L56,76z"/>
<g>
<path style="fill:#17B198;" d="M35.067,52l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.024-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.425,0.984-0.482,1.408-0.128c0.424,0.354,0.482,0.984,0.128,1.408L35.067,52z"/>
</g>
<g>
<path style="fill:#17B198;" d="M35.067,65l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.024-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.425,0.984-0.482,1.408-0.128c0.424,0.354,0.482,0.984,0.128,1.408L35.067,65z"/>
</g>
<g>
<path style="fill:#17B198;" d="M35.067,78l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.024-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.425,0.984-0.482,1.408-0.128c0.424,0.354,0.482,0.984,0.128,1.408L35.067,78z"/>
</g>
<rect x="77" y="79" style="fill:#FACB1B;" width="10" height="3"/>
<rect x="77" y="80" style="fill:#FBE158;" width="5" height="2"/>
<path style="fill:#3E3E3F;" d="M82.013,30.984l-1.857,3.899c0.512,0.678,1.152,1.101,1.857,1.101s1.345-0.422,1.857-1.101
L82.013,30.984z"/>
<path style="fill:#0484AB;" d="M84.5,43c-1.154,0-2.126-0.782-2.413-1.845c-0.026-0.097-0.147-0.097-0.173,0
C81.626,42.218,80.654,43,79.5,43c-1.052,0-1.953-0.65-2.322-1.571C77.139,41.332,77,41.359,77,41.464V80h10V41.464
c0-0.105-0.139-0.131-0.178-0.034C86.453,42.35,85.552,43,84.5,43z"/>
<path style="fill:#21B2D1;" d="M77,80V41.493c0-0.111,0.147-0.139,0.19-0.036C77.565,42.363,78.458,43,79.5,43
c1.159,0,2.134-0.789,2.417-1.859C81.931,41.089,82,41.096,82,41.15V80H77z"/>
<path style="fill:#F7B4B4;" d="M77,82v2c0,1.657,1.343,3,3,3h2v-5H77z"/>
</g>
</g>
<g id="Blue" style="display:none;">
<g style="display:inline;">
<g>
<path style="fill:#FFFFFF;" d="M23,26.84c0-2.121,1.719-3.84,3.84-3.84H55l16,13.44v46.72c0,2.121-1.719,3.84-3.84,3.84H26.84
C24.719,87,23,85.281,23,83.16V26.84z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M55,23v9.6c0,2.121,1.719,3.84,3.84,3.84H71L55,23z"/>
</g>
<g>
<polygon style="fill:#FFFFFF;" points="77,41.5 82,31 87,41.5 87,80 77,80 "/>
</g>
<g>
<polygon style="fill:#E1F5FA;" points="87,47 87,41.5 82,31 82,47 "/>
</g>
<g>
<path style="fill:#E1F5FA;" d="M87,84c0,1.657-1.343,3-3,3h-4c-1.657,0-3-1.343-3-3v-2h10V84z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M62,45c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,44,62,44.45,62,45L62,45z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M56,50c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,49,56,49.45,56,50
L56,50z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M62,58c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,57,62,57.45,62,58L62,58z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M56,63c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,62,56,62.45,56,63
L56,63z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M62,71c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,70,62,70.45,62,71L62,71z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M56,76c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,75,56,75.45,56,76
L56,76z"/>
</g>
<g>
<g>
<path style="fill:#009FC7;" d="M35.067,52l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,52z"/>
</g>
</g>
<g>
<g>
<path style="fill:#009FC7;" d="M35.067,65l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,65z"/>
</g>
</g>
<g>
<g>
<path style="fill:#009FC7;" d="M35.067,78l-3.774-3.774c-0.391-0.39-0.391-1.023,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,78z"/>
</g>
</g>
<g>
<rect x="77" y="79" style="fill:#0F6175;" width="10" height="3"/>
</g>
<g>
<rect x="77" y="80" style="fill:#33899E;" width="5" height="2"/>
</g>
<g>
<path style="fill:#0C333D;" d="M82.013,30.984l-1.857,3.899c0.512,0.678,1.152,1.101,1.857,1.101s1.345-0.423,1.857-1.101
L82.013,30.984z"/>
</g>
<g>
<path style="fill:#0081A1;" d="M84.5,43c-1.154,0-2.126-0.782-2.414-1.845c-0.026-0.097-0.147-0.097-0.173,0
C81.626,42.218,80.654,43,79.5,43c-1.052,0-1.953-0.65-2.322-1.57C77.139,41.332,77,41.359,77,41.464V80h10V41.464
c0-0.105-0.139-0.132-0.178-0.034C86.453,42.35,85.552,43,84.5,43z"/>
</g>
<g>
<path style="fill:#009FC7;" d="M77,80V41.494c0-0.112,0.146-0.14,0.19-0.037C77.565,42.363,78.458,43,79.5,43
c1.159,0,2.134-0.789,2.416-1.858C81.931,41.089,82,41.096,82,41.149V80H77z"/>
</g>
<g>
<path style="fill:#FFFFFF;" d="M77,82v2c0,1.657,1.343,3,3,3h2v-5H77z"/>
</g>
</g>
</g>
<g id="R-Blue" style="display:none;">
<g style="display:inline;">
<circle style="fill:#81D2EB;" cx="55" cy="55" r="55"/>
</g>
<g style="display:inline;">
<g>
<path style="fill:#FFFFFF;" d="M23,26.84c0-2.121,1.719-3.84,3.84-3.84H55l16,13.44v46.72c0,2.121-1.719,3.84-3.84,3.84H26.84
C24.719,87,23,85.281,23,83.16V26.84z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M55,23v9.6c0,2.121,1.719,3.84,3.84,3.84H71L55,23z"/>
</g>
<g>
<polygon style="fill:#FFFFFF;" points="77,41.5 82,31 87,41.5 87,80 77,80 "/>
</g>
<g>
<polygon style="fill:#E1F5FA;" points="87,47 87,41.5 82,31 82,47 "/>
</g>
<g>
<path style="fill:#E1F5FA;" d="M87,84c0,1.657-1.343,3-3,3h-4c-1.657,0-3-1.343-3-3v-2h10V84z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M62,45c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,44,62,44.45,62,45L62,45z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M56,50c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,49,56,49.45,56,50
L56,50z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M62,58c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,57,62,57.45,62,58L62,58z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M56,63c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,62,56,62.45,56,63
L56,63z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M62,71c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,70,62,70.45,62,71L62,71z"/>
</g>
<g>
<path style="fill:#BBE7F2;" d="M56,76c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,75,56,75.45,56,76
L56,76z"/>
</g>
<g>
<g>
<path style="fill:#009FC7;" d="M35.067,52l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,52z"/>
</g>
</g>
<g>
<g>
<path style="fill:#009FC7;" d="M35.067,65l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,65z"/>
</g>
</g>
<g>
<g>
<path style="fill:#009FC7;" d="M35.067,78l-3.774-3.774c-0.391-0.39-0.391-1.023,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,78z"/>
</g>
</g>
<g>
<rect x="77" y="79" style="fill:#0F6175;" width="10" height="3"/>
</g>
<g>
<rect x="77" y="80" style="fill:#33899E;" width="5" height="2"/>
</g>
<g>
<path style="fill:#0C333D;" d="M82.013,30.984l-1.857,3.899c0.512,0.678,1.152,1.101,1.857,1.101s1.345-0.423,1.857-1.101
L82.013,30.984z"/>
</g>
<g>
<path style="fill:#0081A1;" d="M84.5,43c-1.154,0-2.126-0.782-2.414-1.845c-0.026-0.097-0.147-0.097-0.173,0
C81.626,42.218,80.654,43,79.5,43c-1.052,0-1.953-0.65-2.322-1.57C77.139,41.332,77,41.359,77,41.464V80h10V41.464
c0-0.105-0.139-0.132-0.178-0.034C86.453,42.35,85.552,43,84.5,43z"/>
</g>
<g>
<path style="fill:#009FC7;" d="M77,80V41.494c0-0.112,0.146-0.14,0.19-0.037C77.565,42.363,78.458,43,79.5,43
c1.159,0,2.134-0.789,2.416-1.858C81.931,41.089,82,41.096,82,41.149V80H77z"/>
</g>
<g>
<path style="fill:#FFFFFF;" d="M77,82v2c0,1.657,1.343,3,3,3h2v-5H77z"/>
</g>
</g>
</g>
<g id="Green" style="display:none;">
<g style="display:inline;">
<g>
<path style="fill:#FFFFFF;" d="M23,26.84c0-2.121,1.719-3.84,3.84-3.84H55l16,13.44v46.72c0,2.121-1.719,3.84-3.84,3.84H26.84
C24.719,87,23,85.281,23,83.16V26.84z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M55,23v9.6c0,2.121,1.719,3.84,3.84,3.84H71L55,23z"/>
</g>
<g>
<polygon style="fill:#FFFFFF;" points="77,41.5 82,31 87,41.5 87,80 77,80 "/>
</g>
<g>
<polygon style="fill:#D7FCF9;" points="87,47 87,41.5 82,31 82,47 "/>
</g>
<g>
<path style="fill:#D7FCF9;" d="M87,84c0,1.657-1.343,3-3,3h-4c-1.657,0-3-1.343-3-3v-2h10V84z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M62,45c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,44,62,44.45,62,45L62,45z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M56,50c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,49,56,49.45,56,50
L56,50z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M62,58c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,57,62,57.45,62,58L62,58z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M56,63c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,62,56,62.45,56,63
L56,63z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M62,71c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,70,62,70.45,62,71L62,71z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M56,76c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,75,56,75.45,56,76
L56,76z"/>
</g>
<g>
<g>
<path style="fill:#00B8A5;" d="M35.067,52l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.481,0.984,0.128,1.408L35.067,52z"/>
</g>
</g>
<g>
<g>
<path style="fill:#00B8A5;" d="M35.067,65l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.481,0.984,0.128,1.408L35.067,65z"/>
</g>
</g>
<g>
<g>
<path style="fill:#00B8A5;" d="M35.067,78l-3.774-3.774c-0.391-0.39-0.391-1.023,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.481,0.984,0.128,1.408L35.067,78z"/>
</g>
</g>
<g>
<rect x="77" y="79" style="fill:#14635B;" width="10" height="3"/>
</g>
<g>
<rect x="77" y="80" style="fill:#3E948B;" width="5" height="2"/>
</g>
<g>
<path style="fill:#0A3631;" d="M82.013,30.984l-1.857,3.899c0.512,0.678,1.152,1.101,1.857,1.101s1.345-0.423,1.857-1.101
L82.013,30.984z"/>
</g>
<g>
<path style="fill:#009687;" d="M84.5,43c-1.154,0-2.126-0.782-2.414-1.845c-0.026-0.097-0.147-0.097-0.173,0
C81.625,42.218,80.654,43,79.5,43c-1.052,0-1.953-0.65-2.322-1.57C77.139,41.332,77,41.359,77,41.464V80h10V41.464
c0-0.105-0.139-0.132-0.178-0.034C86.453,42.35,85.552,43,84.5,43z"/>
</g>
<g>
<path style="fill:#00B8A5;" d="M77,80V41.494c0-0.112,0.146-0.14,0.19-0.037C77.565,42.363,78.458,43,79.5,43
c1.159,0,2.134-0.789,2.416-1.858C81.931,41.089,82,41.096,82,41.149V80H77z"/>
</g>
<g>
<path style="fill:#FFFFFF;" d="M77,82v2c0,1.657,1.343,3,3,3h2v-5H77z"/>
</g>
</g>
</g>
<g id="R-Green" style="display:none;">
<g style="display:inline;">
<circle style="fill:#87E0C8;" cx="55" cy="55" r="55"/>
</g>
<g style="display:inline;">
<g>
<path style="fill:#FFFFFF;" d="M23,26.84c0-2.121,1.719-3.84,3.84-3.84H55l16,13.44v46.72C71,85.281,69.28,87,67.16,87H26.84
C24.719,87,23,85.281,23,83.16V26.84z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M55,23v9.6c0,2.121,1.719,3.84,3.84,3.84H71L55,23z"/>
</g>
<g>
<polygon style="fill:#FFFFFF;" points="77,41.5 82,31 87,41.5 87,80 77,80 "/>
</g>
<g>
<polygon style="fill:#D7FCF9;" points="87,47 87,41.5 82,31 82,47 "/>
</g>
<g>
<path style="fill:#D7FCF9;" d="M87,84c0,1.657-1.343,3-3,3h-4c-1.657,0-3-1.343-3-3v-2h10V84z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M62,45c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,44,62,44.45,62,45L62,45z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M56,50c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,49,56,49.45,56,50
L56,50z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M62,58c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,57,62,57.45,62,58L62,58z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M56,63c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,62,56,62.45,56,63
L56,63z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M62,71c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,70,62,70.45,62,71L62,71z"/>
</g>
<g>
<path style="fill:#AAF0E9;" d="M56,76c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,75,56,75.45,56,76
L56,76z"/>
</g>
<g>
<g>
<path style="fill:#00B8A5;" d="M35.067,52l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.481,0.984,0.128,1.408L35.067,52z"/>
</g>
</g>
<g>
<g>
<path style="fill:#00B8A5;" d="M35.067,65l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.481,0.984,0.128,1.408L35.067,65z"/>
</g>
</g>
<g>
<g>
<path style="fill:#00B8A5;" d="M35.067,78l-3.774-3.774c-0.391-0.39-0.391-1.023,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.481,0.984,0.128,1.408L35.067,78z"/>
</g>
</g>
<g>
<rect x="77" y="79" style="fill:#14635B;" width="10" height="3"/>
</g>
<g>
<rect x="77" y="80" style="fill:#3E948B;" width="5" height="2"/>
</g>
<g>
<path style="fill:#0A3631;" d="M82.013,30.984l-1.857,3.899c0.512,0.678,1.152,1.101,1.857,1.101
c0.705,0,1.345-0.423,1.857-1.101L82.013,30.984z"/>
</g>
<g>
<path style="fill:#009687;" d="M84.5,43c-1.154,0-2.125-0.782-2.414-1.845c-0.026-0.097-0.147-0.097-0.173,0
C81.625,42.218,80.654,43,79.5,43c-1.052,0-1.953-0.65-2.322-1.57C77.139,41.332,77,41.359,77,41.464V80h10V41.464
c0-0.105-0.139-0.132-0.178-0.034C86.452,42.35,85.552,43,84.5,43z"/>
</g>
<g>
<path style="fill:#00B8A5;" d="M77,80V41.494c0-0.112,0.146-0.14,0.19-0.037C77.565,42.363,78.458,43,79.5,43
c1.159,0,2.134-0.789,2.417-1.858C81.93,41.089,82,41.096,82,41.149V80H77z"/>
</g>
<g>
<path style="fill:#FFFFFF;" d="M77,82v2c0,1.657,1.343,3,3,3h2v-5H77z"/>
</g>
</g>
</g>
<g id="Red" style="display:none;">
<g style="display:inline;">
<g>
<path style="fill:#FFFFFF;" d="M22.896,26.84c0-2.121,1.719-3.84,3.84-3.84h28.16l16,13.44v46.72c0,2.121-1.719,3.84-3.84,3.84
h-40.32c-2.121,0-3.84-1.719-3.84-3.84V26.84z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M54.896,23v9.6c0,2.121,1.719,3.84,3.84,3.84h12.16L54.896,23z"/>
</g>
<g>
<polygon style="fill:#FFFFFF;" points="76.896,41.5 81.896,31 86.896,41.5 86.896,80 76.896,80 "/>
</g>
<g>
<polygon style="fill:#FFEFED;" points="86.896,47 86.896,41.5 81.896,31 81.896,47 "/>
</g>
<g>
<path style="fill:#FFEFED;" d="M86.896,84c0,1.657-1.343,3-3,3h-4c-1.657,0-3-1.343-3-3v-2h10V84z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M61.896,45c0,0.55-0.45,1-1,1h-15c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.446,44,61.896,44.45,61.896,45L61.896,45z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M55.896,50c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9
C55.446,49,55.896,49.45,55.896,50L55.896,50z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M61.896,58c0,0.55-0.45,1-1,1h-15c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.446,57,61.896,57.45,61.896,58L61.896,58z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M55.896,63c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9
C55.446,62,55.896,62.45,55.896,63L55.896,63z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M61.896,71c0,0.55-0.45,1-1,1h-15c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.446,70,61.896,70.45,61.896,71L61.896,71z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M55.896,76c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9
C55.446,75,55.896,75.45,55.896,76L55.896,76z"/>
</g>
<g>
<g>
<path style="fill:#E54B44;" d="M34.963,52l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L34.963,52z"/>
</g>
</g>
<g>
<g>
<path style="fill:#E54B44;" d="M34.963,65l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L34.963,65z"/>
</g>
</g>
<g>
<g>
<path style="fill:#E54B44;" d="M34.963,78l-3.774-3.774c-0.391-0.39-0.391-1.023,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L34.963,78z"/>
</g>
</g>
<g>
<rect x="76.896" y="79" style="fill:#7D261E;" width="10" height="3"/>
</g>
<g>
<rect x="76.896" y="80" style="fill:#B84E44;" width="5" height="2"/>
</g>
<g>
<path style="fill:#4A1410;" d="M81.909,30.984l-1.857,3.899c0.512,0.678,1.152,1.101,1.857,1.101s1.345-0.423,1.857-1.101
L81.909,30.984z"/>
</g>
<g>
<path style="fill:#C23023;" d="M84.396,43c-1.154,0-2.126-0.782-2.414-1.845c-0.026-0.097-0.147-0.097-0.173,0
C81.521,42.218,80.55,43,79.396,43c-1.052,0-1.953-0.65-2.322-1.57c-0.039-0.098-0.178-0.071-0.178,0.034V80h10V41.464
c0-0.105-0.139-0.132-0.178-0.034C86.349,42.35,85.448,43,84.396,43z"/>
</g>
<g>
<path style="fill:#E54B44;" d="M76.896,80V41.494c0-0.112,0.146-0.14,0.19-0.037C77.461,42.363,78.354,43,79.396,43
c1.159,0,2.134-0.789,2.416-1.858c0.014-0.052,0.084-0.046,0.084,0.008V80H76.896z"/>
</g>
<g>
<path style="fill:#FFFFFF;" d="M76.896,82v2c0,1.657,1.343,3,3,3h2v-5H76.896z"/>
</g>
</g>
</g>
<g id="R-Red" style="display:none;">
<g style="display:inline;">
<circle style="fill:#FABBAF;" cx="55" cy="55" r="55"/>
</g>
<g style="display:inline;">
<g>
<path style="fill:#FFFFFF;" d="M23,26.84c0-2.121,1.719-3.84,3.84-3.84H55l16,13.44v46.72c0,2.121-1.719,3.84-3.84,3.84H26.84
C24.719,87,23,85.281,23,83.16V26.84z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M55,23v9.6c0,2.121,1.719,3.84,3.84,3.84H71L55,23z"/>
</g>
<g>
<polygon style="fill:#FFFFFF;" points="77,41.5 82,31 87,41.5 87,80 77,80 "/>
</g>
<g>
<polygon style="fill:#FFEFED;" points="87,47 87,41.5 82,31 82,47 "/>
</g>
<g>
<path style="fill:#FFEFED;" d="M87,84c0,1.657-1.343,3-3,3h-4c-1.657,0-3-1.343-3-3v-2h10V84z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M62,45c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,44,62,44.45,62,45L62,45z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M56,50c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,49,56,49.45,56,50
L56,50z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M62,58c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,57,62,57.45,62,58L62,58z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M56,63c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,62,56,62.45,56,63
L56,63z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M62,71c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,70,62,70.45,62,71L62,71z"/>
</g>
<g>
<path style="fill:#FFD7D4;" d="M56,76c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,75,56,75.45,56,76
L56,76z"/>
</g>
<g>
<g>
<path style="fill:#E54B44;" d="M35.067,52l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,52z"/>
</g>
</g>
<g>
<g>
<path style="fill:#E54B44;" d="M35.067,65l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,65z"/>
</g>
</g>
<g>
<g>
<path style="fill:#E54B44;" d="M35.067,78l-3.774-3.774c-0.391-0.39-0.391-1.023,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,78z"/>
</g>
</g>
<g>
<rect x="77" y="79" style="fill:#7D261E;" width="10" height="3"/>
</g>
<g>
<rect x="77" y="80" style="fill:#B84E44;" width="5" height="2"/>
</g>
<g>
<path style="fill:#4A1410;" d="M82.013,30.984l-1.857,3.899c0.512,0.678,1.152,1.101,1.857,1.101s1.345-0.423,1.857-1.101
L82.013,30.984z"/>
</g>
<g>
<path style="fill:#C23023;" d="M84.5,43c-1.154,0-2.126-0.782-2.414-1.845c-0.026-0.097-0.147-0.097-0.173,0
C81.626,42.218,80.654,43,79.5,43c-1.052,0-1.953-0.65-2.322-1.57C77.139,41.332,77,41.359,77,41.464V80h10V41.464
c0-0.105-0.139-0.132-0.178-0.034C86.453,42.35,85.552,43,84.5,43z"/>
</g>
<g>
<path style="fill:#E54B44;" d="M77,80V41.494c0-0.112,0.146-0.14,0.19-0.037C77.565,42.363,78.458,43,79.5,43
c1.159,0,2.134-0.789,2.416-1.858C81.931,41.089,82,41.096,82,41.149V80H77z"/>
</g>
<g>
<path style="fill:#FFFFFF;" d="M77,82v2c0,1.657,1.343,3,3,3h2v-5H77z"/>
</g>
</g>
</g>
<g id="Yellow" style="display:none;">
<g style="display:inline;">
<g>
<path style="fill:#FFFFFF;" d="M22.896,26.84c0-2.121,1.719-3.84,3.84-3.84h28.16l16,13.44v46.72c0,2.121-1.719,3.84-3.84,3.84
h-40.32c-2.121,0-3.84-1.719-3.84-3.84V26.84z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M54.896,23v9.6c0,2.121,1.719,3.84,3.84,3.84h12.16L54.896,23z"/>
</g>
<g>
<polygon style="fill:#FFFFFF;" points="76.896,41.5 81.896,31 86.896,41.5 86.896,80 76.896,80 "/>
</g>
<g>
<polygon style="fill:#FFF5D9;" points="86.896,47 86.896,41.5 81.896,31 81.896,47 "/>
</g>
<g>
<path style="fill:#FFF5D9;" d="M86.896,84c0,1.657-1.343,3-3,3h-4c-1.657,0-3-1.343-3-3v-2h10V84z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M61.896,45c0,0.55-0.45,1-1,1h-15c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.446,44,61.896,44.45,61.896,45L61.896,45z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M55.896,50c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9
C55.446,49,55.896,49.45,55.896,50L55.896,50z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M61.896,58c0,0.55-0.45,1-1,1h-15c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.446,57,61.896,57.45,61.896,58L61.896,58z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M55.896,63c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9
C55.446,62,55.896,62.45,55.896,63L55.896,63z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M61.896,71c0,0.55-0.45,1-1,1h-15c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.446,70,61.896,70.45,61.896,71L61.896,71z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M55.896,76c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9
C55.446,75,55.896,75.45,55.896,76L55.896,76z"/>
</g>
<g>
<g>
<path style="fill:#FA9200;" d="M34.963,52l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.481,0.984,0.128,1.408L34.963,52z"/>
</g>
</g>
<g>
<g>
<path style="fill:#FA9200;" d="M34.963,65l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.481,0.984,0.128,1.408L34.963,65z"/>
</g>
</g>
<g>
<g>
<path style="fill:#FA9200;" d="M34.963,78l-3.774-3.774c-0.391-0.39-0.391-1.023,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.481,0.984,0.128,1.408L34.963,78z"/>
</g>
</g>
<g>
<rect x="76.896" y="79" style="fill:#66510D;" width="10" height="3"/>
</g>
<g>
<rect x="76.896" y="80" style="fill:#947615;" width="5" height="2"/>
</g>
<g>
<path style="fill:#3D320C;" d="M81.909,30.984l-1.857,3.899c0.512,0.678,1.152,1.101,1.857,1.101s1.345-0.423,1.857-1.101
L81.909,30.984z"/>
</g>
<g>
<path style="fill:#E07000;" d="M84.396,43c-1.154,0-2.126-0.782-2.414-1.845c-0.026-0.097-0.147-0.097-0.173,0
C81.521,42.218,80.55,43,79.396,43c-1.052,0-1.953-0.65-2.322-1.57c-0.039-0.098-0.178-0.071-0.178,0.034V80h10V41.464
c0-0.105-0.139-0.132-0.178-0.034C86.349,42.35,85.448,43,84.396,43z"/>
</g>
<g>
<path style="fill:#FA9200;" d="M76.896,80V41.494c0-0.112,0.146-0.14,0.19-0.037C77.461,42.363,78.354,43,79.396,43
c1.159,0,2.134-0.789,2.416-1.858c0.014-0.052,0.084-0.046,0.084,0.008V80H76.896z"/>
</g>
<g>
<path style="fill:#FFFFFF;" d="M76.896,82v2c0,1.657,1.343,3,3,3h2v-5H76.896z"/>
</g>
</g>
</g>
<g id="R-Yellow" style="display:none;">
<g style="display:inline;">
<circle style="fill:#FFD75E;" cx="55" cy="55" r="55"/>
</g>
<g style="display:inline;">
<g>
<path style="fill:#FFFFFF;" d="M23,26.84c0-2.121,1.719-3.84,3.84-3.84H55l16,13.44v46.72c0,2.121-1.719,3.84-3.84,3.84H26.84
C24.719,87,23,85.281,23,83.16V26.84z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M55,23v9.6c0,2.121,1.719,3.84,3.84,3.84H71L55,23z"/>
</g>
<g>
<polygon style="fill:#FFFFFF;" points="77,41.5 82,31 87,41.5 87,80 77,80 "/>
</g>
<g>
<polygon style="fill:#FFF5D9;" points="87,47 87,41.5 82,31 82,47 "/>
</g>
<g>
<path style="fill:#FFF5D9;" d="M87,84c0,1.657-1.343,3-3,3h-4c-1.657,0-3-1.343-3-3v-2h10V84z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M62,45c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,44,62,44.45,62,45L62,45z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M56,50c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,49,56,49.45,56,50
L56,50z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M62,58c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,57,62,57.45,62,58L62,58z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M56,63c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,62,56,62.45,56,63
L56,63z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M62,71c0,0.55-0.45,1-1,1H46c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h15
C61.55,70,62,70.45,62,71L62,71z"/>
</g>
<g>
<path style="fill:#FFE9A1;" d="M56,76c0,0.55-0.45,1-1,1h-9c-0.55,0-1-0.45-1-1l0,0c0-0.55,0.45-1,1-1h9C55.55,75,56,75.45,56,76
L56,76z"/>
</g>
<g>
<g>
<path style="fill:#FA9200;" d="M35.067,52l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,52z"/>
</g>
</g>
<g>
<g>
<path style="fill:#FA9200;" d="M35.067,65l-3.774-3.774c-0.391-0.39-0.391-1.024,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,65z"/>
</g>
</g>
<g>
<g>
<path style="fill:#FA9200;" d="M35.067,78l-3.774-3.774c-0.391-0.39-0.391-1.023,0-1.414c0.391-0.391,1.023-0.391,1.414,0
l2.226,2.226l4.299-5.159c0.354-0.424,0.984-0.481,1.408-0.128c0.424,0.353,0.482,0.984,0.128,1.408L35.067,78z"/>
</g>
</g>
<g>
<rect x="77" y="79" style="fill:#66510D;" width="10" height="3"/>
</g>
<g>
<rect x="77" y="80" style="fill:#947615;" width="5" height="2"/>
</g>
<g>
<path style="fill:#3D320C;" d="M82.013,30.984l-1.857,3.899c0.512,0.678,1.152,1.101,1.857,1.101s1.345-0.423,1.857-1.101
L82.013,30.984z"/>
</g>
<g>
<path style="fill:#E07000;" d="M84.5,43c-1.154,0-2.126-0.782-2.414-1.845c-0.026-0.097-0.147-0.097-0.173,0
C81.626,42.218,80.654,43,79.5,43c-1.052,0-1.953-0.65-2.322-1.57C77.139,41.332,77,41.359,77,41.464V80h10V41.464
c0-0.105-0.139-0.132-0.178-0.034C86.453,42.35,85.552,43,84.5,43z"/>
</g>
<g>
<path style="fill:#FA9200;" d="M77,80V41.494c0-0.112,0.146-0.14,0.19-0.037C77.565,42.363,78.458,43,79.5,43
c1.159,0,2.134-0.789,2.416-1.858C81.931,41.089,82,41.096,82,41.149V80H77z"/>
</g>
<g>
<path style="fill:#FFFFFF;" d="M77,82v2c0,1.657,1.343,3,3,3h2v-5H77z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 80 80" style="enable-background:new 0 0 80 80;" xml:space="preserve">
<style type="text/css">
.st0{fill:#4B4B4B;}
</style>
<g id="XMLID_52_">
<path id="XMLID_1171_" class="st0" d="M46.5,78.9c-7.7,0-15.5,0-23.2,0c-5.5,0-9.2-3.7-9.2-9.2c0-15.5,0-31,0-46.5
c0-5.5,3.7-9.2,9.1-9.2c15.6,0,31.2,0,46.7,0c5.2,0,9,3.8,9,9c0,15.5,0,31.1,0,46.6c0,5.5-3.7,9.2-9.2,9.2
C62,78.9,54.2,78.9,46.5,78.9z M20.5,46.5c0,7.4,0,14.9,0,22.3c0,2.4,1.3,3.7,3.7,3.7c14.8,0,29.7,0,44.5,0c2.5,0,3.7-1.3,3.7-3.7
c0-14.8,0-29.7,0-44.5c0-2.5-1.3-3.7-3.8-3.7c-14.8,0-29.7,0-44.5,0c-2.4,0-3.7,1.3-3.7,3.7C20.5,31.6,20.5,39.1,20.5,46.5z"/>
<path id="XMLID_1170_" class="st0" d="M10.8,65.9c-5.6,0.4-9.7-3.5-9.7-8.9c0-15.6,0-31.3,0-46.9c0-5.2,3.8-8.9,9-8.9
c15.7,0,31.4,0,47,0c5.3,0,9.2,4.2,8.8,9.7c-0.4,0-0.8,0-1.2,0c-15.1,0-30.3,0-45.4,0c-5.1,0-8.5,3.5-8.5,8.5c0,15.1,0,30.2,0,45.3
C10.8,65,10.8,65.4,10.8,65.9z"/>
<path id="XMLID_899_" class="st0" d="M37.9,50.5c4.7-4.7,9.3-9.3,13.8-13.9c1.8-1.8,3.5-3.5,5.3-5.3c2.1-2.1,4.8-2,6.9,0.1
c2.3,2.3,2.5,5,0.4,7.1c-7.7,7.7-15.3,15.3-23,23c-2.1,2.1-4.8,2.1-6.8,0c-2.2-2.2-4.5-4.5-6.7-6.8c-1.9-2-1.9-4.6,0-6.6
c1.4-1.5,2.9-2.5,5-1.9c0.8,0.2,1.6,0.7,2.2,1.3C36.1,48.5,36.9,49.5,37.9,50.5z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View File

@@ -0,0 +1,31 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
@Injectable()
export class NotificationService {
notificationsdSource = new Subject<string>();
notifications = this.notificationsdSource.asObservable();
sendNotification(message: string) {
this.notificationsdSource.next(message);
}
}

View File

@@ -19,3 +19,7 @@
.activiti-form-debug-container .debug-toggle-text:hover {
font-weight: bold;
}
.activiti-form-hide-button {
display: none;
}

View File

@@ -1,11 +1,10 @@
<div>
<div *ngIf="!hasForm()">
<h3 style="text-align: center">Please select a Task</h3>
<h3 style="text-align: center">Please select a Visit</h3>
</div>
<div *ngIf="hasForm()">
<div class="mdl-card mdl-shadow--2dp activiti-form-container">
<div *ngIf="form.taskName" class="mdl-card__title">
<div *ngIf="isTitleEnabled()" class="mdl-card__title">
<h2 class="mdl-card__title-text">{{form.taskName}}</h2>
</div>
<div class="mdl-card__media">
@@ -21,11 +20,12 @@
<button *ngFor="let outcome of form.outcomes"
alfresco-mdl-button
[class.mdl-button--colored]="!outcome.isSystem"
[class.activiti-form-hide-button]="!isOutcomeButtonEnabled(outcome)"
(click)="onOutcomeClicked(outcome, $event)">
{{outcome.name}}
</button>
</div>
<div class="mdl-card__menu">
<div *ngIf="showRefreshButton" class="mdl-card__menu" >
<button (click)="onRefreshClicked()"
class="mdl-button mdl-button--icon mdl-js-button mdl-js-ripple-effect">
<i class="material-icons">refresh</i>
@@ -34,7 +34,6 @@
</div>
</div>
</div>
<!--
For debugging and data visualisation purposes,
will be removed during future revisions

View File

@@ -19,7 +19,9 @@ import {
Component,
OnInit, AfterViewChecked, OnChanges,
SimpleChange,
Input
Input,
Output,
EventEmitter
} from '@angular/core';
import { MATERIAL_DESIGN_DIRECTIVES } from 'ng2-alfresco-core';
@@ -32,6 +34,36 @@ import { ContainerWidget } from './widgets/container/container.widget';
declare let __moduleName: string;
declare var componentHandler;
/**
* @Input
* ActivitiForm can show 3 forms searching by 3 type of params:
* 1) Form attached to a task passing the {taskId}.
* 2) Form that are only defined with the {formId} (in this case you receive only the form definition and the form will not be
* attached to any process, usefull in case you want to use Activitiform as form designer), in this case you can pass also other 2
* parameters:
* - {saveOption} as parameter to tell what is the function to call on the save action.
* - {data} to fill the form field with some data, the id of the form must to match the name of the field of the provided data object.
* 3) Form that are only defined with the {formName} (in this case you receive only the form definition and the form will not be
* attached to any process, usefull in case you want to use Activitiform as form designer),
* in this case you can pass also other 2 parameters:
* - {saveOption} as parameter to tell what is the function to call on the save action.
* - {data} to fill the form field with some data, the id of the form must to match the name of the field of the provided data object.
*
* {showTitle} boolean - to hide the title of the form pass false, default true;
*
* {showRefreshButton} boolean - to hide the refresh button of the form pass false, default true;
*
* {showCompleteButton} boolean - to hide the complete button of the form pass false, default true;
*
* {showSaveButton} boolean - to hide the save button of the form pass false, default true;
*
* @Output
* {formLoaded} EventEmitter - This event is fired when the form is loaded, it pass all the value in the form.
* {formSaved} EventEmitter - This event is fired when the form is saved, it pass all the value in the form.
* {formCompleted} EventEmitter - This event is fired when the form is completed, it pass all the value in the form.
*
* @returns {ActivitiForm} .
*/
@Component({
moduleId: __moduleName,
selector: 'activiti-form',
@@ -45,19 +77,74 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
@Input()
taskId: string;
@Input()
formId: string;
@Input()
formName: string;
@Input()
data: any;
@Input()
showTitle: boolean = true;
@Input()
showCompleteButton: boolean = true;
@Input()
showSaveButton: boolean = true;
@Input()
readOnly: boolean = false;
@Input()
showRefreshButton: boolean = true;
@Output()
formSaved = new EventEmitter();
@Output()
formCompleted = new EventEmitter();
@Output()
formLoaded = new EventEmitter();
form: FormModel;
debugMode: boolean = false;
constructor(private formService: FormService) {
}
hasForm(): boolean {
return this.form ? true : false;
}
constructor(private formService: FormService) {}
isTitleEnabled(): boolean {
return this.form.taskName && this.showTitle;
}
isOutcomeButtonEnabled(outcome: any): boolean {
if (outcome.name === 'Complete') {
return this.showCompleteButton;
}
if (outcome.name === 'Save') {
return this.showSaveButton;
}
return true;
}
ngOnInit() {
if (this.taskId) {
this.loadForm(this.taskId);
}
if (this.formId) {
this.getFormDefinitionById();
}
if (this.formName) {
this.getFormDefinitionByName();
}
}
ngAfterViewChecked() {
@@ -72,11 +159,21 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
if (taskId && taskId.currentValue) {
this.loadForm(taskId.currentValue);
}
let formId = changes['formId'];
if (formId && formId.currentValue) {
this.getFormDefinitionById();
}
let formName = changes['formName'];
if (formName && formName.currentValue) {
this.getFormDefinitionByName();
}
}
onOutcomeClicked(outcome: FormOutcomeModel, event?: Event) {
if (outcome) {
if (!this.readOnly && outcome) {
if (outcome.isSystem) {
if (outcome.id === '$save') {
return this.saveTaskForm();
@@ -86,9 +183,13 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
return this.completeTaskForm();
}
if (outcome.id === '$custom') {
this.formSaved.emit(this.form.values);
}
} else {
// Note: Activiti is using NAME field rather than ID for outcomes
if (outcome.name) {
this.formSaved.emit(this.form.values);
return this.completeTaskForm(outcome.name);
}
}
@@ -99,13 +200,54 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
if (this.taskId) {
this.loadForm(this.taskId);
}
if (this.formId) {
this.getFormDefinitionById();
}
if (this.formName) {
this.getFormDefinitionByName();
}
}
private loadForm(taskId: string) {
let data = this.data;
this.formService
.getTaskForm(taskId)
.subscribe(
form => this.form = new FormModel(form),
form => {
this.form = new FormModel(form, data, null, this.readOnly);
this.formLoaded.emit(this.form.values);
},
err => console.log(err)
);
}
private getFormDefinitionById() {
this.formService
.getFormDefinitionById(this.formId)
.subscribe(
form => {
console.log('Get Form By definition Id', form);
this.form = new FormModel(form, this.data, this.formSaved, this.readOnly);
this.formLoaded.emit(this.form.values);
},
err => console.log(err)
);
}
private getFormDefinitionByName() {
this.formService
.getFormDefinitionByName(this.formName)
.subscribe(
id => {
this.formService.getFormDefinitionById(id).subscribe(
form => {
console.log('Get Form By Form definition Name', form);
this.form = new FormModel(form, this.data, this.formSaved, this.readOnly);
this.formLoaded.emit(this.form.values);
},
err => console.log(err)
);
},
err => console.log(err)
);
}
@@ -113,10 +255,10 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
private saveTaskForm() {
this.formService.saveTaskForm(this.form.taskId, this.form.values).subscribe(
(response) => {
console.log(response);
alert('Saved');
console.log('Saved task', response);
this.formSaved.emit(this.form.values);
},
(err) => window.alert(err)
(err) => console.log(err)
);
}
@@ -125,11 +267,10 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
.completeTaskForm(this.form.taskId, this.form.values, outcome)
.subscribe(
(response) => {
console.log(response);
alert('Saved');
console.log('Completed task', response);
this.formCompleted.emit(this.form.values);
},
(err) => window.alert(err)
(err) => console.log(err)
);
}
}

View File

@@ -29,6 +29,7 @@ export class FormFieldTypes {
static HYPERLINK: string = 'hyperlink';
static RADIO_BUTTONS: string = 'radio-buttons';
static DISPLAY_VALUE: string = 'readonly';
static READONLY_TEXT: string = 'readonly-text';
}
export class FormWidgetModel {
@@ -128,9 +129,9 @@ export class FormFieldModel extends FormWidgetModel {
*/
// TODO: needs review
if (json.type === FormFieldTypes.DROPDOWN) {
if (value === '') {
value = 'empty';
}
if (value === '') {
value = 'empty';
}
}
/*
@@ -177,7 +178,17 @@ export class FormFieldModel extends FormWidgetModel {
this.form.values[this.id] = this.options[0].id;
}
} else {
this.form.values[this.id] = this.value;
if (!this.isIngonreType()) {
this.form.values[this.id] = this.value;
}
}
}
private isIngonreType(): boolean {
if (this.type === FormFieldTypes.READONLY_TEXT) {
return true;
} else {
return false;
}
}
}
@@ -337,7 +348,7 @@ export class FormModel {
return this._taskId;
}
get taskName(): string{
get taskName(): string {
return this._taskName;
}
@@ -365,14 +376,14 @@ export class FormModel {
return this.outcomes && this.outcomes.length > 0;
}
constructor(json?: any) {
constructor(json?: any, data?: any, saveOption?: any, readOnly: boolean = false) {
if (json) {
this._json = json;
this._id = json.id;
this._name = json.name;
this._taskId = json.taskId;
this._taskName = json.taskName || this.UNSET_TASK_NAME;
this._taskName = json.taskName || json.name || this.UNSET_TASK_NAME;
let tabCache: WidgetModelCache<TabModel> = {};
@@ -383,7 +394,12 @@ export class FormModel {
return model;
});
this.fields = (json.fields || []).map(obj => new ContainerModel(this, obj));
this.fields = (json.fields || json.formDefinition.fields || []).map(obj => new ContainerModel(this, obj));
if (data) {
this.updateFormValueWithProvaidedDataModel(data);
}
for (let i = 0; i < this.fields.length; i++) {
let field = this.fields[i];
if (field.tab) {
@@ -393,18 +409,55 @@ export class FormModel {
}
}
}
if (this.isATaskForm()) {
let saveOutcome = new FormOutcomeModel(this, {id: '$save', name: 'Save'});
saveOutcome.isSystem = true;
let saveOutcome = new FormOutcomeModel(this, { id: '$save', name: 'Save' });
saveOutcome.isSystem = true;
let completeOutcome = new FormOutcomeModel(this, {id: '$complete', name: 'Complete'});
completeOutcome.isSystem = true;
let completeOutcome = new FormOutcomeModel(this, { id: '$complete', name: 'Complete' });
completeOutcome.isSystem = true;
let customOutcomes = (json.outcomes || []).map(obj => new FormOutcomeModel(this, obj));
let customOutcomes = (json.outcomes || []).map(obj => new FormOutcomeModel(this, obj));
this.outcomes = [saveOutcome].concat(
customOutcomes.length > 0 ? customOutcomes : [completeOutcome]
);
} else {
if (saveOption && saveOption.observers.length > 0) {
let saveOutcome = new FormOutcomeModel(this, {id: '$custom', name: 'Save'});
saveOutcome.isSystem = true;
this.outcomes = [saveOutcome].concat(
customOutcomes.length > 0 ? customOutcomes : [completeOutcome]
);
this.outcomes = [saveOutcome];
}
}
}
}
private updateFormValueWithProvaidedDataModel(data: any) {
for (let i = 0; i < this.fields.length; i++) {
let containerModel = this.fields[i];
if (containerModel) {
for (let i = 0; i < containerModel.columns.length; i++) {
let containerModelColumn = containerModel.columns[i];
if (containerModelColumn) {
for (let i = 0; i < containerModelColumn.fields.length; i++) {
let formField = containerModelColumn.fields[i];
if (data[formField.id]) {
formField.value = data[formField.id];
formField.json.value = data[formField.id];
}
}
}
}
}
}
}
/**
* Check if the form is associated to a task or if is only the form definition
* @returns {boolean}
*/
private isATaskForm(): boolean {
return this._json.fields ? true : false;
}
}

View File

@@ -96,6 +96,27 @@ export class FormService {
.catch(this.handleError);
}
getFormDefinitionById(id: string): Observable<any> {
let url = `${this.alfrescoSettingsService.bpmHost}/activiti-app/app/rest/form-models/${id}`;
let options = this.getRequestOptions();
return this.http
.get(url, options)
.map(this.toJson)
.catch(this.handleError);
}
getFormDefinitionByName(name: string): Observable<any> {
let url = `${this.alfrescoSettingsService.bpmHost}` +
`/activiti-app/app/rest/models?filter=myReusableForms&filterText=${name}&modelType=2`;
let options = this.getRequestOptions();
return this.http
.get(url, options)
.map(this.getFormId)
.catch(this.handleError);
}
private getHeaders(): Headers {
return new Headers({
'Accept': 'application/json',
@@ -109,6 +130,11 @@ export class FormService {
return new RequestOptions({headers: headers});
}
private getFormId(res: Response) {
let body = res.json();
return body.data[0].id || {};
}
private toJson(res: Response) {
let body = res.json();
return body || {};

View File

@@ -0,0 +1,3 @@
.mdl-list__item {
cursor: pointer;
}

View File

@@ -32,8 +32,8 @@ describe('ActivitiFilters', () => {
let filterList: ActivitiFilters;
let fakeGlobalFilter = [];
fakeGlobalFilter.push(new FilterModel('FakeInvolvedTasks', false, 'glyphicon-align-left', '', 'open', 'fake-involved'));
fakeGlobalFilter.push(new FilterModel('FakeMyTasks', false, 'glyphicon-align-left', '', 'open', 'fake-assignee'));
fakeGlobalFilter.push(new FilterModel('FakeInvolvedTasks', false, 'glyphicon-align-left', '', 'open', 'fake-involved'));
fakeGlobalFilter.push(new FilterModel('FakeMyTasks', false, 'glyphicon-align-left', '', 'open', 'fake-assignee'));
let fakeGlobalFilterPromise = new Promise(function (resolve, reject) {
resolve(fakeGlobalFilter);
@@ -55,9 +55,8 @@ describe('ActivitiFilters', () => {
it('should return the filter task list', (done) => {
spyOn(filterList.activiti, 'getTaskListFilters').and.returnValue(Observable.fromPromise(fakeGlobalFilterPromise));
filterList.onSuccess.subscribe( (res) => {
filterList.onSuccess.subscribe((res) => {
expect(res).toBeDefined();
expect(res).toEqual('Filter task list loaded');
expect(filterList.filters).toBeDefined();
expect(filterList.filters.length).toEqual(2);
expect(filterList.filters[0].name).toEqual('FakeInvolvedTasks');
@@ -68,12 +67,32 @@ describe('ActivitiFilters', () => {
filterList.ngOnInit();
});
it('should return the filter task list, filtered By Name', (done) => {
let fakeDeployedApplicationsPromise = new Promise(function (resolve, reject) {
resolve({});
});
spyOn(filterList.activiti, 'getDeployedApplications').and.returnValue(Observable.fromPromise(fakeDeployedApplicationsPromise));
spyOn(filterList.activiti, 'getTaskListFilters').and.returnValue(Observable.fromPromise(fakeGlobalFilterPromise));
filterList.appName = 'test';
filterList.onSuccess.subscribe((res) => {
let deployApp: any = filterList.activiti.getDeployedApplications;
expect(deployApp.calls.count()).toEqual(1);
expect(res).toBeDefined();
done();
});
filterList.ngOnInit();
});
it('should emit an error with a bad response', (done) => {
spyOn(filterList.activiti, 'getTaskListFilters').and.returnValue(Observable.fromPromise(fakeErrorFilterPromise));
filterList.onError.subscribe( (err) => {
filterList.onError.subscribe((err) => {
expect(err).toBeDefined();
expect(err).toEqual('Error to load a task filter list');
done();
});

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Component, Output, EventEmitter, OnInit} from '@angular/core';
import { Component, Output, EventEmitter, OnInit, Input } from '@angular/core';
import { AlfrescoTranslationService, AlfrescoAuthenticationService, AlfrescoPipeTranslate } from 'ng2-alfresco-core';
import { ActivitiTaskListService } from './../services/activiti-tasklist.service';
import { FilterModel } from '../models/filter.model';
@@ -29,8 +29,9 @@ declare let __moduleName: string;
selector: 'activiti-filters',
moduleId: __moduleName,
templateUrl: './activiti-filters.component.html',
styleUrls: ['activiti-filters.component.css'],
providers: [ActivitiTaskListService],
pipes: [ AlfrescoPipeTranslate ]
pipes: [AlfrescoPipeTranslate]
})
export class ActivitiFilters implements OnInit {
@@ -39,10 +40,16 @@ export class ActivitiFilters implements OnInit {
filterClick: EventEmitter<FilterModel> = new EventEmitter<FilterModel>();
@Output()
onSuccess: EventEmitter<string> = new EventEmitter<string>();
onSuccess: EventEmitter<any> = new EventEmitter<any>();
@Output()
onError: EventEmitter<string> = new EventEmitter<string>();
onError: EventEmitter<any> = new EventEmitter<any>();
@Input()
appId: string;
@Input()
appName: string;
private filterObserver: Observer<FilterModel>;
filter$: Observable<FilterModel>;
@@ -50,6 +57,7 @@ export class ActivitiFilters implements OnInit {
currentFilter: FilterModel;
filters: FilterModel [] = [];
/**
* Constructor
* @param auth
@@ -58,7 +66,7 @@ export class ActivitiFilters implements OnInit {
constructor(private auth: AlfrescoAuthenticationService,
private translate: AlfrescoTranslationService,
public activiti: ActivitiTaskListService) {
this.filter$ = new Observable<FilterModel>(observer => this.filterObserver = observer).share();
this.filter$ = new Observable<FilterModel>(observer => this.filterObserver = observer).share();
if (translate) {
translate.addTranslationFolder('node_modules/ng2-activiti-tasklist');
@@ -78,20 +86,39 @@ export class ActivitiFilters implements OnInit {
* @param tasks
*/
private load() {
this.activiti.getTaskListFilters().subscribe(
if (this.appName) {
this.filterByAppName();
} else {
this.filterByAppId(this.appId);
}
}
private filterByAppId(appId) {
this.activiti.getTaskListFilters(appId).subscribe(
(res: FilterModel[]) => {
res.forEach((filter) => {
this.filterObserver.next(filter);
});
this.onSuccess.emit('Filter task list loaded');
this.onSuccess.emit(res);
},
(err) => {
console.log(err);
this.onError.emit('Error to load a task filter list');
this.onError.emit(err);
}
);
}
private filterByAppName() {
this.activiti.getDeployedApplications(this.appName).subscribe(
application => {
this.filterByAppId(application.id);
},
(err) => {
console.log(err);
this.onError.emit(err);
});
}
/**
* Pass the selected filter as next
* @param filter

View File

@@ -13,5 +13,15 @@
<activiti-checklist [taskId]="taskId" #activitichecklist></activiti-checklist>
</div>
</div>
<activiti-form *ngIf="taskDetails.formKey" [taskId]="taskId" #activitiForm ></activiti-form>
</div>
<activiti-form *ngIf="taskDetails.formKey" [taskId]="taskId"
[showTitle]="showTitle"
[showRefreshButton]="showRefreshButton"
[showCompleteButton]="showCompleteButton"
[showSaveButton]="showSaveButton"
(formSaved)='formSavedEmitter($event)'
(formCompleted)='formCompletedEmitter($event)'
(formLoaded)='formLoadedEmitter($event)'
#activitiForm>
</activiti-form>
</div>

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { Component, Input, OnInit, ViewChild, Output, EventEmitter } from '@angular/core';
import { AlfrescoTranslationService, AlfrescoAuthenticationService, AlfrescoPipeTranslate } from 'ng2-alfresco-core';
import { ActivitiTaskListService } from './../services/activiti-tasklist.service';
import { ActivitiTaskHeader } from './activiti-task-header.component';
@@ -51,6 +51,30 @@ export class ActivitiTaskDetails implements OnInit {
@ViewChild('activitichecklist')
activitichecklist: any;
@Input()
showTitle: boolean = true;
@Input()
showCompleteButton: boolean = true;
@Input()
showSaveButton: boolean = true;
@Input()
readOnly: boolean = false;
@Input()
showRefreshButton: boolean = true;
@Output()
formSaved = new EventEmitter();
@Output()
formCompleted = new EventEmitter();
@Output()
formLoaded = new EventEmitter();
taskDetails: TaskDetailsModel;
taskForm: FormModel;
@@ -111,4 +135,16 @@ export class ActivitiTaskDetails implements OnInit {
}
);
}
formSavedEmitter(data: any) {
this.formSaved.emit(data);
}
formCompletedEmitter(data: any) {
this.formCompleted.emit(data);
}
formLoadedEmitter(data: any) {
this.formLoaded.emit(data);
}
}

View File

@@ -0,0 +1,3 @@
alfresco-datatable >>> .data-cell {
cursor: pointer;
}

View File

@@ -96,7 +96,6 @@ describe('ActivitiTaskList', () => {
taskList.onSuccess.subscribe( (res) => {
expect(res).toBeDefined();
expect(res).toEqual('Task List loaded');
expect(taskList.tasks).toBeDefined();
expect(taskList.isTaskListEmpty()).not.toBeTruthy();
expect(taskList.tasks.getRows().length).toEqual(2);
@@ -114,7 +113,6 @@ describe('ActivitiTaskList', () => {
taskList.onError.subscribe( (err) => {
expect(err).toBeDefined();
expect(err).toEqual('Error to load a tasks list');
done();
});

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Component, Input, Output, EventEmitter, OnInit} from '@angular/core';
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
import { AlfrescoTranslationService, AlfrescoAuthenticationService, AlfrescoPipeTranslate } from 'ng2-alfresco-core';
import { ALFRESCO_DATATABLE_DIRECTIVES, ObjectDataTableAdapter, DataTableAdapter, DataRowEvent } from 'ng2-alfresco-datatable';
import { ActivitiTaskListService } from './../services/activiti-tasklist.service';
@@ -28,9 +28,10 @@ declare let __moduleName: string;
selector: 'activiti-tasklist',
moduleId: __moduleName,
templateUrl: './activiti-tasklist.component.html',
styleUrls: ['./activiti-tasklist.component.css'],
directives: [ALFRESCO_DATATABLE_DIRECTIVES],
providers: [ActivitiTaskListService],
pipes: [ AlfrescoPipeTranslate ]
pipes: [AlfrescoPipeTranslate]
})
export class ActivitiTaskList implements OnInit {
@@ -50,10 +51,10 @@ export class ActivitiTaskList implements OnInit {
rowClick: EventEmitter<string> = new EventEmitter<string>();
@Output()
onSuccess: EventEmitter<string> = new EventEmitter<string>();
onSuccess: EventEmitter<any> = new EventEmitter<any>();
@Output()
onError: EventEmitter<string> = new EventEmitter<string>();
onError: EventEmitter<any> = new EventEmitter<any>();
data: DataTableAdapter;
@@ -90,10 +91,10 @@ export class ActivitiTaskList implements OnInit {
this.activiti.getTasks(filter).subscribe(
(res) => {
this.renderTasks(res.data);
this.onSuccess.emit('Task List loaded');
this.onSuccess.emit(res);
}, (err) => {
console.error(err);
this.onError.emit('Error to load a tasks list');
this.onError.emit(err);
});
}

View File

@@ -29,11 +29,11 @@ export class FilterModel {
icon: string;
filter: FilterParamsModel;
constructor(name: string, recent: boolean, icon: string, query: string, state: string, assignment: string) {
constructor(name: string, recent: boolean, icon: string, query: string, state: string, assignment: string, appDefinitionId?: string) {
this.name = name;
this.recent = recent;
this.icon = icon;
this.filter = new FilterParamsModel(query, state, assignment);
this.filter = new FilterParamsModel(query, state, assignment, appDefinitionId);
}
}
@@ -48,10 +48,12 @@ export class FilterParamsModel {
name: string;
state: string;
assignment: string;
appDefinitionId: string;
constructor(query: string, state: string, assignment: string) {
constructor(query: string, state: string, assignment: string, appDefinitionId?: string) {
this.name = query;
this.state = state;
this.assignment = assignment;
this.appDefinitionId = appDefinitionId;
}
}

View File

@@ -32,18 +32,32 @@ export class ActivitiTaskListService {
constructor(private http: Http, public alfrescoSettingsService: AlfrescoSettingsService) {
}
/**
* Retrive all the Deployed app
* @returns {Observable<any>}
*/
getDeployedApplications(name: string): Observable<any> {
let url = this.alfrescoSettingsService.getBPMApiBaseUrl() + `/api/enterprise/runtime-app-definitions`;
return this.http
.get(url, this.getRequestOptions())
.map((response: Response) => response.json().data.find(p => p.name === name))
.do(data => console.log('Application: ' + JSON.stringify(data)))
.catch(this.handleError);
}
/**
* Retrive all the Tasks filters
* @returns {Observable<any>}
*/
getTaskListFilters(): Observable<any> {
return Observable.fromPromise(this.callApiTaskFilters())
getTaskListFilters(appId?: string): Observable<any> {
return Observable.fromPromise(this.callApiTaskFilters(appId))
.map(res => res.json())
.map((response: any) => {
let filters: FilterModel[] = [];
response.data.forEach((filter) => {
let filterModel = new FilterModel(filter.name, filter.recent, filter.icon,
filter.filter.name, filter.filter.state, filter.filter.assignment);
filter.filter.name, filter.filter.state, filter.filter.assignment, appId);
filters.push(filterModel);
});
return filters;
@@ -160,100 +174,65 @@ export class ActivitiTaskListService {
private callApiTasksFiltered(filter: FilterParamsModel) {
let data = JSON.stringify(filter);
let url = this.alfrescoSettingsService.getBPMApiBaseUrl() + `/api/enterprise/tasks/query`;
let headers = new Headers({
'Content-Type': 'application/json',
'Cache-Control': 'no-cache'
});
let options = new RequestOptions({headers: headers});
return this.http
.post(url, data, options).toPromise();
.post(url, data, this.getRequestOptions()).toPromise();
}
private callApiTaskFilters() {
let url = this.alfrescoSettingsService.getBPMApiBaseUrl() + `/api/enterprise/filters/tasks`;
let headers = new Headers({
'Content-Type': 'application/json',
'Cache-Control': 'no-cache'
});
let options = new RequestOptions({headers: headers});
private callApiTaskFilters(appId?: string) {
let url = this.alfrescoSettingsService.getBPMApiBaseUrl();
if (appId) {
url = url + `/api/enterprise/filters/tasks?appId=${appId}`;
} else {
url = url + `/api/enterprise/filters/tasks`;
}
return this.http
.get(url, options).toPromise();
.get(url, this.getRequestOptions()).toPromise();
}
private callApiTaskDetails(id: string) {
let url = this.alfrescoSettingsService.getBPMApiBaseUrl() + `/api/enterprise/tasks/${id}`;
let headers = new Headers({
'Content-Type': 'application/json',
'Cache-Control': 'no-cache'
});
let options = new RequestOptions({headers: headers});
return this.http
.get(url, options).toPromise();
.get(url, this.getRequestOptions()).toPromise();
}
private callApiTaskComments(id: string) {
let url = this.alfrescoSettingsService.getBPMApiBaseUrl() + `/api/enterprise/tasks/${id}/comments`;
let headers = new Headers({
'Content-Type': 'application/json',
'Cache-Control': 'no-cache'
});
let options = new RequestOptions({headers: headers});
return this.http
.get(url, options).toPromise();
.get(url, this.getRequestOptions()).toPromise();
}
private callApiAddTaskComment(id: string, message: string) {
let url = this.alfrescoSettingsService.getBPMApiBaseUrl() + `/api/enterprise/tasks/${id}/comments`;
let headers = new Headers({
'Content-Type': 'application/json',
'Cache-Control': 'no-cache'
});
let body = JSON.stringify({message: message});
let options = new RequestOptions({headers: headers});
return this.http
.post(url, body, options).toPromise();
.post(url, body, this.getRequestOptions()).toPromise();
}
private callApiAddTask(task: TaskDetailsModel) {
let url = this.alfrescoSettingsService.getBPMApiBaseUrl() + `/api/enterprise/tasks/${task.parentTaskId}/checklist`;
let headers = new Headers({
'Content-Type': 'application/json',
'Cache-Control': 'no-cache'
});
let body = JSON.stringify(task);
let options = new RequestOptions({headers: headers});
return this.http
.post(url, body, options).toPromise();
.post(url, body, this.getRequestOptions()).toPromise();
}
private callApiTaskChecklist(id: string) {
let url = this.alfrescoSettingsService.getBPMApiBaseUrl() + `/api/enterprise/tasks/${id}/checklist`;
let headers = new Headers({
'Content-Type': 'application/json',
'Cache-Control': 'no-cache'
});
let options = new RequestOptions({headers: headers});
return this.http
.get(url, options).toPromise();
.get(url, this.getRequestOptions()).toPromise();
}
private callApiCompleteTask(id: string) {
let url = this.alfrescoSettingsService.getBPMApiBaseUrl() + `/api/enterprise/tasks/${id}/action/complete`;
let headers = new Headers({
'Content-Type': 'application/json',
'Cache-Control': 'no-cache'
});
let options = new RequestOptions({headers: headers});
return this.http
.put(url, options).toPromise();
.put(url, this.getRequestOptions()).toPromise();
}
@@ -267,4 +246,15 @@ export class ActivitiTaskListService {
return Observable.throw(error || 'Server error');
}
private getHeaders(): Headers {
return new Headers({
'Accept': 'application/json',
'Content-Type': 'application/json'
});
}
private getRequestOptions(): RequestOptions {
let headers = this.getHeaders();
return new RequestOptions({headers: headers});
}
}

View File

@@ -348,7 +348,7 @@ describe('AlfrescoLogin', () => {
expect(component.success).toBe(true);
expect(compiled.querySelector('#login-success')).toBeDefined();
expect(compiled.querySelector('#login-success').innerHTML).toEqual('LOGIN.MESSAGES.LOGIN-SUCCESS');
expect(component.onSuccess.emit).toHaveBeenCalledWith({value: 'Login OK'});
expect(component.onSuccess.emit).toHaveBeenCalledWith(true);
});
it('should emit onError event after the login has failed', () => {
@@ -379,7 +379,7 @@ describe('AlfrescoLogin', () => {
expect(component.success).toBe(false);
expect(compiled.querySelector('#login-error')).toBeDefined();
expect(compiled.querySelector('#login-error').innerText).toEqual('LOGIN.MESSAGES.LOGIN-ERROR');
expect(component.onError.emit).toHaveBeenCalledWith({value: 'Login KO'});
expect(component.onError.emit).toHaveBeenCalledWith('Fake server error');
});
it('should render the password in clear when the toggleShowPassword is call', () => {

View File

@@ -108,15 +108,11 @@ export class AlfrescoLoginComponent {
.subscribe(
(token: any) => {
this.success = true;
this.onSuccess.emit({
value: 'Login OK'
});
this.onSuccess.emit(token);
},
(err: any) => {
this.error = true;
this.onError.emit({
value: 'Login KO'
});
this.onError.emit(err);
console.log(err);
this.success = false;
},