From ab5803f90ad45d23b57ea6758cab919fed7ac1e5 Mon Sep 17 00:00:00 2001 From: "maxim.gorbatyuk" Date: Thu, 8 Feb 2024 09:23:33 +0600 Subject: [PATCH 1/7] added chart but it does not work --- .../people-distribution-chart-object.ts | 57 +++++++++++++++++++ .../people-distribution-chart.component.html | 8 +++ .../people-distribution-chart.component.scss | 11 ++++ ...eople-distribution-chart.component.spec.ts | 28 +++++++++ .../people-distribution-chart.component.ts | 38 +++++++++++++ .../salaries-chart.component.html | 6 ++ src/app/modules/salaries/salaries.module.ts | 2 + 7 files changed, 150 insertions(+) create mode 100644 src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart-object.ts create mode 100644 src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.html create mode 100644 src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.scss create mode 100644 src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.spec.ts create mode 100644 src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.ts diff --git a/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart-object.ts b/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart-object.ts new file mode 100644 index 00000000..5b686bc2 --- /dev/null +++ b/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart-object.ts @@ -0,0 +1,57 @@ +import { Chart } from 'chart.js/auto'; +import { RandomRgbColor } from '../random-rgb-color'; +import { KazakhstanCity, KazakhstanCityEnum } from '@models/salaries/kazakhstan-city'; +import { UserSalary } from '@models/salaries/salary.model'; +import { SalariesChart } from '../salaries-chart/salaries-chart'; +import { UserProfessionEnum } from '@models/salaries/user-profession'; +import { CompanyType } from '@models/salaries/company-type'; + +interface ChartDatasetType { + label: string; + data: Array; + backgroundColor: string; +} + +export class PeopleDistributionChartObject extends Chart { + + private readonly datasets: Array = []; + + constructor(canvasId: string, chart: SalariesChart) { + const datasets: Array = []; + + const maxcount = chart.salaries.filter(x => x.company === CompanyType.Local).length; + + // TODO mgorbatyuk: wrong calculation, should be fixed + chart.salariesByMoneyBarChart?.itemsByProfession.forEach((x, i) => { + datasets.push({ + label: UserProfessionEnum.label(x.profession), + data: x.items.map(s => (s.count/ maxcount * 100)), + backgroundColor: new RandomRgbColor().toString(0.6), + }); + }); + + super( + canvasId, + { + type: 'bar', + data: { + labels: ["Казахстанские компании", "Иностранные компании"], + datasets: datasets, + }, + options: { + indexAxis: 'y', + responsive: true, + scales: { + x: { + stacked: true, + }, + y: { + stacked: true, + } + } + }, + }); + + this.datasets = datasets; + } +} \ No newline at end of file diff --git a/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.html b/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.html new file mode 100644 index 00000000..a299117f --- /dev/null +++ b/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.html @@ -0,0 +1,8 @@ +
+
Расппределение респондентов по специальностям
+
+
+ {{chartDataLocal}} +
+
+
\ No newline at end of file diff --git a/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.scss b/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.scss new file mode 100644 index 00000000..7a884e25 --- /dev/null +++ b/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.scss @@ -0,0 +1,11 @@ +canvas { + min-height: 250px; +} + +#canvas-container { + position: relative; + width: 100%; + height: 100%; + min-height: 250px; + max-height: 500px; +} \ No newline at end of file diff --git a/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.spec.ts b/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.spec.ts new file mode 100644 index 00000000..e3d08117 --- /dev/null +++ b/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.spec.ts @@ -0,0 +1,28 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { mostUsedImports, testUtilStubs, mostUsedServices } from '@shared/test-utils'; +import { PeopleDistributionChartComponent } from './people-distribution-chart.component'; + +describe('PeopleDistributionChartComponent', () => { + let component: PeopleDistributionChartComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [PeopleDistributionChartComponent], + imports: [...mostUsedImports], + providers: [...testUtilStubs, ...mostUsedServices], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + }) + .compileComponents(); + + fixture = TestBed.createComponent(PeopleDistributionChartComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.ts b/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.ts new file mode 100644 index 00000000..4682c4ce --- /dev/null +++ b/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.ts @@ -0,0 +1,38 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { UserSalary, UserSalaryAdminDto } from '@models/salaries/salary.model'; +import { PeopleDistributionChartObject } from './people-distribution-chart-object'; +import { SalariesChart } from '../salaries-chart/salaries-chart'; + +@Component({ + selector: 'app-people-distribution-chart', + templateUrl: './people-distribution-chart.component.html', + styleUrl: './people-distribution-chart.component.scss' +}) +export class PeopleDistributionChartComponent { + + @Input() + chart: SalariesChart | null = null; + + chartDataLocal: PeopleDistributionChartObject | null = null; + + readonly canvasId = 'canvas_' + Math.random().toString(36).substring(7); + + constructor() {} + + ngAfterViewInit() { + this.initChart(); + } + + private initChart(): void { + if (this.chart == null) { + return; + } + + this.chartDataLocal = new PeopleDistributionChartObject(this.canvasId, this.chart); + + var chartEl = document.getElementById(this.canvasId); + if (chartEl != null && chartEl.parentElement != null) { + chartEl.style.height = chartEl?.parentElement.style.height ?? '100%'; + } + } +} diff --git a/src/app/modules/salaries/components/salaries-chart/salaries-chart.component.html b/src/app/modules/salaries/components/salaries-chart/salaries-chart.component.html index 9ca80007..f5cfe7e1 100644 --- a/src/app/modules/salaries/components/salaries-chart/salaries-chart.component.html +++ b/src/app/modules/salaries/components/salaries-chart/salaries-chart.component.html @@ -104,6 +104,12 @@ +
+ +
+
Date: Sat, 10 Feb 2024 00:09:56 +0600 Subject: [PATCH 2/7] Adjusted response interfaces --- .../salaries-chart-js-object.ts | 4 +-- .../salaries-chart/stub-salaries-chart.ts | 10 ++++---- src/app/services/user-salaries.service.ts | 25 +++++++++++++------ 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/app/modules/salaries/components/salaries-by-grades-chart/salaries-chart-js-object.ts b/src/app/modules/salaries/components/salaries-by-grades-chart/salaries-chart-js-object.ts index c6a0e216..dd8f4b05 100644 --- a/src/app/modules/salaries/components/salaries-by-grades-chart/salaries-chart-js-object.ts +++ b/src/app/modules/salaries/components/salaries-by-grades-chart/salaries-chart-js-object.ts @@ -27,7 +27,7 @@ export class SalariesChartJsObject extends Chart { datasets.push({ profession: x.profession, label: UserProfession[x.profession].toString(), - data: x.items.map(x => x.count), + data: x.items, borderWidth: 2, borderColor: color.toString(1), backgroundColor: color.toString(0.7), @@ -41,7 +41,7 @@ export class SalariesChartJsObject extends Chart { profession: null, type: 'bar' as ChartType, label: 'Все', - data: chartData.items.map(x => x.count), + data: chartData.items, borderWidth: 1, borderColor: randomColor.toString(1), backgroundColor: randomColor.toString(0.5), diff --git a/src/app/modules/salaries/components/salaries-chart/stub-salaries-chart.ts b/src/app/modules/salaries/components/salaries-chart/stub-salaries-chart.ts index 4772c00f..b879f700 100644 --- a/src/app/modules/salaries/components/salaries-chart/stub-salaries-chart.ts +++ b/src/app/modules/salaries/components/salaries-chart/stub-salaries-chart.ts @@ -26,11 +26,7 @@ export class StubSalariesChart extends SalariesChart { salariesByMoneyBarChart: { items: StubSalariesChart.salaryLabels.map((x) => { const value = parseInt(x); - return { - start: value, - end: value + 250_000, - count: StubSalariesChart.getRandomNumber(100, 25), - }; + return StubSalariesChart.getRandomNumber(100, 25); }), itemsByProfession: [], labels: StubSalariesChart.salaryLabels, @@ -38,6 +34,10 @@ export class StubSalariesChart extends SalariesChart { salariesByMoneyBarChartForRemote: null, rangeStart: new Date(), rangeEnd: new Date(), + developersByGradeDistributionDataForLocal: null, + developersByGradeDistributionDataForRemote: null, + professionsDistributionDataForLocal: null, + professionsDistributionDataForRemote: null, }); } diff --git a/src/app/services/user-salaries.service.ts b/src/app/services/user-salaries.service.ts index 03c866dc..c5c4183e 100644 --- a/src/app/services/user-salaries.service.ts +++ b/src/app/services/user-salaries.service.ts @@ -42,24 +42,25 @@ export interface SalariesChartResponse { salariesByMoneyBarChartForRemote: SalariesByMoneyBarChart | null; currentUserSalary: UserSalaryAdminDto | null; + + developersByGradeDistributionDataForLocal: DevelopersByGradeDistributionData | null; + developersByGradeDistributionDataForRemote: DevelopersByGradeDistributionData | null; + + professionsDistributionDataForLocal: DevelopersByProfessionsDistributionData | null; + professionsDistributionDataForRemote: DevelopersByProfessionsDistributionData | null; } export interface SalariesByProfessionMoneyBarChartItem { profession: UserProfession; - items: Array; + items: Array; } export interface SalariesByMoneyBarChart { labels: string[]; - items: Array; + items: Array; itemsByProfession: Array; } -export interface SalariesByMoneyBarChartItem { - // TODO mgorbatyuk: remove this model - count: number; -} - export interface CreateSalaryRecordResponse { isSuccess: boolean; errorMessage: string | null; @@ -91,6 +92,16 @@ export interface SalariesAddingTrendAdminChart { salariesPerUser: number; } +export interface DevelopersByProfessionsDistributionData { + all: number; + items: Array<{profession: UserProfession, count: number}>; +} + +export interface DevelopersByGradeDistributionData { + all: number; + items: Array<{grade: DeveloperGrade | null, count: number}>; +} + export enum SalariesAdminOrderingType { Undefined = 0, CreatedAtAsc = 1, From 2be5933ef027760230447e186bccb8606813443f Mon Sep 17 00:00:00 2001 From: "maxim.gorbatyuk" Date: Sat, 10 Feb 2024 00:19:13 +0600 Subject: [PATCH 3/7] rename --- .../people-distribution-chart-object.ts | 11 ----------- .../people-distribution-chart.component.html | 0 .../people-distribution-chart.component.scss | 0 .../people-distribution-chart.component.spec.ts | 0 .../people-distribution-chart.component.ts | 0 .../components/salaries-chart/salaries-chart.ts | 13 ++++++++++++- 6 files changed, 12 insertions(+), 12 deletions(-) rename src/app/modules/salaries/components/{people-distribution-chart => professions-distribution-chart}/people-distribution-chart-object.ts (75%) rename src/app/modules/salaries/components/{people-distribution-chart => professions-distribution-chart}/people-distribution-chart.component.html (100%) rename src/app/modules/salaries/components/{people-distribution-chart => professions-distribution-chart}/people-distribution-chart.component.scss (100%) rename src/app/modules/salaries/components/{people-distribution-chart => professions-distribution-chart}/people-distribution-chart.component.spec.ts (100%) rename src/app/modules/salaries/components/{people-distribution-chart => professions-distribution-chart}/people-distribution-chart.component.ts (100%) diff --git a/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart-object.ts b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart-object.ts similarity index 75% rename from src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart-object.ts rename to src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart-object.ts index 5b686bc2..ba2d4fd9 100644 --- a/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart-object.ts +++ b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart-object.ts @@ -19,17 +19,6 @@ export class PeopleDistributionChartObject extends Chart { constructor(canvasId: string, chart: SalariesChart) { const datasets: Array = []; - const maxcount = chart.salaries.filter(x => x.company === CompanyType.Local).length; - - // TODO mgorbatyuk: wrong calculation, should be fixed - chart.salariesByMoneyBarChart?.itemsByProfession.forEach((x, i) => { - datasets.push({ - label: UserProfessionEnum.label(x.profession), - data: x.items.map(s => (s.count/ maxcount * 100)), - backgroundColor: new RandomRgbColor().toString(0.6), - }); - }); - super( canvasId, { diff --git a/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.html b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.html similarity index 100% rename from src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.html rename to src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.html diff --git a/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.scss b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.scss similarity index 100% rename from src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.scss rename to src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.scss diff --git a/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.spec.ts b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.spec.ts similarity index 100% rename from src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.spec.ts rename to src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.spec.ts diff --git a/src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.ts b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.ts similarity index 100% rename from src/app/modules/salaries/components/people-distribution-chart/people-distribution-chart.component.ts rename to src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.ts diff --git a/src/app/modules/salaries/components/salaries-chart/salaries-chart.ts b/src/app/modules/salaries/components/salaries-chart/salaries-chart.ts index e1c1520e..c091d421 100644 --- a/src/app/modules/salaries/components/salaries-chart/salaries-chart.ts +++ b/src/app/modules/salaries/components/salaries-chart/salaries-chart.ts @@ -1,5 +1,5 @@ import { formatNumber } from "@angular/common"; -import { SalariesByMoneyBarChart, SalariesChartResponse } from "@services/user-salaries.service"; +import { DevelopersByGradeDistributionData, DevelopersByProfessionsDistributionData, SalariesByMoneyBarChart, SalariesChartResponse } from "@services/user-salaries.service"; import { SalariesPerProfession } from "../salaries-per-profession"; import { UserSalary, UserSalaryAdminDto } from "@models/salaries/salary.model"; @@ -23,6 +23,12 @@ export class SalariesChart { readonly currentUserSalary: UserSalaryAdminDto | null = null; readonly currentUserSalaryValue: string | null = null; + readonly developersByGradeDistributionDataForLocal: DevelopersByGradeDistributionData | null; + readonly developersByGradeDistributionDataForRemote: DevelopersByGradeDistributionData | null; + + readonly professionsDistributionDataForLocal: DevelopersByProfessionsDistributionData | null; + readonly professionsDistributionDataForRemote: DevelopersByProfessionsDistributionData | null; + readonly hasRemoteSalaries: boolean; constructor(readonly data: SalariesChartResponse) { @@ -48,6 +54,11 @@ export class SalariesChart { this.currentUserSalaryValue = data.currentUserSalary ? SalariesChart.formatNumber(data.currentUserSalary.value) : null; + + this.developersByGradeDistributionDataForLocal = data.developersByGradeDistributionDataForLocal; + this.developersByGradeDistributionDataForRemote = data.developersByGradeDistributionDataForRemote; + this.professionsDistributionDataForLocal = data.professionsDistributionDataForLocal; + this.professionsDistributionDataForRemote = data.professionsDistributionDataForRemote; } private static formatNumber(value: number | null): string | null { From b6820c035066637f9fe7ee84f65d8c8ed120dbc4 Mon Sep 17 00:00:00 2001 From: "maxim.gorbatyuk" Date: Sat, 10 Feb 2024 00:48:41 +0600 Subject: [PATCH 4/7] Adjustment --- .../people-distribution-chart-object.ts | 27 +++++++++++++++---- src/app/modules/salaries/salaries.module.ts | 2 +- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart-object.ts b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart-object.ts index ba2d4fd9..52d5ab50 100644 --- a/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart-object.ts +++ b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart-object.ts @@ -19,12 +19,31 @@ export class PeopleDistributionChartObject extends Chart { constructor(canvasId: string, chart: SalariesChart) { const datasets: Array = []; + const professions = chart.professionsDistributionDataForLocal?.items.map(x => x.profession) ?? []; + chart.professionsDistributionDataForRemote?.items.forEach(item => { + if (professions.some(x => x === item.profession)) { + return; + } + + professions.push(item.profession); + }); + + datasets.push( + { + label: "Казахстанская компания", + data: chart.professionsDistributionDataForLocal?.items.map(x => { + return (x.count / chart.professionsDistributionDataForLocal!.all) * 100; + }) ?? [], + backgroundColor: new RandomRgbColor().toString(0.8), + } + ); + super( canvasId, { type: 'bar', data: { - labels: ["Казахстанские компании", "Иностранные компании"], + labels: professions.map(x => UserProfessionEnum.label(x)), datasets: datasets, }, options: { @@ -32,11 +51,9 @@ export class PeopleDistributionChartObject extends Chart { responsive: true, scales: { x: { - stacked: true, + min: 0, + max: 100 }, - y: { - stacked: true, - } } }, }); diff --git a/src/app/modules/salaries/salaries.module.ts b/src/app/modules/salaries/salaries.module.ts index 3c7a67f6..6146d65c 100644 --- a/src/app/modules/salaries/salaries.module.ts +++ b/src/app/modules/salaries/salaries.module.ts @@ -14,7 +14,7 @@ import { EditSalaryComponent } from './components/edit-salary/edit-salary.compon import { CitiesDoughnutChartComponent } from './components/cities-doughnut-chart/cities-doughnut-chart.component'; import { GradesMinMaxChartComponent } from './components/grades-min-max-salaries-chart/grades-min-max-chart.component'; import { SalariesSkillsChartComponent } from './components/salaries-skills-chart/salaries-skills-chart.component'; -import { PeopleDistributionChartComponent } from './components/people-distribution-chart/people-distribution-chart.component'; +import { PeopleDistributionChartComponent } from './components/professions-distribution-chart/people-distribution-chart.component'; @NgModule({ declarations: [ From c2b809de5b71769123cfb0a2711ee5f395389a8f Mon Sep 17 00:00:00 2001 From: "maxim.gorbatyuk" Date: Sat, 10 Feb 2024 10:16:13 +0600 Subject: [PATCH 5/7] changes --- .../people-distribution-chart-object.ts | 48 +++++++++++++------ .../people-distribution-chart.component.scss | 5 +- .../salaries-chart/salaries-chart.ts | 13 +---- .../salaries-chart/stub-salaries-chart.ts | 4 -- src/app/services/user-salaries.service.ts | 16 ------- 5 files changed, 36 insertions(+), 50 deletions(-) diff --git a/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart-object.ts b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart-object.ts index 52d5ab50..fbc7ffe0 100644 --- a/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart-object.ts +++ b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart-object.ts @@ -3,7 +3,7 @@ import { RandomRgbColor } from '../random-rgb-color'; import { KazakhstanCity, KazakhstanCityEnum } from '@models/salaries/kazakhstan-city'; import { UserSalary } from '@models/salaries/salary.model'; import { SalariesChart } from '../salaries-chart/salaries-chart'; -import { UserProfessionEnum } from '@models/salaries/user-profession'; +import { UserProfession, UserProfessionEnum } from '@models/salaries/user-profession'; import { CompanyType } from '@models/salaries/company-type'; interface ChartDatasetType { @@ -19,24 +19,42 @@ export class PeopleDistributionChartObject extends Chart { constructor(canvasId: string, chart: SalariesChart) { const datasets: Array = []; - const professions = chart.professionsDistributionDataForLocal?.items.map(x => x.profession) ?? []; - chart.professionsDistributionDataForRemote?.items.forEach(item => { - if (professions.some(x => x === item.profession)) { + const professions: Array = []; + chart.salaries.forEach(x => { + if (professions.some(y => y === x.profession)) { return; } - professions.push(item.profession); + professions.push(x.profession); }); - datasets.push( - { - label: "Казахстанская компания", - data: chart.professionsDistributionDataForLocal?.items.map(x => { - return (x.count / chart.professionsDistributionDataForLocal!.all) * 100; - }) ?? [], - backgroundColor: new RandomRgbColor().toString(0.8), - } - ); + console.log('Professions', professions); + + const salariesLocal = chart.salaries.filter(x => x.company === CompanyType.Local); + if (salariesLocal.length > 0) { + datasets.push( + { + label: "Казахстанская компания", + data: professions.map(x => { + return (salariesLocal.filter(s => s.profession === x).length / salariesLocal.length) * 100; + }), + backgroundColor: new RandomRgbColor().toString(0.8), + } + ); + } + + const salariesRemote = chart.salaries.filter(x => x.company === CompanyType.Remote); + if (salariesRemote.length > 0) { + datasets.push( + { + label: "Иностранная компания", + data: professions.map(x => { + return (salariesRemote.filter(s => s.profession === x).length / salariesRemote.length) * 100; + }), + backgroundColor: new RandomRgbColor().toString(0.8), + } + ); + } super( canvasId, @@ -48,7 +66,7 @@ export class PeopleDistributionChartObject extends Chart { }, options: { indexAxis: 'y', - responsive: true, + responsive: false, scales: { x: { min: 0, diff --git a/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.scss b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.scss index 7a884e25..101a560c 100644 --- a/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.scss +++ b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.scss @@ -1,11 +1,10 @@ canvas { - min-height: 250px; + min-height: 450px; } #canvas-container { position: relative; width: 100%; height: 100%; - min-height: 250px; - max-height: 500px; + min-height: 450px; } \ No newline at end of file diff --git a/src/app/modules/salaries/components/salaries-chart/salaries-chart.ts b/src/app/modules/salaries/components/salaries-chart/salaries-chart.ts index c091d421..e1c1520e 100644 --- a/src/app/modules/salaries/components/salaries-chart/salaries-chart.ts +++ b/src/app/modules/salaries/components/salaries-chart/salaries-chart.ts @@ -1,5 +1,5 @@ import { formatNumber } from "@angular/common"; -import { DevelopersByGradeDistributionData, DevelopersByProfessionsDistributionData, SalariesByMoneyBarChart, SalariesChartResponse } from "@services/user-salaries.service"; +import { SalariesByMoneyBarChart, SalariesChartResponse } from "@services/user-salaries.service"; import { SalariesPerProfession } from "../salaries-per-profession"; import { UserSalary, UserSalaryAdminDto } from "@models/salaries/salary.model"; @@ -23,12 +23,6 @@ export class SalariesChart { readonly currentUserSalary: UserSalaryAdminDto | null = null; readonly currentUserSalaryValue: string | null = null; - readonly developersByGradeDistributionDataForLocal: DevelopersByGradeDistributionData | null; - readonly developersByGradeDistributionDataForRemote: DevelopersByGradeDistributionData | null; - - readonly professionsDistributionDataForLocal: DevelopersByProfessionsDistributionData | null; - readonly professionsDistributionDataForRemote: DevelopersByProfessionsDistributionData | null; - readonly hasRemoteSalaries: boolean; constructor(readonly data: SalariesChartResponse) { @@ -54,11 +48,6 @@ export class SalariesChart { this.currentUserSalaryValue = data.currentUserSalary ? SalariesChart.formatNumber(data.currentUserSalary.value) : null; - - this.developersByGradeDistributionDataForLocal = data.developersByGradeDistributionDataForLocal; - this.developersByGradeDistributionDataForRemote = data.developersByGradeDistributionDataForRemote; - this.professionsDistributionDataForLocal = data.professionsDistributionDataForLocal; - this.professionsDistributionDataForRemote = data.professionsDistributionDataForRemote; } private static formatNumber(value: number | null): string | null { diff --git a/src/app/modules/salaries/components/salaries-chart/stub-salaries-chart.ts b/src/app/modules/salaries/components/salaries-chart/stub-salaries-chart.ts index b879f700..ca4ac7ab 100644 --- a/src/app/modules/salaries/components/salaries-chart/stub-salaries-chart.ts +++ b/src/app/modules/salaries/components/salaries-chart/stub-salaries-chart.ts @@ -34,10 +34,6 @@ export class StubSalariesChart extends SalariesChart { salariesByMoneyBarChartForRemote: null, rangeStart: new Date(), rangeEnd: new Date(), - developersByGradeDistributionDataForLocal: null, - developersByGradeDistributionDataForRemote: null, - professionsDistributionDataForLocal: null, - professionsDistributionDataForRemote: null, }); } diff --git a/src/app/services/user-salaries.service.ts b/src/app/services/user-salaries.service.ts index c5c4183e..ad904c71 100644 --- a/src/app/services/user-salaries.service.ts +++ b/src/app/services/user-salaries.service.ts @@ -42,12 +42,6 @@ export interface SalariesChartResponse { salariesByMoneyBarChartForRemote: SalariesByMoneyBarChart | null; currentUserSalary: UserSalaryAdminDto | null; - - developersByGradeDistributionDataForLocal: DevelopersByGradeDistributionData | null; - developersByGradeDistributionDataForRemote: DevelopersByGradeDistributionData | null; - - professionsDistributionDataForLocal: DevelopersByProfessionsDistributionData | null; - professionsDistributionDataForRemote: DevelopersByProfessionsDistributionData | null; } export interface SalariesByProfessionMoneyBarChartItem { @@ -92,16 +86,6 @@ export interface SalariesAddingTrendAdminChart { salariesPerUser: number; } -export interface DevelopersByProfessionsDistributionData { - all: number; - items: Array<{profession: UserProfession, count: number}>; -} - -export interface DevelopersByGradeDistributionData { - all: number; - items: Array<{grade: DeveloperGrade | null, count: number}>; -} - export enum SalariesAdminOrderingType { Undefined = 0, CreatedAtAsc = 1, From 53987fb5d3fc30754a2abb8ec81ba0ac1da6b211 Mon Sep 17 00:00:00 2001 From: "maxim.gorbatyuk" Date: Sat, 10 Feb 2024 11:03:06 +0600 Subject: [PATCH 6/7] Adjust --- .../people-distribution-chart-object.ts | 73 +++++++++++++------ .../people-distribution-chart.component.scss | 9 ++- 2 files changed, 59 insertions(+), 23 deletions(-) diff --git a/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart-object.ts b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart-object.ts index fbc7ffe0..14816859 100644 --- a/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart-object.ts +++ b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart-object.ts @@ -9,7 +9,7 @@ import { CompanyType } from '@models/salaries/company-type'; interface ChartDatasetType { label: string; data: Array; - backgroundColor: string; + backgroundColor: Array; } export class PeopleDistributionChartObject extends Chart { @@ -19,59 +19,90 @@ export class PeopleDistributionChartObject extends Chart { constructor(canvasId: string, chart: SalariesChart) { const datasets: Array = []; - const professions: Array = []; + let professions: Array<{profession: UserProfession, label: string, count: number}> = []; chart.salaries.forEach(x => { - if (professions.some(y => y === x.profession)) { + + const existingItem = professions.find(p => p.profession === x.profession); + if (existingItem != null) { + existingItem.count++; return; } - professions.push(x.profession); + professions.push({ + profession: x.profession, + label: UserProfessionEnum.label(x.profession), + count: 1, + }); }); - console.log('Professions', professions); + professions = professions.sort((a, b) => b.count - a.count); + + const professionsToInclude = professions.filter((x) => x.count > 10); + const salariesNotINcluded = professions.filter((x) => x.count <= 10); const salariesLocal = chart.salaries.filter(x => x.company === CompanyType.Local); if (salariesLocal.length > 0) { + + const dataForDataset = professionsToInclude.map(x => { + return { + value: (salariesLocal.filter(s => s.profession === x.profession).length / salariesLocal.length) * 100, + color: new RandomRgbColor().toString(0.8), + }; + }); + + if (salariesNotINcluded.length > 0) { + const count = salariesNotINcluded.map(x => x.count).reduce((a, b) => a + b); + dataForDataset.push({ + value: count / salariesLocal.length * 100, + color: new RandomRgbColor().toString(0.8), + }); + } + datasets.push( { - label: "Казахстанская компания", - data: professions.map(x => { - return (salariesLocal.filter(s => s.profession === x).length / salariesLocal.length) * 100; - }), - backgroundColor: new RandomRgbColor().toString(0.8), + label: "Специальность", + data: dataForDataset.map(x => x.value), + backgroundColor: dataForDataset.map(x => x.color), } ); } - const salariesRemote = chart.salaries.filter(x => x.company === CompanyType.Remote); + /*const salariesRemote = chart.salaries.filter(x => x.company === CompanyType.Remote); if (salariesRemote.length > 0) { datasets.push( { label: "Иностранная компания", data: professions.map(x => { - return (salariesRemote.filter(s => s.profession === x).length / salariesRemote.length) * 100; + return (salariesRemote.filter(s => s.profession === x.profession).length / salariesRemote.length) * 100; }), backgroundColor: new RandomRgbColor().toString(0.8), } ); + }*/ + + const labels = professionsToInclude.map(x => x.label); + if (salariesNotINcluded.length > 0) { + labels.push("Другие"); } super( canvasId, { - type: 'bar', + type: 'doughnut', data: { - labels: professions.map(x => UserProfessionEnum.label(x)), + labels: labels, datasets: datasets, }, options: { - indexAxis: 'y', - responsive: false, - scales: { - x: { - min: 0, - max: 100 - }, + aspectRatio: 1, + responsive: true, + plugins: { + legend: { + position: 'left', + title: { + display: false, + }, + } } }, }); diff --git a/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.scss b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.scss index 101a560c..b5b84b83 100644 --- a/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.scss +++ b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.scss @@ -1,10 +1,15 @@ canvas { - min-height: 450px; + min-height: 250px; } #canvas-container { position: relative; width: 100%; height: 100%; - min-height: 450px; + min-height: 250px; + max-height: 500px; +} + +.list-group-item { + font-size: smaller; } \ No newline at end of file From e6f50bba18b647adcbaadd3e3cdc71a53d1de381 Mon Sep 17 00:00:00 2001 From: "maxim.gorbatyuk" Date: Sat, 10 Feb 2024 11:27:28 +0600 Subject: [PATCH 7/7] adjusted --- ...alaries-admin-paginated-table.component.ts | 1 - .../people-distribution-chart-object.ts | 45 ++++++++----------- .../people-distribution-chart.component.html | 20 +++++++-- .../people-distribution-chart.component.ts | 42 +++++++++++++++-- .../salaries-chart.component.html | 12 ++--- 5 files changed, 78 insertions(+), 42 deletions(-) diff --git a/src/app/modules/admin/components/salaries/salaries-admin-paginated-table/salaries-admin-paginated-table.component.ts b/src/app/modules/admin/components/salaries/salaries-admin-paginated-table/salaries-admin-paginated-table.component.ts index a7041ff7..e4335dd5 100644 --- a/src/app/modules/admin/components/salaries/salaries-admin-paginated-table/salaries-admin-paginated-table.component.ts +++ b/src/app/modules/admin/components/salaries/salaries-admin-paginated-table/salaries-admin-paginated-table.component.ts @@ -63,7 +63,6 @@ export class SalariesAdminPaginatedTableComponent { 'Are you sure to delete?', () => { this.deleteRequested.emit(salary); - console.log('deleteRequested'); } ) ); diff --git a/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart-object.ts b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart-object.ts index 14816859..2f7fa569 100644 --- a/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart-object.ts +++ b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart-object.ts @@ -1,10 +1,7 @@ import { Chart } from 'chart.js/auto'; import { RandomRgbColor } from '../random-rgb-color'; -import { KazakhstanCity, KazakhstanCityEnum } from '@models/salaries/kazakhstan-city'; import { UserSalary } from '@models/salaries/salary.model'; -import { SalariesChart } from '../salaries-chart/salaries-chart'; import { UserProfession, UserProfessionEnum } from '@models/salaries/user-profession'; -import { CompanyType } from '@models/salaries/company-type'; interface ChartDatasetType { label: string; @@ -16,11 +13,15 @@ export class PeopleDistributionChartObject extends Chart { private readonly datasets: Array = []; - constructor(canvasId: string, chart: SalariesChart) { + constructor( + canvasId: string, + salaries: Array, + otherLimit: number, + title: string) { const datasets: Array = []; let professions: Array<{profession: UserProfession, label: string, count: number}> = []; - chart.salaries.forEach(x => { + salaries.forEach(x => { const existingItem = professions.find(p => p.profession === x.profession); if (existingItem != null) { @@ -37,15 +38,14 @@ export class PeopleDistributionChartObject extends Chart { professions = professions.sort((a, b) => b.count - a.count); - const professionsToInclude = professions.filter((x) => x.count > 10); - const salariesNotINcluded = professions.filter((x) => x.count <= 10); + const professionsToInclude = professions.filter((x) => x.count > otherLimit); + const salariesNotINcluded = professions.filter((x) => x.count <= otherLimit); - const salariesLocal = chart.salaries.filter(x => x.company === CompanyType.Local); - if (salariesLocal.length > 0) { + if (salaries.length > 0) { const dataForDataset = professionsToInclude.map(x => { return { - value: (salariesLocal.filter(s => s.profession === x.profession).length / salariesLocal.length) * 100, + value: (salaries.filter(s => s.profession === x.profession).length / salaries.length) * 100, color: new RandomRgbColor().toString(0.8), }; }); @@ -53,33 +53,20 @@ export class PeopleDistributionChartObject extends Chart { if (salariesNotINcluded.length > 0) { const count = salariesNotINcluded.map(x => x.count).reduce((a, b) => a + b); dataForDataset.push({ - value: count / salariesLocal.length * 100, + value: count / salaries.length * 100, color: new RandomRgbColor().toString(0.8), }); } datasets.push( { - label: "Специальность", + label: "Процентное соотношение", data: dataForDataset.map(x => x.value), backgroundColor: dataForDataset.map(x => x.color), } ); } - /*const salariesRemote = chart.salaries.filter(x => x.company === CompanyType.Remote); - if (salariesRemote.length > 0) { - datasets.push( - { - label: "Иностранная компания", - data: professions.map(x => { - return (salariesRemote.filter(s => s.profession === x.profession).length / salariesRemote.length) * 100; - }), - backgroundColor: new RandomRgbColor().toString(0.8), - } - ); - }*/ - const labels = professionsToInclude.map(x => x.label); if (salariesNotINcluded.length > 0) { labels.push("Другие"); @@ -94,13 +81,17 @@ export class PeopleDistributionChartObject extends Chart { datasets: datasets, }, options: { - aspectRatio: 1, responsive: true, plugins: { legend: { position: 'left', title: { - display: false, + text: title, + font: { + size: 16, + }, + position: 'start', + display: true, }, } } diff --git a/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.html b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.html index a299117f..e16b4cbe 100644 --- a/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.html +++ b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.html @@ -1,8 +1,20 @@
-
Расппределение респондентов по специальностям
-
-
- {{chartDataLocal}} +
Распределение по специальностям
+ +
График отображает распределение респондентов по специальностям в процентном соотношении к общему числу
+
+ +
+
+ {{chartDataLocal}} +
+ +
+
+ {{chartDataRemote}} +
+
+
\ No newline at end of file diff --git a/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.ts b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.ts index 4682c4ce..f56ce699 100644 --- a/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.ts +++ b/src/app/modules/salaries/components/professions-distribution-chart/people-distribution-chart.component.ts @@ -2,6 +2,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { UserSalary, UserSalaryAdminDto } from '@models/salaries/salary.model'; import { PeopleDistributionChartObject } from './people-distribution-chart-object'; import { SalariesChart } from '../salaries-chart/salaries-chart'; +import { CompanyType } from '@models/salaries/company-type'; @Component({ selector: 'app-people-distribution-chart', @@ -14,8 +15,10 @@ export class PeopleDistributionChartComponent { chart: SalariesChart | null = null; chartDataLocal: PeopleDistributionChartObject | null = null; + chartDataRemote: PeopleDistributionChartObject | null = null; - readonly canvasId = 'canvas_' + Math.random().toString(36).substring(7); + readonly canvasIdLocal = 'canvas_' + Math.random().toString(36); + readonly canvasIdRemote = 'canvas_' + Math.random().toString(36); constructor() {} @@ -24,15 +27,46 @@ export class PeopleDistributionChartComponent { } private initChart(): void { - if (this.chart == null) { + if (this.chart == null || this.chart.salaries.length == 0) { return; } - this.chartDataLocal = new PeopleDistributionChartObject(this.canvasId, this.chart); + const localSalaries = this.chart.salaries.filter(x => x.company === CompanyType.Local); + const remoteSalaries = this.chart.salaries.filter(x => x.company === CompanyType.Remote); - var chartEl = document.getElementById(this.canvasId); + if (localSalaries.length > 0) { + this.chartDataLocal = this.initChartWithParams( + this.canvasIdLocal, + localSalaries, + 10, + "Казахстан"); + } + + if (remoteSalaries.length > 0) { + this.chartDataRemote = this.initChartWithParams( + this.canvasIdRemote, + remoteSalaries, + 3, + "Мир (удаленка)"); + } + } + + private initChartWithParams( + canvasId: string, + salaries: Array, + otherLimit: number, + title: string): PeopleDistributionChartObject { + const chart = new PeopleDistributionChartObject( + canvasId, + salaries, + otherLimit, + title); + + var chartEl = document.getElementById(canvasId); if (chartEl != null && chartEl.parentElement != null) { chartEl.style.height = chartEl?.parentElement.style.height ?? '100%'; } + + return chart; } } diff --git a/src/app/modules/salaries/components/salaries-chart/salaries-chart.component.html b/src/app/modules/salaries/components/salaries-chart/salaries-chart.component.html index f5cfe7e1..b8fa4626 100644 --- a/src/app/modules/salaries/components/salaries-chart/salaries-chart.component.html +++ b/src/app/modules/salaries/components/salaries-chart/salaries-chart.component.html @@ -104,12 +104,6 @@
-
- -
-
+
+ +
+