refactor: clean code

This commit is contained in:
Polianin Nikita 2024-12-18 06:57:27 +03:00
parent 3af8c43cd9
commit a2d4151cc3
47 changed files with 181 additions and 150 deletions

View File

@ -15,7 +15,7 @@ import {Router} from "@angular/router";
import {Injectable} from "@angular/core"; import {Injectable} from "@angular/core";
import {RequestBuilder, RequestData} from "@api/RequestBuilder"; import {RequestBuilder, RequestData} from "@api/RequestBuilder";
import {ToastrService} from "ngx-toastr"; import {ToastrService} from "ngx-toastr";
import {AuthRoles} from "@model/AuthRoles"; import {AuthRoles} from "@model/authRoles";
export enum AvailableVersion { export enum AvailableVersion {
v1 v1
@ -69,7 +69,7 @@ export default abstract class ApiService {
}).pipe( }).pipe(
catchError(error => { catchError(error => {
if (!secondTry && error.status === 401) if (!secondTry && error.status === 401)
return this.handle401Error().pipe( return this.handle401Error(error).pipe(
switchMap(() => this.sendHttpRequest<Type>(method, request, true)) switchMap(() => this.sendHttpRequest<Type>(method, request, true))
); );
else { else {
@ -88,7 +88,7 @@ export default abstract class ApiService {
}); });
} }
private handle401Error(): Observable<any> { private handle401Error(error: any): Observable<any> {
if (ApiService.isRefreshingToken.value) if (ApiService.isRefreshingToken.value)
return ApiService.refreshTokenSubject.asObservable(); return ApiService.refreshTokenSubject.asObservable();
@ -103,7 +103,7 @@ export default abstract class ApiService {
ApiService.isRefreshingToken.next(false); ApiService.isRefreshingToken.next(false);
ApiService.refreshTokenSubject.error(err); ApiService.refreshTokenSubject.error(err);
ApiService.refreshTokenSubject = new ReplaySubject(1); ApiService.refreshTokenSubject = new ReplaySubject(1);
throw err; throw error;
}) })
); );
} }

View File

@ -19,7 +19,7 @@ export default class AuthApiService extends ApiService {
return this.post<AuthRoles>(request); return this.post<AuthRoles>(request);
} }
public reLogin(){ public reLogin() {
let request = this.createRequestBuilder() let request = this.createRequestBuilder()
.setEndpoint('ReLogin') .setEndpoint('ReLogin')
.setWithCredentials() .setWithCredentials()

View File

@ -1,7 +1,7 @@
import {Injectable} from "@angular/core"; import {Injectable} from "@angular/core";
import ApiService, {AvailableVersion} from "@api/api.service"; import ApiService, {AvailableVersion} from "@api/api.service";
import {DateOnly} from "@model/DateOnly"; import {DateOnly} from "@model/dateOnly";
import {PeriodTimes} from "@model/pairPeriodTime"; import {PairPeriodTime} from "@model/pairPeriodTime";
import {ScheduleRequest} from "@api/v1/scheduleRequest"; import {ScheduleRequest} from "@api/v1/scheduleRequest";
import {ScheduleResponse} from "@api/v1/scheduleResponse"; import {ScheduleResponse} from "@api/v1/scheduleResponse";
import {map} from "rxjs"; import {map} from "rxjs";
@ -16,7 +16,7 @@ export class ScheduleService extends ApiService {
} }
public pairPeriod() { public pairPeriod() {
return this.get<PeriodTimes>('PairPeriod'); return this.get<PairPeriodTime>('PairPeriod');
} }
public postSchedule(data: ScheduleRequest) { public postSchedule(data: ScheduleRequest) {

View File

@ -1 +1 @@
<mat-progress-spinner [color]="color" mode="indeterminate" [diameter]="scale" /> <mat-progress-spinner [color]="color" mode="indeterminate" [diameter]="scale"/>

View File

@ -1,13 +1,11 @@
import {Component, Input} from '@angular/core'; import {Component, Input} from '@angular/core';
import {MatProgressSpinner} from "@angular/material/progress-spinner"; import {MatProgressSpinner} from "@angular/material/progress-spinner";
import {NgStyle} from "@angular/common";
@Component({ @Component({
selector: 'app-data-spinner', selector: 'app-data-spinner',
standalone: true, standalone: true,
imports: [ imports: [
MatProgressSpinner, MatProgressSpinner,
NgStyle
], ],
templateUrl: './data-spinner.component.html' templateUrl: './data-spinner.component.html'
}) })

View File

@ -22,8 +22,8 @@
<hr/> <hr/>
<div class="app-footer-copyright"> <div class="app-footer-copyright">
<span>Powered by <a href="https://winsomnia.net">Winsomnia</a> &copy;{{ currentYear }}.</span> <span>Powered by <a href="https://winsomnia.net">Winsomnia</a> &copy;{{ currentYear }}.</span>
<a href="https://opensource.org/license/mit/">Code licensed under an MIT-style License.</a> <a href="https://opensource.org/license/mit/">Code licensed under an MIT-style License.</a>
<span>Current Version: {{ version }}</span> <span>Current Version: {{ version }}</span>
</div> </div>
</div> </div>
</footer> </footer>

View File

@ -1,16 +1,13 @@
import { Component } from '@angular/core'; import {Component} from '@angular/core';
import {MatToolbar} from "@angular/material/toolbar"; import {MatToolbar} from "@angular/material/toolbar";
import {MatAnchor, MatButton} from "@angular/material/button";
import {HasRoleDirective} from "@/directives/has-role.directive"; import {HasRoleDirective} from "@/directives/has-role.directive";
import {AuthRoles} from "@model/AuthRoles"; import {AuthRoles} from "@model/authRoles";
@Component({ @Component({
selector: 'app-header', selector: 'app-header',
standalone: true, standalone: true,
imports: [ imports: [
MatToolbar, MatToolbar,
MatButton,
MatAnchor,
HasRoleDirective HasRoleDirective
], ],
templateUrl: './header.component.html', templateUrl: './header.component.html',

View File

@ -1,14 +1,13 @@
import {Component, EventEmitter, Input, Output} from '@angular/core'; import {Component, EventEmitter, Input, Output} from '@angular/core';
import {DataSpinnerComponent} from "@component/common/data-spinner/data-spinner.component"; import {DataSpinnerComponent} from "@component/common/data-spinner/data-spinner.component";
import {MatIcon} from "@angular/material/icon"; import {MatIcon} from "@angular/material/icon";
import {MatButton, MatFabButton} from "@angular/material/button"; import {MatFabButton} from "@angular/material/button";
@Component({ @Component({
selector: 'app-loading-indicator', selector: 'app-loading-indicator',
standalone: true, standalone: true,
imports: [ imports: [
DataSpinnerComponent, DataSpinnerComponent,
MatButton,
MatIcon, MatIcon,
MatFabButton MatFabButton
], ],

View File

@ -3,7 +3,6 @@ import {MatTableDataSource, MatTableModule} from "@angular/material/table";
import {MatIcon} from "@angular/material/icon"; import {MatIcon} from "@angular/material/icon";
import {DatePipe} from "@angular/common"; import {DatePipe} from "@angular/common";
import {addDays} from "@progress/kendo-date-math"; import {addDays} from "@progress/kendo-date-math";
import {MatDivider} from "@angular/material/divider";
import {DataSpinnerComponent} from "@component/common/data-spinner/data-spinner.component"; import {DataSpinnerComponent} from "@component/common/data-spinner/data-spinner.component";
import {ScheduleResponse} from "@api/v1/scheduleResponse"; import {ScheduleResponse} from "@api/v1/scheduleResponse";
@ -23,7 +22,6 @@ interface Dictionary {
MatTableModule, MatTableModule,
MatIcon, MatIcon,
DatePipe, DatePipe,
MatDivider,
DataSpinnerComponent DataSpinnerComponent
], ],
templateUrl: './table.component.html', templateUrl: './table.component.html',

View File

@ -90,7 +90,7 @@
} }
@if (((filteredGroupsSpecialist && filteredGroupsSpecialist.length > 0 && filteredGroupsBehaviour && filteredGroupsBehaviour.length > 0) || @if (((filteredGroupsSpecialist && filteredGroupsSpecialist.length > 0 && filteredGroupsBehaviour && filteredGroupsBehaviour.length > 0) ||
((!filteredGroupsSpecialist || filteredGroupsSpecialist.length === 0) && filteredGroupsBehaviour && filteredGroupsBehaviour.length > 0)) && ((!filteredGroupsSpecialist || filteredGroupsSpecialist.length === 0) && filteredGroupsBehaviour && filteredGroupsBehaviour.length > 0)) &&
filteredGroupsMagistracy && filteredGroupsMagistracy.length > 0) { filteredGroupsMagistracy && filteredGroupsMagistracy.length > 0) {
<div class="div-wrapper"> <div class="div-wrapper">
<hr/> <hr/>

View File

@ -23,7 +23,8 @@
Кабинет Кабинет
</mat-panel-title> </mat-panel-title>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<mat-chip-listbox hideSingleSelectionIndicator (change)="onLectureHallSelected($event.value)" [formControl]="formLectureHalls" #lectureChip> <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 }}

View File

@ -1,5 +1,4 @@
import {Component, EventEmitter, ViewChild} from '@angular/core'; import {Component, EventEmitter, ViewChild} from '@angular/core';
import {AsyncPipe} from "@angular/common";
import {MatAccordion, MatExpansionModule, MatExpansionPanel} from "@angular/material/expansion"; import {MatAccordion, MatExpansionModule, MatExpansionPanel} from "@angular/material/expansion";
import {MatChipListbox, MatChipsModule} from "@angular/material/chips"; import {MatChipListbox, MatChipsModule} from "@angular/material/chips";
import {catchError} from "rxjs"; import {catchError} from "rxjs";
@ -22,7 +21,6 @@ enum Enclosure {
imports: [ imports: [
MatChipsModule, MatChipsModule,
MatExpansionModule, MatExpansionModule,
AsyncPipe,
ReactiveFormsModule, ReactiveFormsModule,
MatAccordion, MatAccordion,
LoadingIndicatorComponent LoadingIndicatorComponent

View File

@ -1,11 +1,14 @@
<!--suppress CssInvalidPropertyValue --> <!--suppress CssInvalidPropertyValue -->
<button mat-button [matMenuTriggerFor]="menu" #menuTrigger="matMenuTrigger" [id]="idButton" style="margin-bottom: 10px;">{{ textButton }}</button> <button mat-button [matMenuTriggerFor]="menu" #menuTrigger="matMenuTrigger" [id]="idButton"
style="margin-bottom: 10px;">{{ textButton }}
</button>
<mat-menu #menu="matMenu" [hasBackdrop]="false" class="menu-options"> <mat-menu #menu="matMenu" [hasBackdrop]="false" class="menu-options">
<div (click)="$event.stopPropagation()" (keydown)="$event.stopPropagation()" style="padding: 0 15px 15px"> <div (click)="$event.stopPropagation()" (keydown)="$event.stopPropagation()" style="padding: 0 15px 15px">
<div class="header-menu"> <div class="header-menu">
<mat-form-field appearance="outline" color="accent" style="display:flex;"> <mat-form-field appearance="outline" color="accent" style="display:flex;">
<input matInput placeholder="Поиск..." [(ngModel)]="searchQuery" [disabled]="data === null || data.length === 0"> <input matInput placeholder="Поиск..." [(ngModel)]="searchQuery"
[disabled]="data === null || data.length === 0">
<button mat-icon-button matSuffix (click)="clearSearchQuery()" [disabled]="data === null || data.length === 0"> <button mat-icon-button matSuffix (click)="clearSearchQuery()" [disabled]="data === null || data.length === 0">
<mat-icon style="color: var(--mdc-filled-button-label-text-color);">close</mat-icon> <mat-icon style="color: var(--mdc-filled-button-label-text-color);">close</mat-icon>
</button> </button>

View File

@ -4,7 +4,6 @@ import {MatTab, MatTabGroup} from "@angular/material/tabs";
import {Observable} from "rxjs"; import {Observable} from "rxjs";
import {FormsModule, 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 {GroupComponent} from "@component/schedule/tabs/group/group.component"; import {GroupComponent} from "@component/schedule/tabs/group/group.component";
import {ProfessorComponent} from "@component/schedule/tabs/professor/professor.component"; import {ProfessorComponent} from "@component/schedule/tabs/professor/professor.component";
import {LectureHallComponent} from "@component/schedule/tabs/lecture-hall/lecture-hall.component"; import {LectureHallComponent} from "@component/schedule/tabs/lecture-hall/lecture-hall.component";
@ -15,7 +14,7 @@ import {DisciplineService} from "@api/v1/discipline.service";
import {LectureHallService} from "@api/v1/lectureHall.service"; import {LectureHallService} from "@api/v1/lectureHall.service";
import {GroupService} from "@api/v1/group.service"; import {GroupService} from "@api/v1/group.service";
import {ProfessorService} from "@api/v1/professor.service"; import {ProfessorService} from "@api/v1/professor.service";
import {AuthRoles} from "@model/AuthRoles"; import {AuthRoles} from "@model/authRoles";
import {HasRoleDirective} from "@/directives/has-role.directive"; import {HasRoleDirective} from "@/directives/has-role.directive";
import {TabSelectType, TabStorageService} from "@service/tab-storage.service"; import {TabSelectType, TabStorageService} from "@service/tab-storage.service";
import {ScheduleRequest} from "@api/v1/scheduleRequest"; import {ScheduleRequest} from "@api/v1/scheduleRequest";
@ -36,7 +35,6 @@ export enum TabsSelect {
MatTab, MatTab,
ReactiveFormsModule, ReactiveFormsModule,
MatButton, MatButton,
DataSpinnerComponent,
GroupComponent, GroupComponent,
ProfessorComponent, ProfessorComponent,
LectureHallComponent, LectureHallComponent,

View File

@ -1,6 +1,6 @@
import {Directive, Input, TemplateRef, ViewContainerRef} from '@angular/core'; import {Directive, Input, TemplateRef, ViewContainerRef} from '@angular/core';
import AuthApiService from "@api/v1/authApiService"; import AuthApiService from "@api/v1/authApiService";
import {AuthRoles} from "@model/AuthRoles"; import {AuthRoles} from "@model/authRoles";
import {catchError, of} from "rxjs"; import {catchError, of} from "rxjs";
@Directive({ @Directive({
@ -13,7 +13,8 @@ export class HasRoleDirective {
private templateRef: TemplateRef<any>, private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef, private viewContainer: ViewContainerRef,
private authService: AuthApiService private authService: AuthApiService
) {} ) {
}
@Input() set appHasRole(role: AuthRoles) { @Input() set appHasRole(role: AuthRoles) {
this.viewContainer.clear(); this.viewContainer.clear();
@ -29,6 +30,6 @@ export class HasRoleDirective {
this.viewContainer.createEmbeddedView(this.templateRef); this.viewContainer.createEmbeddedView(this.templateRef);
else else
this.viewContainer.clear(); this.viewContainer.clear();
}) });
} }
} }

View File

@ -8,10 +8,12 @@
<link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet"> <link
href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap"
rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head> </head>
<body class="mat-typography"> <body class="mat-typography">
<app-root></app-root> <app-root></app-root>
</body> </body>
</html> </html>

View File

@ -1,6 +1,6 @@
import { bootstrapApplication } from '@angular/platform-browser'; import {bootstrapApplication} from '@angular/platform-browser';
import { appConfig } from './app/app.config'; import {appConfig} from './app/app.config';
import { AppComponent } from './app/app.component'; import {AppComponent} from './app/app.component';
bootstrapApplication(AppComponent, appConfig) bootstrapApplication(AppComponent, appConfig)
.catch((err) => console.error(err)); .catch((err) => console.error(err));

View File

@ -17,7 +17,7 @@
.formLogin form { .formLogin form {
display: flex; display: flex;
flex-direction:column; flex-direction: column;
} }
.formLoginButton { .formLoginButton {

View File

@ -57,7 +57,7 @@
</form> </form>
<mat-error> <mat-error>
{{errorText}} {{ errorText }}
</mat-error> </mat-error>
<div class="formLoginButton"> <div class="formLoginButton">

View File

@ -1,4 +1,4 @@
import { Component } from '@angular/core'; import {Component} from '@angular/core';
import {MatDialogActions, MatDialogContent, MatDialogRef, MatDialogTitle} from '@angular/material/dialog'; import {MatDialogActions, MatDialogContent, MatDialogRef, MatDialogTitle} from '@angular/material/dialog';
import {MatButton} from "@angular/material/button"; import {MatButton} from "@angular/material/button";
@ -15,7 +15,8 @@ import {MatButton} from "@angular/material/button";
}) })
export class ConfirmDialogComponent { export class ConfirmDialogComponent {
constructor(public dialogRef: MatDialogRef<ConfirmDialogComponent>) { } constructor(public dialogRef: MatDialogRef<ConfirmDialogComponent>) {
}
protected onConfirm(): void { protected onConfirm(): void {
this.dialogRef.close(true); this.dialogRef.close(true);

View File

@ -1,5 +1,5 @@
export interface CacheRequest { export interface CacheRequest {
server: string; server: string;
port: number; port: number;
password?: string; password?: string;
} }

View File

@ -1,8 +1,8 @@
export interface DatabaseRequest { export interface DatabaseRequest {
server: string; server: string;
port: number; port: number;
database: string; database: string;
user: string; user: string;
ssl: boolean; ssl: boolean;
password?: string; password?: string;
} }

View File

@ -1,8 +1,8 @@
export interface EmailRequest { export interface EmailRequest {
server: string; server: string;
from: string; from: string;
password: string; password: string;
port: number; port: number;
ssl: boolean; ssl: boolean;
user: string; user: string;
} }

View File

@ -1,5 +1,5 @@
export interface LoggingRequest { export interface LoggingRequest {
enableLogToFile: boolean; enableLogToFile: boolean;
logFileName?: string; logFileName?: string;
logFilePath?: string; logFilePath?: string;
} }

View File

@ -1,4 +1,4 @@
export interface ScheduleConfigurationRequest { export interface ScheduleConfigurationRequest {
cronUpdateSchedule?: string; cronUpdateSchedule?: string;
startTerm: string; startTerm: string;
} }

View File

@ -1,5 +1,5 @@
export interface CreateUserRequest { export interface CreateUserRequest {
email: string; email: string;
username: string; username: string;
password: string; password: string;
} }

View File

@ -1,4 +1,4 @@
export interface LoginRequest { export interface LoginRequest {
username: string; username: string;
password: string; password: string;
} }

View File

@ -1,7 +1,7 @@
export interface ScheduleRequest { export interface ScheduleRequest {
groups?: Array<number>; groups?: Array<number>;
isEven?: boolean; isEven?: boolean;
disciplines?: Array<number>; disciplines?: Array<number>;
professors?: Array<number>; professors?: Array<number>;
lectureHalls?: Array<number>; lectureHalls?: Array<number>;
} }

View File

@ -1,6 +1,6 @@
import {TwoFactorAuthentication} from "@model/twoFactorAuthentication"; import {TwoFactorAuthentication} from "@model/twoFactorAuthentication";
export interface TwoFactorAuthRequest { export interface TwoFactorAuthRequest {
code: string; code: string;
method: TwoFactorAuthentication; method: TwoFactorAuthentication;
} }

View File

@ -1,7 +1,7 @@
import {OAuthProvider} from "@model/oAuthProvider"; import {OAuthProvider} from "@model/oAuthProvider";
export interface AvailableOAuthProvidersResponse { export interface AvailableOAuthProvidersResponse {
providerName: string; providerName: string;
provider: OAuthProvider; provider: OAuthProvider;
redirect: string; redirect: string;
} }

View File

@ -1,5 +1,5 @@
export interface CampusBasicInfoResponse { export interface CampusBasicInfoResponse {
id: number; id: number;
codeName: string; codeName: string;
fullName?: string; fullName?: string;
} }

View File

@ -1,6 +1,6 @@
export interface CampusDetailsResponse { export interface CampusDetailsResponse {
id: number; id: number;
codeName: string; codeName: string;
fullName?: string; fullName?: string;
address?: string; address?: string;
} }

View File

@ -1,8 +1,8 @@
import {CacheType} from "@model/cacheType"; import {CacheType} from "@model/cacheType";
export interface CacheResponse { export interface CacheResponse {
type: CacheType; type: CacheType;
server?: string; server?: string;
port: number; port: number;
password?: string; password?: string;
} }

View File

@ -1,12 +1,12 @@
import {DatabaseType} from "@model/databaseType"; import {DatabaseType} from "@model/databaseType";
export interface DatabaseResponse { export interface DatabaseResponse {
type: DatabaseType; type: DatabaseType;
server?: string; server?: string;
port: number; port: number;
database?: string; database?: string;
user?: string; user?: string;
ssl: boolean; ssl: boolean;
password?: string; password?: string;
pathToDatabase?: string; pathToDatabase?: string;
} }

View File

@ -1,4 +1,4 @@
export interface DisciplineResponse { export interface DisciplineResponse {
id: number; id: number;
name: string; name: string;
} }

View File

@ -1,4 +1,4 @@
export interface ErrorResponse { export interface ErrorResponse {
error: string; error: string;
code: number; code: number;
} }

View File

@ -1,4 +1,4 @@
export interface FacultyResponse { export interface FacultyResponse {
id: number; id: number;
name: string; name: string;
} }

View File

@ -1,7 +1,7 @@
export interface GroupDetailsResponse { export interface GroupDetailsResponse {
id: number; id: number;
name: string; name: string;
courseNumber: number; courseNumber: number;
facultyId?: number; facultyId?: number;
facultyName?: string; facultyName?: string;
} }

View File

@ -1,6 +1,6 @@
export interface GroupResponse { export interface GroupResponse {
id: number; id: number;
name: string; name: string;
courseNumber: number; courseNumber: number;
facultyId?: number; facultyId?: number;
} }

View File

@ -1,7 +1,7 @@
export interface LectureHallDetailsResponse { export interface LectureHallDetailsResponse {
id: number; id: number;
name: string; name: string;
campusId: number; campusId: number;
campusName?: string; campusName?: string;
campusCode?: string; campusCode?: string;
} }

View File

@ -1,5 +1,5 @@
export interface LectureHallResponse { export interface LectureHallResponse {
id: number; id: number;
name: string; name: string;
campusId: number; campusId: number;
} }

View File

@ -1,5 +1,5 @@
export interface ProfessorResponse { export interface ProfessorResponse {
id: number; id: number;
name: string; name: string;
altName?: string; altName?: string;
} }

View File

@ -1,21 +1,21 @@
import {DayOfWeek} from "@model/dayOfWeek"; import {DayOfWeek} from "@model/dayOfWeek";
export interface ScheduleResponse { export interface ScheduleResponse {
dayOfWeek: DayOfWeek; dayOfWeek: DayOfWeek;
pairNumber: number; pairNumber: number;
isEven: boolean; isEven: boolean;
discipline: string; discipline: string;
disciplineId: number; disciplineId: number;
isExcludedWeeks?: boolean; isExcludedWeeks?: boolean;
weeks?: Array<number>; weeks?: Array<number>;
typeOfOccupations: Array<string>; typeOfOccupations: Array<string>;
group: string; group: string;
groupId: number; groupId: number;
lectureHalls: Array<string | null>; lectureHalls: Array<string | null>;
lectureHallsId: Array<number | null>; lectureHallsId: Array<number | null>;
professors: Array<string | null>; professors: Array<string | null>;
professorsId: Array<number | null>; professorsId: Array<number | null>;
campus: Array<string | null>; campus: Array<string | null>;
campusId: Array<number | null>; campusId: Array<number | null>;
linkToMeet: Array<string | null>; linkToMeet: Array<string | null>;
} }

View File

@ -1,7 +1,7 @@
export interface PasswordPolicy { export interface PasswordPolicy {
minimumLength: number; minimumLength: number;
requireLetter: boolean; requireLetter: boolean;
requireLettersDifferentCase: boolean; requireLettersDifferentCase: boolean;
requireDigit: boolean; requireDigit: boolean;
requireSpecialCharacter: boolean; requireSpecialCharacter: boolean;
} }

View File

@ -9,10 +9,10 @@ export class TimeOnly {
if (hourOrTime instanceof Date) { if (hourOrTime instanceof Date) {
this._ticks = hourOrTime.getTime(); this._ticks = hourOrTime.getTime();
} else if (typeof hourOrTime === 'number' && minute !== undefined && second !== undefined) { } else if (typeof hourOrTime === 'number' && minute !== undefined && second !== undefined) {
this._ticks = new Date(2000, 0, 1, hourOrTime, minute, second, 0).getTime() this._ticks = new Date(2000, 0, 1, hourOrTime, minute, second, 0).getTime();
} else if (typeof hourOrTime === 'string') { } else if (typeof hourOrTime === 'string') {
const [h, m, s] = hourOrTime.split(':').map(Number); const [h, m, s] = hourOrTime.split(':').map(Number);
this._ticks = new Date(2000, 0, 1, h, m, s, 0).getTime() this._ticks = new Date(2000, 0, 1, h, m, s, 0).getTime();
} else { } else {
throw new Error('Invalid constructor arguments'); throw new Error('Invalid constructor arguments');
} }
@ -35,7 +35,7 @@ export class TimeOnly {
} }
toTimeWithoutSeconds(): string { toTimeWithoutSeconds(): string {
return `${String(this.hour).padStart(2, '0')}:${String(this.minute).padStart(2, '0')}` return `${String(this.hour).padStart(2, '0')}:${String(this.minute).padStart(2, '0')}`;
} }
toString(): string { toString(): string {

View File

@ -1,4 +1,4 @@
export interface PairPeriodTime { export interface PairPeriodTime {
start: string; start: string;
end: string; end: string;
} }

View File

@ -17,38 +17,46 @@ body {
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
} }
.toast-top-center { .toast-top-center {
top: 0; top: 0;
right: 0; right: 0;
width: 100%; width: 100%;
} }
.toast-bottom-center { .toast-bottom-center {
bottom: 0; bottom: 0;
right: 0; right: 0;
width: 100%; width: 100%;
} }
.toast-top-full-width { .toast-top-full-width {
top: 0; top: 0;
right: 0; right: 0;
width: 100%; width: 100%;
} }
.toast-bottom-full-width { .toast-bottom-full-width {
bottom: 0; bottom: 0;
right: 0; right: 0;
width: 100%; width: 100%;
} }
.toast-top-left { .toast-top-left {
top: 12px; top: 12px;
left: 12px; left: 12px;
} }
.toast-top-right { .toast-top-right {
top: 12px; top: 12px;
right: 12px; right: 12px;
} }
.toast-bottom-right { .toast-bottom-right {
right: 12px; right: 12px;
bottom: 12px; bottom: 12px;
} }
.toast-bottom-left { .toast-bottom-left {
bottom: 12px; bottom: 12px;
left: 12px; left: 12px;
@ -58,17 +66,21 @@ body {
.toast-title { .toast-title {
font-weight: bold; font-weight: bold;
} }
.toast-message { .toast-message {
word-wrap: break-word; word-wrap: break-word;
} }
.toast-message a, .toast-message a,
.toast-message label { .toast-message label {
color: #FFFFFF; color: #FFFFFF;
} }
.toast-message a:hover { .toast-message a:hover {
color: #CCCCCC; color: #CCCCCC;
text-decoration: none; text-decoration: none;
} }
.toast-close-button { .toast-close-button {
position: relative; position: relative;
right: -0.3em; right: -0.3em;
@ -80,6 +92,7 @@ body {
text-shadow: 0 1px 0 #ffffff; text-shadow: 0 1px 0 #ffffff;
/* opacity: 0.8; */ /* opacity: 0.8; */
} }
.toast-close-button:hover, .toast-close-button:hover,
.toast-close-button:focus { .toast-close-button:focus {
color: #000000; color: #000000;
@ -87,6 +100,7 @@ body {
cursor: pointer; cursor: pointer;
opacity: 0.4; opacity: 0.4;
} }
/*Additional properties for button version /*Additional properties for button version
iOS requires the button element instead of an anchor tag. iOS requires the button element instead of an anchor tag.
If you want the anchor version, it requires `href="#"`.*/ If you want the anchor version, it requires `href="#"`.*/
@ -96,14 +110,17 @@ button.toast-close-button {
background: transparent; background: transparent;
border: 0; border: 0;
} }
.toast-container { .toast-container {
pointer-events: none; pointer-events: none;
position: fixed; position: fixed;
z-index: 999999; z-index: 999999;
} }
.toast-container * { .toast-container * {
box-sizing: border-box; box-sizing: border-box;
} }
.toast-container .ngx-toastr { .toast-container .ngx-toastr {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
@ -117,55 +134,68 @@ button.toast-close-button {
box-shadow: 0 0 12px #999999; box-shadow: 0 0 12px #999999;
color: #FFFFFF; color: #FFFFFF;
} }
.toast-container .ngx-toastr:hover { .toast-container .ngx-toastr:hover {
box-shadow: 0 0 12px #000000; box-shadow: 0 0 12px #000000;
opacity: 1; opacity: 1;
cursor: pointer; cursor: pointer;
} }
/* https://github.com/FortAwesome/Font-Awesome-Pro/blob/master/advanced-options/raw-svg/regular/info-circle.svg */ /* https://github.com/FortAwesome/Font-Awesome-Pro/blob/master/advanced-options/raw-svg/regular/info-circle.svg */
.toast-info { .toast-info {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA1MTIgNTEyJyB3aWR0aD0nNTEyJyBoZWlnaHQ9JzUxMic+PHBhdGggZmlsbD0ncmdiKDI1NSwyNTUsMjU1KScgZD0nTTI1NiA4QzExOS4wNDMgOCA4IDExOS4wODMgOCAyNTZjMCAxMzYuOTk3IDExMS4wNDMgMjQ4IDI0OCAyNDhzMjQ4LTExMS4wMDMgMjQ4LTI0OEM1MDQgMTE5LjA4MyAzOTIuOTU3IDggMjU2IDh6bTAgMTEwYzIzLjE5NiAwIDQyIDE4LjgwNCA0MiA0MnMtMTguODA0IDQyLTQyIDQyLTQyLTE4LjgwNC00Mi00MiAxOC44MDQtNDIgNDItNDJ6bTU2IDI1NGMwIDYuNjI3LTUuMzczIDEyLTEyIDEyaC04OGMtNi42MjcgMC0xMi01LjM3My0xMi0xMnYtMjRjMC02LjYyNyA1LjM3My0xMiAxMi0xMmgxMnYtNjRoLTEyYy02LjYyNyAwLTEyLTUuMzczLTEyLTEydi0yNGMwLTYuNjI3IDUuMzczLTEyIDEyLTEyaDY0YzYuNjI3IDAgMTIgNS4zNzMgMTIgMTJ2MTAwaDEyYzYuNjI3IDAgMTIgNS4zNzMgMTIgMTJ2MjR6Jy8+PC9zdmc+"); background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA1MTIgNTEyJyB3aWR0aD0nNTEyJyBoZWlnaHQ9JzUxMic+PHBhdGggZmlsbD0ncmdiKDI1NSwyNTUsMjU1KScgZD0nTTI1NiA4QzExOS4wNDMgOCA4IDExOS4wODMgOCAyNTZjMCAxMzYuOTk3IDExMS4wNDMgMjQ4IDI0OCAyNDhzMjQ4LTExMS4wMDMgMjQ4LTI0OEM1MDQgMTE5LjA4MyAzOTIuOTU3IDggMjU2IDh6bTAgMTEwYzIzLjE5NiAwIDQyIDE4LjgwNCA0MiA0MnMtMTguODA0IDQyLTQyIDQyLTQyLTE4LjgwNC00Mi00MiAxOC44MDQtNDIgNDItNDJ6bTU2IDI1NGMwIDYuNjI3LTUuMzczIDEyLTEyIDEyaC04OGMtNi42MjcgMC0xMi01LjM3My0xMi0xMnYtMjRjMC02LjYyNyA1LjM3My0xMiAxMi0xMmgxMnYtNjRoLTEyYy02LjYyNyAwLTEyLTUuMzczLTEyLTEydi0yNGMwLTYuNjI3IDUuMzczLTEyIDEyLTEyaDY0YzYuNjI3IDAgMTIgNS4zNzMgMTIgMTJ2MTAwaDEyYzYuNjI3IDAgMTIgNS4zNzMgMTIgMTJ2MjR6Jy8+PC9zdmc+");
} }
/* https://github.com/FortAwesome/Font-Awesome-Pro/blob/master/advanced-options/raw-svg/regular/times-circle.svg */ /* https://github.com/FortAwesome/Font-Awesome-Pro/blob/master/advanced-options/raw-svg/regular/times-circle.svg */
.toast-error { .toast-error {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA1MTIgNTEyJyB3aWR0aD0nNTEyJyBoZWlnaHQ9JzUxMic+PHBhdGggZmlsbD0ncmdiKDI1NSwyNTUsMjU1KScgZD0nTTI1NiA4QzExOSA4IDggMTE5IDggMjU2czExMSAyNDggMjQ4IDI0OCAyNDgtMTExIDI0OC0yNDhTMzkzIDggMjU2IDh6bTEyMS42IDMxMy4xYzQuNyA0LjcgNC43IDEyLjMgMCAxN0wzMzggMzc3LjZjLTQuNyA0LjctMTIuMyA0LjctMTcgMEwyNTYgMzEybC02NS4xIDY1LjZjLTQuNyA0LjctMTIuMyA0LjctMTcgMEwxMzQuNCAzMzhjLTQuNy00LjctNC43LTEyLjMgMC0xN2w2NS42LTY1LTY1LjYtNjUuMWMtNC43LTQuNy00LjctMTIuMyAwLTE3bDM5LjYtMzkuNmM0LjctNC43IDEyLjMtNC43IDE3IDBsNjUgNjUuNyA2NS4xLTY1LjZjNC43LTQuNyAxMi4zLTQuNyAxNyAwbDM5LjYgMzkuNmM0LjcgNC43IDQuNyAxMi4zIDAgMTdMMzEyIDI1Nmw2NS42IDY1LjF6Jy8+PC9zdmc+"); background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA1MTIgNTEyJyB3aWR0aD0nNTEyJyBoZWlnaHQ9JzUxMic+PHBhdGggZmlsbD0ncmdiKDI1NSwyNTUsMjU1KScgZD0nTTI1NiA4QzExOSA4IDggMTE5IDggMjU2czExMSAyNDggMjQ4IDI0OCAyNDgtMTExIDI0OC0yNDhTMzkzIDggMjU2IDh6bTEyMS42IDMxMy4xYzQuNyA0LjcgNC43IDEyLjMgMCAxN0wzMzggMzc3LjZjLTQuNyA0LjctMTIuMyA0LjctMTcgMEwyNTYgMzEybC02NS4xIDY1LjZjLTQuNyA0LjctMTIuMyA0LjctMTcgMEwxMzQuNCAzMzhjLTQuNy00LjctNC43LTEyLjMgMC0xN2w2NS42LTY1LTY1LjYtNjUuMWMtNC43LTQuNy00LjctMTIuMyAwLTE3bDM5LjYtMzkuNmM0LjctNC43IDEyLjMtNC43IDE3IDBsNjUgNjUuNyA2NS4xLTY1LjZjNC43LTQuNyAxMi4zLTQuNyAxNyAwbDM5LjYgMzkuNmM0LjcgNC43IDQuNyAxMi4zIDAgMTdMMzEyIDI1Nmw2NS42IDY1LjF6Jy8+PC9zdmc+");
} }
/* https://github.com/FortAwesome/Font-Awesome-Pro/blob/master/advanced-options/raw-svg/regular/check.svg */ /* https://github.com/FortAwesome/Font-Awesome-Pro/blob/master/advanced-options/raw-svg/regular/check.svg */
.toast-success { .toast-success {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA1MTIgNTEyJyB3aWR0aD0nNTEyJyBoZWlnaHQ9JzUxMic+PHBhdGggZmlsbD0ncmdiKDI1NSwyNTUsMjU1KScgZD0nTTE3My44OTggNDM5LjQwNGwtMTY2LjQtMTY2LjRjLTkuOTk3LTkuOTk3LTkuOTk3LTI2LjIwNiAwLTM2LjIwNGwzNi4yMDMtMzYuMjA0YzkuOTk3LTkuOTk4IDI2LjIwNy05Ljk5OCAzNi4yMDQgMEwxOTIgMzEyLjY5IDQzMi4wOTUgNzIuNTk2YzkuOTk3LTkuOTk3IDI2LjIwNy05Ljk5NyAzNi4yMDQgMGwzNi4yMDMgMzYuMjA0YzkuOTk3IDkuOTk3IDkuOTk3IDI2LjIwNiAwIDM2LjIwNGwtMjk0LjQgMjk0LjQwMWMtOS45OTggOS45OTctMjYuMjA3IDkuOTk3LTM2LjIwNC0uMDAxeicvPjwvc3ZnPg=="); background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA1MTIgNTEyJyB3aWR0aD0nNTEyJyBoZWlnaHQ9JzUxMic+PHBhdGggZmlsbD0ncmdiKDI1NSwyNTUsMjU1KScgZD0nTTE3My44OTggNDM5LjQwNGwtMTY2LjQtMTY2LjRjLTkuOTk3LTkuOTk3LTkuOTk3LTI2LjIwNiAwLTM2LjIwNGwzNi4yMDMtMzYuMjA0YzkuOTk3LTkuOTk4IDI2LjIwNy05Ljk5OCAzNi4yMDQgMEwxOTIgMzEyLjY5IDQzMi4wOTUgNzIuNTk2YzkuOTk3LTkuOTk3IDI2LjIwNy05Ljk5NyAzNi4yMDQgMGwzNi4yMDMgMzYuMjA0YzkuOTk3IDkuOTk3IDkuOTk3IDI2LjIwNiAwIDM2LjIwNGwtMjk0LjQgMjk0LjQwMWMtOS45OTggOS45OTctMjYuMjA3IDkuOTk3LTM2LjIwNC0uMDAxeicvPjwvc3ZnPg==");
} }
/* https://github.com/FortAwesome/Font-Awesome-Pro/blob/master/advanced-options/raw-svg/regular/exclamation-triangle.svg */ /* https://github.com/FortAwesome/Font-Awesome-Pro/blob/master/advanced-options/raw-svg/regular/exclamation-triangle.svg */
.toast-warning { .toast-warning {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA1NzYgNTEyJyB3aWR0aD0nNTc2JyBoZWlnaHQ9JzUxMic+PHBhdGggZmlsbD0ncmdiKDI1NSwyNTUsMjU1KScgZD0nTTU2OS41MTcgNDQwLjAxM0M1ODcuOTc1IDQ3Mi4wMDcgNTY0LjgwNiA1MTIgNTI3Ljk0IDUxMkg0OC4wNTRjLTM2LjkzNyAwLTU5Ljk5OS00MC4wNTUtNDEuNTc3LTcxLjk4N0wyNDYuNDIzIDIzLjk4NWMxOC40NjctMzIuMDA5IDY0LjcyLTMxLjk1MSA4My4xNTQgMGwyMzkuOTQgNDE2LjAyOHpNMjg4IDM1NGMtMjUuNDA1IDAtNDYgMjAuNTk1LTQ2IDQ2czIwLjU5NSA0NiA0NiA0NiA0Ni0yMC41OTUgNDYtNDYtMjAuNTk1LTQ2LTQ2LTQ2em0tNDMuNjczLTE2NS4zNDZsNy40MTggMTM2Yy4zNDcgNi4zNjQgNS42MDkgMTEuMzQ2IDExLjk4MiAxMS4zNDZoNDguNTQ2YzYuMzczIDAgMTEuNjM1LTQuOTgyIDExLjk4Mi0xMS4zNDZsNy40MTgtMTM2Yy4zNzUtNi44NzQtNS4wOTgtMTIuNjU0LTExLjk4Mi0xMi42NTRoLTYzLjM4M2MtNi44ODQgMC0xMi4zNTYgNS43OC0xMS45ODEgMTIuNjU0eicvPjwvc3ZnPg=="); background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA1NzYgNTEyJyB3aWR0aD0nNTc2JyBoZWlnaHQ9JzUxMic+PHBhdGggZmlsbD0ncmdiKDI1NSwyNTUsMjU1KScgZD0nTTU2OS41MTcgNDQwLjAxM0M1ODcuOTc1IDQ3Mi4wMDcgNTY0LjgwNiA1MTIgNTI3Ljk0IDUxMkg0OC4wNTRjLTM2LjkzNyAwLTU5Ljk5OS00MC4wNTUtNDEuNTc3LTcxLjk4N0wyNDYuNDIzIDIzLjk4NWMxOC40NjctMzIuMDA5IDY0LjcyLTMxLjk1MSA4My4xNTQgMGwyMzkuOTQgNDE2LjAyOHpNMjg4IDM1NGMtMjUuNDA1IDAtNDYgMjAuNTk1LTQ2IDQ2czIwLjU5NSA0NiA0NiA0NiA0Ni0yMC41OTUgNDYtNDYtMjAuNTk1LTQ2LTQ2LTQ2em0tNDMuNjczLTE2NS4zNDZsNy40MTggMTM2Yy4zNDcgNi4zNjQgNS42MDkgMTEuMzQ2IDExLjk4MiAxMS4zNDZoNDguNTQ2YzYuMzczIDAgMTEuNjM1LTQuOTgyIDExLjk4Mi0xMS4zNDZsNy40MTgtMTM2Yy4zNzUtNi44NzQtNS4wOTgtMTIuNjU0LTExLjk4Mi0xMi42NTRoLTYzLjM4M2MtNi44ODQgMC0xMi4zNTYgNS43OC0xMS45ODEgMTIuNjU0eicvPjwvc3ZnPg==");
} }
.toast-container.toast-top-center .ngx-toastr, .toast-container.toast-top-center .ngx-toastr,
.toast-container.toast-bottom-center .ngx-toastr { .toast-container.toast-bottom-center .ngx-toastr {
width: 300px; width: 300px;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }
.toast-container.toast-top-full-width .ngx-toastr, .toast-container.toast-top-full-width .ngx-toastr,
.toast-container.toast-bottom-full-width .ngx-toastr { .toast-container.toast-bottom-full-width .ngx-toastr {
width: 96%; width: 96%;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }
.ngx-toastr { .ngx-toastr {
background-color: #030303; background-color: #030303;
pointer-events: auto; pointer-events: auto;
} }
.toast-success { .toast-success {
background-color: #51A351; background-color: #51A351;
} }
.toast-error { .toast-error {
background-color: #BD362F; background-color: #BD362F;
} }
.toast-info { .toast-info {
background-color: #2F96B4; background-color: #2F96B4;
} }
.toast-warning { .toast-warning {
background-color: #F89406; background-color: #F89406;
} }
.toast-progress { .toast-progress {
position: absolute; position: absolute;
left: 0; left: 0;
@ -174,27 +204,32 @@ button.toast-close-button {
background-color: #000000; background-color: #000000;
opacity: 0.4; opacity: 0.4;
} }
/* Responsive Design */ /* Responsive Design */
@media all and (max-width: 240px) { @media all and (max-width: 240px) {
.toast-container .ngx-toastr.div { .toast-container .ngx-toastr.div {
padding: 8px 8px 8px 50px; padding: 8px 8px 8px 50px;
width: 11em; width: 11em;
} }
.toast-container .toast-close-button { .toast-container .toast-close-button {
right: -0.2em; right: -0.2em;
top: -0.2em; top: -0.2em;
} }
} }
@media all and (min-width: 241px) and (max-width: 480px) { @media all and (min-width: 241px) and (max-width: 480px) {
.toast-container .ngx-toastr.div { .toast-container .ngx-toastr.div {
padding: 8px 8px 8px 50px; padding: 8px 8px 8px 50px;
width: 18em; width: 18em;
} }
.toast-container .toast-close-button { .toast-container .toast-close-button {
right: -0.2em; right: -0.2em;
top: -0.2em; top: -0.2em;
} }
} }
@media all and (min-width: 481px) and (max-width: 768px) { @media all and (min-width: 481px) and (max-width: 768px) {
.toast-container .ngx-toastr.div { .toast-container .ngx-toastr.div {
padding: 15px 15px 15px 50px; padding: 15px 15px 15px 50px;