feat: add import to excel
Made at the request of the customer
This commit is contained in:
parent
7c66f31bac
commit
38b877608f
@ -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<Type>(method, doneEndpoint, {
|
||||
withCredentials: request.withCredentials,
|
||||
headers: request.httpHeaders,
|
||||
body: request.data
|
||||
body: request.data,
|
||||
}).pipe(
|
||||
catchError(error => {
|
||||
if (!secondTry && error.status === 401)
|
||||
|
22
src/api/v1/import.service.ts
Normal file
22
src/api/v1/import.service.ts
Normal file
@ -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'
|
||||
});
|
||||
}
|
||||
}
|
@ -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<ScheduleResponse[]>]>();
|
||||
@Output() eventResult = new EventEmitter<[TabsSelect, number, Observable<ScheduleResponse[]>, 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() {
|
||||
this.eventResult.emit(
|
||||
[
|
||||
TabsSelect.Other,
|
||||
0,
|
||||
this.scheduleApi.postSchedule(({
|
||||
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(data),
|
||||
data
|
||||
]
|
||||
);
|
||||
}
|
||||
|
8
src/pages/schedule/confirm-dialog.component.html
Normal file
8
src/pages/schedule/confirm-dialog.component.html
Normal file
@ -0,0 +1,8 @@
|
||||
<h1 mat-dialog-title>Подтверждение</h1>
|
||||
<div mat-dialog-content>
|
||||
<p>Вы уверены, что хотите запросить Excel с выбранными данными?</p>
|
||||
</div>
|
||||
<div mat-dialog-actions>
|
||||
<button mat-button color="accent" (click)="onConfirm()">Запросить</button>
|
||||
<button mat-button (click)="onCancel()">Отмена</button>
|
||||
</div>
|
27
src/pages/schedule/confirm-dialog.component.ts
Normal file
27
src/pages/schedule/confirm-dialog.component.ts
Normal file
@ -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<ConfirmDialogComponent>) { }
|
||||
|
||||
protected onConfirm(): void {
|
||||
this.dialogRef.close(true);
|
||||
}
|
||||
|
||||
protected onCancel(): void {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
}
|
@ -12,7 +12,12 @@
|
||||
<app-table [currentWeek]="currentWeek" [startWeek]="startWeek" [data]="data" [isLoad]="isLoadTable" [disciplineWithWeeks]="disciplineWithWeeks"/>
|
||||
</mat-sidenav-content>
|
||||
|
||||
<mat-sidenav-content>
|
||||
<mat-sidenav-content style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<mat-checkbox (change)="changeDisciplineWeeksView($event.checked)" [checked]="disciplineWithWeeks">Показать недели в дисциплине</mat-checkbox>
|
||||
@if(excelImportLoader) {
|
||||
<app-data-spinner/>
|
||||
} @else {
|
||||
<button mat-button (click)="openDialog()" *appHasRole="AuthRoles.Admin">Импортировать расписание (.xlsx)</button>
|
||||
}
|
||||
</mat-sidenav-content>
|
||||
</mat-sidenav-container>
|
||||
|
@ -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<ScheduleResponse[]>]) {
|
||||
protected result(data: [TabsSelect, number, Observable<ScheduleResponse[]>, 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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user