diff --git a/src/app/components/navbar/navbar.component.ts b/src/app/components/navbar/navbar.component.ts index 33f36ff1..94e36662 100644 --- a/src/app/components/navbar/navbar.component.ts +++ b/src/app/components/navbar/navbar.component.ts @@ -141,6 +141,11 @@ export class NavbarComponent implements OnInit, OnDestroy { url: "/salaries/historical-data", show: hasCurrentUser, }, + { + title: "Курсы валют", + url: "/salaries/currencies-chart", + show: hasCurrentUser, + }, ], }, ]; diff --git a/src/app/modules/admin/components/currencies-page/currencies-page.component.html b/src/app/modules/admin/components/currencies-page/currencies-page.component.html index e23dc7b0..a70581dd 100644 --- a/src/app/modules/admin/components/currencies-page/currencies-page.component.html +++ b/src/app/modules/admin/components/currencies-page/currencies-page.component.html @@ -1,26 +1,146 @@ Курсы валюты -
+
-
-
- +
+
+ +
+ +
+
- - - - + + + + + + - - - + + + + +
ВалютаКурсДата
Дата публикацииВалюты (JSON)СозданоОбновленоДействия
{{ item.currencyString }}{{ item.value }} kzt{{ item.pubDate | date: "yyyy-MM-dd" }}{{ item.currencyDate | date: "yyyy-MM-dd" }} + + {{ item.createdAt | date: "yyyy-MM-dd HH:mm" }}{{ item.updatedAt | date: "yyyy-MM-dd HH:mm" }} + +
+ +
+ +
+ + + + +
+
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ +
+
+
+ + + +
+
+ Дата: + {{ selectedCurrencyItem.currencyDate | date: "yyyy-MM-dd" }} +
+
+ Данные валют (JSON): +
{{
+        selectedCurrencyItem.currencies | json
+      }}
+
+
+
+ + +
+ +
+
+ + +
+ Нет записей о курсах валют. Создайте первую запись! +
+
diff --git a/src/app/modules/admin/components/currencies-page/currencies-page.component.scss b/src/app/modules/admin/components/currencies-page/currencies-page.component.scss index e69de29b..790d927a 100644 --- a/src/app/modules/admin/components/currencies-page/currencies-page.component.scss +++ b/src/app/modules/admin/components/currencies-page/currencies-page.component.scss @@ -0,0 +1,12 @@ +.currencies-json { + max-height: 400px; + overflow-y: auto; + font-size: 0.85rem; + white-space: pre-wrap; + word-break: break-all; +} + +code { + font-size: 0.8rem; +} + diff --git a/src/app/modules/admin/components/currencies-page/currencies-page.component.spec.ts b/src/app/modules/admin/components/currencies-page/currencies-page.component.spec.ts index e562c8c4..78b068a0 100644 --- a/src/app/modules/admin/components/currencies-page/currencies-page.component.spec.ts +++ b/src/app/modules/admin/components/currencies-page/currencies-page.component.spec.ts @@ -1,13 +1,10 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core"; -import { - mostUsedImports, - mostUsedServices, - testUtilStubs, -} from "@shared/test-utils"; - -import { AdminToolsService } from "@services/admin-tools.service"; import { CurrenciesPageComponent } from "./currencies-page.component"; +import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core"; +import { TitleService } from "@services/title.service"; +import { CurrenciesCollectionService } from "@services/currencies-collection.service"; +import { AlertService } from "@shared/components/alert/services/alert.service"; +import { of } from "rxjs"; describe("CurrenciesPageComponent", () => { let component: CurrenciesPageComponent; @@ -16,13 +13,35 @@ describe("CurrenciesPageComponent", () => { beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [CurrenciesPageComponent], - imports: [...mostUsedImports], - providers: [...testUtilStubs, ...mostUsedServices, AdminToolsService], + providers: [ + { + provide: TitleService, + useValue: { + setTitle: jasmine.createSpy("setTitle"), + resetTitle: jasmine.createSpy("resetTitle"), + }, + }, + { + provide: CurrenciesCollectionService, + useValue: { + getAll: jasmine + .createSpy("getAll") + .and.returnValue(of({ results: [], totalItems: 0 })), + create: jasmine.createSpy("create"), + delete: jasmine.createSpy("delete"), + }, + }, + { + provide: AlertService, + useValue: { + success: jasmine.createSpy("success"), + error: jasmine.createSpy("error"), + }, + }, + ], schemas: [CUSTOM_ELEMENTS_SCHEMA], }).compileComponents(); - }); - beforeEach(() => { fixture = TestBed.createComponent(CurrenciesPageComponent); component = fixture.componentInstance; fixture.detectChanges(); diff --git a/src/app/modules/admin/components/currencies-page/currencies-page.component.ts b/src/app/modules/admin/components/currencies-page/currencies-page.component.ts index 7851175b..5a1d64a5 100644 --- a/src/app/modules/admin/components/currencies-page/currencies-page.component.ts +++ b/src/app/modules/admin/components/currencies-page/currencies-page.component.ts @@ -1,9 +1,18 @@ import { Component, OnDestroy, OnInit } from "@angular/core"; import { TitleService } from "@services/title.service"; -import { HealthCheckItem } from "../health-check-table/health-check-item"; -import { AdminToolsService } from "@services/admin-tools.service"; import { untilDestroyed } from "@shared/subscriptions/until-destroyed"; import { CurrencyItem } from "./currency-item"; +import { defaultPageParams } from "@models/page-params"; +import { PaginatedList } from "@models/paginated-list"; +import { + CurrenciesCollectionService, + CurrencyRecord, +} from "@services/currencies-collection.service"; +import { DialogMessage } from "@shared/components/dialogs/models/dialog-message"; +import { ConfirmMsg } from "@shared/components/dialogs/models/confirm-msg"; +import { AlertService } from "@shared/components/alert/services/alert.service"; +import { CurrencyRecordEditForm } from "./currency-record-edit-form"; +import { CurrencyType } from "@services/admin-tools.service"; @Component({ templateUrl: "./currencies-page.component.html", @@ -11,28 +20,114 @@ import { CurrencyItem } from "./currency-item"; standalone: false, }) export class CurrenciesPageComponent implements OnInit, OnDestroy { - authorizationToken: string | null = null; - configs: string | null = null; - healthCheckItems: Array = []; - currencies: Array = []; + currencies: Array | null = null; + source: PaginatedList | null = null; + currentPage: number = 1; + + confirmDeletionMessage: DialogMessage | null = null; + editForm: CurrencyRecordEditForm | null = null; + + // For viewing currencies JSON + selectedCurrencyItem: CurrencyItem | null = null; + + // Currency options for select box + readonly currencyOptions = [ + { value: CurrencyType.USD, label: "USD" }, + { value: CurrencyType.EUR, label: "EUR" }, + { value: CurrencyType.RUB, label: "RUB" }, + { value: CurrencyType.KGS, label: "KGS" }, + { value: CurrencyType.SAR, label: "SAR" }, + { value: CurrencyType.AED, label: "AED" }, + { value: CurrencyType.CAD, label: "CAD" }, + ]; constructor( private readonly titleService: TitleService, - private readonly adminToolsService: AdminToolsService, + private readonly currenciesService: CurrenciesCollectionService, + private readonly alert: AlertService, ) { this.titleService.setTitle("Курсы валют"); } ngOnInit(): void { - this.adminToolsService - .getCurrencies() + this.currencies = null; + this.source = null; + this.loadData(this.currentPage); + } + + loadData(pageToRequest: number): void { + this.currencies = null; + this.source = null; + this.currentPage = pageToRequest; + + this.currenciesService + .getAll({ + page: this.currentPage, + pageSize: defaultPageParams.pageSize, + }) .pipe(untilDestroyed(this)) - .subscribe((currencies) => { - this.currencies = currencies.map((x) => new CurrencyItem(x)); + .subscribe((response) => { + this.currencies = response.results.map((x) => new CurrencyItem(x)); + this.source = response; }); } ngOnDestroy(): void { this.titleService.resetTitle(); } + + create(): void { + this.editForm = new CurrencyRecordEditForm(); + } + + onEditFormSubmit(): void { + if (this.editForm == null) { + return; + } + + const createRequest = this.editForm.createRequestOrNull(); + if (createRequest == null) { + return; + } + + this.currenciesService + .create(createRequest) + .pipe(untilDestroyed(this)) + .subscribe(() => { + this.alert.success("Запись о курсе валюты была создана"); + this.editForm = null; + this.loadData(this.currentPage); + }); + } + + onEditModalDlgClose(): void { + this.editForm = null; + } + + openDeleteDialog(item: CurrencyItem): void { + this.confirmDeletionMessage = new DialogMessage( + new ConfirmMsg( + "Удалить запись", + `Вы уверены, что хотите удалить запись от ${new Date(item.currencyDate).toLocaleDateString()}?`, + () => { + this.currenciesService + .delete(item.id) + .pipe(untilDestroyed(this)) + .subscribe(() => { + this.alert.success("Запись была удалена"); + this.confirmDeletionMessage = null; + this.loadData(this.currentPage); + }); + }, + ), + ); + } + + showCurrenciesDetails(item: CurrencyItem): void { + this.selectedCurrencyItem = item; + } + + closeCurrenciesDetails(): void { + this.selectedCurrencyItem = null; + } } diff --git a/src/app/modules/admin/components/currencies-page/currency-item.ts b/src/app/modules/admin/components/currencies-page/currency-item.ts index ad99cad2..20064e98 100644 --- a/src/app/modules/admin/components/currencies-page/currency-item.ts +++ b/src/app/modules/admin/components/currencies-page/currency-item.ts @@ -1,15 +1,27 @@ -import { CurrencyData, CurrencyType } from "@services/admin-tools.service"; - -export class CurrencyItem implements CurrencyData { - constructor(private readonly item: CurrencyData) { - this.value = item.value; - this.currency = item.currency; - this.currencyString = CurrencyType[item.currency]; - this.pubDate = item.pubDate; +import { CurrencyRecord } from "@services/currencies-collection.service"; + +export class CurrencyItem implements CurrencyRecord { + constructor(private readonly item: CurrencyRecord) { + this.id = item.id; + this.currencyDate = item.currencyDate; + this.currencies = item.currencies; + this.createdAt = item.createdAt; + this.updatedAt = item.updatedAt; + + // Create truncated JSON string for display (15 chars) + const jsonString = JSON.stringify(item.currencies); + this.currenciesShort = + jsonString.length > 15 ? jsonString.substring(0, 15) + "..." : jsonString; + this.currenciesJson = jsonString; } - readonly value: number; - readonly currency: CurrencyType; - readonly currencyString: string; - readonly pubDate: Date; + readonly id: string; + readonly currencyDate: string; + readonly currencies: Record; + readonly createdAt: string; + readonly updatedAt: string; + + // Display helpers + readonly currenciesShort: string; + readonly currenciesJson: string; } diff --git a/src/app/modules/admin/components/currencies-page/currency-record-edit-form.ts b/src/app/modules/admin/components/currencies-page/currency-record-edit-form.ts new file mode 100644 index 00000000..da8880f4 --- /dev/null +++ b/src/app/modules/admin/components/currencies-page/currency-record-edit-form.ts @@ -0,0 +1,28 @@ +import { FormControl, FormGroup, Validators } from "@angular/forms"; +import { CreateCurrencyRecordRequest } from "@services/currencies-collection.service"; +import { CurrencyType } from "@services/admin-tools.service"; + +export class CurrencyRecordEditForm extends FormGroup { + constructor() { + super({ + currency: new FormControl(CurrencyType.USD, [Validators.required]), + value: new FormControl(null, [Validators.required, Validators.min(0.0001)]), + pubDate: new FormControl(new Date().toISOString().split("T")[0], [ + Validators.required, + ]), + }); + } + + createRequestOrNull(): CreateCurrencyRecordRequest | null { + if (!this.valid) { + this.markAllAsTouched(); + return null; + } + + return { + currency: this.value.currency, + value: this.value.value, + currencyDate: this.value.pubDate, + }; + } +} diff --git a/src/app/modules/salaries/components/currencies-chart-page/currencies-chart-object.ts b/src/app/modules/salaries/components/currencies-chart-page/currencies-chart-object.ts new file mode 100644 index 00000000..baba15bd --- /dev/null +++ b/src/app/modules/salaries/components/currencies-chart-page/currencies-chart-object.ts @@ -0,0 +1,157 @@ +import { Chart, ChartType, PointStyle } from "chart.js/auto"; +import { WeeklyCurrencyChartData } from "@services/currencies-collection.service"; +import { CurrencyType } from "@services/admin-tools.service"; +import { RgbColor } from "../rgb-color"; + +interface ChartDatasetType { + label: string; + data: Array; + borderWidth: number; + borderColor: string; + backgroundColor: string; + pointStyle: PointStyle; + hidden?: boolean; +} + +// Fixed colors for each currency for consistent appearance +const CURRENCY_COLORS: Record = { + [CurrencyType.Undefined]: new RgbColor(128, 128, 128), + [CurrencyType.KZT]: new RgbColor(0, 123, 255), + [CurrencyType.USD]: new RgbColor(34, 139, 34), + [CurrencyType.EUR]: new RgbColor(65, 105, 225), + [CurrencyType.RUB]: new RgbColor(220, 53, 69), + [CurrencyType.KGS]: new RgbColor(255, 193, 7), + [CurrencyType.SAR]: new RgbColor(111, 66, 193), + [CurrencyType.AED]: new RgbColor(23, 162, 184), + [CurrencyType.CAD]: new RgbColor(232, 62, 140), +}; + +const CURRENCY_LABELS: Record = { + [CurrencyType.Undefined]: "Не указано", + [CurrencyType.KZT]: "KZT", + [CurrencyType.USD]: "USD", + [CurrencyType.EUR]: "EUR", + [CurrencyType.RUB]: "RUB", + [CurrencyType.KGS]: "KGS", + [CurrencyType.SAR]: "SAR", + [CurrencyType.AED]: "AED", + [CurrencyType.CAD]: "CAD", +}; + +export class CurrenciesChartObject extends Chart { + private readonly datasets: Array = []; + + constructor(canvasId: string, chartData: WeeklyCurrencyChartData[]) { + // Ensure chartData is an array + const safeChartData = Array.isArray(chartData) ? chartData : []; + + // Get all unique currencies from the data + const allCurrencies = new Set(); + safeChartData.forEach((week) => { + week.averageCurrencies?.forEach((c) => { + if (c.currency !== CurrencyType.KZT) { + allCurrencies.add(c.currency); + } + }); + }); + + // Create labels from week dates + const labels = safeChartData.map((week) => { + const startDate = new Date(week.weekStartDate); + return startDate.toISOString().slice(0, 10); + }); + + // Create datasets for each currency + const datasets: Array = []; + allCurrencies.forEach((currency) => { + const data = safeChartData.map((week) => { + const currencyItem = week.averageCurrencies?.find( + (c) => c.currency === currency, + ); + return currencyItem?.value ?? 0; + }); + + const color = CURRENCY_COLORS[currency] ?? new RgbColor(128, 128, 128); + const label = CURRENCY_LABELS[currency] ?? CurrencyType[currency]; + + datasets.push( + new DatasetItem(label, data, 2, color, "circle" as PointStyle), + ); + }); + + super(canvasId, { + type: "line" as ChartType, + data: { + labels: labels, + datasets: datasets, + }, + options: { + responsive: true, + maintainAspectRatio: false, + interaction: { + mode: "index", + intersect: false, + }, + scales: { + y: { + beginAtZero: false, + position: "left", + title: { + display: true, + text: "Курс в KZT", + }, + }, + x: { + title: { + display: true, + text: "Неделя", + }, + }, + }, + elements: { + line: { + tension: 0.3, + }, + }, + plugins: { + legend: { + position: "bottom", + title: { + display: false, + }, + }, + tooltip: { + callbacks: { + label: function (context) { + const value = context.parsed.y; + if (value == null) { + return `${context.dataset.label}: N/A`; + } + return `${context.dataset.label}: ${value.toFixed(2)} KZT`; + }, + }, + }, + }, + }, + }); + + this.datasets = datasets; + } +} + +class DatasetItem implements ChartDatasetType { + constructor( + readonly label: string, + readonly data: number[], + readonly borderWidth: number, + readonly color: RgbColor, + readonly pointStyle: PointStyle, + ) { + this.borderColor = color.toString(1); + this.backgroundColor = color.toString(0.5); + } + + readonly borderColor: string; + readonly backgroundColor: string; +} + diff --git a/src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.html b/src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.html new file mode 100644 index 00000000..8bdf0590 --- /dev/null +++ b/src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.html @@ -0,0 +1,75 @@ +График курсов валют + +
+
+
+
+
+
Динамика курсов валют
+

+ Средние значения курсов валют по неделям относительно KZT +

+
+ +
+ + +
+ +
+ + +
+ {{ errorMessage }} +
+ + +
+
+ + + +
+

Нет данных для отображения графика

+
+ + +
+ {{ chart }} +
+ + +
+
+ Примечание: График показывает средние курсы валют за + каждую неделю. Нажмите на название валюты в легенде, чтобы + скрыть/показать соответствующую линию. +
+
+
+
+
diff --git a/src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.scss b/src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.scss new file mode 100644 index 00000000..a79dd82e --- /dev/null +++ b/src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.scss @@ -0,0 +1,10 @@ +.chart-container { + position: relative; + height: 450px; + width: 100%; +} + +canvas { + width: 100% !important; + height: 100% !important; +} diff --git a/src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.spec.ts b/src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.spec.ts new file mode 100644 index 00000000..b8812d1f --- /dev/null +++ b/src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.spec.ts @@ -0,0 +1,57 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { CurrenciesChartPageComponent } from "./currencies-chart-page.component"; +import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core"; +import { TitleService } from "@services/title.service"; +import { CurrenciesCollectionService } from "@services/currencies-collection.service"; +import { of } from "rxjs"; + +describe("CurrenciesChartPageComponent", () => { + let component: CurrenciesChartPageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [CurrenciesChartPageComponent], + providers: [ + { + provide: TitleService, + useValue: { + setTitle: jasmine.createSpy("setTitle"), + resetTitle: jasmine.createSpy("resetTitle"), + }, + }, + { + provide: CurrenciesCollectionService, + useValue: { + getChartData: jasmine.createSpy("getChartData").and.returnValue( + of([ + { + weekStartDate: "2024-01-01T00:00:00Z", + weekEndDate: "2024-01-07T00:00:00Z", + averageCurrencies: [ + { currency: 3, value: 450.5 }, + { currency: 4, value: 490.25 }, + ], + }, + ]), + ), + }, + }, + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }).compileComponents(); + + fixture = TestBed.createComponent(CurrenciesChartPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); + + it("should load chart data on init", () => { + expect(component.chartData).toBeTruthy(); + expect(component.isLoading).toBeFalse(); + }); +}); diff --git a/src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.ts b/src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.ts new file mode 100644 index 00000000..bcfd81e8 --- /dev/null +++ b/src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.ts @@ -0,0 +1,82 @@ +import { Component, OnDestroy, OnInit } from "@angular/core"; +import { TitleService } from "@services/title.service"; +import { + CurrenciesCollectionService, + CurrencyChartResponse, + WeeklyCurrencyChartData, +} from "@services/currencies-collection.service"; +import { untilDestroyed } from "@shared/subscriptions/until-destroyed"; +import { CurrenciesChartObject } from "./currencies-chart-object"; + +@Component({ + selector: "app-currencies-chart-page", + templateUrl: "./currencies-chart-page.component.html", + styleUrls: ["./currencies-chart-page.component.scss"], + standalone: false, +}) +export class CurrenciesChartPageComponent implements OnInit, OnDestroy { + chartData: WeeklyCurrencyChartData[] | null = null; + chart: CurrenciesChartObject | null = null; + readonly canvasId = "currencies_chart_" + Math.random().toString(36); + isLoading = true; + errorMessage: string | null = null; + + constructor( + private readonly titleService: TitleService, + private readonly currenciesService: CurrenciesCollectionService, + ) { + this.titleService.setTitle("График курсов валют"); + } + + ngOnInit(): void { + this.loadChartData(); + } + + ngOnDestroy(): void { + this.titleService.resetTitle(); + this.chart?.destroy(); + } + + private loadChartData(): void { + this.isLoading = true; + this.errorMessage = null; + + this.currenciesService + .getChartData() + .pipe(untilDestroyed(this)) + .subscribe({ + next: (response: CurrencyChartResponse) => { + // API returns { weeklyData: [...], fromDate, toDate } + this.chartData = response.weeklyData ?? []; + this.isLoading = false; + + // Initialize chart after data is loaded and view is ready + setTimeout(() => this.initChart(), 0); + }, + error: (err) => { + this.isLoading = false; + this.errorMessage = + "Не удалось загрузить данные графика. Попробуйте позже."; + console.error("Failed to load currency chart data:", err); + }, + }); + } + + private initChart(): void { + if (!this.chartData || this.chartData.length === 0) { + return; + } + + this.chart?.destroy(); + this.chart = new CurrenciesChartObject(this.canvasId, this.chartData); + + const chartEl = document.getElementById(this.canvasId); + if (chartEl != null && chartEl.parentElement != null) { + chartEl.style.height = chartEl?.parentElement.style.height ?? "100%"; + } + } + + refresh(): void { + this.loadChartData(); + } +} diff --git a/src/app/modules/salaries/salaries-routing.module.ts b/src/app/modules/salaries/salaries-routing.module.ts index 46570ac3..8a226fe7 100644 --- a/src/app/modules/salaries/salaries-routing.module.ts +++ b/src/app/modules/salaries/salaries-routing.module.ts @@ -3,6 +3,7 @@ import { RouterModule, Routes } from "@angular/router"; import { SalariesChartComponent } from "./components/salaries-chart/salaries-chart.component"; import { HistoricalChartsPageComponent } from "./components/historical-charts-page/historical-charts-page.component"; import { AddSalaryComponent } from "./components/add-salary/add-salary.component"; +import { CurrenciesChartPageComponent } from "./components/currencies-chart-page/currencies-chart-page.component"; const routes: Routes = [ { @@ -17,6 +18,10 @@ const routes: Routes = [ path: "historical-data", component: HistoricalChartsPageComponent, }, + { + path: "currencies-chart", + component: CurrenciesChartPageComponent, + }, ]; @NgModule({ diff --git a/src/app/modules/salaries/salaries.module.ts b/src/app/modules/salaries/salaries.module.ts index 9079051c..0e2d2539 100644 --- a/src/app/modules/salaries/salaries.module.ts +++ b/src/app/modules/salaries/salaries.module.ts @@ -34,6 +34,7 @@ import { SalariesByGenderChartComponent } from "./components/people-by-gender-ch import { UsefulnessRatingComponent } from "./components/salaries-chart/usefulness-rating/usefulness-rating.component"; import { ThankYouForFeedbackComponent } from "./components/salaries-chart/thank-you-for-feedback/thank-you-for-feedback.component"; import { SurveyHistoricalChartComponent } from "./components/historical-charts-page/survey-historical-chart/survey-historical-chart.component"; +import { CurrenciesChartPageComponent } from "./components/currencies-chart-page/currencies-chart-page.component"; @NgModule({ declarations: [ @@ -68,6 +69,7 @@ import { SurveyHistoricalChartComponent } from "./components/historical-charts-p UsefulnessRatingComponent, ThankYouForFeedbackComponent, SurveyHistoricalChartComponent, + CurrenciesChartPageComponent, ], imports: [ CommonModule, diff --git a/src/app/services/currencies-collection.service.ts b/src/app/services/currencies-collection.service.ts new file mode 100644 index 00000000..5eea2fe3 --- /dev/null +++ b/src/app/services/currencies-collection.service.ts @@ -0,0 +1,70 @@ +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 { CurrencyType } from "./admin-tools.service"; + +export interface CurrencyRecord { + id: string; + currencyDate: string; + currencies: Record; // JSON object with currency data (e.g., { "USD": 450.5, "EUR": 490.25 }) + createdAt: string; + updatedAt: string; +} + +export interface CreateCurrencyRecordRequest { + currency: CurrencyType; + value: number; + currencyDate: string; +} + +export interface CurrencyItemDto { + currency: CurrencyType; + value: number; +} + +export interface WeeklyCurrencyChartData { + weekStartDate: string; + weekEndDate: string; + averageCurrencies: CurrencyItemDto[]; +} + +export interface CurrencyChartResponse { + weeklyData: WeeklyCurrencyChartData[]; + fromDate: string; + toDate: string; +} + +@Injectable({ + providedIn: "root", +}) +export class CurrenciesCollectionService { + private readonly apiUrl: string; + + constructor(private readonly api: ApiService) { + this.apiUrl = `/api/currencies/`; + } + + getAll( + pageParams: PageParams = defaultPageParams, + ): Observable> { + return this.api.get>( + this.apiUrl + "?" + new ConvertObjectToHttpParams(pageParams).get(), + ); + } + + create(data: CreateCurrencyRecordRequest): Observable { + return this.api.post(this.apiUrl, data); + } + + delete(id: string): Observable { + return this.api.delete(this.apiUrl + id); + } + + getChartData(): Observable { + return this.api.get(this.apiUrl + "chart"); + } +} +