Repository navigation
Added classes - #113
Added classes#113
Conversation
WalkthroughThis PR introduces a currencies management feature with an admin page for CRUD operations and pagination, a salaries module chart page for visualizing weekly currency data, a new service layer for API integration, and navbar integration to link to the new chart view. Changes
Sequence DiagramssequenceDiagram
participant User as User
participant AdminUI as Admin Page
participant Service as CurrenciesService
participant API as Backend API
participant Dialog as Dialog/Modal
Note over User,API: Currency Record CRUD Flow
User->>AdminUI: Opens currencies page
AdminUI->>Service: loadData(page)
Service->>API: GET /api/currencies?page=1&size=10
API-->>Service: PaginatedList<CurrencyRecord>
Service-->>AdminUI: currencies[] + pagination info
AdminUI->>AdminUI: Render table
User->>Dialog: Click "Создать запись"
Dialog->>User: Show edit form modal
User->>Dialog: Fill form (currency, value, date)
User->>Dialog: Submit
Dialog->>Service: create(CreateCurrencyRecordRequest)
Service->>API: POST /api/currencies/
API-->>Service: CurrencyRecord
Service-->>Dialog: Success
Dialog->>AdminUI: Close & refresh
AdminUI->>Service: loadData(currentPage)
Service->>API: GET /api/currencies?...
API-->>Service: Updated PaginatedList
AdminUI->>User: Show updated table + success alert
User->>AdminUI: Click delete on row
AdminUI->>Dialog: Open confirm dialog
User->>Dialog: Confirm deletion
Dialog->>Service: delete(id)
Service->>API: DELETE /api/currencies/{id}
API-->>Service: void (success)
Service-->>AdminUI: Deletion confirmed
AdminUI->>Service: loadData(currentPage)
Service->>API: GET /api/currencies?...
API-->>Service: Updated PaginatedList
AdminUI->>User: Refresh table + success alert
sequenceDiagram
participant User as User
participant ChartUI as Currencies Chart Page
participant Service as CurrenciesService
participant API as Backend API
participant Chart as Chart.js
Note over User,Chart: Chart Data Fetch & Render Flow
User->>ChartUI: Navigate to currencies-chart
ChartUI->>ChartUI: ngOnInit() → isLoading=true
ChartUI->>Service: getChartData()
Service->>API: GET /api/currencies/chart
API-->>Service: CurrencyChartResponse{weeklyData[]}
Service-->>ChartUI: Observable<CurrencyChartResponse>
ChartUI->>ChartUI: Store weeklyData, isLoading=false
ChartUI->>ChartUI: initChart()
ChartUI->>Chart: new CurrenciesChartObject(weeklyData)
Chart->>Chart: Build datasets per currency<br/>(excluding KZT)
Chart->>Chart: Set labels from weekStartDate
Chart->>Chart: Create Chart.js instance
Chart-->>ChartUI: chart object
ChartUI->>User: Render canvas with chart
User->>ChartUI: Click refresh button
ChartUI->>Service: getChartData()
Service->>API: GET /api/currencies/chart
API-->>Service: Updated CurrencyChartResponse
Service-->>ChartUI: Observable<CurrencyChartResponse>
ChartUI->>ChartUI: Update weeklyData
ChartUI->>Chart: Destroy old chart
ChartUI->>Chart: Create new CurrenciesChartObject
Chart-->>ChartUI: Updated chart
ChartUI->>User: Re-render chart with fresh data
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (19)
src/app/modules/admin/components/currencies-page/currencies-page.component.scss (2)
1-7: Consider usingword-break: break-wordinstead.The
word-break: break-allproperty can break words at any character, which may reduce readability for JSON content. Usingbreak-wordwould break only at word boundaries when possible, providing better text flow.🔎 Suggested alternative
.currencies-json { max-height: 400px; overflow-y: auto; font-size: 0.85rem; white-space: pre-wrap; - word-break: break-all; + word-break: break-word; }
9-11: Scope thecodeselector more specifically.While Angular's view encapsulation should scope this to the component, using a bare
codeselector is less explicit. Consider scoping it within.currencies-json codefor clarity and to avoid any potential conflicts.🔎 More specific selector
+.currencies-json code { -code { font-size: 0.8rem; }src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.scss (2)
7-10: Avoid using!importantfor canvas sizing.The
!importantflags suggest a specificity conflict with other styles. This is generally a code smell. Consider using more specific selectors or restructuring the CSS to avoid the need for!important.🔎 Alternative approach
If you're fighting with chart library styles, try scoping more specifically:
-canvas { +.chart-container canvas { - width: 100% !important; - height: 100% !important; + width: 100%; + height: 100%; }If the chart library applies inline styles, you may need to configure the library options instead of overriding with CSS.
1-5: Consider responsive chart height.The fixed height of 450px works for desktop, but might not scale well on smaller screens. Consider using viewport-relative units or media queries for better responsiveness.
🔎 Responsive alternative
.chart-container { position: relative; - height: 450px; + height: min(450px, 60vh); width: 100%; }src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.spec.ts (2)
49-56: Consider expanding test coverage when time permits.The current tests cover basic component creation and data loading, which is a good start for MVP. However, consider adding tests for:
- Error handling when chart data fails to load
- Chart rendering and initialization
- Component cleanup (ngOnDestroy, subscription management)
- Different data scenarios (empty data, single data point, etc.)
Based on learnings, I understand you're prioritizing MVP functionality, so this can be deferred.
41-41: CUSTOM_ELEMENTS_SCHEMA might hide component declaration issues.Using
CUSTOM_ELEMENTS_SCHEMAsuppresses errors for unrecognized elements, which can mask missing component declarations or imports. If the component uses specific Angular components or directives, consider declaring them explicitly in the test configuration instead.src/app/modules/admin/components/currencies-page/currencies-page.component.spec.ts (1)
50-52: Consider expanding test coverage for CRUD operations.The test suite currently only verifies component creation. Consider adding tests for:
- Create/delete operations and their success/error flows
- Pagination behavior
- Form validation in the edit dialog
- Alert service integration
Based on learnings, you may choose to defer this to a later phase while focusing on MVP functionality.
src/app/modules/admin/components/currencies-page/currency-record-edit-form.ts (1)
6-14: Consider using typed form controls for better type safety.The FormControl instances could benefit from explicit generic type parameters to leverage Angular's typed forms feature (available since Angular 14).
🔎 Proposed refactor with typed controls
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], [ + currency: new FormControl<CurrencyType>(CurrencyType.USD, [Validators.required]), + value: new FormControl<number | null>(null, [Validators.required, Validators.min(0.0001)]), + pubDate: new FormControl<string>(new Date().toISOString().split("T")[0], [ Validators.required, ]), }); }As per coding guidelines, proper typing improves maintainability and catches type errors at compile time.
src/app/modules/salaries/components/currencies-chart-page/currencies-chart-object.ts (2)
29-39: Consider i18n support for hardcoded Russian text.Multiple locations contain hardcoded Russian strings:
- Currency labels (lines 29-39): "Не указано"
- Axis titles (lines 101, 107): "Курс в KZT", "Неделя"
- Tooltip suffix (line 130): "KZT"
For better i18n readiness, consider using Angular's i18n framework or extracting these strings to a translatable resource.
As per coding guidelines, templates and code should be i18n ready.
Also applies to: 101-101, 107-107, 130-130
52-54: Document the KZT filtering rationale.The code explicitly excludes
CurrencyType.KZTfrom the chart without explanation. Consider adding a comment explaining why KZT is filtered (e.g., "KZT is excluded as it's the base currency for comparison").src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.html (2)
1-1: Template lacks i18n support.All user-facing text is hardcoded in Russian. Consider using Angular's i18n attributes or translation pipes to make the template localizable.
Example:
<app-page-header i18n>График курсов валют</app-page-header>As per coding guidelines, templates should be i18n ready.
Also applies to: 8-11, 19-19, 51-51, 68-70
39-49: Add accessibility attributes to decorative SVG.The SVG icon should include
aria-hidden="true"since it's decorative and the adjacent text already conveys the "no data" message.🔎 Proposed accessibility improvement
<svg + aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="64" height="64"As per coding guidelines, ensure proper accessibility with ARIA attributes.
src/app/modules/admin/components/currencies-page/currencies-page.component.html (2)
27-27: Add trackBy function for performance.The *ngFor loop should use a trackBy function to optimize rendering performance and prevent unnecessary DOM updates when the list refreshes.
🔎 Suggested implementation
In the component TypeScript file, add:
trackByCurrencyId(index: number, item: CurrencyItem): string { return item.id; }Then update the template:
-<tr *ngFor="let item of currencies" class="small"> +<tr *ngFor="let item of currencies; trackBy: trackByCurrencyId" class="small">As per coding guidelines for Angular templates.
84-84: Consider trackBy for consistency.While
currencyOptionsis a static readonly array, adding a trackBy function improves consistency and is a best practice for all *ngFor loops.🔎 Suggested implementation
In the component TypeScript file, add:
trackByCurrencyValue(index: number, option: { value: CurrencyType; label: string }): CurrencyType { return option.value; }Then update the template:
-<option *ngFor="let option of currencyOptions" [ngValue]="option.value"> +<option *ngFor="let option of currencyOptions; trackBy: trackByCurrencyValue" [ngValue]="option.value">As per coding guidelines for Angular templates.
src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.ts (3)
20-20: Use a more deterministic approach for canvas ID.Using
Math.random()for generating the canvas ID makes testing difficult and is not SSR-friendly. Consider using a static counter or a more predictable approach.🔎 Suggested implementation
+ private static chartIdCounter = 0; - readonly canvasId = "currencies_chart_" + Math.random().toString(36); + readonly canvasId = `currencies_chart_${CurrenciesChartPageComponent.chartIdCounter++}`;As per coding guidelines for Angular components.
65-77: Use @ViewChild instead of document.getElementById.Direct DOM manipulation via
document.getElementByIdbreaks Angular's abstraction and makes testing harder. Use@ViewChildandElementRefto access the canvas element.🔎 Suggested implementation
+import { Component, ElementRef, OnDestroy, OnInit, ViewChild } from "@angular/core"; + @ViewChild('currenciesChart', { static: false }) + canvasRef!: ElementRef<HTMLCanvasElement>; 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%"; + if (this.canvasRef?.nativeElement?.parentElement) { + this.canvasRef.nativeElement.style.height = + this.canvasRef.nativeElement.parentElement.style.height || "100%"; } }In the template, add the template reference variable:
<canvas #currenciesChart [id]="canvasId"></canvas>As per coding guidelines for Angular components.
56-61: Consider using a logging service instead of console.error.While
console.errorworks, using a centralized logging service provides better error tracking, monitoring, and consistency across the application.As per coding guidelines for Angular services and error handling best practices.
src/app/modules/admin/components/currencies-page/currency-item.ts (1)
3-27: Consider using getters instead of copying all fields.The constructor copies all fields from the
itemparameter, which creates redundant data storage. You could use getters to expose the properties directly from the stored item.🔎 Suggested implementation
export class CurrencyItem implements CurrencyRecord { + private readonly _currenciesShort: string; + private readonly _currenciesJson: string; + 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 = + this._currenciesShort = jsonString.length > 15 ? jsonString.substring(0, 15) + "..." : jsonString; - this.currenciesJson = jsonString; + this._currenciesJson = jsonString; } - readonly id: string; - readonly currencyDate: string; - readonly currencies: Record<string, number>; - readonly createdAt: string; - readonly updatedAt: string; + get id(): string { return this.item.id; } + get currencyDate(): string { return this.item.currencyDate; } + get currencies(): Record<string, number> { return this.item.currencies; } + get createdAt(): string { return this.item.createdAt; } + get updatedAt(): string { return this.item.updatedAt; } // Display helpers - readonly currenciesShort: string; - readonly currenciesJson: string; + get currenciesShort(): string { return this._currenciesShort; } + get currenciesJson(): string { return this._currenciesJson; } }As per coding guidelines for TypeScript code organization.
src/app/modules/admin/components/currencies-page/currencies-page.component.ts (1)
17-22: Consider using OnPush change detection strategy.This component follows a data-flow pattern where state is replaced rather than mutated, making it a good candidate for
ChangeDetectionStrategy.OnPushto improve performance.🔎 Suggested implementation
-import { Component, OnDestroy, OnInit } from "@angular/core"; +import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from "@angular/core"; @Component({ templateUrl: "./currencies-page.component.html", styleUrls: ["./currencies-page.component.scss"], standalone: false, + changeDetection: ChangeDetectionStrategy.OnPush, })Note: You may need to inject
ChangeDetectorRefand callmarkForCheck()after async operations if you encounter view update issues.As per coding guidelines for Angular components - OnPush change detection where applicable.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
src/app/components/navbar/navbar.component.tssrc/app/modules/admin/components/currencies-page/currencies-page.component.htmlsrc/app/modules/admin/components/currencies-page/currencies-page.component.scsssrc/app/modules/admin/components/currencies-page/currencies-page.component.spec.tssrc/app/modules/admin/components/currencies-page/currencies-page.component.tssrc/app/modules/admin/components/currencies-page/currency-item.tssrc/app/modules/admin/components/currencies-page/currency-record-edit-form.tssrc/app/modules/salaries/components/currencies-chart-page/currencies-chart-object.tssrc/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.htmlsrc/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.scsssrc/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.spec.tssrc/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.tssrc/app/modules/salaries/salaries-routing.module.tssrc/app/modules/salaries/salaries.module.tssrc/app/services/currencies-collection.service.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.ts
⚙️ CodeRabbit configuration file
**/*.ts: Review TypeScript code for:
- Proper typing and avoiding 'any' type
- Consistent use of interfaces and type aliases
- Null safety with strict null checks
- Proper error handling
- Code organization and single responsibility
Files:
src/app/modules/admin/components/currencies-page/currency-record-edit-form.tssrc/app/modules/salaries/salaries-routing.module.tssrc/app/services/currencies-collection.service.tssrc/app/modules/admin/components/currencies-page/currencies-page.component.tssrc/app/modules/salaries/salaries.module.tssrc/app/components/navbar/navbar.component.tssrc/app/modules/salaries/components/currencies-chart-page/currencies-chart-object.tssrc/app/modules/admin/components/currencies-page/currencies-page.component.spec.tssrc/app/modules/admin/components/currencies-page/currency-item.tssrc/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.spec.tssrc/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.ts
**/*.component.html
⚙️ CodeRabbit configuration file
**/*.component.html: Review Angular templates for:
Files:
src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.htmlsrc/app/modules/admin/components/currencies-page/currencies-page.component.html
**/*.component.scss
⚙️ CodeRabbit configuration file
**/*.component.scss: Review component styles for:
- Proper use of :host and ::ng-deep (deprecated, suggest alternatives)
- CSS variable usage for theming
- Responsive design patterns
- BEM or consistent naming convention
Files:
src/app/modules/admin/components/currencies-page/currencies-page.component.scsssrc/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.scss
**/*.service.ts
⚙️ CodeRabbit configuration file
**/*.service.ts: Review Angular services for:
- Proper dependency injection with providedIn
- RxJS best practices (operators, error handling, memory leaks)
- HTTP error handling and retry strategies
- Caching strategies where appropriate
- Single responsibility principle
Files:
src/app/services/currencies-collection.service.ts
**/*.component.ts
⚙️ CodeRabbit configuration file
**/*.component.ts: Review Angular components for:
- Proper use of lifecycle hooks (ngOnInit, ngOnDestroy, etc.)
- OnPush change detection strategy where applicable
- Proper subscription management (unsubscribe, takeUntilDestroyed, async pipe)
- Input/Output decorator usage and proper typing
- Avoiding logic in templates
- Component size and potential need for splitting
Files:
src/app/modules/admin/components/currencies-page/currencies-page.component.tssrc/app/components/navbar/navbar.component.tssrc/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.ts
**/*.spec.ts
⚙️ CodeRabbit configuration file
**/*.spec.ts: Review test files for:
- Adequate test coverage
- Proper use of TestBed and component fixtures
- Mock services and HTTP testing
- Async testing with fakeAsync/waitForAsync
- Isolated unit tests vs integration tests
Files:
src/app/modules/admin/components/currencies-page/currencies-page.component.spec.tssrc/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.spec.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: maximgorbatyuk
Repo: Techinterview-space/frontend PR: 74
File: src/app/modules/companies/components/add-review-page/add-review-page.component.spec.ts:31-33
Timestamp: 2025-04-30T04:43:39.004Z
Learning: maximgorbatyuk is prioritizing MVP functionality for the companies feature and may address expanded test coverage (form validation, submission, error handling, rating selection) in a later phase.
🧬 Code graph analysis (6)
src/app/modules/admin/components/currencies-page/currency-record-edit-form.ts (1)
src/app/services/currencies-collection.service.ts (1)
CreateCurrencyRecordRequest(17-21)
src/app/services/currencies-collection.service.ts (3)
src/app/models/page-params.ts (2)
PageParams(8-8)defaultPageParams(10-13)src/app/models/paginated-list.ts (1)
PaginatedList(17-19)src/app/shared/value-objects/convert-object-to-http.ts (1)
ConvertObjectToHttpParams(4-35)
src/app/modules/admin/components/currencies-page/currencies-page.component.ts (5)
src/app/modules/admin/components/currencies-page/currency-item.ts (1)
CurrencyItem(3-27)src/app/models/paginated-list.ts (1)
PaginatedList(17-19)src/app/services/currencies-collection.service.ts (1)
CurrencyRecord(9-15)src/app/modules/admin/components/currencies-page/currency-record-edit-form.ts (1)
CurrencyRecordEditForm(5-28)src/app/models/page-params.ts (1)
defaultPageParams(10-13)
src/app/modules/salaries/components/currencies-chart-page/currencies-chart-object.ts (2)
src/app/modules/salaries/components/rgb-color.ts (1)
RgbColor(1-35)src/app/services/currencies-collection.service.ts (1)
WeeklyCurrencyChartData(28-32)
src/app/modules/admin/components/currencies-page/currency-item.ts (1)
src/app/services/currencies-collection.service.ts (1)
CurrencyRecord(9-15)
src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.ts (2)
src/app/services/currencies-collection.service.ts (2)
WeeklyCurrencyChartData(28-32)CurrencyChartResponse(34-38)src/app/modules/salaries/components/currencies-chart-page/currencies-chart-object.ts (1)
CurrenciesChartObject(41-140)
🔇 Additional comments (6)
src/app/components/navbar/navbar.component.ts (1)
144-148: LGTM!The new navigation link is correctly structured and consistent with other salary-related menu items. The visibility control using
hasCurrentUserappropriately restricts access to authenticated users.src/app/modules/salaries/salaries-routing.module.ts (1)
6-6: LGTM!The new route is properly structured and consistent with existing route definitions. The import and route configuration correctly integrate the CurrenciesChartPageComponent into the salaries module routing.
Also applies to: 21-24
src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.spec.ts (1)
12-47: Test setup looks correct.The TestBed configuration properly mocks the required services (TitleService and CurrenciesCollectionService) with appropriate spy behavior. The mock data structure aligns with the expected chart data format.
src/app/modules/salaries/salaries.module.ts (1)
37-37: LGTM!The new CurrenciesChartPageComponent is correctly imported and declared in the module.
Also applies to: 72-72
src/app/modules/admin/components/currencies-page/currency-record-edit-form.ts (1)
16-27: LGTM!The validation logic correctly handles form state and field mapping from
pubDatetocurrencyDatematches theCreateCurrencyRecordRequestinterface.src/app/modules/salaries/components/currencies-chart-page/currencies-chart-object.ts (1)
44-80: LGTM!The chart construction logic is well-structured:
- Safely handles empty/invalid data (line 46)
- Properly maps weekly data to datasets
- Uses nullish coalescing for missing values (line 71)
- Chart.js configuration is appropriate for a multi-currency line chart
Also applies to: 82-139
| 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; | ||
| }); |
There was a problem hiding this comment.
Add error handling to the subscription.
The loadData subscription lacks an error handler. If the API call fails, the user receives no feedback and the loading state may persist indefinitely.
🔎 Suggested fix
this.currenciesService
.getAll({
page: this.currentPage,
pageSize: defaultPageParams.pageSize,
})
.pipe(untilDestroyed(this))
- .subscribe((response) => {
- this.currencies = response.results.map((x) => new CurrencyItem(x));
- this.source = response;
+ .subscribe({
+ next: (response) => {
+ this.currencies = response.results.map((x) => new CurrencyItem(x));
+ this.source = response;
+ },
+ error: (err) => {
+ this.alert.error("Не удалось загрузить данные. Попробуйте позже.");
+ this.currencies = [];
+ console.error("Failed to load currencies:", err);
+ }
});As per coding guidelines for Angular components and proper error handling.
🤖 Prompt for AI Agents
In src/app/modules/admin/components/currencies-page/currencies-page.component.ts
around lines 63 to 72, the subscription to currenciesService.getAll(...) has no
error handler so failures leave the user without feedback and may keep loading
state stuck; add an error callback (or use pipe with catchError/finalize) to
handle API errors: set any loading flags to false in both success and error
paths (or use finalize), set an error state/message or invoke the app/toast
error notification with a clear message, and keep untilDestroyed(this) on the
stream; ensure source/currencies are only set on success and that errors are
logged for debugging.
| this.currenciesService | ||
| .create(createRequest) | ||
| .pipe(untilDestroyed(this)) | ||
| .subscribe(() => { | ||
| this.alert.success("Запись о курсе валюты была создана"); | ||
| this.editForm = null; | ||
| this.loadData(this.currentPage); | ||
| }); |
There was a problem hiding this comment.
Add error handling to the create subscription.
The create subscription lacks an error handler. If the API call fails, the user receives no feedback and the form remains open with no indication of the failure.
🔎 Suggested fix
this.currenciesService
.create(createRequest)
.pipe(untilDestroyed(this))
- .subscribe(() => {
- this.alert.success("Запись о курсе валюты была создана");
- this.editForm = null;
- this.loadData(this.currentPage);
+ .subscribe({
+ next: () => {
+ this.alert.success("Запись о курсе валюты была создана");
+ this.editForm = null;
+ this.loadData(this.currentPage);
+ },
+ error: (err) => {
+ this.alert.error("Не удалось создать запись. Попробуйте позже.");
+ console.error("Failed to create currency record:", err);
+ }
});As per coding guidelines for Angular components and proper error handling.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| this.currenciesService | |
| .create(createRequest) | |
| .pipe(untilDestroyed(this)) | |
| .subscribe(() => { | |
| this.alert.success("Запись о курсе валюты была создана"); | |
| this.editForm = null; | |
| this.loadData(this.currentPage); | |
| }); | |
| this.currenciesService | |
| .create(createRequest) | |
| .pipe(untilDestroyed(this)) | |
| .subscribe({ | |
| next: () => { | |
| this.alert.success("Запись о курсе валюты была создана"); | |
| this.editForm = null; | |
| this.loadData(this.currentPage); | |
| }, | |
| error: (err) => { | |
| this.alert.error("Не удалось создать запись. Попробуйте позже."); | |
| console.error("Failed to create currency record:", err); | |
| } | |
| }); |
| this.currenciesService | ||
| .delete(item.id) | ||
| .pipe(untilDestroyed(this)) | ||
| .subscribe(() => { | ||
| this.alert.success("Запись была удалена"); | ||
| this.confirmDeletionMessage = null; | ||
| this.loadData(this.currentPage); | ||
| }); |
There was a problem hiding this comment.
Add error handling to the delete subscription.
The delete subscription lacks an error handler. If the API call fails, the user receives no feedback and the confirmation dialog may remain open.
🔎 Suggested fix
this.currenciesService
.delete(item.id)
.pipe(untilDestroyed(this))
- .subscribe(() => {
- this.alert.success("Запись была удалена");
- this.confirmDeletionMessage = null;
- this.loadData(this.currentPage);
+ .subscribe({
+ next: () => {
+ this.alert.success("Запись была удалена");
+ this.confirmDeletionMessage = null;
+ this.loadData(this.currentPage);
+ },
+ error: (err) => {
+ this.alert.error("Не удалось удалить запись. Попробуйте позже.");
+ this.confirmDeletionMessage = null;
+ console.error("Failed to delete currency record:", err);
+ }
});As per coding guidelines for Angular components and proper error handling.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| this.currenciesService | |
| .delete(item.id) | |
| .pipe(untilDestroyed(this)) | |
| .subscribe(() => { | |
| this.alert.success("Запись была удалена"); | |
| this.confirmDeletionMessage = null; | |
| this.loadData(this.currentPage); | |
| }); | |
| this.currenciesService | |
| .delete(item.id) | |
| .pipe(untilDestroyed(this)) | |
| .subscribe({ | |
| next: () => { | |
| this.alert.success("Запись была удалена"); | |
| this.confirmDeletionMessage = null; | |
| this.loadData(this.currentPage); | |
| }, | |
| error: (err) => { | |
| this.alert.error("Не удалось удалить запись. Попробуйте позже."); | |
| this.confirmDeletionMessage = null; | |
| console.error("Failed to delete currency record:", err); | |
| } | |
| }); |
🤖 Prompt for AI Agents
In src/app/modules/admin/components/currencies-page/currencies-page.component.ts
around lines 113-120, the subscription to this.currenciesService.delete(...) has
no error handler; add an error callback to the subscribe so API failures are
handled: on success keep the existing behavior (show success alert, clear
confirmDeletionMessage, reload data), and on error call this.alert.error(...)
with a user-friendly message (and optionally log the error), ensure
confirmDeletionMessage is cleared or left intact per UX decision (prefer
clearing so dialog closes), and keep the untilDestroyed(this) operator; also
consider disabling any loading state before returning from the error handler.
| *ngIf="!isLoading && !errorMessage && chartData && chartData.length > 0" | ||
| class="chart-container" | ||
| > | ||
| <canvas [id]="canvasId">{{ chart }}</canvas> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's look at the HTML file around line 59
head -80 src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.html | tail -30Repository: Techinterview-space/frontend
Length of output: 1138
🏁 Script executed:
# Also check the TypeScript component to understand the context
find src/app/modules/salaries/components/currencies-chart-page -name "*.ts" -type fRepository: Techinterview-space/frontend
Length of output: 347
🏁 Script executed:
# Search for similar canvas patterns with interpolation
rg -n -C2 '<canvas.*>\{\{.*\}\}</canvas>' --type htmlRepository: Techinterview-space/frontend
Length of output: 14224
🏁 Script executed:
cat -n src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.ts | head -100Repository: Techinterview-space/frontend
Length of output: 3140
🏁 Script executed:
# Check if 'chart' property is defined and what it contains
rg -n 'chart\s*[:=]' src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.tsRepository: Techinterview-space/frontend
Length of output: 198
Remove the {{ chart }} interpolation from the canvas element.
The canvas element doesn't render interpolated content as fallback text—when chart is an object, this results in [object Object], which provides no meaningful fallback. If fallback content is needed for accessibility, use a descriptive string instead. This pattern appears throughout the codebase but serves no practical purpose.
Additionally, consider using [attr.id] instead of [id] for consistency with similar chart components elsewhere in the project.
🤖 Prompt for AI Agents
In
src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.html
around line 59, remove the "{{ chart }}" interpolation inside the <canvas>
because canvas does not render object interpolation (it results in "[object
Object]") and provides no useful fallback; replace the attribute binding [id]
with [attr.id] for consistency with other chart components, and if accessible
fallback text is required, add a static descriptive string (e.g., "Currency
chart") between the canvas tags instead of interpolating the chart object.
| 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); | ||
| }, |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Use AfterViewChecked or ngAfterViewInit instead of setTimeout.
The setTimeout(() => this.initChart(), 0) is an anti-pattern in Angular. Use proper lifecycle hooks to ensure the view is ready before initializing the chart.
🔎 Suggested implementation
Remove the setTimeout and initialize the chart after the view is ready:
+ private needsChartInit = false;
+
ngOnInit(): void {
this.loadChartData();
}
+ ngAfterViewChecked(): void {
+ if (this.needsChartInit && this.chartData) {
+ this.needsChartInit = false;
+ this.initChart();
+ }
+ }
private loadChartData(): void {
this.isLoading = true;
this.errorMessage = null;
this.currenciesService
.getChartData()
.pipe(untilDestroyed(this))
.subscribe({
next: (response: CurrencyChartResponse) => {
this.chartData = response.weeklyData ?? [];
this.isLoading = false;
-
- // Initialize chart after data is loaded and view is ready
- setTimeout(() => this.initChart(), 0);
+ this.needsChartInit = true;
},Don't forget to add AfterViewChecked to the implements clause.
As per coding guidelines for Angular components.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
src/app/modules/salaries/components/currencies-chart-page/currencies-chart-page.component.ts
around lines 48-55, remove the setTimeout anti-pattern and instead initialize
the chart from a proper lifecycle hook: add AfterViewChecked (or ngAfterViewInit
if one-time) to the component's implements clause, introduce a small guarded
flag (e.g., chartInitialized) or check that isLoading is false and chartData
exists, then call this.initChart() from ngAfterViewChecked/ngAfterViewInit once
the view is ready and chart hasn't been initialized; also remove the setTimeout
block in the subscription next handler and only set this.chartData and
this.isLoading there so the lifecycle hook triggers initialization.
| getAll( | ||
| pageParams: PageParams = defaultPageParams, | ||
| ): Observable<PaginatedList<CurrencyRecord>> { | ||
| return this.api.get<PaginatedList<CurrencyRecord>>( | ||
| this.apiUrl + "?" + new ConvertObjectToHttpParams(pageParams).get(), | ||
| ); | ||
| } | ||
|
|
||
| create(data: CreateCurrencyRecordRequest): Observable<CurrencyRecord> { | ||
| return this.api.post<CurrencyRecord>(this.apiUrl, data); | ||
| } | ||
|
|
||
| delete(id: string): Observable<void> { | ||
| return this.api.delete<void>(this.apiUrl + id); | ||
| } | ||
|
|
||
| getChartData(): Observable<CurrencyChartResponse> { | ||
| return this.api.get<CurrencyChartResponse>(this.apiUrl + "chart"); | ||
| } |
There was a problem hiding this comment.
Add error handling and retry strategies to service methods.
The service methods return raw observables without error handling, retry logic, or transformation. This forces each consumer to implement error handling independently, leading to inconsistency.
🔎 Recommended error handling pattern
+import { catchError, retry } from 'rxjs/operators';
+import { throwError } from 'rxjs';
getAll(
pageParams: PageParams = defaultPageParams,
): Observable<PaginatedList<CurrencyRecord>> {
return this.api.get<PaginatedList<CurrencyRecord>>(
this.apiUrl + "?" + new ConvertObjectToHttpParams(pageParams).get(),
- );
+ ).pipe(
+ retry(1),
+ catchError(err => {
+ console.error('Failed to fetch currency records:', err);
+ return throwError(() => new Error('Unable to load currency data'));
+ })
+ );
}
create(data: CreateCurrencyRecordRequest): Observable<CurrencyRecord> {
- return this.api.post<CurrencyRecord>(this.apiUrl, data);
+ return this.api.post<CurrencyRecord>(this.apiUrl, data).pipe(
+ catchError(err => {
+ console.error('Failed to create currency record:', err);
+ return throwError(() => new Error('Unable to create currency record'));
+ })
+ );
}
delete(id: string): Observable<void> {
- return this.api.delete<void>(this.apiUrl + id);
+ return this.api.delete<void>(this.apiUrl + id).pipe(
+ catchError(err => {
+ console.error('Failed to delete currency record:', err);
+ return throwError(() => new Error('Unable to delete currency record'));
+ })
+ );
}
getChartData(): Observable<CurrencyChartResponse> {
- return this.api.get<CurrencyChartResponse>(this.apiUrl + "chart");
+ return this.api.get<CurrencyChartResponse>(this.apiUrl + "chart").pipe(
+ retry(1),
+ catchError(err => {
+ console.error('Failed to fetch chart data:', err);
+ return throwError(() => new Error('Unable to load chart data'));
+ })
+ );
}Alternatively, consider using a centralized logging service instead of console.error.
As per coding guidelines for Angular services and RxJS best practices.
🤖 Prompt for AI Agents
In src/app/services/currencies-collection.service.ts around lines 50 to 68, the
methods return raw observables without retry or centralized error handling;
update each method (getAll, create, delete, getChartData) to pipe their API
calls through RxJS operators: add a retry strategy (e.g., retry with exponential
backoff or a fixed retry count using retryWhen/scan/delay or the retry
operator), then catchError to transform/normalize errors into a consistent
DomainError or Observable throwError, and call a centralized logging service
(inject LoggerService or similar) inside catchError instead of console.error;
also add necessary RxJS imports and ensure the public method signatures remain
unchanged while returning the transformed Observable types.
Summary by CodeRabbit
Release Notes
✏️ Tip: You can customize this high-level summary in your review settings.