diff --git a/src/app/components/admin-navbar/admin-navbar.component.ts b/src/app/components/admin-navbar/admin-navbar.component.ts index b9af29c..fa5eb76 100644 --- a/src/app/components/admin-navbar/admin-navbar.component.ts +++ b/src/app/components/admin-navbar/admin-navbar.component.ts @@ -123,6 +123,26 @@ export class AdminNavbarComponent { }, ], }, + { + title: "Github bot", + links: [ + { + title: "Запрошенные профили", + url: "/admin/github/profiles", + isExternal: false, + }, + { + title: "Чаты", + url: "/admin/github/chats", + isExternal: false, + }, + { + title: "Джобы", + url: "/admin/github/processing-jobs", + isExternal: false, + }, + ], + }, { title: "Инструменты", links: [ diff --git a/src/app/models/github.ts b/src/app/models/github.ts new file mode 100644 index 0000000..d2f847c --- /dev/null +++ b/src/app/models/github.ts @@ -0,0 +1,24 @@ +export interface GitHubProfile { + username: string; + version: number; + requestsCount: number; + dataSyncedAt: Date; + createdAt: Date; + updatedAt: Date; +} + +export interface GitHubChat { + id: string; + chatId: number; + username: string; + isAdmin: boolean; + messagesCount: number; + createdAt: Date; + updatedAt: Date; +} + +export interface GitHubProcessingJob { + username: string; + createdAt: Date; + updatedAt: Date; +} diff --git a/src/app/modules/admin/admin-routing.module.ts b/src/app/modules/admin/admin-routing.module.ts index 417c3ab..058c8f0 100644 --- a/src/app/modules/admin/admin-routing.module.ts +++ b/src/app/modules/admin/admin-routing.module.ts @@ -19,6 +19,9 @@ import { CompaniesAdminPageComponent } from "./components/companies/companies-ad import { CompanyAdminPageComponent } from "./components/companies/company-admin-page/company-admin-page.component"; import { ReviewsToApprovePageComponent } from "./components/companies/reviews-to-approve/reviews-to-approve-page.component"; import { InlineRepliesStatsComponent } from "./components/telegram/inline-replies-stats/inline-replies-stats.component"; +import { GitHubProfilesPageComponent } from "./components/github/github-profiles-page/github-profiles-page.component"; +import { GitHubChatsPageComponent } from "./components/github/github-chats-page/github-chats-page.component"; +import { GitHubJobsPageComponent } from "./components/github/github-jobs-page/github-jobs-page.component"; const routes: Routes = [ { path: "", component: AdminStartPageComponent }, @@ -61,6 +64,10 @@ const routes: Routes = [ component: ReviewsToApprovePageComponent, }, { path: "companies/:id", component: CompanyAdminPageComponent }, + + { path: "github/profiles", component: GitHubProfilesPageComponent }, + { path: "github/chats", component: GitHubChatsPageComponent }, + { path: "github/processing-jobs", component: GitHubJobsPageComponent }, ]; @NgModule({ diff --git a/src/app/modules/admin/admin.module.ts b/src/app/modules/admin/admin.module.ts index f267ede..64eaea5 100644 --- a/src/app/modules/admin/admin.module.ts +++ b/src/app/modules/admin/admin.module.ts @@ -23,12 +23,16 @@ import { StatDataCacheRecordsComponent } from "./components/telegram/stat-data-c import { GenerateQrPageComponent } from "./components/generate-qr-code-page/generate-qr-page.component"; import { CurrenciesPageComponent } from "./components/currencies-page/currencies-page.component"; import { AdminDashboardService } from "./services/admin-dashboard.service"; +import { GitHubAdminService } from "@services/github-admin.service"; import { CompaniesAdminPageComponent } from "./components/companies/companies-admin-page/companies-admin-page.component"; import { CompanyAdminPageComponent } from "./components/companies/company-admin-page/company-admin-page.component"; import { ReviewsToApprovePageComponent } from "./components/companies/reviews-to-approve/reviews-to-approve-page.component"; import { InlineRepliesStatsComponent } from "./components/telegram/inline-replies-stats/inline-replies-stats.component"; +import { GitHubProfilesPageComponent } from "./components/github/github-profiles-page/github-profiles-page.component"; +import { GitHubChatsPageComponent } from "./components/github/github-chats-page/github-chats-page.component"; +import { GitHubJobsPageComponent } from "./components/github/github-jobs-page/github-jobs-page.component"; -const adminServices = [AdminDashboardService]; +const adminServices = [AdminDashboardService, GitHubAdminService]; @NgModule({ declarations: [ @@ -54,6 +58,9 @@ const adminServices = [AdminDashboardService]; CompanyAdminPageComponent, ReviewsToApprovePageComponent, InlineRepliesStatsComponent, + GitHubProfilesPageComponent, + GitHubChatsPageComponent, + GitHubJobsPageComponent, ], imports: [ CommonModule, diff --git a/src/app/modules/admin/components/companies/company-admin-page/company-admin-page.component.html b/src/app/modules/admin/components/companies/company-admin-page/company-admin-page.component.html index 0cc43d0..18e2f5b 100644 --- a/src/app/modules/admin/components/companies/company-admin-page/company-admin-page.component.html +++ b/src/app/modules/admin/components/companies/company-admin-page/company-admin-page.component.html @@ -199,18 +199,18 @@
Среднее: {{ - reviewToShow?.totalRating | number: "1.2-2" + reviewToShow.totalRating | number: "1.2-2" }}
Плюсы
-
+
Минусы
-
+
diff --git a/src/app/modules/admin/components/github/github-chats-page/github-chats-page.component.html b/src/app/modules/admin/components/github/github-chats-page/github-chats-page.component.html new file mode 100644 index 0000000..2dd7ca6 --- /dev/null +++ b/src/app/modules/admin/components/github/github-chats-page/github-chats-page.component.html @@ -0,0 +1,85 @@ +Чаты с Github Bot + +
+
+
+ +
+
+ + +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + + + + + + +
Chat IDUsernameОбращенийAdmin?СозданоОбновлено
{{ chat.chatId }}{{ chat.username }}{{ chat.messagesCount | formatAsMoney }}{{ chat.isAdmin }}{{ chat.createdAt | date: "yyyy-MM-dd HH:mm" }}{{ chat.updatedAt | date: "yyyy-MM-dd HH:mm" }}
+
+ +
+ +
+
+
+
+ + +
+
+
Нет данных.
+
+
+
+ + +
+ +
+
diff --git a/src/app/modules/admin/components/github/github-chats-page/github-chats-page.component.spec.ts b/src/app/modules/admin/components/github/github-chats-page/github-chats-page.component.spec.ts new file mode 100644 index 0000000..e570eb0 --- /dev/null +++ b/src/app/modules/admin/components/github/github-chats-page/github-chats-page.component.spec.ts @@ -0,0 +1,33 @@ +import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { + mostUsedImports, + testUtilStubs, + mostUsedServices, +} from "@shared/test-utils"; +import { GitHubChatsPageComponent } from "./github-chats-page.component"; +import { GitHubAdminService } from "@services/github-admin.service"; + +describe("GitHubChatsPageComponent", () => { + let component: GitHubChatsPageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [GitHubChatsPageComponent], + imports: [...mostUsedImports], + providers: [...testUtilStubs, ...mostUsedServices, GitHubAdminService], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(GitHubChatsPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/modules/admin/components/github/github-chats-page/github-chats-page.component.ts b/src/app/modules/admin/components/github/github-chats-page/github-chats-page.component.ts new file mode 100644 index 0000000..c66ca63 --- /dev/null +++ b/src/app/modules/admin/components/github/github-chats-page/github-chats-page.component.ts @@ -0,0 +1,64 @@ +import { Component, OnDestroy, OnInit } from "@angular/core"; +import { defaultPageParams } from "@models/page-params"; +import { PaginatedList } from "@models/paginated-list"; +import { TitleService } from "@services/title.service"; +import { untilDestroyed } from "@shared/subscriptions/until-destroyed"; +import { GitHubAdminService } from "@services/github-admin.service"; +import { GitHubChat } from "@models/github"; +import { ConfirmMsg } from "@shared/components/dialogs/models/confirm-msg"; +import { DialogMessage } from "@shared/components/dialogs/models/dialog-message"; + +@Component({ + templateUrl: "./github-chats-page.component.html", + standalone: false, +}) +export class GitHubChatsPageComponent implements OnInit, OnDestroy { + chats: Array | null = null; + source: PaginatedList | null = null; + currentPage: number = 1; + searchQuery: string = ""; + + constructor( + private readonly service: GitHubAdminService, + titleService: TitleService, + ) { + titleService.setTitle("GitHub Telegram Bot Chats"); + } + + ngOnInit(): void { + this.chats = null; + this.source = null; + this.loadData(this.currentPage); + } + + loadData(pageToRequest: number): void { + this.chats = null; + this.source = null; + this.currentPage = pageToRequest; + + this.service + .getChats({ + page: this.currentPage, + pageSize: defaultPageParams.pageSize, + search: this.searchQuery || null, + }) + .pipe(untilDestroyed(this)) + .subscribe((x) => { + this.chats = x.results; + this.source = x; + }); + } + + onSearch(): void { + this.loadData(1); + } + + clearSearch(): void { + this.searchQuery = ""; + this.loadData(1); + } + + ngOnDestroy(): void { + // ignored + } +} diff --git a/src/app/modules/admin/components/github/github-jobs-page/github-jobs-page.component.html b/src/app/modules/admin/components/github/github-jobs-page/github-jobs-page.component.html new file mode 100644 index 0000000..c8f42ed --- /dev/null +++ b/src/app/modules/admin/components/github/github-jobs-page/github-jobs-page.component.html @@ -0,0 +1,65 @@ +GitHub джобы + +
+
+
+
+ +
+ +
+ + + + + + + + + + + + + + +
UsernameСозданоОбновлено
{{ job.username }}{{ job.createdAt | date: "yyyy-MM-dd HH:mm" }}{{ job.updatedAt | date: "yyyy-MM-dd HH:mm" }} + +
+
+
+
+
+ + +
+
+
Нет джобов.
+
+
+
+ + +
+ +
+
+ + diff --git a/src/app/modules/admin/components/github/github-jobs-page/github-jobs-page.component.spec.ts b/src/app/modules/admin/components/github/github-jobs-page/github-jobs-page.component.spec.ts new file mode 100644 index 0000000..d62ee79 --- /dev/null +++ b/src/app/modules/admin/components/github/github-jobs-page/github-jobs-page.component.spec.ts @@ -0,0 +1,33 @@ +import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { + mostUsedImports, + testUtilStubs, + mostUsedServices, +} from "@shared/test-utils"; +import { GitHubJobsPageComponent } from "./github-jobs-page.component"; +import { GitHubAdminService } from "@services/github-admin.service"; + +describe("GitHubJobsPageComponent", () => { + let component: GitHubJobsPageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [GitHubJobsPageComponent], + imports: [...mostUsedImports], + providers: [...testUtilStubs, ...mostUsedServices, GitHubAdminService], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(GitHubJobsPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/modules/admin/components/github/github-jobs-page/github-jobs-page.component.ts b/src/app/modules/admin/components/github/github-jobs-page/github-jobs-page.component.ts new file mode 100644 index 0000000..fc3e7da --- /dev/null +++ b/src/app/modules/admin/components/github/github-jobs-page/github-jobs-page.component.ts @@ -0,0 +1,64 @@ +import { Component, OnDestroy, OnInit } from "@angular/core"; +import { TitleService } from "@services/title.service"; +import { untilDestroyed } from "@shared/subscriptions/until-destroyed"; +import { GitHubAdminService } from "@services/github-admin.service"; +import { GitHubProcessingJob } from "@models/github"; +import { ConfirmMsg } from "@shared/components/dialogs/models/confirm-msg"; +import { DialogMessage } from "@shared/components/dialogs/models/dialog-message"; + +@Component({ + templateUrl: "./github-jobs-page.component.html", + standalone: false, +}) +export class GitHubJobsPageComponent implements OnInit, OnDestroy { + jobs: Array | null = null; + confirmDeletionMessage: DialogMessage | null = null; + + constructor( + private readonly service: GitHubAdminService, + titleService: TitleService, + ) { + titleService.setTitle("GitHub Джобы"); + } + + ngOnInit(): void { + this.jobs = null; + this.loadData(); + } + + loadData(): void { + this.jobs = null; + + this.service + .getProcessingJobs() + .pipe(untilDestroyed(this)) + .subscribe((x) => { + this.jobs = x; + }); + } + + openDeleteDialog(job: GitHubProcessingJob): void { + this.confirmDeletionMessage = new DialogMessage( + new ConfirmMsg( + "Удалить джобу", + `Вы уверены, что хотите удалить джобу "${job.username}"?`, + () => { + this.deleteJob(job); + }, + ), + ); + } + + deleteJob(job: GitHubProcessingJob): void { + this.service + .deleteProcessingJob(job.username) + .pipe(untilDestroyed(this)) + .subscribe(() => { + this.loadData(); + }); + } + + ngOnDestroy(): void { + // ignored + } +} diff --git a/src/app/modules/admin/components/github/github-profiles-page/github-profiles-page.component.html b/src/app/modules/admin/components/github/github-profiles-page/github-profiles-page.component.html new file mode 100644 index 0000000..d0155be --- /dev/null +++ b/src/app/modules/admin/components/github/github-profiles-page/github-profiles-page.component.html @@ -0,0 +1,102 @@ +Запрошенные GitHub профили + +
+
+
+ +
+
+ + +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + +
UsernameВерсияЗапросовПоследний синкСозданоОбновлено
+ {{ profile.username }} + {{ profile.version }}{{ profile.requestsCount }}{{ profile.dataSyncedAt | date: "yyyy-MM-dd HH:mm" }}{{ profile.createdAt | date: "yyyy-MM-dd HH:mm" }}{{ profile.updatedAt | date: "yyyy-MM-dd HH:mm" }} + +
+
+ +
+ +
+
+
+
+ + +
+
+
Нет данных.
+
+
+
+ + +
+ +
+
+ + diff --git a/src/app/modules/admin/components/github/github-profiles-page/github-profiles-page.component.spec.ts b/src/app/modules/admin/components/github/github-profiles-page/github-profiles-page.component.spec.ts new file mode 100644 index 0000000..3cb48a4 --- /dev/null +++ b/src/app/modules/admin/components/github/github-profiles-page/github-profiles-page.component.spec.ts @@ -0,0 +1,33 @@ +import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { + mostUsedImports, + testUtilStubs, + mostUsedServices, +} from "@shared/test-utils"; +import { GitHubProfilesPageComponent } from "./github-profiles-page.component"; +import { GitHubAdminService } from "@services/github-admin.service"; + +describe("GitHubProfilesPageComponent", () => { + let component: GitHubProfilesPageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [GitHubProfilesPageComponent], + imports: [...mostUsedImports], + providers: [...testUtilStubs, ...mostUsedServices, GitHubAdminService], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(GitHubProfilesPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/modules/admin/components/github/github-profiles-page/github-profiles-page.component.ts b/src/app/modules/admin/components/github/github-profiles-page/github-profiles-page.component.ts new file mode 100644 index 0000000..61e16ba --- /dev/null +++ b/src/app/modules/admin/components/github/github-profiles-page/github-profiles-page.component.ts @@ -0,0 +1,87 @@ +import { Component, OnDestroy, OnInit } from "@angular/core"; +import { defaultPageParams } from "@models/page-params"; +import { PaginatedList } from "@models/paginated-list"; +import { TitleService } from "@services/title.service"; +import { untilDestroyed } from "@shared/subscriptions/until-destroyed"; +import { GitHubAdminService } from "@services/github-admin.service"; +import { GitHubProfile } from "@models/github"; +import { ConfirmMsg } from "@shared/components/dialogs/models/confirm-msg"; +import { DialogMessage } from "@shared/components/dialogs/models/dialog-message"; + +@Component({ + templateUrl: "./github-profiles-page.component.html", + standalone: false, +}) +export class GitHubProfilesPageComponent implements OnInit, OnDestroy { + profiles: Array | null = null; + source: PaginatedList | null = null; + currentPage: number = 1; + searchQuery: string = ""; + + confirmDeletionMessage: DialogMessage | null = null; + + constructor( + private readonly service: GitHubAdminService, + titleService: TitleService, + ) { + titleService.setTitle("GitHub профили"); + } + + ngOnInit(): void { + this.profiles = null; + this.source = null; + this.loadData(this.currentPage); + } + + loadData(pageToRequest: number): void { + this.profiles = null; + this.source = null; + this.currentPage = pageToRequest; + + this.service + .getProfiles({ + page: this.currentPage, + pageSize: defaultPageParams.pageSize, + search: this.searchQuery || null, + }) + .pipe(untilDestroyed(this)) + .subscribe((x) => { + this.profiles = x.results; + this.source = x; + }); + } + + onSearch(): void { + this.loadData(1); + } + + clearSearch(): void { + this.searchQuery = ""; + this.loadData(1); + } + + ngOnDestroy(): void { + // ignored + } + + deleteProfile(profile: GitHubProfile): void { + this.service + .deleteProfile(profile.username) + .pipe(untilDestroyed(this)) + .subscribe(() => { + this.loadData(this.currentPage); + }); + } + + openDeleteDialog(profile: GitHubProfile): void { + this.confirmDeletionMessage = new DialogMessage( + new ConfirmMsg( + "Удалить кэш профиля", + `Вы уверены, что хотите удалить кэш профиля "${profile.username}"?`, + () => { + this.deleteProfile(profile); + }, + ), + ); + } +} diff --git a/src/app/modules/home/components/about-us/about-us.component.html b/src/app/modules/home/components/about-us/about-us.component.html index 82de679..ff5ff50 100644 --- a/src/app/modules/home/components/about-us/about-us.component.html +++ b/src/app/modules/home/components/about-us/about-us.component.html @@ -333,6 +333,75 @@
+
+
+
Наши друзья
+
+ Админы нескольких телеграм-каналов решили помочь проекту, внедрив + регулярные апдейты зарплат по специальностям. +
+ +
+
+
Технологии
diff --git a/src/app/modules/home/components/telegram-bot/telegram-bot.component.html b/src/app/modules/home/components/telegram-bot/telegram-bot.component.html index 206ea56..73aa122 100644 --- a/src/app/modules/home/components/telegram-bot/telegram-bot.component.html +++ b/src/app/modules/home/components/telegram-bot/telegram-bot.component.html @@ -5,9 +5,10 @@
Да, у нас есть телеграм бот - @techinterview_salaries_bot, который полезен в - телеграм чатах. Самый стандартный usecase - ниже на скриншоте (картинки - кликабельные): + @techinterview_salaries_bot, который полезен в телеграм чатах. Самый стандартный usecase - ниже на + скриншоте (картинки кликабельные):

Для того, чтоб ответить таким сообщением, нужно тегнуть бота - @techinterview_salaries_bot, а затем кликнуть/тапнуть на вариант "Все специальности" в - выпадающем меню или начать вводить название специальности, чтобы - отфильтровать анкеты. + @techinterview_salaries_bot, а затем + кликнуть/тапнуть на вариант "Все специальности" в выпадающем меню или + начать вводить название специальности, чтобы отфильтровать анкеты.

Ниже представлена гифка, как это выглядит в деле (гифки кликабельные): diff --git a/src/app/services/github-admin.service.ts b/src/app/services/github-admin.service.ts new file mode 100644 index 0000000..266d913 --- /dev/null +++ b/src/app/services/github-admin.service.ts @@ -0,0 +1,54 @@ +import { Injectable } from "@angular/core"; +import { defaultPageParams, PageParams } from "@models/page-params"; +import { PaginatedList } from "@models/paginated-list"; +import { ConvertObjectToHttpParams } from "@shared/value-objects/convert-object-to-http"; +import { Observable } from "rxjs"; +import { ApiService } from "./api.service"; +import { GitHubProfile, GitHubChat, GitHubProcessingJob } from "@models/github"; + +export interface GitHubProfilesQueryParams extends PageParams { + search: string | null; +} + +export interface GitHubChatsQueryParams extends PageParams { + search: string | null; +} + +@Injectable() +export class GitHubAdminService { + private readonly apiUrl: string; + + constructor(private readonly api: ApiService) { + this.apiUrl = `/api/github/`; + } + + getProfiles( + params: GitHubProfilesQueryParams, + ): Observable> { + const httpParams = new ConvertObjectToHttpParams(params).get(); + return this.api.get>( + this.apiUrl + "profiles?" + httpParams, + ); + } + + getChats( + params: GitHubChatsQueryParams, + ): Observable> { + const httpParams = new ConvertObjectToHttpParams(params).get(); + return this.api.get>( + this.apiUrl + "chats?" + httpParams, + ); + } + + getProcessingJobs(): Observable { + return this.api.get(this.apiUrl + "processing-jobs"); + } + + deleteProcessingJob(username: string): Observable { + return this.api.delete(this.apiUrl + "processing-jobs/" + username); + } + + deleteProfile(username: string): Observable { + return this.api.delete(this.apiUrl + "profiles/" + username); + } +} diff --git a/src/index.html b/src/index.html index 07166be..749fe60 100644 --- a/src/index.html +++ b/src/index.html @@ -75,86 +75,44 @@