Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shutov / Create interface to upload employee #2633

Open
wants to merge 81 commits into
base: develop
Choose a base branch
from

Conversation

MarianS23
Copy link
Contributor

@MarianS23 MarianS23 commented Sep 14, 2024

Summary by CodeRabbit

Based on the comprehensive summary of changes, here are the release notes:

Release Notes

  • New Features

    • Added a new section for employee management in the provider's personal cabinet
    • Introduced Excel file upload functionality for employee data
    • Added a new navigation link for providers to access employee management
  • Improvements

    • Enhanced validation for employee data imports
    • Added support for role-based navigation and access control
    • Improved user interface for file uploads with detailed error reporting
  • Bug Fixes

    • Minor formatting and syntax corrections across various components
  • Technical Updates

    • Added new services for import validation and Excel processing
    • Introduced new utility functions and constants for data handling

@humanmantis
Copy link
Contributor

Sync branch with develop before I can start the review 🥺

@MarianS23 MarianS23 marked this pull request as draft January 11, 2025 18:20
@MarianS23 MarianS23 marked this pull request as ready for review January 26, 2025 17:05
Copy link

coderabbitai bot commented Jan 26, 2025

Walkthrough

This pull request introduces several enhancements to the application, focusing on employee management and import functionality for providers. The changes span multiple components and services, adding new routes, components, and validation mechanisms for uploading and managing employee data. Key additions include a new employee upload feature, routing for provider employees, and comprehensive validation services for imported Excel files. The modifications primarily target the personal cabinet section for providers, introducing new tabs, upload capabilities, and role-based navigation options.

Changes

File Change Summary
src/app/header/header.component.html Added conditional menu item for provider and provider deputy roles linking to employee page
src/app/shared/base-components/upload-excel/upload-excel.component.* New base component for Excel file uploads with comprehensive styling and functionality
src/app/shared/services/import-validation/import-validation.service.ts New service for validating imported employee data with extensive error checking
src/app/shared/services/excel-upload-processor/excel-upload-processor.service.ts New service for processing Excel file uploads and conversions
src/app/shell/personal-cabinet/provider/provider-employees/* New components for managing provider employees, including upload and listing functionality
src/app/shell/personal-cabinet/provider/provider-routing.module.ts Added new route for provider employees
src/app/shared/constants/regex-constants.ts Added new RNOKPP regex constant
src/app/shared/enum/enumUA/import-export.ts Added new enumerations for import/export column names and roles
src/app/shared/models/admin-import-export.model.ts Added new interfaces for employee data validation
src/app/shell/admin-tools/admin-tools.component.html Added new navigation link for import-export

Sequence Diagram

sequenceDiagram
    participant User
    participant ProviderEmployeesComponent
    participant UploadExcelComponent
    participant ImportValidationService
    participant ExcelUploadProcessorService
    participant EmployeeUploadProcessorService

    User->>ProviderEmployeesComponent: Navigate to Employees Tab
    User->>UploadExcelComponent: Select Excel File
    UploadExcelComponent->>ExcelUploadProcessorService: Convert Excel to JSON
    ExcelUploadProcessorService-->>UploadExcelComponent: Return Parsed Data
    UploadExcelComponent->>ImportValidationService: Validate Employee Data
    ImportValidationService-->>UploadExcelComponent: Return Validation Results
    UploadExcelComponent->>EmployeeUploadProcessorService: Upload Valid Employees
    EmployeeUploadProcessorService-->>UploadExcelComponent: Confirm Upload
Loading

Poem

🐰 Hop, hop, hooray! New features today!
Excel files dance, employees take flight
Validation tight, roles shining bright
Provider's domain, now clear and light
CodeRabbit's magic makes workflows delight! 🎉

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 27

🔭 Outside diff range comments (1)
src/app/shared/components/institution-hierarchy/institution-hierarchy.component.html (1)

Restore the *ngIf="hierarchy.shouldDisplay" condition

The shouldDisplay property is actively used in the component's logic to control the visibility of hierarchy levels. Its removal will cause unnecessary rendering of unpopulated levels, as the property is still managed in the component's lifecycle:

  • Initialized as false in new hierarchy elements
  • Set to true when new hierarchy elements are populated
🔗 Analysis chain

Line range hint 18-31: Verify the removal of *ngIf condition.

The removal of the *ngIf="hierarchy.shouldDisplay" condition might lead to unnecessary rendering of hierarchy elements. Please confirm if this was intentional and consider the performance implications.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for usage of shouldDisplay property
rg "shouldDisplay" --type ts

Length of output: 369


Script:

#!/bin/bash
# Get the full implementation of the component
ast-grep --pattern 'export class InstitutionHierarchyComponent {
  $$$
}'

# Look for similar conditional rendering patterns
rg "shouldDisplay" -B 2 -A 2

Length of output: 1822

♻️ Duplicate comments (2)
src/app/shared/services/import-validation/import-validation.service.ts (1)

27-43: 🛠️ Refactor suggestion

Refactor validation logic for better maintainability.

The validation logic can be improved in several ways:

  1. Extract magic numbers into named constants
  2. Use a validator map pattern for better maintainability
  3. Consider handling multiple errors vs early return

Here's a suggested refactor:

const VALIDATION_CONSTANTS = {
  MIN_LENGTH: 2,
  MAX_LENGTH: 50
};

const VALIDATORS = {
  checkEmpty: (value: any) => !value,
  checkLength: (value: string) => 
    value.length <= VALIDATION_CONSTANTS.MIN_LENGTH || 
    value.length > VALIDATION_CONSTANTS.MAX_LENGTH,
  checkLanguage: (value: string) => !NO_LATIN_REGEX.test(value),
  checkDuplicate: (value: any, items: any[]) => this.findDuplicates(items, value),
  checkAssignedRole: (value: string) => 
    value !== ImportEmployeesChosenRole.employee && 
    value !== ImportEmployeesChosenRole.deputyDirector
};

private validateField(fieldName: string, item: any, items: any, config: FieldValidationConfig): void {
  Object.entries(config).forEach(([validationType, shouldValidate]) => {
    if (shouldValidate && VALIDATORS[validationType]?.(item[fieldName], items)) {
      item.errors[`${fieldName}${validationType.charAt(0).toUpperCase() + validationType.slice(1)}`] = true;
      // Optionally return early if you want to show only first error
      // return;
    }
  });
}
src/app/shared/base-components/upload-excel/upload-excel.component.ts (1)

23-23: 🛠️ Refactor suggestion

Replace 'any' types with proper interfaces.

Using 'any' reduces type safety. Consider creating proper interfaces for the data structures.

-  public selectedFile: any = null;
+  public selectedFile: File | null = null;

-  public renamingKeys(items: any[]): any[] {
+  public renamingKeys<T, U>(items: T[]): U[] {

Also applies to: 171-173

🧹 Nitpick comments (63)
src/app/shell/details/details.component.css (1)

1-5: Consider standardizing breakpoints and margins.

The media query uses a custom breakpoint of 750px, which might lead to inconsistent responsive behavior. Additionally, the .wrapper class has different margin values across components (2rem 1rem 0 in provider-about.component.css).

Consider:

  1. Using standard breakpoints (e.g., Bootstrap's 768px for md)
  2. Standardizing margins across components using CSS variables
-@media (max-width: 750px) {
+:root {
+  --wrapper-margin-top: 20px;
+  --wrapper-margin: 2rem 1rem 0;
+}
+
+@media (max-width: 768px) {
   .wrapper {
-    margin-top: 20px;
+    margin-top: var(--wrapper-margin-top);
   }
 }
src/app/shared/components/institution-hierarchy/institution-hierarchy.component.ts (2)

Line range hint 1-199: Consider these improvements for better maintainability and type safety.

While the overall implementation is solid, here are some suggestions to enhance the code:

  1. Consider using a common pattern for subscription handling:
private readonly subscriptions = new Subscription();

ngOnInit(): void {
  this.subscriptions.add(
    this.institutions$.pipe(
      takeUntil(this.destroy$)
    ).subscribe(...)
  );
}
  1. Add stricter typing for the form controls:
@Input() public instituitionHierarchyIdFormControl!: FormControl<string | null>;
@Input() public instituitionIdFormControl!: FormControl<string | null>;
  1. Consider extracting the hierarchy creation logic into a separate service to improve testability and reduce component complexity.

Would you like me to provide a detailed implementation for any of these suggestions?

🧰 Tools
🪛 GitHub Check: build_and_test

[warning] 67-67:
Unexpected console statement


Line range hint 19-23: Optimize change detection strategy.

The component uses OnPush change detection, which is great for performance. However, consider these optimizations:

  1. Some markForCheck() calls might be unnecessary when using async pipe in the template
  2. State updates through the store automatically trigger change detection
  3. Form control value changes automatically trigger change detection

Review each markForCheck() call and consider if it's truly needed. For example, when using async pipe with *ngFor="let item of items$ | async", manual change detection is not required.

🧰 Tools
🪛 GitHub Check: build_and_test

[warning] 67-67:
Unexpected console statement

src/app/shared/styles/calendar.css (5)

1-8: Consider using CSS variables and responsive dimensions.

The calendar container has hardcoded values that could be improved:

  1. Extract the color and shadow values into CSS variables for better maintainability
  2. Consider using relative units (rem/em) for max-width/height to support different screen sizes
+:root {
+  --calendar-max-width: 20rem;
+  --calendar-max-height: 19.8rem;
+  --calendar-bg: #ffffff;
+  --calendar-shadow: 0px 8px 10px -5px rgba(0, 0, 0, 0.2),
+                     0px 16px 24px 2px rgba(0, 0, 0, 0.14),
+                     0px 6px 30px 5px rgba(0, 0, 0, 0.12);
+  --calendar-radius: 1.5rem;
+}
 ::ng-deep .mat-datepicker-content .mat-calendar {
-  max-width: 320px;
-  max-height: 317px;
+  max-width: var(--calendar-max-width);
+  max-height: var(--calendar-max-height);
   padding: 1rem;
-  background: #ffffff;
-  box-shadow: 0px 8px 10px -5px rgba(0, 0, 0, 0.2), 0px 16px 24px 2px rgba(0, 0, 0, 0.14), 0px 6px 30px 5px rgba(0, 0, 0, 0.12);
-  border-radius: 24px;
+  background: var(--calendar-bg);
+  box-shadow: var(--calendar-shadow);
+  border-radius: var(--calendar-radius);
 }

10-13: Avoid using !important in CSS rules.

The !important declaration should be avoided as it makes styles harder to maintain and override when needed. Consider restructuring the specificity of your selectors instead.

 ::ng-deep .mat-datepicker-content {
   box-shadow: none;
-  background-color: transparent !important;
+  background-color: transparent;
 }

29-36: Extract color values into CSS variables.

The color #3849f9 is used multiple times and should be defined as a CSS variable for better maintainability and consistency.

+:root {
+  --primary-color: #3849f9;
+}
 ::ng-deep .mat-calendar-body-selected {
-  background-color: #3849f9;
+  background-color: var(--primary-color);
 }
 
 ::ng-deep .mat-calendar-body-today:not(.mat-calendar-body-selected):not(.mat-calendar-body-comparison-identical) {
-  border-color: #3849f9;
+  border-color: var(--primary-color);
   background-color: transparent;
 }

38-40: Enhance hover interaction and use CSS variable.

Add a transition for smoother hover effect and use the CSS variable for color consistency.

 ::ng-deep .mat-datepicker-toggle-default-icon:hover {
-  color: #3849f9;
+  color: var(--primary-color);
+  transition: color 0.2s ease;
 }

1-40: Consider alternatives to ::ng-deep selector.

The ::ng-deep combinator is deprecated and will be removed in future Angular versions. Consider these alternatives:

  1. Use Angular's ViewEncapsulation.None
  2. Move styles to a global stylesheet
  3. Use CSS custom properties (variables) exposed by Angular Material

Would you like me to provide examples of implementing these alternatives?

src/app/shared/enum/enumUA/import-export.ts (2)

13-13: Fix typo in Ukrainian text.

The Ukrainian word "Імя" is missing an apostrophe. It should be "Ім'я".

-  employeeName = 'Імя',
+  employeeName = 'Ім'я',

1-22: Consider restructuring enums for better maintainability.

The current structure has three separate enums that are closely related. Consider combining them into a more maintainable structure using a TypeScript namespace or a const enum pattern.

Example approach:

export namespace EmployeeImport {
  export enum ColumnNames {
    SequenceNumber = 'sequenceNumber',
    // ... other column names
  }

  export enum Headers {
    SequenceNumber = '№',
    // ... other headers
  }

  export enum Roles {
    Employee = 'Співробітник ЗО',
    DeputyDirector = 'Заступник директора ЗО'
  }
}
src/app/shared/models/admin-import-export.model.ts (3)

1-8: Consider reducing duplication between Employee interfaces.

The Employee interface largely duplicates the properties from ValidEmployee. Consider extending the base interface to reduce duplication.

export interface ValidEmployee {
  id: number;
  employeeSurname: string;
  employeeName: string;
  employeeFatherName: string;
  employeeRNOKPP: number;
  employeeAssignedRole: string;
}

- export interface Employee {
-   sequenceNumber: number;
-   employeeSurname: string;
-   employeeName: string;
-   employeeFatherName: string;
-   employeeRNOKPP: number;
-   employeeAssignedRole: string;
-   errors: EmployeeValidationErrors;
- }
+ export interface Employee extends ValidEmployee {
+   sequenceNumber: number;
+   errors: EmployeeValidationErrors;
+ }

Also applies to: 9-17


19-37: Consider using a more type-safe approach for validation errors.

The current structure allows for undefined validation errors. Consider using a more type-safe approach with discriminated unions.

export type ValidationErrorType = 
  | 'empty'
  | 'length'
  | 'language'
  | 'format'
  | 'duplicate';

export interface EmployeeValidationError {
  field: keyof ValidEmployee;
  type: ValidationErrorType;
}

export type EmployeeValidationErrors = EmployeeValidationError[];

39-51: Consider using TypeScript's built-in utility types for validation config.

The current validation config structure could benefit from TypeScript's utility types to ensure type safety and reduce potential errors.

export type ValidatableFields = keyof Omit<ValidEmployee, 'id'>;

export type ValidationChecks = {
  empty: boolean;
  length: boolean;
  language: boolean;
  assignedRole: boolean;
  RNOKPP: boolean;
  duplicate: boolean;
};

export type FieldValidationConfig = Partial<ValidationChecks>;

export interface FieldsConfig {
  fieldName: ValidatableFields;
  validationParam: FieldValidationConfig;
}
src/app/shared/services/import-validation/import-validation.service.ts (1)

6-10: Remove unnecessary constructor.

The empty constructor can be removed as it serves no purpose.

@Injectable({
  providedIn: 'root'
})
export class ImportValidationService {
-  constructor() {}
🧰 Tools
🪛 Biome (1.9.4)

[error] 10-10: This constructor is unnecessary.

Unsafe fix: Remove the unnecessary constructor.

(lint/complexity/noUselessConstructor)

src/app/shared/services/employee-upload-processor/employee-upload-processor.service.spec.ts (1)

23-51: Enhance test coverage with additional test cases.

While the current tests cover basic functionality, consider adding these test cases:

  • Empty employee list
  • Invalid provider ID
  • Different HTTP error codes (400, 401, 403)
  • Malformed response handling

Example additional test:

it('should handle empty employee list', () => {
  const mockItems = [];
  const mockId = '123';
  
  service.uploadEmployeesList(mockItems, mockId).subscribe(
    () => fail('should have failed with empty list'),
    (error) => {
      expect(error.status).toBe(400);
      expect(error.body).toContain('empty list');
    }
  );
  
  const req = httpTestingController.expectOne('/api/v1/Provider/Upload/123/employees/upload');
  req.flush('Cannot upload empty list', { status: 400, statusText: 'Bad Request' });
  httpTestingController.verify();
});
src/app/shared/services/excel-upload-processor/excel-upload-processor.service.ts (1)

15-48: Consider adding type safety to the convertExcelToJSON method.

The method returns Observable<any[]>. Consider creating an interface for the expected data structure.

-  public convertExcelToJSON(file: File, standartHeadersBase: string[], columnNamesBase: string[]): Observable<any[]> {
+  public convertExcelToJSON<T>(file: File, standartHeadersBase: string[], columnNamesBase: string[]): Observable<T[]> {
src/app/shared/services/import-validation/import-validation.service.spec.ts (2)

17-35: Consider using more descriptive test data.

The test data uses generic names like 'invalidRole'. Consider using real-world examples that better represent actual use cases.

-    const items = [{ name: '', role: 'invalidRole', errors: {} }];
+    const items = [{ name: '', role: 'SuperManager', errors: {} }];

79-99: Add test cases for RNOKPP validation.

The test acknowledges that RNOKPP validation is not implemented but doesn't create a reminder to implement it.

Would you like me to create an issue to track the implementation of RNOKPP validation?

src/app/shared/services/excel-upload-processor/excel-upload-processor.service.spec.ts (1)

67-98: Add test cases for different Excel file formats.

The test only covers XLSX format. Consider adding tests for XLS and CSV formats.

it('should handle different Excel file formats', (done) => {
  const formats = [
    { type: 'application/vnd.ms-excel', ext: 'xls' },
    { type: 'text/csv', ext: 'csv' }
  ];
  // Test implementation
});
src/app/shared/base-components/upload-excel/upload-excel.component.spec.ts (3)

76-76: Remove the Ukrainian comment.

The comment "Тестуємо правильний URL" should be in English for consistency.

-  const req = httpMock.expectOne(`/api/v1/Provider/Upload/${component.currentUserId}/employees/upload`); // Тестуємо правильний URL
+  const req = httpMock.expectOne(`/api/v1/Provider/Upload/${component.currentUserId}/employees/upload`); // Testing correct URL

147-166: Avoid setTimeout in tests.

The getCurrentUserId tests use setTimeout which can make tests flaky. Consider using fakeAsync and tick() for more reliable async testing.

-  it('should update currentUserId with the value from the store', (done) => {
+  it('should update currentUserId with the value from the store', fakeAsync(() => {
     const mockUserId = '1234';
     mockStore.select.mockReturnValue(of(mockUserId));
     component.getCurrentUserId();
-    setTimeout(() => {
-      expect(component.currentUserId).toBe(mockUserId);
-      done();
-    }, 0);
+    tick();
+    expect(component.currentUserId).toBe(mockUserId);
   });

280-281: Remove eslint-disable comments.

Instead of disabling eslint, consider using a more idiomatic way to access private members in tests.

-      // eslint-disable-next-line @typescript-eslint/dot-notation, dot-notation
-      checkForInvalidDataSpy = jest.spyOn(component['importValidationService'], 'checkForInvalidData').mockImplementation(() => {});
+      checkForInvalidDataSpy = jest.spyOn(component['importValidationService'] as ImportValidationService, 'checkForInvalidData').mockImplementation(() => {});
src/app/shared/styles/hide-aside-animation.css (1)

6-16: Consider performance optimization for the hide animation.

While the animation uses performant properties (transform and opacity), setting position: absolute at the end of the animation can cause layout shifts. Consider:

  1. Setting position: absolute at the start of animation
  2. Using will-change: transform, opacity for better performance
 @keyframes hide {
+  0% {
+    position: absolute;
+  }
   40% {
     opacity: 0;
     transform: translateX(100%);
   }
   100% {
     opacity: 0;
     transform: translateX(100%);
-    position: absolute;
   }
 }
+
+.hide {
+  will-change: transform, opacity;
+}
src/app/shared/styles/validation-form.css (3)

3-6: Use semantic color variables instead of hardcoded values.

Consider using semantic color variables for better maintainability and consistency.

 .step-input.ng-invalid.ng-touched,
 .step-textarea.ng-invalid.ng-touched,
-.redBorder {
+.validation-error {
   outline: none;
-  border: 1px solid #c72a21;
+  border: 1px solid var(--error-color);
 }

8-10: Add transition for smooth opacity changes.

The disabled state would benefit from a smooth transition.

 .disabled-button {
   opacity: 0.5;
+  transition: opacity 0.2s ease-in-out;
 }

12-14: Use semantic color variable for highlight text.

Similar to the validation border, use a semantic color variable for consistency.

 .highlight-text {
-  color: red;
+  color: var(--error-color);
 }
src/app/shared/components/stretch-cell/stretch-cell/stretch-cell.component.css (1)

13-13: Consider using a standardized z-index scale.

The z-index value of 20 appears arbitrary. Consider implementing a z-index scale/system to maintain consistent layering across the application.

src/app/shared/styles/config.css (1)

2-2: Consider using CSS custom properties for consistent values.

Multiple hardcoded values (colors, widths) could be moved to CSS custom properties for better maintainability:

  • Width: 500px
  • Color: #3849f9
  • Font weight: 600
:root {
+  --config-width: 500px;
+  --primary-color: #3849f9;
+  --font-weight-semibold: 600;
}

.config {
-  width: 500px;
+  width: var(--config-width);
}

.config .link {
-  color: #3849f9;
-  font-weight: 600;
+  color: var(--primary-color);
+  font-weight: var(--font-weight-semibold);
}

Also applies to: 23-24

src/app/shared/styles/info-hint.css (1)

15-15: Add font-family fallbacks for better cross-browser support.

Specify fallback fonts to ensure consistent rendering across different systems:

-  font-family: "Open Sans";
+  font-family: "Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
src/app/shared/styles/application-list.css (2)

30-31: Add font-family fallbacks and consider using system fonts.

The "Innerspace" font might not be available on all systems. Add appropriate fallbacks:

-  font-family: "Innerspace";
+  font-family: "Innerspace", system-ui, -apple-system, sans-serif;

35-43: Consider adopting a mobile-first approach.

The current media query targets desktop-first. Consider inverting the approach for better maintainability:

-@media (max-width: 1500px) {
+@media (min-width: 1501px) {
  .dropdown {
-    position: static;
-    margin-bottom: 1rem;
-    justify-content: space-between;
+    position: absolute;
+    right: 0px;
+    justify-content: flex-end;
  }
}
src/app/shared/styles/status-themes.css (2)

1-28: Consider using CSS variables for color values.

The color values are hardcoded throughout the file. Consider extracting them into CSS variables for better maintainability and consistency.

+:root {
+  --color-editing: #ffbd00;
+  --color-approved: #199953;
+  --color-pending: #007bda;
+  --color-blocked: #c72a21;
+  --color-editing-bg: rgba(255, 189, 0, 0.1);
+  --color-approved-bg: rgba(25, 153, 83, 0.1);
+}

 :host.Editing {
-  background: linear-gradient(0deg, rgba(255, 189, 0, 0.1), rgba(255, 189, 0, 0.1)), #ffffff;
+  background: linear-gradient(0deg, var(--color-editing-bg), var(--color-editing-bg)), #ffffff;
 }
 :host.Editing .status-icon, :host.Editing .status-title {
-  color: #ffbd00;
+  color: var(--color-editing);
 }

17-22: Consider merging shared styles for Pending and Recheck statuses.

Since Pending and Recheck statuses share the same styles, consider using a CSS class to reduce duplication.

+.status-info {
+  background: #e1f1f9;
+}
+.status-info .status-icon, .status-info .status-title {
+  color: #007bda;
+}

-:host.Pending, :host.Recheck {
-  background: #e1f1f9;
-}
-:host.Pending .status-icon, :host.Pending .status-title, :host.Recheck .status-icon, :host.Recheck .status-title {
-  color: #007bda;
-}

+:host.Pending, :host.Recheck {
+  @extend .status-info;
+}
src/app/shared/components/info-menu/info-menu.component.css (1)

43-43: Use consistent color format.

The color value rgba(0, 0, 0, 0.8705882353) uses a decimal alpha value. Consider using a more readable hex format or a rounded decimal.

-  color: rgba(0, 0, 0, 0.8705882353);
+  color: rgba(0, 0, 0, 0.87);
src/app/shared/styles/list-wrappers.css (1)

1-11: Remove duplicate flex-wrap property.

The flex-wrap: wrap property is declared twice in .empty-list-wrapper.

 .empty-list-wrapper {
   display: flex;
   flex-wrap: wrap;
   justify-content: flex-start;
   align-items: center;
   background: #e5e6f1;
   border-radius: 5px;
   min-height: 66px;
   margin: 2rem 0;
-  flex-wrap: wrap;
 }
src/app/shared/components/filters-list/full-search-bar/full-search-bar.component.css (1)

6-6: Consider using CSS clamp for responsive width.

Instead of using calc() with viewport units, consider using clamp() for a more fluid responsive design.

-  max-width: calc(100vw - 1.25rem);
+  max-width: clamp(320px, 100vw - 1.25rem, 1200px);
src/app/shared/components/image-carousel/image-carousel.component.css (2)

1-7: Consider making image height responsive

The fixed height of 400px might not be optimal for all screen sizes. Consider using relative units or media queries for better responsiveness.

 .image {
   -o-object-fit: contain;
      object-fit: contain;
   width: 100%;
-  height: 400px;
+  height: min(400px, 50vh);
   position: relative;
 }

28-28: Use CSS variables for consistent theming

The hardcoded color value #3849f9 appears multiple times. Consider using CSS variables for better maintainability.

- background-color: #3849f9;
+ background-color: var(--primary-color, #3849f9);
src/app/shared/components/paginator/paginator.component.css (1)

6-14: Add font fallbacks and fix line height

The font declaration lacks fallbacks, and line height uses comma instead of decimal point.

-  font-family: Innerspace;
+  font-family: "Innerspace", system-ui, sans-serif;
-  line-height: 14, 6px;
+  line-height: 14.6px;
src/app/shell/details/details-tabs/workshop-about/workshop-about.component.css (1)

22-29: Use relative units for font sizes

Consider using relative units for better accessibility and responsiveness.

-  font-size: 13px;
+  font-size: 0.8125rem;
-  line-height: 17, 7px;
+  line-height: 1.36;
src/app/shared/styles/navigation-tabs.css (2)

45-49: Consider using CSS custom properties for breakpoints

Hardcoded breakpoint values could be maintained centrally using CSS custom properties.

-@media (max-width: 1200px) {
+@media (max-width: var(--breakpoint-desktop, 1200px)) {

16-22: Consolidate box-sizing declarations

Box-sizing property is repeated in multiple selectors.

Consider moving the box-sizing property to a higher-level selector or using inheritance:

:host {
  box-sizing: border-box;
}

:host *,
:host *::before,
:host *::after {
  box-sizing: inherit;
}
src/app/shared/styles/pagination.css (1)

12-13: Extract color values into CSS custom properties.

Hardcoded color values make theme changes difficult to maintain. Consider extracting them into CSS custom properties:

+:root {
+  --text-color-primary: #444444;
+  --text-color-accent: #3849f9;
+}

-  color: #444444;
+  color: var(--text-color-primary);

-  color: #3849f9;
+  color: var(--text-color-accent);

Also applies to: 15-16, 28-29, 34-36, 40-42, 48-50

src/app/shared/styles/tables.css (2)

85-86: Avoid using !important.

Using !important makes styles difficult to override and maintain. Consider restructuring the selectors to achieve the desired specificity:

-.star-column {
-  padding: 0 !important;
+}
+
+/* Increase specificity instead of using !important */
+.table-container .star-column {
+  padding: 0;
+}

76-78: Consider accessibility for custom scrollbar styles.

Custom scrollbars may impact accessibility. Ensure the scrollbar remains visible and usable for all users. Consider adding styles for better contrast and touch targets:

::-webkit-scrollbar {
  height: 12px;
+  background-color: #f5f5f5;
+}
+
+::-webkit-scrollbar-thumb {
+  background-color: #888;
+  border-radius: 6px;
+}
+
+::-webkit-scrollbar-track {
+  background-color: #f5f5f5;
+}
src/app/shared/styles/full-width-card.css (1)

24-31: Extract typography settings into variables.

Font families and sizes should be defined as variables for consistency:

+:root {
+  --font-family-primary: "Innerspace";
+  --font-family-secondary: "Open Sans", sans-serif;
+  --font-size-title: 18px;
+  --line-height-title: 20px;
+}

.title {
-  font-family: "Innerspace";
+  font-family: var(--font-family-primary);
-  font-size: 18px;
-  line-height: 20px;
+  font-size: var(--font-size-title);
+  line-height: var(--line-height-title);
src/app/shared/styles/buttons.css (1)

3-4: Consider using CSS variables for consistent color management

The color values are hardcoded throughout the file. Consider using CSS variables for better maintainability and consistency.

+ :root {
+   --primary-color: #3849f9;
+   --text-color: #444444;
+   --border-color: #e3e3e3;
+   --disabled-color: #dddcdc;
+ }

  .btn {
    color: white;
-   background-color: #3849f9;
-   border: 1px solid #3849f9;
+   background-color: var(--primary-color);
+   border: 1px solid var(--primary-color);
    /* ... */
  }

Also applies to: 16-16, 26-26, 30-31, 36-36, 56-57

src/app/shared/components/filters-list/age-filter/age-filter.component.css (1)

55-63: Improve clear button accessibility and positioning

The clear button's visibility toggle and absolute positioning might cause issues with keyboard navigation and screen readers.

 .clear-age {
   cursor: pointer;
-  visibility: hidden;
+  opacity: 0;
+  transition: opacity 0.2s ease-in-out;
   position: absolute;
   font-size: 18px;
   color: #adadad;
   top: 8px;
-  left: 114px;
+  right: 8px;
 }
src/app/shared/components/phone-form-control/phone-form-control.component.css (1)

33-41: Improve input styling consistency

The country search input uses multiple !important declarations. Consider using CSS variables and proper specificity.

- .country-search {
+ :host {
+   --search-input-padding: 13px;
+   --search-input-margin: 4px;
+   --search-input-font-size: 13px;
+ }

+ .country-search {
   outline: none !important;
-  padding-left: 13px !important;
-  margin: 4px !important;
-  font-size: 13px !important;
+  padding-left: var(--search-input-padding);
+  margin: var(--search-input-margin);
+  font-size: var(--search-input-font-size);
   font-family: "Open Sans", sans-serif !important;
   width: calc(100% - 20px) !important;
   box-sizing: border-box;
 }
src/app/shared/components/message-bar/message-bar.component.css (3)

6-23: Consider replacing hardcoded colors with CSS variables.

Using CSS variables for colors would improve maintainability and make theme changes easier.

+:root {
+  --color-text-default: #444444;
+  --color-success-bg: #c4ebc5;
+  --color-success-border: #199953;
+  --color-error-bg: #f8e4dd;
+  --color-error-border: #ff5252;
+}

.mat-mdc-snack-bar-container {
-  color: #444444;
+  color: var(--color-text-default);
}

::ng-deep .success {
-  background: #c4ebc5;
+  background: var(--color-success-bg);
}
::ng-deep .success .icon {
-  border: solid 1px #199953;
-  color: #199953;
+  border: solid 1px var(--color-success-border);
+  color: var(--color-success-border);
}

Also applies to: 60-74, 76-82


1-4: Extract repeated values into CSS variables.

Common values like border-radius and spacing should be defined as CSS variables for consistency.

+:root {
+  --border-radius-default: 2rem;
+  --spacing-icon: 0.5rem;
+}

.popup-body {
  min-height: 30px;
-  border-radius: 2rem;
+  border-radius: var(--border-radius-default);
}

.icon {
-  margin-right: 0.5rem;
-  border-radius: 2rem;
+  margin-right: var(--spacing-icon);
+  border-radius: var(--border-radius-default);
}

Also applies to: 36-47


6-23: Consider alternatives to ::ng-deep.

The ::ng-deep combinator is deprecated. Consider using CSS custom properties or component-specific selectors.

Alternative approaches:

  1. Use CSS custom properties (variables) passed from parent components
  2. Use more specific selectors targeting component classes
  3. Consider using Angular's ViewEncapsulation.None if styles must be global

Also applies to: 17-20, 21-23

src/app/shared/components/theme-switcher/theme-switcher.component.css (2)

3-16: Consider using CSS variables for colors.

Extract hardcoded color values into CSS variables for better maintainability and theme consistency.

+:root {
+  --theme-primary-color: #3849f9;
+  --theme-secondary-color: #032998;
+  --theme-disabled-color: #c0c0c0;
+  --theme-track-color: #ffffff;
+}
 ::ng-deep .light-theme .mat-mdc-slide-toggle,
 ::ng-deep .dark-theme .mat-mdc-slide-toggle {
-  --mdc-switch-unselected-track-color: #ffffff;
+  --mdc-switch-unselected-track-color: var(--theme-track-color);
   // Apply similar changes to other color values

59-61: Add aria-hidden attribute to decorative icons.

For better accessibility, add aria-hidden to the icon paths that are hidden with display: none.

 ::ng-deep .theme-slide-toggle .mdc-switch__icon path {
   display: none;
+  aria-hidden: true;
 }
src/app/shared/styles/cards.css (2)

1-9: Centralize design tokens using CSS variables.

Consider extracting hardcoded values into CSS variables for better maintainability:

  • Colors (#3849f9, #333333, etc.)
  • Font families (Innerspace, Open Sans)
  • Box shadows
+:root {
+  --card-primary-color: #3849f9;
+  --card-text-color: #333333;
+  --card-font-primary: 'Innerspace';
+  --card-font-secondary: 'Open Sans';
+  --card-box-shadow: 0px 6px 16px rgba(0, 0, 0, 0.08);
+}
 .card {
   width: 300px;
   min-height: 430px;
   display: flex;
   justify-content: flex-start;
   flex-direction: column;
   align-items: flex-start;
-  box-shadow: 0px 6px 16px rgba(0, 0, 0, 0.08);
+  box-shadow: var(--card-box-shadow);
 }

10-16: Maintain consistent units throughout the styles.

The codebase mixes px and rem units. Consider standardizing to rem for better scalability:

 .card-title {
-  font-family: Innerspace;
+  font-family: var(--card-font-primary);
   font-style: normal;
   font-weight: bold;
-  font-size: 18px;
-  line-height: 20px;
+  font-size: 1.125rem;
+  line-height: 1.25rem;
 }

Also applies to: 40-52

src/app/shared/base-components/upload-excel/upload-excel.component.scss (1)

162-187: Optimize animation performance.

Consider using transform and opacity for better performance. Also, reduce the number of box-shadow changes:

 @keyframes loader {
   0% {
-    box-shadow:
-      10px 0 #3849f9,
-      -10px 0 #3849f933;
-    background: #3849f9;
+    transform: translateX(0);
+    opacity: 1;
   }
   50% {
-    box-shadow:
-      10px 0 #3849f9,
-      -10px 0 #3849f933;
-    background: #3849f933;
+    transform: translateX(10px);
+    opacity: 0.2;
   }
   100% {
-    box-shadow:
-      10px 0 #3849f933,
-      -10px 0 #3849f9;
-    background: #3849f9;
+    transform: translateX(0);
+    opacity: 1;
   }
 }
src/app/header/header.component.html (1)

148-154: Refactor duplicated role check into a method.

Consider extracting the role check into a method for better maintainability:

+// In the component class
+isProviderRole(role: string): boolean {
+  return role === Role.provider || role === Role.providerDeputy;
+}

-<a
-  *ngIf="user.role === Role.provider || user.role === Role.providerDeputy"
-  [routerLink]="'./personal-cabinet/provider/provider-employees'">
+<a
+  *ngIf="isProviderRole(user.role)"
+  [routerLink]="'./personal-cabinet/provider/provider-employees'">
src/app/shared/styles/details.css (2)

1-7: Consider using CSS variables for consistent theming.

The color values and key measurements are hardcoded throughout the file. Consider using CSS variables for better maintainability and theming support.

+:root {
+  --primary-color: #3849f9;
+  --text-color: #333333;
+  --border-color: rgba(0, 0, 0, 0.12);
+  --shadow-color: rgba(0, 0, 0, 0.08);
+}

 .container {
   background: #ffffff;
-  box-shadow: 0px 6px 16px rgba(0, 0, 0, 0.08);
+  box-shadow: 0px 6px 16px var(--shadow-color);
   width: 100%;
   padding: 0;
   margin-bottom: 2rem;
 }

 .container .details-link {
   display: flex;
   flex-direction: row;
   align-items: center;
-  color: #3849f9;
+  color: var(--primary-color);
 }

 :host ::ng-deep .mat-tab-label-active {
-  color: #3849f9;
+  color: var(--primary-color);
   opacity: 1;
 }

 :host ::ng-deep .mat-tab-group.mat-primary .mat-ink-bar {
-  background-color: #3849f9;
+  background-color: var(--primary-color);
   height: 2px;
 }

Also applies to: 42-43, 68-69, 73-74


126-128: Ensure consistent object-fit behavior across browsers.

The vendor prefix for object-fit is unnecessary in modern browsers. Consider simplifying the code.

 .coverImage {
   max-width: 100%;
   min-height: 400px;
-  -o-object-fit: contain;
-     object-fit: contain;
+  object-fit: contain;
 }
src/app/shared/components/filters-list/price-filter/price-filter.component.css (1)

98-106: Improve clear price button positioning for better maintainability.

The clear price button uses absolute positioning with hardcoded values, which could break with different text lengths or in different languages. Consider using a more flexible approach.

 .clear-price {
   cursor: pointer;
   visibility: hidden;
   position: absolute;
   font-size: 18px;
   color: #adadad;
   top: 12px;
-  left: 114px;
+  right: 12px;
 }
src/app/shared/components/notifications/notifications-list/notifications-list.component.css (2)

32-35: Consider dynamic max-height for better content visibility.

The fixed max-height of 300px might truncate content unnecessarily. Consider using a dynamic approach based on viewport height.

 .accordions-wrapper {
-  max-height: 300px;
+  max-height: 80vh;
   overflow: auto;
 }

85-89: Standardize transition properties.

Different transition properties are used for similar animations. Consider standardizing them for consistency.

 .info__link {
-  transition: color 0.1s ease;
+  transition: all 0.1s ease;
 }

 .body-notification__content:hover > .icon {
   opacity: 1;
-  transition: opacity 0.1s;
+  transition: all 0.1s ease;
 }

Also applies to: 105-108

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6ba50be and 755f2ad.

⛔ Files ignored due to path filters (66)
  • package.json is excluded by none and included by none
  • src/app/app.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/footer/footer.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/header/progress-bar/progress-bar.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/category-card/category-card.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/filters-list/age-filter/age-filter.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/filters-list/category-check-box/category-check-box.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/filters-list/filters-list.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/filters-list/full-search-bar/full-search-bar.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/filters-list/price-filter/price-filter.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/filters-list/user-radius-set/user-radius-set.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/image-carousel/image-carousel.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/info-menu/info-menu.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/map/map.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/message-bar/message-bar.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/navigation-bar/navigation-bar.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/navigation-mobile-bar/navigation-mobile-bar.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/notifications/notifications-list/notifications-list.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/notifications/notifications.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/paginator/paginator.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/phone-form-control/phone-form-control.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/scroll-to-top/scroll-to-top.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/sidenav-filters/sidenav-filters.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/sidenav-menu/sidenav-menu.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/stretch-cell/stretch-cell/stretch-cell.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/components/theme-switcher/theme-switcher.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/application-list.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/application-statuses.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/buttons.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/cabinet.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/calendar.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/card-wrapper.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/cards.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/config.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/details.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/dropdown.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/full-width-card.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/hide-aside-animation.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/info-hint.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/info.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/list-wrappers.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/navigation-tabs.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/pagination.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/search.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/side-menu-cards.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/status-themes.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/tables.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/toggle-button.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/validation-form.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/working-hours.css.map is excluded by !**/*.map and included by src/**
  • src/app/shared/styles/workshop-status.css.map is excluded by !**/*.map and included by src/**
  • src/app/shell/details/details-tabs/provider-about/provider-about.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shell/details/details-tabs/reviews/reviews.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shell/details/details-tabs/reviews/stars/stars.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shell/details/details-tabs/workshop-about/workshop-about.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shell/details/details.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shell/info/info.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shell/personal-cabinet/parent/parent-applications/parent-applications.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shell/personal-cabinet/provider/provider-applications/provider-applications.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shell/personal-cabinet/provider/provider-org-info/provider-org-info.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/child-info-box/child-info-box.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shell/personal-cabinet/shared-cabinet/messages/chat/message/message.component.css.map is excluded by !**/*.map and included by src/**
  • src/app/shell/shell.component.css.map is excluded by !**/*.map and included by src/**
  • src/assets/i18n/en.json is excluded by !src/assets/** and included by src/**
  • src/assets/i18n/uk.json is excluded by !src/assets/** and included by src/**
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock and included by none
📒 Files selected for processing (82)
  • src/app/app.component.css (1 hunks)
  • src/app/footer/footer.component.css (1 hunks)
  • src/app/header/header.component.html (1 hunks)
  • src/app/header/header.component.scss (1 hunks)
  • src/app/header/progress-bar/progress-bar.component.css (1 hunks)
  • src/app/shared/base-components/upload-excel/upload-excel.component.scss (1 hunks)
  • src/app/shared/base-components/upload-excel/upload-excel.component.spec.ts (1 hunks)
  • src/app/shared/base-components/upload-excel/upload-excel.component.ts (1 hunks)
  • src/app/shared/components/category-card/category-card.component.css (1 hunks)
  • src/app/shared/components/filters-list/age-filter/age-filter.component.css (1 hunks)
  • src/app/shared/components/filters-list/category-check-box/category-check-box.component.css (1 hunks)
  • src/app/shared/components/filters-list/filters-list.component.css (1 hunks)
  • src/app/shared/components/filters-list/full-search-bar/full-search-bar.component.css (1 hunks)
  • src/app/shared/components/filters-list/price-filter/price-filter.component.css (1 hunks)
  • src/app/shared/components/filters-list/searchbar/searchbar.component.html (1 hunks)
  • src/app/shared/components/filters-list/searchbar/searchbar.component.scss (0 hunks)
  • src/app/shared/components/filters-list/user-radius-set/user-radius-set.component.css (1 hunks)
  • src/app/shared/components/filters-list/working-hours/working-hours.component.html (2 hunks)
  • src/app/shared/components/image-carousel/image-carousel.component.css (1 hunks)
  • src/app/shared/components/info-menu/info-menu.component.css (1 hunks)
  • src/app/shared/components/institution-hierarchy/institution-hierarchy.component.html (1 hunks)
  • src/app/shared/components/institution-hierarchy/institution-hierarchy.component.ts (1 hunks)
  • src/app/shared/components/map/map.component.css (1 hunks)
  • src/app/shared/components/message-bar/message-bar.component.css (1 hunks)
  • src/app/shared/components/navigation-bar/navigation-bar.component.css (1 hunks)
  • src/app/shared/components/navigation-mobile-bar/navigation-mobile-bar.component.css (1 hunks)
  • src/app/shared/components/notifications/notifications-list/notifications-list.component.css (1 hunks)
  • src/app/shared/components/notifications/notifications.component.css (1 hunks)
  • src/app/shared/components/paginator/paginator.component.css (1 hunks)
  • src/app/shared/components/phone-form-control/phone-form-control.component.css (1 hunks)
  • src/app/shared/components/scroll-to-top/scroll-to-top.component.css (1 hunks)
  • src/app/shared/components/sidenav-filters/sidenav-filters.component.css (1 hunks)
  • src/app/shared/components/sidenav-menu/sidenav-menu.component.css (1 hunks)
  • src/app/shared/components/stretch-cell/stretch-cell/stretch-cell.component.css (1 hunks)
  • src/app/shared/components/theme-switcher/theme-switcher.component.css (1 hunks)
  • src/app/shared/constants/regex-constants.ts (1 hunks)
  • src/app/shared/enum/enumUA/import-export.ts (1 hunks)
  • src/app/shared/models/admin-import-export.model.ts (1 hunks)
  • src/app/shared/services/employee-upload-processor/employee-upload-processor.service.spec.ts (1 hunks)
  • src/app/shared/services/employee-upload-processor/employee-upload-processor.service.ts (1 hunks)
  • src/app/shared/services/excel-upload-processor/excel-upload-processor.service.spec.ts (1 hunks)
  • src/app/shared/services/excel-upload-processor/excel-upload-processor.service.ts (1 hunks)
  • src/app/shared/services/import-validation/import-validation.service.spec.ts (1 hunks)
  • src/app/shared/services/import-validation/import-validation.service.ts (1 hunks)
  • src/app/shared/styles/application-list.css (1 hunks)
  • src/app/shared/styles/application-statuses.css (1 hunks)
  • src/app/shared/styles/buttons.css (1 hunks)
  • src/app/shared/styles/cabinet.css (1 hunks)
  • src/app/shared/styles/calendar.css (1 hunks)
  • src/app/shared/styles/card-wrapper.css (1 hunks)
  • src/app/shared/styles/cards.css (1 hunks)
  • src/app/shared/styles/config.css (1 hunks)
  • src/app/shared/styles/details.css (1 hunks)
  • src/app/shared/styles/details.scss (1 hunks)
  • src/app/shared/styles/dropdown.css (1 hunks)
  • src/app/shared/styles/full-width-card.css (1 hunks)
  • src/app/shared/styles/hide-aside-animation.css (1 hunks)
  • src/app/shared/styles/info-hint.css (1 hunks)
  • src/app/shared/styles/info.css (1 hunks)
  • src/app/shared/styles/list-wrappers.css (1 hunks)
  • src/app/shared/styles/navigation-tabs.css (1 hunks)
  • src/app/shared/styles/pagination.css (1 hunks)
  • src/app/shared/styles/search.css (1 hunks)
  • src/app/shared/styles/side-menu-cards.css (1 hunks)
  • src/app/shared/styles/status-themes.css (1 hunks)
  • src/app/shared/styles/tables.css (1 hunks)
  • src/app/shared/styles/toggle-button.css (1 hunks)
  • src/app/shared/styles/validation-form.css (1 hunks)
  • src/app/shared/styles/working-hours.css (1 hunks)
  • src/app/shared/styles/workshop-status.css (1 hunks)
  • src/app/shared/utils/admin.utils.ts (1 hunks)
  • src/app/shell/admin-tools/admin-tools.component.html (1 hunks)
  • src/app/shell/admin-tools/admin-tools.component.ts (2 hunks)
  • src/app/shell/details/details-tabs/provider-about/provider-about.component.css (1 hunks)
  • src/app/shell/details/details-tabs/reviews/reviews.component.css (1 hunks)
  • src/app/shell/details/details-tabs/reviews/stars/stars.component.css (1 hunks)
  • src/app/shell/details/details-tabs/workshop-about/workshop-about.component.css (1 hunks)
  • src/app/shell/details/details.component.css (1 hunks)
  • src/app/shell/details/provider-details/provider-details.component.html (1 hunks)
  • src/app/shell/details/workshop-details/workshop-details.component.html (1 hunks)
  • src/app/shell/info/info.component.css (1 hunks)
  • src/app/shell/personal-cabinet/parent/parent-applications/parent-applications.component.css (1 hunks)
⛔ Files not processed due to max files limit (25)
  • src/app/shell/personal-cabinet/personal-cabinet.component.html
  • src/app/shell/personal-cabinet/provider/create-position/create-position.component.html
  • src/app/shell/personal-cabinet/provider/create-position/position-form/create-position-form.component.html
  • src/app/shell/personal-cabinet/provider/create-position/position-form/create-position-form.component.scss
  • src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/working-hours-form-wrapper/working-hours-form/working-hours-form.component.html
  • src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/working-hours-form-wrapper/working-hours-form/working-hours-form.component.scss
  • src/app/shell/personal-cabinet/provider/provider-applications/provider-applications.component.css
  • src/app/shell/personal-cabinet/provider/provider-employees-upload/provider-employees-upload.component.html
  • src/app/shell/personal-cabinet/provider/provider-employees-upload/provider-employees-upload.component.scss
  • src/app/shell/personal-cabinet/provider/provider-employees-upload/provider-employees-upload.component.spec.ts
  • src/app/shell/personal-cabinet/provider/provider-employees-upload/provider-employees-upload.component.ts
  • src/app/shell/personal-cabinet/provider/provider-employees/provider-employees.component.html
  • src/app/shell/personal-cabinet/provider/provider-employees/provider-employees.component.scss
  • src/app/shell/personal-cabinet/provider/provider-employees/provider-employees.component.spec.ts
  • src/app/shell/personal-cabinet/provider/provider-employees/provider-employees.component.ts
  • src/app/shell/personal-cabinet/provider/provider-org-info/provider-org-info.component.css
  • src/app/shell/personal-cabinet/provider/provider-positions/provider-positions.component.html
  • src/app/shell/personal-cabinet/provider/provider-positions/provider-positions.component.scss
  • src/app/shell/personal-cabinet/provider/provider-routing.module.ts
  • src/app/shell/personal-cabinet/provider/provider.module.ts
  • src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/application-card.component.html
  • src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/child-info-box/child-info-box.component.css
  • src/app/shell/personal-cabinet/shared-cabinet/messages/chat/message/message.component.css
  • src/app/shell/shell.component.css
  • src/styles.scss
💤 Files with no reviewable changes (1)
  • src/app/shared/components/filters-list/searchbar/searchbar.component.scss
✅ Files skipped from review due to trivial changes (28)
  • src/app/header/header.component.scss
  • src/app/shared/components/filters-list/searchbar/searchbar.component.html
  • src/app/header/progress-bar/progress-bar.component.css
  • src/app/shell/personal-cabinet/parent/parent-applications/parent-applications.component.css
  • src/app/shared/styles/card-wrapper.css
  • src/app/shell/details/provider-details/provider-details.component.html
  • src/app/app.component.css
  • src/app/shared/components/filters-list/filters-list.component.css
  • src/app/shared/styles/details.scss
  • src/app/shell/details/details-tabs/provider-about/provider-about.component.css
  • src/app/shared/components/scroll-to-top/scroll-to-top.component.css
  • src/app/shared/components/filters-list/working-hours/working-hours.component.html
  • src/app/shared/components/sidenav-filters/sidenav-filters.component.css
  • src/app/shared/components/filters-list/user-radius-set/user-radius-set.component.css
  • src/app/shared/styles/search.css
  • src/app/shared/components/filters-list/category-check-box/category-check-box.component.css
  • src/app/shared/styles/working-hours.css
  • src/app/shared/styles/workshop-status.css
  • src/app/shell/details/workshop-details/workshop-details.component.html
  • src/app/shell/details/details-tabs/reviews/stars/stars.component.css
  • src/app/shell/details/details-tabs/reviews/reviews.component.css
  • src/app/shared/styles/cabinet.css
  • src/app/shared/components/notifications/notifications.component.css
  • src/app/shared/styles/info.css
  • src/app/shared/styles/application-statuses.css
  • src/app/shared/components/navigation-mobile-bar/navigation-mobile-bar.component.css
  • src/app/shared/styles/side-menu-cards.css
  • src/app/shared/components/navigation-bar/navigation-bar.component.css
🧰 Additional context used
🪛 Biome (1.9.4)
src/app/shared/components/message-bar/message-bar.component.css

[error] 45-45: Unexpected shorthand property place-content after justify-content

(lint/suspicious/noShorthandPropertyOverrides)

src/app/shared/styles/toggle-button.css

[error] 16-16: Unexpected shorthand property background after background-color

(lint/suspicious/noShorthandPropertyOverrides)

src/app/shared/services/import-validation/import-validation.service.ts

[error] 10-10: This constructor is unnecessary.

Unsafe fix: Remove the unnecessary constructor.

(lint/complexity/noUselessConstructor)

🪛 GitHub Check: build_and_test
src/app/shared/components/institution-hierarchy/institution-hierarchy.component.ts

[warning] 67-67:
Unexpected console statement

🔇 Additional comments (15)
src/app/shell/details/details.component.css (1)

6-9: LGTM!

The .workshop-page-wrap class implementation follows good practices for container styling.

src/app/shared/styles/calendar.css (1)

15-27: Review accessibility impact and remove !important declaration.

Two concerns in this section:

  1. Using order property might affect keyboard navigation and screen reader behavior
  2. Using !important for margin should be avoided
 ::ng-deep .mat-calendar-controls {
-  margin: 0 !important;
+  margin: 0;
   display: flex;
   justify-content: space-between;
 }

Run this script to check for potential accessibility issues with reordering:

✅ Verification successful

Remove unnecessary !important declaration

The accessibility concern about reordering can be safely dismissed as Angular Material's calendar component handles keyboard navigation appropriately. However, the !important declaration should still be removed as it's unnecessary when using ::ng-deep for style overrides.

 ::ng-deep .mat-calendar-controls {
-  margin: 0 !important;
+  margin: 0;
   display: flex;
   justify-content: space-between;
 }
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for keyboard navigation handlers that might be affected by the order change
rg -l "keydown|keyup|keypress" | xargs rg "mat-calendar-previous-button"

Length of output: 72


Script:

#!/bin/bash
# Search for calendar-related component files
fd -e ts -e html calendar

# Look for ARIA attributes and roles in calendar-related files
rg -i "aria-|role=" --type html --type typescript | grep -i "calendar"

# Search for calendar navigation implementation
ast-grep --pattern 'class $_ implements $_ {
  $$$
  keydown($$$) {
    $$$
  }
  $$$
}'

Length of output: 224


Script:

#!/bin/bash
# Search for Material calendar documentation references
rg -l "MatCalendar|mat-calendar"

# Search for calendar-related files with proper type specification
rg -t html -t js -t ts -i "aria-|role=" | grep -i "calendar"

Length of output: 166

src/app/shell/admin-tools/admin-tools.component.ts (1)

7-7: LGTM!

The changes follow the existing pattern in the component and are implemented correctly.

Also applies to: 21-21

src/app/shared/utils/admin.utils.ts (1)

45-47: LGTM! Clean implementation of role check.

The new canManageImports function follows the established pattern of role-checking functions and maintains good separation of concerns.

src/app/shared/constants/regex-constants.ts (1)

20-21: LGTM! Well-documented regex constant.

The RNOKPP_REGEX implementation correctly validates 10-digit numbers and follows the established pattern in the file.

src/app/shared/services/excel-upload-processor/excel-upload-processor.service.ts (2)

90-92: Replace window.alert with a more user-friendly notification system.

Using window.alert is not recommended for modern web applications. Consider using a modal or toast notification system.


28-28: Consider adding error handling for XLSX.read operation.

The XLSX.read operation could throw an error if the file is corrupted or in an invalid format. This should be handled explicitly.

-          const workBook: XLSX.WorkBook = XLSX.read(binaryString, { type: 'array', WTF: true, raw: true, cellFormula: false });
+          let workBook: XLSX.WorkBook;
+          try {
+            workBook = XLSX.read(binaryString, { type: 'array', WTF: true, raw: true, cellFormula: false });
+          } catch (error) {
+            this.showAlert(this.translate.instant('IMPORT/EXPORT.INVALID_FILE_FORMAT'));
+            observer.error(this.translate.instant('IMPORT/EXPORT.INVALID_FILE_FORMAT'));
+            return;
+          }

Also applies to: 32-32

src/app/shell/info/info.component.css (1)

1-9: LGTM! Good responsive design implementation.

The CSS implements responsive design correctly with:

  • Fluid width for images
  • Appropriate mobile breakpoint
  • Proper space optimization for mobile view
src/app/shared/components/stretch-cell/stretch-cell/stretch-cell.component.css (1)

17-17: Review asymmetric padding implementation.

The padding 0 0 0 10px creates an invisible click area that extends only to the left. This might cause unexpected behavior when users try to resize from the right side.

Consider using a symmetric padding or alternative approach:

-  padding: 0 0 0 10px;
+  padding: 0 5px;
src/app/shared/components/map/map.component.css (1)

28-28: Reconsider using viewport height for positioning.

Using calc(100vh - 250px) for positioning can lead to issues with mobile browsers due to dynamic viewport changes when the address bar shows/hides.

Consider using a container-relative positioning approach or CSS custom properties for better maintainability:

-    top: calc(100vh - 250px);
+    bottom: 250px;
✅ Verification successful

Confirmed: Viewport height usage should be replaced with bottom positioning

The viewport height calculation is used in a mobile-specific media query for the geolocation button's positioning. The suggested bottom: 250px approach is indeed more reliable for mobile browsers and achieves the same layout goal without potential viewport calculation issues.

@media (max-width: 750px) {
  .geolocation-button {
-    top: calc(100vh - 250px);
+    bottom: 250px;
  }
}
🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Get the complete CSS file content
cat src/app/shared/components/map/map.component.css

# Search for viewport-related calculations in the component
rg "vh" src/app/shared/components/map/map.component.css

# Look for mobile-specific media queries
rg "@media" src/app/shared/components/map/map.component.css

Length of output: 761

src/app/shared/components/info-menu/info-menu.component.css (1)

19-38: 🛠️ Refactor suggestion

Consider alternatives to ::ng-deep.

The ::ng-deep combinator is deprecated. Consider using Angular's ViewEncapsulation.None or creating a global style file for these overrides.

src/app/shared/styles/pagination.css (1)

41-42: Verify Material Icons font availability.

The code relies on the Material Icons font family. Please ensure it's properly imported in the project.

Also applies to: 49-50

✅ Verification successful

Material Icons font is properly imported

The font is correctly imported from Google Fonts CDN in src/index.html, ensuring its availability for the pagination arrows.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if Material Icons font is imported
rg -l "Material.+Icons" --type html --type css

Length of output: 136


Script:

#!/bin/bash
# Check the Material Icons font import in index.html
rg "Material.+Icons" -A 2 -B 2 src/index.html

Length of output: 518

src/app/footer/footer.component.css (1)

1-82: LGTM! Well-structured responsive design

The footer implementation follows good responsive design practices with appropriate breakpoints and flexbox usage.

src/app/shared/base-components/upload-excel/upload-excel.component.scss (1)

3-8: Use kebab-case for SCSS variables.

Convert camelCase variable names to kebab-case for consistency:

-$duplicatedValue: #fd0707b3;
-$emptyField: #fd07071a;
-$wrongLength: #0d138980;
-$wrongLanguage: #22b01280;
-$wrongFormat: #ffff0080;
+$duplicated-value: #fd0707b3;
+$empty-field: #fd07071a;
+$wrong-length: #0d138980;
+$wrong-language: #22b01280;
+$wrong-format: #ffff0080;
src/app/shared/components/sidenav-menu/sidenav-menu.component.css (1)

45-49: Review z-index stacking context.

The z-index value of 10001 seems arbitrarily high. Consider documenting the stacking context or using a lower value if possible.

✅ Verification successful

Z-index value is consistent with codebase patterns

The z-index of 10001 is justified as it matches the sidenav-filters component and follows the established layering strategy in the codebase where critical overlay UI components use values in the 10000+ range.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other z-index declarations to understand the stacking context
rg -n "z-index:" --type css

Length of output: 3991

@@ -65,6 +64,7 @@
} else {
this.setFieldsDescriptionSubscribe();
}
this.institutions$.subscribe((data) => console.log(data));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove the debug subscription.

This subscription has multiple issues:

  1. It creates a memory leak as it's not being unsubscribed
  2. It contains a console.log statement which shouldn't be in production code
  3. It's redundant as the data is already being managed by the NGXS store

Remove this line:

-    this.institutions$.subscribe((data) => console.log(data));

If you need to debug the institutions data, consider:

  1. Using the Redux DevTools to inspect the store state
  2. Adding proper logging service integration
  3. Using the async pipe in the template to bind to the institutions$ observable
🧰 Tools
🪛 GitHub Check: build_and_test

[warning] 67-67:
Unexpected console statement

Comment on lines +12 to +17
public uploadEmployeesList(items: unknown, id: string): Observable<HttpResponse<string>> {
return this.http.put(`${this.baseApiURL}/Provider/Upload/${id}/employees/upload`, items, {
observe: 'response',
responseType: 'text'
});
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider improving type safety and API endpoint structure.

  1. The items parameter should be strongly typed based on the Employee or ValidEmployee interface instead of unknown.
  2. The API endpoint structure /Provider/Upload/{id}/employees/upload seems redundant with two "upload" segments.

Consider these improvements:

- public uploadEmployeesList(items: unknown, id: string): Observable<HttpResponse<string>> {
-   return this.http.put(`${this.baseApiURL}/Provider/Upload/${id}/employees/upload`, items, {
+ public uploadEmployeesList(items: ValidEmployee[], id: string): Observable<HttpResponse<string>> {
+   return this.http.put(`${this.baseApiURL}/providers/${id}/employees`, items, {

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 21 to 41
::ng-deep .ngx-mat-tel-input-mat-menu-panel {
max-width: none !important;
width: auto !important;
min-width: 400px !important;
max-height: 500px;
overflow-x: hidden;
box-sizing: border-box;
border-radius: 5px !important;
}
::ng-deep .ngx-mat-tel-input-mat-menu-panel .mat-mdc-menu-content {
padding: 0;
}
::ng-deep .ngx-mat-tel-input-mat-menu-panel .country-search {
outline: none !important;
padding-left: 13px !important;
margin: 4px !important;
font-size: 13px !important;
font-family: "Open Sans", sans-serif !important;
width: calc(100% - 20px) !important;
box-sizing: border-box;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Reduce reliance on ::ng-deep and !important

The extensive use of ::ng-deep and !important makes the styles harder to maintain. Consider using Angular Material's theming system.

- ::ng-deep .ngx-mat-tel-input-mat-menu-panel {
+ :host {
+   --tel-input-menu-width: 400px;
+   --tel-input-menu-height: 500px;
+ }

+ .ngx-mat-tel-input-mat-menu-panel {
   max-width: none !important;
   width: auto !important;
-  min-width: 400px !important;
+  min-width: var(--tel-input-menu-width);
-  max-height: 500px;
+  max-height: var(--tel-input-menu-height);
   overflow-x: hidden;
   box-sizing: border-box;
   border-radius: 5px !important;
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 44 to 46
justify-content: center;
place-content: center;
align-items: center;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove redundant place-content property.

The place-content property is redundant when justify-content and align-items are already specified.

  box-sizing: border-box;
  justify-content: center;
- place-content: center;
  align-items: center;
📝 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.

Suggested change
justify-content: center;
place-content: center;
align-items: center;
justify-content: center;
align-items: center;
🧰 Tools
🪛 Biome (1.9.4)

[error] 45-45: Unexpected shorthand property place-content after justify-content

(lint/suspicious/noShorthandPropertyOverrides)

Comment on lines 112 to 115
:host ::ng-deep .mat-form-field-appearance-legacy .mat-form-field-wrapper {
padding: 0;
margin: 0;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove duplicate CSS rules.

These styles are already defined at lines 85-88.

-:host ::ng-deep .mat-form-field-appearance-legacy .mat-form-field-wrapper {
-  padding: 0;
-  margin: 0;
-}

Comment on lines 7 to 12
::ng-deep .lang-option .mat-select-value {
color: white !important;
overflow: visible;
padding-right: 0px !important;
font-family: "Innerspace";
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid using !important declarations.

The use of !important makes styles harder to maintain and override when necessary. Consider restructuring the selectors to achieve the desired specificity.

 ::ng-deep .lang-option .mat-select-value {
-  color: white !important;
+  color: white;
   overflow: visible;
-  padding-right: 0px !important;
+  padding-right: 0;
   font-family: "Innerspace";
 }

 ::ng-deep .lang-option-text .mat-option-text {
   width: 0;
   padding: 0;
   font-size: 0.7rem;
   font-weight: bold;
   font-family: "Open Sans", sans-serif;
   text-align: center;
-  background-color: white !important;
+  background-color: white;
 }

Also applies to: 14-22

@humanmantis
Copy link
Contributor

Why there are a lot of css and css.map files?
Remove them first

@ita-social-projects ita-social-projects deleted a comment from coderabbitai bot Jan 30, 2025
@humanmantis
Copy link
Contributor

@coderabbitai full review

Copy link

coderabbitai bot commented Jan 30, 2025

✅ Actions performed

Full review triggered.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

♻️ Duplicate comments (3)
src/app/shared/components/institution-hierarchy/institution-hierarchy.component.ts (1)

67-67: ⚠️ Potential issue

Remove the debug subscription.

This subscription has multiple issues:

  1. It creates a memory leak as it's not being unsubscribed
  2. It contains a console.log statement which shouldn't be in production code
  3. It's redundant as the data is already being managed by the NGXS store

Remove this line:

-    this.institutions$.subscribe((data) => console.log(data));

If you need to debug the institutions data, consider:

  1. Using the Redux DevTools to inspect the store state
  2. Adding proper logging service integration
  3. Using the async pipe in the template to bind to the institutions$ observable
🧰 Tools
🪛 GitHub Check: build_and_test

[warning] 67-67:
Unexpected console statement

src/app/shared/services/employee-upload-processor/employee-upload-processor.service.ts (1)

12-17: 🛠️ Refactor suggestion

Improve type safety and API endpoint structure.

  1. The items parameter should be strongly typed using the ValidEmployee interface.
  2. The API endpoint structure needs improvement to follow REST conventions.
- public uploadEmployeesList(items: unknown, id: string): Observable<HttpResponse<string>> {
-   return this.http.put(`${this.baseApiURL}/Provider/Upload/${id}/employees/upload`, items, {
+ public uploadEmployeesList(items: ValidEmployee[], id: string): Observable<HttpResponse<string>> {
+   return this.http.put(`${this.baseApiURL}/providers/${id}/employees`, items, {
src/app/shell/personal-cabinet/provider/provider-employees-upload/provider-employees-upload.component.html (1)

77-77: 🛠️ Refactor suggestion

Move inline style to CSS class.

Replace the inline style with a CSS class for better maintainability.

-            <td mat-cell style="min-width: 30px" *matCellDef="let element">{{ dataSource.indexOf(element) + 1 }}</td>
+            <td mat-cell class="sequence-number-cell" *matCellDef="let element">{{ dataSource.indexOf(element) + 1 }}</td>
🧹 Nitpick comments (25)
src/app/shell/personal-cabinet/provider/create-position/position-form/create-position-form.component.scss (1)

4-6: LGTM! Consider enhancing class naming for better maintainability.

The syntax fix is correct. However, consider making the class name more specific to its context (e.g., .position-form-textarea or .create-position-textarea) to improve maintainability and avoid potential naming conflicts in the multi-step form process.

-.step-textarea {
+.create-position-textarea {
   margin-top: 0;
 }
src/app/shell/admin-tools/admin-tools.component.html (1)

28-30: Consider reorganizing navigation menu items.

The import-export functionality might be better placed near related items like "PROVIDERS" or "USERS" instead of at the end of the navigation menu. This would create a more intuitive grouping of related features.

src/app/shared/services/import-validation/import-validation.service.ts (1)

10-10: Remove the unnecessary constructor.

This constructor is empty and provides no additional functionality. Consider removing it based on the static analysis suggestion.

-  constructor() {}
🧰 Tools
🪛 Biome (1.9.4)

[error] 10-10: This constructor is unnecessary.

Unsafe fix: Remove the unnecessary constructor.

(lint/complexity/noUselessConstructor)

src/app/shared/services/excel-upload-processor/excel-upload-processor.service.ts (1)

68-71: Enhance clarity in header validation.

Your header checks work well, but consider providing users with more specific messages or instructions in their preferred language (via translation) on how to fix unexpected headers or missing columns.

src/app/shared/base-components/upload-excel/upload-excel.component.ts (1)

94-99: Reset values before enabling loading.

A minor suggestion: reset everything first, then set isLoading = true to keep state changes more predictable.

public onFileSelected(event: Event): void {
  const target = event.target as HTMLInputElement;
  this.selectedFile = target.files[0];
-  this.isLoading = true;
  this.resetValues();
+  this.isLoading = true;
  this.excelService.convertExcelToJSON(...
src/app/shared/models/admin-import-export.model.ts (2)

1-8: Reduce code duplication in employee interfaces.

Consider creating a base interface for common employee fields to avoid duplication between ValidEmployee and Employee interfaces.

+ export interface BaseEmployee {
+   employeeSurname: string;
+   employeeName: string;
+   employeeFatherName: string;
+   employeeRNOKPP: number;
+   employeeAssignedRole: string;
+ }
+
- export interface ValidEmployee {
+ export interface ValidEmployee extends BaseEmployee {
   id: number;
-   employeeSurname: string;
-   employeeName: string;
-   employeeFatherName: string;
-   employeeRNOKPP: number;
-   employeeAssignedRole: string;
}

- export interface Employee {
+ export interface Employee extends BaseEmployee {
   sequenceNumber: number;
-   employeeSurname: string;
-   employeeName: string;
-   employeeFatherName: string;
-   employeeRNOKPP: number;
-   employeeAssignedRole: string;
   errors: EmployeeValidationErrors;
}

Also applies to: 9-17


39-46: Improve type safety in validation configuration.

Consider using string literals or enums for validation types to make the configuration more type-safe.

+ export type ValidationCheckType = 'empty' | 'length' | 'language' | 'assignedRole' | 'RNOKPP' | 'duplicate';
+
- export interface FieldValidationConfig {
+ export interface FieldValidationConfig extends Record<`check${Capitalize<ValidationCheckType>}`, boolean> {
-   checkEmpty?: boolean;
-   checkLength?: boolean;
-   checkLanguage?: boolean;
-   checkAssignedRole?: boolean;
-   checkRNOKPP?: boolean;
-   checkDuplicate?: boolean;
}
src/app/shared/constants/regex-constants.ts (1)

20-21: Optimize the regex pattern by removing unnecessary capturing group.

The pattern can be simplified since the capturing group isn't being used.

-export const RNOKPP_REGEX: RegExp = /^(\d{10})$/;
+export const RNOKPP_REGEX: RegExp = /^\d{10}$/;
src/app/shell/personal-cabinet/provider/provider-employees-upload/provider-employees-upload.component.spec.ts (1)

65-88: Add edge cases to renamingKeys test.

The test only covers the happy path. Consider adding tests for:

  • Empty array
  • Missing fields
  • Invalid RNOKPP format
  it('should handle empty array in renamingKeys', () => {
    const result = component.renamingKeys([]);
    expect(result).toEqual([]);
  });

  it('should handle missing fields in renamingKeys', () => {
    const inputItems = [{
      employeeAssignedRole: 'Employee',
      employeeName: 'FirstName'
    }];
    expect(() => component.renamingKeys(inputItems)).toThrow();
  });
src/app/shared/services/excel-upload-processor/excel-upload-processor.service.spec.ts (2)

124-124: Consider using translation keys for error messages

The error messages are currently hardcoded in Ukrainian. Consider using translation keys for better internationalization support.

-        expect(error).toEqual('Заголовки не відповідають очікуваним');
+        expect(error).toEqual(translateService.instant('IMPORT.HEADERS_INVALID'));

-        expect(error).toEqual('Помилка при читанні файлу');
+        expect(error).toEqual(translateService.instant('IMPORT.FILE_READ_ERROR'));

Also applies to: 147-147


67-98: Consider adding more test cases for Excel data validation

The current test only verifies basic Excel file processing. Consider adding test cases for:

  • Empty Excel files
  • Files with missing required columns
  • Files with invalid data formats
  • Maximum file size validation
src/app/shell/personal-cabinet/provider/provider-employees/provider-employees.component.scss (1)

4-7: Consider alternative to ::ng-deep and !important

The use of ::ng-deep and !important flags might cause maintenance issues:

  • ::ng-deep is deprecated and may be removed in future Angular versions
  • !important makes styles harder to override and maintain

Consider using CSS custom properties or component-specific selectors:

-::ng-deep .mat-mdc-tab-group.mat-mdc-tab-group-stretch-tabs > .mat-mdc-tab-header .mat-mdc-tab {
-  flex-grow: 0 !important;
-  margin-left: 10px;
-}
+:host {
+  --tab-flex-grow: 0;
+  --tab-margin-left: 10px;
+}
+
+.provider-employees-tabs .mat-mdc-tab {
+  flex-grow: var(--tab-flex-grow);
+  margin-left: var(--tab-margin-left);
+}
src/app/shell/personal-cabinet/provider/provider-employees/provider-employees.component.html (1)

2-7: Ensure consistent styling across tabs.

The first tab has a class="mat-tab-width" while the second tab doesn't. Either apply the class consistently or remove if unnecessary.

src/app/shared/enum/enumUA/import-export.ts (1)

1-8: Add JSDoc documentation for the enum.

Please add documentation describing the purpose and usage of ImportEmployeesColumnsNames.

+/**
+ * Column names for employee import Excel file.
+ * Maps to the expected column structure in the upload template.
+ */
 export enum ImportEmployeesColumnsNames {
src/app/shell/personal-cabinet/provider/create-position/create-position.component.html (1)

Line range hint 27-33: Consider disabling button during form submission.

The submit button's disabled state doesn't account for the actual submission state, only the loading state. This could lead to double submissions.

           <button
             class="btn"
-            [disabled]="(isLoading$ | async) || !PositionFormGroup.dirty || PositionFormGroup.invalid"
+            [disabled]="(isLoading$ | async) || !PositionFormGroup.dirty || PositionFormGroup.invalid || isSubmitting"
             mat-button
             type="submit"
             (click)="onSubmit()">
src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/working-hours-form-wrapper/working-hours-form/working-hours-form.component.scss (1)

102-105: Make button selectors more specific.

The current button selectors are too generic and could affect other buttons in the component unintentionally.

Consider making the selectors more specific:

-button:disabled,
-button {
+.working-hours-form-button:disabled,
+.working-hours-form-button {
   border: none;
   background-color: transparent;
}
src/app/shared/services/employee-upload-processor/employee-upload-processor.service.spec.ts (2)

23-37: Add request headers validation and edge cases.

The upload test should verify request headers and handle edge cases.

Add validation for Content-Type header and test with an empty list:

 it('should call HttpClient.put with correct parameters and return response', () => {
   const mockResponse = 'Upload successful';
   const mockItems = [{ name: 'Employee1' }];
   const mockId = '123';
   service.uploadEmployeesList(mockItems, mockId).subscribe((response) => {
     expect(response.body).toBe(mockResponse);
     expect(response.status).toBe(200);
   });
   const req = httpTestingController.expectOne('/api/v1/Provider/Upload/123/employees/upload');
   expect(req.request.method).toBe('PUT');
   expect(req.request.body).toEqual(mockItems);
   expect(req.request.responseType).toBe('text');
+  expect(req.request.headers.get('Content-Type')).toBe('application/json');
   req.flush(mockResponse, { status: 200, statusText: 'OK' });
   httpTestingController.verify();
 });

+it('should handle empty list', () => {
+  const mockItems = [];
+  const mockId = '123';
+  service.uploadEmployeesList(mockItems, mockId).subscribe((response) => {
+    expect(response.status).toBe(200);
+  });
+  const req = httpTestingController.expectOne('/api/v1/Provider/Upload/123/employees/upload');
+  expect(req.request.body).toEqual([]);
+  req.flush('Success', { status: 200, statusText: 'OK' });
+});

39-51: Enhance error handling test coverage.

The error test could be more comprehensive by testing different error scenarios.

Add tests for different HTTP error codes:

 it('should handle error response from HttpClient', () => {
   const mockItems = [{ name: 'Employee1' }];
   const mockId = '123';
+  const errorScenarios = [
+    { status: 400, message: 'Bad Request' },
+    { status: 401, message: 'Unauthorized' },
+    { status: 500, message: 'Internal Server Error' }
+  ];
+
+  errorScenarios.forEach(scenario => {
     service.uploadEmployeesList(mockItems, mockId).subscribe(
       () => {},
       (error) => {
-        expect(error).toEqual(new HttpResponse({ body: 'Error uploading employees', status: 500 }));
+        expect(error).toEqual(new HttpResponse({ 
+          body: scenario.message,
+          status: scenario.status 
+        }));
       }
     );
     const req = httpTestingController.expectOne('/api/v1/Provider/Upload/123/employees/upload');
-    req.flush('Error uploading employees', { status: 500, statusText: 'Internal Server Error' });
+    req.flush(scenario.message, { 
+      status: scenario.status,
+      statusText: scenario.message 
+    });
     httpTestingController.verify();
+  });
 });
src/app/shell/personal-cabinet/personal-cabinet.component.html (1)

39-43: Consider consolidating role checks.

The role check condition is duplicated with the administration section above.

Consider consolidating the role checks:

-    <ng-container *ngIf="userRole === Role.provider || userRole === Role.providerDeputy">
-      <a mat-tab-link [routerLinkActive]="['active']" [routerLink]="'./provider/administration'">
-        {{ 'ENUM.NAV_BAR_NAME.ADMINISTRATION' | translate | uppercase }}
-      </a>
-    </ng-container>
-    <ng-container *ngIf="userRole === Role.provider || userRole === Role.providerDeputy">
-      <a mat-tab-link [routerLinkActive]="['active']" [routerLink]="'./provider/provider-employees'">
-        {{ 'ENUM.NAV_BAR_NAME.EMPLOYEES' | translate | uppercase }}
-      </a>
-    </ng-container>
+    <ng-container *ngIf="userRole === Role.provider || userRole === Role.providerDeputy">
+      <a mat-tab-link [routerLinkActive]="['active']" [routerLink]="'./provider/administration'">
+        {{ 'ENUM.NAV_BAR_NAME.ADMINISTRATION' | translate | uppercase }}
+      </a>
+      <a mat-tab-link [routerLinkActive]="['active']" [routerLink]="'./provider/provider-employees'">
+        {{ 'ENUM.NAV_BAR_NAME.EMPLOYEES' | translate | uppercase }}
+      </a>
+    </ng-container>
src/app/shared/services/import-validation/import-validation.service.spec.ts (1)

16-35: Consider adding more test cases for field validation.

While the current test covers basic validation, consider adding tests for:

  • Multiple items with errors
  • Empty role with checkEmpty true
  • Valid role scenarios
src/app/shared/base-components/upload-excel/upload-excel.component.scss (2)

162-198: Optimize animation performance.

The animations could be more performant by using transform and opacity instead of color and box-shadow.

 @keyframes loader {
   0% {
-    box-shadow:
-      10px 0 #3849f9,
-      -10px 0 #3849f933;
-    background: #3849f9;
+    transform: translateX(0);
+    opacity: 1;
   }
   50% {
-    box-shadow:
-      10px 0 #3849f9,
-      -10px 0 #3849f933;
-    background: #3849f933;
+    transform: translateX(10px);
+    opacity: 0.2;
   }
   100% {
-    box-shadow:
-      10px 0 #3849f933,
-      -10px 0 #3849f9;
-    background: #3849f9;
+    transform: translateX(-10px);
+    opacity: 1;
   }
 }

157-160: Extract magic numbers to variables.

Consider extracting pixel values to semantic variables for better maintainability.

+$loader-size: 8px;
+$loader-margin: 15px;
+
 .loader {
   align-self: baseline;
-  margin: 15px;
-  width: 8px;
+  margin: $loader-margin;
+  width: $loader-size;
   aspect-ratio: 1;
   border-radius: 50%;
 }
src/app/shared/base-components/upload-excel/upload-excel.component.spec.ts (3)

44-44: Remove isLoading initialization from setup.

Setting isLoading=true in setup might affect test cases expecting the default state. This should be set only in specific test cases where loading state is relevant.

-    component.isLoading = true;

145-145: Remove empty test suite.

The initializeLoadingIndicatorObserver test suite is empty. Either implement the tests or remove the empty describe block.

-  describe('initializeLoadingIndicatorObserver method', () => {});

108-144: Add missing edge cases for file validation.

The file selection tests should include additional edge cases:

  • Invalid file types
  • Empty files
  • Files exceeding size limits

Example test case to add:

it('should handle invalid file type', () => {
  const mockFile = new File(['dummy content'], 'test.txt', {
    type: 'text/plain'
  });
  const event = { target: { files: [mockFile] } } as unknown as Event;
  component.onFileSelected(event);
  expect(component.isLoading).toBe(false);
  expect(window.alert).toHaveBeenCalledWith('Please select a valid Excel file');
});
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a7d77c2 and bcba375.

⛔ Files ignored due to path filters (4)
  • package.json is excluded by none and included by none
  • src/assets/i18n/en.json is excluded by !src/assets/** and included by src/**
  • src/assets/i18n/uk.json is excluded by !src/assets/** and included by src/**
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock and included by none
📒 Files selected for processing (45)
  • src/app/header/header.component.html (1 hunks)
  • src/app/header/header.component.scss (1 hunks)
  • src/app/shared/base-components/upload-excel/upload-excel.component.scss (1 hunks)
  • src/app/shared/base-components/upload-excel/upload-excel.component.spec.ts (1 hunks)
  • src/app/shared/base-components/upload-excel/upload-excel.component.ts (1 hunks)
  • src/app/shared/components/filters-list/searchbar/searchbar.component.html (1 hunks)
  • src/app/shared/components/filters-list/searchbar/searchbar.component.scss (0 hunks)
  • src/app/shared/components/filters-list/working-hours/working-hours.component.html (2 hunks)
  • src/app/shared/components/institution-hierarchy/institution-hierarchy.component.html (1 hunks)
  • src/app/shared/components/institution-hierarchy/institution-hierarchy.component.ts (1 hunks)
  • src/app/shared/constants/regex-constants.ts (1 hunks)
  • src/app/shared/enum/enumUA/import-export.ts (1 hunks)
  • src/app/shared/models/admin-import-export.model.ts (1 hunks)
  • src/app/shared/services/employee-upload-processor/employee-upload-processor.service.spec.ts (1 hunks)
  • src/app/shared/services/employee-upload-processor/employee-upload-processor.service.ts (1 hunks)
  • src/app/shared/services/excel-upload-processor/excel-upload-processor.service.spec.ts (1 hunks)
  • src/app/shared/services/excel-upload-processor/excel-upload-processor.service.ts (1 hunks)
  • src/app/shared/services/import-validation/import-validation.service.spec.ts (1 hunks)
  • src/app/shared/services/import-validation/import-validation.service.ts (1 hunks)
  • src/app/shared/styles/details.scss (1 hunks)
  • src/app/shared/utils/admin.utils.ts (1 hunks)
  • src/app/shell/admin-tools/admin-tools.component.html (1 hunks)
  • src/app/shell/admin-tools/admin-tools.component.ts (2 hunks)
  • src/app/shell/details/provider-details/provider-details.component.html (1 hunks)
  • src/app/shell/details/workshop-details/workshop-details.component.html (1 hunks)
  • src/app/shell/personal-cabinet/personal-cabinet.component.html (1 hunks)
  • src/app/shell/personal-cabinet/provider/create-position/create-position.component.html (1 hunks)
  • src/app/shell/personal-cabinet/provider/create-position/position-form/create-position-form.component.html (1 hunks)
  • src/app/shell/personal-cabinet/provider/create-position/position-form/create-position-form.component.scss (1 hunks)
  • src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/working-hours-form-wrapper/working-hours-form/working-hours-form.component.html (1 hunks)
  • src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/working-hours-form-wrapper/working-hours-form/working-hours-form.component.scss (1 hunks)
  • src/app/shell/personal-cabinet/provider/provider-employees-upload/provider-employees-upload.component.html (1 hunks)
  • src/app/shell/personal-cabinet/provider/provider-employees-upload/provider-employees-upload.component.scss (1 hunks)
  • src/app/shell/personal-cabinet/provider/provider-employees-upload/provider-employees-upload.component.spec.ts (1 hunks)
  • src/app/shell/personal-cabinet/provider/provider-employees-upload/provider-employees-upload.component.ts (1 hunks)
  • src/app/shell/personal-cabinet/provider/provider-employees/provider-employees.component.html (1 hunks)
  • src/app/shell/personal-cabinet/provider/provider-employees/provider-employees.component.scss (1 hunks)
  • src/app/shell/personal-cabinet/provider/provider-employees/provider-employees.component.spec.ts (1 hunks)
  • src/app/shell/personal-cabinet/provider/provider-employees/provider-employees.component.ts (1 hunks)
  • src/app/shell/personal-cabinet/provider/provider-positions/provider-positions.component.html (1 hunks)
  • src/app/shell/personal-cabinet/provider/provider-positions/provider-positions.component.scss (1 hunks)
  • src/app/shell/personal-cabinet/provider/provider-routing.module.ts (1 hunks)
  • src/app/shell/personal-cabinet/provider/provider.module.ts (2 hunks)
  • src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/application-card.component.html (1 hunks)
  • src/styles.scss (0 hunks)
💤 Files with no reviewable changes (2)
  • src/app/shared/components/filters-list/searchbar/searchbar.component.scss
  • src/styles.scss
🧰 Additional context used
🪛 GitHub Check: build_and_test
src/app/shared/components/institution-hierarchy/institution-hierarchy.component.ts

[warning] 67-67:
Unexpected console statement

🪛 Biome (1.9.4)
src/app/shell/personal-cabinet/provider/provider-employees/provider-employees.component.spec.ts

[error] 16-22: Disallow duplicate setup and teardown hooks.

Disallow beforeEach duplicacy inside the describe function.

(lint/suspicious/noDuplicateTestHooks)

src/app/shared/services/import-validation/import-validation.service.ts

[error] 10-10: This constructor is unnecessary.

Unsafe fix: Remove the unnecessary constructor.

(lint/complexity/noUselessConstructor)

🔇 Additional comments (40)
src/app/shell/personal-cabinet/provider/provider-positions/provider-positions.component.html (1)

11-11: LGTM!

The minor formatting change to add a space before the closing tag is valid and doesn't affect functionality.

src/app/shared/components/institution-hierarchy/institution-hierarchy.component.html (2)

15-15: LGTM!

The closing tag formatting change looks good.


Line range hint 18-31: Verify the removal of conditional rendering.

The removal of *ngIf="hierarchy.shouldDisplay" means hierarchy levels will always be displayed. Please verify if this is the intended behavior, as it might show empty or irrelevant hierarchy levels that were previously hidden.

Let's check the component's TypeScript file to understand the previous logic:

✅ Verification successful

Removal of conditional rendering is correct and intentional

The visibility of hierarchy levels is properly managed through the hierarchyArray manipulation:

  • Elements are only added to the array when they should be displayed
  • Subsequent levels are automatically removed (sliced) when a parent selection changes
  • The shouldDisplay property was redundant as the array structure itself handles the visibility logic
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find and analyze the component's TypeScript file to understand the hierarchy display logic

# Search for the component file
echo "Searching for the component file..."
rg -l "institution-hierarchy.component.ts"

# If found, look for shouldDisplay usage
echo "Analyzing shouldDisplay usage..."
rg -A 5 "shouldDisplay" --type ts

# Look for related test cases
echo "Looking for related test cases..."
rg -l "institution-hierarchy.component.spec.ts"

Length of output: 2057


Script:

#!/bin/bash
# Check recent changes and hierarchy management logic

# Look for hierarchy level management in the component
echo "Analyzing hierarchy level management..."
rg -A 10 "hierarchyArray" --type ts

# Find related hierarchy management code
echo "Looking for related hierarchy management..."
rg -A 5 "GetInstitutionHierarchyChildrenById" --type ts

# Check for any state management related to hierarchy
echo "Checking state management..."
rg -A 5 "InstitutionHierarchyState" --type ts

Length of output: 19747

src/app/shell/admin-tools/admin-tools.component.ts (1)

7-7: LGTM! Clean implementation following existing patterns.

The addition of canManageImports follows the established pattern of other admin utility functions, maintaining consistency in the codebase.

Also applies to: 21-21

src/app/shared/utils/admin.utils.ts (1)

45-47: Consider expanding role access for import functionality.

The function currently restricts import functionality to techAdmin only. Given that the PR's objective is to create an employee upload interface, consider whether other admin roles (like ministryAdmin or regionAdmin) should also have access to this functionality, similar to how canManageInstitution allows multiple roles.

Let's verify the requirements and current role permissions:

✅ Verification successful

Restricting employee import to techAdmin role is appropriate.

The current implementation aligns with the platform's role-based access patterns where system-wide data import operations are restricted to techAdmin role. While other admin roles (ministryAdmin, regionAdmin) can manage institutions within their scope, sensitive operations like data imports that could affect system integrity are consistently restricted to techAdmin.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for role-based access patterns in the codebase
echo "Searching for role permission patterns..."
rg "role === Role\." -B 2 -A 2

# Search for employee-related functionality to understand access patterns
echo "Searching for employee-related functionality..."
rg -l "employee" "src"

Length of output: 30673

src/app/shell/admin-tools/admin-tools.component.html (1)

28-30: Add missing translation key for import-export navigation.

The translation key 'ENUM.NAV_BAR_NAME.IMPORT-EXPORT_PROVIDERS' needs to be added to the translation files:

  • Add to src/assets/i18n/en.json under the NAV_BAR_NAME section
  • Add to src/assets/i18n/uk.json under the NAV_BAR_NAME section
src/app/shared/services/import-validation/import-validation.service.ts (3)

13-13: Double-check usage of findDuplicates return value.

You're calling this.findDuplicates(items, item); but not using its returned boolean. If it’s meant to drive further logic (e.g., error handling or UI feedback), ensure you capture and handle the return value.


21-25: Inline the filter result to simplify.

As previously suggested, you can return the filtered array's length comparison directly, removing the extra variable.

public findDuplicates(items: any[], item: unknown): boolean {
-  const result = rnokppList.filter((e) => e === item);
-  return result.length > 1;
+  return rnokppList.filter((e) => e === item).length > 1;
}

30-30: Elevate magic numbers to constants.

As mentioned in a past comment, the values 2 and 50 should be replaced with named constants to improve readability and maintainability.

- } else if (config.checkLength && (item[fieldName].length <= 2 || item[fieldName].length > 50)) {
+ const MIN_FIELD_LENGTH = 2;
+ const MAX_FIELD_LENGTH = 50;
+ } else if (config.checkLength && (item[fieldName].length <= MIN_FIELD_LENGTH || item[fieldName].length > MAX_FIELD_LENGTH)) {
src/app/shared/services/excel-upload-processor/excel-upload-processor.service.ts (2)

19-22: Localize error messages.

You’re mixing in local language (“Помилка при читанні файлу”) while already using the TranslateService for some messages. Externalize and localize all user-facing strings for consistency.

- observer.error('Помилка при читанні файлу');
+ const localizedError = this.translate.instant('IMPORT/EXPORT.FILE_READ_ERROR');
+ observer.error(localizedError);

37-38: Localize the unexpected headers error.

Similarly, convert “Заголовки не відповідають очікуваним” to a translated string.

- observer.error('Заголовки не відповідають очікуваним');
+ observer.error(this.translate.instant('IMPORT/EXPORT.INVALID_HEADERS'));
src/app/shared/base-components/upload-excel/upload-excel.component.ts (2)

39-45: Unsubscribe from the store subscription to prevent leaks.

As noted previously, store subscriptions should be cleaned up. If you want to keep it alive for the entire component lifecycle, that’s acceptable, but in many cases, unsubscribing on OnDestroy is safer.

public getCurrentUserId(): void {
-  this.store
+  const sub = this.store
   .select((state) => state.registration?.provider?.id)
   .subscribe((id) => {
     this.currentUserId = id;
   });
+  this.subscription.add(sub);
}

119-122: Avoid mutating the original array with splice.

Using splice directly modifies the original array. Consider an alternative to preserve immutability.

- const cutItems = items.splice(100, items.length);
- return Boolean(cutItems.length);
+ return items.length > 100;
src/app/shell/personal-cabinet/provider/provider-employees/provider-employees.component.spec.ts (1)

22-24: Enhance test coverage.

The current test suite only verifies component creation. Consider adding tests for:

  • Component initialization
  • Tab navigation
  • Employee upload functionality
src/app/shell/personal-cabinet/provider/provider-routing.module.ts (1)

17-17: Consider adding NotEmployeeGuard to provider-employees route.

The 'administration' route uses NotEmployeeGuard for access control. Consider if similar protection is needed for the provider-employees route.

- { path: 'provider-employees', component: ProviderEmployeesComponent },
+ { path: 'provider-employees', component: ProviderEmployeesComponent, canActivate: [NotEmployeeGuard] },
src/app/shell/personal-cabinet/provider/provider-employees-upload/provider-employees-upload.component.ts (1)

1-16: LGTM! Component setup and inheritance look good.

The component correctly extends UploadExcelComponent with proper type parameter and implements necessary lifecycle hooks.

src/app/shell/personal-cabinet/provider/provider.module.ts (1)

58-63: LGTM! Components are correctly declared in the module.

The new components are properly added to the module's declarations array.

src/app/shared/services/excel-upload-processor/excel-upload-processor.service.spec.ts (1)

1-153: Well-structured test suite with good coverage!

The test suite is comprehensive and covers essential scenarios including:

  • Service initialization
  • Loading state management
  • Header validation
  • File processing
  • Error handling
src/app/shell/personal-cabinet/provider/provider-employees-upload/provider-employees-upload.component.scss (1)

1-1: LGTM! Good reuse of base component styles

Following the DRY principle by extending styles from the base upload-excel component.

src/app/shell/personal-cabinet/provider/provider-employees/provider-employees.component.html (1)

2-4: First tab appears incomplete.

The first tab contains only placeholder text. Please implement the intended component or remove the tab if it's not ready for this PR.

src/app/shared/enum/enumUA/import-export.ts (1)

19-22: Verify role consistency.

Please ensure these roles align with existing role definitions in the system.

✅ Verification successful

Import/Export roles are properly isolated and consistent

The ImportEmployeesChosenRole enum is correctly defined for its specific import/export context and doesn't conflict with other role definitions in the system. The Ukrainian labels are appropriately used only in this context.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other role definitions to verify consistency
rg -t typescript "enum.*Role" --glob "!import-export.ts"

Length of output: 93


Script:

#!/bin/bash
# Search for role-related enums and types
rg -t ts "enum.*Role|type.*Role|interface.*Role" -A 5

# Search for specific role values in Ukrainian
rg -t ts "Співробітник ЗО|Заступник директора ЗО"

Length of output: 4501

src/app/shell/details/provider-details/provider-details.component.html (1)

3-3: LGTM!

The self-closing img tag improves HTML syntax consistency.

src/app/shared/components/filters-list/searchbar/searchbar.component.html (1)

17-17: LGTM!

The formatting change improves code readability while maintaining functionality.

src/app/shell/personal-cabinet/provider/create-position/create-position.component.html (1)

2-2: LGTM!

The condition simplification maintains the same logic while improving readability.

src/app/shell/personal-cabinet/provider/provider-positions/provider-positions.component.scss (1)

93-93: LGTM!

The addition of a newline at the end of file follows good practices.

src/app/shared/styles/details.scss (1)

149-153: LGTM!

The media query formatting changes follow best practices and improve readability.

src/app/shell/personal-cabinet/personal-cabinet.component.html (1)

41-41: Verify translation key existence.

Ensure that the translation key 'ENUM.NAV_BAR_NAME.EMPLOYEES' exists in the translation files.

src/app/shared/components/filters-list/working-hours/working-hours.component.html (1)

35-37: LGTM!

The formatting changes improve code readability while maintaining the same functionality.

Also applies to: 51-53

src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/working-hours-form-wrapper/working-hours-form/working-hours-form.component.html (1)

27-42: LGTM!

The restructuring of time input sections and timepicker bindings improves code organization while maintaining proper form controls and validation.

Also applies to: 47-64, 74-75, 82-82

src/app/header/header.component.scss (1)

207-207: LGTM!

The formatting change follows CSS style conventions.

src/app/shared/services/import-validation/import-validation.service.spec.ts (4)

1-15: LGTM!

The service creation and setup is properly tested.


37-77: LGTM!

The length and language validation tests are comprehensive.


79-99: Good practice: Documenting unimplemented features in tests.

The test clearly documents that RNOKPP validation is not yet implemented while ensuring the code handles this gracefully.


100-113: LGTM!

The role validation test covers the invalid case properly.

src/app/shared/base-components/upload-excel/upload-excel.component.scss (1)

3-8: Use kebab-case for SCSS variables.

Following the team's conventions mentioned in past reviews:

-$duplicated-value: #fd0707b3;
-$empty-field: #fd07071a;
-$wrong-length: #0d138980;
-$wrong-language: #22b01280;
-$wrong-format: #ffff0080;
+$duplicated-value-color: #fd0707b3;
+$empty-field-color: #fd07071a;
+$wrong-length-color: #0d138980;
+$wrong-language-color: #22b01280;
+$wrong-format-color: #ffff0080;
src/app/shell/details/workshop-details/workshop-details.component.html (1)

4-4: LGTM!

The change properly uses a self-closing tag for the img element, which aligns with HTML best practices.

src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/application-card.component.html (1)

90-94: LGTM!

The changes improve code readability by properly formatting the button element and its attributes.

src/app/shell/personal-cabinet/provider/create-position/position-form/create-position-form.component.html (1)

50-52: LGTM!

The changes improve code readability by properly formatting the label element and its attributes.

src/app/header/header.component.html (1)

148-154: LGTM!

The new menu item is properly implemented with:

  • Correct role-based access control for provider and providerDeputy roles
  • Consistent routing pattern
  • Translation support
  • Follows the existing menu item structure
src/app/shell/personal-cabinet/provider/provider-employees-upload/provider-employees-upload.component.html (1)

1-161: LGTM for the overall implementation!

The employee upload component is well-structured with:

  • Clear file upload workflow
  • Comprehensive error handling and display
  • Proper data validation visualization
  • Accessible tooltips for error information

Comment on lines +69 to +77
public renamingKeys(items: any[]): any[] {
return items.map((item) => ({
assignedRole: (item as any).employeeAssignedRole,
middleName: (item as any).employeeFatherName,
firstName: (item as any).employeeName,
rnokpp: (item as any).employeeRNOKPP.toString(),
lastName: (item as any).employeeSurname
}));
}
Copy link

@coderabbitai coderabbitai bot Jan 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add type safety to the renamingKeys method.

The method uses any type which bypasses TypeScript's type checking. Consider using a proper interface for the input and output types.

-  public renamingKeys(items: any[]): any[] {
+  interface InputEmployee {
+    employeeAssignedRole: string;
+    employeeFatherName: string;
+    employeeName: string;
+    employeeRNOKPP: number;
+    employeeSurname: string;
+  }
+
+  interface OutputEmployee {
+    assignedRole: string;
+    middleName: string;
+    firstName: string;
+    rnokpp: string;
+    lastName: string;
+  }
+
+  public renamingKeys(items: InputEmployee[]): OutputEmployee[] {
     return items.map((item) => ({
-      assignedRole: (item as any).employeeAssignedRole,
-      middleName: (item as any).employeeFatherName,
-      firstName: (item as any).employeeName,
-      rnokpp: (item as any).employeeRNOKPP.toString(),
-      lastName: (item as any).employeeSurname
+      assignedRole: item.employeeAssignedRole,
+      middleName: item.employeeFatherName,
+      firstName: item.employeeName,
+      rnokpp: item.employeeRNOKPP.toString(),
+      lastName: item.employeeSurname
     }));
   }
📝 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.

Suggested change
public renamingKeys(items: any[]): any[] {
return items.map((item) => ({
assignedRole: (item as any).employeeAssignedRole,
middleName: (item as any).employeeFatherName,
firstName: (item as any).employeeName,
rnokpp: (item as any).employeeRNOKPP.toString(),
lastName: (item as any).employeeSurname
}));
}
interface InputEmployee {
employeeAssignedRole: string;
employeeFatherName: string;
employeeName: string;
employeeRNOKPP: number;
employeeSurname: string;
}
interface OutputEmployee {
assignedRole: string;
middleName: string;
firstName: string;
rnokpp: string;
lastName: string;
}
public renamingKeys(items: InputEmployee[]): OutputEmployee[] {
return items.map((item) => ({
assignedRole: item.employeeAssignedRole,
middleName: item.employeeFatherName,
firstName: item.employeeName,
rnokpp: item.employeeRNOKPP.toString(),
lastName: item.employeeSurname
}));
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, do not use any as type

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@humanmantis Thanks for confirming! I'll add this as a learning to ensure we maintain type safety in future reviews.


✏️ Learnings added
Learnt from: humanmantis
PR: ita-social-projects/OoS-Frontend#2633
File: src/app/shell/personal-cabinet/provider/provider-employees-upload/provider-employees-upload.component.ts:69-77
Timestamp: 2025-02-01T19:44:34.418Z
Learning: Avoid using 'any' type in TypeScript code. Instead, define proper interfaces or types to maintain type safety and improve code maintainability.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Comment on lines +20 to +24
<div
class="disabled"
*ngIf="!startTimeFormControl.enabled"
(click)="markWorkDaysAsTouched()"
(keydown.Enter)="markWorkDaysAsTouched()">
</div>
(keydown.Enter)="markWorkDaysAsTouched()"></div>
Copy link

@coderabbitai coderabbitai bot Jan 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add keyboard accessibility attributes to the disabled overlay.

The disabled div uses click and keydown.Enter handlers but lacks proper accessibility attributes.

Add role and tabindex attributes:

 <div
   class="disabled"
   *ngIf="!startTimeFormControl.enabled"
   (click)="markWorkDaysAsTouched()"
-  (keydown.Enter)="markWorkDaysAsTouched()"></div>
+  (keydown.Enter)="markWorkDaysAsTouched()"
+  role="button"
+  tabindex="0"
+  aria-label="{{ 'SELECT_WORKDAYS_FIRST' | translate }}"></div>
📝 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.

Suggested change
<div
class="disabled"
*ngIf="!startTimeFormControl.enabled"
(click)="markWorkDaysAsTouched()"
(keydown.Enter)="markWorkDaysAsTouched()">
</div>
(keydown.Enter)="markWorkDaysAsTouched()"></div>
<div
class="disabled"
*ngIf="!startTimeFormControl.enabled"
(click)="markWorkDaysAsTouched()"
(keydown.Enter)="markWorkDaysAsTouched()"
role="button"
tabindex="0"
aria-label="{{ 'SELECT_WORKDAYS_FIRST' | translate }}"></div>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be ignored

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@humanmantis Understood, I'll skip such accessibility suggestions in future reviews.

🐰 nods understandingly


✏️ Learnings added
Learnt from: humanmantis
PR: ita-social-projects/OoS-Frontend#2633
File: src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/working-hours-form-wrapper/working-hours-form/working-hours-form.component.html:20-24
Timestamp: 2025-02-01T19:44:32.316Z
Learning: Accessibility suggestions for adding ARIA attributes and keyboard navigation improvements can be skipped for this codebase as per team's preference.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

fxLayout="column"
fxLayoutAlign="start start">
<button class="btn btn-gray" (click)="leave.emit(application)" [class.hidden]="application.status === ApplicationStatuses.Left || application.status === ApplicationStatuses.Rejected">
<div fxLayout="column" fxLayoutAlign="start start">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are not using flex-layout anymore

})
export class ImportValidationService {
constructor() {}
public checkForInvalidData(items: any[], config: FieldsConfig[]): void {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type?

});
}

public findDuplicates(items: any[], item: unknown): boolean {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

types?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants