From 2f280b2b956d0c7d34b8ad335431405d27966022 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Wed, 13 Mar 2024 04:35:27 +0300 Subject: [PATCH] feat: add a retry button in case of a load error --- .../loading-indicator.component.html | 7 +++++ .../loading-indicator.component.ts | 20 +++++++++++++ .../schedule-tabs-group.component.html | 16 +++++----- .../schedule-tabs-group.component.ts | 26 ++++++++++------- .../schedule-tabs-lecture-hall.component.html | 8 ++--- .../schedule-tabs-lecture-hall.component.ts | 14 +++++---- .../schedule-tabs-other.component.html | 17 ++++++----- .../schedule-tabs-other.component.ts | 21 ++++++++------ .../schedule-tabs-professor.component.html | 8 ++--- .../schedule-tabs-professor.component.ts | 29 ++++++++++--------- 10 files changed, 107 insertions(+), 59 deletions(-) create mode 100644 src/components/loading-indicator/loading-indicator.component.html create mode 100644 src/components/loading-indicator/loading-indicator.component.ts diff --git a/src/components/loading-indicator/loading-indicator.component.html b/src/components/loading-indicator/loading-indicator.component.html new file mode 100644 index 0000000..87083ed --- /dev/null +++ b/src/components/loading-indicator/loading-indicator.component.html @@ -0,0 +1,7 @@ +@if (loading) { + +} @else { + +} diff --git a/src/components/loading-indicator/loading-indicator.component.ts b/src/components/loading-indicator/loading-indicator.component.ts new file mode 100644 index 0000000..8ec20ad --- /dev/null +++ b/src/components/loading-indicator/loading-indicator.component.ts @@ -0,0 +1,20 @@ +import {Component, EventEmitter, Input, Output} from '@angular/core'; +import {DataSpinnerComponent} from "@component/data-spinner/data-spinner.component"; +import {MatIcon} from "@angular/material/icon"; +import {MatButton, MatFabButton} from "@angular/material/button"; + +@Component({ + selector: 'app-loading-indicator', + standalone: true, + imports: [ + DataSpinnerComponent, + MatButton, + MatIcon, + MatFabButton + ], + templateUrl: './loading-indicator.component.html' +}) +export class LoadingIndicatorComponent { + @Input() loading: boolean = true; + @Output() retryFunction: EventEmitter = new EventEmitter(); +} diff --git a/src/components/schedule-tabs/schedule-tabs-group/schedule-tabs-group.component.html b/src/components/schedule-tabs/schedule-tabs-group/schedule-tabs-group.component.html index 7149905..c2d88b9 100644 --- a/src/components/schedule-tabs/schedule-tabs-group/schedule-tabs-group.component.html +++ b/src/components/schedule-tabs/schedule-tabs-group/schedule-tabs-group.component.html @@ -5,30 +5,31 @@ Факультет - + @for (faculty of faculties | async; track $index) { {{ faculty.name }} } @empty { - + } - + Курс - + @for (course of courseNumbers | async; track $index) { {{ course }} } @empty { - + } @@ -39,13 +40,14 @@ Группа - + @for (group of filteredGroups | async; track $index) { {{ group.name }} } @empty { - + } diff --git a/src/components/schedule-tabs/schedule-tabs-group/schedule-tabs-group.component.ts b/src/components/schedule-tabs/schedule-tabs-group/schedule-tabs-group.component.ts index 073e08d..6f4508d 100644 --- a/src/components/schedule-tabs/schedule-tabs-group/schedule-tabs-group.component.ts +++ b/src/components/schedule-tabs/schedule-tabs-group/schedule-tabs-group.component.ts @@ -1,29 +1,31 @@ import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core'; import {MatExpansionModule, MatExpansionPanel} from "@angular/material/expansion"; -import {DataSpinnerComponent} from "@component/data-spinner/data-spinner.component"; import {MatChipListboxChange, MatChipsModule} from '@angular/material/chips'; import {FormControl, ReactiveFormsModule} from "@angular/forms"; import {AsyncPipe} from "@angular/common"; import {map, Observable, of} from "rxjs"; import {FacultyResponse} from "@model/facultyResponse"; import {GroupResponse} from "@model/groupResponse"; +import {LoadingIndicatorComponent} from "@component/loading-indicator/loading-indicator.component"; @Component({ selector: 'app-schedule-tabs-group', standalone: true, imports: [ MatExpansionModule, - DataSpinnerComponent, MatChipsModule, ReactiveFormsModule, + LoadingIndicatorComponent, AsyncPipe ], templateUrl: './schedule-tabs-group.component.html', styleUrl: './schedule-tabs-group.component.css' }) + export class ScheduleTabsGroupComponent { - protected facultiesId: number | null = null; + protected facultyId: number | null = null; protected courseNumber: number | null = null; + protected filteredGroups: Observable = of([]); protected courseNumbers: Observable = of([]); protected groups: Observable = of([]); @@ -35,8 +37,12 @@ export class ScheduleTabsGroupComponent { @ViewChild('groupPanel') groupPanel!: MatExpansionPanel; @Input() faculties: Observable = of([]); + @Input() facultiesLoaded: boolean | null = false; + @Output() facultiesLoadRetry: EventEmitter = new EventEmitter(); + @Input() groupsLoaded: boolean | null = false; + @Output() groupsLoadRetry: EventEmitter = new EventEmitter(); - @Input() set groupsSet(data: Observable) { + @Input() set setGroups(data: Observable) { this.groups = data; this.courseNumbers = this.groups.pipe( map(data => data.map(g => g.courseNumber)), @@ -48,23 +54,23 @@ export class ScheduleTabsGroupComponent { @Output() groupSelected = new EventEmitter(); @Output() facultySelected = new EventEmitter(); - protected selectedFaculty(event: MatChipListboxChange) { + protected chooseFaculty(event: MatChipListboxChange) { this.courseNumber = null; this.groups = of([]); this.chipGroup.reset(); this.chipCourse.reset(); if (event.value === undefined || event.value === null) { - this.facultiesId = null; + this.facultyId = null; return; } - this.facultiesId = event.value; + this.facultyId = event.value; this.courseNumberPanel.open(); - this.facultySelected.emit(this.facultiesId!); + this.facultySelected.emit(this.facultyId!); } - protected selectCourseNumber(event: MatChipListboxChange) { + protected chooseCourseNumber(event: MatChipListboxChange) { this.filteredGroups = of([]); this.chipGroup.reset(); @@ -79,7 +85,7 @@ export class ScheduleTabsGroupComponent { this.filteredGroups = of(data.filter(g => g.courseNumber === this.courseNumber))); } - protected selectGroup(event: MatChipListboxChange) { + protected chooseGroup(event: MatChipListboxChange) { if (event.value === undefined || event.value === null) return; diff --git a/src/components/schedule-tabs/schedule-tabs-lecture-hall/schedule-tabs-lecture-hall.component.html b/src/components/schedule-tabs/schedule-tabs-lecture-hall/schedule-tabs-lecture-hall.component.html index c4a98d3..b132e17 100644 --- a/src/components/schedule-tabs/schedule-tabs-lecture-hall/schedule-tabs-lecture-hall.component.html +++ b/src/components/schedule-tabs/schedule-tabs-lecture-hall/schedule-tabs-lecture-hall.component.html @@ -5,13 +5,13 @@ Кампус - + @for (campus of campuses | async; track $index) { {{ campus.codeName }} } @empty { - + } @@ -22,13 +22,13 @@ Кабинет - + @for (lectureHall of lectureHalls | async; track $index) { {{ lectureHall.name }} } @empty { - + } diff --git a/src/components/schedule-tabs/schedule-tabs-lecture-hall/schedule-tabs-lecture-hall.component.ts b/src/components/schedule-tabs/schedule-tabs-lecture-hall/schedule-tabs-lecture-hall.component.ts index 29eed08..3072c5a 100644 --- a/src/components/schedule-tabs/schedule-tabs-lecture-hall/schedule-tabs-lecture-hall.component.ts +++ b/src/components/schedule-tabs/schedule-tabs-lecture-hall/schedule-tabs-lecture-hall.component.ts @@ -1,23 +1,23 @@ import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core'; import {AsyncPipe} from "@angular/common"; -import {DataSpinnerComponent} from "@component/data-spinner/data-spinner.component"; import {MatAccordion, MatExpansionModule, MatExpansionPanel} from "@angular/material/expansion"; import {MatChipListboxChange, MatChipsModule} from "@angular/material/chips"; import {Observable, of} from "rxjs"; import {CampusBasicInfoResponse} from "@model/campusBasicInfoResponse"; import {FormControl, ReactiveFormsModule} from "@angular/forms"; import {LectureHallResponse} from "@model/lectureHallResponse"; +import {LoadingIndicatorComponent} from "@component/loading-indicator/loading-indicator.component"; @Component({ selector: 'app-schedule-tabs-lecture-hall', standalone: true, imports: [ MatChipsModule, - DataSpinnerComponent, MatExpansionModule, AsyncPipe, ReactiveFormsModule, - MatAccordion + MatAccordion, + LoadingIndicatorComponent ], templateUrl: './schedule-tabs-lecture-hall.component.html', styleUrl: './schedule-tabs-lecture-hall.component.css' @@ -29,12 +29,16 @@ export class ScheduleTabsLectureHallComponent { @ViewChild('lecturePanel') lecturePanel!: MatExpansionPanel; @Input() campuses: Observable = of([]); + @Input() campusesLoaded: boolean | null = false; + @Output() campusesLoadRetry: EventEmitter = new EventEmitter(); @Input() lectureHalls: Observable = of([]); + @Input() lectureHallsLoaded: boolean | null = false; + @Output() lectureHallsLoadRetry: EventEmitter = new EventEmitter(); @Output() campusSelected = new EventEmitter(); @Output() lectureHallSelected = new EventEmitter(); - protected selectedCampus(event: MatChipListboxChange) { + protected chooseCampus(event: MatChipListboxChange) { this.chipLecture.reset(); if (event.value === undefined || event.value === null) { @@ -49,7 +53,7 @@ export class ScheduleTabsLectureHallComponent { this.campusSelected.emit(this.campusId!); } - protected selectedLectureHall(event: MatChipListboxChange) { + protected chooseLectureHall(event: MatChipListboxChange) { if (event.value === undefined || event.value === null) return; diff --git a/src/components/schedule-tabs/schedule-tabs-other/schedule-tabs-other.component.html b/src/components/schedule-tabs/schedule-tabs-other/schedule-tabs-other.component.html index a6e5bfc..9c7b0b5 100644 --- a/src/components/schedule-tabs/schedule-tabs-other/schedule-tabs-other.component.html +++ b/src/components/schedule-tabs/schedule-tabs-other/schedule-tabs-other.component.html @@ -1,31 +1,34 @@ - +
-
- +

@if (data.length === 0) { - + } @else { @if (filteredData.value.length !== 0) { - +
- +
{{ item.name }}
diff --git a/src/components/schedule-tabs/schedule-tabs-other/schedule-tabs-other.component.ts b/src/components/schedule-tabs/schedule-tabs-other/schedule-tabs-other.component.ts index 251f7dc..4e4dad8 100644 --- a/src/components/schedule-tabs/schedule-tabs-other/schedule-tabs-other.component.ts +++ b/src/components/schedule-tabs/schedule-tabs-other/schedule-tabs-other.component.ts @@ -1,4 +1,4 @@ -import {Component, HostListener, Input, ViewChild} from '@angular/core'; +import {Component, EventEmitter, HostListener, Input, Output, ViewChild} from '@angular/core'; import {MatMenu, MatMenuTrigger} from "@angular/material/menu"; import {MatCheckbox} from "@angular/material/checkbox"; import {BehaviorSubject} from "rxjs"; @@ -6,12 +6,12 @@ import {MatButton, MatIconButton} from "@angular/material/button"; import {MatFormField, MatSuffix} from "@angular/material/form-field"; import {MatIcon} from "@angular/material/icon"; import {FormsModule} from "@angular/forms"; -import {DataSpinnerComponent} from "@component/data-spinner/data-spinner.component"; import {MatListOption, MatSelectionList} from "@angular/material/list"; import {ScrollingModule} from "@angular/cdk/scrolling"; import {ScrollingModule as ExperimentalScrollingModule} from '@angular/cdk-experimental/scrolling'; import {MatDivider} from "@angular/material/divider"; import {MatInput} from "@angular/material/input"; +import {LoadingIndicatorComponent} from "@component/loading-indicator/loading-indicator.component"; export interface SelectData { id: number, @@ -30,7 +30,6 @@ export interface SelectData { MatIcon, FormsModule, MatCheckbox, - DataSpinnerComponent, MatSelectionList, MatListOption, MatDivider, @@ -38,7 +37,8 @@ export interface SelectData { MatInput, MatSuffix, ScrollingModule, - ExperimentalScrollingModule + ExperimentalScrollingModule, + LoadingIndicatorComponent ], templateUrl: './schedule-tabs-other.component.html', styleUrl: './schedule-tabs-other.component.css' @@ -49,10 +49,13 @@ export class ScheduleTabsOtherComponent { protected data: SelectData[] = []; @Input() idButton!: string; - @Input() buttonText!: string; + @Input() textButton!: string; @ViewChild('menuTrigger') menuTrigger!: MatMenuTrigger; @ViewChild('chooseCheckbox') chooseCheckbox!: MatCheckbox; + @Input() dataLoaded: boolean | null = false; + @Output() retryLoadData: EventEmitter = new EventEmitter(); + get selectedIds(): number[] { return this.data.filter(x => x.selected).map(x => x.id); } @@ -60,7 +63,7 @@ export class ScheduleTabsOtherComponent { set Data(data: SelectData[]) { this.data = data; this.data.forEach(x => x.selected = false); - this.filteredData.next(this.data) + this.filteredData.next(this.data); } protected get searchQuery(): string { @@ -83,7 +86,7 @@ export class ScheduleTabsOtherComponent { )); } - protected clearSearch(): void { + protected clearSearchQuery(): void { this.searchQuery = ''; } @@ -101,7 +104,7 @@ export class ScheduleTabsOtherComponent { this.updateCheckBox(); } - protected selectData() { + protected checkData() { const check: boolean = this.filteredData.value.some(x => !x.selected) && !this.filteredData.value.every(x => x.selected); const updatedData = this.filteredData.value.map(data => { @@ -113,7 +116,7 @@ export class ScheduleTabsOtherComponent { this.updateCheckBox(); } - selectChange(item: number) { + protected checkboxStateChange(item: number) { const data = this.data.find(x => x.id === item)!; data.selected = !data.selected; const updatedData = this.filteredData.value; diff --git a/src/components/schedule-tabs/schedule-tabs-professor/schedule-tabs-professor.component.html b/src/components/schedule-tabs/schedule-tabs-professor/schedule-tabs-professor.component.html index 5c5d54a..3fc2074 100644 --- a/src/components/schedule-tabs/schedule-tabs-professor/schedule-tabs-professor.component.html +++ b/src/components/schedule-tabs/schedule-tabs-professor/schedule-tabs-professor.component.html @@ -1,13 +1,13 @@
- @if (teachers.length === 0) { - + @if (professors.length === 0) { + } @else { - + - @for (option of filteredTeachers | async; track option) { + @for (option of filteredProfessors | async; track option) { {{ option.name }} diff --git a/src/components/schedule-tabs/schedule-tabs-professor/schedule-tabs-professor.component.ts b/src/components/schedule-tabs/schedule-tabs-professor/schedule-tabs-professor.component.ts index 644efce..798bccb 100644 --- a/src/components/schedule-tabs/schedule-tabs-professor/schedule-tabs-professor.component.ts +++ b/src/components/schedule-tabs/schedule-tabs-professor/schedule-tabs-professor.component.ts @@ -3,9 +3,9 @@ import {MatFormField, MatInput} from "@angular/material/input"; import {FormControl, ReactiveFormsModule} from "@angular/forms"; import {MatAutocompleteModule, MatAutocompleteSelectedEvent} from "@angular/material/autocomplete"; import {AsyncPipe} from "@angular/common"; -import {DataSpinnerComponent} from "@component/data-spinner/data-spinner.component"; import {map, Observable, startWith} from "rxjs"; import {ProfessorResponse} from "@model/professorResponse"; +import {LoadingIndicatorComponent} from "@component/loading-indicator/loading-indicator.component"; @Component({ selector: 'app-schedule-tabs-professor', @@ -13,43 +13,46 @@ import {ProfessorResponse} from "@model/professorResponse"; imports: [ MatAutocompleteModule, MatFormField, - DataSpinnerComponent, AsyncPipe, ReactiveFormsModule, - MatInput + MatInput, + LoadingIndicatorComponent ], templateUrl: './schedule-tabs-professor.component.html', styleUrl: './schedule-tabs-professor.component.css' }) export class ScheduleTabsProfessorComponent implements OnInit { - protected teacherControl = new FormControl(); - protected filteredTeachers!: Observable; + protected professorControl = new FormControl(); + protected filteredProfessors!: Observable; - @Input() teachers: ProfessorResponse[] = []; + @Input() professors: ProfessorResponse[] = []; @Output() professorSelected = new EventEmitter(); + @Input() professorsLoaded: boolean | null = false; + @Output() professorsLoadRetry: EventEmitter = new EventEmitter(); + ngOnInit(): void { - this.filteredTeachers = this.teacherControl.valueChanges.pipe( + this.filteredProfessors = this.professorControl.valueChanges.pipe( startWith(''), - map(value => this._filterTeachers(value)) + map(value => this._filterProfessors(value)) ); } - private _filterTeachers(value: string | number): ProfessorResponse[] { + private _filterProfessors(value: string | number): ProfessorResponse[] { if (typeof value === 'string') { if (value === '') return []; const filterValue = value.toLowerCase(); - return this.teachers.filter(teacher => teacher.name.toLowerCase().includes(filterValue)); + return this.professors.filter(teacher => teacher.name.toLowerCase().includes(filterValue)); } else { - const selectedTeacher = this.teachers.find(teacher => teacher.id === value); + const selectedTeacher = this.professors.find(teacher => teacher.id === value); return selectedTeacher ? [selectedTeacher] : []; } } protected onOptionSelected(event: MatAutocompleteSelectedEvent) { - const selectedOption = this.teachers.find(teacher => teacher.id === event.option.value); + const selectedOption = this.professors.find(teacher => teacher.id === event.option.value); if (selectedOption) { - this.teacherControl.setValue(selectedOption.name); + this.professorControl.setValue(selectedOption.name); this.professorSelected.emit(selectedOption.id); } }