diff --git a/src/api/api.service.ts b/src/api/api.service.ts index 3b0ebdb..02d2031 100644 --- a/src/api/api.service.ts +++ b/src/api/api.service.ts @@ -23,7 +23,7 @@ export enum AvailableVersion { @Injectable() export default abstract class ApiService { - constructor(private http: HttpClient, private notify: ToastrService, private router: Router) { + constructor(protected http: HttpClient, protected notify: ToastrService, private router: Router) { } private apiUrl = environment.apiUrl; @@ -65,7 +65,7 @@ export default abstract class ApiService { return this.http.request(method, doneEndpoint, { withCredentials: request.withCredentials, headers: request.httpHeaders, - body: request.data + body: request.data, }).pipe( catchError(error => { if (!secondTry && error.status === 401) diff --git a/src/api/v1/import.service.ts b/src/api/v1/import.service.ts new file mode 100644 index 0000000..fe6dcfa --- /dev/null +++ b/src/api/v1/import.service.ts @@ -0,0 +1,22 @@ +import {Injectable} from "@angular/core"; +import ApiService, {AvailableVersion} from "@api/api.service"; +import {ScheduleRequest} from "@api/v1/scheduleRequest"; + +@Injectable() +export class ImportService extends ApiService { + public readonly basePath = 'Import/'; + public readonly version = AvailableVersion.v1; + + public importToExcel(data: ScheduleRequest) { + let request = this.createRequestBuilder() + .setData(data) + .setEndpoint('ImportToExcel') + .build; + + console.log(this.combinedUrl(request)); + console.log(data); + return this.http.post(this.combinedUrl(request), data, { + responseType: 'blob' + }); + } +} diff --git a/src/components/schedule/tabs/tabs.component.ts b/src/components/schedule/tabs/tabs.component.ts index 5ec9a7b..c62f224 100644 --- a/src/components/schedule/tabs/tabs.component.ts +++ b/src/components/schedule/tabs/tabs.component.ts @@ -18,6 +18,7 @@ import {ProfessorService} from "@api/v1/professor.service"; import {AuthRoles} from "@model/AuthRoles"; import {HasRoleDirective} from "@/directives/has-role.directive"; import {TabSelectType, TabStorageService} from "@service/tab-storage.service"; +import {ScheduleRequest} from "@api/v1/scheduleRequest"; export enum TabsSelect { Group, @@ -48,7 +49,7 @@ export enum TabsSelect { }) export class TabsComponent implements AfterViewInit { - @Output() eventResult = new EventEmitter<[TabsSelect, number, Observable]>(); + @Output() eventResult = new EventEmitter<[TabsSelect, number, Observable, ScheduleRequest]>(); private currentTab: number = -1; constructor(private scheduleApi: ScheduleService, @@ -68,7 +69,8 @@ export class TabsComponent implements AfterViewInit { [ TabsSelect.Group, event, - this.scheduleApi.getByGroup(event) + this.scheduleApi.getByGroup(event), + {groups: [event]} ] )); @@ -76,7 +78,8 @@ export class TabsComponent implements AfterViewInit { [ TabsSelect.Professor, event, - this.scheduleApi.getByProfessor(event) + this.scheduleApi.getByProfessor(event), + {professors: [event]} ] )); @@ -84,7 +87,8 @@ export class TabsComponent implements AfterViewInit { [ TabsSelect.LectureHall, event, - this.scheduleApi.getByLectureHall(event) + this.scheduleApi.getByLectureHall(event), + {lectureHalls: [event]} ] )); @@ -196,16 +200,19 @@ export class TabsComponent implements AfterViewInit { protected readonly AuthRoles = AuthRoles; protected otherFilter() { + const data: ScheduleRequest = ({ + groups: this.groupEx.selectedIds, + disciplines: this.disciplineEx.selectedIds, + professors: this.professorEx.selectedIds, + lectureHalls: this.lectureHallEx.selectedIds + }); + this.eventResult.emit( [ TabsSelect.Other, 0, - this.scheduleApi.postSchedule(({ - groups: this.groupEx.selectedIds, - disciplines: this.disciplineEx.selectedIds, - professors: this.professorEx.selectedIds, - lectureHalls: this.lectureHallEx.selectedIds - })) + this.scheduleApi.postSchedule(data), + data ] ); } diff --git a/src/pages/schedule/confirm-dialog.component.html b/src/pages/schedule/confirm-dialog.component.html new file mode 100644 index 0000000..aa7e712 --- /dev/null +++ b/src/pages/schedule/confirm-dialog.component.html @@ -0,0 +1,8 @@ +

Подтверждение

+
+

Вы уверены, что хотите запросить Excel с выбранными данными?

+
+
+ + +
diff --git a/src/pages/schedule/confirm-dialog.component.ts b/src/pages/schedule/confirm-dialog.component.ts new file mode 100644 index 0000000..8a46065 --- /dev/null +++ b/src/pages/schedule/confirm-dialog.component.ts @@ -0,0 +1,27 @@ +import { Component } from '@angular/core'; +import {MatDialogActions, MatDialogContent, MatDialogRef, MatDialogTitle} from '@angular/material/dialog'; +import {MatButton} from "@angular/material/button"; + +@Component({ + selector: 'app-confirm-dialog', + templateUrl: './confirm-dialog.component.html', + imports: [ + MatDialogTitle, + MatDialogContent, + MatDialogActions, + MatButton + ], + standalone: true +}) +export class ConfirmDialogComponent { + + constructor(public dialogRef: MatDialogRef) { } + + protected onConfirm(): void { + this.dialogRef.close(true); + } + + protected onCancel(): void { + this.dialogRef.close(false); + } +} diff --git a/src/pages/schedule/schedule.component.html b/src/pages/schedule/schedule.component.html index c9c92ca..dc38806 100644 --- a/src/pages/schedule/schedule.component.html +++ b/src/pages/schedule/schedule.component.html @@ -12,7 +12,12 @@ - + Показать недели в дисциплине + @if(excelImportLoader) { + + } @else { + + } diff --git a/src/pages/schedule/schedule.component.ts b/src/pages/schedule/schedule.component.ts index 44aa4c7..e7003fc 100644 --- a/src/pages/schedule/schedule.component.ts +++ b/src/pages/schedule/schedule.component.ts @@ -1,55 +1,62 @@ -import {Component, LOCALE_ID, OnInit, ViewChild} from '@angular/core'; -import {TableComponent} from "@component/schedule/table/table.component"; -import {MatFormField, MatInput} from "@angular/material/input"; -import {MatButton} from "@angular/material/button"; -import {FormsModule} from "@angular/forms"; +import {Component, LOCALE_ID, ViewChild} from '@angular/core'; import {AdditionalText, TableHeaderComponent} from "@component/schedule/table-header/table-header.component"; import {addDays, weekInYear} from "@progress/kendo-date-math"; -import {MatCard} from "@angular/material/card"; -import {MatSidenavModule} from "@angular/material/sidenav"; import {TabsComponent, TabsSelect} from "@component/schedule/tabs/tabs.component"; import {catchError, Observable} from "rxjs"; import {ScheduleService} from "@api/v1/schedule.service"; import {ScheduleResponse} from "@api/v1/scheduleResponse"; import {PeriodTimes} from "@model/pairPeriodTime"; -import {MatCheckbox} from "@angular/material/checkbox"; import {ActivatedRoute} from "@angular/router"; import {TabStorageService} from "@service/tab-storage.service"; +import {MatDialog} from "@angular/material/dialog"; +import {ConfirmDialogComponent} from "@page/schedule/confirm-dialog.component"; +import {AuthRoles} from "@model/AuthRoles"; +import {ImportService} from "@api/v1/import.service"; +import {ScheduleRequest} from "@api/v1/scheduleRequest"; +import {ToastrService} from "ngx-toastr"; +import {MatSidenavModule} from "@angular/material/sidenav"; +import {TableComponent} from "@component/schedule/table/table.component"; +import {MatCheckbox} from "@angular/material/checkbox"; +import {DataSpinnerComponent} from "@component/common/data-spinner/data-spinner.component"; +import {MatButton} from "@angular/material/button"; +import {HasRoleDirective} from "@/directives/has-role.directive"; @Component({ selector: 'app-schedule', standalone: true, imports: [ - TableComponent, - MatInput, - MatFormField, - MatButton, - FormsModule, - TableHeaderComponent, - MatCard, MatSidenavModule, TabsComponent, - MatCheckbox + TableHeaderComponent, + TableComponent, + MatCheckbox, + DataSpinnerComponent, + MatButton, + HasRoleDirective ], templateUrl: './schedule.component.html', styleUrl: './schedule.component.css', providers: [ ScheduleService, + ImportService, {provide: LOCALE_ID, useValue: 'ru-RU'} ] }) -export class ScheduleComponent implements OnInit { +export class ScheduleComponent { + private lastRequest: ScheduleRequest | null = null; + protected startWeek: Date; protected data: ScheduleResponse[] = []; protected startTerm: Date; protected isLoadTable: boolean = false; protected pairPeriods: PeriodTimes = {}; protected disciplineWithWeeks: boolean = false; + protected excelImportLoader: boolean = false; @ViewChild('tableHeader') childComponent!: TableHeaderComponent; - constructor(api: ScheduleService, route: ActivatedRoute) { + constructor(api: ScheduleService, route: ActivatedRoute, private importApi: ImportService, private notify: ToastrService, public dialog: MatDialog) { route.queryParams.subscribe(params => { TabStorageService.selectDataFromQuery(params); }); @@ -71,11 +78,10 @@ export class ScheduleComponent implements OnInit { }); } - ngOnInit(): void { - } - - protected result(data: [TabsSelect, number, Observable]) { + protected result(data: [TabsSelect, number, Observable, ScheduleRequest]) { this.isLoadTable = true; + this.lastRequest = data[3]; + data[2] .pipe(catchError(error => { this.data = []; @@ -147,4 +153,35 @@ export class ScheduleComponent implements OnInit { localStorage.setItem('disciplineWithWeeks', checked.toString()); this.disciplineWithWeeks = checked; } + + protected openDialog() { + if (this.lastRequest == null) { + this.notify.error("It is not possible to make an import request because the table data has not been selected", "Import error"); + return; + } + const dialogRef = this.dialog.open(ConfirmDialogComponent); + + dialogRef.afterClosed().subscribe(result => { + if (result && this.lastRequest != null) { + this.excelImportLoader = true; + this.importApi.importToExcel(this.lastRequest).subscribe({ + next: (blob: Blob) => { + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = 'schedule.xlsx'; + a.click(); + window.URL.revokeObjectURL(url); + this.excelImportLoader = false; + }, + error: _ => { + this.excelImportLoader = false; + this.notify.error("Failed to import Excel file"); + } + }); + } + }); + } + + protected readonly AuthRoles = AuthRoles; }