diff --git a/src/app/doubtfire-angular.module.ts b/src/app/doubtfire-angular.module.ts index fa5708f4d9..09bf6851f4 100644 --- a/src/app/doubtfire-angular.module.ts +++ b/src/app/doubtfire-angular.module.ts @@ -296,6 +296,7 @@ import {PortfoliosAssessmentComponent} from './units/states/portfolios/directive import {UnitGroupsComponent} from './units/states/groups/unit-groups/unit-groups.component'; import {RolloverComponent} from './units/states/rollover/rollover.component'; import {ProjectGroupsComponent} from './projects/states/groups/project-groups/project-groups.component'; +import {SummaryTaskStatusChartComponent} from './visualisations/summary-task-status-chart/summary-task-status-chart.component'; @NgModule({ // Components we declare @@ -440,6 +441,7 @@ import {ProjectGroupsComponent} from './projects/states/groups/project-groups/pr RolloverComponent, ProjectGroupsComponent, GroupMemberContributionAssignerComponent, + SummaryTaskStatusChartComponent, ], // Services we provide providers: [ diff --git a/src/app/doubtfire-angularjs.module.ts b/src/app/doubtfire-angularjs.module.ts index 4855d04ad6..0aed2df656 100644 --- a/src/app/doubtfire-angularjs.module.ts +++ b/src/app/doubtfire-angularjs.module.ts @@ -209,6 +209,7 @@ import {PortfolioAddExtraFilesStepComponent} from './projects/states/portfolio/d import {UnitGroupsComponent} from './units/states/groups/unit-groups/unit-groups.component'; import {ProjectGroupsComponent} from './projects/states/groups/project-groups/project-groups.component'; import {GroupMemberContributionAssignerComponent} from './groups/group-member-contribution-assigner/group-member-contribution-assigner.component'; +import {SummaryTaskStatusChartComponent} from './visualisations/summary-task-status-chart/summary-task-status-chart.component'; export const DoubtfireAngularJSModule = angular.module('doubtfire', [ 'doubtfire.config', @@ -559,3 +560,8 @@ DoubtfireAngularJSModule.directive( 'fGroupMemberContributionAssigner', downgradeComponent({component: GroupMemberContributionAssignerComponent}), ); + +DoubtfireAngularJSModule.directive( + 'fSummaryTaskStatusChart', + downgradeComponent({component: SummaryTaskStatusChartComponent}), +); diff --git a/src/app/units/states/analytics/analytics.tpl.html b/src/app/units/states/analytics/analytics.tpl.html index abc07aea56..c518a36708 100644 --- a/src/app/units/states/analytics/analytics.tpl.html +++ b/src/app/units/states/analytics/analytics.tpl.html @@ -20,4 +20,5 @@

--> + diff --git a/src/app/visualisations/summary-task-status-chart/summary-task-status-chart.component.html b/src/app/visualisations/summary-task-status-chart/summary-task-status-chart.component.html new file mode 100644 index 0000000000..4b07dbd1cb --- /dev/null +++ b/src/app/visualisations/summary-task-status-chart/summary-task-status-chart.component.html @@ -0,0 +1,25 @@ +
+ + + All + Geelong + Online + + + + +
diff --git a/src/app/visualisations/summary-task-status-chart/summary-task-status-chart.component.scss b/src/app/visualisations/summary-task-status-chart/summary-task-status-chart.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/visualisations/summary-task-status-chart/summary-task-status-chart.component.ts b/src/app/visualisations/summary-task-status-chart/summary-task-status-chart.component.ts new file mode 100644 index 0000000000..3d6ef66ce0 --- /dev/null +++ b/src/app/visualisations/summary-task-status-chart/summary-task-status-chart.component.ts @@ -0,0 +1,171 @@ +import {Component, Injector, Input, OnInit, ViewContainerRef} from '@angular/core'; +import {Unit} from 'src/app/api/models/unit'; +import {TaskService} from 'src/app/api/services/task.service'; +import {GradeService} from 'src/app/common/services/grade.service'; +import {TooltipService} from '@swimlane/ngx-charts'; +const actualData = { + 'Geelong': { + '1.1P': {'1': 4, '2': 3, '9': 3, '10': 1}, + '1.2P': {'1': 4, '2': 3, '7': 1, '8': 1, '4': 1, '9': 1}, + '1.3P': {'1': 4, '2': 4, '9': 3}, + '1.4C': {'1': 6, '2': 4, '9': 1}, + '2.1P': {'1': 7, '2': 3, '9': 1}, + '2.2P': {'1': 7, '2': 3, '5': 1}, + '2.3P': {'1': 7, '2': 3, '9': 1}, + '2.4P': {'1': 7, '2': 4}, + '2.5C': {'1': 7, '2': 3, '9': 1}, + '3.1P': {'1': 8, '2': 3}, + '3.2P': {'1': 8, '2': 3}, + '3.3P': {'1': 8, '2': 2, '10': 1}, + '3.4C': {'1': 8, '2': 2, '5': 1}, + '3.5C': {'1': 8, '2': 1, '8': 1, '7': 1}, + '3.6D': {'1': 11}, + '4.1P': {'1': 8, '2': 1, '10': 1, '5': 1}, + '4.2C': {'1': 8, '2': 1, '10': 1, '9': 1}, + '4.3C': {'1': 8, '2': 1, '9': 2}, + 'T1': {'1': 8, '2': 1, '9': 2}, + '5.1P': {'1': 10, '2': 1}, + '5.2P': {'1': 10, '2': 1}, + '5.3C': {'1': 10, '2': 1}, + '5.4C': {'1': 10, '8': 1}, + '5.5D': {'1': 11}, + '6.1P': {'1': 10, '9': 1}, + '7.1P': {'1': 11}, + '7.2D': {'1': 11}, + '8.1P': {'1': 11}, + '8.2P': {'1': 11}, + 'T2': {'1': 11}, + '9.1P': {'1': 11}, + '9.2C': {'1': 11}, + '10.1H': {'1': 11}, + '10.2H': {'1': 11}, + '11.1P': {'1': 11}, + '6.2D': {'1': 11}, + 'T10': {'1': 11}, + }, + 'Online': { + '1.1P': {'1': 3, '2': 2}, + '1.2P': {'1': 3, '2': 2}, + '1.3P': {'1': 3, '2': 2}, + '1.4C': {'1': 4, '2': 1}, + '2.1P': {'1': 3, '2': 2}, + '2.2P': {'1': 3, '2': 2}, + '2.3P': {'1': 3, '2': 2}, + '2.4P': {'1': 3, '2': 2}, + '2.5C': {'1': 4, '2': 1}, + '3.1P': {'1': 3, '2': 2}, + '3.2P': {'1': 3, '2': 1, '10': 1}, + '3.3P': {'1': 3, '2': 2}, + '3.4C': {'1': 4, '2': 1}, + '3.5C': {'1': 4, '2': 1}, + '3.6D': {'1': 4, '2': 1}, + '4.1P': {'1': 3, '2': 1, '9': 1}, + '4.2C': {'1': 4, '2': 1}, + '4.3C': {'1': 4, '2': 1}, + 'T1': {'1': 3, '2': 1, '9': 1}, + '5.1P': {'1': 4, '10': 1}, + '5.2P': {'1': 4, '5': 1}, + '5.3C': {'1': 4, '2': 1}, + '5.4C': {'1': 4, '10': 1}, + '5.5D': {'1': 4, '2': 1}, + '6.1P': {'1': 4, '9': 1}, + '7.1P': {'1': 5}, + '7.2D': {'1': 5}, + '8.1P': {'1': 5}, + '8.2P': {'1': 5}, + 'T2': {'1': 5}, + '9.1P': {'1': 5}, + '9.2C': {'1': 5}, + '10.1H': {'1': 5}, + '10.2H': {'1': 5}, + '11.1P': {'1': 5}, + '6.2D': {'1': 5}, + 'T10': {'1': 5}, + }, +}; + +@Component({ + selector: 'f-summary-task-status-chart', + templateUrl: './summary-task-status-chart.component.html', + styleUrl: './summary-task-status-chart.component.scss', +}) +export class SummaryTaskStatusChartComponent implements OnInit { + @Input() unit: Unit; + + data = []; + + // view: any[] = [700, 400]; + + // options + showXAxis: boolean = true; + showYAxis: boolean = true; + gradient: boolean = false; + showLegend: boolean = true; + showXAxisLabel: boolean = true; + xAxisLabel: string = 'Task'; + showYAxisLabel: boolean = true; + yAxisLabel: string = 'Status'; + animations: boolean = true; + + colorScheme = { + domain: ['#5AA454', '#C7B42C', '#AAAAAA'], + }; + + constructor( + private gradeService: GradeService, + private taskService: TaskService, + private chartToolTipService: TooltipService, + private viewContainerRef: ViewContainerRef, + private injectorObj: Injector, + ) { + // https://github.com/swimlane/ngx-charts/issues/1428#issuecomment-659237562 + this.chartToolTipService = this.injectorObj.get(TooltipService); + this.viewContainerRef = this.injectorObj.get(ViewContainerRef); + } + + statusLabelsArr = Array.from(this.taskService.statusLabels.values()); + + campusFilter: string = 'all'; + + ngOnInit(): void { + this.chartToolTipService.injectionService.setRootViewContainer(this.viewContainerRef); + + this.colorScheme.domain = [...this.taskService.statusColors.values()]; + this.refreshData(); + } + + refreshData() { + const mergedData: Record> = {}; + + // combine all campuses + Object.values(actualData).forEach((campusData) => { + Object.entries(campusData).forEach(([taskDef, counts]) => { + mergedData[taskDef] = mergedData[taskDef] || {}; + Object.entries(counts).forEach(([status, value]) => { + mergedData[taskDef][status] = (mergedData[taskDef][status] || 0) + value; + }); + }); + }); + + // if a campus filter is set, use only that campus + const dataSource = + this.campusFilter && this.campusFilter !== 'all' && actualData[this.campusFilter] + ? actualData[this.campusFilter] + : mergedData; + + // build chart series + const data = Object.entries(dataSource).map(([taskDef, counts]) => ({ + name: taskDef, + series: this.statusLabelsArr.map((label, idx) => ({ + name: label, + value: counts[String(idx)] ?? 0, + })), + })); + + this.data = data; + } + + onSelect(event) { + console.log(event); + } +}