diff --git a/CSETWebNg/src/app/app-routing.module.ts b/CSETWebNg/src/app/app-routing.module.ts
index 62cbae386..748c06a25 100644
--- a/CSETWebNg/src/app/app-routing.module.ts
+++ b/CSETWebNg/src/app/app-routing.module.ts
@@ -238,6 +238,7 @@ import { TutorialMvraComponent } from './assessment/prepare/maturity/tutorial-mv
import { AllAnsweredquestionsComponent } from './reports/all-answeredquestions/all-answeredquestions.component';
import { AllCommentsmarkedComponent } from './reports/all-commentsmarked/all-commentsmarked.component';
import { AllReviewedComponent } from './reports/all-reviewed/all-reviewed.component';
+import { AnalyticsResultsComponent } from './assessment/results/analytics-results/analytics-results.component';
import { Cmmc2LevelsComponent } from './assessment/prepare/maturity/cmmc2-levels/cmmc2-levels.component';
const appRoutes: Routes = [
@@ -470,6 +471,7 @@ const appRoutes: Routes = [
{ path: 'mvra-summary-page', component: MvraSummaryPageComponent },
{ path: 'cpg-summary-page', component: CpgSummaryComponent },
{ path: 'cpg-practices-page', component: CpgPracticesComponent },
+ { path: 'analytics-results-page', component: AnalyticsResultsComponent},
{ path: 'analysis', component: AnalysisComponent },
{ path: 'dashboard', component: DashboardComponent },
{ path: 'ranked-questions', component: RankedQuestionsComponent },
diff --git a/CSETWebNg/src/app/app.module.ts b/CSETWebNg/src/app/app.module.ts
index 803fbdc06..868f1426a 100644
--- a/CSETWebNg/src/app/app.module.ts
+++ b/CSETWebNg/src/app/app.module.ts
@@ -677,6 +677,7 @@ import { AllCommentsmarkedComponent } from './reports/all-commentsmarked/all-com
import { AllReviewedComponent } from './reports/all-reviewed/all-reviewed.component';
import { QuestionsReviewedComponent } from './reports/questions-reviewed/questions-reviewed.component';
import { RolesChangedComponent } from './dialogs/roles-changed/roles-changed.component';
+import { AnalyticsResultsComponent } from './assessment/results/analytics-results/analytics-results.component';
@NgModule({ declarations: [
@@ -1206,7 +1207,8 @@ import { RolesChangedComponent } from './dialogs/roles-changed/roles-changed.com
AllCommentsmarkedComponent,
AllReviewedComponent,
QuestionsReviewedComponent,
- RolesChangedComponent
+ RolesChangedComponent,
+ AnalyticsResultsComponent
],
bootstrap: [AppComponent], imports: [BrowserModule,
BrowserAnimationsModule,
diff --git a/CSETWebNg/src/app/assessment/results/analytics-results/analytics-results.component.html b/CSETWebNg/src/app/assessment/results/analytics-results/analytics-results.component.html
new file mode 100644
index 000000000..8883b4bfc
--- /dev/null
+++ b/CSETWebNg/src/app/assessment/results/analytics-results/analytics-results.component.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+ Analytics
+
+
+ My Sector
+ All Sectors
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSETWebNg/src/app/assessment/results/analytics-results/analytics-results.component.scss b/CSETWebNg/src/app/assessment/results/analytics-results/analytics-results.component.scss
new file mode 100644
index 000000000..2c3d1d589
--- /dev/null
+++ b/CSETWebNg/src/app/assessment/results/analytics-results/analytics-results.component.scss
@@ -0,0 +1,16 @@
+.mat-card-margin-10 {
+ margin: 10px;
+ float: left;
+ width: 99%;
+ }
+ .mat-row:hover {
+ background-color: #f2f2f2;
+ cursor: pointer;
+ }
+
+ .table-container {
+ position: relative;
+ height: 350px;
+ max-height: 350px;
+ overflow: auto;
+ }
\ No newline at end of file
diff --git a/CSETWebNg/src/app/assessment/results/analytics-results/analytics-results.component.ts b/CSETWebNg/src/app/assessment/results/analytics-results/analytics-results.component.ts
new file mode 100644
index 000000000..0d6513cf2
--- /dev/null
+++ b/CSETWebNg/src/app/assessment/results/analytics-results/analytics-results.component.ts
@@ -0,0 +1,152 @@
+import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
+import { AnalyticsService } from '../../../services/analytics.service';
+import { NavigationService } from '../../../services/navigation/navigation.service';
+import Chart, { ChartConfiguration, ChartType, registerables } from 'chart.js/auto';
+import { AssessmentService } from '../../../services/assessment.service';
+import { AggregationService } from '../../../services/aggregation.service';
+import { AssessmentDetail } from '../../../models/assessment-info.model';
+
+Chart.register(...registerables);
+
+@Component({
+ selector: 'app-analytics-results',
+ templateUrl: './analytics-results.component.html',
+ styleUrls: ['./analytics-results.component.scss']
+})
+export class AnalyticsResultsComponent implements OnInit {
+
+ sectorId: any;
+ assessmentId: any;
+ modelId: any;
+ minData: number[] = [];
+ medianData: number[] = [];
+ maxData: number[] = [];
+ currentUserData: number[] = [];
+ labels: string[] = [];
+
+ @ViewChild('barCanvas') private barCanvas!: ElementRef
;
+ private barChart!: Chart;
+
+ // Toggle state
+ dataType: "mySector" | "allSectors" = "mySector";
+
+ constructor(
+ public navSvc: NavigationService,
+ public analyticsSvc: AnalyticsService,
+ public assessSvc: AssessmentService,
+ public aggregSvc: AggregationService
+ ) { }
+
+ ngOnInit(): void {
+ this.assessSvc.getAssessmentDetail().subscribe((resp: AssessmentDetail) => {
+ this.assessmentId = resp.id;
+ this.sectorId = resp.sectorId;
+ this.modelId = resp.maturityModel.modelId;
+ // Fetch initial data after getting assessment details
+ this.getAnalyticsResults();
+ });
+ }
+
+ ngAfterViewInit(): void {
+ // Initialize the chart after the view is initialized
+ this.initializeChart();
+ }
+
+ // Get analytics results for specified sector
+ private async getAnalyticsResults(allSectors?: boolean): Promise {
+ try {
+ let result = null;
+ if (allSectors){
+ result = await this.analyticsSvc.getAnalyticResults(this.assessmentId, this.modelId).toPromise();
+ } else {
+ result = await this.analyticsSvc.getAnalyticResults(this.assessmentId, this.modelId, this.sectorId).toPromise();
+ }
+ this.setData(result);
+ } catch (error) {
+ console.error('Error fetching analytics results', error);
+ }
+ }
+
+ private setData(result: any): void {
+ this.minData = result.min || [];
+ this.medianData = result.median || [];
+ this.maxData = result.max || [];
+ this.currentUserData = result.barData?.values || [];
+ this.labels = result.barData?.labels || [];
+ if (this.barChart) {
+ this.updateChart();
+ }
+ }
+
+ private initializeChart(): void {
+ const data: ChartConfiguration<'bar'>['data'] = {
+ labels: this.labels,
+ datasets: [
+ {
+ label: 'Min',
+ data: this.minData,
+ backgroundColor: 'rgba(178, 29, 45, 1)',
+ borderWidth: 1
+ },
+ {
+ label: 'Median',
+ data: this.medianData,
+ backgroundColor: 'rgba(216, 173, 30, 1)',
+ borderWidth: 1
+ },
+ {
+ label: 'Max',
+ data: this.maxData,
+ backgroundColor: 'rgba(16, 145, 71, 1)',
+ borderWidth: 1
+ },
+ {
+ label: 'My Assessment',
+ data: this.currentUserData,
+ backgroundColor: 'rgba(29, 136, 230, 1)',
+ borderWidth: 1
+ }
+ ]
+ };
+
+ const options: ChartConfiguration<'bar'>['options'] = {
+ indexAxis: 'y',
+ responsive: true,
+ scales: {
+ x: {
+ beginAtZero: true,
+ min: 0,
+ max: 100
+ },
+ y: {
+ beginAtZero: true,
+ }
+ }
+ };
+
+ this.barChart = new Chart(this.barCanvas.nativeElement, {
+ type: 'bar' as ChartType,
+ data: data,
+ options: options
+ });
+ }
+
+ private updateChart(): void {
+ this.barChart.data.labels = this.labels;
+ this.barChart.data.datasets[0].data = this.minData;
+ this.barChart.data.datasets[1].data = this.medianData;
+ this.barChart.data.datasets[2].data = this.maxData;
+ this.barChart.data.datasets[3].data = this.currentUserData;
+ this.barChart.update();
+ }
+
+ toggleData(event: any): void {
+ this.dataType = event.value;
+ if (this.dataType === "allSectors") {
+ this.getAnalyticsResults(true);
+ } else {
+ this.getAnalyticsResults();
+ }
+ }
+
+}
diff --git a/CSETWebNg/src/app/services/analytics.service.ts b/CSETWebNg/src/app/services/analytics.service.ts
index 69bc7b266..0a98f6411 100644
--- a/CSETWebNg/src/app/services/analytics.service.ts
+++ b/CSETWebNg/src/app/services/analytics.service.ts
@@ -26,6 +26,16 @@ export class AnalyticsService {
return this.http.get(this.apiUrl + 'getAggregation');
}
+
+ getAnalyticResults(assessmentId: any, maturityModelId: any, sectorId?: any): any {
+ let url = `http://csetac:5210/api/analytics/maturity?modelId=${maturityModelId}&assessmentId=${assessmentId}`;
+
+ if (sectorId) {
+ url += `§orId=${sectorId}`;
+ }
+ return this.http.get(url);
+}
+
getAnalyticsToken(username, password): any {
return this.http.post(
this.analyticsUrl + 'auth/login', { username, password }, this.headers
diff --git a/CSETWebNg/src/assets/navigation/workflow-omni.xml b/CSETWebNg/src/assets/navigation/workflow-omni.xml
index b61aed12c..c33e10a44 100644
--- a/CSETWebNg/src/assets/navigation/workflow-omni.xml
+++ b/CSETWebNg/src/assets/navigation/workflow-omni.xml
@@ -401,6 +401,7 @@
+