refactor: folders and components
This commit is contained in:
@ -0,0 +1,73 @@
|
||||
.nav-button button:nth-child(1) {
|
||||
border-bottom-right-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.nav-button button:nth-child(2) {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.nav-button button:nth-child(3) {
|
||||
border-bottom-left-radius: 0;
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
|
||||
.nav-button button:not(:last-child) {
|
||||
border-right: 1px solid var(--mat-table-row-item-outline-color, rgba(0, 0, 0, 0.12));
|
||||
}
|
||||
|
||||
.nav-button mat-icon {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.app-table-header {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-template-rows: 1fr;
|
||||
grid-column-gap: 15px;
|
||||
grid-row-gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.app-table-header div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.app-table-header div mat-icon {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.app-table-header div:nth-child(1) {
|
||||
grid-area: 1 / 1 / 2 / 2;
|
||||
justify-content: left;
|
||||
}
|
||||
|
||||
.app-table-header div:nth-child(2) {
|
||||
grid-area: 1 / 2 / 2 / 4;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.app-table-header div:nth-child(3) {
|
||||
grid-area: 1 / 4 / 2 / 5;
|
||||
justify-content: right;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 782px) {
|
||||
.app-table-header {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-template-rows: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.app-table-header div:nth-child(1) {
|
||||
grid-area: 1 / 1 / 2 / 3;
|
||||
}
|
||||
|
||||
.app-table-header div:nth-child(2) {
|
||||
grid-area: 2 / 1 / 3 / 4;
|
||||
}
|
||||
|
||||
.app-table-header div:nth-child(3) {
|
||||
grid-area: 1 / 3 / 2 / 4;
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<div class="app-table-header">
|
||||
<div class="mat-h4">
|
||||
<div style="justify-content: flex-start; flex-wrap: wrap">
|
||||
<span style="margin-right: 5px">Расписание занятий {{ additionalText }}</span>
|
||||
@if (dataText.length !== 0) {
|
||||
<span style="color: var(--mdc-checkbox-selected-icon-color);">{{ dataText }}</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mat-h3">
|
||||
Неделя: {{ startWeek | date:"shortDate" }}
|
||||
-
|
||||
{{ addDays(startWeek, 6) | date: "shortDate" }}
|
||||
<span [matBadge]="currentWeek" matBadgeOverlap="false"></span>
|
||||
</div>
|
||||
<div class="nav-button mat-h5">
|
||||
<button mat-flat-button (click)="weekEvent.emit(false)" [disabled]="!canBefore()">
|
||||
<mat-icon fontIcon="navigate_before"/>
|
||||
</button>
|
||||
<button mat-flat-button style="min-width: auto;" (click)="weekEvent.emit(null)">
|
||||
<mat-icon fontIcon="calendar_month"/>
|
||||
</button>
|
||||
<button mat-flat-button (click)="weekEvent.emit(true)">
|
||||
<mat-icon fontIcon="navigate_next"/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,59 @@
|
||||
import {Component, EventEmitter, Input, Output} from '@angular/core';
|
||||
import {DatePipe} from "@angular/common";
|
||||
import {addDays} from "@progress/kendo-date-math";
|
||||
import {MatButton} from "@angular/material/button";
|
||||
import {MatIcon} from "@angular/material/icon";
|
||||
import {MatBadge} from "@angular/material/badge";
|
||||
|
||||
export enum AdditionalText {
|
||||
Group,
|
||||
Professor,
|
||||
LectureHall,
|
||||
Other
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-table-header',
|
||||
standalone: true,
|
||||
imports: [
|
||||
MatBadge,
|
||||
MatIcon,
|
||||
MatButton,
|
||||
DatePipe
|
||||
],
|
||||
templateUrl: './table-header.component.html',
|
||||
styleUrl: './table-header.component.css'
|
||||
})
|
||||
|
||||
export class TableHeaderComponent {
|
||||
protected additionalText: string = '';
|
||||
protected dataText: string = '';
|
||||
|
||||
@Input() startWeek!: Date;
|
||||
@Input() currentWeek!: number;
|
||||
@Output() weekEvent: EventEmitter<boolean | null> = new EventEmitter<boolean | null>();
|
||||
|
||||
AdditionalText(tab: AdditionalText = AdditionalText.Other, data: string | null = null) {
|
||||
if (data !== null) {
|
||||
this.dataText = data;
|
||||
}
|
||||
|
||||
switch (tab) {
|
||||
case AdditionalText.Group:
|
||||
this.additionalText = 'для группы';
|
||||
break;
|
||||
case AdditionalText.LectureHall:
|
||||
this.additionalText = 'в аудитории';
|
||||
break;
|
||||
case AdditionalText.Professor:
|
||||
this.additionalText = 'для преподавателя';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected canBefore(): boolean {
|
||||
return this.currentWeek > 1;
|
||||
}
|
||||
|
||||
protected readonly addDays = addDays;
|
||||
}
|
54
src/components/schedule/table/table.component.css
Normal file
54
src/components/schedule/table/table.component.css
Normal file
@ -0,0 +1,54 @@
|
||||
.schedule-table {
|
||||
border: 1px solid white;
|
||||
}
|
||||
|
||||
.schedule-table td, .schedule-table th {
|
||||
border-right: 1px solid;
|
||||
border-right-color: var(--mat-table-row-item-outline-color, rgba(0, 0, 0, 0.12));
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.schedule-table tr:nth-child(even) {
|
||||
background-color: color-mix(in srgb, var(--mat-table-background-color), white 5%);
|
||||
}
|
||||
|
||||
.schedule-table th {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table-element div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.pair-cell {
|
||||
text-align: center;
|
||||
width: 0;
|
||||
min-width: fit-content;
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
transform: scale(0.85);
|
||||
}
|
||||
|
||||
.table-section {
|
||||
overflow: auto;
|
||||
max-height: 80vh;
|
||||
position: relative
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 1001;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
116
src/components/schedule/table/table.component.html
Normal file
116
src/components/schedule/table/table.component.html
Normal file
@ -0,0 +1,116 @@
|
||||
<section class="mat-elevation-z8 table-section" tabindex="0">
|
||||
@if (dataSource.length === 0 || isLoad) {
|
||||
<div class="overlay">
|
||||
@if (isLoad) {
|
||||
<app-data-spinner/>
|
||||
} @else {
|
||||
<p class="mat-h2">Нет данных.</p>
|
||||
<p class="mat-body-2">Выберите необходимое расписание сверху</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<table mat-table [dataSource]="tableDataSource" class="schedule-table size-icon">
|
||||
<!-- Pair Number -->
|
||||
<ng-container matColumnDef="pairNumber" sticky>
|
||||
<th mat-header-cell *matHeaderCellDef style="width: 0; min-width: fit-content;">
|
||||
Пара
|
||||
<!-- Even Or Odd -->
|
||||
<hr/>
|
||||
@if (currentWeek % 2 === 0) {
|
||||
Четная
|
||||
} @else {
|
||||
Нечетная
|
||||
}
|
||||
</th>
|
||||
|
||||
<td mat-cell *matCellDef="let element" class="pair-cell" sticky>
|
||||
{{ element["pairNumber"] }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Day of Week -->
|
||||
@for (day of daysOfWeek; track $index) {
|
||||
<ng-container [matColumnDef]="day">
|
||||
<!-- Day of Week Name -->
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
<span class="mat-body-1">
|
||||
{{ day }}
|
||||
</span>
|
||||
<br/>
|
||||
<span class="mat-caption">
|
||||
{{ addDays(startWeek, daysOfWeek.indexOf(day)) | date:"shortDate" }}
|
||||
</span>
|
||||
</th>
|
||||
|
||||
<!-- Info About Pairs -->
|
||||
<td mat-cell *matCellDef="let element" class="table-element">
|
||||
@for (elementData of element.data[daysOfWeek.indexOf(day) + 1]; track $index) {
|
||||
<!-- Discipline -->
|
||||
<div class="mat-body-1">{{ elementData["discipline"] }}</div>
|
||||
<!-- Type of Occupation -->
|
||||
<div class="mat-body">({{ elementData["typeOfOccupation"] }})</div>
|
||||
|
||||
<!-- Professors -->
|
||||
@if (checkAvailableData(elementData["professors"])) {
|
||||
<div class="mat-body">
|
||||
<i>
|
||||
<mat-icon fontIcon="person"/>
|
||||
</i>
|
||||
@for (professor of elementData["professors"]; track $index) {
|
||||
@if ($index !== 0) {
|
||||
<br/>
|
||||
}
|
||||
{{ professor }}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Address (Lecture Hall & Campus) -->
|
||||
@if (checkAvailableData(elementData["lectureHalls"]) && checkAvailableData(elementData["campus"])) {
|
||||
<div class="mat-body">
|
||||
<i>
|
||||
<mat-icon fontIcon="location_on"/>
|
||||
</i>
|
||||
@for (lectureHall of elementData["lectureHalls"]; track $index) {
|
||||
@if ($index !== 0) {
|
||||
<br/>
|
||||
}
|
||||
@if (lectureHall) {
|
||||
{{ lectureHall }}
|
||||
} @else {
|
||||
N/A
|
||||
}
|
||||
/
|
||||
@if (elementData["campus"][$index]) {
|
||||
{{ elementData["campus"][$index] }}
|
||||
} @else {
|
||||
N/A
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Group -->
|
||||
@if (!isOneGroup) {
|
||||
<div class="mat-body">
|
||||
<i>
|
||||
<mat-icon fontIcon="group"/>
|
||||
</i>
|
||||
{{ elementData["group"] }}
|
||||
</div>
|
||||
}
|
||||
|
||||
@if ($index + 1 !== element.data[daysOfWeek.indexOf(day) + 1].length) {
|
||||
<hr style="margin: 10px 0;"/>
|
||||
}
|
||||
}
|
||||
|
||||
</td>
|
||||
</ng-container>
|
||||
}
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
</section>
|
89
src/components/schedule/table/table.component.ts
Normal file
89
src/components/schedule/table/table.component.ts
Normal file
@ -0,0 +1,89 @@
|
||||
import {Component, Input, OnChanges} from '@angular/core';
|
||||
import {MatTableDataSource, MatTableModule} from "@angular/material/table";
|
||||
import {MatIcon} from "@angular/material/icon";
|
||||
import {DatePipe} from "@angular/common";
|
||||
import {addDays} from "@progress/kendo-date-math";
|
||||
import {MatDivider} from "@angular/material/divider";
|
||||
import {Schedule} from "@page/schedule/schedule.component";
|
||||
import {DataSpinnerComponent} from "@component/common/data-spinner/data-spinner.component";
|
||||
|
||||
interface TableData {
|
||||
pairNumber: number;
|
||||
data: Dictionary;
|
||||
}
|
||||
|
||||
interface Dictionary {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-table',
|
||||
standalone: true,
|
||||
imports: [
|
||||
MatTableModule,
|
||||
MatIcon,
|
||||
DatePipe,
|
||||
MatDivider,
|
||||
DataSpinnerComponent
|
||||
],
|
||||
templateUrl: './table.component.html',
|
||||
styleUrl: './table.component.css',
|
||||
})
|
||||
|
||||
export class TableComponent implements OnChanges {
|
||||
protected tableDataSource: MatTableDataSource<TableData> = new MatTableDataSource<TableData>([]);
|
||||
protected daysOfWeek: string[] = ['Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'];
|
||||
protected displayedColumns: string[] = ['pairNumber'];
|
||||
protected dataSource: Schedule[] = [];
|
||||
protected isOneGroup: boolean = false;
|
||||
|
||||
@Input() currentWeek!: number;
|
||||
@Input() startWeek!: Date;
|
||||
@Input() isLoad: boolean = false;
|
||||
|
||||
@Input() set data(schedule: Schedule[]) {
|
||||
this.dataSource = schedule;
|
||||
this.convertData();
|
||||
this.isOneGroup = schedule.every((item, _, array) => item.group === array[0].group);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: any) {
|
||||
if (changes.startWeek && !changes.startWeek.firstChange) {
|
||||
this.convertData();
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.displayedColumns = this.displayedColumns.concat(this.daysOfWeek);
|
||||
}
|
||||
|
||||
private convertData() {
|
||||
this.isLoad = true;
|
||||
let tableData: TableData[] = [];
|
||||
|
||||
for (let i: number = 1; i <= 7; i++) {
|
||||
let convertedData: TableData = {
|
||||
pairNumber: i,
|
||||
data: {}
|
||||
};
|
||||
|
||||
for (let k: number = 1; k < 7; k++) {
|
||||
convertedData.data[k.toString()] = this.dataSource.filter(x =>
|
||||
x.pairNumber === i
|
||||
&& x.dayOfWeek === k
|
||||
&& x.isEven === (this.currentWeek % 2 === 0));
|
||||
}
|
||||
|
||||
tableData.push(convertedData);
|
||||
}
|
||||
|
||||
this.tableDataSource = new MatTableDataSource<TableData>(tableData);
|
||||
this.isLoad = false;
|
||||
}
|
||||
|
||||
protected checkAvailableData(data: any[]): boolean {
|
||||
return data && data.length !== 0 && data.every(x => x !== null);
|
||||
}
|
||||
|
||||
protected readonly addDays = addDays;
|
||||
}
|
54
src/components/schedule/tabs/group/group.component.html
Normal file
54
src/components/schedule/tabs/group/group.component.html
Normal file
@ -0,0 +1,54 @@
|
||||
<mat-accordion>
|
||||
<mat-expansion-panel expanded>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
Факультет
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<mat-chip-listbox hideSingleSelectionIndicator (change)="chooseFaculty($event)">
|
||||
@for (faculty of faculties | async; track $index) {
|
||||
<mat-chip-option [value]="faculty.id" color="accent">
|
||||
{{ faculty.name }}
|
||||
</mat-chip-option>
|
||||
} @empty {
|
||||
<app-loading-indicator [loading]="facultiesLoaded !== null" (retryFunction)="facultiesLoadRetry.emit()"/>
|
||||
}
|
||||
</mat-chip-listbox>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<mat-expansion-panel [disabled]="facultyId === null" #courseNumberPanel>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
Курс
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<mat-chip-listbox hideSingleSelectionIndicator (change)="chooseCourseNumber($event)" [formControl]="chipCourse">
|
||||
@for (course of courseNumbers | async; track $index) {
|
||||
<mat-chip-option [value]="course" color="accent">
|
||||
{{ course }}
|
||||
</mat-chip-option>
|
||||
} @empty {
|
||||
<app-loading-indicator [loading]="groupsLoaded !== null"
|
||||
(retryFunction)="groupsLoadRetry.emit(this.facultyId!)"/>
|
||||
}
|
||||
</mat-chip-listbox>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<mat-expansion-panel [disabled]="courseNumber === null" #groupPanel>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
Группа
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<mat-chip-listbox hideSingleSelectionIndicator (change)="chooseGroup($event)" [formControl]="chipGroup">
|
||||
@for (group of filteredGroups | async; track $index) {
|
||||
<mat-chip-option [value]="group.id" color="accent">
|
||||
{{ group.name }}
|
||||
</mat-chip-option>
|
||||
} @empty {
|
||||
<app-loading-indicator [loading]="groupsLoaded !== null"
|
||||
(retryFunction)="groupsLoadRetry.emit(this.facultyId!)"/>
|
||||
}
|
||||
</mat-chip-listbox>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
95
src/components/schedule/tabs/group/group.component.ts
Normal file
95
src/components/schedule/tabs/group/group.component.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core';
|
||||
import {MatExpansionModule, MatExpansionPanel} from "@angular/material/expansion";
|
||||
import {MatChipListboxChange, MatChipsModule} from '@angular/material/chips';
|
||||
import {FormControl, ReactiveFormsModule} from "@angular/forms";
|
||||
import {AsyncPipe} from "@angular/common";
|
||||
import {map, Observable, of} from "rxjs";
|
||||
import {FacultyResponse} from "@model/facultyResponse";
|
||||
import {GroupResponse} from "@model/groupResponse";
|
||||
import {LoadingIndicatorComponent} from "@component/common/loading-indicator/loading-indicator.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-group',
|
||||
standalone: true,
|
||||
imports: [
|
||||
MatExpansionModule,
|
||||
MatChipsModule,
|
||||
ReactiveFormsModule,
|
||||
LoadingIndicatorComponent,
|
||||
AsyncPipe
|
||||
],
|
||||
templateUrl: './group.component.html',
|
||||
styleUrl: './group.component.css'
|
||||
})
|
||||
|
||||
export class GroupComponent {
|
||||
protected facultyId: number | null = null;
|
||||
protected courseNumber: number | null = null;
|
||||
|
||||
protected filteredGroups: Observable<GroupResponse[]> = of([]);
|
||||
protected courseNumbers: Observable<number[]> = of([]);
|
||||
protected groups: Observable<GroupResponse[]> = of([]);
|
||||
|
||||
protected chipCourse: FormControl = new FormControl();
|
||||
protected chipGroup: FormControl = new FormControl();
|
||||
|
||||
@ViewChild('courseNumberPanel') courseNumberPanel!: MatExpansionPanel;
|
||||
@ViewChild('groupPanel') groupPanel!: MatExpansionPanel;
|
||||
|
||||
@Input() faculties: Observable<FacultyResponse[]> = of([]);
|
||||
@Input() facultiesLoaded: boolean | null = false;
|
||||
@Output() facultiesLoadRetry: EventEmitter<void> = new EventEmitter<void>();
|
||||
@Input() groupsLoaded: boolean | null = false;
|
||||
@Output() groupsLoadRetry: EventEmitter<number> = new EventEmitter<number>();
|
||||
|
||||
@Input() set setGroups(data: Observable<GroupResponse[]>) {
|
||||
this.groups = data;
|
||||
this.courseNumbers = this.groups.pipe(
|
||||
map(data => data.map(g => g.courseNumber)),
|
||||
map(courseNumbersArray => courseNumbersArray.filter((value, index, self) => self.indexOf(value) === index)),
|
||||
map(uniqueCourseNumbers => uniqueCourseNumbers.sort((a, b) => a - b))
|
||||
);
|
||||
}
|
||||
|
||||
@Output() groupSelected = new EventEmitter<number>();
|
||||
@Output() facultySelected = new EventEmitter<number>();
|
||||
|
||||
protected chooseFaculty(event: MatChipListboxChange) {
|
||||
this.courseNumber = null;
|
||||
this.groups = of([]);
|
||||
this.chipGroup.reset();
|
||||
this.chipCourse.reset();
|
||||
|
||||
if (event.value === undefined || event.value === null) {
|
||||
this.facultyId = null;
|
||||
return;
|
||||
}
|
||||
this.facultyId = event.value;
|
||||
this.courseNumberPanel.open();
|
||||
|
||||
this.facultySelected.emit(this.facultyId!);
|
||||
}
|
||||
|
||||
protected chooseCourseNumber(event: MatChipListboxChange) {
|
||||
this.filteredGroups = of([]);
|
||||
this.chipGroup.reset();
|
||||
|
||||
if (event.value === undefined || event.value === null) {
|
||||
this.courseNumber = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.courseNumber = event.value;
|
||||
this.groupPanel.open();
|
||||
this.groups.subscribe(data =>
|
||||
this.filteredGroups = of(data.filter(g => g.courseNumber === this.courseNumber)));
|
||||
}
|
||||
|
||||
protected chooseGroup(event: MatChipListboxChange) {
|
||||
if (event.value === undefined || event.value === null)
|
||||
return;
|
||||
|
||||
this.groupPanel.close();
|
||||
this.groupSelected.emit(event.value);
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
<mat-accordion>
|
||||
<mat-expansion-panel expanded>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
Кампус
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<mat-chip-listbox hideSingleSelectionIndicator (change)="chooseCampus($event)">
|
||||
@for (campus of campuses | async; track $index) {
|
||||
<mat-chip-option [value]="campus.id" color="accent">
|
||||
{{ campus.codeName }}
|
||||
</mat-chip-option>
|
||||
} @empty {
|
||||
<app-loading-indicator [loading]="campusesLoaded !== null" (retryFunction)="campusesLoadRetry.emit()"/>
|
||||
}
|
||||
</mat-chip-listbox>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<mat-expansion-panel [disabled]="campusId === null" #lecturePanel>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
Кабинет
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<mat-chip-listbox hideSingleSelectionIndicator (change)="chooseLectureHall($event)" [formControl]="chipLecture">
|
||||
@for (lectureHall of lectureHalls | async; track $index) {
|
||||
<mat-chip-option [value]="lectureHall.id" color="accent">
|
||||
{{ lectureHall.name }}
|
||||
</mat-chip-option>
|
||||
} @empty {
|
||||
<app-loading-indicator [loading]="lectureHallsLoaded !== null" (retryFunction)="lectureHallsLoadRetry.emit()"/>
|
||||
}
|
||||
</mat-chip-listbox>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
@ -0,0 +1,63 @@
|
||||
import {Component, EventEmitter, Input, 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 {Observable, of} from "rxjs";
|
||||
import {CampusBasicInfoResponse} from "@model/campusBasicInfoResponse";
|
||||
import {FormControl, ReactiveFormsModule} from "@angular/forms";
|
||||
import {LectureHallResponse} from "@model/lectureHallResponse";
|
||||
import {LoadingIndicatorComponent} from "@component/common/loading-indicator/loading-indicator.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-lecture-hall',
|
||||
standalone: true,
|
||||
imports: [
|
||||
MatChipsModule,
|
||||
MatExpansionModule,
|
||||
AsyncPipe,
|
||||
ReactiveFormsModule,
|
||||
MatAccordion,
|
||||
LoadingIndicatorComponent
|
||||
],
|
||||
templateUrl: './lecture-hall.component.html',
|
||||
styleUrl: './lecture-hall.component.css'
|
||||
})
|
||||
export class LectureHallComponent {
|
||||
protected campusId: number | null = null;
|
||||
protected chipLecture: FormControl = new FormControl();
|
||||
|
||||
@ViewChild('lecturePanel') lecturePanel!: MatExpansionPanel;
|
||||
|
||||
@Input() campuses: Observable<CampusBasicInfoResponse[]> = of([]);
|
||||
@Input() campusesLoaded: boolean | null = false;
|
||||
@Output() campusesLoadRetry: EventEmitter<void> = new EventEmitter<void>();
|
||||
@Input() lectureHalls: Observable<LectureHallResponse[]> = of([]);
|
||||
@Input() lectureHallsLoaded: boolean | null = false;
|
||||
@Output() lectureHallsLoadRetry: EventEmitter<number> = new EventEmitter<number>();
|
||||
|
||||
@Output() campusSelected = new EventEmitter<number>();
|
||||
@Output() lectureHallSelected = new EventEmitter<number>();
|
||||
|
||||
protected chooseCampus(event: MatChipListboxChange) {
|
||||
this.chipLecture.reset();
|
||||
|
||||
if (event.value === undefined || event.value === null) {
|
||||
this.campusId = null;
|
||||
this.lectureHalls = of([]);
|
||||
return;
|
||||
}
|
||||
|
||||
this.campusId = event.value;
|
||||
this.lecturePanel.open();
|
||||
|
||||
this.campusSelected.emit(this.campusId!);
|
||||
}
|
||||
|
||||
protected chooseLectureHall(event: MatChipListboxChange) {
|
||||
if (event.value === undefined || event.value === null)
|
||||
return;
|
||||
|
||||
this.lecturePanel.close();
|
||||
this.lectureHallSelected.emit(event.value);
|
||||
}
|
||||
}
|
13
src/components/schedule/tabs/other/other.component.css
Normal file
13
src/components/schedule/tabs/other/other.component.css
Normal file
@ -0,0 +1,13 @@
|
||||
.header-menu {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background-color: var(--mat-menu-container-color);
|
||||
z-index: 1;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
45
src/components/schedule/tabs/other/other.component.html
Normal file
45
src/components/schedule/tabs/other/other.component.html
Normal file
@ -0,0 +1,45 @@
|
||||
<!--suppress CssInvalidPropertyValue -->
|
||||
<button mat-button [matMenuTriggerFor]="menu" #menuTrigger="matMenuTrigger" [id]="idButton">{{ textButton }}</button>
|
||||
|
||||
<mat-menu #menu="matMenu" [hasBackdrop]="false" class="menu-options">
|
||||
<div (click)="$event.stopPropagation()" (keydown)="$event.stopPropagation()" style="padding: 0 15px 15px">
|
||||
<div class="header-menu">
|
||||
<mat-form-field appearance="outline" color="accent" style="display:flex;">
|
||||
<input matInput placeholder="Поиск..." [(ngModel)]="searchQuery" [disabled]="data.length === 0">
|
||||
<button mat-icon-button matSuffix (click)="clearSearchQuery()" [disabled]="data.length === 0">
|
||||
<mat-icon style="color: var(--mdc-filled-button-label-text-color);">close</mat-icon>
|
||||
</button>
|
||||
</mat-form-field>
|
||||
|
||||
<div class="button-group">
|
||||
<mat-checkbox (click)="checkData()" [disabled]="data.length === 0" #chooseCheckbox/>
|
||||
<button mat-button (click)="clearAll()" [disabled]="data.length === 0">Очистить</button>
|
||||
</div>
|
||||
<hr/>
|
||||
</div>
|
||||
@if (data.length === 0) {
|
||||
<app-loading-indicator style="display: flex; justify-content: center;" [loading]="dataLoaded !== null"
|
||||
(retryFunction)="retryLoadData.emit()"/>
|
||||
} @else {
|
||||
<mat-selection-list>
|
||||
@if (filteredData.value.length !== 0) {
|
||||
<cdk-virtual-scroll-viewport autosize [minBufferPx]="300" [maxBufferPx]="500"
|
||||
style="height: 250px; width: 260px; overflow-x: clip;">
|
||||
<div *cdkVirtualFor="let item of filteredData">
|
||||
<mat-list-option togglePosition="before" style="height: auto;" [value]="item.id"
|
||||
[selected]="item.selected"
|
||||
(click)="checkboxStateChange(item.id)">
|
||||
<div style="text-wrap: balance;">{{ item.name }}</div>
|
||||
</mat-list-option>
|
||||
<mat-divider style="margin: 10px 0;"/>
|
||||
</div>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
} @else {
|
||||
<div style="color: var(--mdc-text-button-label-text-color);">
|
||||
Нет подходящих элементов
|
||||
</div>
|
||||
}
|
||||
</mat-selection-list>
|
||||
}
|
||||
</div>
|
||||
</mat-menu>
|
141
src/components/schedule/tabs/other/other.component.ts
Normal file
141
src/components/schedule/tabs/other/other.component.ts
Normal file
@ -0,0 +1,141 @@
|
||||
import {Component, EventEmitter, HostListener, Input, Output, ViewChild} from '@angular/core';
|
||||
import {MatMenu, MatMenuTrigger} from "@angular/material/menu";
|
||||
import {MatCheckbox} from "@angular/material/checkbox";
|
||||
import {BehaviorSubject} from "rxjs";
|
||||
import {MatButton, MatIconButton} from "@angular/material/button";
|
||||
import {MatFormField, MatSuffix} from "@angular/material/form-field";
|
||||
import {MatIcon} from "@angular/material/icon";
|
||||
import {FormsModule} from "@angular/forms";
|
||||
import {MatListOption, MatSelectionList} from "@angular/material/list";
|
||||
import {ScrollingModule} from "@angular/cdk/scrolling";
|
||||
import {ScrollingModule as ExperimentalScrollingModule} from '@angular/cdk-experimental/scrolling';
|
||||
import {MatDivider} from "@angular/material/divider";
|
||||
import {MatInput} from "@angular/material/input";
|
||||
import {LoadingIndicatorComponent} from "@component/common/loading-indicator/loading-indicator.component";
|
||||
|
||||
export interface SelectData {
|
||||
id: number,
|
||||
name: string,
|
||||
selected: boolean
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-other',
|
||||
standalone: true,
|
||||
imports: [
|
||||
MatMenuTrigger,
|
||||
MatButton,
|
||||
MatMenu,
|
||||
MatFormField,
|
||||
MatIcon,
|
||||
FormsModule,
|
||||
MatCheckbox,
|
||||
MatSelectionList,
|
||||
MatListOption,
|
||||
MatDivider,
|
||||
MatIconButton,
|
||||
MatInput,
|
||||
MatSuffix,
|
||||
ScrollingModule,
|
||||
ExperimentalScrollingModule,
|
||||
LoadingIndicatorComponent
|
||||
],
|
||||
templateUrl: './other.component.html',
|
||||
styleUrl: './other.component.css'
|
||||
})
|
||||
export class OtherComponent {
|
||||
private _searchQuery: string = '';
|
||||
protected filteredData: BehaviorSubject<SelectData[]> = new BehaviorSubject<SelectData[]>([]);
|
||||
protected data: SelectData[] = [];
|
||||
|
||||
@Input() idButton!: string;
|
||||
@Input() textButton!: string;
|
||||
@ViewChild('menuTrigger') menuTrigger!: MatMenuTrigger;
|
||||
@ViewChild('chooseCheckbox') chooseCheckbox!: MatCheckbox;
|
||||
|
||||
@Input() dataLoaded: boolean | null = false;
|
||||
@Output() retryLoadData: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
||||
get selectedIds(): number[] {
|
||||
return this.data.filter(x => x.selected).map(x => x.id);
|
||||
}
|
||||
|
||||
set Data(data: SelectData[]) {
|
||||
this.data = data;
|
||||
this.data.forEach(x => x.selected = false);
|
||||
this.filteredData.next(this.data);
|
||||
}
|
||||
|
||||
protected get searchQuery(): string {
|
||||
return this._searchQuery;
|
||||
}
|
||||
|
||||
private updateCheckBox() {
|
||||
this.chooseCheckbox.checked = this.data.every(x => x.selected);
|
||||
this.chooseCheckbox.indeterminate = this.data.some(x => x.selected) && !this.chooseCheckbox.checked;
|
||||
}
|
||||
|
||||
protected set searchQuery(value: string) {
|
||||
this._searchQuery = value;
|
||||
this.updateFilteredData();
|
||||
}
|
||||
|
||||
protected updateFilteredData(): void {
|
||||
this.filteredData.next(this.data.filter(x =>
|
||||
x.name.toLowerCase().includes(this.searchQuery.toLowerCase())
|
||||
));
|
||||
}
|
||||
|
||||
protected clearSearchQuery(): void {
|
||||
this.searchQuery = '';
|
||||
}
|
||||
|
||||
protected clearAll(): void {
|
||||
this.data.forEach(x => x.selected = false);
|
||||
|
||||
if (this.searchQuery !== '') {
|
||||
const updatedData = this.filteredData.value.map(x => {
|
||||
return {...x, selected: false};
|
||||
});
|
||||
this.filteredData.next(updatedData);
|
||||
} else
|
||||
this.updateFilteredData();
|
||||
|
||||
this.updateCheckBox();
|
||||
}
|
||||
|
||||
protected checkData() {
|
||||
const check: boolean = this.filteredData.value.some(x => !x.selected) && !this.filteredData.value.every(x => x.selected);
|
||||
|
||||
const updatedData = this.filteredData.value.map(data => {
|
||||
this.data.find(x => x.id === data.id)!.selected = check;
|
||||
return {...data, selected: check};
|
||||
});
|
||||
|
||||
this.filteredData.next(updatedData);
|
||||
this.updateCheckBox();
|
||||
}
|
||||
|
||||
protected checkboxStateChange(item: number) {
|
||||
const data = this.data.find(x => x.id === item)!;
|
||||
data.selected = !data.selected;
|
||||
const updatedData = this.filteredData.value;
|
||||
updatedData.find(x => x.id === item)!.selected = data.selected;
|
||||
this.filteredData.next(updatedData);
|
||||
this.updateCheckBox();
|
||||
}
|
||||
|
||||
@HostListener('document:click', ['$event'])
|
||||
private onClick(event: MouseEvent): void {
|
||||
if (this.menuTrigger && this.menuTrigger.menuOpen
|
||||
&& (event.target as HTMLElement).closest('.mat-menu-content') === null
|
||||
&& !this.isInsideMenuButton(event.target as HTMLElement, this.idButton)) {
|
||||
this.menuTrigger.closeMenu();
|
||||
}
|
||||
}
|
||||
|
||||
private isInsideMenuButton(target: HTMLElement, id: string): boolean {
|
||||
let parentElement: HTMLElement | null = target.parentElement;
|
||||
return (parentElement !== null && parentElement.id === id);
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
.search-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
<div class="search-content">
|
||||
@if (professors.length === 0) {
|
||||
<app-loading-indicator [loading]="professorsLoaded !== null" (retryFunction)="professorsLoadRetry.emit()"/>
|
||||
} @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)"
|
||||
[autoActiveFirstOption]="false" [hideSingleSelectionIndicator]="true">
|
||||
@for (option of filteredProfessors | async; track option) {
|
||||
<mat-option [value]="option.id">
|
||||
{{ option.name }}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
||||
}
|
||||
</div>
|
@ -0,0 +1,59 @@
|
||||
import {Component, EventEmitter, Input, OnInit, Output} from "@angular/core";
|
||||
import {MatFormField, MatInput} from "@angular/material/input";
|
||||
import {FormControl, ReactiveFormsModule} from "@angular/forms";
|
||||
import {MatAutocompleteModule, MatAutocompleteSelectedEvent} from "@angular/material/autocomplete";
|
||||
import {AsyncPipe} from "@angular/common";
|
||||
import {map, Observable, startWith} from "rxjs";
|
||||
import {ProfessorResponse} from "@model/professorResponse";
|
||||
import {LoadingIndicatorComponent} from "@component/common/loading-indicator/loading-indicator.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-professor',
|
||||
standalone: true,
|
||||
imports: [
|
||||
MatAutocompleteModule,
|
||||
MatFormField,
|
||||
AsyncPipe,
|
||||
ReactiveFormsModule,
|
||||
MatInput,
|
||||
LoadingIndicatorComponent
|
||||
],
|
||||
templateUrl: './professor.component.html',
|
||||
styleUrl: './professor.component.css'
|
||||
})
|
||||
export class ProfessorComponent implements OnInit {
|
||||
protected professorControl = new FormControl();
|
||||
protected filteredProfessors!: Observable<ProfessorResponse[]>;
|
||||
|
||||
@Input() professors: ProfessorResponse[] = [];
|
||||
@Output() professorSelected = new EventEmitter<number>();
|
||||
|
||||
@Input() professorsLoaded: boolean | null = false;
|
||||
@Output() professorsLoadRetry: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
||||
ngOnInit(): void {
|
||||
this.filteredProfessors = this.professorControl.valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => this._filterProfessors(value))
|
||||
);
|
||||
}
|
||||
|
||||
private _filterProfessors(value: string | number): ProfessorResponse[] {
|
||||
if (typeof value === 'string') {
|
||||
if (value === '') return [];
|
||||
const filterValue = value.toLowerCase();
|
||||
return this.professors.filter(teacher => teacher.name.toLowerCase().includes(filterValue));
|
||||
} else {
|
||||
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);
|
||||
if (selectedOption) {
|
||||
this.professorControl.setValue(selectedOption.name);
|
||||
this.professorSelected.emit(selectedOption.id);
|
||||
}
|
||||
}
|
||||
}
|
14
src/components/schedule/tabs/tabs.component.css
Normal file
14
src/components/schedule/tabs/tabs.component.css
Normal file
@ -0,0 +1,14 @@
|
||||
.padding-content div {
|
||||
padding: 30px 15px;
|
||||
}
|
||||
|
||||
.margin-other-button {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: space-around;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.margin-other-button app-schedule-tabs-other {
|
||||
margin-right: 15px;
|
||||
}
|
36
src/components/schedule/tabs/tabs.component.html
Normal file
36
src/components/schedule/tabs/tabs.component.html
Normal file
@ -0,0 +1,36 @@
|
||||
<mat-tab-group dynamicHeight mat-stretch-tabs="false" mat-align-tabs="start" color="accent" class="padding-content"
|
||||
(selectedTabChange)="chooseTabs($event)">
|
||||
<mat-tab label="Группа">
|
||||
<div>
|
||||
<app-group (groupSelected)="groupSelected.emit($event)" (facultySelected)="groupLoad($event)"
|
||||
[setGroups]="groups" [faculties]="faculties" [facultiesLoaded]="facultiesLoaded"
|
||||
[groupsLoaded]="groupLoaded" (groupsLoadRetry)="groupLoad($event)"
|
||||
(facultiesLoadRetry)="facultyLoad()"/>
|
||||
</div>
|
||||
</mat-tab>
|
||||
<mat-tab label="Преподаватель">
|
||||
<div>
|
||||
<app-professor (professorSelected)="professorSelected.emit($event)" [professors]="professorsData" [professorsLoaded]="professorsLoaded" (professorsLoadRetry)="professorsLoad()"/>
|
||||
</div>
|
||||
</mat-tab>
|
||||
<mat-tab label="Кабинет">
|
||||
<div>
|
||||
<app-lecture-hall (lectureHallSelected)="lectureHallSelected.emit($event)" [campuses]="campuses"
|
||||
(campusSelected)="lectureHallLoad($event)" [lectureHalls]="lectureHalls"
|
||||
[campusesLoaded]="campusesLoaded" [lectureHallsLoaded]="lectureHallsLoaded"
|
||||
(campusesLoadRetry)="campusLoad()"
|
||||
(lectureHallsLoadRetry)="lectureHallLoad($event)"/>
|
||||
</div>
|
||||
</mat-tab>
|
||||
<mat-tab label="Другое">
|
||||
<div class="margin-other-button">
|
||||
<app-other idButton="disciplines-button" textButton="Дисциплины" #discipline [dataLoaded]="disciplinesLoaded" (retryLoadData)="loadDisciplines()"/>
|
||||
<app-other idButton="lecture-button" textButton="Кабинеты" #lecture [dataLoaded]="campusesLoaded && lectureHallsLoaded" (retryLoadData)="loadLectureHalls()"/>
|
||||
<app-other idButton="group-button" textButton="Группы" #group [dataLoaded]="facultiesLoaded && groupLoaded" (retryLoadData)="loadGroups()"/>
|
||||
<app-other idButton="professor-button" textButton="Профессоры" #professor [dataLoaded]="professorsLoaded" (retryLoadData)="professorsLoad()"/>
|
||||
<section>
|
||||
<button mat-flat-button (click)="onClickNagmi()">Отфильтровать</button>
|
||||
</section>
|
||||
</div>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
277
src/components/schedule/tabs/tabs.component.ts
Normal file
277
src/components/schedule/tabs/tabs.component.ts
Normal file
@ -0,0 +1,277 @@
|
||||
import {Component, EventEmitter, Output, ViewChild} from '@angular/core';
|
||||
import {HttpClientModule} from "@angular/common/http";
|
||||
import {ApiService} from '@service/api.service';
|
||||
import {OtherComponent} from "@component/schedule/tabs/other/other.component";
|
||||
import {MatTab, MatTabChangeEvent, MatTabGroup} from "@angular/material/tabs";
|
||||
import {
|
||||
ProfessorComponent
|
||||
} from "@component/schedule/tabs/professor/professor.component";
|
||||
import {GroupComponent} from "@component/schedule/tabs/group/group.component";
|
||||
import {
|
||||
LectureHallComponent
|
||||
} from "@component/schedule/tabs/lecture-hall/lecture-hall.component";
|
||||
import {ProfessorResponse} from "@model/professorResponse";
|
||||
import {catchError, map, Observable, of, switchMap, tap} from "rxjs";
|
||||
import {GroupResponse} from "@model/groupResponse";
|
||||
import {FacultyResponse} from "@model/facultyResponse";
|
||||
import {CampusBasicInfoResponse} from "@model/campusBasicInfoResponse";
|
||||
import {LectureHallResponse} from "@model/lectureHallResponse";
|
||||
import {ReactiveFormsModule} from "@angular/forms";
|
||||
import {AsyncPipe, NgIf} from "@angular/common";
|
||||
import {DisciplineResponse} from "@model/disciplineResponse";
|
||||
import {MatButton} from "@angular/material/button";
|
||||
import {DataSpinnerComponent} from "@component/common/data-spinner/data-spinner.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-tabs',
|
||||
standalone: true,
|
||||
imports: [
|
||||
HttpClientModule,
|
||||
OtherComponent,
|
||||
MatTabGroup,
|
||||
MatTab,
|
||||
ProfessorComponent,
|
||||
GroupComponent,
|
||||
LectureHallComponent,
|
||||
ReactiveFormsModule,
|
||||
AsyncPipe,
|
||||
NgIf,
|
||||
MatButton,
|
||||
DataSpinnerComponent
|
||||
],
|
||||
templateUrl: './tabs.component.html',
|
||||
styleUrl: './tabs.component.css'
|
||||
})
|
||||
|
||||
export class TabsComponent {
|
||||
protected professorsData: ProfessorResponse[] = [];
|
||||
|
||||
protected faculties: Observable<FacultyResponse[]> = of([]);
|
||||
protected groups: Observable<GroupResponse[]> = of([]);
|
||||
private groupsData: Observable<GroupResponse[]> = of([]);
|
||||
|
||||
protected campuses: Observable<CampusBasicInfoResponse[]> = of([]);
|
||||
protected lectureHalls: Observable<LectureHallResponse[]> = of([]);
|
||||
private lectureHallsData: Observable<LectureHallResponse[]> = of([]);
|
||||
|
||||
// States
|
||||
protected facultiesLoaded: boolean | null = false;
|
||||
protected groupLoaded: boolean | null = false;
|
||||
protected campusesLoaded: boolean | null = false;
|
||||
protected lectureHallsLoaded: boolean | null = false;
|
||||
protected disciplinesLoaded: boolean | null = false;
|
||||
protected professorsLoaded: boolean | null = false;
|
||||
|
||||
@Output() groupSelected: EventEmitter<number> = new EventEmitter<number>();
|
||||
@Output() lectureHallSelected: EventEmitter<number> = new EventEmitter<number>();
|
||||
@Output() professorSelected: EventEmitter<number> = new EventEmitter<number>();
|
||||
|
||||
constructor(private api: ApiService) {
|
||||
this.facultyLoad().then();
|
||||
}
|
||||
|
||||
protected async chooseTabs(event: MatTabChangeEvent) {
|
||||
console.log(event);
|
||||
switch (event.index) {
|
||||
case 0:
|
||||
await this.facultyLoad();
|
||||
break;
|
||||
case 1:
|
||||
this.professorsLoad();
|
||||
break;
|
||||
case 2:
|
||||
await this.campusLoad();
|
||||
break;
|
||||
case 3:
|
||||
await this.extensionLoad();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected async facultyLoad() {
|
||||
if (this.facultiesLoaded === null) this.facultiesLoaded = false;
|
||||
if (this.facultiesLoaded) return;
|
||||
|
||||
this.faculties = this.api.get<FacultyResponse[]>("Faculty/Get").pipe(
|
||||
tap(() => {
|
||||
this.facultiesLoaded = true;
|
||||
}),
|
||||
catchError((error) => {
|
||||
this.facultiesLoaded = null;
|
||||
throw error;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
protected groupLoad(id: number) {
|
||||
if (this.groupLoaded === null) this.groupLoaded = false;
|
||||
|
||||
if (this.groupLoaded)
|
||||
this.groups = this.groupsData.pipe(map(data => data.filter(x => x.facultyId === id)));
|
||||
else
|
||||
this.groups = this.api.get<GroupResponse[]>("Group/GetByFaculty/" + id).pipe(
|
||||
tap(() => {
|
||||
this.groupLoaded = false;
|
||||
}),
|
||||
catchError((error) => {
|
||||
this.groupLoaded = null;
|
||||
throw error;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
protected professorsLoad() {
|
||||
if (this.professorsLoaded === null) this.professorsLoaded = false;
|
||||
|
||||
if (this.professorsLoaded) return;
|
||||
|
||||
this.api.get<ProfessorResponse[]>("Professor/Get").pipe(
|
||||
catchError((error) => {
|
||||
this.professorsLoaded = null;
|
||||
throw error;
|
||||
})
|
||||
).subscribe(data => {
|
||||
this.professorsData = data;
|
||||
this.professorEx.Data = data.map(x =>
|
||||
({
|
||||
id: x.id,
|
||||
name: x.altName ? x.altName : x.name,
|
||||
selected: false
|
||||
}));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
protected async campusLoad() {
|
||||
if (this.campusesLoaded === null) this.campusesLoaded = false;
|
||||
|
||||
if (this.campusesLoaded) return;
|
||||
|
||||
this.campuses = this.api.get<CampusBasicInfoResponse[]>("Campus/Get").pipe(
|
||||
tap(() => {
|
||||
this.campusesLoaded = true;
|
||||
}),
|
||||
catchError((error) => {
|
||||
this.campusesLoaded = null;
|
||||
throw error;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
protected lectureHallLoad(id: number) {
|
||||
if (this.lectureHallsLoaded === null) this.lectureHallsLoaded = false;
|
||||
|
||||
if (this.lectureHallsLoaded)
|
||||
this.lectureHalls = this.lectureHallsData.pipe(map(data => data.filter(x => x.campusId === id)));
|
||||
else
|
||||
this.lectureHalls = this.api.get<LectureHallResponse[]>("LectureHall/GetByCampus/" + id).pipe(
|
||||
tap(() => {
|
||||
this.lectureHallsLoaded = false;
|
||||
}),
|
||||
catchError((error) => {
|
||||
this.lectureHallsLoaded = null;
|
||||
throw error;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
protected async loadLectureHalls() {
|
||||
if (!this.campusesLoaded)
|
||||
await this.campusLoad();
|
||||
|
||||
if (!this.lectureHallsLoaded) {
|
||||
this.lectureHallsData = this.api.get<LectureHallResponse[]>("LectureHall/Get");
|
||||
this.lectureHallsData.pipe(
|
||||
switchMap(lectureHalls => this.campuses.pipe(
|
||||
map(campuses => {
|
||||
return lectureHalls.map(x => {
|
||||
const campus = campuses.find(c => c.id === x.campusId);
|
||||
const codeName = campus ? campus.codeName : '';
|
||||
return {
|
||||
id: x.id,
|
||||
name: `${x.name} (${codeName})`,
|
||||
selected: false
|
||||
};
|
||||
});
|
||||
})
|
||||
)),
|
||||
).subscribe(data => {
|
||||
this.lectureHallEx.Data = data;
|
||||
this.lectureHallsLoaded = true;
|
||||
this.campusesLoaded = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected async loadDisciplines() {
|
||||
if (!this.disciplinesLoaded) {
|
||||
this.api.get<DisciplineResponse[]>("Discipline/Get").pipe(
|
||||
catchError((error) => {
|
||||
this.disciplinesLoaded = null;
|
||||
throw error;
|
||||
})).subscribe(data => {
|
||||
this.disciplineEx.Data = data.map(x =>
|
||||
({
|
||||
id: x.id,
|
||||
name: x.name,
|
||||
selected: false
|
||||
}));
|
||||
this.disciplinesLoaded = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected async loadGroups() {
|
||||
if (!this.facultiesLoaded)
|
||||
await this.facultyLoad();
|
||||
|
||||
if (!this.groupLoaded) {
|
||||
this.groupsData = this.api.get<GroupResponse[]>("Group/Get");
|
||||
this.groupsData.pipe(
|
||||
switchMap(groups => this.faculties.pipe(
|
||||
map(campuses => {
|
||||
return groups.map(x => {
|
||||
const faculties = campuses.find(c => c.id === x.facultyId);
|
||||
const name = faculties ? faculties.name : '';
|
||||
return {
|
||||
id: x.id,
|
||||
name: `${x.name} (${name})`,
|
||||
selected: false
|
||||
};
|
||||
});
|
||||
})
|
||||
))
|
||||
).subscribe(data => {
|
||||
this.groupEx.Data = data;
|
||||
this.groupLoaded = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected async extensionLoad() {
|
||||
// Lecture Hall
|
||||
await this.loadLectureHalls();
|
||||
|
||||
// Disciplines
|
||||
await this.loadDisciplines();
|
||||
|
||||
// Groups
|
||||
await this.loadGroups();
|
||||
|
||||
// Professors
|
||||
if (this.professorsData.length === 0)
|
||||
this.professorsLoad();
|
||||
}
|
||||
|
||||
@ViewChild('discipline') disciplineEx!: OtherComponent;
|
||||
@ViewChild('lecture') lectureHallEx!: OtherComponent;
|
||||
@ViewChild('group') groupEx!: OtherComponent;
|
||||
@ViewChild('professor') professorEx!: OtherComponent;
|
||||
|
||||
onClickNagmi() {
|
||||
console.log('huy = ' + this.disciplineEx.selectedIds);
|
||||
console.log('huy2 = ' + this.lectureHallEx.selectedIds);
|
||||
console.log('huy3 = ' + this.groupEx.selectedIds);
|
||||
console.log('huy3 = ' + this.professorEx.selectedIds);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user