refactor: clean code

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

@ -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;
}) })
); );
} }

@ -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()

@ -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) {

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

@ -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'
}) })

@ -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>

@ -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',

@ -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
], ],

@ -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',

@ -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/>

@ -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 }}

@ -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

@ -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>

@ -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,

@ -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();
}) });
} }
} }

@ -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>

@ -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));

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

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

@ -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);

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

@ -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;
} }

@ -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;
} }

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

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

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

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

@ -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>;
} }

@ -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;
} }

@ -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;
} }

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

@ -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;
} }

@ -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;
} }

@ -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;
} }

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

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

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

@ -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;
} }

@ -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;
} }

@ -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;
} }

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

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

@ -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>;
} }

@ -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;
} }

@ -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 {

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

@ -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;