Initial commit

This commit is contained in:
2025-03-05 21:38:10 +01:00
commit 503909d762
198 changed files with 19203 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
<form [formGroup]="form" (ngSubmit)="changePass()">
<mat-form-field appearance="outline">
<mat-label>Aktualne hasło</mat-label>
<input type="password" matInput formControlName="oldPass">
</mat-form-field><br>
<mat-form-field appearance="outline">
<mat-label>Nowe hasło</mat-label>
<input type="password" matInput formControlName="newPass">
</mat-form-field><br>
<mat-form-field appearance="outline">
<mat-label>Powtórz nowe hasło</mat-label>
<input type="password" matInput formControlName="newPassRepeat">
<mat-error *ngIf="form.errors?.['noMatch']">Hasła muszą się zgadzać</mat-error>
</mat-form-field><br>
<button mat-stroked-button>Zmień hasło</button><br>
<p *ngIf="error" style="color: red;">{{error}}</p>
</form>

View File

@@ -0,0 +1,10 @@
mat-error {
font-size: 10pt;
}
form {
margin: 1rem;
display: flex;
align-items: center;
flex-direction: column;
width: fit-content;
}

View File

@@ -0,0 +1,33 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ChangePasswordDialogComponent } from './change-password-dialog.component';
import { AuthClient } from 'src/app/services/auth.client';
import { MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { ReactiveFormsModule } from '@angular/forms';
import { MatInputModule } from '@angular/material/input';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
describe('ChangePasswordDialogComponent', () => {
let component: ChangePasswordDialogComponent;
let fixture: ComponentFixture<ChangePasswordDialogComponent>;
beforeEach(() => {
const authMock = jasmine.createSpyObj('AuthClient', ['chpass'])
TestBed.configureTestingModule({
declarations: [ChangePasswordDialogComponent],
providers: [
{provide: AuthClient, useValue: authMock},
{provide: MatDialogRef, useValue: {}}
],
imports: [MatDialogModule, MatFormFieldModule, ReactiveFormsModule, MatInputModule, BrowserAnimationsModule]
});
fixture = TestBed.createComponent(ChangePasswordDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,58 @@
import { Component } from '@angular/core';
import { AuthClient } from '../../../services/auth.client';
import { AbstractControl, FormBuilder, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators } from '@angular/forms';
import { catchError, throwError } from 'rxjs';
import { MatDialogRef } from '@angular/material/dialog';
import { Router } from '@angular/router';
import { LocalStorageService } from 'src/app/services/local-storage.service';
@Component({
selector: 'app-change-password-dialog',
templateUrl: './change-password-dialog.component.html',
styleUrls: ['./change-password-dialog.component.scss']
})
export class ChangePasswordDialogComponent {
error: string | null = null;
form: FormGroup;
constructor (private ac: AuthClient, public dr: MatDialogRef<ChangePasswordDialogComponent>, private router: Router, private ls: LocalStorageService) {
this.form = new FormGroup({
oldPass: new FormControl(),
newPass: new FormControl(),
newPassRepeat: new FormControl(),
}, {validators: [this.matchpass(), Validators.required]})
}
private matchpass() : ValidatorFn {
return (control: AbstractControl) : ValidationErrors | null => {
const newpass = control.get('newPass')
const newpassrepeat = control.get("newPassRepeat")
if (newpass?.value != newpassrepeat?.value) {
const err = {noMatch: true}
newpassrepeat?.setErrors(err)
return err
}
newpassrepeat?.setErrors(null)
return null
}
}
protected changePass() {
if (this.form.errors) {
return;
}
this.ac.chpass(this.form.get('oldPass')?.value, this.form.get('newPass')?.value).pipe(catchError((err)=>{
if (err.status == 401) {
this.error = "Niepoprawne dane"
return throwError(() => new Error(err.message))
}
this.error = "Nieznany błąd"
return throwError(() => new Error(err.message))
})).subscribe((data) => {
if (this.error == null) {
this.dr.close()
this.ls.logOut()
this.router.navigateByUrl("/login")
}
})
}
}

View File

@@ -0,0 +1,18 @@
<h1 mat-dialog-title>Czystość</h1>
<mat-dialog-content>
@if (grade) {
<h1>Twoja ocena: <span [ngStyle]="gradeColor()">{{grade}}</span></h1>
}
@else {
<h1>Nie oceniono</h1>
}
<p *ngIf="notes.length > 0">Uwagi:</p>
<ul>
<li *ngFor="let i of notes">{{i.label}}<span *ngIf="i.weight > 1"> ({{i.weight}})</span></li>
</ul>
<p>{{tips}}</p>
<app-date-selector [(date)]="day" [filter]="filter"/>
</mat-dialog-content>
<mat-dialog-actions align="end">
<button mat-icon-button mat-dialog-close><mat-icon>close</mat-icon></button>
</mat-dialog-actions>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CleanComponent } from './clean.component';
describe('CleanComponent', () => {
let component: CleanComponent;
let fixture: ComponentFixture<CleanComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CleanComponent]
})
.compileComponents();
fixture = TestBed.createComponent(CleanComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,67 @@
import { Component, OnInit } from '@angular/core';
import * as moment from 'moment';
import { weekendFilter } from 'src/app/fd.da';
import { UpdatesService } from 'src/app/services/updates.service';
import { CleanNote } from 'src/app/types/clean-note';
@Component({
selector: 'app-clean',
templateUrl: './clean.component.html',
styleUrl: './clean.component.scss'
})
export class CleanComponent implements OnInit {
private _day: moment.Moment = moment()
public get day(): moment.Moment {
return this._day;
}
public set day(value: moment.Moment) {
if (!this.filter(value)) value.isoWeekday(5);
this._day = moment.utc(value).startOf('day');
this.update()
}
grade: number | null = null
notes: CleanNote[] = []
tips: string = ""
filter = weekendFilter
constructor (private updates: UpdatesService) {
this.day = moment.utc();
}
ngOnInit(): void {
this.update()
}
update() {
this.updates.getClean(this.day).subscribe((v) => {
if (v) {
this.grade = v.grade
this.notes = v.notes
this.tips = v.tips
} else {
this.grade = null
this.notes = []
this.tips = ""
}
})
}
protected gradeColor() {
switch (this.grade) {
case 1:
return { color: "red" }
case 2:
return { color: "darkorange" }
case 3:
return { color: "orange" }
case 4:
return { color: "olive" }
case 5:
return { color: "green" }
case 6:
return { color: "springgreen" }
default:
return { color: "inherit" }
}
}
}

View File

@@ -0,0 +1,14 @@
<h1 mat-dialog-title>Klucze</h1>
<mat-dialog-content>
@for (item of keys; track $index) {
@if (item.taken) {
<div>{{item.room}}: Zajęte</div>
}
@else {
<div class="free">{{item.room}}: Wolne</div>
}
}
</mat-dialog-content>
<mat-dialog-actions align="end">
<button mat-icon-button mat-dialog-close><mat-icon>close</mat-icon></button>
</mat-dialog-actions>

View File

@@ -0,0 +1,6 @@
.free {
color: black;
@media (prefers-color-scheme: dark) {
color: white;
}
}

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { KeyComponent } from './key.component';
describe('KeyComponent', () => {
let component: KeyComponent;
let fixture: ComponentFixture<KeyComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [KeyComponent]
})
.compileComponents();
fixture = TestBed.createComponent(KeyComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,21 @@
import { Component, OnInit } from '@angular/core';
import { UpdatesService } from 'src/app/services/updates.service';
import { UKey } from 'src/app/types/key';
@Component({
selector: 'app-key',
templateUrl: './key.component.html',
styleUrl: './key.component.scss'
})
export class KeyComponent implements OnInit {
constructor (private us: UpdatesService){}
keys!: UKey[]
ngOnInit(): void {
this.us.getKeys().subscribe((v) => {
this.keys = v
})
}
}

View File

@@ -0,0 +1,7 @@
<mat-dialog-content>
Czy na pewno chcesz się wylogować?
</mat-dialog-content>
<mat-dialog-actions align="end">
<button mat-button [mat-dialog-close]="true">Tak</button>
<button mat-button mat-dialog-close>Nie</button>
</mat-dialog-actions>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LogoutConfirmationComponent } from './logout-confirmation.component';
import { MatDialogModule } from '@angular/material/dialog';
describe('LogoutConfirmationComponent', () => {
let component: LogoutConfirmationComponent;
let fixture: ComponentFixture<LogoutConfirmationComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [LogoutConfirmationComponent],
imports: [MatDialogModule]
});
fixture = TestBed.createComponent(LogoutConfirmationComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-logout-confirmation',
templateUrl: './logout-confirmation.component.html',
styleUrls: ['./logout-confirmation.component.scss']
})
export class LogoutConfirmationComponent {
}

View File

@@ -0,0 +1,33 @@
<mat-action-list>
<button mat-list-item (click)="logout()">
<mat-icon matListItemIcon color="primary">logout</mat-icon>
<div matListItemTitle>Wyloguj</div>
</button>
<button mat-list-item (click)="openPassChange()">
<mat-icon matListItemIcon color="primary">manage_accounts</mat-icon>
<div matListItemTitle>Zmiana hasła</div>
</button>
<button mat-list-item (click)="checkUpdate()">
<div matListItemIcon [ngSwitch]="checking">
<mat-spinner diameter="25" *ngSwitchCase="true"></mat-spinner>
<mat-icon *ngSwitchCase="false">update</mat-icon>
<mat-icon *ngSwitchCase="'err'" color="warn">error</mat-icon>
<mat-icon *ngSwitchCase="'aval'">upgrade</mat-icon>
</div>
<div matListItemTitle>Sprawdź dostępność aktualizacji</div>
<div matListItemLine>Aktualna wersja: {{version}}</div>
</button>
<button mat-list-item (click)="openKey()" *ngIf="ls.capCheck(32)">
<mat-icon matListItemIcon>key</mat-icon>
<div matListItemTitle>Klucze</div>
</button>
<button mat-list-item *ngIf="ls.capCheck(16) && ls.hasRoom()" (click)="openClean()">
<mat-icon matListItemIcon>cleaning_services</mat-icon>
<div matListItemTitle>Oceny za czystość</div>
</button>
<button mat-list-item (click)="goToAdmin()" *ngIf="ls.admin">
<mat-icon matListItemIcon color="accent">admin_panel_settings</mat-icon>
<div matListItemTitle>Panel administracyjny</div>
<div matListItemLine>Poprzednio Tryb edycji</div>
</button>
</mat-action-list>

View File

@@ -0,0 +1,32 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PersonalComponent } from './personal.component';
import { AuthClient } from 'src/app/services/auth.client';
import { MatDialogModule } from '@angular/material/dialog';
import { SwUpdate } from '@angular/service-worker';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatListModule } from '@angular/material/list';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
describe('PersonalComponent', () => {
let component: PersonalComponent;
let fixture: ComponentFixture<PersonalComponent>;
beforeEach(() => {
const authMock = jasmine.createSpyObj('AuthClient', ['s'])
TestBed.configureTestingModule({
declarations: [PersonalComponent],
providers: [
{provide: AuthClient, useValue: authMock},
],
imports: [MatDialogModule, MatSnackBarModule, MatListModule, BrowserAnimationsModule]
});
fixture = TestBed.createComponent(PersonalComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,64 @@
import { Component } from '@angular/core';
import { AuthClient } from '../../services/auth.client';
import { Router } from '@angular/router';
import { MatDialog } from '@angular/material/dialog';
import { ChangePasswordDialogComponent } from './change-password-dialog/change-password-dialog.component';
import { environment } from 'src/environments/environment';
import { LogoutConfirmationComponent } from './logout-confirmation/logout-confirmation.component';
import { AppUpdateService } from 'src/app/services/app-update.service';
import { LocalStorageService } from 'src/app/services/local-storage.service';
import { KeyComponent } from './key/key.component';
import { CleanComponent } from './clean/clean.component';
@Component({
selector: 'app-personal',
templateUrl: './personal.component.html',
styleUrls: ['./personal.component.scss']
})
export class PersonalComponent {
updateaval: boolean | unknown = false
checking: boolean | "err" | "aval" = false
constructor (private ac: AuthClient, private router: Router, private dialog: MatDialog, readonly update: AppUpdateService, protected ls: LocalStorageService) {}
public version: any = environment.version;
protected logout() {
let dialogRef = this.dialog.open(LogoutConfirmationComponent)
dialogRef.afterClosed().subscribe(result => {
if (result) {
this.ac.logout().subscribe(() => {
this.router.navigateByUrl("/login")
this.ls.logOut()
})
}
})
}
protected openPassChange() {
this.dialog.open(ChangePasswordDialogComponent)
}
protected openKey() {
this.dialog.open(KeyComponent)
}
protected openClean() {
this.dialog.open(CleanComponent)
}
protected goToAdmin() {
this.router.navigateByUrl("admin")
}
protected async checkUpdate() {
this.checking = true
this.update.checkForUpdate().subscribe({
next: (v) => {
this.checking = false
if (v) {
this.checking = "aval"
}
},
error: () => this.checking = "err"
})
this.ac.check()
}
}