Compare commits

...

6 Commits

Author SHA1 Message Date
52b2af097f feat: add options for campuses
All checks were successful
Build and Deploy Angular App / build (push) Successful in 1m27s
2025-02-06 17:26:18 +03:00
ea5e731bd2 feat: add notify for success upload
All checks were successful
Build and Deploy Angular App / build (push) Successful in 48s
2025-02-06 16:42:04 +03:00
74a7fe7eb6 fix: allow save if data is empty 2025-02-06 16:41:43 +03:00
2f9d552e43 refactor: remove unused code 2025-02-06 16:41:24 +03:00
004671c006 feat: add support for the new api
All checks were successful
Build and Deploy Angular App / build (push) Successful in 43s
2025-02-03 03:37:30 +03:00
0f6a1e7a45 fix: use is array instead typeof 2025-02-03 03:36:41 +03:00
8 changed files with 66 additions and 91 deletions

View File

@ -40,7 +40,7 @@ export default abstract class ApiService {
Object.keys(queryParams).forEach(key => {
const value = queryParams[key];
if (value !== null && value !== undefined) {
if (typeof (value) === typeof (Array)) {
if (Array.isArray(value)) {
(value as Array<any>).forEach(x => url.searchParams.append(key, x.toString()));
} else
url.searchParams.append(key, value.toString());

View File

@ -74,14 +74,14 @@ export class ScheduleService extends ApiService {
return this.addAuth(request).post<any>(request);
}
public uploadScheduleFile(files: File[], force: boolean) {
public uploadScheduleFile(files: File[], campus: string[], force: boolean) {
const formData = new FormData();
files.forEach(file => formData.append('files', file, file.name));
const request = this.createRequestBuilder()
.setEndpoint('Upload')
.setData(formData)
.setQueryParams({force: force})
.setQueryParams({force: force, defaultCampus: campus})
.build;
return this.addAuth(request).post(request);

View File

@ -18,11 +18,23 @@
@if (selectedFiles.length > 0) {
<div style="margin-top: 15px;">
<p>Выбранные файлы:</p>
<ul>
@for (file of selectedFiles; track $index) {
<li>{{ file.name }}</li>
}
</ul>
@for (item of selectedFiles; track $index) {
<p>
{{ item.file.name }}
</p>
<mat-form-field color="accent" style="margin-bottom: 18px;">
<mat-label>Кампус по умолчанию</mat-label>
<input matInput type="text" [(ngModel)]="item.campus"
[matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="onSelectCampus($event.option.value, item)">
@for (option of onFilter(item.campus); track $index) {
<mat-option [value]="option">
{{ option }}
</mat-option>
}
</mat-autocomplete>
</mat-form-field>
}
</div>
}
</app-configuration-card>

View File

@ -9,6 +9,12 @@ import {MatButtonModule} from "@angular/material/button";
import {MatIcon} from "@angular/material/icon";
import {ScheduleService} from "@api/v1/configuration/schedule.service";
import {DataSpinnerComponent} from "@component/common/data-spinner/data-spinner.component";
import {MatFormFieldModule} from "@angular/material/form-field";
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
import {MatInput} from "@angular/material/input";
import {ToastrService} from "ngx-toastr";
import {MatAutocomplete, MatAutocompleteTrigger, MatOption} from "@angular/material/autocomplete";
import {CampusService} from "@api/v1/campus.service";
@Component({
selector: 'app-schedule-file-upload',
@ -16,17 +22,36 @@ import {DataSpinnerComponent} from "@component/common/data-spinner/data-spinner.
ConfigurationCardComponent,
MatButtonModule,
MatIcon,
DataSpinnerComponent
DataSpinnerComponent,
MatFormFieldModule,
FormsModule,
MatInput,
MatAutocomplete,
MatAutocompleteTrigger,
MatOption,
ReactiveFormsModule
],
templateUrl: './schedule-file-upload.component.html',
providers: [ScheduleService]
providers: [ScheduleService, CampusService]
})
export class ScheduleFileUploadComponent {
selectedFiles: File[] = [];
fileLoading: boolean = false;
protected selectedFiles: { file: File, campus: string }[] = [];
protected fileLoading: boolean = false;
protected campuses: string[] = [];
@ViewChild('fileInput') input!: ElementRef;
constructor(private dialog: MatDialog, private api: ScheduleService) {
constructor(
private dialog: MatDialog,
private api: ScheduleService,
private notify: ToastrService,
campus: CampusService) {
campus.getCampus().subscribe(data => {
this.campuses = data.map(x => x.codeName);
});
}
protected onSelectCampus(value: string, item: { file: File, campus: string }) {
item.campus = value;
}
protected saveFunction() {
@ -34,23 +59,32 @@ export class ScheduleFileUploadComponent {
const dialogRef = this.dialog.open(ConfirmDeleteScheduleDialogComponent);
return dialogRef.afterClosed().pipe(switchMap(result => {
return this.api.uploadScheduleFile(this.selectedFiles, result);
return this.api.uploadScheduleFile(
this.selectedFiles.map(x => x.file),
this.selectedFiles.map(x => x.campus),
result);
}));
};
}
onFileChooseClick() {
protected onFilter(value: string): string[] {
const filterValue = value?.toLowerCase() || '';
return this.campuses.filter(campus => campus.toLowerCase().includes(filterValue));
}
protected onFileChooseClick() {
this.fileLoading = true;
this.input.nativeElement.click();
}
onFileSelected(event: any): void {
protected onFileSelected(event: any): void {
this.fileLoading = false;
this.selectedFiles = Array.from(event.target.files);
this.selectedFiles = Array.from(event.target.files).map(file => ({file: <File>file, campus: ''}));
}
onUpload(data: Observable<any>): void {
protected onUpload(data: Observable<any>): void {
data.subscribe(_ => {
this.notify.info(`Файлы в размере ${this.selectedFiles.length} успешно загружены. Задача поставлена в очередь`);
this.selectedFiles = [];
});
}

View File

@ -69,9 +69,9 @@ export class SkipUpdateScheduleComponent {
}
validateSaveButton(): boolean {
return this.dateItems.some(item =>
return (this.dateItems.length == 0 || this.dateItems.some(item =>
(item.start && item.end) || item.date
) && JSON.stringify(this.dateItems) != JSON.stringify(this.dateItemsBefore);
)) && JSON.stringify(this.dateItems) != JSON.stringify(this.dateItemsBefore);
}
saveFunction() {

View File

@ -1,10 +0,0 @@
.notification-content {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 5px;
}
.close-button {
margin-left: 8px;
}

View File

@ -1,11 +0,0 @@
<div class="notification-content" [class]="data.className">
<span>
{{ data.message }}
</span>
<button mat-icon-button class="close-button" (click)="dismiss()">
<mat-icon>close</mat-icon>
</button>
</div>
@if (showProgressBar) {
<mat-progress-bar mode="determinate" [value]="progress" [color]="color"/>
}

View File

@ -1,50 +0,0 @@
import {Component, Inject} from '@angular/core';
import {MatIcon} from "@angular/material/icon";
import {MatProgressBar} from "@angular/material/progress-bar";
import {MAT_SNACK_BAR_DATA, MatSnackBarRef} from "@angular/material/snack-bar";
import {MatIconButton} from "@angular/material/button";
@Component({
selector: 'app-notification',
standalone: true,
imports: [
MatIconButton,
MatIcon,
MatProgressBar
],
templateUrl: './notification.component.html',
styleUrl: './notification.component.css'
})
export class NotificationComponent {
showProgressBar: boolean = false;
progress: number = 100;
color: string = "primary";
constructor(@Inject(MAT_SNACK_BAR_DATA) public data: any, private snackBarRef: MatSnackBarRef<NotificationComponent>) {
if (data.duration) {
this.startProgress(data.duration);
this.showProgressBar = true;
}
if (data.color) {
this.color = data.color;
}
}
dismiss(): void {
this.snackBarRef.dismiss();
}
private startProgress(duration: number): void {
const interval: number = duration / 100;
const progressInterval = setInterval(async () => {
this.progress--;
if (this.progress === 0) {
clearInterval(progressInterval);
setTimeout(() => {
this.dismiss();
}, 1000);
}
}, interval);
}
}