feat: add saving of user's selection
This commit is contained in:
		
							
								
								
									
										83
									
								
								src/components/common/tab-storage/tab-storage.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/components/common/tab-storage/tab-storage.component.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| import {MatChipListbox} from "@angular/material/chips"; | ||||
|  | ||||
| export class TabSelect { | ||||
|   public index: number; | ||||
|   public name: string; | ||||
|  | ||||
|   constructor(index: number, name: string) { | ||||
|     this.index = index; | ||||
|     this.name = name; | ||||
|   } | ||||
| } | ||||
|  | ||||
| export enum TabSelectType { | ||||
|   group, | ||||
|   professor, | ||||
|   lecture, | ||||
|   other | ||||
| } | ||||
|  | ||||
| export interface TabSelectData { | ||||
|   selected: TabSelect[] | null; | ||||
|   type: TabSelectType; | ||||
| } | ||||
|  | ||||
| export class TabStorageComponent { | ||||
|   private static dataName = 'tabSelectedData'; | ||||
|  | ||||
|   public static trySelectChip(index: number, chip: MatChipListbox, tryCount: number = 0) { | ||||
|     setTimeout(() => { | ||||
|       if (chip?._chips !== undefined && chip._chips.length !== 0) { | ||||
|         let selected = chip._chips.find(x => x.value == index.toString()); | ||||
|         if (selected !== undefined) { | ||||
|           selected.select(); | ||||
|           return; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       if (tryCount >= 10) | ||||
|         return; | ||||
|  | ||||
|       this.trySelectChip(index, chip, ++tryCount); | ||||
|     }, 100); | ||||
|   } | ||||
|  | ||||
|   public static select(selected: TabSelect, type: TabSelectType, index: number) { | ||||
|     let selectedData = this.selected; | ||||
|  | ||||
|     if (selectedData === null) | ||||
|       selectedData = {} as TabSelectData; | ||||
|  | ||||
|     if (selectedData.type !== type || selectedData.selected === null || selectedData.selected.length === 0) { | ||||
|       if (index !== 0) | ||||
|         return; | ||||
|       else | ||||
|         selectedData.selected = [selected]; | ||||
|     } else { | ||||
|       if (selectedData.selected.length < index) | ||||
|         return; | ||||
|  | ||||
|       if (selectedData.selected.length - 1 === index) | ||||
|         selectedData.selected[index] = selected; | ||||
|       else if (selectedData.selected.length > index + 1) | ||||
|         selectedData.selected = selectedData.selected.slice(0, index + 1); | ||||
|       else | ||||
|         selectedData.selected.push(selected); | ||||
|     } | ||||
|  | ||||
|     selectedData.type = type; | ||||
|  | ||||
|     console.log(selectedData); | ||||
|  | ||||
|     localStorage.setItem(this.dataName, JSON.stringify(selectedData)); | ||||
|   } | ||||
|  | ||||
|   public static get selected(): TabSelectData | null { | ||||
|     let data = localStorage.getItem(this.dataName); | ||||
|  | ||||
|     if (data === null) | ||||
|       return null; | ||||
|  | ||||
|     return JSON.parse(data) as TabSelectData; | ||||
|   } | ||||
| } | ||||
| @@ -5,14 +5,16 @@ | ||||
|         Факультет | ||||
|       </mat-panel-title> | ||||
|     </mat-expansion-panel-header> | ||||
|     <mat-chip-listbox hideSingleSelectionIndicator (change)="chooseFaculty($event)"> | ||||
|     <mat-chip-listbox hideSingleSelectionIndicator (change)="onFacultySelected($event.value)" #facultyChip> | ||||
|       @for (faculty of faculties; track $index) { | ||||
|         <mat-chip-option [value]="faculty.id" color="accent"> | ||||
|           {{ faculty.name }} | ||||
|         </mat-chip-option> | ||||
|       } @empty { | ||||
|         <app-loading-indicator [loading]="facultiesLoaded !== null" | ||||
|                                (retryFunction)="loadFaculties()"/> | ||||
|       } | ||||
|       @if (faculties === null) { | ||||
|         <app-loading-indicator [loading]="true" | ||||
|                                (retryFunction)="loadFaculties()" | ||||
|                                #facultyIndicator/> | ||||
|       } | ||||
|     </mat-chip-listbox> | ||||
|   </mat-expansion-panel> | ||||
| @@ -23,14 +25,18 @@ | ||||
|         Курс | ||||
|       </mat-panel-title> | ||||
|     </mat-expansion-panel-header> | ||||
|     <mat-chip-listbox hideSingleSelectionIndicator (change)="chooseCourseNumber($event)" [formControl]="formChipCourse"> | ||||
|     <mat-chip-listbox hideSingleSelectionIndicator (change)="onCourseSelected($event.value)" | ||||
|                       [formControl]="formChipCourse" | ||||
|                       #courseChip> | ||||
|       @for (course of courseNumbers; track $index) { | ||||
|         <mat-chip-option [value]="course" color="accent"> | ||||
|           {{ course }} | ||||
|         </mat-chip-option> | ||||
|       } @empty { | ||||
|         <app-loading-indicator [loading]="groupsLoaded !== null" | ||||
|                                (retryFunction)="loadCourseGroup()"/> | ||||
|       } | ||||
|       @if (courseNumbers === null) { | ||||
|         <app-loading-indicator [loading]="true" | ||||
|                                (retryFunction)="loadCourseGroup()" | ||||
|                                #courseIndicator/> | ||||
|       } | ||||
|     </mat-chip-listbox> | ||||
|   </mat-expansion-panel> | ||||
| @@ -41,14 +47,18 @@ | ||||
|         Группа | ||||
|       </mat-panel-title> | ||||
|     </mat-expansion-panel-header> | ||||
|     <mat-chip-listbox hideSingleSelectionIndicator (change)="chooseGroup($event)" [formControl]="formChipGroup"> | ||||
|     <mat-chip-listbox hideSingleSelectionIndicator (change)="onGroupSelected($event.value)" | ||||
|                       [formControl]="formChipGroup" | ||||
|                       #groupChip> | ||||
|       @for (group of filteredGroups; track $index) { | ||||
|         <mat-chip-option [value]="group.id" color="accent"> | ||||
|           {{ group.name }} | ||||
|         </mat-chip-option> | ||||
|       } @empty { | ||||
|         <app-loading-indicator [loading]="groupsLoaded !== null" | ||||
|                                (retryFunction)="loadCourseGroup()"/> | ||||
|       } | ||||
|       @if (faculties === null) { | ||||
|         <app-loading-indicator [loading]="true" | ||||
|                                (retryFunction)="loadCourseGroup()" | ||||
|                                #groupIndicator/> | ||||
|       } | ||||
|     </mat-chip-listbox> | ||||
|   </mat-expansion-panel> | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import {Component, EventEmitter, Output, ViewChild} from '@angular/core'; | ||||
| import {MatExpansionModule, MatExpansionPanel} from "@angular/material/expansion"; | ||||
| import {MatChipListboxChange, MatChipsModule} from '@angular/material/chips'; | ||||
| import {MatChipListbox, MatChipsModule} from '@angular/material/chips'; | ||||
| import {FormControl, FormsModule, ReactiveFormsModule} from "@angular/forms"; | ||||
| import {catchError} from "rxjs"; | ||||
| import {LoadingIndicatorComponent} from "@component/common/loading-indicator/loading-indicator.component"; | ||||
| @@ -8,6 +8,12 @@ import {GroupResponse} from "@api/v1/groupResponse"; | ||||
| import {FacultyResponse} from "@api/v1/facultyResponse"; | ||||
| import {FacultyService} from "@api/v1/faculty.service"; | ||||
| import {GroupService} from "@api/v1/group.service"; | ||||
| import {IScheduleTab} from "@component/schedule/tabs/ischedule-tab"; | ||||
| import { | ||||
|   TabSelect, | ||||
|   TabSelectType, | ||||
|   TabStorageComponent | ||||
| } from "@component/common/tab-storage/tab-storage.component"; | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'app-group', | ||||
| @@ -24,121 +30,159 @@ import {GroupService} from "@api/v1/group.service"; | ||||
|   providers: [FacultyService, GroupService] | ||||
| }) | ||||
|  | ||||
| export class GroupComponent { | ||||
| export class GroupComponent implements IScheduleTab { | ||||
|   protected faculties: FacultyResponse[] | null = null; | ||||
|   protected courseNumbers: number[] | null = null; | ||||
|   private groups: GroupResponse[] | null = null; | ||||
|   protected filteredGroups: GroupResponse[] | null = []; | ||||
|  | ||||
|   protected facultyId: number | null = null; | ||||
|   protected courseNumber: number | null = null; | ||||
|  | ||||
|   protected filteredGroups: GroupResponse[] = []; | ||||
|   protected courseNumbers: number[] = []; | ||||
|   private groups: GroupResponse[] = []; | ||||
|  | ||||
|   protected formChipCourse: FormControl = new FormControl(); | ||||
|   protected formChipGroup: FormControl = new FormControl(); | ||||
|  | ||||
|   protected faculties: FacultyResponse[] = []; | ||||
|  | ||||
|   @ViewChild('courseNumberPanel') courseNumberPanel!: MatExpansionPanel; | ||||
|   @ViewChild('groupPanel') groupPanel!: MatExpansionPanel; | ||||
|  | ||||
|   protected facultiesLoaded: boolean | null = false; | ||||
|   protected groupsLoaded: boolean | null = false; | ||||
|   @ViewChild('facultyChip') facultyChip!: MatChipListbox; | ||||
|   @ViewChild('courseChip') courseChip!: MatChipListbox; | ||||
|   @ViewChild('groupChip') groupChip!: MatChipListbox; | ||||
|  | ||||
|   private readonly selected: TabSelect[] | null = null; | ||||
|  | ||||
|   @ViewChild('facultyIndicator') facultyIndicator!: LoadingIndicatorComponent; | ||||
|   @ViewChild('courseIndicator') courseIndicator!: LoadingIndicatorComponent; | ||||
|   @ViewChild('groupIndicator') groupIndicator!: LoadingIndicatorComponent; | ||||
|  | ||||
|   @Output() eventResult = new EventEmitter<number>(); | ||||
|  | ||||
|   constructor(private facultyApi: FacultyService, private groupApi: GroupService) { | ||||
|     this.loadFaculties(); | ||||
|     let selectedData = TabStorageComponent.selected; | ||||
|     if (selectedData !== null && selectedData.selected !== null) { | ||||
|       if (selectedData.type === TabSelectType.group) | ||||
|         this.selected = selectedData.selected; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   protected loadFaculties() { | ||||
|     this.facultiesLoaded = false; | ||||
|     this.facultyApi.getFaculties() | ||||
|       .pipe(catchError(error => { | ||||
|         this.facultiesLoaded = null; | ||||
|         this.facultyIndicator.loading = false; | ||||
|         throw error; | ||||
|       })) | ||||
|       .subscribe(data => { | ||||
|         this.faculties = data; | ||||
|         this.facultiesLoaded = true; | ||||
|         if (this.selected !== null && this.selected.length >= 1) { | ||||
|           let selectedFaculty = data.find(x => x.id === this.selected![0].index); | ||||
|  | ||||
|           if (selectedFaculty === undefined || selectedFaculty.name !== this.selected[0].name) | ||||
|             selectedFaculty = data.find(x => x.name === this.selected![0].name); | ||||
|  | ||||
|           if (selectedFaculty !== undefined) { | ||||
|             TabStorageComponent.trySelectChip(selectedFaculty.id, this.facultyChip); | ||||
|             this.onFacultySelected(selectedFaculty.id); | ||||
|           } | ||||
|         } | ||||
|       }); | ||||
|   } | ||||
|  | ||||
|   private filteringCourseNumber() { | ||||
|     this.courseNumbers = Array.from( | ||||
|       new Set( | ||||
|         this.groups | ||||
|           .filter(x => x.facultyId === this.facultyId) | ||||
|           .map(x => x.courseNumber) | ||||
|       ) | ||||
|     ).sort((a, b) => a - b); | ||||
|   } | ||||
|  | ||||
|   private filteringGroup() { | ||||
|     this.filteredGroups = this.groups.filter(x => x.facultyId === this.facultyId && x.courseNumber === this.courseNumber); | ||||
|   } | ||||
|  | ||||
|   protected loadCourseGroup() { | ||||
|     if (this.groups.length === 0) { | ||||
|       this.groupsLoaded = false; | ||||
|     if (this.facultyId === null) | ||||
|       return; | ||||
|  | ||||
|       this.groupApi.getGroups().pipe( | ||||
|         catchError(error => { | ||||
|           this.groupsLoaded = null; | ||||
|     if (this.groups === null || this.groups.length === 0 || this.groups[0].facultyId !== this.facultyId) { | ||||
|       this.groupApi.getByFaculty(this.facultyId) | ||||
|         .pipe(catchError(error => { | ||||
|           this.groupIndicator.loading = false; | ||||
|           this.courseIndicator.loading = false; | ||||
|           throw error; | ||||
|         }) | ||||
|       ).subscribe(data => { | ||||
|         })) | ||||
|         .subscribe(data => { | ||||
|           this.groups = data; | ||||
|         if (this.courseNumber === null) | ||||
|           this.filteringCourseNumber(); | ||||
|         else | ||||
|           this.filteringGroup(); | ||||
|           this.courseNumbers = Array.from( | ||||
|             new Set( | ||||
|               this.groups! | ||||
|                 .map(x => x.courseNumber) | ||||
|                 .sort((a, b) => a - b)) | ||||
|           ); | ||||
|  | ||||
|         this.groupsLoaded = true; | ||||
|           if (this.selected !== null && this.selected.length >= 2) { | ||||
|             let selectedCourse = this.courseNumbers.find(x => x === this.selected![1].index); | ||||
|  | ||||
|             if (selectedCourse !== undefined) { | ||||
|               TabStorageComponent.trySelectChip(selectedCourse, this.courseChip); | ||||
|               this.onCourseSelected(selectedCourse); | ||||
|             } | ||||
|           } | ||||
|  | ||||
|           if (this.selected !== null && this.selected.length >= 3) { | ||||
|             let selectedGroup = data.find(x => x.id === this.selected![2].index); | ||||
|  | ||||
|             if (selectedGroup === undefined || selectedGroup.name !== this.selected[2].name) | ||||
|               selectedGroup = data.find(x => x.name === this.selected![2].name); | ||||
|  | ||||
|             if (selectedGroup !== undefined) { | ||||
|               TabStorageComponent.trySelectChip(selectedGroup.id, this.groupChip); | ||||
|               this.onGroupSelected(selectedGroup.id); | ||||
|             } | ||||
|           } | ||||
|         }); | ||||
|  | ||||
|       return | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (this.courseNumber === null) | ||||
|       this.filteringCourseNumber(); | ||||
|     else | ||||
|       this.filteringGroup(); | ||||
|     if (this.courseNumber !== null) | ||||
|       this.filteredGroups = this.groups!.filter(x => x.courseNumber === this.courseNumber); | ||||
|   } | ||||
|  | ||||
|   protected chooseFaculty(event: MatChipListboxChange) { | ||||
|   protected onFacultySelected(index: number) { | ||||
|     this.courseNumber = null; | ||||
|     this.groups = []; | ||||
|     this.formChipGroup.reset(); | ||||
|     this.formChipCourse.reset(); | ||||
|  | ||||
|     if (event.value === undefined || event.value === null) { | ||||
|     if (index === undefined) { | ||||
|       this.facultyId = null; | ||||
|       return; | ||||
|     } | ||||
|     this.facultyId = event.value; | ||||
|  | ||||
|     TabStorageComponent.select(new TabSelect(index, this.faculties!.find(x => x.id === index)?.name ?? ''), TabSelectType.group, 0); | ||||
|  | ||||
|     this.facultyId = index; | ||||
|     this.courseNumberPanel.open(); | ||||
|  | ||||
|     this.loadCourseGroup(); | ||||
|   } | ||||
|  | ||||
|   protected chooseCourseNumber(event: MatChipListboxChange) { | ||||
|   protected onCourseSelected(course: number) { | ||||
|     this.filteredGroups = []; | ||||
|     this.formChipGroup.reset(); | ||||
|  | ||||
|     if (event.value === undefined || event.value === null) { | ||||
|     if (course === undefined) { | ||||
|       this.courseNumber = null; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     this.courseNumber = event.value; | ||||
|     TabStorageComponent.select(new TabSelect(course, course.toString()), TabSelectType.group, 1); | ||||
|  | ||||
|     this.courseNumber = course; | ||||
|     this.groupPanel.open(); | ||||
|     this.loadCourseGroup(); | ||||
|   } | ||||
|  | ||||
|   protected chooseGroup(event: MatChipListboxChange) { | ||||
|     if (event.value === undefined || event.value === null) | ||||
|   protected onGroupSelected(index: number) { | ||||
|     if (index === undefined) | ||||
|       return; | ||||
|  | ||||
|     TabStorageComponent.select(new TabSelect(index, this.groups!.find(x => x.id == index)?.name ?? ''), TabSelectType.group, 2); | ||||
|  | ||||
|     this.groupPanel.close(); | ||||
|     this.eventResult.emit(event.value); | ||||
|     this.eventResult.emit(index); | ||||
|   } | ||||
|  | ||||
|   public load() { | ||||
|     if (this.faculties === null) | ||||
|       this.loadFaculties(); | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										6
									
								
								src/components/schedule/tabs/ischedule-tab.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/components/schedule/tabs/ischedule-tab.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| import {EventEmitter} from "@angular/core"; | ||||
|  | ||||
| export interface IScheduleTab { | ||||
|   load(): void; | ||||
|   eventResult: EventEmitter<number>; | ||||
| } | ||||
| @@ -5,13 +5,14 @@ | ||||
|         Кампус | ||||
|       </mat-panel-title> | ||||
|     </mat-expansion-panel-header> | ||||
|     <mat-chip-listbox hideSingleSelectionIndicator (change)="chooseCampus($event)"> | ||||
|     <mat-chip-listbox hideSingleSelectionIndicator (change)="onCampusSelected($event.value)" #campusChip> | ||||
|       @for (campus of campuses; track $index) { | ||||
|         <mat-chip-option [value]="campus.id" color="accent"> | ||||
|           {{ campus.codeName }} | ||||
|         </mat-chip-option> | ||||
|       } @empty { | ||||
|         <app-loading-indicator [loading]="campusesLoaded !== null" (retryFunction)="loadCampuses()"/> | ||||
|       } | ||||
|       @if (campuses === null) { | ||||
|         <app-loading-indicator [loading]="true" (retryFunction)="loadCampuses()" #campusIndicator/> | ||||
|       } | ||||
|     </mat-chip-listbox> | ||||
|   </mat-expansion-panel> | ||||
| @@ -22,13 +23,14 @@ | ||||
|         Кабинет | ||||
|       </mat-panel-title> | ||||
|     </mat-expansion-panel-header> | ||||
|     <mat-chip-listbox hideSingleSelectionIndicator (change)="chooseLectureHall($event)" [formControl]="chipLecture"> | ||||
|     <mat-chip-listbox hideSingleSelectionIndicator (change)="onLectureHallSelected($event.value)" [formControl]="formLectureHalls" #lectureChip> | ||||
|       @for (lectureHall of lectureHallsFiltered; track $index) { | ||||
|         <mat-chip-option [value]="lectureHall.id" color="accent"> | ||||
|           {{ lectureHall.name }} | ||||
|         </mat-chip-option> | ||||
|       } @empty { | ||||
|         <app-loading-indicator [loading]="lectureHallsLoaded !== null" (retryFunction)="loadLectureHalls()"/> | ||||
|       } | ||||
|       @if (lectureHallsFiltered === null) { | ||||
|         <app-loading-indicator [loading]="true" (retryFunction)="loadLectureHalls()" #lectureIndicator/> | ||||
|       } | ||||
|     </mat-chip-listbox> | ||||
|   </mat-expansion-panel> | ||||
|   | ||||
| @@ -1,14 +1,16 @@ | ||||
| import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core'; | ||||
| import {Component, EventEmitter, Output, ViewChild} from '@angular/core'; | ||||
| import {AsyncPipe} from "@angular/common"; | ||||
| import {MatAccordion, MatExpansionModule, MatExpansionPanel} from "@angular/material/expansion"; | ||||
| import {MatChipListboxChange, MatChipsModule} from "@angular/material/chips"; | ||||
| import {catchError, Observable, of} from "rxjs"; | ||||
| import {MatChipListbox, MatChipsModule} from "@angular/material/chips"; | ||||
| import {catchError} from "rxjs"; | ||||
| import {FormControl, ReactiveFormsModule} from "@angular/forms"; | ||||
| import {LoadingIndicatorComponent} from "@component/common/loading-indicator/loading-indicator.component"; | ||||
| import {CampusBasicInfoResponse} from "@api/v1/campusBasicInfoResponse"; | ||||
| import {LectureHallResponse} from "@api/v1/lectureHallResponse"; | ||||
| import {CampusService} from "@api/v1/campus.service"; | ||||
| import {LectureHallService} from "@api/v1/lectureHall.service"; | ||||
| import {IScheduleTab} from "@component/schedule/tabs/ischedule-tab"; | ||||
| import {TabSelect, TabSelectType, TabStorageComponent} from "@component/common/tab-storage/tab-storage.component"; | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'app-lecture-hall', | ||||
| @@ -26,79 +28,115 @@ import {LectureHallService} from "@api/v1/lectureHall.service"; | ||||
|   providers: [CampusService, LectureHallService] | ||||
| }) | ||||
|  | ||||
| export class LectureHallComponent { | ||||
| export class LectureHallComponent implements IScheduleTab { | ||||
|   protected campusId: number | null = null; | ||||
|   protected chipLecture: FormControl = new FormControl(); | ||||
|   protected formLectureHalls: FormControl = new FormControl(); | ||||
|  | ||||
|   @ViewChild('lecturePanel') lecturePanel!: MatExpansionPanel; | ||||
|  | ||||
|   protected campuses: CampusBasicInfoResponse[] = []; | ||||
|   protected campusesLoaded: boolean | null = false; | ||||
|  | ||||
|   private lectureHalls: LectureHallResponse[] = []; | ||||
|   protected lectureHallsFiltered: LectureHallResponse[] = []; | ||||
|   protected lectureHallsLoaded: boolean | null = false; | ||||
|   protected campuses: CampusBasicInfoResponse[] | null = null; | ||||
|   protected lectureHallsFiltered: LectureHallResponse[] | null = null; | ||||
|  | ||||
|   @Output() eventResult = new EventEmitter<number>(); | ||||
|  | ||||
|   @ViewChild('lecturePanel') lecturePanel!: MatExpansionPanel; | ||||
|   @ViewChild('lectureIndicator') lectureIndicator!: LoadingIndicatorComponent; | ||||
|   @ViewChild('campusIndicator') campusIndicator!: LoadingIndicatorComponent; | ||||
|  | ||||
|   @ViewChild('campusChip') campusChip!: MatChipListbox; | ||||
|   @ViewChild('lectureChip') lectureChip!: MatChipListbox; | ||||
|  | ||||
|   private lectureHalls: LectureHallResponse[] | null = null; | ||||
|   private readonly selected: TabSelect[] | null = null; | ||||
|  | ||||
|   constructor(private campusApi: CampusService, private lectureHallApi: LectureHallService) { | ||||
|     this.loadCampuses(); | ||||
|     let selectedData = TabStorageComponent.selected; | ||||
|     if (selectedData !== null && selectedData.selected !== null) { | ||||
|       if (selectedData.type === TabSelectType.lecture) | ||||
|         this.selected = selectedData.selected; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   protected loadCampuses() { | ||||
|     this.campusesLoaded = false; | ||||
|     this.campusApi.getCampus() | ||||
|       .pipe(catchError(error => { | ||||
|         this.campusesLoaded = null; | ||||
|         this.campusIndicator.loading = false; | ||||
|         throw error; | ||||
|       })) | ||||
|       .subscribe(data => { | ||||
|         this.campuses = data; | ||||
|         this.campusesLoaded = true; | ||||
|  | ||||
|         if (this.selected !== null && this.selected.length >= 1) { | ||||
|           let selectedCampus = data.find(x => x.id === this.selected![0].index); | ||||
|  | ||||
|           if (selectedCampus === undefined || selectedCampus.codeName !== this.selected![0].name) | ||||
|             selectedCampus = data.find(x => x.codeName === this.selected![0].name); | ||||
|  | ||||
|           if (selectedCampus !== undefined) { | ||||
|             TabStorageComponent.trySelectChip(selectedCampus.id, this.campusChip); | ||||
|             this.onCampusSelected(selectedCampus.id); | ||||
|           } | ||||
|         } | ||||
|       }); | ||||
|   } | ||||
|  | ||||
|   private filteringLectureHalls() { | ||||
|     this.lectureHallsFiltered = this.lectureHalls.filter(x => x.campusId === this.campusId); | ||||
|     this.lectureHallsFiltered = this.lectureHalls?.filter(x => x.campusId === this.campusId) ?? null; | ||||
|   } | ||||
|  | ||||
|   protected chooseCampus(event: MatChipListboxChange) { | ||||
|     this.chipLecture.reset(); | ||||
|   protected onCampusSelected(index: number) { | ||||
|     this.formLectureHalls.reset(); | ||||
|  | ||||
|     if (event.value === undefined || event.value === null) { | ||||
|     TabStorageComponent.select(new TabSelect(index, this.campuses!.find(x => x.id === index)?.codeName ?? ''), TabSelectType.lecture, 0); | ||||
|  | ||||
|     if (index === undefined) { | ||||
|       this.campusId = null; | ||||
|       this.lectureHalls = []; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     this.campusId = event.value; | ||||
|     this.campusId = index; | ||||
|     this.lecturePanel.open(); | ||||
|  | ||||
|     if (this.lectureHalls.length === 0) | ||||
|     if (this.lectureHalls === null) | ||||
|       this.loadLectureHalls(); | ||||
|     else | ||||
|       this.filteringLectureHalls(); | ||||
|   } | ||||
|  | ||||
|   protected loadLectureHalls() { | ||||
|     this.lectureHallsLoaded = false; | ||||
|     this.lectureHallApi.getLectureHalls() | ||||
|       .pipe(catchError(error => { | ||||
|         this.lectureHallsLoaded = null; | ||||
|         this.lectureIndicator.loading = false; | ||||
|         throw error; | ||||
|       })) | ||||
|       .subscribe(data => { | ||||
|         this.lectureHalls = data; | ||||
|         this.filteringLectureHalls(); | ||||
|         this.lectureHallsLoaded = true; | ||||
|         if (this.selected !== null && this.selected.length >= 2) { | ||||
|           let selectedLecture = data.find(x => x.id === this.selected![1].index); | ||||
|  | ||||
|           if (selectedLecture === undefined || selectedLecture.name !== this.selected![1].name) | ||||
|             selectedLecture = data.find(x => x.name === this.selected![1].name); | ||||
|  | ||||
|           if (selectedLecture !== undefined) { | ||||
|             TabStorageComponent.trySelectChip(selectedLecture.id, this.lectureChip); | ||||
|             this.onLectureHallSelected(selectedLecture.id); | ||||
|           } | ||||
|         } | ||||
|       }); | ||||
|   } | ||||
|  | ||||
|   protected chooseLectureHall(event: MatChipListboxChange) { | ||||
|     if (event.value === undefined || event.value === null) | ||||
|   protected onLectureHallSelected(index: number) { | ||||
|     if (index === undefined) | ||||
|       return; | ||||
|  | ||||
|     TabStorageComponent.select(new TabSelect(index, this.lectureHallsFiltered!.find(x => x.id === index)?.name ?? ''), TabSelectType.lecture, 1); | ||||
|  | ||||
|     this.lecturePanel.close(); | ||||
|     this.eventResult.emit(event.value); | ||||
|     this.eventResult.emit(index); | ||||
|   } | ||||
|  | ||||
|   public load() { | ||||
|     if (this.campuses === null) | ||||
|       this.loadCampuses(); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,12 @@ | ||||
| <div class="search-content"> | ||||
|   @if (professors.length === 0) { | ||||
|     <app-loading-indicator [loading]="professorsLoaded !== null" (retryFunction)="loadProfessors()"/> | ||||
|   @if (professors === null) { | ||||
|     <app-loading-indicator [loading]="true" (retryFunction)="loadProfessors()" | ||||
|                            #professorIndicator/> | ||||
|   } @else { | ||||
|     <mat-form-field color="accent" style="width: 100%;"> | ||||
|       <input type="text" placeholder="Поиск..." matInput [formControl]="professorControl" [matAutocomplete]="auto"> | ||||
|  | ||||
|       <mat-autocomplete #auto="matAutocomplete" (optionSelected)="onOptionSelected($event)" | ||||
|       <mat-autocomplete #auto="matAutocomplete" (optionSelected)="onOptionSelected($event.option.value)" | ||||
|                         [autoActiveFirstOption]="false" [hideSingleSelectionIndicator]="true"> | ||||
|         @for (option of filteredProfessors | async; track option) { | ||||
|           <mat-option [value]="option.id"> | ||||
|   | ||||
| @@ -1,12 +1,14 @@ | ||||
| import {Component, EventEmitter, Input, OnInit, Output} from "@angular/core"; | ||||
| import {Component, EventEmitter, OnInit, Output, ViewChild} from "@angular/core"; | ||||
| import {MatFormField, MatInput} from "@angular/material/input"; | ||||
| import {FormControl, ReactiveFormsModule} from "@angular/forms"; | ||||
| import {MatAutocompleteModule, MatAutocompleteSelectedEvent} from "@angular/material/autocomplete"; | ||||
| import {MatAutocompleteModule} from "@angular/material/autocomplete"; | ||||
| import {AsyncPipe} from "@angular/common"; | ||||
| import {catchError, map, Observable, startWith} from "rxjs"; | ||||
| import {LoadingIndicatorComponent} from "@component/common/loading-indicator/loading-indicator.component"; | ||||
| import {ProfessorResponse} from "@api/v1/professorResponse"; | ||||
| import {ProfessorService} from "@api/v1/professor.service"; | ||||
| import {IScheduleTab} from "@component/schedule/tabs/ischedule-tab"; | ||||
| import {TabSelect, TabSelectType, TabStorageComponent} from "@component/common/tab-storage/tab-storage.component"; | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'app-professor', | ||||
| @@ -23,31 +25,44 @@ import {ProfessorService} from "@api/v1/professor.service"; | ||||
|   styleUrl: './professor.component.css', | ||||
|   providers: [ProfessorService] | ||||
| }) | ||||
| export class ProfessorComponent implements OnInit { | ||||
| export class ProfessorComponent implements OnInit, IScheduleTab { | ||||
|   protected professorControl = new FormControl(); | ||||
|   protected filteredProfessors!: Observable<ProfessorResponse[]>; | ||||
|  | ||||
|   protected professors: ProfessorResponse[] = []; | ||||
|   protected professorsLoaded: boolean | null = false; | ||||
|   protected professors: ProfessorResponse[] | null = null; | ||||
|   private readonly selected: TabSelect[] | null = null; | ||||
|  | ||||
|   @ViewChild('professorIndicator') professorIndicator!: LoadingIndicatorComponent; | ||||
|  | ||||
|   @Output() eventResult = new EventEmitter<number>(); | ||||
|  | ||||
|   constructor(private api: ProfessorService) { | ||||
|     this.loadProfessors(); | ||||
|     let selectedData = TabStorageComponent.selected; | ||||
|     if (selectedData !== null && selectedData.selected !== null) { | ||||
|       if (selectedData.type === TabSelectType.professor) | ||||
|         this.selected = selectedData.selected; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   protected loadProfessors() { | ||||
|     if (this.professors.length === 0) { | ||||
|       this.professorsLoaded = false; | ||||
|  | ||||
|     if (this.professors === null || this.professors.length === 0) { | ||||
|       this.api.getProfessors() | ||||
|         .pipe(catchError(error => { | ||||
|           this.professorsLoaded = null; | ||||
|           this.professorIndicator.loading = false; | ||||
|           throw error; | ||||
|         })) | ||||
|         .subscribe(data => { | ||||
|           this.professors = data; | ||||
|           this.professorsLoaded = true; | ||||
|  | ||||
|           if (this.selected !== null && this.selected.length >= 1) { | ||||
|             let selectedProfessor = data.find(x => x.id === this.selected![0].index); | ||||
|  | ||||
|             if (selectedProfessor === undefined || selectedProfessor.name !== this.selected[0].name) | ||||
|               selectedProfessor = data.find(x => x.name === this.selected![0].name); | ||||
|  | ||||
|             if (selectedProfessor !== undefined) | ||||
|               this.onOptionSelected(selectedProfessor.id); | ||||
|           } | ||||
|         }); | ||||
|     } | ||||
|   } | ||||
| @@ -61,20 +76,32 @@ export class ProfessorComponent implements OnInit { | ||||
|  | ||||
|   private _filterProfessors(value: string | number): ProfessorResponse[] { | ||||
|     if (typeof value === 'string') { | ||||
|       if (value === '') return []; | ||||
|       if (value === '') | ||||
|         return []; | ||||
|  | ||||
|       const filterValue = value.toLowerCase(); | ||||
|       return this.professors.filter(teacher => teacher.name.toLowerCase().includes(filterValue)); | ||||
|       return this.professors?.filter(teacher => teacher.name.toLowerCase().includes(filterValue)) ?? []; | ||||
|     } else { | ||||
|       const selectedTeacher = this.professors.find(teacher => teacher.id === value); | ||||
|       const selectedTeacher = this.professors?.find(teacher => teacher.id === value); | ||||
|       return selectedTeacher ? [selectedTeacher] : []; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   protected onOptionSelected(event: MatAutocompleteSelectedEvent) { | ||||
|     const selectedOption = this.professors.find(teacher => teacher.id === event.option.value); | ||||
|   protected onOptionSelected(index: number) { | ||||
|     if (index === undefined) | ||||
|       return; | ||||
|  | ||||
|     const selectedOption = this.professors?.find(teacher => teacher.id === index); | ||||
|     if (selectedOption) { | ||||
|       this.professorControl.setValue(selectedOption.name); | ||||
|       this.eventResult.emit(selectedOption.id); | ||||
|  | ||||
|       TabStorageComponent.select(new TabSelect(selectedOption.id, selectedOption.name), TabSelectType.professor, 0); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   public load() { | ||||
|     if (this.professors === null) | ||||
|       this.loadProfessors(); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| import {Component, EventEmitter, Output} from '@angular/core'; | ||||
| import {OtherComponent} from "@component/schedule/tabs/other/other.component"; | ||||
| import {MatTab, MatTabChangeEvent, MatTabGroup} from "@angular/material/tabs"; | ||||
| import {AfterViewInit, Component, EventEmitter, Output, ViewChild} from '@angular/core'; | ||||
| import {OtherComponent, SelectData} from "@component/schedule/tabs/other/other.component"; | ||||
| import {MatTab, MatTabGroup} from "@angular/material/tabs"; | ||||
| import {map, Observable} from "rxjs"; | ||||
| import {ReactiveFormsModule} from "@angular/forms"; | ||||
| import {FormsModule, ReactiveFormsModule} from "@angular/forms"; | ||||
| import {MatButton} from "@angular/material/button"; | ||||
| import {DataSpinnerComponent} from "@component/common/data-spinner/data-spinner.component"; | ||||
| import {GroupComponent} from "@component/schedule/tabs/group/group.component"; | ||||
| @@ -10,6 +10,14 @@ import {ProfessorComponent} from "@component/schedule/tabs/professor/professor.c | ||||
| import {LectureHallComponent} from "@component/schedule/tabs/lecture-hall/lecture-hall.component"; | ||||
| import {ScheduleService} from "@api/v1/schedule.service"; | ||||
| import {ScheduleResponse} from "@api/v1/scheduleResponse"; | ||||
| import {IScheduleTab} from "@component/schedule/tabs/ischedule-tab"; | ||||
| import {DisciplineService} from "@api/v1/discipline.service"; | ||||
| import {LectureHallService} from "@api/v1/lectureHall.service"; | ||||
| import {GroupService} from "@api/v1/group.service"; | ||||
| import {ProfessorService} from "@api/v1/professor.service"; | ||||
| import {AuthRoles} from "@model/AuthRoles"; | ||||
| import {HasRoleDirective} from "@/directives/has-role.directive"; | ||||
| import {TabStorageComponent} from "@component/common/tab-storage/tab-storage.component"; | ||||
|  | ||||
| export enum TabsSelect { | ||||
|   Group, | ||||
| @@ -30,17 +38,34 @@ export enum TabsSelect { | ||||
|     DataSpinnerComponent, | ||||
|     GroupComponent, | ||||
|     ProfessorComponent, | ||||
|     LectureHallComponent | ||||
|     LectureHallComponent, | ||||
|     FormsModule, | ||||
|     HasRoleDirective | ||||
|   ], | ||||
|   templateUrl: './tabs.component.html', | ||||
|   styleUrl: './tabs.component.css', | ||||
|   providers: [ScheduleService] | ||||
|   providers: [ScheduleService, DisciplineService, LectureHallService, GroupService, ProfessorService] | ||||
| }) | ||||
|  | ||||
| export class TabsComponent { | ||||
| export class TabsComponent implements AfterViewInit { | ||||
|   @Output() eventResult = new EventEmitter<[TabsSelect, number, Observable<ScheduleResponse[]>]>(); | ||||
|  | ||||
|   constructor(private scheduleApi: ScheduleService) { | ||||
|   constructor(private scheduleApi: ScheduleService, | ||||
|               private disciplineApi: DisciplineService, | ||||
|               private lectureApi: LectureHallService, | ||||
|               private groupApi: GroupService, | ||||
|               private professorApi: ProfessorService) { | ||||
|   } | ||||
|  | ||||
|   ngAfterViewInit(): void { | ||||
|     let selected = TabStorageComponent.selected; | ||||
|  | ||||
|     let index = 0; | ||||
|     if (selected !== null) | ||||
|       index = selected.type; | ||||
|  | ||||
|     this.chooseTabs(index).then(); | ||||
|     this.tabs.selectedIndex = index; | ||||
|   } | ||||
|  | ||||
|   protected groupSelected(id: number) { | ||||
| @@ -145,15 +170,74 @@ export class TabsComponent { | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   protected async chooseTabs(event: MatTabChangeEvent) { | ||||
|     switch (event.index) { | ||||
|   protected async chooseTabs(index: number) { | ||||
|     switch (index) { | ||||
|       case 0: | ||||
|         this.groupTab.load(); | ||||
|         break; | ||||
|       case 1: | ||||
|         this.professorTab.load(); | ||||
|         break; | ||||
|       case 2: | ||||
|         this.lectureHallTab.load(); | ||||
|         break; | ||||
|       case 3: | ||||
|         await this.loadDisciplines(); | ||||
|         await this.loadLectureHalls(); | ||||
|         await this.loadGroups(); | ||||
|         await this.loadProfessors(); | ||||
|         break; | ||||
|       default: | ||||
|         await this.chooseTabs(0); | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|   protected async loadDisciplines() { | ||||
|     this.disciplineApi.getDisciplines().subscribe(data => { | ||||
|       this.disciplineEx.Data = data.map(x => ({ | ||||
|         id: x.id, | ||||
|         name: x.name | ||||
|       }) as SelectData); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   protected async loadLectureHalls() { | ||||
|     this.lectureApi.getLectureHalls().subscribe(data => { | ||||
|       this.lectureHallEx.Data = data.map(x => ({ | ||||
|         id: x.id, | ||||
|         name: x.name | ||||
|       }) as SelectData); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   protected async loadGroups() { | ||||
|     this.groupApi.getGroups().subscribe(data => { | ||||
|       this.groupEx.Data = data.map(x => ({ | ||||
|         id: x.id, | ||||
|         name: x.name | ||||
|       }) as SelectData); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   protected async loadProfessors() { | ||||
|     this.professorApi.getProfessors().subscribe(data => { | ||||
|       this.professorEx.Data = data.map(x => ({ | ||||
|         id: x.id, | ||||
|         name: x.name | ||||
|       }) as SelectData); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   @ViewChild('groupTab') groupTab!: IScheduleTab; | ||||
|   @ViewChild('professorTab') professorTab!: IScheduleTab; | ||||
|   @ViewChild('lectureHallTab') lectureHallTab!: IScheduleTab; | ||||
|  | ||||
|   @ViewChild('discipline') disciplineEx!: OtherComponent; | ||||
|   @ViewChild('lecture') lectureHallEx!: OtherComponent; | ||||
|   @ViewChild('group') groupEx!: OtherComponent; | ||||
|   @ViewChild('professor') professorEx!: OtherComponent; | ||||
|    */ | ||||
|  | ||||
|   @ViewChild('tabGroup') tabs!: MatTabGroup; | ||||
|   protected readonly AuthRoles = AuthRoles; | ||||
| } | ||||
|   | ||||
| @@ -70,8 +70,6 @@ export class ScheduleComponent { | ||||
|             break; | ||||
|           case TabsSelect.Professor: | ||||
|             let indexProfessor = this.data[0].professorsId.findIndex(p => p === data[1]); | ||||
|             console.log(indexProfessor); | ||||
|             console.log(data[1]); | ||||
|             this.childComponent.AdditionalText(AdditionalText.Professor, this.data[0].professors[indexProfessor]); | ||||
|             break; | ||||
|           case TabsSelect.LectureHall: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user