src/app/modules/dashboard/components/report/report.component.ts
OnInit
selector | app-report |
styleUrls | ./report.component.scss |
templateUrl | ./report.component.html |
constructor(reportService: ReportService, activatedRoute: ActivatedRoute, resourceService: ResourceService, toasterService: ToasterService, navigationhelperService: NavigationHelperService, router: Router, telemetryService: TelemetryService, layoutService: LayoutService, cdr: ChangeDetectorRef, userService: UserService, tncService: TncService, location: Location)
|
|||||||||||||||||||||||||||||||||||||||
Parameters :
|
Private closeConfirmationModal |
closeConfirmationModal()
|
Returns :
void
|
Public closeSummaryModal |
closeSummaryModal()
|
Returns :
void
|
Private convertHTMLToCanvas | ||||||
convertHTMLToCanvas(element, options)
|
||||||
Parameters :
Returns :
any
|
Public downloadCSV | ||||||
downloadCSV(downloadUrl?: string)
|
||||||
Parameters :
Returns :
void
|
downloadReport | ||||
downloadReport(reportType)
|
||||
Parameters :
Returns :
void
|
Private downloadReportAsImage |
downloadReportAsImage()
|
Returns :
void
|
Private downloadReportAsPdf |
downloadReportAsPdf()
|
Returns :
void
|
Private fetchConfig | |||||||||
fetchConfig(reportId, hash?: string)
|
|||||||||
Parameters :
Returns :
Observable<any>
|
Public filterChanged | ||||||
filterChanged(data: any)
|
||||||
Parameters :
Returns :
void
|
getAllChartData |
getAllChartData()
|
Returns :
{}
|
getChartData | ||||
getChartData(chart)
|
||||
Parameters :
Returns :
any
|
Private getLatestSummary | ||||||
getLatestSummary(reportId: string)
|
||||||
Parameters :
Returns :
any
|
Public getParametersValueForDropDown | ||||||
getParametersValueForDropDown(report?: object)
|
||||||
Parameters :
Returns :
any
|
getReportViewerTncPolicy |
getReportViewerTncPolicy()
|
Returns :
void
|
Public gotoListPage |
gotoListPage()
|
Returns :
void
|
Private handleAddSummaryStreams |
handleAddSummaryStreams()
|
Returns :
any
|
Public handleConfirmationEvent | ||||||
handleConfirmationEvent(event: boolean)
|
||||||
Parameters :
Returns :
void
|
Public handleParameterChange | ||||
handleParameterChange(val)
|
||||
Parameters :
Returns :
void
|
Private handlePublishBtnStream |
handlePublishBtnStream()
|
Returns :
any
|
Private handleRetireBtnStream |
handleRetireBtnStream()
|
Returns :
any
|
Private handleUpdatedMarkdown |
handleUpdatedMarkdown()
|
Returns :
any
|
initLayout |
initLayout()
|
Returns :
void
|
Private logTelemetry | |||
logTelemetry(undefined)
|
|||
Parameters :
Returns :
void
|
Private mergeClickEventStreams |
mergeClickEventStreams()
|
Returns :
void
|
ngOnInit |
ngOnInit()
|
Returns :
void
|
Public onAddSummary | ||||||
onAddSummary(event: ISummaryObject)
|
||||||
Parameters :
Returns :
void
|
Public onPublish | ||||
onPublish(event)
|
||||
Parameters :
Returns :
void
|
Public onRetire | ||||
onRetire(event)
|
||||
Parameters :
Returns :
void
|
Public openAddSummaryModal | |||||
openAddSummaryModal(undefined: ISummaryObject)
|
|||||
Parameters :
Returns :
void
|
Public openConfirmationModal | ||||||
openConfirmationModal(eventType: "publish" | "retire")
|
||||||
Parameters :
Returns :
void
|
Public openReportSummaryModal |
openReportSummaryModal()
|
Returns :
void
|
Private refreshComponent |
refreshComponent()
|
Returns :
void
|
Private renderReport |
renderReport(reportId: string, hash?: string)
|
Returns :
any
|
resetFilter |
resetFilter()
|
Returns :
void
|
selectedTabChange | ||||
selectedTabChange(event)
|
||||
Parameters :
Returns :
void
|
Public setDownloadUrl | ||||
setDownloadUrl(url)
|
||||
Parameters :
Returns :
void
|
Public setTelemetryInteractEdata | ||||
setTelemetryInteractEdata(val)
|
||||
Parameters :
Returns :
{ id: any; type: string; pageid: any; }
|
Public setTelemetryInteractObject | ||||||
setTelemetryInteractObject(val?: string)
|
||||||
Parameters :
Returns :
{ id: any; type: string; ver: string; }
|
showReportViewerTncForFirstUser |
showReportViewerTncForFirstUser()
|
Returns :
void
|
Private toggleHtmlVisibilty | ||||||
toggleHtmlVisibilty(flag: boolean)
|
||||||
Parameters :
Returns :
void
|
Private _reportSummary |
Type : string
|
Private addSummaryBtnClickStream$ |
Default value : new Subject<ISummaryObject>()
|
chartsComponentList |
Type : QueryList<DataChartComponent>
|
Decorators :
@ViewChildren(DataChartComponent)
|
Public chartsReportData |
Type : any
|
Public confirmationPopupInput |
Type : literal type
|
Public currentReportSummary |
Type : any
|
Private downloadUrl |
Type : string
|
Public exportOptions |
Type : []
|
Default value : ['Pdf', 'Img']
|
filterType |
Type : string
|
Default value : 'report-filter'
|
Public getCDataObj |
Default value : () => {...}
|
Public getParametersValueForDropDown$ |
Type : Observable<any>
|
Public getTelemetryImpressionObj |
Default value : () => {...}
|
Public getTelemetryInteractEdata |
Default value : () => {...}
|
Public globalFilterChange |
Type : Object
|
Private hash |
Type : string
|
Public hideElements |
Type : boolean
|
Public inputForSummaryModal |
Type : any
|
Public isUserReportAdmin |
Default value : false
|
layoutConfiguration |
Type : any
|
Public location |
Type : Location
|
Public markdownUpdated$ |
Default value : new Subject()
|
Private materializedReport |
Default value : false
|
Public noResult |
Type : boolean
|
Public noResultMessage |
Type : INoResultMessage
|
Private publishBtnStream$ |
Default value : new Subject()
|
Public report |
Type : any
|
Public report$ |
Private reportConfig |
Type : object
|
Public reportData |
Type : any
|
reportElement |
Decorators :
@ViewChild('reportElement')
|
Public reportExportInProgress |
Default value : false
|
Public reportObj |
Type : any
|
Public reportResult |
Type : any
|
Public reportService |
Type : ReportService
|
reportViewerTncUrl |
Type : string
|
reportViewerTncVersion |
Type : string
|
Public resetFilters |
Type : Object
|
Private retireBtnStream$ |
Default value : new Subject()
|
Public selectedFilters |
Type : Object
|
Public showChart |
Default value : true
|
Public showComponent |
Default value : true
|
Public showConfirmationModal |
Default value : false
|
Public showExportsOption |
Default value : true
|
showResetFilter |
Default value : true
|
Public showSummaryModal |
Default value : false
|
showTncPopup |
Default value : false
|
Public tncService |
Type : TncService
|
Public type |
Type : ReportType
|
Default value : ReportType.report
|
userProfile |
setMaterializedReportStatus | ||||||
setsetMaterializedReportStatus(val: string)
|
||||||
Parameters :
Returns :
void
|
setParametersHash | ||||
setsetParametersHash(report)
|
||||
Parameters :
Returns :
void
|
import { TelemetryService } from '@sunbird/telemetry';
import { UserService, TncService } from '@sunbird/core';
import { INoResultMessage, ResourceService, ToasterService, NavigationHelperService, LayoutService } from '@sunbird/shared';
import { ActivatedRoute, Router } from '@angular/router';
import { Component, OnInit, ViewChildren, QueryList, ViewChild, ChangeDetectorRef } from '@angular/core';
import { ReportService } from '../../services';
import * as _ from 'lodash-es';
import { Observable, throwError, of, forkJoin, Subject, merge, combineLatest } from 'rxjs';
import { mergeMap, switchMap, map, retry, catchError, tap, pluck, first } from 'rxjs/operators';
import { DataChartComponent } from '../data-chart/data-chart.component';
import html2canvas from 'html2canvas';
import * as jspdf from 'jspdf';
import { ISummaryObject } from '../../interfaces';
import { Location } from '@angular/common';
enum ReportType {
report,
dataset
}
@Component({
selector: 'app-report',
templateUrl: './report.component.html',
styleUrls: ['./report.component.scss']
})
export class ReportComponent implements OnInit {
public report: any;
public showSummaryModal = false;
public report$;
public noResultMessage: INoResultMessage;
public noResult: boolean;
private downloadUrl: string;
public reportObj: any;
public isUserReportAdmin = false;
@ViewChildren(DataChartComponent) chartsComponentList: QueryList<DataChartComponent>;
@ViewChild('reportElement') reportElement;
public hideElements: boolean;
public reportExportInProgress = false;
public exportOptions = ['Pdf', 'Img'];
public inputForSummaryModal: any;
private _reportSummary: string;
private addSummaryBtnClickStream$ = new Subject<ISummaryObject>();
private publishBtnStream$ = new Subject();
private retireBtnStream$ = new Subject();
public markdownUpdated$ = new Subject();
public currentReportSummary: any;
public showComponent = true;
public showConfirmationModal = false;
public confirmationPopupInput: { title: string, event: 'retire' | 'publish', body: string };
private materializedReport = false;
private hash: string;
public getParametersValueForDropDown$: Observable<any>;
public type: ReportType = ReportType.report;
private reportConfig: object;
layoutConfiguration: any;
public selectedFilters: Object;
public showChart = true;
public reportData: any;
public chartsReportData: any;
public globalFilterChange: Object;
public resetFilters: Object;
filterType = 'report-filter';
public reportResult: any;
public showExportsOption = true;
private set setMaterializedReportStatus(val: string) {
this.materializedReport = (val === 'true');
}
userProfile;
reportViewerTncVersion: string;
reportViewerTncUrl: string;
showTncPopup = false;
showResetFilter = true;
private set setParametersHash(report) {
const { hash } = this.activatedRoute.snapshot.params;
this.hash = hash;
if (this.reportService.isReportParameterized(report) && !this.reportService.isUserSuperAdmin()) {
this.hash = _.get(report, 'hashed_val') || this.reportService.getParametersHash(this.report);
}
}
constructor(public reportService: ReportService, private activatedRoute: ActivatedRoute,
private resourceService: ResourceService, private toasterService: ToasterService,
private navigationhelperService: NavigationHelperService,
private router: Router, private telemetryService: TelemetryService, private layoutService: LayoutService,
private cdr: ChangeDetectorRef, private userService: UserService, public tncService: TncService,
public location:Location
) { }
ngOnInit() {
this.initLayout();
this.userService.userData$.pipe(first()).subscribe(async (user) => {
if (user && user.userProfile) {
this.userProfile = user.userProfile;
this.getReportViewerTncPolicy();
}
});
this.report$ = combineLatest(this.activatedRoute.params, this.activatedRoute.queryParams).pipe(
switchMap(params => {
const { reportId, hash } = this.activatedRoute.snapshot.params;
this.setMaterializedReportStatus = this.activatedRoute.snapshot.queryParams.materialize;
return this.reportService.isAuthenticated(_.get(this.activatedRoute, 'snapshot.data.roles')).pipe(
mergeMap((isAuthenticated: boolean) => {
this.noResult = false;
this.hideElements = false;
return isAuthenticated ? this.renderReport(reportId, hash) : throwError({ messageText: 'messages.stmsg.m0144' });
}),
catchError(err => {
console.error('Error while rendering report', err);
this.noResultMessage = {
'messageText': _.get(err, 'messageText') || 'messages.stmsg.m0131'
};
this.noResult = true;
return of({});
})
);
})
);
this.mergeClickEventStreams();
}
/**
* @description fetches a report by its report id
* @param reportId
*/
private fetchConfig(reportId, hash?: string): Observable<any> {
if (this.materializedReport) { hash = null; }
return this.reportService.fetchReportById(reportId, hash).pipe(
mergeMap(apiResponse => {
const report = _.get(apiResponse, 'reports');
return report ? of(_.head(report)) : throwError('No report found');
})
);
}
/**
* @description This function fetches config file, datasource and prepares chart and tables data from it.
* @param reportId
*/
private renderReport(reportId: string, hash?: string) {
return this.fetchConfig(reportId, hash)
.pipe(
switchMap(report => {
const isUserReportAdmin = this.isUserReportAdmin = this.reportService.isUserReportAdmin();
if (!isUserReportAdmin && _.toLower(_.get(report, 'status')) !== 'live') {
return throwError({ messageText: 'messages.stmsg.m0144' });
} else {
this.report = report;
this.type = _.get(report, 'report_type') === 'report' ? ReportType.report : ReportType.dataset;
if (this.reportService.isReportParameterized(report) && _.get(report, 'children.length') &&
!this.reportService.isUserSuperAdmin()) {
return throwError({ messageText: 'messages.emsg.mutliParametersFound' });
}
this.setParametersHash = this.report;
this.getParametersValueForDropDown$ = this.getParametersValueForDropDown(report);
if (this.materializedReport) {
this.report.status = 'draft';
}
const reportConfig = this.reportConfig = _.get(report, 'reportconfig');
this.setDownloadUrl(_.get(reportConfig, 'downloadUrl'));
const dataSource = _.get(reportConfig, 'dataSource') || [];
let updatedDataSource = _.isArray(dataSource) ? dataSource : [{ id: 'default', path: dataSource }];
updatedDataSource = this.reportService.getUpdatedParameterizedPath(updatedDataSource, this.hash);
const charts = _.get(reportConfig, 'charts'), tables = _.get(reportConfig, 'table'), files = _.get(reportConfig, 'files');
return forkJoin(this.reportService.downloadMultipleDataSources(updatedDataSource), this.getLatestSummary(reportId)).pipe(
retry(1),
map((apiResponse) => {
const [data, reportSummary] = apiResponse;
const result: any = Object.assign({});
const chart = (charts && this.reportService.prepareChartData(charts, data, updatedDataSource,
_.get(reportConfig, 'reportLevelDataSourceId'))) || [];
result['charts'] = chart;
result['tables'] = (tables && this.reportService.prepareTableData(tables, data, _.get(reportConfig, 'downloadUrl'),
this.hash)) || [];
result['reportMetaData'] = reportConfig;
result['reportSummary'] = reportSummary;
result['files'] = this.reportService.getParameterizedFiles(files || [], this.hash);
result['lastUpdatedOn'] = this.reportService.getFormattedDate(this.reportService.getLatestLastModifiedOnDate(data));
this.chartsReportData = JSON.parse(JSON.stringify(result));
this.reportData = JSON.parse(JSON.stringify(result));
return result;
})
);
}
})
);
}
/**
* @description Downloads csv file from azure blob storage
* @param downloadUrl
*/
public downloadCSV(downloadUrl?: string) {
this.reportService.downloadReport(downloadUrl || this.downloadUrl).subscribe(
result => {
window.open(result, '_blank');
}, err => {
this.toasterService.error(this.resourceService.messages.emsg.m0076);
}
);
}
/**
* @description sets downloadUrl for active tab
* @param url
*/
public setDownloadUrl(url) {
this.downloadUrl = this.reportService.resolveParameterizedPath(url, this.hash ?
this.reportService.getParameterFromHash(this.hash) : null);
}
public getTelemetryInteractEdata = ({ id = 'report-chart', type = 'click', pageid = this.activatedRoute.snapshot.data.telemetry.pageid,
subtype = null }) => ({ id, type, pageid, ...subtype && { subtype } })
public setTelemetryInteractObject(val?: string) {
return {
id: val || this.activatedRoute.snapshot.params.reportId,
type: 'Report',
ver: '1.0'
};
}
public setTelemetryInteractEdata(val) {
return {
id: val,
type: 'click',
pageid: this.activatedRoute.snapshot.data.telemetry.pageid
};
}
downloadReport(reportType) {
this.reportExportInProgress = true;
this.toggleHtmlVisibilty(true);
setTimeout(() => {
switch (_.toLower( _.get(reportType, 'value'))) {
case 'img': {
this.downloadReportAsImage();
break;
}
case 'pdf': {
this.downloadReportAsPdf();
break;
}
}
const telemetryObj = this.getTelemetryImpressionObj({ type: 'export-request', subtype: _.toLower( _.get(reportType, 'value')) });
this.telemetryService.impression(telemetryObj);
}, 1500);
}
private convertHTMLToCanvas(element, options) {
return html2canvas(element, options);
}
private downloadReportAsPdf() {
this.convertHTMLToCanvas(this.reportElement.nativeElement, {
scrollX: 0,
scrollY: -window.scrollY,
scale: 2
}).then(canvas => {
const imageURL = canvas.toDataURL('image/jpeg');
const pdf = new jspdf('p', 'px', 'a4');
const pageWidth = pdf.internal.pageSize.getWidth();
const imageHeight = (canvas.height * pageWidth) / canvas.width;
pdf.internal.pageSize.setHeight(imageHeight);
pdf.addImage(imageURL, 'JPEG', 10, 8, pageWidth - 28, imageHeight - 24);
pdf.save('report.pdf');
this.toggleHtmlVisibilty(false);
this.reportExportInProgress = false;
}).catch(err => {
this.toggleHtmlVisibilty(false);
this.reportExportInProgress = false;
});
}
private downloadReportAsImage() {
this.convertHTMLToCanvas(this.reportElement.nativeElement, {
scrollX: 0,
scrollY: -window.scrollY,
scale: 2
}).then(canvas => {
const imageURL = canvas.toDataURL('image/jpeg');
const anchorElement = document.createElement('a');
anchorElement.href = imageURL.replace('image/jpeg', 'image/octet-stream');
anchorElement.download = 'report.jpg';
anchorElement.click();
this.toggleHtmlVisibilty(false);
this.reportExportInProgress = false;
}).catch(err => {
this.toggleHtmlVisibilty(false);
this.reportExportInProgress = false;
});
}
private toggleHtmlVisibilty(flag: boolean): void {
this.hideElements = flag;
}
public openAddSummaryModal({ type, title, index = null, chartId = null,
summary = null }: ISummaryObject): void {
this.showSummaryModal = true;
this.inputForSummaryModal = { title, type, index, chartId, summary };
}
public openReportSummaryModal(): void {
this.openAddSummaryModal({
title: (this.currentReportSummary != '' ? 'Update ' : 'Add ') + _.get(this.resourceService, 'frmelmnts.lbl.reportSummary'),
type: 'report',
...(this._reportSummary && { summary: this._reportSummary })
});
}
private getLatestSummary(reportId: string) {
const hash = this.hash;
return this.reportService.getLatestSummary({ reportId, hash }).pipe(
map(reportSummary => {
this._reportSummary = '';
const summaries = this.currentReportSummary = _.map(reportSummary, summaryObj => {
const summary = _.get(summaryObj, 'summary');
this._reportSummary = summary;
return {
label: (this.currentReportSummary != '' ? 'Update ' : 'Add ') + _.get(this.resourceService, 'frmelmnts.lbl.reportSummary'),
text: [summary],
createdOn: _.get(summaryObj, 'createdon')
};
});
return summaries;
})
);
}
public closeSummaryModal(): void {
this.showSummaryModal = false;
}
public onAddSummary(event: ISummaryObject) {
this.addSummaryBtnClickStream$.next(event);
}
public openConfirmationModal(eventType: 'publish' | 'retire') {
const { confirmReportPublish, confirmRetirePublish } = _.get(this.resourceService, 'messages.imsg');
this.confirmationPopupInput = {
title: 'Confirm',
body: eventType === 'publish' ? confirmReportPublish : confirmRetirePublish,
event: eventType
};
this.showConfirmationModal = true;
}
private closeConfirmationModal() {
this.showConfirmationModal = false;
}
public handleConfirmationEvent(event: boolean) {
this.closeConfirmationModal();
if (event) {
switch (this.confirmationPopupInput.event) {
case 'publish': {
this.onPublish(event);
break;
}
case 'retire': {
this.onRetire(event);
break;
}
}
}
}
public onPublish(event) {
this.publishBtnStream$.next(event);
}
public onRetire(event): void {
this.retireBtnStream$.next(event);
}
private mergeClickEventStreams() {
merge(this.handleAddSummaryStreams(), this.handlePublishBtnStream(), this.handleRetireBtnStream(),
this.handleUpdatedMarkdown())
.subscribe(res => {
this.refreshComponent();
}, err => {
console.log({ err });
this.toasterService.error(this.resourceService.messages.emsg.m0005);
});
}
/**
* @description handles click stream from add report and chart summary button
* @private
* @returns
* @memberof ReportComponent
*/
private handleAddSummaryStreams() {
return this.addSummaryBtnClickStream$.pipe(
mergeMap(event => {
const { reportId } = this.activatedRoute.snapshot.params;
event['hash'] = this.hash;
this.closeSummaryModal();
const cdata = [...(event['chartId'] ? [this.getCDataObj({ id: event['chartId'], type: 'Chart' })] : [])];
this.logTelemetry({ type: 'select-submit', cdata, subtype: _.get(event, 'type') });
return this.reportService.addReportSummary({
reportId,
summaryDetails: event
});
}),
tap(res => {
this.toasterService.info(this.resourceService.messages.imsg.reportSummaryAdded);
})
);
}
/**
* @description refreshes the component to show updated data
* @private
* @memberof ReportComponent
*/
private refreshComponent() {
this.showComponent = false;
setTimeout(() => {
this.showComponent = true;
});
}
/**
* @description handles click stream from publish button
* @private
* @returns
* @memberof ReportComponent
*/
private handlePublishBtnStream() {
return this.publishBtnStream$.pipe(
switchMap(event => {
const { reportId } = this.activatedRoute.snapshot.params;
const hash = this.hash;
return this.reportService.publishReport(reportId, hash);
}),
tap(res => {
this.toasterService.info(this.resourceService.messages.imsg.reportPublished);
this.report.status = 'live';
if (this.materializedReport) {
this.materializedReport = false;
this.router.navigate(['.'], { relativeTo: this.activatedRoute, queryParams: {} });
}
})
);
}
private handleRetireBtnStream() {
return this.retireBtnStream$.pipe(
switchMap(event => {
const { reportId } = this.activatedRoute.snapshot.params;
const hash = this.hash;
return this.reportService.retireReport(reportId, hash);
}),
tap(res => {
this.toasterService.info(this.resourceService.messages.imsg.reportRetired);
this.report.status = 'retired';
if (this.materializedReport) {
this.materializedReport = false;
this.router.navigate(['.'], { relativeTo: this.activatedRoute, queryParams: {} });
}
})
);
}
public gotoListPage() {
this.location.back();
}
public getParametersValueForDropDown(report?: object) {
const { reportId } = this.activatedRoute.snapshot.params;
return this.reportService.fetchReportById(reportId).pipe(
pluck('reports'),
switchMap(reports => {
if (this.reportService.isUserSuperAdmin()) {
return this.reportService.getMaterializedChildRows(reports).pipe(
map(response => {
const [reportObj] = response;
return _.get(reportObj, 'children');
})
);
} else {
return of(_.map(_.get(reports[0], 'children') || [],
child => ({ ...child, label: this.reportService.getParameterFromHash(child.hashed_val) })));
}
})
);
}
public handleParameterChange(val) {
const { reportId } = this.activatedRoute.snapshot.params;
const { hashed_val, materialize = false } = _.get(val, 'value');
this.router.navigate(['/dashBoard/reports', reportId, hashed_val], { queryParams: { ...materialize && { materialize } } }).then(() => {
this.refreshComponent();
});
}
public getCDataObj = ({ id, type }) => ({ id, type });
private logTelemetry({ type, cdata = [], subtype = null }) {
const interactData = {
context: {
env: _.get(this.activatedRoute.snapshot.data.telemetry, 'env') || 'reports',
cdata: [...cdata, { id: _.get(this.reportService.getParameterValues('$slug'), 'value'), type: 'State' }]
},
edata: {
...this.getTelemetryInteractEdata({ type, subtype }),
},
object: {
...this.setTelemetryInteractObject(),
rollup: {}
}
};
this.telemetryService.interact(interactData);
}
public getTelemetryImpressionObj = ({ type, subtype = null }) => ({
context: {
env: this.activatedRoute.snapshot.data.telemetry.env
},
object: {
id: this.activatedRoute.snapshot.params.reportId,
type: 'Report',
ver: '1.0'
},
edata: {
type: type || this.activatedRoute.snapshot.data.telemetry.type,
pageid: this.activatedRoute.snapshot.data.telemetry.pageid,
uri: this.router.url,
duration: this.navigationhelperService.getPageLoadTime(),
...(subtype && { subtype })
}
})
private handleUpdatedMarkdown() {
return this.markdownUpdated$.pipe(
switchMap((event: { data: string, type: string }) => {
const updatedReportConfig = {
...this.reportConfig,
dataset: {
...this.reportConfig['dataset'],
[event.type]: btoa(event.data)
}
};
const { reportId } = this.activatedRoute.snapshot.params;
return this.reportService.updateReport(reportId, {
reportconfig: updatedReportConfig
});
})
);
}
initLayout() {
this.layoutConfiguration = this.layoutService.initlayoutConfig();
this.layoutService.switchableLayout()
.subscribe(layoutConfig => {
if (layoutConfig != null) {
this.layoutConfiguration = layoutConfig.layout;
}
});
}
getAllChartData() {
const chartData = [];
if (this.reportData.charts) {
this.reportData.charts.map(chartInfo => {
chartData.push({ id: chartInfo.chartConfig.id, data: chartInfo.chartData });
});
}
return chartData;
}
getChartData(chart) {
const chartInfo = this.chartsReportData.charts.find(data => {
if (data['chartConfig']['id'] == chart['chartConfig']['id']) {
return true;
}
});
return chartInfo;
}
public filterChanged(data: any): void {
if (this.chartsReportData && this.chartsReportData.charts) {
this.chartsReportData.charts.map(element => {
data.chartData.forEach(chart => {
if (chart['id'] === element['chartConfig']['id']) {
element.chartData = chart.data;
}
});
return element;
});
}
this.globalFilterChange = {
chartData: data.chartData,
filters: data.filters
};
this.cdr.detectChanges();
}
resetFilter() {
this.resetFilters = { data: this.getAllChartData(), reset: true };
}
selectedTabChange(event) {
const { type, downloadURL } = _.get(event, 'tab.textLabel');
this.showExportsOption = ['chart', 'download'].includes(type);
this.showResetFilter = ['chart'].includes(type);
downloadURL && this.setDownloadUrl(downloadURL);
}
getReportViewerTncPolicy() {
this.tncService.getReportViewerTnc().subscribe((data) => {
const reportViewerTncData = JSON.parse(_.get(data, 'result.response.value'));
if (_.get(reportViewerTncData, 'latestVersion')) {
this.reportViewerTncVersion = _.get(reportViewerTncData, 'latestVersion');
this.reportViewerTncUrl = _.get(_.get(reportViewerTncData, _.get(reportViewerTncData, 'latestVersion')), 'url');
this.showReportViewerTncForFirstUser();
}
});
}
showReportViewerTncForFirstUser() {
const reportViewerTncObj = _.get(this.userProfile, 'allTncAccepted.reportViewerTnc');
if (!reportViewerTncObj) {
this.showTncPopup = true;
}
}
}
<app-landing-section [noTitle]="true" [layoutConfiguration]="layoutConfiguration"></app-landing-section>
<div [ngClass]="layoutConfiguration ? 'sbt-container relative9 sbt-fluid-content-bg':''">
<div [appTelemetryImpression]="getTelemetryImpressionObj({type: 'page-request'})" #reportElement>
<div class="reports-container w-100" *ngIf="showComponent">
<ng-container *ngIf="(report$ | async) as currentReport else loading">
<div [appTelemetryImpression]="getTelemetryImpressionObj({type: 'page-loaded'})">
<div id="report-header" class="report__header">
<div class="cc-player__content-header relative9 reports-content-header">
<div class="ui container py-16">
<div class="content-header__content p-10">
<div class="d-flex flex-dc flex-basis-1 mr-32 min-w-0 content-header__content__title">
<div class="content-header__title font-weight-bold text-left">
<div class="d-flex">
<div class="d-flex flex-dc flex-basis-4">
<span *ngIf="!hideElements"
class="relative position relative9 reports-back-btn my-0 pl-0 pr-10">
<span class="ui py-8 px-0">
<button type="button"
class="sb-btn sb-btn-link sb-btn-link-primary sb-left-icon-btn back-btn-new mb-5"
aria-label="back-button" (click)="gotoListPage()" tabindex="0">
<i class="icon-svg icon-svg--xxs icon-back mr-4"><svg
class="icon icon-svg--primary">
<use xlink:href="assets/images/sprite.svg#arrow-long-left"></use>
</svg></i>
</button>
</span>
</span>
</div>
<div *ngIf="!noResult">
<div class="report-title"> {{ ( report?.title || currentReport?.reportMetaData?.title ||
currentReport?.reportMetaData?.label ) | titlecase }}
<br />
<span *ngIf="hash && reportService.isUserReportAdmin()">
( {{reportService.getParameterFromHash(hash)}} )
</span>
</div>
<div *ngIf="!noResult">
<span
[ngClass]="{'sb-label-primary-100': report.status === 'retired', 'label-success': report.status === 'live', 'label-warning': report.status === 'draft'}"
*ngIf="isUserReportAdmin && !hideElements" class="px-0">
<span *ngIf="report.status === 'live'" class="sb-live"></span>{{ report?.status |
titlecase }}</span>
<span class="ml-auto sb-last-update-status fnormal sb-color-gray-400 px-10 last-update"
*ngIf="currentReport?.lastUpdatedOn && type === 0">
<span>{{resourceService?.frmelmnts?.lbl?.lastUpdatedOn}} : </span>
{{currentReport?.lastUpdatedOn}}
</span>
</div>
</div>
</div>
</div>
</div>
<div *ngIf="isUserReportAdmin && !hideElements && !noResult"
class="d-flex flex-ai-end flex-w-wrap content-header__buttons">
<div
*ngIf="reportService.isUserReportAdmin() && report.parameters && report.parameters.length && !hideElements"
class="d-flex">
<div class="sb-field filter-drop mb-0 mr-8" *ngIf="getParametersValueForDropDown$ | async as options">
<mat-form-field appearance="fill" class="sb-mat__dropdown mr-8">
<mat-label>Select Parameter</mat-label>
<mat-select role="listbox" aria-label="Select Parameter" [(value)]="selectedLanguage" class="selection"
(selectionChange)="handleParameterChange($event)">
<mat-option class="mat-dropdown__options" role="option"
*ngFor="let option of getParametersValueForDropDown$ | async as options" [value]="option"
attr.aria-label="{{option.label}}">{{option.label}}</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
<button *ngIf="type === 0" appTelemetryInteract
[telemetryInteractObject]="setTelemetryInteractObject()"
[telemetryInteractEdata]="getTelemetryInteractEdata({type: 'report-summary'})"
(click)="openReportSummaryModal()" tabindex="0" type="button"
class="sb-btn sb-btn-normal sb-btn-outline-primary mr-8">
<i class="blue plus alternate icon"></i>
{{ this.currentReportSummary !='' ? 'Update' : 'Add' }} {{
resourceService.frmelmnts.lbl.reportSummary }}
</button>
<button appTelemetryInteract [telemetryInteractObject]="setTelemetryInteractObject()"
[telemetryInteractCdata]="[getCDataObj({id: '' + currentReport?.charts?.length || '0', type: 'CountCharts'}),
getCDataObj({id: reportService.getParameterValues('$slug').value, type: 'State'})]"
[telemetryInteractEdata]="getTelemetryInteractEdata({type: 'select-retire'})"
(click)="openConfirmationModal('retire')" tabindex="0"
*ngIf="report?.status !== 'retired' && !(type === 1 && !reportService?.isUserSuperAdmin())"
type="button" class="sb-btn sb-btn-normal sb-btn-outline-primary mr-8">
<i class="white minus alternate icon"></i>
{{report?.status === 'live' ? 'Retire' : 'Discard'}}
</button>
<button appTelemetryInteract [telemetryInteractObject]="setTelemetryInteractObject()"
[telemetryInteractCdata]="[getCDataObj({id: '' + currentReport?.charts?.length || '0', type: 'CountCharts'}),
getCDataObj({id: reportService.getParameterValues('$slug').value, type: 'State'})]"
[telemetryInteractEdata]="getTelemetryInteractEdata({type: 'select-publish'})"
(click)="openConfirmationModal('publish')" tabindex="0"
*ngIf="report?.status === 'draft' && !(type === 1 && !reportService?.isUserSuperAdmin())"
type="button" class="sb-btn sb-btn-normal sb-btn-primary mr-8">
<i class="white check alternate icon"></i> Publish
</button>
</div>
</div>
<p class="report-description py-15 pl-10"
*ngIf="(report?.description || currentReport?.reportMetaData?.description ) && !noResult"
[innerHTML]="reportService.transformHTML(report?.description || currentReport.reportMetaData.description)">
</p>
</div>
</div>
</div>
<div class="ui container" *ngIf="!noResult">
<div class="twelve wide column">
<div *ngIf="type === 1">
<app-dataset [markdownUpdated$]="markdownUpdated$" [dataset]="report?.reportconfig?.dataset">
</app-dataset>
</div>
<div id="report-body" class="my-16" *ngIf="type === 0">
<mat-tab-group (selectedTabChange)="selectedTabChange($event)"
class="sb-mat__tab sb-mat__tab--tabinacc">
<mat-tab [label]="{'type': 'chart', 'downloadURL': currentReport?.reportMetaData?.downloadUrl}"
appTelemetryInteract
[telemetryInteractObject]="setTelemetryInteractObject(currentReport.reportMetaData.id)"
[telemetryInteractEdata]="setTelemetryInteractEdata('report-graph')">
<ng-template mat-tab-label>
<span>{{resourceService?.frmelmnts?.lbl?.graphs}}</span>
</ng-template>
<ng-container *ngTemplateOutlet="commonReportActions">
</ng-container>
<div class="global-filter px-20 pb-10 d-flex" *ngIf=" currentReport?.reportMetaData?.filters">
<div class="d-flex flex-dc flex-basis-1">
<app-filter [filterType]="filterType" [resetFilters]="resetFilters"
[hideElements]="hideElements" [selectedFilter]="selectedFilters"
[filters]="currentReport.reportMetaData.filters" (filterChanged)="filterChanged($event)"
[chartData]="getAllChartData(currentReport)"
[telemetryInteractObject]="telemetryInteractObject">
</app-filter>
</div>
</div>
<div class="ui bottom p-0 b-0 no-bg my-24">
<div class="sb-graph-section p-24 my-24"
*ngFor="let chart of currentReport.charts; let i = index; let l = last;">
<app-map *ngIf="chart?.chartConfig?.chartType === 'map'" [mapData]="chart?.mapData"
[options]="chart?.chartConfig?.options">
</app-map>
<app-data-chart [globalFilter]="globalFilterChange"
*ngIf="chart?.chartConfig?.chartType !== 'map' && showChart"
[telemetryInteractObject]="setTelemetryInteractObject()" [chartInfo]="getChartData(chart)"
[hideElements]="hideElements" [isUserReportAdmin]="isUserReportAdmin"
(openAddSummaryModal)="openAddSummaryModal($event)" [hash]="hash">
</app-data-chart>
</div>
<div class="ui warning message" *ngIf="!currentReport.charts.length">
{{resourceService?.frmelmnts?.lbl?.graphNotAvailable}}
</div>
</div>
</mat-tab>
<mat-tab *ngFor="let table of currentReport.tables"
[label]="{'type': 'table', 'downloadURL': table?.downloadUrl}" appTelemetryInteract
[telemetryInteractObject]="setTelemetryInteractObject(currentReport.reportMetaData.id)"
[telemetryInteractEdata]="setTelemetryInteractEdata('report_table_'+table.name)">
<ng-template mat-tab-label>
<span>{{table?.name}}</span>
</ng-template>
<ng-template matTabContent>
<div *ngIf="table.data && table.config">
<app-sb-table [config]="table.config" [rowsData]="table.data">
</app-sb-table>
</div>
<div *ngIf="table.data && !table.config">
<ng-container *ngTemplateOutlet="commonReportActions">
</ng-container>
<app-data-table [tableId]="table.id" [headerData]="table.header" [rowsData]="table.data">
</app-data-table>
</div>
<div class="ui warning message" *ngIf="!table.data">
{{resourceService?.frmelmnts?.lbl?.tableNotAvailable}}
</div>
</ng-template>
</mat-tab>
<mat-tab *ngIf="currentReport?.files?.length"
[label]="{'type': 'download', 'downloadURL': currentReport?.reportMetaData?.downloadUrl}"
appTelemetryInteract [telemetryInteractObject]="setTelemetryInteractObject()"
[telemetryInteractEdata]="getTelemetryInteractEdata({type: 'download'})">
<ng-template mat-tab-label>
<span>{{resourceService?.frmelmnts?.lbl?.dashboard?.download}}</span>
</ng-template>
<ng-container *ngTemplateOutlet="commonReportActions">
</ng-container>
<div class="files-block p-20">
<table *ngIf="currentReport?.files?.length" id="downloadTable"
class="sb-table sb-table-striped sb-table-sortable sb-table-fixed" cellspacing="0">
<thead class="sb-table-header sb-table-thead-gray">
<tr>
<th>
<div class="cursor-pointer">
{{resourceService?.frmelmnts?.lbl?.dashboard?.fileName}}
</div>
</th>
<th>
<div class="cursor-pointer">
{{resourceService?.frmelmnts?.lbl?.dashboard?.description}}
</div>
</th>
<th>
<div class="cursor-pointer">
{{resourceService?.frmelmnts?.lbl?.dashboard?.fileSize}}
</div>
</th>
<th>
<div class="cursor-pointer">
{{resourceService?.frmelmnts?.lbl?.createdon}}
</div>
</th>
<th class="table-headerDashboard">
<div class="cursor-pointer">
{{resourceService?.frmelmnts?.lbl?.dashboard?.action}}
</div>
</th>
</tr>
</thead>
<tbody class="sb-table-body">
<tr *ngFor="let file of currentReport.files; let i = index; let l = last;">
<td *ngIf="file.name">
{{file.name}}
</td>
<td *ngIf="!file.name">
--
</td>
<td *ngIf="file.description">
{{file.description}}
</td>
<td *ngIf="!file.description">
--
</td>
<td *ngIf="file.fileSize">
{{file.fileSize}}
</td>
<td *ngIf="!file.fileSize">
--
</td>
<td *ngIf="file.createdOn">
{{file.createdOn}}
</td>
<td *ngIf="!file.createdOn">
--
</td>
<td>
<button type="button" class="sb-btn sb-btn-primary sb-btn-normal ml-auto mr-8"
appTelemetryInteract
[telemetryInteractObject]="setTelemetryInteractObject(currentReport.id)"
[telemetryInteractEdata]="setTelemetryInteractEdata('report-download')"
(click)="downloadCSV(file.downloadUrl)">
{{resourceService?.frmelmnts?.lbl?.dashboard?.downloadfile}}</button>
</td>
</tbody>
</table>
<div class="ui warning message" *ngIf="!currentReport.files.length">
File Not Available
</div>
</div>
</mat-tab>
</mat-tab-group>
</div>
<div class="report__summary" id="report-summary" class="my-16"
*ngIf="currentReportSummary && currentReportSummary.length && type === 0">
<app-report-summary [inputData]="currentReportSummary"></app-report-summary>
</div>
</div>
</div>
</div>
<div *ngIf="noResult">
<app-no-result [data]="noResultMessage"></app-no-result>
</div>
</ng-container>
<ng-template #loading>
<div class="ui container">
<div class="nine wide column workspacesegment">
<app-loader></app-loader>
</div>
</div>
</ng-template>
</div>
</div>
</div>
<div>
<app-tnc-popup [showAcceptTnc]="showTncPopup" [reportViewerTncVersion]="reportViewerTncVersion" [tncUrl]="reportViewerTncUrl" #termsAndCondPopUp *ngIf="showTncPopup">
</app-tnc-popup>
</div>
<app-modal-wrapper *ngIf="reportExportInProgress" [config]="{disableClose: true}" (dismiss)="close.emit()">
<ng-template sbModalContent>
<div class="sb-modal sb-hawkeye-modal">
<div class="transition ui dimmer page modals active visible">
<div class="ui modal transition active visible normal">
<div class="sb-modal-content o-y-visible">
<div class="ui">
<div class="twelve wide column px-0 mb-30">
<app-loader [data]="{loaderMessage: 'Report generation is in Progress. Please wait...'}"></app-loader>
</div>
</div>
</div>
</div>
</div>
</div>
</ng-template>
</app-modal-wrapper>
<app-confirm-popup *ngIf="showConfirmationModal" (confirmation)="handleConfirmationEvent($event)"
[input]="confirmationPopupInput">
</app-confirm-popup>
<app-modal-wrapper [config]="{disableClose: true, size: 'large', panelClass: 'material-modal'}" *ngIf="showSummaryModal">
<ng-template sbModalContent let-data>
<app-add-summary-modal (closeModalEvent)="closeSummaryModal()" [input]="inputForSummaryModal"
(submitButtonEvent)="onAddSummary($event)">
</app-add-summary-modal>
</ng-template>
</app-modal-wrapper>
<ng-template #commonReportActions>
<div class="d-flex my-0" *ngIf="!hideElements">
<div class="d-flex flex-dc flex-basis-1 mr-32 min-w-0">
<h4 class="select-filter-label"
*ngIf="showExportsOption && reportData?.reportMetaData?.filters && showResetFilter"> {{
resourceService?.frmelmnts?.lbl?.selectFilters }} <small> {{
resourceService?.frmelmnts?.lbl?.selectFilterDescription }} </small> </h4>
</div>
<div class="d-flex flex-ai-center flex-jc-flex-end pr-10"
*ngIf="reportData?.reportMetaData?.filters && showResetFilter">
<button class="sb-btn sb-btn-normal sb-btn-outline-primary reset-filter" (click)="resetFilter()" tabindex="0">
{{resourceService?.frmelmnts?.btn?.reset}} {{resourceService?.frmelmnts?.lbl?.filters }}
</button>
</div>
<div class="d-flex flex-ai-center flex-jc-flex-end" >
<button appTelemetryInteract [telemetryInteractObject]="setTelemetryInteractObject()"
[telemetryInteractEdata]="getTelemetryInteractEdata({type: 'download'})" type="button"
*ngIf="reportData?.reportMetaData?.downloadUrl && !reportData?.files?.length"
class="sb-left-icon-btn sb-btn sb-btn-primary sb-btn-normal mr-8" (click)="downloadCSV()" tabindex="0"><i
class="download icon"></i>
{{resourceService?.frmelmnts?.lbl?.downloadCsv}}</button>
<div class="sb-field mb-0 mr-8 d-flex flex-jc-flex-end">
<label>
<mat-form-field appearance="fill" class="sb-mat__dropdown mr-12" [ngClass]="{'ml-auto': !reportData?.reportMetaData?.downloadUrl}">
<mat-label>Export As</mat-label>
<mat-select role="listbox" aria-label="Export As" [(value)]="selectedLanguage" class="selection"
(selectionChange)="downloadReport($event)">
<mat-option class="mat-dropdown__options" role="option" *ngFor="let option of exportOptions" [value]="option"
appTelemetryInteract [telemetryInteractObject]="setTelemetryInteractObject()" [telemetryInteractEdata]="getTelemetryInteractEdata({type: 'export-chart', subtype: option})"
attr.aria-label="{{option}}">{{option}}</mat-option>
</mat-select>
</mat-form-field>
</label>
</div>
</div>
</div>
</ng-template>
./report.component.scss
@use "@project-sunbird/sb-styles/assets/mixins/mixins"as *;
@use 'pages/content-header'as *;
@use 'pages/reports'as *;
.sb-graph-section {
background: var(--sb-graph-section-bg);
}
.report-description {
margin-right: calculateRem(0px) !important;
}
.sb-graph-section.global-filter {
background-color: var(--sbt-compt-bg) !important;
}
.select-filter-label {
padding-left: calculateRem(20px);
font-size: calculateRem(17px);
}
.sbt-report-btns-panel-new {
border-radius: 0px !important;
background: var(--sbt-body-bg) !important;
}
.label-success {
color: var(--success-color);
padding-left: calculateRem(10px);
padding-right: calculateRem(10px);
font-size: calculateRem(14px);
font-weight: 300;
}
.sb-label-primary-100 {
background: none !important;
font-size: calculateRem(14px) !important;
}
.label-warning {
color: var(--accessibility-red);
padding-left: calculateRem(10px);
padding-right: calculateRem(10px);
font-size: calculateRem(14px);
font-weight: 300;
}
.sbt-container {
.reports-container {
padding-top: calculateRem(16px);
}
.reports-content-header {
border-radius: calculateRem(24px) !important;
}
}
.reports-content-header {
background: var(--primary-0);
box-shadow: var(--sbt-box-shadow-3px);
}
.table-view {
background: var(--sbt-body-bg) !important;
}
.reset-filter {
padding-left: calculateRem(20px) !important;
padding-right: calculateRem(20px) !important;
}
::ng-deep {
.mat-checkbox-layout .mat-checkbox-label {
white-space: normal !important;
}
.mat-checkbox-inner-container {
top: 0.25rem !important;
margin: inherit !important;
margin-right: 0.5rem !important;
}
}
.files-block {
background: var(--sbt-body-bg) !important;
margin: 0px !important;
border-radius: 0px 0px calculateRem(24px) calculateRem(24px) !important;
box-shadow: none;
border-top: calculateRem(1px) solid var(--gray-100);
}