Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/app/components/admin-navbar/admin-navbar.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ export class AdminNavbarComponent {
title: "Анкеты вне статистики",
url: "/admin/salaries/not-in-stats",
},
{
title: "Импортированные анкеты",
url: "/admin/salaries/imported-salaries",
},
],
},
{
Expand Down
6 changes: 6 additions & 0 deletions src/app/models/salaries/salary.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import { Currency } from "./currency";
import { KazakhstanCity } from "./kazakhstan-city";
import { Gender } from "@models/enums/gender.enum";

export enum SalarySourceType {
Undefined = 0,
KolesaDevelopersCsv2022 = 1,
}

export interface UserSalarySimple {
value: number;
quarter: number;
Expand All @@ -24,6 +29,7 @@ export interface UserSalary extends UserSalarySimple {
skillId: number | null;
workIndustryId: number | null;
professionId: number | null;
sourceType: SalarySourceType | null;
}

export interface UserSalaryAdminDto extends UserSalary {
Expand Down
5 changes: 5 additions & 0 deletions src/app/modules/admin/admin-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { WorkIndustriesPaginatedTableComponent } from "./components/label-entiti
import { ProfessionsPaginatedTableComponent } from "./components/label-entities/professions-paginated-table/professions-paginated-table.component";
import { TelegramBotUsagesComponent } from "./components/telegram/telegram-bot-usages/telegram-bot-usages.component";
import { TelegramUserSettingsComponent } from "./components/telegram/telegram-user-settings/telegram-user-settings.component";
import { SourcedSalariesAdminPageComponent } from "./components/salaries/sourced-salaries-admin-page/sourced-salaries-admin-page.component";

const routes: Routes = [
{ path: "", component: AdminStartPageComponent },
Expand All @@ -28,6 +29,10 @@ const routes: Routes = [
path: "salaries/not-in-stats",
component: SalariesNotInStatsAdminPageComponent,
},
{
path: "salaries/imported-salaries",
component: SourcedSalariesAdminPageComponent,
},
{ path: "telegram/bot-usages", component: TelegramBotUsagesComponent },
{ path: "telegram/user-settings", component: TelegramUserSettingsComponent },
];
Expand Down
2 changes: 2 additions & 0 deletions src/app/modules/admin/admin.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { WorkIndustriesPaginatedTableComponent } from "./components/label-entiti
import { ProfessionsPaginatedTableComponent } from "./components/label-entities/professions-paginated-table/professions-paginated-table.component";
import { TelegramBotUsagesComponent } from "./components/telegram/telegram-bot-usages/telegram-bot-usages.component";
import { TelegramUserSettingsComponent } from "./components/telegram/telegram-user-settings/telegram-user-settings.component";
import { SourcedSalariesAdminPageComponent } from "./components/salaries/sourced-salaries-admin-page/sourced-salaries-admin-page.component";

@NgModule({
declarations: [
Expand All @@ -30,6 +31,7 @@ import { TelegramUserSettingsComponent } from "./components/telegram/telegram-us
SalariesAdminPageComponent,
SalariesAdminPaginatedTableComponent,
SalariesNotInStatsAdminPageComponent,
SourcedSalariesAdminPageComponent,
SkillsPaginatedTableComponent,
WorkIndustriesPaginatedTableComponent,
ProfessionsPaginatedTableComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
<th>Город</th>
<th>Навык</th>
<th>Сфера</th>
<th>Создано</th>
<th>Источник</th>
<th>Обновлено</th>
<th></th>
</thead>
Expand All @@ -114,7 +114,7 @@
<td>{{ item.city }}</td>
<td>{{ item.skill }}</td>
<td>{{ item.industry }}</td>
<td>{{ item.createdAt | date : "yyyy-MM-dd HH:mm" }}</td>
<td>{{ item.sourceType }}</td>
<td>{{ item.updatedAt | date : "yyyy-MM-dd HH:mm" }}</td>
<td>
<button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { GenderEnum } from "@models/enums/gender.enum";
import { CompanyType } from "@models/salaries/company-type";
import { Currency } from "@models/salaries/currency";
import { KazakhstanCity } from "@models/salaries/kazakhstan-city";
import { UserSalaryAdminDto } from "@models/salaries/salary.model";
import { SalarySourceType, UserSalaryAdminDto } from "@models/salaries/salary.model";
import { LabelEntityDto } from "@services/label-entity.model";

export class SalaryAdminItem {
Expand All @@ -22,6 +22,7 @@ export class SalaryAdminItem {
readonly city: string;
readonly skill: string;
readonly industry: string;
readonly sourceType: string | null;
readonly createdAt: Date;
readonly updatedAt: Date;

Expand Down Expand Up @@ -53,6 +54,11 @@ export class SalaryAdminItem {
item.workIndustryId != null
? industries.find((x) => x.id == item.workIndustryId)?.title ?? "-"
: "-";

this.sourceType = item.sourceType
? SalarySourceType[item.sourceType]
: null;

this.createdAt = item.createdAt;
this.updatedAt = item.updatedAt ?? item.createdAt;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<app-page-header>Импортированные анкеты</app-page-header>

<div class="container my-5" *ngIf="salaries; else dataLoading">
<div class="card">
<div class="card-body">
<app-salaries-admin-paginated-table
[source]="source"
[salaries]="salaries"
[filter]="filter"
[showApproveButton]="false"
(loadDataRequested)="loadData($event)"
(deleteRequested)="deleteSalary($event)"
></app-salaries-admin-paginated-table>
</div>
</div>
</div>

<ng-template #dataLoading>
<div class="container mt-5">
<app-data-loader></app-data-loader>
</div>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { UserAdminService } from "@services/user-admin.service";
import {
mostUsedImports,
testUtilStubs,
mostUsedServices,
} from "@shared/test-utils";
import { SourcedSalariesAdminPageComponent } from "./sourced-salaries-admin-page.component";

describe("SourcedSalariesAdminPageComponent", () => {
let component: SourcedSalariesAdminPageComponent;
let fixture: ComponentFixture<SourcedSalariesAdminPageComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [SourcedSalariesAdminPageComponent],
imports: [...mostUsedImports],
providers: [...testUtilStubs, ...mostUsedServices, UserAdminService],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(SourcedSalariesAdminPageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it("should create", () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Component, OnDestroy, OnInit } from "@angular/core";
import { defaultPageParams } from "@models/page-params";
import { PaginatedList } from "@models/paginated-list";
import { UserSalaryAdminDto } from "@models/salaries/salary.model";
import { TitleService } from "@services/title.service";
import {
AdminAllSalariesQueryParams,
UserSalariesService,
} from "@services/user-salaries.service";
import { untilDestroyed } from "@shared/subscriptions/until-destroyed";
import { AlertService } from "@shared/components/alert/services/alert.service";
import { SalaryAdminItem } from "../salary-admin-item";
import { SalariesTableFilter } from "../salaries-table-filter";
import { LabelEntityDto } from "@services/label-entity.model";

@Component({
templateUrl: "./sourced-salaries-admin-page.component.html",
})
export class SourcedSalariesAdminPageComponent implements OnInit, OnDestroy {
salaries: Array<SalaryAdminItem> | null = null;
source: PaginatedList<UserSalaryAdminDto> | null = null;

professions: Array<LabelEntityDto> = [];
skills: Array<LabelEntityDto> = [];
industries: Array<LabelEntityDto> = [];

filter: SalariesTableFilter | null = null;
currentPage: number = 1;

constructor(
private readonly service: UserSalariesService,
titleService: TitleService,
private readonly alert: AlertService
) {
titleService.setTitle("Импортированные анкеты");
}

ngOnInit(): void {
this.salaries = null;
this.source = null;

this.service
.selectBoxItems()
.pipe(untilDestroyed(this))
.subscribe((x) => {
this.professions = x.professions;
this.skills = x.skills;
this.industries = x.industries;

this.filter = new SalariesTableFilter(this.professions);
this.loadData({
page: this.currentPage,
pageSize: defaultPageParams.pageSize,
...this.filter,
});
});
}

loadData(data: AdminAllSalariesQueryParams): void {
this.salaries = null;
this.source = null;
this.currentPage = data.page;

this.service
.sourcedSalaries(data)
.pipe(untilDestroyed(this))
.subscribe((x) => {
this.salaries = x.results.map(
(x) =>
new SalaryAdminItem(
x,
this.professions,
this.skills,
this.industries
)
);
this.source = x;
});
}

ngOnDestroy(): void {
// ignored
}

deleteSalary(salary: SalaryAdminItem): void {
this.service
.delete(salary.id)
.pipe(untilDestroyed(this))
.subscribe(() => {
this.alert.success("Анкета удалена");
this.ngOnInit();
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,25 @@
<app-salary-chart-global-filters
[filterData]="filterData"
[professions]="professions"
[skills]="skills"
(filtersApplied)="applyGlobalFilters($event)"
(filtersReset)="resetGlobalFilters()"
(shareClicked)="share($event)"
>
</app-salary-chart-global-filters>

<div class="mt-3 alert alert-info" *ngIf="kolesaImportedSalariesWasSelected">
<div class="h5">Внимание!</div>
<div class="mt-2">
Вы просматриваете импортированную статистику <a href="https://zerttey.kolesa.group/developers-2022?utm=techinterview.space" target="_blank" class="text-reset">исследования зарплат работников в IT</a> от 2022 года, проведенного компанией
<a href="https://kolesa.group?utm=techinterview.space" target="_blank" class="text-reset">Kolesa Group</a>.
Статистика была собрана в ноябре-декабре 2021 года и включает в себя данные о зарплатах, специализациях и опыте работы.
Список валют показывает те рейты, которые были актуальны на декабрь 2021 года по данным нацбанка РК.
</div>
<div class="mt-2">
Спасибо команде Kolesa Group за предоставленные данные.
</div>
</div>
</div>

<div class="mb-5 display-4 chapter-title">
Expand Down Expand Up @@ -344,7 +358,7 @@
<span>Исходные данные</span>
</div>

<div class="mb-3">
<div class="mb-3" *ngIf="showSalariesPaginatedTable">
<app-salaries-paginated-table
[filter]="filterData"
[skills]="skills"
Expand All @@ -353,14 +367,14 @@
></app-salaries-paginated-table>
</div>

<div class="mb-3">
<div class="mb-3" *ngIf="noImportSourceWasSelected">
<app-salaries-adding-chart
[filter]="filterData"
></app-salaries-adding-chart>
</div>

<div class="mb-3">
<div class="mb-5">
<div class="mb-5" *ngIf="noImportSourceWasSelected">
<div>
Сырые данные можно выгрузить в
<button
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component, OnDestroy, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { UserSalary } from "@models/salaries/salary.model";
import { SalarySourceType, UserSalary } from "@models/salaries/salary.model";
import { UserSalariesService } from "@services/user-salaries.service";
import { untilDestroyed } from "@shared/subscriptions/until-destroyed";
import { SalariesChart } from "./salaries-chart";
Expand Down Expand Up @@ -46,6 +46,9 @@ export class SalariesChartComponent implements OnInit, OnDestroy {
isAuthenticated = false;
hasPredefinedFilter = false;
shouldShowSurveyBlock = false;
kolesaImportedSalariesWasSelected = false;
noImportSourceWasSelected = true;
showSalariesPaginatedTable = false;

gradeFilter: DeveloperGrade | null = null;

Expand Down Expand Up @@ -84,6 +87,7 @@ export class SalariesChartComponent implements OnInit, OnDestroy {
this.salariesChart = null;
this.openAddSalaryModal = false;
this.currentUserSalary = null;
this.showSalariesPaginatedTable = false;

const shouldLoadSelectBoxItems =
this.skills.length === 0 ||
Expand Down Expand Up @@ -157,7 +161,13 @@ export class SalariesChartComponent implements OnInit, OnDestroy {

this.filterData = data;
const selectedGrade = data.grade ? DeveloperGrade[data.grade] : "empty";
this.gtag.event("salaries_filters_applied", "salary_chart", selectedGrade);
const selectedSourceType = data.salarySourceType
? SalarySourceType[data.salarySourceType]
: "empty";

this.gtag.event("salaries_filters_applied_grade", "salary_chart", selectedGrade);
this.gtag.event("salaries_filters_applied_sourcetype", "salary_chart", selectedSourceType);

this.load(data);
}

Expand Down Expand Up @@ -219,12 +229,19 @@ export class SalariesChartComponent implements OnInit, OnDestroy {
private loadChartWithFilter(
data: SalaryChartGlobalFiltersData | null = null
): void {

const filterToApply = {
grade: data?.grade ?? null,
profsInclude: data?.profsInclude ?? null,
cities: data?.cities ?? null,
skills: data?.skills ?? null,
salarySourceType: data?.salarySourceType ?? null,
quarterTo: data?.quarterTo ?? null,
yearTo: data?.yearTo ?? null,
};

this.service
.charts({
grade: data?.grade ?? null,
profsInclude: data?.profsInclude ?? null,
cities: data?.cities ?? null,
})
.charts(filterToApply)
.pipe(untilDestroyed(this))
.subscribe((x) => {
this.isAuthenticated = x.hasAuthentication;
Expand All @@ -243,9 +260,16 @@ export class SalariesChartComponent implements OnInit, OnDestroy {
const developerProfessionId = 1;
this.showDataStub = false;
this.shouldShowSurveyBlock = !x.hasRecentSurveyReply;
this.showSalariesPaginatedTable = true;
this.showAdjustCurrentSalaryProfessionModal =
x.currentUserSalary != null &&
x.currentUserSalary.professionId === developerProfessionId;

this.noImportSourceWasSelected = filterToApply.salarySourceType == null;
this.kolesaImportedSalariesWasSelected
= filterToApply.salarySourceType == SalarySourceType.KolesaDevelopersCsv2022;

console.log(filterToApply.salarySourceType);
}
});
}
Expand Down
Loading