C:/GoDev/src/TCSTK-Angular/projects/tibco-tcstk/tc-liveapps-lib/src/lib/components/live-apps-documents/live-apps-documents.component.ts
Document List and upload Component
This Component allows to list, upload, download, Documents attached to a Case-Instance or a whole Application. In the Upload Dialog the User is able to select a local File and enter a short Description.
The Component stores also the following Data
Document List
Document Upload Dialog
<tcla-live-apps-documents></tcla-live-apps-documents>
selector | tcla-live-apps-documents |
styleUrls | ./live-apps-documents.component.css |
templateUrl | ./live-apps-documents.component.html |
Properties |
|
Methods |
Inputs |
Outputs |
HostListeners |
Accessors |
constructor(liveapps: LiveAppsService, documentsService: TcDocumentService, dialog: MatDialog)
|
||||||||||||
Parameters :
|
customActions | |
Type : string[]
|
|
Custom Document Buttons (array of text) |
filter | |
Type : string
|
|
NOT used but would allow a search filter on documents |
folderDescription | |
Type : string
|
|
header text on component (defaults to documents) |
folderId | |
Type : string
|
|
The organisation folder to store/retrieve documents |
folderType | |
Type : string
|
|
orgFolders' or 'caseFolders' - different API calls made according to which one this is |
sandboxId | |
Type : number
|
|
sandboxId - this comes from claims resolver |
showHeader | |
Type : boolean
|
|
customActionClicked | |
Type : EventEmitter<DocumentAction>
|
|
Custom Document Action Event: fired when a custom action is clicked for a document (outputs the action name and a document (DocumentAction)) |
window:resize |
Arguments : '$event'
|
window:resize(event)
|
Inherited from
LiveAppsComponent
|
Defined in
LiveAppsComponent:45
|
attachFile | ||||||
attachFile(files: FileList)
|
||||||
Parameters :
Returns :
void
|
ngOnInit |
ngOnInit()
|
Returns :
void
|
openDialog |
openDialog()
|
Returns :
void
|
setFileDescription | ||||||
setFileDescription(description: string)
|
||||||
Parameters :
Returns :
void
|
uploadFile | ||||||
uploadFile(fileToUpload, description)
|
||||||
Parameters :
Returns :
void
|
ngAfterViewInit |
ngAfterViewInit()
|
Inherited from
LiveAppsComponent
|
Defined in
LiveAppsComponent:91
|
Returns :
void
|
ngOnDestroy |
ngOnDestroy()
|
Inherited from
LiveAppsComponent
|
Defined in
LiveAppsComponent:99
|
Returns :
void
|
ngOnInit |
ngOnInit()
|
Inherited from
LiveAppsComponent
|
Defined in
LiveAppsComponent:104
|
Returns :
void
|
setupWidthObserver |
setupWidthObserver()
|
Inherited from
LiveAppsComponent
|
Defined in
LiveAppsComponent:70
|
Returns :
void
|
Public customActionClick |
Default value : () => {...}
|
Public dialog |
Type : MatDialog
|
Public documents |
Type : Document[]
|
Public downloadDocument |
Default value : () => {...}
|
Public errorMessage |
Type : string
|
Public fileDescription |
Type : string
|
Public fileToUpload |
Type : File
|
Default value : undefined
|
Public listDocuments |
Default value : () => {...}
|
Public refresh |
Default value : () => {...}
|
Public removeDocument |
Default value : () => {...}
|
Public showHeader |
Type : boolean
|
Default value : true
|
Whether to show the header bar in the widget - eg. favorites on home page (contains icon etc) - if off icons still appear without bar |
Public uploadDocument |
Default value : () => {...}
|
uploadMessage |
Type : string
|
Public uploadProgress |
Type : number
|
Public viewDocument |
Default value : () => {...}
|
Protected _destroyed$ |
Default value : new Subject()
|
Inherited from
LiveAppsComponent
|
Defined in
LiveAppsComponent:39
|
componentChildDivs |
Type : LiveAppsComponent[]
|
Decorators :
@ViewChildren('componentChildDiv')
|
Inherited from
LiveAppsComponent
|
Defined in
LiveAppsComponent:36
|
componentDiv |
Type : ElementRef
|
Decorators :
@ViewChild('componentDiv', {static: false})
|
Inherited from
LiveAppsComponent
|
Defined in
LiveAppsComponent:35
|
Protected containerChanges$ |
Type : Observable<TcComponent>
|
Inherited from
LiveAppsComponent
|
Defined in
LiveAppsComponent:40
|
Private observer |
Inherited from
LiveAppsComponent
|
Defined in
LiveAppsComponent:42
|
Public resize |
Default value : () => {...}
|
Inherited from
LiveAppsComponent
|
Defined in
LiveAppsComponent:56
|
Public widget |
Type : TcComponent
|
Inherited from
LiveAppsComponent
|
Defined in
LiveAppsComponent:41
|
ShowHeader | ||||||
setShowHeader(showHeader: boolean)
|
||||||
Parameters :
Returns :
void
|
import {Component, EventEmitter, Inject, Input, OnDestroy, OnInit, Output} from '@angular/core';
import {LiveAppsService} from '../../services/live-apps.service';
import { take, takeUntil} from 'rxjs/operators';
import { Document, DocumentAction} from '../../models/tc-document';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material/dialog';
import {LiveAppsComponent} from '../live-apps-component/live-apps-component.component';
import {TcDocumentService} from '../../services/tc-document.service';
import {HttpEventType} from '@angular/common/http';
/**
* Document List and upload Component
*
* This Component allows to list, upload, download, Documents attached to a Case-Instance or a whole Application.
* In the Upload Dialog the User is able to select a local File and enter a short Description.
*
* The Component stores also the following Data
* - uploading User
* - Timestamp
* - File Size
*
* Document List<br>
* ![alt-text](../live-apps-documents.png "Documents Component Image")
* Document Upload Dialog <br>
* ![alt-text](../Docs-Upload.png "Documents Component Image")
*
*@example <tcla-live-apps-documents></tcla-live-apps-documents>
*/
@Component({
selector: 'tcla-live-apps-documents',
templateUrl: './live-apps-documents.component.html',
styleUrls: ['./live-apps-documents.component.css']
})
export class LiveAppsDocumentsComponent extends LiveAppsComponent implements OnInit {
constructor(protected liveapps: LiveAppsService, protected documentsService: TcDocumentService, public dialog: MatDialog) {
super();
}
/**
* sandboxId - this comes from claims resolver
*/
@Input() sandboxId: number;
/**
* orgFolders' or 'caseFolders' - different API calls made according to which one this is
*/
@Input() folderType: string; // 'orgFolders' or 'caseFolders'
/**
* The organisation folder to store/retrieve documents
*/
@Input() folderId: string; // caseRef for caseFolder
/**
* NOT used but would allow a search filter on documents
*/
@Input() filter: string;
/**
* header text on component (defaults to documents)
*/
@Input() folderDescription: string;
/**
* Whether to show the header bar in the widget - eg. favorites on home page (contains icon etc) - if off icons still appear without bar
*/
public showHeader: boolean = true;
@Input('showHeader') set ShowHeader(showHeader: boolean) {
if (showHeader){
this.showHeader = showHeader;
}
}
/**
* Custom Document Buttons (array of text)
*/
@Input() customActions: string[]
/**
* Custom Document Action Event: fired when a custom action is clicked for a document (outputs the action name and a document (DocumentAction))
*/
@Output() customActionClicked: EventEmitter<DocumentAction> = new EventEmitter<DocumentAction>()
public errorMessage: string;
public documents: Document[];
public fileToUpload: File = undefined;
public fileDescription: string;
uploadMessage: string;
public uploadProgress: number;
public refresh = () => {
this.listDocuments();
}
public customActionClick = (action: string, document: Document) => {
this.customActionClicked.emit(new DocumentAction().deserialize(
{
action,
document
}
));
}
public listDocuments = () => {
this.documentsService.listDocuments(this.folderType, this.folderId, this.sandboxId, this.filter)
.pipe(
take(1),
takeUntil(this._destroyed$)
)
.subscribe(documentslist => {
this.documents = documentslist.documents;
}, error => { this.errorMessage = 'Error retrieving case states: ' + error.error.errorMsg; });
}
public uploadDocument = (doc) => {
}
public removeDocument = (doc) => {
this.documentsService.deleteDocument(this.folderType, this.folderId, doc.name, this.sandboxId)
.pipe(
take(1),
takeUntil(this._destroyed$)
)
.subscribe(
val => {
console.log(val);
this.refresh();
}, error => { this.errorMessage = 'Error removing document: ' + error.errorMsg; });
}
public viewDocument = (doc) => {
const viewDocDialogRef = this.dialog.open(LiveAppsDocumentViewerDialogComponent, {
width: '75%',
height: '75%',
data: {
doc: doc,
folderType: this.folderType,
folderId: this.folderId,
sandboxId: this.sandboxId
}
});
viewDocDialogRef.afterClosed().subscribe(result => {
});
}
public downloadDocument = (doc) => {
this.documentsService.downloadDocument(this.folderType, this.folderId, doc.name, doc.artifactVersion, this.sandboxId)
.pipe(
take(1),
takeUntil(this._destroyed$)
)
.subscribe(
data => {
// todo: check if this works on all browsers
const downloadURL = window.URL.createObjectURL(data);
const link = document.createElement('a');
link.href = downloadURL;
link.download = doc.name;
link.click();
}, error => { this.errorMessage = 'Error downloading document: ' + error.errorMsg; });
}
attachFile(files: FileList) {
this.uploadMessage = '';
this.fileToUpload = files.item(0);
}
setFileDescription(description: string) {
this.fileDescription = description;
}
uploadFile(fileToUpload, description) {
this.fileToUpload = fileToUpload;
this.uploadMessage = 'Uploading: ' + fileToUpload.name;
this.fileDescription = description;
this.uploadProgress = 0;
if (this.fileToUpload) {
this.documentsService.uploadDocument(this.folderType, this.folderId, this.sandboxId,
this.fileToUpload, this.fileToUpload.name, this.fileDescription)
.subscribe(
(response: any) => {
if (response.type === HttpEventType.UploadProgress) {
this.uploadProgress = Math.round(100 * response.loaded / response.total);
if (this.uploadProgress === 100) {
this.fileToUpload = undefined;
this.uploadMessage = 'Uploaded: ' + fileToUpload.name;
// api seems not to show new documents straight away sometimes - so this minimizes the chances of that
setTimeout(() => { this.refresh(); }, 1000);
setTimeout(() => { this.uploadMessage = ''; this.uploadProgress = undefined; }, 5000);
}
}
},
error => { console.log('error'); this.errorMessage = 'Error uploading document: ' + error.errorMsg; });
}
}
openDialog(): void {
// only allow if upload not in progress
if (!this.uploadProgress || this.uploadProgress === 100) {
const dialogRef = this.dialog.open(LiveAppsDocumentUploadDialogComponent, {
width: '500px',
data: {}
});
dialogRef.componentInstance.fileevent.subscribe(($e) => {
this.uploadFile($e.file, $e.description);
})
dialogRef.afterClosed().subscribe(result => {
});
}
}
ngOnInit() {
this.refresh();
}
}
@Component({
selector: 'tcla-live-apps-document-upload-dialog',
templateUrl: 'app-live-apps-document-upload-dialog.html',
styleUrls: [ 'app-live-apps-document-upload-dialog.css']
})
export class LiveAppsDocumentUploadDialogComponent {
@Output() fileevent = new EventEmitter<any>();
public fileToUpload: File = undefined;
public description: string = undefined;
constructor(
public dialogRef: MatDialogRef<LiveAppsDocumentUploadDialogComponent>) {}
public uploadFile = () => {
if (this.fileToUpload) {
this.fileevent.emit({ file: this.fileToUpload, description: this.description });
this.dialogRef.close();
}
}
setFileDescription(description: string) {
this.description = description;
}
attachFile(files: FileList) {
// this.uploadMessage = '';
this.fileToUpload = files.item(0);
}
onNoClick(): void {
this.dialogRef.close();
}
}
@Component({
selector: 'tcla-live-apps-document-viewer-dialog',
templateUrl: 'app-live-apps-document-viewer-dialog.html',
styleUrls: [ 'app-live-apps-document-viewer-dialog.css']
})
export class LiveAppsDocumentViewerDialogComponent {
public doc: Document;
public folderType: string;
public folderId: string;
public sandboxId: number;
constructor(
public dialogRef: MatDialogRef<LiveAppsDocumentUploadDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any) {
this.doc = this.data.doc;
this.folderType = this.data.folderType;
this.folderId = this.data.folderId;
this.sandboxId = this.data.sandboxId;
}
onNoClick(): void {
this.dialogRef.close();
}
}
<div class="tcs-documents-pane" fxFill fxLayout="column">
<div *ngIf="showHeader" class="tcs-documents-header" fxLayout="row" fxLayoutAlign="space-between center">
<div fxLayoutAlign="start center">
<mat-icon class="tcs-icon tcs-icon-active tcs-collaboration-feed" svgIcon="tcs-document-library"></mat-icon>
<div class="tcs-documents-header-text">{{folderDescription ? folderDescription : 'Documents'}}</div>
</div>
<mat-icon (click)="openDialog()" [ngClass]="{'tcs-icon-disabled' : uploadProgress < 100}" class="tcs-icon tcs-icon-active tcs-document-edit-buttons tcs-document-upload-icon" [matTooltip]="(uploadProgress && uploadProgress !== 100) ? 'Please wait for existing document to upload' : 'Upload Document'" matTooltipPosition="left" matTooltipShowDelay="1000" svgIcon="tcs-document-upload"></mat-icon>
</div>
<div *ngIf="!showHeader" fxLayout="row" fxLayoutAlign="end center">
<mat-icon style="margin: 5px" [ngClass]="{'tcs-icon-disabled' : uploadProgress < 100}" (click)="openDialog()" class="tcs-icon tcs-icon-active tcs-document-edit-buttons tcs-document-upload-icon" [matTooltip]="(uploadProgress && uploadProgress !== 100) ? 'Please wait for existing document to upload' : 'Upload Document'" matTooltipPosition="left" matTooltipShowDelay="1000" svgIcon="tcs-document-upload"></mat-icon>
</div>
<div fxLayout="column" style="overflow: auto">
<div *ngIf="uploadProgress">
<div>{{uploadMessage}}</div>
<mat-progress-bar
mode="determinate"
[value]="uploadProgress"
>
</mat-progress-bar>
</div>
<div class="tcs-document-list" fxLayout="column">
<div *ngIf="documents && documents.length > 0">
<div class="tcs-document-detail-box tcs-document" *ngFor="let document of documents | orderByDate: 'lastModifiedDate'" fxLayout="column">
<div class="tcs-document-box" fxLayoutAlign="space-between center" fxLayout="row" fxFlex>
<div fxLayoutAlign="start center">
<mat-icon class="tcs-icon tcs-icon-active tcs-document-type-icon" svgIcon="tcs-document-{{document.fileIcon}}"></mat-icon>
</div>
<div class="tcs-document-details-text-box" fxLayout="column" fxLayoutAlign="center start" fxFlex>
<div fxLayout="row" fxLayoutAlign="start center" fxFill>
<div class="tcs-document-name-text" matTooltip="{{document.name}}" matTooltipShowDelay="1000" matTooltipPosition="below">{{document.name | ellipsis: 30}}</div>
<div fxLayout="row" fxLayoutAlign="end center" fxFlex>
<div class="tcs-document-filesize-text"> ({{document.fileSize}})</div>
</div>
</div>
<div class="tcs-document-comment-text" matTooltip="{{document.description}}" matTooltipShowDelay="1000" matTooltipPosition="below">{{document.description | ellipsis: 50}}</div>
<div fxLayout="row" fxLayoutAlign="start center">
<div class="tcs-document-modified-text">{{document.lastModifiedDate | durationSince}}</div>
<div *ngIf="document.lastModifiedByDetails" class="tcs-document-modified-text" matTooltip="{{document.lastModifiedByDetails.username}}" matTooltipShowDelay="1000" matTooltipPosition="below"> by {{document.lastModifiedByDetails.username | ellipsis: 40}}</div>
</div>
</div>
<div fxLayoutAlign="end center">
<mat-icon [matMenuTriggerFor]="docActionMenu" class="tcs-icon tcs-icon-active tcs-document-action-icon" svgIcon="tcs-document-action"></mat-icon>
<mat-menu #docActionMenu="matMenu" class="tcs-icon tcs-icon-active tcs-document-action-icon">
<button class="tcs-document-action-text" mat-menu-item (click)="viewDocument(document)">View</button>
<button class="tcs-document-action-text" mat-menu-item (click)="downloadDocument(document)">Download</button>
<button class="tcs-document-action-text" mat-menu-item (click)="removeDocument(document)">Delete</button>
<button *ngFor="let customAction of customActions" class="tcs-document-action-text" mat-menu-item (click)="customActionClick(customAction, document)">{{customAction}}</button>
</mat-menu>
</div>
</div>
<div class="tcs-document-line"></div>
</div>
</div>
<div *ngIf="!(documents?.length > 0)" fxLayout="row" fxLayoutAlign="center start" fxLayoutGap="10px" style="margin-top: 20px;">
<mat-icon [svgIcon]="'ic-no-docs-icon'" style="height: 48px; width: 48px;"></mat-icon>
<div style="height: 100%" fxLayoutAlign="start center">
<span class="tcs-no-item-text">No documents found</span>
</div>
</div>
</div>
</div>
</div>
./live-apps-documents.component.css
.tcs-hidden-input {
display: none;
}
.tcs-small-pane {
width: 400px;
max-width: 400px;
height: 386px;
max-height: 400px;
border-radius: 3px;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.33);
background-color: #ffffff;
margin: 0;
padding: 0;
}
.tcs-documents-pane {
border-radius: 3px;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.33);
background-color: #ffffff;
}
.tcs-documents-header {
min-height: 40px;
height: 40px;
border-radius: 3px 3px 0px 0px;
box-shadow: 0 1px 2px 0 #dedede;
padding-left: 20px;
padding-right: 20px;
}
.tcs-documents-header-text {
margin-left: 10px;
font-family: Source Sans Pro;
font-size: 18px;
font-weight: 600;
font-style: normal;
font-stretch: normal;
line-height: 1.5;
letter-spacing: 0.3px;
}
.tcs-document-name-text {
font-family: Source Sans Pro;
font-size: 16px;
font-weight: 600;
font-style: normal;
font-stretch: normal;
line-height: normal;
letter-spacing: normal;
color: #0081cb;
}
.tcs-document-comment-text {
font-family: Source Sans Pro;
font-size: 12px;
font-weight: normal;
font-style: normal;
font-stretch: normal;
line-height: normal;
letter-spacing: normal;
color: #727272;
}
.tcs-document-modified-text {
font-family: Source Sans Pro;
font-size: 10px;
font-weight: 600;
font-style: normal;
font-stretch: normal;
line-height: normal;
letter-spacing: -0.1px;
color: #b6b6b6;
}
.tcs-document-filesize-text {
font-family: Source Sans Pro;
font-size: 10px;
font-weight: 600;
font-style: normal;
font-stretch: normal;
line-height: normal;
letter-spacing: -0.1px;
color: #b6b6b6;
}
.tcs-document-details-text-box {
padding-left: 10px;
padding-right: 10px;
padding-top: 2px;
padding-bottom: 2px;
}
:host ::ng-deep .tcs-icon-disabled.tcs-icon.tcs-document-edit-buttons path.svg-content {
fill: #b6b6b6;
}
:host ::ng-deep .tcs-icon-disabled.tcs-icon.tcs-document-edit-buttons:hover path.svg-content {
fill: #b6b6b6;
cursor: not-allowed;
}
:host ::ng-deep .tcs-icon.tcs-document-edit-buttons:hover path.svg-content {
fill: #0081cb;
cursor: pointer;
}
:host ::ng-deep .tcs-icon.tcs-document-edit-icon:hover path.svg-content {
fill: #0081cb;
cursor: pointer;
}
.tcs-document-detail-box {
min-height: 85px;
flex-shrink: 0;
padding-left: 20px;
padding-right: 20px;
}
.tcs-document-detail-box:hover {
background-color: #EEF0F7;
transition: background-color 0.5s;
}
.tcs-document-line {
margin-left: 20px;
padding: 0px;
margin-top: 0px;
margin-right: 20px;
margin-bottom: 0px;
border-bottom-color: #f4f4f4;
border-bottom-width: 1.1px;
border-bottom-style: solid;
}
.tcs-document-list {
overflow-y: auto;
}
.tcs-no-item-text {
font-family: Source Sans Pro;
font-size: 16px;
font-weight: normal;
font-style: normal;
font-stretch: normal;
line-height: normal;
letter-spacing: normal;
color: #b6b6b6;
}