Compare commits
6 Commits
95a593bdb6
...
8acb30c2c3
Author | SHA1 | Date | |
---|---|---|---|
8acb30c2c3 | |||
1d79860df3 | |||
48bc7ca005 | |||
3cea5f7982 | |||
e0a2ba257c | |||
2e36b06aea |
@ -1,6 +1,6 @@
|
|||||||
# MIREA schedule by Winsomnia
|
# MIREA schedule by Winsomnia
|
||||||
|
|
||||||
[![Angular Release](https://img.shields.io/badge/v18.1-8?style=flat-square&label=Angular&labelColor=512BD4&color=606060)](https://github.com/angular/angular-cli)
|
[![Angular Release](https://img.shields.io/badge/v18.2-8?style=flat-square&label=Angular&labelColor=512BD4&color=606060)](https://github.com/angular/angular-cli)
|
||||||
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](https://opensource.org/licenses/MIT)
|
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](https://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
This project provides a Web interface for working with the MIREA schedule.
|
This project provides a Web interface for working with the MIREA schedule.
|
||||||
|
2417
package-lock.json
generated
2417
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
30
package.json
30
package.json
@ -10,26 +10,26 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^18.1.3",
|
"@angular/animations": "^18.2.1",
|
||||||
"@angular/cdk": "~18.1.3",
|
"@angular/cdk": "~18.2.1",
|
||||||
"@angular/cdk-experimental": "^18.1.3",
|
"@angular/cdk-experimental": "^18.2.1",
|
||||||
"@angular/common": "^18.1.3",
|
"@angular/common": "^18.2.1",
|
||||||
"@angular/compiler": "^18.1.3",
|
"@angular/compiler": "^18.2.1",
|
||||||
"@angular/core": "^18.1.3",
|
"@angular/core": "^18.2.1",
|
||||||
"@angular/forms": "^18.1.3",
|
"@angular/forms": "^18.2.1",
|
||||||
"@angular/material": "~18.1.3",
|
"@angular/material": "~18.2.1",
|
||||||
"@angular/platform-browser": "^18.1.3",
|
"@angular/platform-browser": "^18.2.1",
|
||||||
"@angular/platform-browser-dynamic": "^18.1.3",
|
"@angular/platform-browser-dynamic": "^18.2.1",
|
||||||
"@angular/router": "^18.1.3",
|
"@angular/router": "^18.2.1",
|
||||||
"@progress/kendo-date-math": "^1.5.13",
|
"@progress/kendo-date-math": "^1.5.13",
|
||||||
"rxjs": "~7.8.1",
|
"rxjs": "~7.8.1",
|
||||||
"tslib": "^2.6.3",
|
"tslib": "^2.6.3",
|
||||||
"zone.js": "~0.14.8"
|
"zone.js": "~0.15.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^18.1.3",
|
"@angular-devkit/build-angular": "^18.2.1",
|
||||||
"@angular/cli": "^18.1.3",
|
"@angular/cli": "^18.2.1",
|
||||||
"@angular/compiler-cli": "^18.1.3",
|
"@angular/compiler-cli": "^18.2.1",
|
||||||
"@types/jasmine": "~5.1.4",
|
"@types/jasmine": "~5.1.4",
|
||||||
"jasmine-core": "~5.2.0",
|
"jasmine-core": "~5.2.0",
|
||||||
"karma": "~6.4.4",
|
"karma": "~6.4.4",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {catchError, filter, mergeMap, Observable, retryWhen, switchMap, take, tap, timer} from "rxjs";
|
import {catchError, filter, mergeMap, Observable, retryWhen, switchMap, tap, timer} from "rxjs";
|
||||||
import {HttpClient, HttpErrorResponse} from "@angular/common/http";
|
import {HttpClient, HttpErrorResponse} from "@angular/common/http";
|
||||||
import {NotifyColor, OpenNotifyService} from "@service/open-notify.service";
|
import {NotifyColor, OpenNotifyService} from "@service/open-notify.service";
|
||||||
import {environment} from "@environment";
|
import {environment} from "@environment";
|
||||||
@ -144,7 +144,7 @@ export default abstract class ApiService implements SetRequestBuilderAfterBuild
|
|||||||
|
|
||||||
private handleError(error: HttpErrorResponse): void {
|
private handleError(error: HttpErrorResponse): void {
|
||||||
// todo: change to Retry-After condition
|
// todo: change to Retry-After condition
|
||||||
if (error.error.toString().includes("setup")) {
|
if (error.error && error.error.toString().includes("setup")) {
|
||||||
this.router.navigate(['/setup/']).then();
|
this.router.navigate(['/setup/']).then();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ export class FacultyService extends ApiService {
|
|||||||
.setQueryParams({page: page, pageSize: pageSize})
|
.setQueryParams({page: page, pageSize: pageSize})
|
||||||
.build<ApiService>()
|
.build<ApiService>()
|
||||||
.get<FacultyResponse[]>();
|
.get<FacultyResponse[]>();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getById(id: number) {
|
public getById(id: number) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
<section class="mat-elevation-z8 table-section" tabindex="0">
|
<section class="mat-elevation-z8 table-section" tabindex="0" [style.overflow]="(dataSource.length === 0 || isLoad ? 'hidden' : 'auto')">
|
||||||
@if (dataSource.length === 0 || isLoad) {
|
@if (dataSource.length === 0 || isLoad) {
|
||||||
<div class="overlay">
|
<div class="overlay">
|
||||||
@if (isLoad) {
|
@if (isLoad) {
|
||||||
|
@ -5,14 +5,16 @@
|
|||||||
Факультет
|
Факультет
|
||||||
</mat-panel-title>
|
</mat-panel-title>
|
||||||
</mat-expansion-panel-header>
|
</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) {
|
@for (faculty of faculties; track $index) {
|
||||||
<mat-chip-option [value]="faculty.id" color="accent">
|
<mat-chip-option [value]="faculty.id" color="accent">
|
||||||
{{ faculty.name }}
|
{{ faculty.name }}
|
||||||
</mat-chip-option>
|
</mat-chip-option>
|
||||||
} @empty {
|
}
|
||||||
<app-loading-indicator [loading]="facultiesLoaded !== null"
|
@if (faculties === null) {
|
||||||
(retryFunction)="loadFaculties()"/>
|
<app-loading-indicator [loading]="true"
|
||||||
|
(retryFunction)="loadFaculties()"
|
||||||
|
#facultyIndicator/>
|
||||||
}
|
}
|
||||||
</mat-chip-listbox>
|
</mat-chip-listbox>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
@ -23,14 +25,18 @@
|
|||||||
Курс
|
Курс
|
||||||
</mat-panel-title>
|
</mat-panel-title>
|
||||||
</mat-expansion-panel-header>
|
</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) {
|
@for (course of courseNumbers; track $index) {
|
||||||
<mat-chip-option [value]="course" color="accent">
|
<mat-chip-option [value]="course" color="accent">
|
||||||
{{ course }}
|
{{ course }}
|
||||||
</mat-chip-option>
|
</mat-chip-option>
|
||||||
} @empty {
|
}
|
||||||
<app-loading-indicator [loading]="groupsLoaded !== null"
|
@if (courseNumbers === null) {
|
||||||
(retryFunction)="loadCourseGroup()"/>
|
<app-loading-indicator [loading]="true"
|
||||||
|
(retryFunction)="loadCourseGroup()"
|
||||||
|
#courseIndicator/>
|
||||||
}
|
}
|
||||||
</mat-chip-listbox>
|
</mat-chip-listbox>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
@ -41,14 +47,18 @@
|
|||||||
Группа
|
Группа
|
||||||
</mat-panel-title>
|
</mat-panel-title>
|
||||||
</mat-expansion-panel-header>
|
</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) {
|
@for (group of filteredGroups; track $index) {
|
||||||
<mat-chip-option [value]="group.id" color="accent">
|
<mat-chip-option [value]="group.id" color="accent">
|
||||||
{{ group.name }}
|
{{ group.name }}
|
||||||
</mat-chip-option>
|
</mat-chip-option>
|
||||||
} @empty {
|
}
|
||||||
<app-loading-indicator [loading]="groupsLoaded !== null"
|
@if (faculties === null) {
|
||||||
(retryFunction)="loadCourseGroup()"/>
|
<app-loading-indicator [loading]="true"
|
||||||
|
(retryFunction)="loadCourseGroup()"
|
||||||
|
#groupIndicator/>
|
||||||
}
|
}
|
||||||
</mat-chip-listbox>
|
</mat-chip-listbox>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {Component, EventEmitter, Output, ViewChild} from '@angular/core';
|
import {Component, EventEmitter, Output, ViewChild} from '@angular/core';
|
||||||
import {MatExpansionModule, MatExpansionPanel} from "@angular/material/expansion";
|
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 {FormControl, FormsModule, ReactiveFormsModule} from "@angular/forms";
|
||||||
import {catchError} from "rxjs";
|
import {catchError} from "rxjs";
|
||||||
import {LoadingIndicatorComponent} from "@component/common/loading-indicator/loading-indicator.component";
|
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 {FacultyResponse} from "@api/v1/facultyResponse";
|
||||||
import {FacultyService} from "@api/v1/faculty.service";
|
import {FacultyService} from "@api/v1/faculty.service";
|
||||||
import {GroupService} from "@api/v1/group.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({
|
@Component({
|
||||||
selector: 'app-group',
|
selector: 'app-group',
|
||||||
@ -24,121 +30,159 @@ import {GroupService} from "@api/v1/group.service";
|
|||||||
providers: [FacultyService, GroupService]
|
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 facultyId: number | null = null;
|
||||||
protected courseNumber: number | null = null;
|
protected courseNumber: number | null = null;
|
||||||
|
|
||||||
protected filteredGroups: GroupResponse[] = [];
|
|
||||||
protected courseNumbers: number[] = [];
|
|
||||||
private groups: GroupResponse[] = [];
|
|
||||||
|
|
||||||
protected formChipCourse: FormControl = new FormControl();
|
protected formChipCourse: FormControl = new FormControl();
|
||||||
protected formChipGroup: FormControl = new FormControl();
|
protected formChipGroup: FormControl = new FormControl();
|
||||||
|
|
||||||
protected faculties: FacultyResponse[] = [];
|
|
||||||
|
|
||||||
@ViewChild('courseNumberPanel') courseNumberPanel!: MatExpansionPanel;
|
@ViewChild('courseNumberPanel') courseNumberPanel!: MatExpansionPanel;
|
||||||
@ViewChild('groupPanel') groupPanel!: MatExpansionPanel;
|
@ViewChild('groupPanel') groupPanel!: MatExpansionPanel;
|
||||||
|
|
||||||
protected facultiesLoaded: boolean | null = false;
|
@ViewChild('facultyChip') facultyChip!: MatChipListbox;
|
||||||
protected groupsLoaded: boolean | null = false;
|
@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>();
|
@Output() eventResult = new EventEmitter<number>();
|
||||||
|
|
||||||
constructor(private facultyApi: FacultyService, private groupApi: GroupService) {
|
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() {
|
protected loadFaculties() {
|
||||||
this.facultiesLoaded = false;
|
|
||||||
this.facultyApi.getFaculties()
|
this.facultyApi.getFaculties()
|
||||||
.pipe(catchError(error => {
|
.pipe(catchError(error => {
|
||||||
this.facultiesLoaded = null;
|
this.facultyIndicator.loading = false;
|
||||||
throw error;
|
throw error;
|
||||||
}))
|
}))
|
||||||
.subscribe(data => {
|
.subscribe(data => {
|
||||||
this.faculties = 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() {
|
protected loadCourseGroup() {
|
||||||
if (this.groups.length === 0) {
|
if (this.facultyId === null)
|
||||||
this.groupsLoaded = false;
|
return;
|
||||||
|
|
||||||
this.groupApi.getGroups().pipe(
|
if (this.groups === null || this.groups.length === 0 || this.groups[0].facultyId !== this.facultyId) {
|
||||||
catchError(error => {
|
this.groupApi.getByFaculty(this.facultyId)
|
||||||
this.groupsLoaded = null;
|
.pipe(catchError(error => {
|
||||||
|
this.groupIndicator.loading = false;
|
||||||
|
this.courseIndicator.loading = false;
|
||||||
throw error;
|
throw error;
|
||||||
})
|
}))
|
||||||
).subscribe(data => {
|
.subscribe(data => {
|
||||||
this.groups = data;
|
this.groups = data;
|
||||||
if (this.courseNumber === null)
|
this.courseNumbers = Array.from(
|
||||||
this.filteringCourseNumber();
|
new Set(
|
||||||
else
|
this.groups!
|
||||||
this.filteringGroup();
|
.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)
|
if (this.courseNumber !== null)
|
||||||
this.filteringCourseNumber();
|
this.filteredGroups = this.groups!.filter(x => x.courseNumber === this.courseNumber);
|
||||||
else
|
|
||||||
this.filteringGroup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected chooseFaculty(event: MatChipListboxChange) {
|
protected onFacultySelected(index: number) {
|
||||||
this.courseNumber = null;
|
this.courseNumber = null;
|
||||||
this.groups = [];
|
this.groups = [];
|
||||||
this.formChipGroup.reset();
|
this.formChipGroup.reset();
|
||||||
this.formChipCourse.reset();
|
this.formChipCourse.reset();
|
||||||
|
|
||||||
if (event.value === undefined || event.value === null) {
|
if (index === undefined) {
|
||||||
this.facultyId = null;
|
this.facultyId = null;
|
||||||
return;
|
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.courseNumberPanel.open();
|
||||||
|
|
||||||
this.loadCourseGroup();
|
this.loadCourseGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected chooseCourseNumber(event: MatChipListboxChange) {
|
protected onCourseSelected(course: number) {
|
||||||
this.filteredGroups = [];
|
this.filteredGroups = [];
|
||||||
this.formChipGroup.reset();
|
this.formChipGroup.reset();
|
||||||
|
|
||||||
if (event.value === undefined || event.value === null) {
|
if (course === undefined) {
|
||||||
this.courseNumber = null;
|
this.courseNumber = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.courseNumber = event.value;
|
TabStorageComponent.select(new TabSelect(course, course.toString()), TabSelectType.group, 1);
|
||||||
|
|
||||||
|
this.courseNumber = course;
|
||||||
this.groupPanel.open();
|
this.groupPanel.open();
|
||||||
this.loadCourseGroup();
|
this.loadCourseGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected chooseGroup(event: MatChipListboxChange) {
|
protected onGroupSelected(index: number) {
|
||||||
if (event.value === undefined || event.value === null)
|
if (index === undefined)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
TabStorageComponent.select(new TabSelect(index, this.groups!.find(x => x.id == index)?.name ?? ''), TabSelectType.group, 2);
|
||||||
|
|
||||||
this.groupPanel.close();
|
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-panel-title>
|
||||||
</mat-expansion-panel-header>
|
</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) {
|
@for (campus of campuses; track $index) {
|
||||||
<mat-chip-option [value]="campus.id" color="accent">
|
<mat-chip-option [value]="campus.id" color="accent">
|
||||||
{{ campus.codeName }}
|
{{ campus.codeName }}
|
||||||
</mat-chip-option>
|
</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-chip-listbox>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
@ -22,13 +23,14 @@
|
|||||||
Кабинет
|
Кабинет
|
||||||
</mat-panel-title>
|
</mat-panel-title>
|
||||||
</mat-expansion-panel-header>
|
</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) {
|
@for (lectureHall of lectureHallsFiltered; track $index) {
|
||||||
<mat-chip-option [value]="lectureHall.id" color="accent">
|
<mat-chip-option [value]="lectureHall.id" color="accent">
|
||||||
{{ lectureHall.name }}
|
{{ lectureHall.name }}
|
||||||
</mat-chip-option>
|
</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-chip-listbox>
|
||||||
</mat-expansion-panel>
|
</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 {AsyncPipe} from "@angular/common";
|
||||||
import {MatAccordion, MatExpansionModule, MatExpansionPanel} from "@angular/material/expansion";
|
import {MatAccordion, MatExpansionModule, MatExpansionPanel} from "@angular/material/expansion";
|
||||||
import {MatChipListboxChange, MatChipsModule} from "@angular/material/chips";
|
import {MatChipListbox, MatChipsModule} from "@angular/material/chips";
|
||||||
import {catchError, Observable, of} from "rxjs";
|
import {catchError} from "rxjs";
|
||||||
import {FormControl, ReactiveFormsModule} from "@angular/forms";
|
import {FormControl, ReactiveFormsModule} from "@angular/forms";
|
||||||
import {LoadingIndicatorComponent} from "@component/common/loading-indicator/loading-indicator.component";
|
import {LoadingIndicatorComponent} from "@component/common/loading-indicator/loading-indicator.component";
|
||||||
import {CampusBasicInfoResponse} from "@api/v1/campusBasicInfoResponse";
|
import {CampusBasicInfoResponse} from "@api/v1/campusBasicInfoResponse";
|
||||||
import {LectureHallResponse} from "@api/v1/lectureHallResponse";
|
import {LectureHallResponse} from "@api/v1/lectureHallResponse";
|
||||||
import {CampusService} from "@api/v1/campus.service";
|
import {CampusService} from "@api/v1/campus.service";
|
||||||
import {LectureHallService} from "@api/v1/lectureHall.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({
|
@Component({
|
||||||
selector: 'app-lecture-hall',
|
selector: 'app-lecture-hall',
|
||||||
@ -26,79 +28,115 @@ import {LectureHallService} from "@api/v1/lectureHall.service";
|
|||||||
providers: [CampusService, LectureHallService]
|
providers: [CampusService, LectureHallService]
|
||||||
})
|
})
|
||||||
|
|
||||||
export class LectureHallComponent {
|
export class LectureHallComponent implements IScheduleTab {
|
||||||
protected campusId: number | null = null;
|
protected campusId: number | null = null;
|
||||||
protected chipLecture: FormControl = new FormControl();
|
protected formLectureHalls: FormControl = new FormControl();
|
||||||
|
|
||||||
@ViewChild('lecturePanel') lecturePanel!: MatExpansionPanel;
|
protected campuses: CampusBasicInfoResponse[] | null = null;
|
||||||
|
protected lectureHallsFiltered: LectureHallResponse[] | null = null;
|
||||||
protected campuses: CampusBasicInfoResponse[] = [];
|
|
||||||
protected campusesLoaded: boolean | null = false;
|
|
||||||
|
|
||||||
private lectureHalls: LectureHallResponse[] = [];
|
|
||||||
protected lectureHallsFiltered: LectureHallResponse[] = [];
|
|
||||||
protected lectureHallsLoaded: boolean | null = false;
|
|
||||||
|
|
||||||
@Output() eventResult = new EventEmitter<number>();
|
@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) {
|
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() {
|
protected loadCampuses() {
|
||||||
this.campusesLoaded = false;
|
|
||||||
this.campusApi.getCampus()
|
this.campusApi.getCampus()
|
||||||
.pipe(catchError(error => {
|
.pipe(catchError(error => {
|
||||||
this.campusesLoaded = null;
|
this.campusIndicator.loading = false;
|
||||||
throw error;
|
throw error;
|
||||||
}))
|
}))
|
||||||
.subscribe(data => {
|
.subscribe(data => {
|
||||||
this.campuses = 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() {
|
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) {
|
protected onCampusSelected(index: number) {
|
||||||
this.chipLecture.reset();
|
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.campusId = null;
|
||||||
this.lectureHalls = [];
|
this.lectureHalls = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.campusId = event.value;
|
this.campusId = index;
|
||||||
this.lecturePanel.open();
|
this.lecturePanel.open();
|
||||||
|
|
||||||
if (this.lectureHalls.length === 0)
|
if (this.lectureHalls === null)
|
||||||
this.loadLectureHalls();
|
this.loadLectureHalls();
|
||||||
else
|
else
|
||||||
this.filteringLectureHalls();
|
this.filteringLectureHalls();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected loadLectureHalls() {
|
protected loadLectureHalls() {
|
||||||
this.lectureHallsLoaded = false;
|
|
||||||
this.lectureHallApi.getLectureHalls()
|
this.lectureHallApi.getLectureHalls()
|
||||||
.pipe(catchError(error => {
|
.pipe(catchError(error => {
|
||||||
this.lectureHallsLoaded = null;
|
this.lectureIndicator.loading = false;
|
||||||
throw error;
|
throw error;
|
||||||
}))
|
}))
|
||||||
.subscribe(data => {
|
.subscribe(data => {
|
||||||
this.lectureHalls = data;
|
this.lectureHalls = data;
|
||||||
this.filteringLectureHalls();
|
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) {
|
protected onLectureHallSelected(index: number) {
|
||||||
if (event.value === undefined || event.value === null)
|
if (index === undefined)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
TabStorageComponent.select(new TabSelect(index, this.lectureHallsFiltered!.find(x => x.id === index)?.name ?? ''), TabSelectType.lecture, 1);
|
||||||
|
|
||||||
this.lecturePanel.close();
|
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">
|
<div class="search-content">
|
||||||
@if (professors.length === 0) {
|
@if (professors === null) {
|
||||||
<app-loading-indicator [loading]="professorsLoaded !== null" (retryFunction)="loadProfessors()"/>
|
<app-loading-indicator [loading]="true" (retryFunction)="loadProfessors()"
|
||||||
|
#professorIndicator/>
|
||||||
} @else {
|
} @else {
|
||||||
<mat-form-field color="accent" style="width: 100%;">
|
<mat-form-field color="accent" style="width: 100%;">
|
||||||
<input type="text" placeholder="Поиск..." matInput [formControl]="professorControl" [matAutocomplete]="auto">
|
<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">
|
[autoActiveFirstOption]="false" [hideSingleSelectionIndicator]="true">
|
||||||
@for (option of filteredProfessors | async; track option) {
|
@for (option of filteredProfessors | async; track option) {
|
||||||
<mat-option [value]="option.id">
|
<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 {MatFormField, MatInput} from "@angular/material/input";
|
||||||
import {FormControl, ReactiveFormsModule} from "@angular/forms";
|
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 {AsyncPipe} from "@angular/common";
|
||||||
import {catchError, map, Observable, startWith} from "rxjs";
|
import {catchError, map, Observable, startWith} from "rxjs";
|
||||||
import {LoadingIndicatorComponent} from "@component/common/loading-indicator/loading-indicator.component";
|
import {LoadingIndicatorComponent} from "@component/common/loading-indicator/loading-indicator.component";
|
||||||
import {ProfessorResponse} from "@api/v1/professorResponse";
|
import {ProfessorResponse} from "@api/v1/professorResponse";
|
||||||
import {ProfessorService} from "@api/v1/professor.service";
|
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({
|
@Component({
|
||||||
selector: 'app-professor',
|
selector: 'app-professor',
|
||||||
@ -23,31 +25,44 @@ import {ProfessorService} from "@api/v1/professor.service";
|
|||||||
styleUrl: './professor.component.css',
|
styleUrl: './professor.component.css',
|
||||||
providers: [ProfessorService]
|
providers: [ProfessorService]
|
||||||
})
|
})
|
||||||
export class ProfessorComponent implements OnInit {
|
export class ProfessorComponent implements OnInit, IScheduleTab {
|
||||||
protected professorControl = new FormControl();
|
protected professorControl = new FormControl();
|
||||||
protected filteredProfessors!: Observable<ProfessorResponse[]>;
|
protected filteredProfessors!: Observable<ProfessorResponse[]>;
|
||||||
|
|
||||||
protected professors: ProfessorResponse[] = [];
|
protected professors: ProfessorResponse[] | null = null;
|
||||||
protected professorsLoaded: boolean | null = false;
|
private readonly selected: TabSelect[] | null = null;
|
||||||
|
|
||||||
|
@ViewChild('professorIndicator') professorIndicator!: LoadingIndicatorComponent;
|
||||||
|
|
||||||
@Output() eventResult = new EventEmitter<number>();
|
@Output() eventResult = new EventEmitter<number>();
|
||||||
|
|
||||||
constructor(private api: ProfessorService) {
|
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() {
|
protected loadProfessors() {
|
||||||
if (this.professors.length === 0) {
|
if (this.professors === null || this.professors.length === 0) {
|
||||||
this.professorsLoaded = false;
|
|
||||||
|
|
||||||
this.api.getProfessors()
|
this.api.getProfessors()
|
||||||
.pipe(catchError(error => {
|
.pipe(catchError(error => {
|
||||||
this.professorsLoaded = null;
|
this.professorIndicator.loading = false;
|
||||||
throw error;
|
throw error;
|
||||||
}))
|
}))
|
||||||
.subscribe(data => {
|
.subscribe(data => {
|
||||||
this.professors = 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[] {
|
private _filterProfessors(value: string | number): ProfessorResponse[] {
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
if (value === '') return [];
|
if (value === '')
|
||||||
|
return [];
|
||||||
|
|
||||||
const filterValue = value.toLowerCase();
|
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 {
|
} else {
|
||||||
const selectedTeacher = this.professors.find(teacher => teacher.id === value);
|
const selectedTeacher = this.professors?.find(teacher => teacher.id === value);
|
||||||
return selectedTeacher ? [selectedTeacher] : [];
|
return selectedTeacher ? [selectedTeacher] : [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected onOptionSelected(event: MatAutocompleteSelectedEvent) {
|
protected onOptionSelected(index: number) {
|
||||||
const selectedOption = this.professors.find(teacher => teacher.id === event.option.value);
|
if (index === undefined)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const selectedOption = this.professors?.find(teacher => teacher.id === index);
|
||||||
if (selectedOption) {
|
if (selectedOption) {
|
||||||
this.professorControl.setValue(selectedOption.name);
|
this.professorControl.setValue(selectedOption.name);
|
||||||
this.eventResult.emit(selectedOption.id);
|
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,18 +1,19 @@
|
|||||||
<mat-tab-group dynamicHeight mat-stretch-tabs="false" mat-align-tabs="start" color="accent" class="padding-content"
|
<mat-tab-group dynamicHeight mat-stretch-tabs="false" mat-align-tabs="start" color="accent" class="padding-content"
|
||||||
(selectedTabChange)="chooseTabs($event)">
|
#tabGroup
|
||||||
|
(selectedTabChange)="chooseTabs($event.index)">
|
||||||
<mat-tab label="Группа">
|
<mat-tab label="Группа">
|
||||||
<div>
|
<div>
|
||||||
<app-group (eventResult)="groupSelected($event)"/>
|
<app-group #groupTab (eventResult)="groupSelected($event)"/>
|
||||||
</div>
|
</div>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
<mat-tab label="Преподаватель">
|
<mat-tab label="Преподаватель">
|
||||||
<div>
|
<div>
|
||||||
<app-professor (eventResult)="professorSelected($event)"/>
|
<app-professor #professorTab (eventResult)="professorSelected($event)"/>
|
||||||
</div>
|
</div>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
<mat-tab label="Кабинет">
|
<mat-tab label="Кабинет">
|
||||||
<div>
|
<div>
|
||||||
<app-lecture-hall (eventResult)="lectureHallSelected($event)"/>
|
<app-lecture-hall #lectureHallTab (eventResult)="lectureHallSelected($event)"/>
|
||||||
</div>
|
</div>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
<!--
|
<!--
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import {Component, EventEmitter, Output} from '@angular/core';
|
import {AfterViewInit, Component, EventEmitter, Output, ViewChild} from '@angular/core';
|
||||||
import {OtherComponent} from "@component/schedule/tabs/other/other.component";
|
import {OtherComponent, SelectData} from "@component/schedule/tabs/other/other.component";
|
||||||
import {MatTab, MatTabChangeEvent, MatTabGroup} from "@angular/material/tabs";
|
import {MatTab, MatTabGroup} from "@angular/material/tabs";
|
||||||
import {map, Observable} from "rxjs";
|
import {map, Observable} from "rxjs";
|
||||||
import {ReactiveFormsModule} from "@angular/forms";
|
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
|
||||||
import {MatButton} from "@angular/material/button";
|
import {MatButton} from "@angular/material/button";
|
||||||
import {DataSpinnerComponent} from "@component/common/data-spinner/data-spinner.component";
|
import {DataSpinnerComponent} from "@component/common/data-spinner/data-spinner.component";
|
||||||
import {GroupComponent} from "@component/schedule/tabs/group/group.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 {LectureHallComponent} from "@component/schedule/tabs/lecture-hall/lecture-hall.component";
|
||||||
import {ScheduleService} from "@api/v1/schedule.service";
|
import {ScheduleService} from "@api/v1/schedule.service";
|
||||||
import {ScheduleResponse} from "@api/v1/scheduleResponse";
|
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 {
|
export enum TabsSelect {
|
||||||
Group,
|
Group,
|
||||||
@ -30,17 +38,34 @@ export enum TabsSelect {
|
|||||||
DataSpinnerComponent,
|
DataSpinnerComponent,
|
||||||
GroupComponent,
|
GroupComponent,
|
||||||
ProfessorComponent,
|
ProfessorComponent,
|
||||||
LectureHallComponent
|
LectureHallComponent,
|
||||||
|
FormsModule,
|
||||||
|
HasRoleDirective
|
||||||
],
|
],
|
||||||
templateUrl: './tabs.component.html',
|
templateUrl: './tabs.component.html',
|
||||||
styleUrl: './tabs.component.css',
|
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[]>]>();
|
@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) {
|
protected groupSelected(id: number) {
|
||||||
@ -145,15 +170,74 @@ export class TabsComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async chooseTabs(event: MatTabChangeEvent) {
|
protected async chooseTabs(index: number) {
|
||||||
switch (event.index) {
|
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('discipline') disciplineEx!: OtherComponent;
|
||||||
@ViewChild('lecture') lectureHallEx!: OtherComponent;
|
@ViewChild('lecture') lectureHallEx!: OtherComponent;
|
||||||
@ViewChild('group') groupEx!: OtherComponent;
|
@ViewChild('group') groupEx!: OtherComponent;
|
||||||
@ViewChild('professor') professorEx!: OtherComponent;
|
@ViewChild('professor') professorEx!: OtherComponent;
|
||||||
*/
|
|
||||||
|
@ViewChild('tabGroup') tabs!: MatTabGroup;
|
||||||
|
protected readonly AuthRoles = AuthRoles;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export const environment = {
|
export const environment = {
|
||||||
apiUrl: 'http://localhost:5269/api/',
|
apiUrl: 'http://localhost:8080/api/',
|
||||||
maxRetry: 5,
|
maxRetry: 5,
|
||||||
retryDelay: 1500
|
retryDelay: 1500
|
||||||
};
|
};
|
||||||
|
@ -11,7 +11,7 @@ import {FocusNextDirective} from "@/directives/focus-next.directive";
|
|||||||
import {DataSpinnerComponent} from "@component/common/data-spinner/data-spinner.component";
|
import {DataSpinnerComponent} from "@component/common/data-spinner/data-spinner.component";
|
||||||
import AuthApiService from "@api/v1/authApiService";
|
import AuthApiService from "@api/v1/authApiService";
|
||||||
import {Router} from "@angular/router";
|
import {Router} from "@angular/router";
|
||||||
import {catchError, of} from "rxjs";
|
import {catchError} from "rxjs";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-login',
|
selector: 'app-login',
|
||||||
@ -43,7 +43,7 @@ export class LoginComponent {
|
|||||||
constructor(private formBuilder: FormBuilder, private auth: AuthApiService, private route: Router) {
|
constructor(private formBuilder: FormBuilder, private auth: AuthApiService, private route: Router) {
|
||||||
this.auth.getRole()
|
this.auth.getRole()
|
||||||
.subscribe(data => {
|
.subscribe(data => {
|
||||||
if (data != null)
|
if (data !== null)
|
||||||
route.navigate(['admin']).then();
|
route.navigate(['admin']).then();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -70,8 +70,6 @@ export class ScheduleComponent {
|
|||||||
break;
|
break;
|
||||||
case TabsSelect.Professor:
|
case TabsSelect.Professor:
|
||||||
let indexProfessor = this.data[0].professorsId.findIndex(p => p === data[1]);
|
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]);
|
this.childComponent.AdditionalText(AdditionalText.Professor, this.data[0].professors[indexProfessor]);
|
||||||
break;
|
break;
|
||||||
case TabsSelect.LectureHall:
|
case TabsSelect.LectureHall:
|
||||||
|
@ -29,7 +29,7 @@ export class WelcomeComponent {
|
|||||||
Validators.minLength(16)
|
Validators.minLength(16)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
protected apiToGetToken : string = environment.apiUrl;
|
protected apiToGetToken: string = environment.apiUrl;
|
||||||
|
|
||||||
constructor(private navigationService: NavigationService, private api: SetupService) {
|
constructor(private navigationService: NavigationService, private api: SetupService) {
|
||||||
this.apiToGetToken += AvailableVersion[this.api.version];
|
this.apiToGetToken += AvailableVersion[this.api.version];
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {EventEmitter, Injectable} from '@angular/core';
|
import {EventEmitter, Injectable} from '@angular/core';
|
||||||
import {HttpClient, HttpHeaders} from "@angular/common/http";
|
import {HttpClient, HttpHeaders} from "@angular/common/http";
|
||||||
import {catchError, Observable, of, tap} from "rxjs";
|
import {Observable, tap} from "rxjs";
|
||||||
import {TokenResponse} from "@api/v1/tokenResponse";
|
import {TokenResponse} from "@api/v1/tokenResponse";
|
||||||
import ApiService from "@api/api.service";
|
import ApiService from "@api/api.service";
|
||||||
|
|
||||||
@ -36,7 +36,6 @@ export class AuthToken {
|
|||||||
})
|
})
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
public expireTokenChange = new EventEmitter<Date>();
|
public expireTokenChange = new EventEmitter<Date>();
|
||||||
public tokenChangeError = new EventEmitter();
|
|
||||||
|
|
||||||
constructor(private http: HttpClient) {
|
constructor(private http: HttpClient) {
|
||||||
}
|
}
|
||||||
@ -60,8 +59,9 @@ export class AuthService {
|
|||||||
public refreshToken(): Observable<TokenResponse> {
|
public refreshToken(): Observable<TokenResponse> {
|
||||||
const token = localStorage.getItem(ApiService.tokenKey);
|
const token = localStorage.getItem(ApiService.tokenKey);
|
||||||
|
|
||||||
|
console.log(token);
|
||||||
if (!token)
|
if (!token)
|
||||||
return of({} as TokenResponse);
|
throw new Error("token is not found");
|
||||||
|
|
||||||
const authToken = JSON.parse(token) as AuthToken;
|
const authToken = JSON.parse(token) as AuthToken;
|
||||||
|
|
||||||
@ -69,10 +69,6 @@ export class AuthService {
|
|||||||
case AvailableAuthenticationProvider.Bearer:
|
case AvailableAuthenticationProvider.Bearer:
|
||||||
return this.http.get<TokenResponse>(authToken.endpoint, {withCredentials: true})
|
return this.http.get<TokenResponse>(authToken.endpoint, {withCredentials: true})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(error => {
|
|
||||||
this.tokenChangeError.emit();
|
|
||||||
throw error;
|
|
||||||
}),
|
|
||||||
tap(response => {
|
tap(response => {
|
||||||
const newExpireDate = new Date(response.expiresIn);
|
const newExpireDate = new Date(response.expiresIn);
|
||||||
const oldExpireDate = new Date(authToken.expiresIn);
|
const oldExpireDate = new Date(authToken.expiresIn);
|
||||||
|
@ -15,11 +15,6 @@ export class TokenRefreshService {
|
|||||||
constructor(private authService: AuthService) {
|
constructor(private authService: AuthService) {
|
||||||
this.setRefreshTokenExpireMs(AuthService.tokenExpiresIn.getTime() - 1000 - Date.now());
|
this.setRefreshTokenExpireMs(AuthService.tokenExpiresIn.getTime() - 1000 - Date.now());
|
||||||
|
|
||||||
authService.tokenChangeError.subscribe(_ => {
|
|
||||||
console.debug('Token change error event received');
|
|
||||||
this.tokenRefreshing$.next(false);
|
|
||||||
this.stopTokenRefresh();
|
|
||||||
});
|
|
||||||
authService.expireTokenChange.subscribe(date => {
|
authService.expireTokenChange.subscribe(date => {
|
||||||
console.debug('Expire token change event received:', date);
|
console.debug('Expire token change event received:', date);
|
||||||
this.setRefreshTokenExpireMs(date.getTime() - 1000 - Date.now());
|
this.setRefreshTokenExpireMs(date.getTime() - 1000 - Date.now());
|
||||||
|
Loading…
Reference in New Issue
Block a user