From a5632ae6d7ce8fd9b55984fb9aa9bd25b8e0a1af Mon Sep 17 00:00:00 2001 From: AkunaPatlata <94176568+AkunaPatlata@users.noreply.github.com> Date: Wed, 29 May 2024 14:26:42 +0300 Subject: [PATCH 01/25] Dvorak/2546 interaction buttons fisplay fo mobile phone fixed (#2551) * Dvorak / 2546 Interaction buttons display for mobile phone view fixed. * Changed padding for only small screens * Fixed card styles for provider * Deleted padding for kebab menu --- .../application-card/application-card.component.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/application-card.component.scss b/src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/application-card.component.scss index 22716d1ce..0b165c23b 100644 --- a/src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/application-card.component.scss +++ b/src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/application-card.component.scss @@ -3,6 +3,11 @@ .card-block { width: auto; + + &:first-child { + width: 100%; + } + &__parent { flex: 2; } @@ -172,6 +177,7 @@ &__actions { align-items: flex-start; + padding-left: 0 !important; } } From 2a99e48bb7d566884e990e420341a7baef088758 Mon Sep 17 00:00:00 2001 From: Denys Oliinyk <68949838+doliinyk@users.noreply.github.com> Date: Thu, 6 Jun 2024 08:01:26 +0300 Subject: [PATCH 02/25] Oliinyk / 2555 Filters/History of change filters editing and naming (#2557) * Add type-restriction for filter options, customized log value, fixed naming * Fixed tests * Fixed reset button * Add tests --- src/app/shared/constants/drop-down.ts | 12 ++- .../history-log-filters.component.html | 24 +++--- .../history-log-filters.component.spec.ts | 19 ++++- .../history-log-filters.component.ts | 48 ++++++++--- .../history-log-table.component.html | 8 +- .../history-log-table.component.spec.ts | 79 +++++++++++++++++++ .../history-log-table.component.ts | 27 ++++++- src/assets/i18n/uk.json | 6 +- 8 files changed, 183 insertions(+), 40 deletions(-) diff --git a/src/app/shared/constants/drop-down.ts b/src/app/shared/constants/drop-down.ts index 8cb5d22b3..264236a0b 100644 --- a/src/app/shared/constants/drop-down.ts +++ b/src/app/shared/constants/drop-down.ts @@ -26,7 +26,8 @@ export const ProviderOptions = [ export const ProviderAdminOptions = [ { value: 'All', - label: 'HISTORY_LOG.PROVIDER_ADMIN_FILTERS.ALL' + label: 'HISTORY_LOG.PROVIDER_ADMIN_FILTERS.ALL', + default: true }, { value: 'Deputies', @@ -41,11 +42,13 @@ export const ProviderAdminOptions = [ export const ProviderAdminOperationOptions = [ { value: 'Create', - label: 'HISTORY_LOG.PROVIDER_ADMIN_FILTERS.ADD_ADMIN_OPTION' + label: 'HISTORY_LOG.PROVIDER_ADMIN_FILTERS.ADD_ADMIN_OPTION', + type: ['Assistants'] }, { value: 'Delete', - label: 'HISTORY_LOG.PROVIDER_ADMIN_FILTERS.REMOVE_ADMIN_OPTION' + label: 'HISTORY_LOG.PROVIDER_ADMIN_FILTERS.REMOVE_ADMIN_OPTION', + type: ['Assistants'] }, { value: 'Block', @@ -71,7 +74,8 @@ export const ApplicationOptions = [ export const ParentsBlockingByAdminOptions = [ { value: 'All', - label: 'HISTORY_LOG.USERS_FILTERS.ALL' + label: 'HISTORY_LOG.USERS_FILTERS.ALL', + default: true }, { value: 'Blocked', diff --git a/src/app/shell/admin-tools/data/history-log/history-log-filters/history-log-filters.component.html b/src/app/shell/admin-tools/data/history-log/history-log-filters/history-log-filters.component.html index 0c961f041..0821f6c57 100644 --- a/src/app/shell/admin-tools/data/history-log/history-log-filters/history-log-filters.component.html +++ b/src/app/shell/admin-tools/data/history-log/history-log-filters/history-log-filters.component.html @@ -5,10 +5,8 @@ {{ 'FORMS.PLACEHOLDERS.ENTER_DATE_RANGE' | translate }} - - + + @@ -16,14 +14,16 @@ - - - {{ 'FORMS.PLACEHOLDERS.ALL_FILTERS' | translate }} - - - {{ option.label | translate }} - + + + + {{ option.label | translate }} + + diff --git a/src/app/shell/admin-tools/data/history-log/history-log-filters/history-log-filters.component.spec.ts b/src/app/shell/admin-tools/data/history-log/history-log-filters/history-log-filters.component.spec.ts index 0a45c539f..79dd63f0a 100644 --- a/src/app/shell/admin-tools/data/history-log/history-log-filters/history-log-filters.component.spec.ts +++ b/src/app/shell/admin-tools/data/history-log/history-log-filters/history-log-filters.component.spec.ts @@ -1,4 +1,4 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { FormBuilder, FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatNativeDateModule, MatOptionModule } from '@angular/material/core'; import { MatDatepickerModule } from '@angular/material/datepicker'; @@ -9,7 +9,7 @@ import { MatSelectModule } from '@angular/material/select'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { TranslateModule } from '@ngx-translate/core'; -import { FilterOptions, HistoryLogTypes } from 'shared/enum/history.log'; +import { FilterOptions, FormControlNames, HistoryLogTypes } from 'shared/enum/history.log'; import { HistoryLogFiltersComponent } from './history-log-filters.component'; describe('HistoryLogFiltersComponent', () => { @@ -69,7 +69,7 @@ describe('HistoryLogFiltersComponent', () => { test.each` tab | tabName | expectedFormControlName ${HistoryLogTypes.Providers} | ${'Providers'} | ${FilterOptions.PropertyName} - ${HistoryLogTypes.ProviderAdmins} | ${'ProviderAdmins'} | ${FilterOptions.OperationType} + ${HistoryLogTypes.ProviderAdmins} | ${'ProviderAdmins'} | ${FilterOptions.AdminType} ${HistoryLogTypes.Applications} | ${'Applications'} | ${FilterOptions.PropertyName} ${HistoryLogTypes.Users} | ${'Users'} | ${FilterOptions.ShowParents} `( @@ -83,13 +83,24 @@ describe('HistoryLogFiltersComponent', () => { ); it('should add additional form control to filtersForm when tabName is equal to ProviderAdmins', () => { - const expectedAdditionalFormControlName = FilterOptions.AdminType; + const expectedAdditionalFormControlName = FilterOptions.OperationType; component.tabName = HistoryLogTypes.ProviderAdmins; expect(component.filtersList[1].controlName).toBe(expectedAdditionalFormControlName); expect(Object.keys(component.filtersForm.controls)).toContain(expectedAdditionalFormControlName); }); + + it('should filter additional form control options when tabName is equal to ProviderAdmins', fakeAsync(() => { + component.tabName = HistoryLogTypes.ProviderAdmins; + + component.filtersForm.get(FormControlNames.AdminType).setValue('Deputies'); + tick(500); + fixture.detectChanges(); + + const options = component.filtersList[1].options.filter((option) => option.available ?? true).map((option) => option.value); + expect(options).toEqual(['', 'Block', 'Update', 'Reinvite']); + })); }); describe('applyFilters method', () => { diff --git a/src/app/shell/admin-tools/data/history-log/history-log-filters/history-log-filters.component.ts b/src/app/shell/admin-tools/data/history-log/history-log-filters/history-log-filters.component.ts index 409bdf34d..988f03e77 100644 --- a/src/app/shell/admin-tools/data/history-log/history-log-filters/history-log-filters.component.ts +++ b/src/app/shell/admin-tools/data/history-log/history-log-filters/history-log-filters.component.ts @@ -1,5 +1,6 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; +import { Subject, takeUntil } from 'rxjs'; import { DropdownOptionsConfig } from 'shared/constants/drop-down'; import { CustomFormControlNames, FilterOptions, FormControlNames, HistoryLogTypes } from 'shared/enum/history.log'; @@ -11,7 +12,7 @@ import { DateFilters, DropdownData, FilterData } from 'shared/models/history-log styleUrls: ['./history-log-filters.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) -export class HistoryLogFiltersComponent implements OnInit { +export class HistoryLogFiltersComponent implements OnInit, OnDestroy { @Input() public dropdownOptions: DropdownData; @Output() public filterData = new EventEmitter(); @@ -25,6 +26,7 @@ export class HistoryLogFiltersComponent implements OnInit { private baseCountOfFiltersFormFields = 2; private _tabName: HistoryLogTypes; + private readonly destroy$: Subject = new Subject(); constructor(private fb: FormBuilder) {} @@ -46,6 +48,11 @@ export class HistoryLogFiltersComponent implements OnInit { this.emitFilterData(); } + public ngOnDestroy(): void { + this.destroy$.next(true); + this.destroy$.complete(); + } + public applyFilters(): void { this.emitFilterData(); } @@ -53,7 +60,8 @@ export class HistoryLogFiltersComponent implements OnInit { public onResetFilters(): void { this.filtersForm.reset(); Object.keys(this.filtersForm.controls).forEach((control: string) => { - this.filtersForm.get(control).setValue(''); + const value = this.dropdownOptionsConfig[control]?.find((option) => option.default)?.value || ''; + this.filtersForm.get(control).setValue(value); }); this.dateFromFilters.emit({ [FilterOptions.DateFrom]: '', [FilterOptions.DateTo]: '' }); } @@ -67,16 +75,22 @@ export class HistoryLogFiltersComponent implements OnInit { private setFiltersDependOnTab(tabName: HistoryLogTypes): void { switch (tabName) { case HistoryLogTypes.Providers: - this.addFormControlForFiltersForm([CustomFormControlNames.ProvidersPropertyName]); + this.addFormControlForFiltersForm(CustomFormControlNames.ProvidersPropertyName); break; case HistoryLogTypes.ProviderAdmins: - this.addFormControlForFiltersForm([CustomFormControlNames.OperationType, CustomFormControlNames.AdminType]); + this.addFormControlForFiltersForm(CustomFormControlNames.AdminType, CustomFormControlNames.OperationType); + this.addFormControlListeners(FormControlNames.AdminType, (adminType: string) => { + const adminTypeOptions = this.filtersList.find((filter) => filter.controlName === FormControlNames.AdminType).options; + const operationTypeOptions = this.filtersList.find((filter) => filter.controlName === FormControlNames.OperationType).options; + const isDefault = adminTypeOptions.find((option) => option.value === adminType).default; + operationTypeOptions.forEach((option) => (option.available = (isDefault || option.type?.includes(adminType)) ?? true)); + }); break; case HistoryLogTypes.Applications: - this.addFormControlForFiltersForm([CustomFormControlNames.ApplicationsPropertyName]); + this.addFormControlForFiltersForm(CustomFormControlNames.ApplicationsPropertyName); break; case HistoryLogTypes.Users: - this.addFormControlForFiltersForm([CustomFormControlNames.ShowParents]); + this.addFormControlForFiltersForm(CustomFormControlNames.ShowParents); break; } } @@ -137,13 +151,27 @@ export class HistoryLogFiltersComponent implements OnInit { return this.setTimeDependsOnTimezone(dateFrom, dateTo); } - private addFormControlForFiltersForm(formControlNames: string[]): void { + private addFormControlForFiltersForm(...formControlNames: string[]): void { formControlNames.forEach((controlName: string) => { - this.filtersForm.addControl(FormControlNames[controlName], new FormControl('')); + const options = [...this.dropdownOptionsConfig[controlName]]; + + const defaultOption = options.find((option) => option.default); + const value = defaultOption?.value || ''; + if (defaultOption) { + defaultOption.label = 'FORMS.PLACEHOLDERS.ALL_FILTERS'; + } else { + options.unshift({ value, label: 'FORMS.PLACEHOLDERS.ALL_FILTERS' }); + } + + this.filtersForm.addControl(FormControlNames[controlName], new FormControl(value)); this.filtersList.push({ controlName: FormControlNames[controlName], - options: this.dropdownOptionsConfig[controlName] + options }); }); } + + private addFormControlListeners(formControlName: FormControlNames, callback: (value: string) => void): void { + this.filtersForm.get(formControlName).valueChanges.pipe(takeUntil(this.destroy$)).subscribe(callback); + } } diff --git a/src/app/shell/admin-tools/data/history-log/history-log-table/history-log-table.component.html b/src/app/shell/admin-tools/data/history-log/history-log-table/history-log-table.component.html index efb77a48e..a7df07a8f 100644 --- a/src/app/shell/admin-tools/data/history-log/history-log-table/history-log-table.component.html +++ b/src/app/shell/admin-tools/data/history-log/history-log-table/history-log-table.component.html @@ -73,9 +73,7 @@ *matCellDef="let element" [ngClass]="{ 'application-status': isApplicationHistoryType }" class="{{ element.oldValue }}"> - {{ - (isApplicationHistoryType ? statusTitles[element?.oldValue] : element?.oldValue) | emptyValueTransform: DASH_VALUE | translate - }} + {{ getCustomLogValue(element, 'oldValue') | emptyValueTransform: DASH_VALUE | translate }} @@ -86,9 +84,7 @@ *matCellDef="let element" [ngClass]="{ 'application-status': isApplicationHistoryType }" class="{{ element.newValue }}"> - {{ - (isApplicationHistoryType ? statusTitles[element?.newValue] : element?.newValue) | emptyValueTransform: DASH_VALUE | translate - }} + {{ getCustomLogValue(element, 'newValue') | emptyValueTransform: DASH_VALUE | translate }} diff --git a/src/app/shell/admin-tools/data/history-log/history-log-table/history-log-table.component.spec.ts b/src/app/shell/admin-tools/data/history-log/history-log-table/history-log-table.component.spec.ts index c8b3fe38a..db3c66884 100644 --- a/src/app/shell/admin-tools/data/history-log/history-log-table/history-log-table.component.spec.ts +++ b/src/app/shell/admin-tools/data/history-log/history-log-table/history-log-table.component.spec.ts @@ -6,6 +6,7 @@ import { RouterTestingModule } from '@angular/router/testing'; import { TranslateModule } from '@ngx-translate/core'; import { NgxsModule } from '@ngxs/store'; +import { ApplicationTitles } from 'shared/enum/enumUA/statuses'; import { HistoryLogTypes } from 'shared/enum/history.log'; import { HistoryLogTableComponent } from './history-log-table.component'; @@ -34,4 +35,82 @@ describe('HistoryLogTableComponent', () => { it('should return TRUE if isApplicationHistoryType called and tableType is Applications', () => { expect(component.isApplicationHistoryType).toBeTruthy(); }); + + describe('getCustomLogValue method', () => { + it('should return HISTORY_LOG.USER_WAS_BLOCKED for oldValue when operationType is Block and oldValue is truthy', () => { + const element = { operationType: 'Block', oldValue: '1' } as any; + + const result = component.getCustomLogValue(element, 'oldValue'); + + expect(result).toBe('HISTORY_LOG.USER_WAS_BLOCKED'); + }); + + it('should return HISTORY_LOG.USER_WAS_UNBLOCKED for oldValue when operationType is Block and oldValue is falsy', () => { + const element = { operationType: 'Block', oldValue: '0' } as any; + + const result = component.getCustomLogValue(element, 'oldValue'); + + expect(result).toBe('HISTORY_LOG.USER_WAS_UNBLOCKED'); + }); + + it('should return status title for oldValue when isApplicationHistoryType is true', () => { + const element = { oldValue: 'Pending' } as any; + jest.spyOn(component, 'isApplicationHistoryType', 'get').mockReturnValue(true); + + const result = component.getCustomLogValue(element, 'oldValue'); + + expect(result).toBe(ApplicationTitles.Pending); + }); + + it('should return oldValue directly when isApplicationHistoryType is false', () => { + const element = { oldValue: 'someValue' } as any; + jest.spyOn(component, 'isApplicationHistoryType', 'get').mockReturnValue(false); + + const result = component.getCustomLogValue(element, 'oldValue'); + + expect(result).toBe('someValue'); + }); + + it('should return HISTORY_LOG.USER_WAS_BLOCKED for newValue when operationType is Block and newValue is truthy', () => { + const element = { operationType: 'Block', newValue: '1' } as any; + + const result = component.getCustomLogValue(element, 'newValue'); + + expect(result).toBe('HISTORY_LOG.USER_WAS_BLOCKED'); + }); + + it('should return HISTORY_LOG.USER_WAS_UNBLOCKED for newValue when operationType is Block and newValue is falsy', () => { + const element = { operationType: 'Block', newValue: '0' } as any; + + const result = component.getCustomLogValue(element, 'newValue'); + + expect(result).toBe('HISTORY_LOG.USER_WAS_UNBLOCKED'); + }); + + it('should return status title for newValue when isApplicationHistoryType is true', () => { + const element = { newValue: 'Approved' } as any; + jest.spyOn(component, 'isApplicationHistoryType', 'get').mockReturnValue(true); + + const result = component.getCustomLogValue(element, 'newValue'); + + expect(result).toBe(ApplicationTitles.Approved); + }); + + it('should return newValue directly when isApplicationHistoryType is false', () => { + const element = { newValue: 'anotherValue' } as any; + jest.spyOn(component, 'isApplicationHistoryType', 'get').mockReturnValue(false); + + const result = component.getCustomLogValue(element, 'newValue'); + + expect(result).toBe('anotherValue'); + }); + + it('should return empty string for unknown column', () => { + const element = {} as any; + + const result = component.getCustomLogValue(element, 'unknownColumn'); + + expect(result).toBe(''); + }); + }); }); diff --git a/src/app/shell/admin-tools/data/history-log/history-log-table/history-log-table.component.ts b/src/app/shell/admin-tools/data/history-log/history-log-table/history-log-table.component.ts index 37a219018..e465655ed 100644 --- a/src/app/shell/admin-tools/data/history-log/history-log-table/history-log-table.component.ts +++ b/src/app/shell/admin-tools/data/history-log/history-log-table/history-log-table.component.ts @@ -1,6 +1,6 @@ import { AfterViewInit, Component, Input, OnInit, ViewChild } from '@angular/core'; -import { MatSort } from '@angular/material/sort'; import { MatLegacyTableDataSource as MatTableDataSource } from '@angular/material/legacy-table'; +import { MatSort } from '@angular/material/sort'; import { ColumnsListForChangesLogHistory } from 'shared/constants/changes-log'; import { Constants } from 'shared/constants/constants'; @@ -59,4 +59,29 @@ export class HistoryLogTableComponent implements OnInit, AfterViewInit { public ngAfterViewInit(): void { this.dataSource.sort = this.sort; } + + public getCustomLogValue( + element: ProviderHistory & ProviderAdminHistory & ApplicationHistory & ParentsBlockingByAdminHistory, + column: string + ): string { + switch (column) { + case 'oldValue': + if (element.operationType === 'Block') { + return +element.oldValue ? 'HISTORY_LOG.USER_WAS_BLOCKED' : 'HISTORY_LOG.USER_WAS_UNBLOCKED'; + } else if (this.isApplicationHistoryType) { + return this.statusTitles[element?.oldValue]; + } else { + return element?.oldValue; + } + case 'newValue': + if (element.operationType === 'Block') { + return +element.newValue ? 'HISTORY_LOG.USER_WAS_BLOCKED' : 'HISTORY_LOG.USER_WAS_UNBLOCKED'; + } else if (this.isApplicationHistoryType) { + return this.statusTitles[element?.newValue]; + } else { + return element?.newValue; + } + } + return ''; + } } diff --git a/src/assets/i18n/uk.json b/src/assets/i18n/uk.json index 97df602b5..25baae055 100644 --- a/src/assets/i18n/uk.json +++ b/src/assets/i18n/uk.json @@ -462,7 +462,7 @@ "INFO_MENU": { "UNLIMITED": "Якщо вибрати опцію \"Без обмежень\" - гурток не матиме обмеження кількості користувачів. Якщо ввести кількість доступних місць для користувачів - після досягнення ліміту набір буде автоматично закрито і подачу заявок зупинено автоматично.", "LICENSE": "Додайте ліцензію, щоб отримати більше інтересу та підвищити рівень довіри учасників до гуртків вашого закладу. Отримавши статус затвердженої ліцензії, на сторінці попереднього перегляду закладу або гуртка з'явиться значок підтвердження", - "OWNERSHIP_CAN_NOT_BE_CHANGED": "Форма власності не може бути змінена. Зареєструйте новий аккаунт." + "OWNERSHIP_CAN_NOT_BE_CHANGED": "Форма власності не може бути змінена. Зареєструйте новий акаунт." }, "ALL_WORKSHOPS_ACCESS": "Буде надано доступ до всіх гуртків", "INSTITUTION_TYPE": "Тип закладу", @@ -619,10 +619,10 @@ "PROVIDER_ADMIN_FILTERS": { "ALL": "Усі", "DEPUTIES": "Заступники", - "ASSISTANTS": "Помічники", + "ASSISTANTS": "Адміністратори гуртків", "ADD_ADMIN_OPTION": "Додавання адміністратора до гуртка", "REMOVE_ADMIN_OPTION": "Видалення адміністратора з гуртка", - "BLOCK_ADMIN_OPTION": "Адміністратора було заблоковано", + "BLOCK_ADMIN_OPTION": "Адміністратора було заблоковано/розблоковано", "UPDATE_ADMIN_OPTION": "Дані про адміністратора були оновлені", "REINVITE_ADMIN_OPTION": "Адміністратор був запрошений повторно" }, From 35148e0f06f6cb1cbc193ce0f67056abb2e877d8 Mon Sep 17 00:00:00 2001 From: AkunaPatlata <94176568+AkunaPatlata@users.noreply.github.com> Date: Thu, 6 Jun 2024 11:37:57 +0300 Subject: [PATCH 03/25] Dvorak/2546 Fixed some styles in personal cabinets applications (#2562) --- src/app/shared/styles/application-statuses.scss | 1 + .../application-card/application-card.component.html | 6 +----- .../application-card/application-card.component.scss | 5 +++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/app/shared/styles/application-statuses.scss b/src/app/shared/styles/application-statuses.scss index bf0211245..9c07b4495 100644 --- a/src/app/shared/styles/application-statuses.scss +++ b/src/app/shared/styles/application-statuses.scss @@ -7,6 +7,7 @@ } .AcceptedForSelection { color: #27a3ff; + max-width: 201px !important; } .Rejected, .Blocked, diff --git a/src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/application-card.component.html b/src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/application-card.component.html index 6afafb48d..ed9fff2de 100644 --- a/src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/application-card.component.html +++ b/src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/application-card.component.html @@ -90,11 +90,7 @@
- +
Date: Thu, 6 Jun 2024 11:46:22 +0300 Subject: [PATCH 04/25] Smyk / 2552 There is no limitation of the time range when choosing the date of achievement of the workshop (#2553) * Changed min limit of datepicker to 100 years before current date. Sonarlint fixes * Set max limit of datepicker to 100 years after current data * Removed redefinition of 'destroy$' field, as it was done in other classes that are extended from CreateFormComponent class, where 'destroy$' property is already defined --- .../create-achievement.component.html | 2 + .../create-achievement.component.ts | 76 ++++++++++--------- 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/src/app/shell/personal-cabinet/provider/create-achievement/create-achievement.component.html b/src/app/shell/personal-cabinet/provider/create-achievement/create-achievement.component.html index cfe04d214..7cdfd1489 100644 --- a/src/app/shell/personal-cabinet/provider/create-achievement/create-achievement.component.html +++ b/src/app/shell/personal-cabinet/provider/create-achievement/create-achievement.component.html @@ -115,6 +115,8 @@

{{ 'FORMS.HEADERS.ADD_ACHIEVEMENT' | translate }}

id="achievement-date" matInput [matDatepicker]="picker" + [min]="minDate" + [max]="maxDate" formControlName="achievementDate" placeholder="{{ 'FORMS.PLACEHOLDERS.SHORT_DATE_FORMAT' | translate }}" /> diff --git a/src/app/shell/personal-cabinet/provider/create-achievement/create-achievement.component.ts b/src/app/shell/personal-cabinet/provider/create-achievement/create-achievement.component.ts index 0c126bcd8..bd3258040 100644 --- a/src/app/shell/personal-cabinet/provider/create-achievement/create-achievement.component.ts +++ b/src/app/shell/personal-cabinet/provider/create-achievement/create-achievement.component.ts @@ -36,37 +36,24 @@ import { CreateFormComponent } from '../../shared-cabinet/create-form/create-for styleUrls: ['./create-achievement.component.scss'] }) export class CreateAchievementComponent extends CreateFormComponent implements OnInit, OnDestroy { - readonly validationConstants = ValidationConstants; - @Select(SharedUserState.selectedWorkshop) - workshop$: Observable; + private workshop$: Observable; @Select(ProviderState.approvedChildren) - approvedChildren$: Observable>; + private approvedChildren$: Observable>; @Select(ProviderState.selectedAchievement) - selectedAchievement$: Observable; + private selectedAchievement$: Observable; @Select(MetaDataState.achievementsTypes) - achievementsTypes$: Observable; - - AchievementFormGroup: FormGroup; - workshop: Workshop; - achievement: Achievement; - workshopId: string; - approvedChildren: SearchResponse; - destroy$: Subject = new Subject(); - isSaving: boolean = false; - - get teachersFormControl(): FormControl { - return this.AchievementFormGroup.get('teachers') as FormControl; - } - - get childrenFormControl(): FormControl { - return this.AchievementFormGroup.get('children') as FormControl; - } - - get achievementTypeIdFormControl(): FormControl { - return this.AchievementFormGroup.get('achievementTypeId') as FormControl; - } - + private achievementsTypes$: Observable; + + public workshop: Workshop; + private readonly validationConstants = ValidationConstants; + private AchievementFormGroup: FormGroup; + private achievement: Achievement; + private workshopId: string; + private approvedChildren: SearchResponse; + private isSaving: boolean = false; + private minDate: Date; + private maxDate: Date; private achievementId: string; constructor( @@ -93,11 +80,26 @@ export class CreateAchievementComponent extends CreateFormComponent implements O this.subscribeOnDirtyForm(this.AchievementFormGroup); } - ngOnInit(): void { + public get teachersFormControl(): FormControl { + return this.AchievementFormGroup.get('teachers') as FormControl; + } + + public get childrenFormControl(): FormControl { + return this.AchievementFormGroup.get('children') as FormControl; + } + + public get achievementTypeIdFormControl(): FormControl { + return this.AchievementFormGroup.get('achievementTypeId') as FormControl; + } + + public ngOnInit(): void { this.getData(); + const currentDate = new Date(); + this.minDate = new Date(currentDate.getFullYear() - 100, currentDate.getMonth(), currentDate.getDate()); + this.maxDate = new Date(currentDate.getFullYear() + 100, currentDate.getMonth(), currentDate.getDate()); } - determineEditMode(): void { + public determineEditMode(): void { this.achievementId = this.route.snapshot.queryParamMap.get('achievementId'); this.editMode = !!this.achievementId; if (this.editMode) { @@ -106,7 +108,7 @@ export class CreateAchievementComponent extends CreateFormComponent implements O this.addNavPath(); } - getData(): void { + public getData(): void { this.workshopId = this.route.snapshot.paramMap.get('param'); this.store.dispatch([new GetWorkshopById(this.workshopId), new GetChildrenByWorkshopId(this.workshopId), new GetAchievementsType()]); @@ -122,7 +124,7 @@ export class CreateAchievementComponent extends CreateFormComponent implements O }); } - setEditMode(): void { + public setEditMode(): void { this.store.dispatch(new GetAchievementById(this.achievementId)); this.selectedAchievement$ .pipe( @@ -136,7 +138,7 @@ export class CreateAchievementComponent extends CreateFormComponent implements O }); } - addNavPath(): void { + public addNavPath(): void { let prevPath: Navigation; if (this.editMode) { @@ -170,7 +172,7 @@ export class CreateAchievementComponent extends CreateFormComponent implements O ); } - onSubmit(): void { + public onSubmit(): void { if (this.isSaving) { return; } @@ -205,11 +207,11 @@ export class CreateAchievementComponent extends CreateFormComponent implements O }); } - onCancel(): void { + public onCancel(): void { this.router.navigate(['/details/workshop', this.workshopId], { queryParams: { status: 'Achievements' } }); } - onRemoveItem(item: string, control: string): void { + public onRemoveItem(item: string, control: string): void { const formControl = this.AchievementFormGroup.get(control); const items = formControl.value; if (items.indexOf(item) >= 0) { @@ -222,11 +224,11 @@ export class CreateAchievementComponent extends CreateFormComponent implements O } } - compareEntities(person1: Person, person2: Person): boolean { + public compareEntities(person1: Person, person2: Person): boolean { return person1.id === person2.id; } - ngOnDestroy(): void { + public ngOnDestroy(): void { this.store.dispatch(new ResetProviderWorkshopDetails()); this.destroy$.next(true); this.destroy$.unsubscribe(); From b2b405b25be2673cff0aaa8a1ddd3b46788ebcdd Mon Sep 17 00:00:00 2001 From: Andrii Smyk <71979027+AndriiSmyk@users.noreply.github.com> Date: Thu, 6 Jun 2024 11:49:43 +0300 Subject: [PATCH 05/25] =?UTF-8?q?Changed=20ukrainian=20variant=20of=20'Sen?= =?UTF-8?q?d'=20in=20chat=20between=20parent=20and=20SP=20from=20'=D0=92?= =?UTF-8?q?=D1=96=D0=B4=D0=BF=D1=80=D0=B0=D0=B2=D0=B8=D1=82=D0=B8'=20to=20?= =?UTF-8?q?'=D0=9D=D0=B0=D0=B4=D1=96=D1=81=D0=BB=D0=B0=D1=82=D0=B8'=20(#25?= =?UTF-8?q?60)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/i18n/uk.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/i18n/uk.json b/src/assets/i18n/uk.json index 25baae055..c2bef0495 100644 --- a/src/assets/i18n/uk.json +++ b/src/assets/i18n/uk.json @@ -545,7 +545,7 @@ "SEND_INVITATION": "Надіслати запрошення", "SEND_MESSAGE": "Надіслати повідомлення", "CANCEL": "Скасувати", - "SEND": "Відправити", + "SEND": "Надіслати", "EDIT": "Редагувати", "SAVE": "Зберегти", "BACK": "Назад", From f269dc7652479ab13e268dd1d8a93379b98247ee Mon Sep 17 00:00:00 2001 From: Yurii Romanchak <71885527+Kazumen@users.noreply.github.com> Date: Tue, 11 Jun 2024 14:30:08 +0300 Subject: [PATCH 06/25] Romanchak/Parent registration update (#2554) * Created CreateParentComponent, added guard, translation and routing * Fixed guard, added new translate for message bar * Added tests for CreateParentComponent and CreateParentGuard. Added logic for button submit * Fixed lint * Fixed tests * Fixed lint * Fixed lint * Added new test for service * Fixed lint * Fixed test for personal cabinet guard * Added exclusions for coverage * Added payload for Fail and Success create provider and parent * Fixed message bar for provider and parent. Also added condition for dialog menu from workshop details * Fixed comments * Fixed comment * Fixed lint --- sonar-project.properties | 2 +- src/app/header/header.component.html | 7 +- ...gistered-user-warning-modal.component.html | 2 +- ...registered-user-warning-modal.component.ts | 14 +- src/app/shared/enum/enumUA/message-bar.ts | 2 + src/app/shared/enum/enumUA/navigation-bar.ts | 3 +- src/app/shared/models/parent.model.ts | 6 + .../services/parent/parent.service.spec.ts | 12 +- .../shared/services/parent/parent.service.ts | 6 +- src/app/shared/store/parent.actions.ts | 59 +++++++- src/app/shared/store/parent.state.ts | 80 +++++++++-- src/app/shared/store/provider.state.ts | 2 +- src/app/shared/store/registration.state.ts | 5 +- .../side-menu/actions/actions.component.ts | 7 +- .../create-parent.component.html | 56 ++++++++ .../create-parent.component.scss | 15 ++ .../create-parent.component.spec.ts | 98 +++++++++++++ .../create-parent/create-parent.component.ts | 135 ++++++++++++++++++ .../create-parent/create-parent.guard.spec.ts | 47 ++++++ .../create-parent/create-parent.guard.ts | 21 +++ .../personal-cabinet/parent/parent.module.ts | 8 +- .../personal-cabinet.guard.spec.ts | 29 ++-- .../personal-cabinet.guard.ts | 22 ++- .../create-provider.component.ts | 3 +- src/app/shell/shell-routing.module.ts | 9 ++ src/app/shell/shell.module.ts | 2 + src/assets/i18n/en.json | 6 +- src/assets/i18n/uk.json | 8 +- 28 files changed, 620 insertions(+), 46 deletions(-) create mode 100644 src/app/shell/personal-cabinet/parent/create-parent/create-parent.component.html create mode 100644 src/app/shell/personal-cabinet/parent/create-parent/create-parent.component.scss create mode 100644 src/app/shell/personal-cabinet/parent/create-parent/create-parent.component.spec.ts create mode 100644 src/app/shell/personal-cabinet/parent/create-parent/create-parent.component.ts create mode 100644 src/app/shell/personal-cabinet/parent/create-parent/create-parent.guard.spec.ts create mode 100644 src/app/shell/personal-cabinet/parent/create-parent/create-parent.guard.ts diff --git a/sonar-project.properties b/sonar-project.properties index 7e5fec65b..ee3cd2c34 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,6 +1,6 @@ sonar.projectKey=ita-social-projects-oos-frontend sonar.organization=ita-social-projects sonar.exclusions=**/*.spec.ts -sonar.coverage.exclusions=**/*.spec.ts,**/*.model.ts +sonar.coverage.exclusions=**/*.spec.ts,**/*.model.ts,**/*.state.ts,**/*.actions.ts sonar.cpd.exclusions=**/*.spec.ts sonar.javascript.lcov.reportPaths=./coverage/lcov.info diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html index ee8c1d383..055ec3f2e 100644 --- a/src/app/header/header.component.html +++ b/src/app/header/header.component.html @@ -145,10 +145,15 @@

{{ headerSubtitle }}

- + + + + diff --git a/src/app/shared/components/unregistered-user-warning-modal/unregistered-user-warning-modal.component.html b/src/app/shared/components/unregistered-user-warning-modal/unregistered-user-warning-modal.component.html index b08e7ff47..cb2ee1b29 100644 --- a/src/app/shared/components/unregistered-user-warning-modal/unregistered-user-warning-modal.component.html +++ b/src/app/shared/components/unregistered-user-warning-modal/unregistered-user-warning-modal.component.html @@ -10,7 +10,7 @@

-

diff --git a/src/app/shared/components/unregistered-user-warning-modal/unregistered-user-warning-modal.component.ts b/src/app/shared/components/unregistered-user-warning-modal/unregistered-user-warning-modal.component.ts index 92fcd17b0..446bbfe6f 100644 --- a/src/app/shared/components/unregistered-user-warning-modal/unregistered-user-warning-modal.component.ts +++ b/src/app/shared/components/unregistered-user-warning-modal/unregistered-user-warning-modal.component.ts @@ -1,9 +1,13 @@ import { Component, Inject } from '@angular/core'; import { MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA } from '@angular/material/legacy-dialog'; +import { Router } from '@angular/router'; import { Store } from '@ngxs/store'; +import { ModeConstants } from 'shared/constants/constants'; +import { Role } from 'shared/enum/role'; import { ModalData } from 'shared/models/modal-data.model'; import { Login } from 'shared/store/registration.actions'; +import { RegistrationState } from 'shared/store/registration.state'; @Component({ selector: 'app-unregistered-user-warning-modal', @@ -13,10 +17,16 @@ import { Login } from 'shared/store/registration.actions'; export class UnregisteredUserWarningModalComponent { constructor( @Inject(MAT_DIALOG_DATA) public data: ModalData, - private store: Store + private store: Store, + private router: Router ) {} public login(): void { - this.store.dispatch(new Login(false)); + const role = this.store.selectSnapshot(RegistrationState.role); + if (role === Role.unauthorized) { + this.store.dispatch(new Login(false)); + } else { + this.router.navigate(['/create-parent', ModeConstants.NEW]); + } } } diff --git a/src/app/shared/enum/enumUA/message-bar.ts b/src/app/shared/enum/enumUA/message-bar.ts index 335b0eda8..ef1af338c 100644 --- a/src/app/shared/enum/enumUA/message-bar.ts +++ b/src/app/shared/enum/enumUA/message-bar.ts @@ -55,12 +55,14 @@ export enum SnackbarText { updateDeputy = 'SERVICE_MESSAGES.SNACK_BAR_TEXT.UPDATE_PROVIDER_DEPUTY', completeRegistration = 'SERVICE_MESSAGES.SNACK_BAR_TEXT.COMPLETE_PROVIDER_REGISTRATION', + completeUserRegistration = 'SERVICE_MESSAGES.SNACK_BAR_TEXT.COMPLETE_USER_REGISTRATION', createRating = 'SERVICE_MESSAGES.SNACK_BAR_TEXT.CREATE_RATING', updatePortal = 'SERVICE_MESSAGES.SNACK_BAR_TEXT.UPDATE_PORTAL', updateUser = 'SERVICE_MESSAGES.SNACK_BAR_TEXT.UPDATE_USER', + createUser = 'SERVICE_MESSAGES.SNACK_BAR_TEXT.CREATE_USER', blockPerson = 'SERVICE_MESSAGES.SNACK_BAR_TEXT.BLOCK_USER', unblockPerson = 'SERVICE_MESSAGES.SNACK_BAR_TEXT.UNBLOCK_USER', diff --git a/src/app/shared/enum/enumUA/navigation-bar.ts b/src/app/shared/enum/enumUA/navigation-bar.ts index 47a617f5a..93e4b9ddb 100644 --- a/src/app/shared/enum/enumUA/navigation-bar.ts +++ b/src/app/shared/enum/enumUA/navigation-bar.ts @@ -40,7 +40,8 @@ export enum NavBarName { Messages = 'ENUM.NAV_BAR_NAME.MESSAGES', Chat = 'ENUM.NAV_BAR_NAME.CHAT', HistoryLog = 'ENUM.NAV_BAR_NAME.HISTORY_OF_CHANGES', - Statistics = 'ENUM.NAV_BAR_NAME.STATISTICS' + Statistics = 'ENUM.NAV_BAR_NAME.STATISTICS', + CreateNewUser = 'ENUM.NAV_BAR_NAME.USER_REGISTRATION' } export enum PersonalCabinetTitle { diff --git a/src/app/shared/models/parent.model.ts b/src/app/shared/models/parent.model.ts index 8393d75b2..c82b858a1 100644 --- a/src/app/shared/models/parent.model.ts +++ b/src/app/shared/models/parent.model.ts @@ -38,3 +38,9 @@ export interface ParentBlockedData { isBlocked: boolean; reason?: string; } + +export interface ParentPayload { + phoneNumber: string; + gender: string; + dateOfBirth: string; +} diff --git a/src/app/shared/services/parent/parent.service.spec.ts b/src/app/shared/services/parent/parent.service.spec.ts index 8271a17f7..036a3e00f 100644 --- a/src/app/shared/services/parent/parent.service.spec.ts +++ b/src/app/shared/services/parent/parent.service.spec.ts @@ -2,7 +2,7 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/ import { TestBed } from '@angular/core/testing'; import { NgxsModule } from '@ngxs/store'; -import { Parent, ParentBlockedData } from 'shared/models/parent.model'; +import { Parent, ParentBlockedData, ParentPayload } from 'shared/models/parent.model'; import { ParentService } from './parent.service'; describe('ParentService', () => { @@ -48,4 +48,14 @@ describe('ParentService', () => { expect(req.request.method).toEqual('POST'); }); + + it('should create new parent', (done) => { + const parentPayload: ParentPayload = { dateOfBirth: '07/04/2004', phoneNumber: '+380971221515', gender: 'male' }; + service.createParent(parentPayload).subscribe(done); + + const req = httpTestingController.expectOne(`${baseApiUrl}`); + req.flush(null); + + expect(req.request.method).toEqual('POST'); + }); }); diff --git a/src/app/shared/services/parent/parent.service.ts b/src/app/shared/services/parent/parent.service.ts index 63dc544f8..2a9ad6dba 100644 --- a/src/app/shared/services/parent/parent.service.ts +++ b/src/app/shared/services/parent/parent.service.ts @@ -2,7 +2,7 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; -import { Parent, ParentBlockedData } from 'shared/models/parent.model'; +import { Parent, ParentBlockedData, ParentPayload } from 'shared/models/parent.model'; @Injectable({ providedIn: 'root' @@ -22,4 +22,8 @@ export class ParentService { public blockUnblockParent(parentBlockedData: ParentBlockedData): Observable { return this.http.post(`${this.baseApiUrl}/BlockUnblockParent`, parentBlockedData); } + + public createParent(parentPayload: ParentPayload): Observable { + return this.http.post(`${this.baseApiUrl}`, parentPayload); + } } diff --git a/src/app/shared/store/parent.actions.ts b/src/app/shared/store/parent.actions.ts index 9d0f2596c..0ba18bf8d 100644 --- a/src/app/shared/store/parent.actions.ts +++ b/src/app/shared/store/parent.actions.ts @@ -3,12 +3,13 @@ import { HttpErrorResponse } from '@angular/common/http'; import { Application } from 'shared/models/application.model'; import { Child, ChildrenParameters, RequestParams } from 'shared/models/child.model'; import { Favorite } from 'shared/models/favorite.model'; -import { ParentBlockedData } from 'shared/models/parent.model'; +import { Parent, ParentBlockedData, ParentPayload } from 'shared/models/parent.model'; import { PaginationParameters } from 'shared/models/query-parameters.model'; import { Rate } from 'shared/models/rating'; export class GetStatusIsAllowToApply { static readonly type = '[parent] get child status By child and workshop ids'; + constructor( public childId: string, public workshopId: string @@ -17,6 +18,7 @@ export class GetStatusIsAllowToApply { export class GetStatusAllowedToReview { static readonly type = '[parent] get parent status By parent id'; + constructor( public parentId: string, public workshopId: string @@ -25,6 +27,7 @@ export class GetStatusAllowedToReview { export class GetReviewedStatus { static readonly type = '[parent] get reviewed status for workshop by parent id'; + constructor( public parentId: string, public workshopId: string @@ -33,76 +36,91 @@ export class GetReviewedStatus { export class GetFavoriteWorkshops { static readonly type = '[parent] get favorite parents workshops'; + constructor() {} } export class GetFavoriteWorkshopsByUserId { static readonly type = '[parent] get favorite workshops by UserId'; + constructor(public paginationParameters: PaginationParameters) {} } export class CreateFavoriteWorkshop { static readonly type = '[parent] create favorite workshop'; + constructor(public payload: Favorite) {} } export class DeleteFavoriteWorkshop { static readonly type = '[parent] delete favorite workshop'; + constructor(public payload: string) {} } export class GetUsersChildren { static readonly type = '[parent] get users Children'; + constructor(public parameters: ChildrenParameters) {} } export class GetUsersChildById { static readonly type = '[parent] get users Children by Id'; + constructor(public payload: string) {} } export class GetAllUsersChildren { static readonly type = '[parent] get all users Children'; + constructor() {} } export class GetAllUsersChildrenByParentId { static readonly type = '[parent] get all users Children by Parent Id'; + constructor(public payload: RequestParams) {} } export class CreateChild { static readonly type = '[parent] create Child'; + constructor(public payload: Child) {} } export class OnCreateChildFail { static readonly type = '[parent] create Child fail'; + constructor(public payload: HttpErrorResponse) {} } export class OnCreateChildSuccess { static readonly type = '[parent] create Child success'; + constructor() {} } export class CreateChildren { static readonly type = '[parent] create Children'; + constructor(public payload: Child[]) {} } export class OnCreateChildrenFail { static readonly type = '[parent] create Children fail'; + constructor(public payload: HttpErrorResponse) {} } export class OnCreateChildrenSuccess { static readonly type = '[parent] create Children success'; + constructor(public multipleChildren?: boolean) {} } export class DeleteChildById { static readonly type = '[parent] delete Children'; + constructor( public payload: string, public parameters: ChildrenParameters @@ -111,105 +129,144 @@ export class DeleteChildById { export class OnDeleteChildSuccess { static readonly type = '[parent] delete Children success'; + constructor(public parameters: ChildrenParameters) {} } export class OnDeleteChildFail { static readonly type = '[parent] delete Children fail'; + constructor(public payload: HttpErrorResponse) {} } export class UpdateChild { static readonly type = '[parent] update Child'; + constructor(public payload: Child) {} } export class OnUpdateChildFail { static readonly type = '[parent] update Child fail'; + constructor(public payload: HttpErrorResponse) {} } export class OnUpdateChildSuccess { static readonly type = '[parent] update Child success'; + constructor() {} } export class ResetSelectedChild { static readonly type = '[parent] reset selected child'; + constructor() {} } export class CreateRating { static readonly type = '[parent] create Rating'; + constructor(public payload: Rate) {} } export class OnCreateRatingFail { static readonly type = '[parent] create Rating fail'; + constructor(public payload: HttpErrorResponse) {} } export class OnCreateRatingSuccess { static readonly type = '[parent] create Rating success'; + constructor() {} } export class DeleteRatingById { static readonly type = '[parent] delete Rating'; + constructor(public payload: number) {} } export class OnDeleteRatingFail { static readonly type = '[parent] delete Rating fail'; + constructor(public payload: HttpErrorResponse) {} } export class OnDeleteRatingSuccess { static readonly type = '[parent] delete Rating success'; + constructor(public payload: number) {} } export class CreateApplication { static readonly type = '[parent] create Application'; + constructor(public payload: Application) {} } export class OnCreateApplicationFail { static readonly type = '[parent] create Application fail'; + constructor(public payload: HttpErrorResponse) {} } export class OnCreateApplicationSuccess { static readonly type = '[parent] create Application success'; + constructor() {} } export class OnBlockParent { static readonly type = '[parent] block Parent'; + constructor(public payload: ParentBlockedData) {} } export class OnBlockParentSuccess { static readonly type = '[parent] block Parent success'; + constructor() {} } export class OnBlockParentFail { static readonly type = '[parent] block Parent fail'; + constructor(public payload: HttpErrorResponse) {} } export class OnUnblockParent { static readonly type = '[parent] unblock Parent'; + constructor(public payload: ParentBlockedData) {} } export class OnUnblockParentSuccess { static readonly type = '[parent] unblock Parent success'; + constructor() {} } export class OnUnblockParentFail { static readonly type = '[parent] unblock Parent fail'; + + constructor(public payload: HttpErrorResponse) {} +} + +export class CreateParent { + static readonly type = '[parent] create Parent'; + + constructor(public payload: ParentPayload) {} +} + +export class OnCreateParentSuccess { + static readonly type = '[parent] create Parent success'; + + constructor(public payload: Parent) {} +} + +export class OnCreateParentFail { + static readonly type = '[parent] create Parent fail'; + constructor(public payload: HttpErrorResponse) {} } diff --git a/src/app/shared/store/parent.state.ts b/src/app/shared/store/parent.state.ts index d7f045ad0..e35408967 100644 --- a/src/app/shared/store/parent.state.ts +++ b/src/app/shared/store/parent.state.ts @@ -12,6 +12,7 @@ import { Application } from 'shared/models/application.model'; import { Child } from 'shared/models/child.model'; import { Favorite } from 'shared/models/favorite.model'; import { TruncatedItem } from 'shared/models/item.model'; +import { Parent } from 'shared/models/parent.model'; import { Rate } from 'shared/models/rating'; import { SearchResponse } from 'shared/models/search.model'; import { WorkshopCard } from 'shared/models/workshop.model'; @@ -20,6 +21,8 @@ import { ChildrenService } from 'shared/services/children/children.service'; import { ParentService } from 'shared/services/parent/parent.service'; import { RatingService } from 'shared/services/rating/rating.service'; import { FavoriteWorkshopsService } from 'shared/services/workshops/favorite-workshops/favorite-workshops.service'; +import { OnCreateProviderFail } from 'shared/store/provider.actions'; +import { CheckAuth } from 'shared/store/registration.actions'; import { MarkFormDirty, ShowMessageBar } from './app.actions'; import { CreateApplication, @@ -60,7 +63,10 @@ import { OnUpdateChildFail, OnUpdateChildSuccess, ResetSelectedChild, - UpdateChild + UpdateChild, + CreateParent, + OnCreateParentFail, + OnCreateParentSuccess } from './parent.actions'; export interface ParentStateModel { @@ -212,9 +218,14 @@ export class ParentState { @Action(GetUsersChildren) getUsersChildren({ patchState }: StateContext, { parameters }: GetUsersChildren): Observable> { patchState({ isLoading: true }); - return this.childrenService - .getUsersChildren(parameters) - .pipe(tap((children: SearchResponse) => patchState({ children: children ?? EMPTY_RESULT, isLoading: false }))); + return this.childrenService.getUsersChildren(parameters).pipe( + tap((children: SearchResponse) => + patchState({ + children: children ?? EMPTY_RESULT, + isLoading: false + }) + ) + ); } @Action(GetUsersChildById) @@ -228,9 +239,14 @@ export class ParentState { @Action(GetAllUsersChildren) getAllUsersChildren({ patchState }: StateContext, {}: GetAllUsersChildren): Observable> { patchState({ isLoading: true }); - return this.childrenService - .getAllUsersChildren() - .pipe(tap((children: SearchResponse) => patchState({ children: children ?? EMPTY_RESULT, isLoading: false }))); + return this.childrenService.getAllUsersChildren().pipe( + tap((children: SearchResponse) => + patchState({ + children: children ?? EMPTY_RESULT, + isLoading: false + }) + ) + ); } @Action(GetAllUsersChildrenByParentId) @@ -259,7 +275,13 @@ export class ParentState { @Action(OnDeleteChildSuccess) onDeleteChildSuccess({ dispatch }: StateContext, { parameters }: OnDeleteChildSuccess): void { - dispatch([new ShowMessageBar({ message: SnackbarText.deleteChild, type: 'success' }), new GetUsersChildren(parameters)]); + dispatch([ + new ShowMessageBar({ + message: SnackbarText.deleteChild, + type: 'success' + }), + new GetUsersChildren(parameters) + ]); } @Action(UpdateChild) @@ -365,7 +387,13 @@ export class ParentState { @Action(OnDeleteChildSuccess) onDeleteRatingSuccess({ dispatch }: StateContext, { parameters }: OnDeleteChildSuccess): void { - dispatch([new ShowMessageBar({ message: SnackbarText.deleteChild, type: 'success' }), new GetUsersChildren(parameters)]); + dispatch([ + new ShowMessageBar({ + message: SnackbarText.deleteChild, + type: 'success' + }), + new GetUsersChildren(parameters) + ]); } @Action(CreateApplication) @@ -404,7 +432,13 @@ export class ParentState { @Action(OnCreateApplicationSuccess) onCreateApplicationSuccess({ dispatch }: StateContext, {}: OnCreateApplicationSuccess): void { - dispatch([new ShowMessageBar({ message: SnackbarText.createApplication, type: 'success' }), new MarkFormDirty(false)]); + dispatch([ + new ShowMessageBar({ + message: SnackbarText.createApplication, + type: 'success' + }), + new MarkFormDirty(false) + ]); this.router.navigate(['']); } @@ -448,4 +482,30 @@ export class ParentState { OnUnblockParentFail({ dispatch }: StateContext, { payload }: OnUnblockParentFail): void { dispatch(new ShowMessageBar({ message: payload.error, type: 'error' })); } + + @Action(CreateParent) + createParent({ dispatch }: StateContext, { payload }: CreateParent): Observable { + return this.parentService.createParent(payload).pipe( + tap((res: Parent) => dispatch(new OnCreateParentSuccess(res))), + catchError((error) => dispatch(new OnCreateParentFail(error))) + ); + } + + @Action(OnCreateParentSuccess) + onCreateParentSuccess({ dispatch }: StateContext, { payload }: OnCreateParentSuccess): void { + dispatch(new CheckAuth()).subscribe(() => this.router.navigate(['/personal-cabinet/config'])); + dispatch([ + new ShowMessageBar({ + message: SnackbarText.createUser, + type: 'success' + }), + new MarkFormDirty(false) + ]); + } + + @Action(OnCreateParentFail) + onCreateParentFail({ dispatch }: StateContext, { payload }: OnCreateProviderFail): void { + const message = SnackbarText.error; + dispatch(new ShowMessageBar({ message, type: 'error' })); + } } diff --git a/src/app/shared/store/provider.state.ts b/src/app/shared/store/provider.state.ts index 36c8db7a0..2e254710a 100644 --- a/src/app/shared/store/provider.state.ts +++ b/src/app/shared/store/provider.state.ts @@ -443,7 +443,7 @@ export class ProviderState { } @Action(OnCreateProviderSuccess) - onCreateProviderSuccess({ dispatch }: StateContext, {}: OnCreateProviderSuccess): void { + onCreateProviderSuccess({ dispatch }: StateContext, { payload }: OnCreateProviderSuccess): void { dispatch(new CheckAuth()).subscribe(() => this.router.navigate(['/personal-cabinet/provider/info'])); dispatch([ new ShowMessageBar({ diff --git a/src/app/shared/store/registration.state.ts b/src/app/shared/store/registration.state.ts index a849053a1..e554c3246 100644 --- a/src/app/shared/store/registration.state.ts +++ b/src/app/shared/store/registration.state.ts @@ -179,12 +179,13 @@ export class RegistrationState { @Action(CheckRegistration) checkRegistration({ dispatch, getState, patchState }: StateContext): void { const state = getState(); - if (state.user.isRegistered) { dispatch(new GetProfile()); patchState({ isAuthorizationLoading: false }); } else { - this.router.navigate(['/create-provider', ModeConstants.NEW]).finally(() => patchState({ isAuthorizationLoading: false })); + this.router + .navigate([state.user.role === Role.parent ? '/create-parent' : '/create-provider', ModeConstants.NEW]) + .finally(() => patchState({ isAuthorizationLoading: false })); } } diff --git a/src/app/shell/details/side-menu/actions/actions.component.ts b/src/app/shell/details/side-menu/actions/actions.component.ts index aec8e5e33..57f8e2887 100644 --- a/src/app/shell/details/side-menu/actions/actions.component.ts +++ b/src/app/shell/details/side-menu/actions/actions.component.ts @@ -77,7 +77,9 @@ export class ActionsComponent implements OnInit, OnDestroy { this.role$.pipe(takeUntil(this.destroy$)).subscribe((role) => (this.role = role)); this.parent$.pipe(takeUntil(this.destroy$)).subscribe((parent) => (this.parentId = parent.id)); this.selectedProvider$.pipe(takeUntil(this.destroy$)).subscribe((provider) => (this.selectedProviderId = provider.id)); - this.store.dispatch(new GetBlockedParents(this.selectedProviderId, this.parentId)); + if (this.parentId) { + this.store.dispatch(new GetBlockedParents(this.selectedProviderId, this.parentId)); + } this.isBlocked$.pipe(takeUntil(this.destroy$)).subscribe((blockedParent) => (this.isBlocked = blockedParent !== null)); combineLatest([this.favoriteWorkshops$, this.route.params]) .pipe(takeUntil(this.destroy$)) @@ -93,7 +95,8 @@ export class ActionsComponent implements OnInit, OnDestroy { } public onOpenDialog(type: ModalConfirmationDescription): void { - if (this.role === Role.unauthorized) { + const isRegistered = this.store.selectSnapshot(RegistrationState.isRegistered); + if (this.role === Role.unauthorized || !isRegistered) { this.dialog.open(UnregisteredUserWarningModalComponent, { autoFocus: false, restoreFocus: false, diff --git a/src/app/shell/personal-cabinet/parent/create-parent/create-parent.component.html b/src/app/shell/personal-cabinet/parent/create-parent/create-parent.component.html new file mode 100644 index 000000000..932f71ca4 --- /dev/null +++ b/src/app/shell/personal-cabinet/parent/create-parent/create-parent.component.html @@ -0,0 +1,56 @@ +
+
+
+

+ {{'FORMS.HEADERS.NEW_USER' | translate | uppercase }} +

+
+
+
+ + + + {{ 'FORMS.CONTROL_VALUES.MALE' | translate }} + {{ 'FORMS.CONTROL_VALUES.FEMALE' | translate }} + + + + + + + + + + + + + + + + +
+ + {{ 'AGREEMENTS.I_AGREE_FOR_DATA_USE' | translate }} + + + {{ 'AGREEMENTS.I_AM_NOT_ROBOT' | translate }} + +
+
+ +
+ +
+
diff --git a/src/app/shell/personal-cabinet/parent/create-parent/create-parent.component.scss b/src/app/shell/personal-cabinet/parent/create-parent/create-parent.component.scss new file mode 100644 index 000000000..f51e53c73 --- /dev/null +++ b/src/app/shell/personal-cabinet/parent/create-parent/create-parent.component.scss @@ -0,0 +1,15 @@ +@import 'src/app/shared/styles/create-form.scss'; +@import 'src/app/shared/styles/validation-form.scss'; +@import 'src/app/shared/styles/create-form-wrapper.scss'; +@import 'src/app/shared/styles/buttons.scss'; +.wrapper { + padding: 1rem 5rem; + margin-bottom: 2rem; + &-title { + text-align: center; + } +} +.step { + padding: 2px !important; +} + diff --git a/src/app/shell/personal-cabinet/parent/create-parent/create-parent.component.spec.ts b/src/app/shell/personal-cabinet/parent/create-parent/create-parent.component.spec.ts new file mode 100644 index 000000000..8e2529f1a --- /dev/null +++ b/src/app/shell/personal-cabinet/parent/create-parent/create-parent.component.spec.ts @@ -0,0 +1,98 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatLegacyDialogModule } from '@angular/material/legacy-dialog'; +import { RouterTestingModule } from '@angular/router/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { of } from 'rxjs'; +import { ConfirmationModalWindowComponent } from 'shared/components/confirmation-modal-window/confirmation-modal-window.component'; +import { Constants } from 'shared/constants/constants'; +import { ModalConfirmationType } from 'shared/enum/modal-confirmation'; +import { CreateParent } from 'shared/store/parent.actions'; +import { Store, NgxsModule } from '@ngxs/store'; +import { ReactiveFormsModule, FormsModule, FormBuilder, FormControl } from '@angular/forms'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { + MatLegacyDialogModule as MatDialogModule, + MatLegacyDialogRef as MatDialogRef, + MatLegacyDialog as MatDialog +} from '@angular/material/legacy-dialog'; +import { CreateParentComponent } from './create-parent.component'; + +describe('CreateParentComponent', () => { + let component: CreateParentComponent; + let fixture: ComponentFixture; + let fb: FormBuilder; + let store: Store; + let expectingMatDialogData: object; + let matDialogSpy: jest.SpyInstance; + let matDialog: MatDialog; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [CreateParentComponent], + imports: [ + NgxsModule.forRoot([]), + ReactiveFormsModule, + FormsModule, + BrowserAnimationsModule, + RouterTestingModule, + BrowserAnimationsModule, + MatDialogModule, + MatLegacyDialogModule, + TranslateModule.forRoot() + ] + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(CreateParentComponent); + store = TestBed.inject(Store); + component = fixture.componentInstance; + fb = TestBed.inject(FormBuilder); + matDialog = TestBed.inject(MatDialog); + component.userCreateFormGroup = fb.group({ + gender: new FormControl(''), + dateOfBirth: new FormControl(''), + phoneNumber: new FormControl('') + }); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should initialize form group on init', () => { + component.ngOnInit(); + expect(component.userCreateFormGroup).toBeTruthy(); + expect(component.userCreateFormGroup.controls.dateOfBirth).toBeTruthy(); + expect(component.userCreateFormGroup.controls.gender).toBeTruthy(); + expect(component.userCreateFormGroup.controls.phoneNumber).toBeTruthy(); + }); + + it('should dispatch CreateParent action on form submit', () => { + jest.spyOn(store, 'dispatch'); + component.userCreateFormGroup.setValue({ + dateOfBirth: '2021-01-01', + gender: 'male', + phoneNumber: '1234567890' + }); + + component.onSubmit(); + expect(store.dispatch).toHaveBeenCalledWith(expect.any(CreateParent)); + }); + + it('should open confirmation dialog on cancel if not registered', () => { + expectingMatDialogData = { + width: Constants.MODAL_SMALL, + data: { + type: ModalConfirmationType.leaveRegistration, + property: '' + } + }; + matDialogSpy = jest.spyOn(matDialog, 'open').mockReturnValue({ + afterClosed: () => of(true) + } as MatDialogRef); + component.onCancel(); + expect(matDialogSpy).toHaveBeenCalledTimes(1); + expect(matDialogSpy).toHaveBeenCalledWith(ConfirmationModalWindowComponent, expectingMatDialogData); + }); +}); diff --git a/src/app/shell/personal-cabinet/parent/create-parent/create-parent.component.ts b/src/app/shell/personal-cabinet/parent/create-parent/create-parent.component.ts new file mode 100644 index 000000000..8e53772e5 --- /dev/null +++ b/src/app/shell/personal-cabinet/parent/create-parent/create-parent.component.ts @@ -0,0 +1,135 @@ +import { AfterViewInit, Component, OnDestroy, OnInit } from '@angular/core'; +import { filter, switchMap, takeUntil } from 'rxjs/operators'; +import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { Select, Store } from '@ngxs/store'; +import { Observable } from 'rxjs'; +import { ActivatedRoute, Router } from '@angular/router'; +import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog'; + +import { SnackbarText } from 'shared/enum/enumUA/message-bar'; +import { Role, Subrole } from 'shared/enum/role'; +import { CreateParent } from 'shared/store/parent.actions'; +import { RegistrationState } from 'shared/store/registration.state'; +import { Util } from 'shared/utils/utils'; +import { Parent, ParentPayload } from 'shared/models/parent.model'; +import { NavigationBarService } from 'shared/services/navigation-bar/navigation-bar.service'; +import { ValidationConstants } from 'shared/constants/validation'; +import { User } from 'shared/models/user.model'; +import { ConfirmationModalWindowComponent } from 'shared/components/confirmation-modal-window/confirmation-modal-window.component'; +import { Constants } from 'shared/constants/constants'; +import { ModalConfirmationType } from 'shared/enum/modal-confirmation'; +import { ClearMessageBar, MarkFormDirty, ShowMessageBar } from 'shared/store/app.actions'; +import { CreateFormComponent } from '../../shared-cabinet/create-form/create-form.component'; + +@Component({ + selector: 'app-create-parent', + templateUrl: './create-parent.component.html', + styleUrls: ['./create-parent.component.scss'] +}) +export class CreateParentComponent extends CreateFormComponent implements OnInit, OnDestroy, AfterViewInit { + @Select(RegistrationState.user) + private user$: Observable; + + protected user: User; + protected parent: Parent; + protected isAgreed: boolean; + protected isNotRobot: boolean; + protected userCreateFormGroup: FormGroup; + protected role: Role; + protected subrole: Subrole; + protected maxDate: Date = Util.getMaxBirthDate(ValidationConstants.AGE_MAX); + protected minDate: Date = Util.getMinBirthDate(ValidationConstants.BIRTH_AGE_MAX); + protected RobotFormControl = new FormControl(false); + protected AgreementFormControl = new FormControl(false); + protected readonly validationConstants = ValidationConstants; + protected readonly Role = Role; + + constructor( + protected store: Store, + protected route: ActivatedRoute, + protected navigationBarService: NavigationBarService, + private fb: FormBuilder, + private router: Router, + private matDialog: MatDialog + ) { + super(store, route, navigationBarService); + + this.userCreateFormGroup = this.fb.group({ + dateOfBirth: new FormControl('', Validators.required), + gender: new FormControl('', Validators.required), + phoneNumber: new FormControl('', Validators.required) + }); + } + + public ngOnInit(): void { + this.user$ + .pipe( + filter((user: User) => !!user), + takeUntil(this.destroy$) + ) + .subscribe(() => { + this.role = this.store.selectSnapshot(RegistrationState.role); + this.subrole = this.store.selectSnapshot(RegistrationState.subrole); + this.subscribeOnDirtyForm(this.userCreateFormGroup); + this.setEditMode(); + }); + this.RobotFormControl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((val: boolean) => (this.isNotRobot = val)); + this.AgreementFormControl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((val: boolean) => (this.isAgreed = val)); + } + + public ngOnDestroy(): void { + super.ngOnDestroy(); + const isRegistered = this.store.selectSnapshot(RegistrationState.isRegistered); + if (!isRegistered) { + this.store.dispatch( + new ShowMessageBar({ + message: SnackbarText.completeUserRegistration, + type: 'warningYellow', + verticalPosition: 'bottom', + infinityDuration: true, + unclosable: true + }) + ); + } + } + + public ngAfterViewInit(): void { + this.store.dispatch(new ClearMessageBar()); + } + + public onCancel(): void { + const isRegistered = this.store.selectSnapshot(RegistrationState.isRegistered); + + if (!isRegistered) { + this.matDialog + .open(ConfirmationModalWindowComponent, { + width: Constants.MODAL_SMALL, + data: { + type: ModalConfirmationType.leaveRegistration, + property: '' + } + }) + .afterClosed() + .pipe( + filter(Boolean), + switchMap(() => this.store.dispatch(new MarkFormDirty(false))) + ) + .subscribe(() => this.router.navigate([''])); + } else { + this.router.navigate(['/personal-cabinet/parent/config']); + } + } + + public onSubmit(): void { + const parentPayload: ParentPayload = { + dateOfBirth: this.userCreateFormGroup.controls.dateOfBirth.value, + gender: this.userCreateFormGroup.controls.gender.value, + phoneNumber: this.userCreateFormGroup.controls.phoneNumber.value + }; + this.store.dispatch(new CreateParent(parentPayload)); + } + + public setEditMode(): void {} + + public addNavPath(): void {} +} diff --git a/src/app/shell/personal-cabinet/parent/create-parent/create-parent.guard.spec.ts b/src/app/shell/personal-cabinet/parent/create-parent/create-parent.guard.spec.ts new file mode 100644 index 000000000..fe74ffb5d --- /dev/null +++ b/src/app/shell/personal-cabinet/parent/create-parent/create-parent.guard.spec.ts @@ -0,0 +1,47 @@ +import { TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; +import { NgxsModule, Store } from '@ngxs/store'; +import { of } from 'rxjs'; +import { User } from 'shared/models/user.model'; + +import { CreateParentGuard } from './create-parent.guard'; + +describe('CreateParentGuard', () => { + let guard: CreateParentGuard; + let store: Store; + let router: Router; + let mockUser: User; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [NgxsModule.forRoot([])] + }); + + guard = TestBed.inject(CreateParentGuard); + store = TestBed.inject(Store); + router = TestBed.inject(Router); + + mockUser = { + dateOfBirth: '01/01/2004', + firstName: 'ГАв', + id: '23', + isBlocked: false, + isRegistered: false, + lastName: 'фівв', + role: 'parent' + }; + }); + + it('should be created', () => { + expect(guard).toBeTruthy(); + }); + + it('should return TRUE if canLoad called if user is parent and not registered', (done) => { + jest.spyOn(store, 'select').mockReturnValue(of(mockUser)); + const canLoad = guard.canLoad(); + canLoad.subscribe((value) => { + expect(value).toBeTruthy(); + done(); + }); + }); +}); diff --git a/src/app/shell/personal-cabinet/parent/create-parent/create-parent.guard.ts b/src/app/shell/personal-cabinet/parent/create-parent/create-parent.guard.ts new file mode 100644 index 000000000..5e07c499c --- /dev/null +++ b/src/app/shell/personal-cabinet/parent/create-parent/create-parent.guard.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { filter, map } from 'rxjs/operators'; +import { User } from 'shared/models/user.model'; +import { Role } from 'shared/enum/role'; +import { Select } from '@ngxs/store'; +import { RegistrationState } from 'shared/store/registration.state'; + +@Injectable({ + providedIn: 'root' +}) +export class CreateParentGuard { + @Select(RegistrationState.user) + private user$: Observable; + public canLoad(): Observable { + return this.user$.pipe( + filter((user: User) => !!user), + map((user: User) => user.role === Role.parent && user.isRegistered === false) + ); + } +} diff --git a/src/app/shell/personal-cabinet/parent/parent.module.ts b/src/app/shell/personal-cabinet/parent/parent.module.ts index 6515ff809..625413311 100644 --- a/src/app/shell/personal-cabinet/parent/parent.module.ts +++ b/src/app/shell/personal-cabinet/parent/parent.module.ts @@ -4,9 +4,9 @@ import { FlexLayoutModule } from '@angular/flex-layout'; import { RouterModule } from '@angular/router'; import { FormsModule } from '@angular/forms'; import { TranslateModule } from '@ngx-translate/core'; +import { SharedModule } from 'shared/shared.module'; +import { MaterialModule } from 'shared/modules/material.module'; import { SharedCabinetModule } from '../shared-cabinet/shared-cabinet.module'; -import { SharedModule } from '../../../shared/shared.module'; -import { MaterialModule } from '../../../shared/modules/material.module'; import { ParentRoutingModule } from './parent-routing.module'; import { ChildFormComponent } from './create-child/child-form/child-form.component'; import { CreateChildComponent } from './create-child/create-child.component'; @@ -16,6 +16,7 @@ import { FavoriteWorkshopsComponent } from './favorite-workshops/favorite-worksh import { ParentApplicationsComponent } from './parent-applications/parent-applications.component'; import { ChildCardComponent } from './children/child-card/child-card.component'; import { PersonCardComponent } from './create-application/person-card/person-card.component'; +import { CreateParentComponent } from './create-parent/create-parent.component'; @NgModule({ declarations: [ @@ -26,7 +27,8 @@ import { PersonCardComponent } from './create-application/person-card/person-car FavoriteWorkshopsComponent, ParentApplicationsComponent, ChildCardComponent, - PersonCardComponent + PersonCardComponent, + CreateParentComponent ], imports: [ CommonModule, diff --git a/src/app/shell/personal-cabinet/personal-cabinet.guard.spec.ts b/src/app/shell/personal-cabinet/personal-cabinet.guard.spec.ts index 0bbc63aa3..3cc83ad43 100644 --- a/src/app/shell/personal-cabinet/personal-cabinet.guard.spec.ts +++ b/src/app/shell/personal-cabinet/personal-cabinet.guard.spec.ts @@ -1,15 +1,25 @@ import { TestBed } from '@angular/core/testing'; -import { Router } from '@angular/router'; +import { Router, UrlTree } from '@angular/router'; import { NgxsModule, Store } from '@ngxs/store'; import { of } from 'rxjs'; +import { User } from 'shared/models/user.model'; +import { RegistrationState } from 'shared/store/registration.state'; -import { ModeConstants } from 'shared/constants/constants'; import { PersonalCabinetGuard } from './personal-cabinet.guard'; -describe('ProviderGuard', () => { +describe('PersonalCabinetGuard', () => { let guard: PersonalCabinetGuard; let store: Store; let router: Router; + const mockUser: User = { + dateOfBirth: '01/01/2004', + firstName: 'ГАв', + id: '23', + isBlocked: false, + isRegistered: false, + lastName: 'фівв', + role: 'provider' + }; beforeEach(() => { TestBed.configureTestingModule({ @@ -25,10 +35,10 @@ describe('ProviderGuard', () => { }); it('should return TRUE if canLoad called if user is registered', (done) => { - jest.spyOn(store, 'select').mockReturnValue(of(true)); + mockUser.isRegistered = true; + jest.spyOn(store, 'select').mockReturnValue(of(mockUser)); const canLoad = guard.canLoad(); - canLoad.subscribe((value) => { expect(value).toEqual(true); done(); @@ -36,12 +46,15 @@ describe('ProviderGuard', () => { }); it('should return UrlTree if canLoad called if user is not registered', (done) => { - jest.spyOn(store, 'select').mockReturnValue(of(false)); + mockUser.isRegistered = false; + const mockUrlTree = new UrlTree(); + jest.spyOn(router, 'createUrlTree').mockReturnValue(mockUrlTree); + jest.spyOn(store, 'select').mockReturnValue(of(mockUser)); const canLoad = guard.canLoad(); - canLoad.subscribe((value) => { - expect(value).toEqual(router.createUrlTree(['/create-provider', ModeConstants.NEW])); + console.log('value' + value); + expect(value).toEqual(mockUrlTree); done(); }); }); diff --git a/src/app/shell/personal-cabinet/personal-cabinet.guard.ts b/src/app/shell/personal-cabinet/personal-cabinet.guard.ts index 19ac9abf9..20b05a1ed 100644 --- a/src/app/shell/personal-cabinet/personal-cabinet.guard.ts +++ b/src/app/shell/personal-cabinet/personal-cabinet.guard.ts @@ -1,25 +1,33 @@ import { Injectable } from '@angular/core'; import { Router, UrlTree } from '@angular/router'; import { Select } from '@ngxs/store'; -import { Observable } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; +import { Observable, of } from 'rxjs'; +import { filter, map, switchMap } from 'rxjs/operators'; import { ModeConstants } from 'shared/constants/constants'; import { RegistrationState } from 'shared/store/registration.state'; +import { User } from 'shared/models/user.model'; +import { Role } from 'shared/enum/role'; @Injectable({ providedIn: 'root' }) export class PersonalCabinetGuard { - @Select(RegistrationState.isRegistered) - private isRegistered$: Observable; + @Select(RegistrationState.user) + private user$: Observable; constructor(private router: Router) {} public canLoad(): Observable { - return this.isRegistered$.pipe( - filter((isRegistered: boolean) => isRegistered !== undefined), - map((isRegistered: boolean) => isRegistered || this.router.createUrlTree(['/create-provider', ModeConstants.NEW])) + return this.user$.pipe( + filter((user: User) => !!user), + map((user: User) => { + if (user.isRegistered) { + return user.isRegistered; + } else { + return this.router.createUrlTree([user.role === Role.parent ? '/create-parent' : '/create-provider', ModeConstants.NEW]); + } + }) ); } } diff --git a/src/app/shell/personal-cabinet/provider/create-provider/create-provider.component.ts b/src/app/shell/personal-cabinet/provider/create-provider/create-provider.component.ts index 416454b0a..4ae1f0860 100644 --- a/src/app/shell/personal-cabinet/provider/create-provider/create-provider.component.ts +++ b/src/app/shell/personal-cabinet/provider/create-provider/create-provider.component.ts @@ -97,7 +97,8 @@ export class CreateProviderComponent extends CreateFormComponent implements OnIn public ngOnDestroy(): void { super.ngOnDestroy(); - if (!this.isEditMode) { + const isRegistered = this.store.selectSnapshot(RegistrationState.isRegistered); + if (!this.isEditMode && !isRegistered) { this.store.dispatch( new ShowMessageBar({ message: SnackbarText.completeRegistration, diff --git a/src/app/shell/shell-routing.module.ts b/src/app/shell/shell-routing.module.ts index 4955c8c2f..7e4b6121e 100644 --- a/src/app/shell/shell-routing.module.ts +++ b/src/app/shell/shell-routing.module.ts @@ -32,6 +32,8 @@ import { ProviderGuard } from './personal-cabinet/provider/provider.guard'; import { CreateGuard } from './personal-cabinet/shared-cabinet/create-form/create.guard'; import { UserConfigEditComponent } from './personal-cabinet/shared-cabinet/user-config/user-config-edit/user-config-edit.component'; import { ResultComponent } from './result/result.component'; +import { CreateParentComponent } from './personal-cabinet/parent/create-parent/create-parent.component'; +import { CreateParentGuard } from './personal-cabinet/parent/create-parent/create-parent.guard'; const routes: Routes = [ { path: '', component: MainComponent }, @@ -160,6 +162,13 @@ const routes: Routes = [ canLoad: [ParentGuard], canDeactivate: [CreateGuard] }, + { + path: 'create-parent/:param', + component: CreateParentComponent, + loadChildren: () => import('./personal-cabinet/parent/parent.module').then((m) => m.ParentModule), + canDeactivate: [CreateGuard], + canLoad: [CreateParentGuard] + }, { path: '**', component: ErrorPageComponent } ]; diff --git a/src/app/shell/shell.module.ts b/src/app/shell/shell.module.ts index 6073e5499..872bda745 100644 --- a/src/app/shell/shell.module.ts +++ b/src/app/shell/shell.module.ts @@ -31,6 +31,7 @@ import { ResultComponent } from './result/result.component'; import { WorkshopCardsListComponent } from './result/workshop-cards-list/workshop-cards-list.component'; import { WorkshopMapViewListComponent } from './result/workshop-map-view-list/workshop-map-view-list.component'; import { ShellRoutingModule } from './shell-routing.module'; +import { CreateParentGuard } from './personal-cabinet/parent/create-parent/create-parent.guard'; @NgModule({ declarations: [ @@ -63,6 +64,7 @@ import { ShellRoutingModule } from './shell-routing.module'; ProviderGuard, ParentGuard, CreateProviderGuard, + CreateParentGuard, IsMobileGuard, NotProviderAdminGuard, { provide: HTTP_INTERCEPTORS, useClass: HttpTokenInterceptor, multi: true }, diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 6a795d62a..7bd861b35 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -132,7 +132,8 @@ "INFORMATION_ABOUT": "Information about", "FAVORITE": "Favorite", "LAWS_AND_REGULATIONS": "Laws and regulations", - "PROVIDER_REGISTRATION": "Provider registration" + "PROVIDER_REGISTRATION": "Provider registration", + "USER_REGISTRATION": "User registration" }, "INSTITUTION_TYPE": { "COMPLEX": "Complex", @@ -329,6 +330,7 @@ "LEGAL_ADDRESS": "Legal address", "ACTUAL_ADDRESS": "Actual address", "EDIT": "EDIT", + "NEW_USER": "NEW USER REGISTRATION", "EDIT_INFO": "EDIT INFORMATION", "SUBHEADER_TITLE": "Title", "PROVIDER_ADMINS_SUBHEADER": "Login data will be sent to the user's email for logging into the system.", @@ -747,11 +749,13 @@ "CREATE_PROVIDER_DEPUTY": "Deputy director successfully created!", "UPDATE_PROVIDER_DEPUTY": "Deputy director successfully edited!", "COMPLETE_PROVIDER_REGISTRATION": "Please complete registration of the provider.", + "COMPLETE_USER_REGISTRATION": "Please complete registration of the user.", "CREATE_RATING": "Successfully rated!", "UPDATE_PORTAL": "Portal information successfully edited", "UPDATE_USER": "Personal information successfully edited!", "BLOCK_USER": "User blocked successfully!", "UNBLOCK_USER": "User unblocked successfully!", + "CREATE_USER": "User successfully created", "ACCESS_IS_RESTRICTED": "Your access to the workshops of this institution is restricted", "ACCESS_IS_RESTRICTED_FULL_DESCRIPTION": "If you have any questions, please contact us at the contact numbers of the institution", "SEND_INVITATION": "Invitation successfully sent!", diff --git a/src/assets/i18n/uk.json b/src/assets/i18n/uk.json index c2bef0495..5cbc87d47 100644 --- a/src/assets/i18n/uk.json +++ b/src/assets/i18n/uk.json @@ -132,7 +132,8 @@ "LAWS_AND_REGULATIONS": "Нормативно-правові акти", "HISTORY_OF_CHANGES": "Історія змін", "STATISTICS": "Статистичні дані", - "PROVIDER_REGISTRATION": "Реєстрація закладу" + "PROVIDER_REGISTRATION": "Реєстрація закладу", + "USER_REGISTRATION": "Реєстрація користувача" }, "INSTITUTION_TYPE": { "COMPLEX": "Комплексний", @@ -333,7 +334,8 @@ "SUBHEADER_TITLE": "Заголовок", "PROVIDER_ADMINS_SUBHEADER": "Дані для входу будуть надіслані на електронну пошту користувача для логування в систему.", "ADD_ACHIEVEMENT": "ДОДАТИ ДОСЯГНЕННЯ", - "ADD_ADMINS_SUBHEADER": "Дані для входу будуть надіслані на електронну пошту користувача для логування в систему. Пароль є тимчасовим і повинен бути заміненим при першому вході" + "ADD_ADMINS_SUBHEADER": "Дані для входу будуть надіслані на електронну пошту користувача для логування в систему. Пароль є тимчасовим і повинен бути заміненим при першому вході", + "NEW_USER": "РЕЄСТРАЦІЯ НОВОГО КОРИСТУВАЧА" }, "LABELS": { "AGE_OF_PARTICIPANTS": "Вік учасників", @@ -749,11 +751,13 @@ "CREATE_PROVIDER_DEPUTY": "Заступника директора успішно створено!", "UPDATE_PROVIDER_DEPUTY": "Заступника директора успішно відредаговано!", "COMPLETE_PROVIDER_REGISTRATION": "Будь ласка, завершіть реєстрацію закладу.", + "COMPLETE_USER_REGISTRATION": "Будь ласка, завершіть реєстрацію користувача.", "CREATE_RATING": "Оцінка успішно поставлена!", "UPDATE_PORTAL": "Інформація про портал успішно відредагована", "UPDATE_USER": "Особиста інформація успішно відредагована!", "BLOCK_USER": "Користувач успішно заблокований!", "UNBLOCK_USER": "Користувач успішно розблокований!", + "CREATE_USER": "Користувач успішно створено", "ACCESS_IS_RESTRICTED": "Вам обмежено доступ до гуртків цього закладу", "ACCESS_IS_RESTRICTED_FULL_DESCRIPTION": "У разі виникнення запитань, зв'яжіться з нами за контактними телефонами закладу", "SEND_INVITATION": "Запрошення успішно надіслано!", From fdcaee0e40dd82be9406461c25ba0ac15baa7c77 Mon Sep 17 00:00:00 2001 From: AkunaPatlata <94176568+AkunaPatlata@users.noreply.github.com> Date: Tue, 11 Jun 2024 15:05:14 +0300 Subject: [PATCH 07/25] Dvorak / 2564 Size if Send button is fixed (#2570) --- .../shared-cabinet/messages/chat/chat.component.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/shell/personal-cabinet/shared-cabinet/messages/chat/chat.component.scss b/src/app/shell/personal-cabinet/shared-cabinet/messages/chat/chat.component.scss index 3ce1c30e0..d157a646b 100644 --- a/src/app/shell/personal-cabinet/shared-cabinet/messages/chat/chat.component.scss +++ b/src/app/shell/personal-cabinet/shared-cabinet/messages/chat/chat.component.scss @@ -122,6 +122,7 @@ line-height: 15px; padding: 10px 30px; min-width: auto; + height: fit-content; .mat-icon { display: none; @@ -143,6 +144,7 @@ .main-functional-block { display: flex; align-self: stretch; + align-items: center; } @media (max-width: 530px) { @@ -153,7 +155,7 @@ .btn { padding: 10px 16px; margin: 0 8px; - + span { display: none; } From e2e55968181242872d13550445432463b31882ce Mon Sep 17 00:00:00 2001 From: AkunaPatlata <94176568+AkunaPatlata@users.noreply.github.com> Date: Thu, 13 Jun 2024 14:21:26 +0300 Subject: [PATCH 08/25] Dvorak/2568 the indication of the currency is shown correctly (#2572) * Not final result * Dvorak / 2568 The indication of currency positioning fixed * Minor fix * Code refactored --- .../create-about-form.component.html | 8 +++++-- .../create-about-form.component.scss | 22 ++++++++++++++++++- src/assets/i18n/en.json | 2 ++ src/assets/i18n/uk.json | 2 ++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/create-about-form.component.html b/src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/create-about-form.component.html index 58aaa2f66..0af4ccdaf 100644 --- a/src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/create-about-form.component.html +++ b/src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/create-about-form.component.html @@ -160,13 +160,17 @@ [min]="validationConstants.MIN_PRICE" [max]="validationConstants.MAX_PRICE" /> +

{{ 'FORMS.UAH' | translate }}

-

{{ 'FORMS.UAH_FOR' | translate }}

+
+

{{ 'FORMS.UAH_FOR' | translate }}

+

{{ 'FORMS.FOR' | translate }}

+
diff --git a/src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/create-about-form.component.scss b/src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/create-about-form.component.scss index 369762f18..58dbc5cee 100644 --- a/src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/create-about-form.component.scss +++ b/src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/create-about-form.component.scss @@ -16,6 +16,12 @@ .price-text { width: 65px; + display: block; +} + +.price-only-uah, +.for-only-text { + display: none; } .price-input { @@ -34,7 +40,6 @@ } .price::after { - content: 'грн'; font-weight: 700; color: #aaaaaa; padding: 5px; @@ -42,6 +47,15 @@ width: 65px; } + .price-text { + display: none; + } + + .price-only-uah, + .for-only-text { + display: block; + } + .price, .radio-button-wrap { place-content: flex-start !important; @@ -52,6 +66,12 @@ } } +@media (max-width: 410px) { + .payment-type-input { + width: 90%; + } +} + :host ::ng-deep .mat-form-field-infix { width: auto !important; } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 7bd861b35..f078657a8 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -432,6 +432,8 @@ "SELECT_ORGANIZATION_TYPE": "Select institution type" }, "UAH_FOR": "UAH for", + "UAH": "UAH", + "FOR": "for", "VALIDATIONS": { "INVALID_BIRTHDAY": "Date of birth is out of range.", "INVALID_CHARACTERS": ", please use only cyrillic", diff --git a/src/assets/i18n/uk.json b/src/assets/i18n/uk.json index 5cbc87d47..11b84a0fe 100644 --- a/src/assets/i18n/uk.json +++ b/src/assets/i18n/uk.json @@ -432,6 +432,8 @@ "SELECT_ORGANIZATION_TYPE": "Оберіть тип організації" }, "UAH_FOR": "грн за", + "UAH": "грн", + "FOR": "за", "VALIDATIONS": { "INVALID_BIRTHDAY": "Дата народження виходить за межі допустимих.", "INVALID_CHARACTERS": ", використовуйте, будь ласка, тільки кирилицю", From caab4220bb1ad9cc354c1c8cab1497d616e4bce4 Mon Sep 17 00:00:00 2001 From: AkunaPatlata <94176568+AkunaPatlata@users.noreply.github.com> Date: Thu, 13 Jun 2024 14:25:38 +0300 Subject: [PATCH 09/25] Dvorak / 2565 Message is being send by pressing Enter (#2567) * Dvorak / 2565 Message is being send by pressing Enter * Tests added * Modified tests * jest.spyOn added --- angular.json | 17 ++--------- .../messages/chat/chat.component.html | 20 ++++++++----- .../messages/chat/chat.component.spec.ts | 28 +++++++++++++++++++ .../messages/chat/chat.component.ts | 7 +++++ 4 files changed, 51 insertions(+), 21 deletions(-) diff --git a/angular.json b/angular.json index 245576bc6..0d8a628e4 100644 --- a/angular.json +++ b/angular.json @@ -2,12 +2,7 @@ "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "cli": { "analytics": false, - "schematicCollections": [ - "@cypress/schematic", - "@angular-eslint/schematics", - "@angular-eslint/schematics", - "@schematics/angular" - ] + "schematicCollections": ["@cypress/schematic", "@angular-eslint/schematics", "@angular-eslint/schematics", "@schematics/angular"] }, "version": 1, "newProjectRoot": "projects", @@ -40,10 +35,7 @@ "src/favicon.ico", "src/assets" ], - "styles": [ - "./node_modules/leaflet/dist/leaflet.css", - "src/styles.scss" - ], + "styles": ["./node_modules/leaflet/dist/leaflet.css", "src/styles.scss"], "scripts": [], "vendorChunk": true, "extractLicenses": false, @@ -144,10 +136,7 @@ "lint": { "builder": "@angular-eslint/builder:lint", "options": { - "lintFilePatterns": [ - "src/**/*.ts", - "src/**/*.html" - ] + "lintFilePatterns": ["src/**/*.ts", "src/**/*.html"] } }, "cypress-run": { diff --git a/src/app/shell/personal-cabinet/shared-cabinet/messages/chat/chat.component.html b/src/app/shell/personal-cabinet/shared-cabinet/messages/chat/chat.component.html index fd26e44ce..9f57ab112 100644 --- a/src/app/shell/personal-cabinet/shared-cabinet/messages/chat/chat.component.html +++ b/src/app/shell/personal-cabinet/shared-cabinet/messages/chat/chat.component.html @@ -11,7 +11,8 @@

@@ -26,9 +27,7 @@

diff --git a/src/app/shared/components/filters-list/city-filter/city-filter.component.html b/src/app/shared/components/filters-list/city-filter/city-filter.component.html index 9cb36a865..bc9100973 100644 --- a/src/app/shared/components/filters-list/city-filter/city-filter.component.html +++ b/src/app/shared/components/filters-list/city-filter/city-filter.component.html @@ -1,13 +1,32 @@ - - - + + {{ codeficator.settlement | translate }}
-
class="step-input" placeholder="{{ 'FORMS.PLACEHOLDERS.SELECT_ACHIEVEMENT' | translate }}"> -
+
{{ achievement.title }}
- + @@ -41,7 +50,12 @@

{{ 'FORMS.HEADERS.ADD_ACHIEVEMENT' | translate }}

*ngFor="let child of childrenFormControl.value" [removable]="true" (removed)="onRemoveItem(child, 'children')"> -
+
{{ child | getFullName }}
cancel @@ -49,7 +63,12 @@

{{ 'FORMS.HEADERS.ADD_ACHIEVEMENT' | translate }}

-
+
{{ child | getFullName }}
@@ -62,7 +81,9 @@

{{ 'FORMS.HEADERS.ADD_ACHIEVEMENT' | translate }}

{{ 'BANNERS.ADD_ACHIEVEMENT_PARTICIPANTS' | translate }} - +
@@ -86,7 +107,12 @@

{{ 'FORMS.HEADERS.ADD_ACHIEVEMENT' | translate }}

-
+
{{ teacher | getFullName }}
cancel @@ -95,7 +121,12 @@

{{ 'FORMS.HEADERS.ADD_ACHIEVEMENT' | translate }}

-
+
{{ teacher | getFullName }}
@@ -125,7 +156,9 @@

{{ 'FORMS.HEADERS.ADD_ACHIEVEMENT' | translate }}

- + {{ AchievementFormGroup.get('title').value.length }}/{{ validationConstants.MAX_DESCRIPTION_LENGTH_2000 }} diff --git a/src/app/shell/personal-cabinet/provider/create-achievement/create-achievement.component.ts b/src/app/shell/personal-cabinet/provider/create-achievement/create-achievement.component.ts index bd3258040..b2d468ee8 100644 --- a/src/app/shell/personal-cabinet/provider/create-achievement/create-achievement.component.ts +++ b/src/app/shell/personal-cabinet/provider/create-achievement/create-achievement.component.ts @@ -3,11 +3,12 @@ import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms' import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog'; import { ActivatedRoute, Router } from '@angular/router'; import { Select, Store } from '@ngxs/store'; -import { combineLatest, Observable, Subject } from 'rxjs'; +import { combineLatest, Observable } from 'rxjs'; import { filter, takeUntil } from 'rxjs/operators'; -import { MUST_CONTAIN_LETTERS } from 'shared/constants/regex-constants'; + import { ConfirmationModalWindowComponent } from 'shared/components/confirmation-modal-window/confirmation-modal-window.component'; import { Constants } from 'shared/constants/constants'; +import { MUST_CONTAIN_LETTERS } from 'shared/constants/regex-constants'; import { ValidationConstants } from 'shared/constants/validation'; import { NavBarName } from 'shared/enum/enumUA/navigation-bar'; import { ModalConfirmationType } from 'shared/enum/modal-confirmation'; diff --git a/src/app/shell/personal-cabinet/provider/create-address-form/create-address-form.component.html b/src/app/shell/personal-cabinet/provider/create-address-form/create-address-form.component.html index ca6c2d1cf..a01ff0380 100644 --- a/src/app/shell/personal-cabinet/provider/create-address-form/create-address-form.component.html +++ b/src/app/shell/personal-cabinet/provider/create-address-form/create-address-form.component.html @@ -1,4 +1,5 @@ - - -
+
- - - + + {{ codeficator.settlement | translate }}
{{ codeficator.fullName }} @@ -37,8 +44,10 @@
- +

{{ context.settlementControl.value?.fullName }} @@ -52,17 +61,20 @@ - + - + - + - \ No newline at end of file + diff --git a/src/app/shell/personal-cabinet/provider/create-provider-admin/create-provider-admin.component.ts b/src/app/shell/personal-cabinet/provider/create-provider-admin/create-provider-admin.component.ts index 400d87ae7..4819337b8 100644 --- a/src/app/shell/personal-cabinet/provider/create-provider-admin/create-provider-admin.component.ts +++ b/src/app/shell/personal-cabinet/provider/create-provider-admin/create-provider-admin.component.ts @@ -39,11 +39,6 @@ const defaultValidators: ValidatorFn[] = [ styleUrls: ['./create-provider-admin.component.scss'] }) export class CreateProviderAdminComponent extends CreateFormComponent implements OnInit, OnDestroy { - public readonly validationConstants = ValidationConstants; - public readonly mailFormPlaceholder = Constants.MAIL_FORMAT_PLACEHOLDER; - public readonly WorkshopDeclination = WorkshopDeclination; - public readonly providerAdminRole = ProviderAdminRole; - @Select(RegistrationState.provider) public provider$: Observable; @Select(ProviderState.truncated) @@ -51,9 +46,10 @@ export class CreateProviderAdminComponent extends CreateFormComponent implements @Select(ProviderState.selectedProviderAdmin) public providerAdmin$: Observable; - private provider: Provider; - private providerRole: ProviderAdminRole; - private providerAdminId: string; + public readonly validationConstants = ValidationConstants; + public readonly mailFormPlaceholder = Constants.MAIL_FORMAT_PLACEHOLDER; + public readonly WorkshopDeclination = WorkshopDeclination; + public readonly providerAdminRole = ProviderAdminRole; public ProviderAdminFormGroup: FormGroup; public managedWorkshopIds: string[]; @@ -61,6 +57,10 @@ export class CreateProviderAdminComponent extends CreateFormComponent implements public entityControl = new FormControl(); public formTitle: string; + private provider: Provider; + private providerRole: ProviderAdminRole; + private providerAdminId: string; + constructor( protected store: Store, protected route: ActivatedRoute, diff --git a/src/app/shell/personal-cabinet/provider/create-provider/create-contacts-form/create-contacts-form.component.html b/src/app/shell/personal-cabinet/provider/create-provider/create-contacts-form/create-contacts-form.component.html index d050db592..e64ec4a77 100644 --- a/src/app/shell/personal-cabinet/provider/create-provider/create-contacts-form/create-contacts-form.component.html +++ b/src/app/shell/personal-cabinet/provider/create-provider/create-contacts-form/create-contacts-form.component.html @@ -2,7 +2,9 @@

{{ 'FORMS.HEADERS.LEGAL_ADDRESS' | translate }}

-
@@ -17,10 +19,11 @@

{{ 'FORMS.HEADERS.ACTUAL_ADDRESS' | translate }}

-
+
-
diff --git a/src/app/shell/personal-cabinet/provider/create-provider/create-contacts-form/create-contacts-form.component.ts b/src/app/shell/personal-cabinet/provider/create-provider/create-contacts-form/create-contacts-form.component.ts index 33aea4d8e..ad829094b 100644 --- a/src/app/shell/personal-cabinet/provider/create-provider/create-contacts-form/create-contacts-form.component.ts +++ b/src/app/shell/personal-cabinet/provider/create-provider/create-contacts-form/create-contacts-form.component.ts @@ -18,13 +18,13 @@ export class CreateContactsFormComponent implements OnInit, OnDestroy { @Output() public passActualAddressFormGroup = new EventEmitter(); @Output() public passLegalAddressFormGroup = new EventEmitter(); - private destroy$: Subject = new Subject(); - public legalAddressFormGroup: FormGroup; public actualAddressFormGroup: FormGroup; public searchFormGroup: FormGroup; public isSameAddressControl: FormControl = new FormControl(false); + private destroy$: Subject = new Subject(); + constructor() {} public get searchActualFormGroup(): FormGroup { diff --git a/src/app/shell/personal-cabinet/provider/create-provider/create-photo-form/create-photo-form.component.ts b/src/app/shell/personal-cabinet/provider/create-provider/create-photo-form/create-photo-form.component.ts index 350f307dd..e074065da 100644 --- a/src/app/shell/personal-cabinet/provider/create-provider/create-photo-form/create-photo-form.component.ts +++ b/src/app/shell/personal-cabinet/provider/create-provider/create-photo-form/create-photo-form.component.ts @@ -12,6 +12,11 @@ import { Provider, ProviderSectionItem } from '../../../../../shared/models/prov styleUrls: ['./create-photo-form.component.scss'] }) export class CreatePhotoFormComponent implements OnInit { + @Input() public provider: Provider; + @Input() public isImagesFeature: boolean; + + @Output() public passPhotoFormGroup = new EventEmitter(); + public readonly validationConstants = ValidationConstants; public readonly cropperConfig = { @@ -25,19 +30,10 @@ export class CreatePhotoFormComponent implements OnInit { croppedQuality: CropperConfigurationConstants.croppedQuality }; - @Input() public provider: Provider; - @Input() public isImagesFeature: boolean; - - @Output() public passPhotoFormGroup = new EventEmitter(); - - private editFormGroup: FormGroup; - public PhotoFormGroup: FormGroup; public SectionItemsFormArray = new FormArray([]); - public get providerSectionItemsControl(): AbstractControl { - return this.PhotoFormGroup.get('providerSectionItems'); - } + private editFormGroup: FormGroup; constructor( private formBuilder: FormBuilder, @@ -53,6 +49,10 @@ export class CreatePhotoFormComponent implements OnInit { }); } + public get providerSectionItemsControl(): AbstractControl { + return this.PhotoFormGroup.get('providerSectionItems'); + } + public ngOnInit(): void { this.passPhotoFormGroup.emit(this.PhotoFormGroup); if (this.provider) { diff --git a/src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/create-about-form.component.ts b/src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/create-about-form.component.ts index 38e40b06a..7a2452c35 100644 --- a/src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/create-about-form.component.ts +++ b/src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/create-about-form.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { Subject } from 'rxjs'; -import { debounceTime, takeUntil } from 'rxjs/operators'; +import { takeUntil } from 'rxjs/operators'; import { Constants, CropperConfigurationConstants } from 'shared/constants/constants'; import { FormValidators, ValidationConstants } from 'shared/constants/validation'; @@ -118,6 +118,40 @@ export class CreateAboutFormComponent implements OnInit, OnDestroy { } } + /** + * This method fills inputs with information of edited workshop + */ + public activateEditMode(): void { + this.AboutFormGroup.patchValue(this.workshop, { emitEvent: false }); + if (this.workshop.coverImageId) { + this.AboutFormGroup.get('coverImageId').setValue([this.workshop.coverImageId], { emitEvent: false }); + } + if (this.workshop.price) { + this.setPriceControlValue(this.workshop.price, 'enable', false); + this.setPayRateControlValue(this.workshop.payRate, 'enable', false); + this.priceRadioBtn.setValue(true); + } else { + this.setPriceControlValue(null, 'disable', false); + this.setPayRateControlValue(null, 'disable', false); + } + + if (this.workshop.availableSeats === this.UNLIMITED_SEATS) { + this.setAvailableSeatsControlValue(null, 'disable', false); + } else { + this.setAvailableSeatsControlValue(this.availableSeats, 'enable', false); + this.availableSeatsRadioBtnControl.setValue(false); + } + + this.competitiveSelectionRadioBtn.setValue(this.workshop.competitiveSelection); + this.competitiveSelectionDescriptionFormControl = new FormControl(this.workshop.competitiveSelectionDescription, [ + Validators.pattern(MUST_CONTAIN_LETTERS), + Validators.required + ]); + if (this.workshop.competitiveSelection) { + this.AboutFormGroup.setControl('competitiveSelectionDescription', this.competitiveSelectionDescriptionFormControl); + } + } + private initForm(): void { this.AboutFormGroup = this.formBuilder.group({ title: new FormControl('', [ @@ -229,40 +263,6 @@ export class CreateAboutFormComponent implements OnInit, OnDestroy { }); } - /** - * This method fills inputs with information of edited workshop - */ - public activateEditMode(): void { - this.AboutFormGroup.patchValue(this.workshop, { emitEvent: false }); - if (this.workshop.coverImageId) { - this.AboutFormGroup.get('coverImageId').setValue([this.workshop.coverImageId], { emitEvent: false }); - } - if (this.workshop.price) { - this.setPriceControlValue(this.workshop.price, 'enable', false); - this.setPayRateControlValue(this.workshop.payRate, 'enable', false); - this.priceRadioBtn.setValue(true); - } else { - this.setPriceControlValue(null, 'disable', false); - this.setPayRateControlValue(null, 'disable', false); - } - - if (this.workshop.availableSeats === this.UNLIMITED_SEATS) { - this.setAvailableSeatsControlValue(null, 'disable', false); - } else { - this.setAvailableSeatsControlValue(this.availableSeats, 'enable', false); - this.availableSeatsRadioBtnControl.setValue(false); - } - - this.competitiveSelectionRadioBtn.setValue(this.workshop.competitiveSelection); - this.competitiveSelectionDescriptionFormControl = new FormControl(this.workshop.competitiveSelectionDescription, [ - Validators.pattern(MUST_CONTAIN_LETTERS), - Validators.required - ]); - if (this.workshop.competitiveSelection) { - this.AboutFormGroup.setControl('competitiveSelectionDescription', this.competitiveSelectionDescriptionFormControl); - } - } - /** * This method makes input enable if radiobutton value * is true and sets the value to the FormGroup @@ -278,14 +278,6 @@ export class CreateAboutFormComponent implements OnInit, OnDestroy { this.AboutFormGroup.removeControl('competitiveSelectionDescription'); } }); - - if (this.AboutFormGroup.get('competitiveSelectionDescription')) { - this.AboutFormGroup.get('competitiveSelectionDescription') - .valueChanges.pipe(debounceTime(1000), takeUntil(this.destroy$)) - .subscribe((disabilityOptionsDesc: string) => { - console.log('New description:', disabilityOptionsDesc); - }); - } } private showHintAboutClosingWorkshop(): void { diff --git a/src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/working-hours-form-wrapper/working-hours-form/working-hours-form.component.ts b/src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/working-hours-form-wrapper/working-hours-form/working-hours-form.component.ts index ef499e180..d7ff9e768 100644 --- a/src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/working-hours-form-wrapper/working-hours-form/working-hours-form.component.ts +++ b/src/app/shell/personal-cabinet/provider/create-workshop/create-about-form/working-hours-form-wrapper/working-hours-form/working-hours-form.component.ts @@ -22,7 +22,7 @@ export class WorkingHoursFormComponent implements OnInit, OnDestroy { @Output() public dataChanged = new EventEmitter(); public destroy$: Subject = new Subject(); - public days: WorkingDaysToggleValue[] = WorkingDaysValues.map((value: WorkingDaysToggleValue) => Object.assign({}, value)); + public days: WorkingDaysToggleValue[] = WorkingDaysValues.map((value: WorkingDaysToggleValue) => ({ ...value })); public workingDays: Set = new Set(); public workdaysFormControl = new FormControl(['']); public startTimeFormControl = new FormControl(''); diff --git a/src/app/shell/personal-cabinet/provider/create-workshop/create-description-form/create-description-form.component.html b/src/app/shell/personal-cabinet/provider/create-workshop/create-description-form/create-description-form.component.html index 7d51c0ffe..70c304e5c 100644 --- a/src/app/shell/personal-cabinet/provider/create-workshop/create-description-form/create-description-form.component.html +++ b/src/app/shell/personal-cabinet/provider/create-workshop/create-description-form/create-description-form.component.html @@ -1,33 +1,54 @@
- + - + - - + {{ 'NOT_AVAILABLE' | translate }} {{ 'AVAILABLE' | translate }} - - + + - +
@@ -35,9 +56,10 @@
- {{ 'FORMS.PLACEHOLDERS.KEYWORDS_START' | - translate }} {{ validationConstants.MAX_KEYWORDS_LENGTH }} - {{ 'FORMS.PLACEHOLDERS.KEYWORDS_END' | translate }} + {{ 'FORMS.PLACEHOLDERS.KEYWORDS_START' | translate }} {{ validationConstants.MAX_KEYWORDS_LENGTH }} + {{ 'FORMS.PLACEHOLDERS.KEYWORDS_END' | translate }}
@@ -46,9 +68,16 @@ close - +
diff --git a/src/app/shell/personal-cabinet/provider/create-workshop/create-workshop-address/create-workshop-address.component.html b/src/app/shell/personal-cabinet/provider/create-workshop/create-workshop-address/create-workshop-address.component.html index e5f4f79cb..aa2c3dbee 100644 --- a/src/app/shell/personal-cabinet/provider/create-workshop/create-workshop-address/create-workshop-address.component.html +++ b/src/app/shell/personal-cabinet/provider/create-workshop/create-workshop-address/create-workshop-address.component.html @@ -1,14 +1,12 @@
- +
{{ 'FORMS.VALIDATIONS.INVALID_MAP' | translate }}

{{ 'FORMS.LABELS.OR_SELECT_ON_THE_MAP' | translate }}

- +
diff --git a/src/app/shell/personal-cabinet/provider/provider-admins/provider-admins.component.scss b/src/app/shell/personal-cabinet/provider/provider-admins/provider-admins.component.scss index 1d549742f..fc4188502 100644 --- a/src/app/shell/personal-cabinet/provider/provider-admins/provider-admins.component.scss +++ b/src/app/shell/personal-cabinet/provider/provider-admins/provider-admins.component.scss @@ -62,4 +62,4 @@ text-overflow: ellipsis; white-space: nowrap; overflow: hidden; -} \ No newline at end of file +} diff --git a/src/app/shell/personal-cabinet/provider/provider-applications/provider-applications.component.ts b/src/app/shell/personal-cabinet/provider/provider-applications/provider-applications.component.ts index 864e667d6..1de0504cb 100644 --- a/src/app/shell/personal-cabinet/provider/provider-applications/provider-applications.component.ts +++ b/src/app/shell/personal-cabinet/provider/provider-applications/provider-applications.component.ts @@ -30,14 +30,14 @@ import { CabinetDataComponent } from '../../shared-cabinet/cabinet-data.componen templateUrl: './provider-applications.component.html' }) export class ProviderApplicationsComponent extends CabinetDataComponent implements OnInit, OnDestroy { - public readonly WorkshopDeclination = WorkshopDeclination; - @Select(ProviderState.truncated) public workshops$: Observable; @Select(RegistrationState.provider) private provider$: Observable; + + public readonly WorkshopDeclination = WorkshopDeclination; + public provider: Provider; - private providerId: string; public applicationParams: ApplicationFilterParameters = { property: null, @@ -49,6 +49,8 @@ export class ProviderApplicationsComponent extends CabinetDataComponent implemen from: 0 }; + private providerId: string; + constructor( protected store: Store, protected matDialog: MatDialog, @@ -58,35 +60,6 @@ export class ProviderApplicationsComponent extends CabinetDataComponent implemen super(store, matDialog); } - protected init(): void { - this.provider$.pipe(filter(Boolean), takeUntil(this.destroy$)).subscribe((provider: Provider) => { - this.provider = provider; - switch (this.subrole) { - case Subrole.None: - this.applicationParams.property = ApplicationEntityType.provider; - this.providerId = provider.id; - break; - case Subrole.ProviderDeputy: - case Subrole.ProviderAdmin: - this.applicationParams.property = ApplicationEntityType.ProviderAdmin; - this.providerId = this.store.selectSnapshot(RegistrationState.user).id; - break; - } - this.getProviderWorkshops(); - }); - this.actions$.pipe(ofActionSuccessful(BlockParent, UnBlockParent), takeUntil(this.destroy$)).subscribe(() => this.onGetApplications()); - } - - protected addNavPath(): void { - this.store.dispatch( - new PushNavPath({ - name: NavBarName.Applications, - isActive: false, - disable: true - }) - ); - } - /** * This method changes status of emitted event to "approved" * @param application event @@ -174,6 +147,35 @@ export class ProviderApplicationsComponent extends CabinetDataComponent implemen this.onGetApplications(); } + protected init(): void { + this.provider$.pipe(filter(Boolean), takeUntil(this.destroy$)).subscribe((provider: Provider) => { + this.provider = provider; + switch (this.subrole) { + case Subrole.None: + this.applicationParams.property = ApplicationEntityType.provider; + this.providerId = provider.id; + break; + case Subrole.ProviderDeputy: + case Subrole.ProviderAdmin: + this.applicationParams.property = ApplicationEntityType.ProviderAdmin; + this.providerId = this.store.selectSnapshot(RegistrationState.user).id; + break; + } + this.getProviderWorkshops(); + }); + this.actions$.pipe(ofActionSuccessful(BlockParent, UnBlockParent), takeUntil(this.destroy$)).subscribe(() => this.onGetApplications()); + } + + protected addNavPath(): void { + this.store.dispatch( + new PushNavPath({ + name: NavBarName.Applications, + isActive: false, + disable: true + }) + ); + } + private onGetApplications(): void { this.store.dispatch(new GetApplicationsByPropertyId(this.providerId, this.applicationParams)); } diff --git a/src/app/shell/personal-cabinet/provider/provider-org-info/provider-org-info.component.ts b/src/app/shell/personal-cabinet/provider/provider-org-info/provider-org-info.component.ts index ddb4674c1..cdf1b89ca 100644 --- a/src/app/shell/personal-cabinet/provider/provider-org-info/provider-org-info.component.ts +++ b/src/app/shell/personal-cabinet/provider/provider-org-info/provider-org-info.component.ts @@ -13,7 +13,7 @@ import { ProviderComponent } from '../provider.component'; styleUrls: ['./provider-org-info.component.scss'] }) export class ProviderOrgInfoComponent extends ProviderComponent implements OnInit, OnDestroy { - readonly ownershipTypes = OwnershipTypes; + public readonly ownershipTypes = OwnershipTypes; constructor( protected store: Store, @@ -22,7 +22,7 @@ export class ProviderOrgInfoComponent extends ProviderComponent implements OnIni super(store, matDialog); } - addNavPath(): void { + protected addNavPath(): void { this.store.dispatch( new PushNavPath({ name: NavBarName.ProviderInfo, @@ -32,5 +32,5 @@ export class ProviderOrgInfoComponent extends ProviderComponent implements OnIni ); } - initProviderData(): void {} + protected initProviderData(): void {} } diff --git a/src/app/shell/personal-cabinet/provider/provider.component.ts b/src/app/shell/personal-cabinet/provider/provider.component.ts index 63f9283f1..8afc68bb3 100644 --- a/src/app/shell/personal-cabinet/provider/provider.component.ts +++ b/src/app/shell/personal-cabinet/provider/provider.component.ts @@ -15,10 +15,11 @@ import { CabinetDataComponent } from '../shared-cabinet/cabinet-data.component'; }) export abstract class ProviderComponent extends CabinetDataComponent implements OnInit, OnDestroy { @Select(RegistrationState.provider) - provider$: Observable; - provider: Provider; + public provider$: Observable; @Select(ProviderState.isLoading) - isLoading$: Observable; + public isLoading$: Observable; + + public provider: Provider; constructor( protected store: Store, @@ -27,15 +28,15 @@ export abstract class ProviderComponent extends CabinetDataComponent implements super(store, matDialog); } - protected abstract initProviderData(): void; - /** * This method subscribe on provider and get its workshops */ - init(): void { + protected init(): void { this.provider$.pipe(filter(Boolean), takeUntil(this.destroy$)).subscribe((provider: Provider) => { this.provider = provider; this.initProviderData(); }); } + + protected abstract initProviderData(): void; } diff --git a/src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/child-info-box/child-info-box.component.ts b/src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/child-info-box/child-info-box.component.ts index 276c09e88..4c43cd926 100644 --- a/src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/child-info-box/child-info-box.component.ts +++ b/src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/child-info-box/child-info-box.component.ts @@ -13,25 +13,25 @@ import { Util } from 'shared/utils/utils'; styleUrls: ['./child-info-box.component.scss'] }) export class ChildInfoBoxComponent implements OnInit { - readonly gender = Gender; - readonly constants: typeof Constants = Constants; - readonly YearDeclination = YearDeclination; + @Input() public child: Child; - constructor(private detectedDevice: DetectedDeviceService) {} + public readonly gender = Gender; + public readonly constants: typeof Constants = Constants; + public readonly YearDeclination = YearDeclination; - @Input() child: Child; + public isMobile = false; + public childFullName: string; + public parentFullName: string; + public parentPhoneNumber: string; + public parentEmail: string; - isMobile = false; - childFullName: string; - parentFullName: string; - parentPhoneNumber: string; - parentEmail: string; + constructor(private detectedDevice: DetectedDeviceService) {} - get childAge(): number { + public get childAge(): number { return Util.getChildAge(this.child); } - ngOnInit(): void { + public ngOnInit(): void { this.isMobile = this.detectedDevice.checkedDevice(); this.parentFullName = Util.getFullName(this.child.parent); this.childFullName = Util.getFullName(this.child); diff --git a/src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/info-status/info-status.component.html b/src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/info-status/info-status.component.html index b9f360312..f19dee691 100644 --- a/src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/info-status/info-status.component.html +++ b/src/app/shell/personal-cabinet/shared-cabinet/applications/application-card/info-status/info-status.component.html @@ -9,8 +9,16 @@ {{ ApplicationTitles[status] | translate }}
-
{{ competitiveSelectionDescription }}
-
({{ ApplicationStatusDescription[status] | translate }})
+
+ {{ competitiveSelectionDescription }} +
+
+ ({{ ApplicationStatusDescription[status] | translate }}) +

{{ reason }}

diff --git a/src/app/shell/personal-cabinet/shared-cabinet/messages/chat/message/message.component.html b/src/app/shell/personal-cabinet/shared-cabinet/messages/chat/message/message.component.html index a67c088da..a09207e3f 100644 --- a/src/app/shell/personal-cabinet/shared-cabinet/messages/chat/message/message.component.html +++ b/src/app/shell/personal-cabinet/shared-cabinet/messages/chat/message/message.component.html @@ -2,8 +2,7 @@

{{ senderName }}

-
+
{{ message.text }} {{ message.readDateTime ? 'done_all' : 'done' }} {{ message.createdDateTime | date: Constants.SHORT_TIME_24_HOUR_SYSTEM }} diff --git a/src/app/shell/personal-cabinet/shared-cabinet/messages/message-card/message-card.component.html b/src/app/shell/personal-cabinet/shared-cabinet/messages/message-card/message-card.component.html index cdc7dbaf0..2d3aa88f3 100644 --- a/src/app/shell/personal-cabinet/shared-cabinet/messages/message-card/message-card.component.html +++ b/src/app/shell/personal-cabinet/shared-cabinet/messages/message-card/message-card.component.html @@ -1,12 +1,15 @@ - +

{{ chatRoom.parent | getFullName }}

{{ chatRoom.workshop.title }}

- {{ chatRoom.lastMessage.createdDateTime | date: Constants.FULL_DATE_FORMAT : '': translateService.currentLang}} + {{ chatRoom.lastMessage.createdDateTime | date: Constants.FULL_DATE_FORMAT : '' : translateService.currentLang }}

@@ -26,8 +29,7 @@

{{ chatRoom.parent | getFullName }}

- more_vert + more_vert diff --git a/src/app/shell/personal-cabinet/shared-cabinet/messages/messages.component.ts b/src/app/shell/personal-cabinet/shared-cabinet/messages/messages.component.ts index 933bb1d35..59341182d 100644 --- a/src/app/shell/personal-cabinet/shared-cabinet/messages/messages.component.ts +++ b/src/app/shell/personal-cabinet/shared-cabinet/messages/messages.component.ts @@ -33,9 +33,6 @@ import { CabinetDataComponent } from '../cabinet-data.component'; styleUrls: ['./messages.component.scss'] }) export class MessagesComponent extends CabinetDataComponent { - readonly WorkshopDeclination = WorkshopDeclination; - readonly noMessagesTitle = NoResultsTitle.noMessages; - @Select(ProviderState.truncated) protected workshops$: Observable; @Select(RegistrationState.provider) @@ -43,16 +40,20 @@ export class MessagesComponent extends CabinetDataComponent { @Select(ChatState.chatRooms) private chatRooms$: Observable>; - providerId: string; - filterFormControl: FormControl = new FormControl(''); - chatRooms: SearchResponse; - currentPage: PaginationElement = PaginationConstants.firstPage; - chatRoomsParameters: ChatRoomsParameters = { + public readonly WorkshopDeclination = WorkshopDeclination; + public readonly noMessagesTitle = NoResultsTitle.noMessages; + + public providerId: string; + public filterFormControl: FormControl = new FormControl(''); + public chatRooms: SearchResponse; + public currentPage: PaginationElement = PaginationConstants.firstPage; + public chatRoomsParameters: ChatRoomsParameters = { role: null, workshopIds: null, searchText: null, size: PaginationConstants.CHATROOMS_PER_PAGE }; + constructor( protected store: Store, protected matDialog: MatDialog @@ -60,42 +61,13 @@ export class MessagesComponent extends CabinetDataComponent { super(store, matDialog); } - protected init(): void { - this.chatRoomsParameters.role = this.role; - - if (this.role === Role.provider) { - this.provider$ - .pipe( - filter((provider: Provider) => !!provider), - takeUntil(this.destroy$) - ) - .subscribe((provider: Provider) => { - this.providerId = provider.id; - this.getProviderWorkshops(); - }); - } - - this.getChatRooms(); - this.setListeners(); - } - - protected addNavPath(): void { - this.store.dispatch( - new PushNavPath({ - name: NavBarName.Messages, - isActive: false, - disable: true - }) - ); - } - - getProviderWorkshops(): void { + public getProviderWorkshops(): void { if (this.subrole === Subrole.None) { this.store.dispatch(new GetWorkshopListByProviderId(this.providerId)); } } - setListeners(): void { + public setListeners(): void { this.chatRooms$ .pipe(filter(Boolean), takeUntil(this.destroy$)) .subscribe((chatRooms: SearchResponse) => (this.chatRooms = chatRooms)); @@ -148,22 +120,51 @@ export class MessagesComponent extends CabinetDataComponent { .subscribe(); } - onEntitiesSelect(workshopIds: string[]): void { + public onEntitiesSelect(workshopIds: string[]): void { this.currentPage = PaginationConstants.firstPage; this.chatRoomsParameters.workshopIds = workshopIds; this.getChatRooms(); } - onItemsPerPageChange(itemsPerPage: number): void { + public onItemsPerPageChange(itemsPerPage: number): void { this.chatRoomsParameters.size = itemsPerPage; this.onPageChange(PaginationConstants.firstPage); } - onPageChange(page: PaginationElement): void { + public onPageChange(page: PaginationElement): void { this.currentPage = page; this.getChatRooms(); } + protected init(): void { + this.chatRoomsParameters.role = this.role; + + if (this.role === Role.provider) { + this.provider$ + .pipe( + filter((provider: Provider) => !!provider), + takeUntil(this.destroy$) + ) + .subscribe((provider: Provider) => { + this.providerId = provider.id; + this.getProviderWorkshops(); + }); + } + + this.getChatRooms(); + this.setListeners(); + } + + protected addNavPath(): void { + this.store.dispatch( + new PushNavPath({ + name: NavBarName.Messages, + isActive: false, + disable: true + }) + ); + } + private getChatRooms(): void { Util.setFromPaginationParam(this.chatRoomsParameters, this.currentPage, this.chatRooms?.totalAmount); this.store.dispatch(new GetChatRooms(this.chatRoomsParameters)); diff --git a/src/app/shell/personal-cabinet/shared-cabinet/user-config/user-config.component.ts b/src/app/shell/personal-cabinet/shared-cabinet/user-config/user-config.component.ts index 5fb97fd8a..8784b8e17 100644 --- a/src/app/shell/personal-cabinet/shared-cabinet/user-config/user-config.component.ts +++ b/src/app/shell/personal-cabinet/shared-cabinet/user-config/user-config.component.ts @@ -17,15 +17,15 @@ import { environment } from '../../../../../environments/environment'; styleUrls: ['./user-config.component.scss'] }) export class UserConfigComponent implements OnInit, OnDestroy { - public readonly gender = Gender; - public readonly dateFormat = Constants.SHORT_DATE_FORMAT; - public readonly role = Role; - @Select(RegistrationState.user) public user$: Observable; @Select(RegistrationState.role) public role$: Observable; + public readonly gender = Gender; + public readonly dateFormat = Constants.SHORT_DATE_FORMAT; + public readonly role = Role; + public authServer: string = environment.stsServer; public culture: string = localStorage.getItem('ui-culture'); public link: string; diff --git a/src/app/shell/result/ordering/ordering.component.html b/src/app/shell/result/ordering/ordering.component.html index bcb7c4116..084de6fd7 100644 --- a/src/app/shell/result/ordering/ordering.component.html +++ b/src/app/shell/result/ordering/ordering.component.html @@ -1,6 +1,10 @@ - + {{ 'BY_RATING' | translate }} diff --git a/src/app/shell/result/result.component.html b/src/app/shell/result/result.component.html index ccd319da9..d44613fd1 100644 --- a/src/app/shell/result/result.component.html +++ b/src/app/shell/result/result.component.html @@ -5,7 +5,12 @@

{{ (filteredWorkshops$ | async)?.totalAmount | translateCases: WorkshopDeclination }}

- @@ -16,16 +21,22 @@

- +
- +
- +
@@ -46,15 +57,24 @@

- - view_module {{ 'LIST' | translate }} - map {{ 'MAP' | translate }} diff --git a/src/app/shell/result/workshop-cards-list/workshop-cards-list.component.ts b/src/app/shell/result/workshop-cards-list/workshop-cards-list.component.ts index 7a2fba357..f90cf3174 100644 --- a/src/app/shell/result/workshop-cards-list/workshop-cards-list.component.ts +++ b/src/app/shell/result/workshop-cards-list/workshop-cards-list.component.ts @@ -20,24 +20,24 @@ import { Util } from 'shared/utils/utils'; styleUrls: ['./workshop-cards-list.component.scss'] }) export class WorkshopCardsListComponent implements OnInit, OnDestroy { - readonly noResultWorkshops = NoResultsTitle.noResult; - readonly Role = Role; - - @Input() workshops$: Observable>; - @Input() paginationParameters: PaginationParameters; - @Input() role: string; - @Input() currentPage: PaginationElement; + @Input() public workshops$: Observable>; + @Input() public paginationParameters: PaginationParameters; + @Input() public role: string; + @Input() public currentPage: PaginationElement; @Select(FilterState.isLoading) - isLoadingResultPage$: Observable; + public isLoadingResultPage$: Observable; + + public readonly noResultWorkshops = NoResultsTitle.noResult; + public readonly Role = Role; - parent: boolean; - workshops: SearchResponse; - destroy$: Subject = new Subject(); + public parent: boolean; + public workshops: SearchResponse; + public destroy$: Subject = new Subject(); constructor(public store: Store) {} - ngOnInit(): void { + public ngOnInit(): void { this.workshops$ .pipe( takeUntil(this.destroy$), @@ -46,17 +46,17 @@ export class WorkshopCardsListComponent implements OnInit, OnDestroy { .subscribe((workshops: SearchResponse) => (this.workshops = workshops)); } - onPageChange(page: PaginationElement): void { + public onPageChange(page: PaginationElement): void { this.currentPage = page; this.getWorkshops(); } - onItemsPerPageChange(itemsPerPage: number): void { + public onItemsPerPageChange(itemsPerPage: number): void { this.paginationParameters.size = itemsPerPage; this.onPageChange(PaginationConstants.firstPage); } - ngOnDestroy(): void { + public ngOnDestroy(): void { this.destroy$.next(true); this.destroy$.unsubscribe(); } diff --git a/src/app/shell/result/workshop-map-view-list/workshop-map-view-list.component.ts b/src/app/shell/result/workshop-map-view-list/workshop-map-view-list.component.ts index ef0882881..5a608440f 100644 --- a/src/app/shell/result/workshop-map-view-list/workshop-map-view-list.component.ts +++ b/src/app/shell/result/workshop-map-view-list/workshop-map-view-list.component.ts @@ -84,10 +84,10 @@ export class WorkshopMapViewListComponent implements OnInit, OnDestroy { if (duration < 1000 && Math.abs(direction[0]) > 30 && Math.abs(direction[0]) > Math.abs(direction[1] * 3)) { const swipe = direction[0] < 0 ? 'next' : 'previous'; this.direct = swipe; - if (swipe === 'next') { - this.selectedWorkshops.length - 1 > this.currentWorkShopIndex && this.currentWorkShopIndex++; - } else { - this.currentWorkShopIndex >= 1 && this.currentWorkShopIndex--; + if (swipe === 'next' && this.selectedWorkshops.length - 1 > this.currentWorkShopIndex) { + this.currentWorkShopIndex++; + } else if (this.currentWorkShopIndex >= 1) { + this.currentWorkShopIndex--; } } } diff --git a/yarn.lock b/yarn.lock index 79856b0e6..939e38dd7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4093,6 +4093,11 @@ ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: dependencies: type-fest "^0.21.3" +ansi-escapes@^6.2.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-6.2.1.tgz#76c54ce9b081dad39acec4b5d53377913825fb0f" + integrity sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig== + ansi-html-community@^0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" @@ -4127,7 +4132,7 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -ansi-styles@^6.1.0: +ansi-styles@^6.0.0, ansi-styles@^6.1.0, ansi-styles@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== @@ -4569,6 +4574,13 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.0.1" +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + browser-process-hrtime@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" @@ -4731,6 +4743,11 @@ chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + char-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" @@ -4808,6 +4825,13 @@ cli-cursor@3.1.0, cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" +cli-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-4.0.0.tgz#3cecfe3734bf4fe02a8361cbdc0f6fe28c6a57ea" + integrity sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg== + dependencies: + restore-cursor "^4.0.0" + cli-spinners@2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" @@ -4835,6 +4859,14 @@ cli-truncate@^2.1.0: slice-ansi "^3.0.0" string-width "^4.2.0" +cli-truncate@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-4.0.0.tgz#6cc28a2924fee9e25ce91e973db56c7066e6172a" + integrity sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA== + dependencies: + slice-ansi "^5.0.0" + string-width "^7.0.0" + cli-width@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" @@ -4911,7 +4943,7 @@ color-support@^1.1.3: resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== -colorette@^2.0.10, colorette@^2.0.16: +colorette@^2.0.10, colorette@^2.0.16, colorette@^2.0.20: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== @@ -4933,6 +4965,11 @@ commander@^6.2.1: resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== +commander@~12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" + integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== + comment-parser@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.4.1.tgz#bdafead37961ac079be11eb7ec65c4d021eaf9cc" @@ -5284,6 +5321,13 @@ debug@^3.1.0, debug@^3.2.7: dependencies: ms "^2.1.1" +debug@~4.3.4: + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + dependencies: + ms "2.1.2" + decimal.js@^10.2.1, decimal.js@^10.4.2: version "10.4.3" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" @@ -5520,6 +5564,11 @@ emittery@^0.13.1: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== +emoji-regex@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.3.0.tgz#76998b9268409eb3dae3de989254d456e70cfe23" + integrity sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -6094,6 +6143,11 @@ eventemitter3@^4.0.0: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + events@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" @@ -6134,6 +6188,21 @@ execa@^5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" +execa@~8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" + integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^8.0.1" + human-signals "^5.0.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^4.1.0" + strip-final-newline "^3.0.0" + executable@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" @@ -6351,6 +6420,13 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + finalhandler@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" @@ -6579,6 +6655,11 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-east-asian-width@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz#5e6ebd9baee6fb8b7b6bd505221065f0cd91f64e" + integrity sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA== + get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" @@ -6607,6 +6688,11 @@ get-stream@^6.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +get-stream@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" + integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== + get-symbol-description@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" @@ -6991,6 +7077,11 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +human-signals@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" + integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -6998,6 +7089,11 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" +husky@^9.0.11: + version "9.0.11" + resolved "https://registry.yarnpkg.com/husky/-/husky-9.0.11.tgz#fc91df4c756050de41b3e478b2158b87c1e79af9" + integrity sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw== + iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -7260,6 +7356,18 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-fullwidth-code-point@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" + integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== + +is-fullwidth-code-point@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz#9609efced7c2f97da7b60145ef481c787c7ba704" + integrity sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA== + dependencies: + get-east-asian-width "^1.0.0" + is-generator-fn@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" @@ -7366,6 +7474,11 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" @@ -8270,6 +8383,11 @@ license-webpack-plugin@4.0.2: dependencies: webpack-sources "^3.0.0" +lilconfig@~3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.2.tgz#e4a7c3cb549e3a606c8dcc32e5ae1005e62c05cb" + integrity sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow== + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -8280,6 +8398,22 @@ lines-and-columns@~2.0.3: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.4.tgz#d00318855905d2660d8c0822e3f5a4715855fc42" integrity sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A== +lint-staged@^15.2.7: + version "15.2.7" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-15.2.7.tgz#97867e29ed632820c0fb90be06cd9ed384025649" + integrity sha512-+FdVbbCZ+yoh7E/RosSdqKJyUM2OEjTciH0TFNkawKgvFp1zbGlEC39RADg+xKBG1R4mhoH2j85myBQZ5wR+lw== + dependencies: + chalk "~5.3.0" + commander "~12.1.0" + debug "~4.3.4" + execa "~8.0.1" + lilconfig "~3.1.1" + listr2 "~8.2.1" + micromatch "~4.0.7" + pidtree "~0.6.0" + string-argv "~0.3.2" + yaml "~2.4.2" + listr2@^3.8.3: version "3.14.0" resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.14.0.tgz#23101cc62e1375fd5836b248276d1d2b51fdbe9e" @@ -8294,6 +8428,18 @@ listr2@^3.8.3: through "^2.3.8" wrap-ansi "^7.0.0" +listr2@~8.2.1: + version "8.2.3" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-8.2.3.tgz#c494bb89b34329cf900e4e0ae8aeef9081d7d7a5" + integrity sha512-Lllokma2mtoniUOS94CcOErHWAug5iu7HOmDrvWgpw8jyQH2fomgB+7lZS4HWZxytUuQwkGOwe49FvwVaA85Xw== + dependencies: + cli-truncate "^4.0.0" + colorette "^2.0.20" + eventemitter3 "^5.0.1" + log-update "^6.0.0" + rfdc "^1.4.1" + wrap-ansi "^9.0.0" + loader-runner@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" @@ -8377,6 +8523,17 @@ log-update@^4.0.0: slice-ansi "^4.0.0" wrap-ansi "^6.2.0" +log-update@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-6.0.0.tgz#0ddeb7ac6ad658c944c1de902993fce7c33f5e59" + integrity sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw== + dependencies: + ansi-escapes "^6.2.0" + cli-cursor "^4.0.0" + slice-ansi "^7.0.0" + strip-ansi "^7.1.0" + wrap-ansi "^9.0.0" + loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -8525,6 +8682,14 @@ micromatch@^4.0.2, micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" +micromatch@~4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5" + integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" @@ -8547,6 +8712,11 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + mini-css-extract-plugin@2.7.6: version "2.7.6" resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz#282a3d38863fddcd2e0c220aaed5b90bc156564d" @@ -8942,6 +9112,13 @@ npm-run-path@^4.0.0, npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" +npm-run-path@^5.1.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f" + integrity sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ== + dependencies: + path-key "^4.0.0" + npmlog@^6.0.0: version "6.0.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" @@ -9122,6 +9299,13 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + open@8.4.2, open@^8.0.9, open@^8.4.0: version "8.4.2" resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" @@ -9334,6 +9518,11 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" @@ -9377,6 +9566,11 @@ picomatch@2.3.1, picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pidtree@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c" + integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== + pify@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -9886,6 +10080,14 @@ restore-cursor@^3.1.0: onetime "^5.1.0" signal-exit "^3.0.2" +restore-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-4.0.0.tgz#519560a4318975096def6e609d44100edaa4ccb9" + integrity sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" @@ -9911,6 +10113,11 @@ rfdc@^1.3.0: resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.1.tgz#2b6d4df52dffe8bb346992a10ea9451f24373a8f" integrity sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg== +rfdc@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -10216,7 +10423,7 @@ signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== -signal-exit@^4.0.1: +signal-exit@^4.0.1, signal-exit@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== @@ -10265,6 +10472,22 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" +slice-ansi@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a" + integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== + dependencies: + ansi-styles "^6.0.0" + is-fullwidth-code-point "^4.0.0" + +slice-ansi@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-7.1.0.tgz#cd6b4655e298a8d1bdeb04250a433094b347b9a9" + integrity sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg== + dependencies: + ansi-styles "^6.2.1" + is-fullwidth-code-point "^5.0.0" + smart-buffer@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" @@ -10449,6 +10672,11 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== +string-argv@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" + integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== + string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -10475,6 +10703,15 @@ string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" +string-width@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.2.0.tgz#b5bb8e2165ce275d4d43476dd2700ad9091db6dc" + integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== + dependencies: + emoji-regex "^10.3.0" + get-east-asian-width "^1.0.0" + strip-ansi "^7.1.0" + string.prototype.matchall@^4.0.10: version "4.0.11" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz#1092a72c59268d2abaad76582dccc687c0297e0a" @@ -10542,7 +10779,7 @@ string_decoder@~1.1.1: dependencies: ansi-regex "^5.0.1" -strip-ansi@^7.0.1: +strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== @@ -10564,6 +10801,11 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + strip-json-comments@3.1.1, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -11504,6 +11746,15 @@ wrap-ansi@^8.1.0: string-width "^5.0.1" strip-ansi "^7.0.1" +wrap-ansi@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-9.0.0.tgz#1a3dc8b70d85eeb8398ddfb1e4a02cd186e58b3e" + integrity sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q== + dependencies: + ansi-styles "^6.2.1" + string-width "^7.0.0" + strip-ansi "^7.1.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -11557,6 +11808,11 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yaml@~2.4.2: + version "2.4.5" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.5.tgz#60630b206dd6d84df97003d33fc1ddf6296cca5e" + integrity sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg== + yargs-parser@21.1.1, yargs-parser@^21.0.1, yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" From da197a8d02100df1080c918128463c7116274319 Mon Sep 17 00:00:00 2001 From: AkunaPatlata <94176568+AkunaPatlata@users.noreply.github.com> Date: Wed, 17 Jul 2024 12:25:41 +0300 Subject: [PATCH 25/25] Dvorak / 2600 Header links focus indicator fixed (#2601) --- src/app/header/header.component.html | 12 ++++++------ src/app/header/header.component.scss | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html index b870e9d15..2f9dc95b8 100644 --- a/src/app/header/header.component.html +++ b/src/app/header/header.component.html @@ -4,18 +4,18 @@