feat: Added file formatting

This commit is contained in:
2025-06-11 11:56:39 +02:00
parent 772fc52cf6
commit a25a90c0d7
164 changed files with 4163 additions and 3242 deletions

8
.prettierignore Normal file
View File

@@ -0,0 +1,8 @@
angular.json
compose.yml
*.md
tsconfig*.json
*.html
.vscode
package*.json
ngsw-config.json

6
.prettierrc Normal file
View File

@@ -0,0 +1,6 @@
{
"arrowParens": "avoid",
"semi": false,
"trailingComma": "es5",
"bracketSpacing": true
}

17
package-lock.json generated
View File

@@ -40,6 +40,7 @@
"karma-coverage": "~2.2.0", "karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0", "karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.0.0", "karma-jasmine-html-reporter": "~2.0.0",
"prettier": "3.5.3",
"typescript": "~5.8.3" "typescript": "~5.8.3"
} }
}, },
@@ -7794,6 +7795,22 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/prettier": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
"dev": true,
"license": "MIT",
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/proc-log": { "node_modules/proc-log": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz",

View File

@@ -43,6 +43,7 @@
"karma-coverage": "~2.2.0", "karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0", "karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.0.0", "karma-jasmine-html-reporter": "~2.0.0",
"prettier": "3.5.3",
"typescript": "~5.8.3" "typescript": "~5.8.3"
} }
} }

View File

@@ -1,23 +1,23 @@
:host { :host {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
} }
mat-paginator { mat-paginator {
margin-top: auto; margin-top: auto;
} }
mat-form-field { mat-form-field {
flex-grow: 1; flex-grow: 1;
} }
#upper-bar { #upper-bar {
display: flex; display: flex;
} }
button[mat-icon-button] { button[mat-icon-button] {
margin-left: 4pt; margin-left: 4pt;
margin-right: 4pt; margin-right: 4pt;
margin-top: 4pt; margin-top: 4pt;
} }

View File

@@ -1,52 +1,50 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { AccountMgmtComponent } from './account-mgmt.component'; import { AccountMgmtComponent } from './account-mgmt.component'
import { AdminCommService } from '../admin-comm.service'; import { AdminCommService } from '../admin-comm.service'
import { MatDialogModule } from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog'
import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatSnackBarModule } from '@angular/material/snack-bar'
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field'
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon'
import { MatPaginatorModule } from '@angular/material/paginator'; import { MatPaginatorModule } from '@angular/material/paginator'
import { of } from 'rxjs'; import { of } from 'rxjs'
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table'
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input'
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'
describe('AccountMgmtComponent', () => { describe('AccountMgmtComponent', () => {
let component: AccountMgmtComponent; let component: AccountMgmtComponent
let fixture: ComponentFixture<AccountMgmtComponent>; let fixture: ComponentFixture<AccountMgmtComponent>
let acMock let acMock
beforeEach(async () => { beforeEach(async () => {
acMock = { acMock = {
accs: { accs: {
getAccs: jasmine.createSpy("getAccs").and.returnValue(of()) getAccs: jasmine.createSpy('getAccs').and.returnValue(of()),
} },
} }
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [AccountMgmtComponent], declarations: [AccountMgmtComponent],
providers: [ providers: [{ provide: AdminCommService, useValue: acMock }],
{provide: AdminCommService, useValue: acMock}
],
imports: [ imports: [
MatDialogModule, MatDialogModule,
MatSnackBarModule, MatSnackBarModule,
MatFormFieldModule, MatFormFieldModule,
MatIconModule, MatIconModule,
MatPaginatorModule, MatPaginatorModule,
MatTableModule, MatTableModule,
MatInputModule, MatInputModule,
BrowserAnimationsModule, BrowserAnimationsModule,
MatProgressSpinnerModule MatProgressSpinnerModule,
] ],
}).compileComponents(); }).compileComponents()
fixture = TestBed.createComponent(AccountMgmtComponent); fixture = TestBed.createComponent(AccountMgmtComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,49 +1,57 @@
import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core'; import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core'
import { AdminCommService } from '../admin-comm.service'; import { AdminCommService } from '../admin-comm.service'
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog'
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table'
import { MatPaginator } from '@angular/material/paginator'; import { MatPaginator } from '@angular/material/paginator'
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar'
import { UserEditComponent } from './user-edit/user-edit.component'; import { UserEditComponent } from './user-edit/user-edit.component'
import { LocalStorageService } from 'src/app/services/local-storage.service'; import { LocalStorageService } from 'src/app/services/local-storage.service'
import { Group } from 'src/app/types/group'; import { Group } from 'src/app/types/group'
import User from 'src/app/types/user'; import User from 'src/app/types/user'
@Component({ @Component({
selector: 'app-account-mgmt', selector: 'app-account-mgmt',
templateUrl: './account-mgmt.component.html', templateUrl: './account-mgmt.component.html',
styleUrls: ['./account-mgmt.component.scss'], styleUrls: ['./account-mgmt.component.scss'],
standalone: false standalone: false,
}) })
export class AccountMgmtComponent implements OnInit, AfterViewInit { export class AccountMgmtComponent implements OnInit, AfterViewInit {
protected groups: Group[] = [] protected groups: Group[] = []
users: MatTableDataSource<Omit<User, "pass">> users: MatTableDataSource<Omit<User, 'pass'>>
loading = false loading = false
@ViewChild(MatPaginator) paginator!: MatPaginator @ViewChild(MatPaginator) paginator!: MatPaginator
constructor(readonly ac:AdminCommService, private dialog: MatDialog, private sb: MatSnackBar, protected readonly ls: LocalStorageService) { constructor(
this.users = new MatTableDataSource<Omit<User, "pass">>(); readonly ac: AdminCommService,
this.users.filterPredicate = (data: Record<string, any>, filter: string): boolean => { private dialog: MatDialog,
const dataStr = Object.keys(data).reduce((curr: string, key: string) => { private sb: MatSnackBar,
if (["_id", "admin", "groups", "__v", "locked"].find(v => v == key)) { protected readonly ls: LocalStorageService
return curr + '' ) {
} this.users = new MatTableDataSource<Omit<User, 'pass'>>()
return curr + data[key] + '⫂' this.users.filterPredicate = (
}, '').toLowerCase() data: Record<string, any>,
filter: string
): boolean => {
const dataStr = Object.keys(data)
.reduce((curr: string, key: string) => {
if (['_id', 'admin', 'groups', '__v', 'locked'].find(v => v == key)) {
return curr + ''
}
return curr + data[key] + '⫂'
}, '')
.toLowerCase()
const filternew = filter.trim().toLowerCase() const filternew = filter.trim().toLowerCase()
return dataStr.indexOf(filternew) != -1 return dataStr.indexOf(filternew) != -1
} }
} }
ngAfterViewInit() { ngAfterViewInit() {
this.users.paginator = this.paginator this.users.paginator = this.paginator
} }
ngOnInit() { ngOnInit() {
this.loading = true this.loading = true
this.ac.accs.getAccs().subscribe((data)=>{ this.ac.accs.getAccs().subscribe(data => {
this.loading = false this.loading = false
this.users.data = data.users this.users.data = data.users
this.groups = data.groups this.groups = data.groups
@@ -56,9 +64,18 @@ export class AccountMgmtComponent implements OnInit, AfterViewInit {
} }
openUserCard(id?: string) { openUserCard(id?: string) {
this.dialog.open<UserEditComponent, UserEditComponent.InputData, UserEditComponent.ReturnData>(UserEditComponent, {data: {id: id, type: id ? "edit" : "new", groups: this.groups}}).afterClosed().subscribe(r => { this.dialog
if (r) this.ngOnInit() .open<
}) UserEditComponent,
UserEditComponent.InputData,
UserEditComponent.ReturnData
>(UserEditComponent, {
data: { id: id, type: id ? 'edit' : 'new', groups: this.groups },
})
.afterClosed()
.subscribe(r => {
if (r) this.ngOnInit()
})
} }
collumns = ['name', 'surname', 'uname', 'actions'] collumns = ['name', 'surname', 'uname', 'actions']

View File

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

View File

@@ -1,11 +1,9 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core'
@Component({ @Component({
selector: 'app-user-delete', selector: 'app-user-delete',
templateUrl: './user-delete.component.html', templateUrl: './user-delete.component.html',
styleUrls: ['./user-delete.component.scss'], styleUrls: ['./user-delete.component.scss'],
standalone: false standalone: false,
}) })
export class UserDeleteComponent { export class UserDeleteComponent {}
}

View File

@@ -1,32 +1,32 @@
:host { :host {
padding: 8pt; padding: 8pt;
display: block; display: block;
} }
form { form {
margin-top: 1ch !important; margin-top: 1ch !important;
display: flex; display: flex;
grid-auto-flow: column; grid-auto-flow: column;
flex-direction: row; flex-direction: row;
flex-wrap: wrap; flex-wrap: wrap;
align-items: center;
justify-content: center;
column-gap: 3ch;
div {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: repeat(5, 1fr);
align-items: center; align-items: center;
justify-content: center; button {
column-gap: 3ch; align-self: stretch;
div { justify-self: stretch;
display: grid; height: auto;
grid-template-columns: 1fr; margin-bottom: 1lh;
grid-template-rows: repeat(5, 1fr);
align-items: center;
button {
align-self: stretch;
justify-self: stretch;
height: auto;
margin-bottom: 1lh;
}
} }
}
} }
mat-dialog-actions { mat-dialog-actions {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
} }

View File

@@ -1,24 +1,26 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { UserEditComponent } from './user-edit.component'; import { UserEditComponent } from './user-edit.component'
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import {
import { MatFormFieldModule } from '@angular/material/form-field'; MAT_DIALOG_DATA,
import { NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms'; MatDialogModule,
import { MatInputModule } from '@angular/material/input'; MatDialogRef,
import { NoopAnimationsModule } from '@angular/platform-browser/animations'; } from '@angular/material/dialog'
import { AdminCommService } from '../../admin-comm.service'; import { MatFormFieldModule } from '@angular/material/form-field'
import { forwardRef } from '@angular/core'; import { NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms'
import { MatSelectModule } from '@angular/material/select'; import { MatInputModule } from '@angular/material/input'
import { NoopAnimationsModule } from '@angular/platform-browser/animations'
import { AdminCommService } from '../../admin-comm.service'
import { forwardRef } from '@angular/core'
import { MatSelectModule } from '@angular/material/select'
describe('UserEditComponent', () => { describe('UserEditComponent', () => {
let component: UserEditComponent; let component: UserEditComponent
let fixture: ComponentFixture<UserEditComponent>; let fixture: ComponentFixture<UserEditComponent>
let acMock let acMock
beforeEach(async () => { beforeEach(async () => {
acMock = { acMock = {}
}
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [UserEditComponent], declarations: [UserEditComponent],
imports: [ imports: [
@@ -27,20 +29,20 @@ describe('UserEditComponent', () => {
ReactiveFormsModule, ReactiveFormsModule,
MatInputModule, MatInputModule,
NoopAnimationsModule, NoopAnimationsModule,
MatSelectModule MatSelectModule,
], ],
providers: [ providers: [
{ provide: MatDialogRef, useValue: {} }, { provide: MatDialogRef, useValue: {} },
{ provide: MAT_DIALOG_DATA, useValue: { groups: [] } }, { provide: MAT_DIALOG_DATA, useValue: { groups: [] } },
{ provide: AdminCommService, useValue: acMock }, { provide: AdminCommService, useValue: acMock },
] ],
}).compileComponents(); }).compileComponents()
fixture = TestBed.createComponent(UserEditComponent); fixture = TestBed.createComponent(UserEditComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,53 +1,57 @@
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core'
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; import {
import { FormControl, FormGroup } from '@angular/forms'; MAT_DIALOG_DATA,
import { LocalStorageService } from 'src/app/services/local-storage.service'; MatDialog,
import { Group } from 'src/app/types/group'; MatDialogRef,
import { AdminCommService } from '../../admin-comm.service'; } from '@angular/material/dialog'
import { UserDeleteComponent } from '../user-delete/user-delete.component'; import { FormControl, FormGroup } from '@angular/forms'
import { MatSnackBar } from '@angular/material/snack-bar'; import { LocalStorageService } from 'src/app/services/local-storage.service'
import { UserResetComponent } from '../user-reset/user-reset.component'; import { Group } from 'src/app/types/group'
import { catchError, throwError } from 'rxjs'; import { AdminCommService } from '../../admin-comm.service'
import { DateTime } from 'luxon'; import { UserDeleteComponent } from '../user-delete/user-delete.component'
import { MatSnackBar } from '@angular/material/snack-bar'
import { UserResetComponent } from '../user-reset/user-reset.component'
import { catchError, throwError } from 'rxjs'
import { DateTime } from 'luxon'
export namespace UserEditComponent { export namespace UserEditComponent {
export type InputData = {type: "new" | "edit", id?: string, groups: Group[]} export type InputData = { type: 'new' | 'edit'; id?: string; groups: Group[] }
export type ReturnData = true | undefined export type ReturnData = true | undefined
} }
@Component({ @Component({
selector: 'app-user-edit', selector: 'app-user-edit',
templateUrl: './user-edit.component.html', templateUrl: './user-edit.component.html',
styleUrls: ['./user-edit.component.scss'], styleUrls: ['./user-edit.component.scss'],
standalone: false standalone: false,
}) })
export class UserEditComponent { export class UserEditComponent {
lockout = false; lockout = false
locked = false; locked = false
loading = false; loading = false
form: FormGroup = new FormGroup({ form: FormGroup = new FormGroup({
fname: new FormControl<string>(""), fname: new FormControl<string>(''),
surname: new FormControl<string>(""), surname: new FormControl<string>(''),
room: new FormControl<string>(""), room: new FormControl<string>(''),
uname: new FormControl<string>(""), uname: new FormControl<string>(''),
groups: new FormControl<Array<string>>([]), groups: new FormControl<Array<string>>([]),
flags: new FormControl<Array<number>>([]), flags: new FormControl<Array<number>>([]),
}) })
groups: Group[] groups: Group[]
id?: string id?: string
regDate?: DateTime; regDate?: DateTime
constructor ( constructor(
public dialogRef: MatDialogRef<UserEditComponent>, public dialogRef: MatDialogRef<UserEditComponent>,
@Inject(MAT_DIALOG_DATA) public data: UserEditComponent.InputData, @Inject(MAT_DIALOG_DATA) public data: UserEditComponent.InputData,
readonly ls: LocalStorageService, readonly ls: LocalStorageService,
readonly acu: AdminCommService, readonly acu: AdminCommService,
private dialog: MatDialog, private dialog: MatDialog,
private sb: MatSnackBar private sb: MatSnackBar
) { ) {
this.groups = data.groups this.groups = data.groups
if (data.type == "edit") { if (data.type == 'edit') {
this.id = data.id this.id = data.id
this.acu.accs.getUser(data.id!).subscribe((r) => { this.acu.accs.getUser(data.id!).subscribe(r => {
this.regDate = DateTime.fromISO(r.regDate) this.regDate = DateTime.fromISO(r.regDate)
var flags: Array<number> = [] var flags: Array<number> = []
if (r.admin) { if (r.admin) {
@@ -62,64 +66,83 @@ export class UserEditComponent {
} }
this.locked = r.locked ? true : false this.locked = r.locked ? true : false
this.lockout = r.lockout this.lockout = r.lockout
this.form.get("fname")?.setValue(r.fname) this.form.get('fname')?.setValue(r.fname)
this.form.get("surname")?.setValue(r.surname) this.form.get('surname')?.setValue(r.surname)
this.form.get("room")?.setValue(r.room) this.form.get('room')?.setValue(r.room)
this.form.get("uname")?.setValue(r.uname) this.form.get('uname')?.setValue(r.uname)
this.form.get("groups")?.setValue(r.groups) this.form.get('groups')?.setValue(r.groups)
this.form.get("flags")?.setValue(flags) this.form.get('flags')?.setValue(flags)
}) })
} }
} }
protected submit() { protected submit() {
this.loading = true this.loading = true
if (this.data.type == "edit") { if (this.data.type == 'edit') {
this.acu.accs.putAcc(this.id!, this.getForm()).pipe(catchError((err)=>{ this.acu.accs
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.") .putAcc(this.id!, this.getForm())
return throwError(()=> new Error(err.message)) .pipe(
})).subscribe((data)=> { catchError(err => {
if (data.status == 200) { this.sb.open('Wystąpił błąd. Skontaktuj się z obsługą programu.')
this.sb.open("Użytkownik został zmodyfikowany.", undefined, {duration: 2500}) return throwError(() => new Error(err.message))
this.dialogRef.close(true) })
} else { )
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.") .subscribe(data => {
this.loading = false if (data.status == 200) {
} this.sb.open('Użytkownik został zmodyfikowany.', undefined, {
}) duration: 2500,
})
this.dialogRef.close(true)
} else {
this.sb.open('Wystąpił błąd. Skontaktuj się z obsługą programu.')
this.loading = false
}
})
} else { } else {
this.acu.accs.postAcc(this.getForm()).pipe(catchError((err)=>{ this.acu.accs
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.") .postAcc(this.getForm())
return throwError(()=> new Error(err.message)) .pipe(
})).subscribe((data)=> { catchError(err => {
if (data.status == 201) { this.sb.open('Wystąpił błąd. Skontaktuj się z obsługą programu.')
this.sb.open("Użytkownik został utworzony.", undefined, {duration: 2500}) return throwError(() => new Error(err.message))
this.dialogRef.close(true) })
} else { )
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.") .subscribe(data => {
this.loading = false if (data.status == 201) {
} this.sb.open('Użytkownik został utworzony.', undefined, {
}) duration: 2500,
})
this.dialogRef.close(true)
} else {
this.sb.open('Wystąpił błąd. Skontaktuj się z obsługą programu.')
this.loading = false
}
})
} }
} }
protected disableLockout() { protected disableLockout() {
this.loading = true this.loading = true
this.acu.accs.clearLockout(this.id!).pipe(catchError((err)=>{ this.acu.accs
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.") .clearLockout(this.id!)
return throwError(()=> new Error(err.message)) .pipe(
})).subscribe((s) => { catchError(err => {
if (s.status == 200) { this.sb.open('Wystąpił błąd. Skontaktuj się z obsługą programu.')
this.loading = false return throwError(() => new Error(err.message))
this.lockout = false })
} else { )
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.") .subscribe(s => {
this.loading = false if (s.status == 200) {
} this.loading = false
}) this.lockout = false
} else {
this.sb.open('Wystąpił błąd. Skontaktuj się z obsługą programu.')
this.loading = false
}
})
} }
protected getForm() { protected getForm() {
return { return {
fname: this.form.get('fname')?.value, fname: this.form.get('fname')?.value,
surname: this.form.get('surname')?.value, surname: this.form.get('surname')?.value,
@@ -127,48 +150,60 @@ export class UserEditComponent {
uname: this.form.get('uname')?.value, uname: this.form.get('uname')?.value,
groups: this.form.get('groups')?.value, groups: this.form.get('groups')?.value,
flags: (() => { flags: (() => {
var value = this.form.get('flags')?.value.reduce((a: number,b: number)=>a+b,0) var value = this.form
.get('flags')
?.value.reduce((a: number, b: number) => a + b, 0)
if (this.ls.capCheck(32)) { if (this.ls.capCheck(32)) {
return value return value
} else { } else {
return undefined return undefined
} }
})() })(),
} }
} }
protected delete() { protected delete() {
this.dialog.open(UserDeleteComponent).afterClosed().subscribe(reply => { this.dialog
if (reply) { .open(UserDeleteComponent)
this.acu.accs.deleteAcc(this.id!).subscribe((res) => { .afterClosed()
if (res.status == 200) { .subscribe(reply => {
this.sb.open("Użytkownik został usunięty.", undefined, {duration: 2500}) if (reply) {
this.dialogRef.close() this.acu.accs.deleteAcc(this.id!).subscribe(res => {
} else { if (res.status == 200) {
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.") this.sb.open('Użytkownik został usunięty.', undefined, {
console.error(res); duration: 2500,
} })
}) this.dialogRef.close()
} } else {
}) this.sb.open('Wystąpił błąd. Skontaktuj się z obsługą programu.')
console.error(res)
}
})
}
})
} }
protected resetPass() { protected resetPass() {
this.loading = true this.loading = true
this.dialog.open(UserResetComponent).afterClosed().subscribe((res) => { this.dialog
if (res == true) { .open(UserResetComponent)
this.acu.accs.resetPass(this.id!).subscribe((patch)=>{ .afterClosed()
if (patch.status == 200) { .subscribe(res => {
this.sb.open("Hasło zostało zresetowane", undefined, {duration: 2500}) if (res == true) {
this.loading = false this.acu.accs.resetPass(this.id!).subscribe(patch => {
} if (patch.status == 200) {
}) this.sb.open('Hasło zostało zresetowane', undefined, {
} duration: 2500,
}) })
this.loading = false
}
})
}
})
} }
protected toggleLock(state: boolean) { protected toggleLock(state: boolean) {
this.acu.accs.putAcc(this.id!, {locked: state}).subscribe((res) => { this.acu.accs.putAcc(this.id!, { locked: state }).subscribe(res => {
if (res.status == 200) { if (res.status == 200) {
this.locked = state this.locked = state
} }

View File

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

View File

@@ -1,11 +1,9 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core'
@Component({ @Component({
selector: 'app-user-reset', selector: 'app-user-reset',
templateUrl: './user-reset.component.html', templateUrl: './user-reset.component.html',
styleUrls: ['./user-reset.component.scss'], styleUrls: ['./user-reset.component.scss'],
standalone: false standalone: false,
}) })
export class UserResetComponent { export class UserResetComponent {}
}

View File

@@ -1,25 +1,35 @@
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing'
import { AdminCommService } from './admin-comm.service'; import { AdminCommService } from './admin-comm.service'
import { HttpClient, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; import {
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; HttpClient,
provideHttpClient,
withInterceptorsFromDi,
} from '@angular/common/http'
import {
HttpTestingController,
provideHttpClientTesting,
} from '@angular/common/http/testing'
describe('AdminCommService', () => { describe('AdminCommService', () => {
let service: AdminCommService; let service: AdminCommService
let httpClient: HttpClient let httpClient: HttpClient
let httpTestingController: HttpTestingController let httpTestingController: HttpTestingController
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [], imports: [],
providers: [provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting()] providers: [
}); provideHttpClient(withInterceptorsFromDi()),
service = TestBed.inject(AdminCommService); provideHttpClientTesting(),
httpClient = TestBed.inject(HttpClient); ],
httpTestingController = TestBed.inject(HttpTestingController); })
}); service = TestBed.inject(AdminCommService)
httpClient = TestBed.inject(HttpClient)
httpTestingController = TestBed.inject(HttpTestingController)
})
it('should be created', () => { it('should be created', () => {
expect(service).toBeTruthy(); expect(service).toBeTruthy()
}); })
}); })

View File

@@ -1,285 +1,468 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http'
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core'
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment'
import { Menu } from '../types/menu'; import { Menu } from '../types/menu'
import { Status } from '../types/status'; import { Status } from '../types/status'
import { Group } from '../types/group'; import { Group } from '../types/group'
import { map } from 'rxjs'; import { map } from 'rxjs'
import { Notification } from '../types/notification'; import { Notification } from '../types/notification'
import { News } from '../types/news'; import { News } from '../types/news'
import { AKey } from '../types/key'; import { AKey } from '../types/key'
import { IUSettings } from './settings/settings.component'; import { IUSettings } from './settings/settings.component'
import User from '../types/user'; import User from '../types/user'
import { DateTime } from 'luxon'; import { DateTime } from 'luxon'
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root',
}) })
export class AdminCommService { export class AdminCommService {
constructor(private http: HttpClient) {}
constructor(private http: HttpClient) { }
//#region Menu //#region Menu
menu = { menu = {
getMenu: (start?: DateTime | null, end?: DateTime | null) => { getMenu: (start?: DateTime | null, end?: DateTime | null) => {
if (start && end) { if (start && end) {
const body = {start: start.toString(), end: end.toString()} const body = { start: start.toString(), end: end.toString() }
return this.http.get<(Omit<Menu, "day"> & {day: string})[]>(environment.apiEndpoint+"/admin/menu", {withCredentials: true, params: body}) return this.http.get<(Omit<Menu, 'day'> & { day: string })[]>(
environment.apiEndpoint + '/admin/menu',
{ withCredentials: true, params: body }
)
} }
return return
}, },
getOpts: () => { getOpts: () => {
return this.http.get<any>(environment.apiEndpoint+`/admin/menu/opts`, {withCredentials: true}) return this.http.get<any>(environment.apiEndpoint + `/admin/menu/opts`, {
withCredentials: true,
})
}, },
postMenu: (file: File) => { postMenu: (file: File) => {
if (file) { if (file) {
const formData = new FormData(); const formData = new FormData()
formData.append("menu", file) formData.append('menu', file)
return this.http.post<Status>(environment.apiEndpoint+"/admin/menu/upload", formData, {withCredentials: true}) return this.http.post<Status>(
environment.apiEndpoint + '/admin/menu/upload',
formData,
{ withCredentials: true }
)
} }
return return
}, },
editSn: (id: string, content: Menu['sn']) => { editSn: (id: string, content: Menu['sn']) => {
return this.putMenu(id, {sn: content}) return this.putMenu(id, { sn: content })
}, },
editOb: (id: string, content: Menu['ob']) => { editOb: (id: string, content: Menu['ob']) => {
return this.putMenu(id, {ob: content}) return this.putMenu(id, { ob: content })
}, },
editKol: (id: string, content: Menu['kol']) => { editKol: (id: string, content: Menu['kol']) => {
return this.putMenu(id, {kol: content}) return this.putMenu(id, { kol: content })
}, },
editTitle: (id: string, content: Menu['dayTitle']) => { editTitle: (id: string, content: Menu['dayTitle']) => {
return this.putMenu(id, {dayTitle: content}) return this.putMenu(id, { dayTitle: content })
}, },
print: (start?: DateTime | null, end?: DateTime | null) => { print: (start?: DateTime | null, end?: DateTime | null) => {
if (start && end) { if (start && end) {
const body = {start: start.toString(), end: end.toString()} const body = { start: start.toString(), end: end.toString() }
return this.http.get(environment.apiEndpoint+"/admin/menu/print", {withCredentials: true, params: body, responseType: "text"}) return this.http.get(environment.apiEndpoint + '/admin/menu/print', {
withCredentials: true,
params: body,
responseType: 'text',
})
} }
return return
}, },
stat: (day: DateTime, m: "ob" | "kol") => { stat: (day: DateTime, m: 'ob' | 'kol') => {
return this.http.get<{y: number, n: number}>(environment.apiEndpoint+`/admin/menu/${day.toISO()}/votes/${m}`, {withCredentials: true}) return this.http.get<{ y: number; n: number }>(
environment.apiEndpoint + `/admin/menu/${day.toISO()}/votes/${m}`,
{ withCredentials: true }
)
}, },
new: { new: {
single: (day: DateTime) => { single: (day: DateTime) => {
return this.http.post<Status>(environment.apiEndpoint+`/admin/menu/${day.toISO()}`, null, {withCredentials: true}) return this.http.post<Status>(
environment.apiEndpoint + `/admin/menu/${day.toISO()}`,
null,
{ withCredentials: true }
)
}, },
range: (start: DateTime, count: number) => { range: (start: DateTime, count: number) => {
return this.http.post<Status>(environment.apiEndpoint+`/admin/menu/${start.toISO()}/${count}/`, null, {withCredentials: true}) return this.http.post<Status>(
} environment.apiEndpoint + `/admin/menu/${start.toISO()}/${count}/`,
null,
{ withCredentials: true }
)
},
}, },
rm: (id: string) => { rm: (id: string) => {
return this.http.delete<Status>(environment.apiEndpoint+`/admin/menu/${id}`, {withCredentials: true}) return this.http.delete<Status>(
} environment.apiEndpoint + `/admin/menu/${id}`,
{ withCredentials: true }
)
},
} }
private putMenu(id: string, update: Partial<Menu>) { private putMenu(id: string, update: Partial<Menu>) {
return this.http.put<Status>(environment.apiEndpoint+`/admin/menu/${id}`, update, {withCredentials: true}) return this.http.put<Status>(
environment.apiEndpoint + `/admin/menu/${id}`,
update,
{ withCredentials: true }
)
} }
//#endregion //#endregion
//#region News //#region News
news = { news = {
getNews: () => { getNews: () => {
return this.http.get<News[]>(environment.apiEndpoint+`/admin/news`, {withCredentials: true}) return this.http.get<News[]>(environment.apiEndpoint + `/admin/news`, {
withCredentials: true,
})
}, },
postNews: (title: string, content: string) => { postNews: (title: string, content: string) => {
return this.http.post<any>(environment.apiEndpoint+`/admin/news`, {title: title, content: content}, {withCredentials: true}) return this.http.post<any>(
environment.apiEndpoint + `/admin/news`,
{ title: title, content: content },
{ withCredentials: true }
)
}, },
deleteNews: (id: string) => { deleteNews: (id: string) => {
return this.http.delete<any>(environment.apiEndpoint+`/admin/news/${id}`, {withCredentials: true}) return this.http.delete<any>(
environment.apiEndpoint + `/admin/news/${id}`,
{ withCredentials: true }
)
}, },
toggleNews: (id: string, inverter: boolean) => { toggleNews: (id: string, inverter: boolean) => {
return this.putNews(id,{visible: !inverter}) return this.putNews(id, { visible: !inverter })
}, },
togglePin: (id: string, inverter: boolean) => { togglePin: (id: string, inverter: boolean) => {
return this.putNews(id,{pinned: !inverter}) return this.putNews(id, { pinned: !inverter })
}, },
updateNews: (id: string, title: string, content: string) => { updateNews: (id: string, title: string, content: string) => {
return this.putNews(id,{title: title, content: content, date: Date.now}) return this.putNews(id, {
} title: title,
content: content,
date: Date.now,
})
},
} }
private putNews(id: string, update: object) { private putNews(id: string, update: object) {
return this.http.put<any>(environment.apiEndpoint+`/admin/news/${id}`, update, {withCredentials: true}) return this.http.put<any>(
environment.apiEndpoint + `/admin/news/${id}`,
update,
{ withCredentials: true }
)
} }
//#endregion //#endregion
//#region amgmt //#region amgmt
accs = { accs = {
getAccs: () => { getAccs: () => {
return this.http.get<{ return this.http.get<{
users: Omit<User, "pass">[], users: Omit<User, 'pass'>[]
groups: Group[] groups: Group[]
}>(environment.apiEndpoint+`/admin/accs`, {withCredentials: true}) }>(environment.apiEndpoint + `/admin/accs`, { withCredentials: true })
}, },
postAcc: (item: any) => { postAcc: (item: any) => {
return this.http.post<Status>(environment.apiEndpoint+`/admin/accs`, item, {withCredentials: true}) return this.http.post<Status>(
environment.apiEndpoint + `/admin/accs`,
item,
{ withCredentials: true }
)
}, },
putAcc: (id: string, update: Partial<User>) => { putAcc: (id: string, update: Partial<User>) => {
return this.http.put<Status>(environment.apiEndpoint+`/admin/accs/${id}`, update, {withCredentials: true}) return this.http.put<Status>(
environment.apiEndpoint + `/admin/accs/${id}`,
update,
{ withCredentials: true }
)
}, },
resetPass: (id: string) => { resetPass: (id: string) => {
return this.http.patch<Status>(environment.apiEndpoint+`/admin/accs/${id}/reset`, {}, {withCredentials: true}) return this.http.patch<Status>(
environment.apiEndpoint + `/admin/accs/${id}/reset`,
{},
{ withCredentials: true }
)
}, },
deleteAcc: (id: string) => { deleteAcc: (id: string) => {
return this.http.delete<Status>(environment.apiEndpoint+`/admin/accs/${id}`, {withCredentials: true}) return this.http.delete<Status>(
environment.apiEndpoint + `/admin/accs/${id}`,
{ withCredentials: true }
)
}, },
getUser: (id: string) => { getUser: (id: string) => {
return this.http.get<Omit<User, "pass" | "regDate"> & {lockout: boolean, regDate: string}>(environment.apiEndpoint+`/admin/accs/${id}`, {withCredentials: true}) return this.http.get<
Omit<User, 'pass' | 'regDate'> & { lockout: boolean; regDate: string }
>(environment.apiEndpoint + `/admin/accs/${id}`, {
withCredentials: true,
})
}, },
clearLockout: (id: string) => { clearLockout: (id: string) => {
return this.http.delete<Status>(environment.apiEndpoint+`/admin/accs/${id}/lockout`, {withCredentials: true}) return this.http.delete<Status>(
} environment.apiEndpoint + `/admin/accs/${id}/lockout`,
{ withCredentials: true }
)
},
} }
//#endregion //#endregion
//#region Groups //#region Groups
groups = { groups = {
getGroups: () => { getGroups: () => {
return this.http.get<Group[]>(environment.apiEndpoint+`/admin/groups`, {withCredentials: true}) return this.http.get<Group[]>(environment.apiEndpoint + `/admin/groups`, {
withCredentials: true,
})
}, },
newGroup: (name: string) => { newGroup: (name: string) => {
return this.http.post<Status>(environment.apiEndpoint+`/admin/groups`, {name: name}, {withCredentials: true}) return this.http.post<Status>(
environment.apiEndpoint + `/admin/groups`,
{ name: name },
{ withCredentials: true }
)
}, },
editName: (id: string, name: string) => { editName: (id: string, name: string) => {
return this.putGroups(id, {name: name.trim()}) return this.putGroups(id, { name: name.trim() })
}, },
remove: (id: string) => { remove: (id: string) => {
return this.http.delete<Status>(environment.apiEndpoint+`/admin/groups/${id}`, {withCredentials: true}) return this.http.delete<Status>(
} environment.apiEndpoint + `/admin/groups/${id}`,
{ withCredentials: true }
)
},
} }
private putGroups(id: string, update: Partial<Group>) { private putGroups(id: string, update: Partial<Group>) {
return this.http.put<Status>(environment.apiEndpoint+`/admin/groups/${id}`, update, {withCredentials: true}) return this.http.put<Status>(
environment.apiEndpoint + `/admin/groups/${id}`,
update,
{ withCredentials: true }
)
} }
//#endregion //#endregion
//#region Notif //#region Notif
notif = { notif = {
send: (n: Notification) => { send: (n: Notification) => {
return this.http.post<{sent: number, possible: number}>(environment.apiEndpoint+"/admin/notif/send", n, {withCredentials: true}) return this.http.post<{ sent: number; possible: number }>(
environment.apiEndpoint + '/admin/notif/send',
n,
{ withCredentials: true }
)
}, },
getGroups: () => { getGroups: () => {
return this.http.get<Group[]>(environment.apiEndpoint+"/admin/notif/groups", {withCredentials: true}) return this.http.get<Group[]>(
environment.apiEndpoint + '/admin/notif/groups',
{ withCredentials: true }
)
}, },
outbox: { outbox: {
getSent: () => { getSent: () => {
return this.http.get<{_id: string, sentDate: string, title: string}[]>(environment.apiEndpoint+"/admin/notif/outbox", {withCredentials: true}).pipe(map(v => v.map(i => ({ return this.http
...i, .get<
sentDate: DateTime.fromISO(i.sentDate) { _id: string; sentDate: string; title: string }[]
})))) >(environment.apiEndpoint + '/admin/notif/outbox', { withCredentials: true })
.pipe(
map(v =>
v.map(i => ({
...i,
sentDate: DateTime.fromISO(i.sentDate),
}))
)
)
}, },
getBody: (id: string) => { getBody: (id: string) => {
return this.http.get(environment.apiEndpoint+`/admin/notif/outbox/${id}/message`, {withCredentials: true, responseType: "text"}) return this.http.get(
environment.apiEndpoint + `/admin/notif/outbox/${id}/message`,
{ withCredentials: true, responseType: 'text' }
)
}, },
getRcpts: (id: string) => { getRcpts: (id: string) => {
return this.http.get<{_id: string, uname: string, room?: string, fname?: string, surname?: string}[]>(environment.apiEndpoint+`/admin/notif/outbox/${id}/rcpts`, {withCredentials: true}) return this.http.get<
} {
} _id: string
uname: string
room?: string
fname?: string
surname?: string
}[]
>(environment.apiEndpoint + `/admin/notif/outbox/${id}/rcpts`, {
withCredentials: true,
})
},
},
} }
//#endregion //#endregion
//#region Keys //#region Keys
keys = { keys = {
getKeys: () => { getKeys: () => {
return this.http.get<(Omit<AKey, "borrow" | "tb"> & {borrow: string, tb?: string})[]>(environment.apiEndpoint+`/admin/keys`, {withCredentials: true}).pipe(map((v) => { return this.http
return v.map((r) => { .get<
let newkey: any = {...r} (Omit<AKey, 'borrow' | 'tb'> & { borrow: string; tb?: string })[]
newkey.borrow = DateTime.fromISO(r.borrow!) >(environment.apiEndpoint + `/admin/keys`, { withCredentials: true })
if (newkey.tb) newkey.tb = DateTime.fromISO(r.tb!) .pipe(
return newkey as AKey map(v => {
}) return v.map(r => {
})) let newkey: any = { ...r }
newkey.borrow = DateTime.fromISO(r.borrow!)
if (newkey.tb) newkey.tb = DateTime.fromISO(r.tb!)
return newkey as AKey
})
})
)
}, },
avalKeys: () => { avalKeys: () => {
return this.http.get<string[]>(environment.apiEndpoint+`/admin/keys/available`, {withCredentials: true}) return this.http.get<string[]>(
environment.apiEndpoint + `/admin/keys/available`,
{ withCredentials: true }
)
}, },
postKey: (room: string, uname: string) => { postKey: (room: string, uname: string) => {
return this.http.post<Status>(environment.apiEndpoint+`/admin/keys/`, {room: room, whom: uname}, {withCredentials: true}) return this.http.post<Status>(
environment.apiEndpoint + `/admin/keys/`,
{ room: room, whom: uname },
{ withCredentials: true }
)
}, },
returnKey: (id: string) => { returnKey: (id: string) => {
return this.putKeys(id, {tb: DateTime.now()}) return this.putKeys(id, { tb: DateTime.now() })
} },
} }
private putKeys(id: string, update: Partial<AKey>) { private putKeys(id: string, update: Partial<AKey>) {
return this.http.put<Status>(environment.apiEndpoint+`/admin/keys/${id}`, update, {withCredentials: true}) return this.http.put<Status>(
environment.apiEndpoint + `/admin/keys/${id}`,
update,
{ withCredentials: true }
)
} }
//#endregion //#endregion
//#region Clean //#region Clean
clean = { clean = {
getConfig: () => { getConfig: () => {
return this.http.get<{rooms: string[], things: string[]}>(environment.apiEndpoint+`/admin/clean/config`, {withCredentials: true}) return this.http.get<{ rooms: string[]; things: string[] }>(
environment.apiEndpoint + `/admin/clean/config`,
{ withCredentials: true }
)
}, },
getClean: (date: string, room: string) => { getClean: (date: string, room: string) => {
return this.http.get<{_id: string, date: string, grade: number, gradeDate: string, notes: {label: string, weight: number}[], room: string, tips: string} | null>(environment.apiEndpoint+`/admin/clean/${date}/${room}`, {withCredentials: true}) return this.http.get<{
_id: string
date: string
grade: number
gradeDate: string
notes: { label: string; weight: number }[]
room: string
tips: string
} | null>(environment.apiEndpoint + `/admin/clean/${date}/${room}`, {
withCredentials: true,
})
}, },
postClean: (obj: Object) => { postClean: (obj: Object) => {
return this.http.post<Status>(environment.apiEndpoint+`/admin/clean/`, obj, {withCredentials: true}) return this.http.post<Status>(
environment.apiEndpoint + `/admin/clean/`,
obj,
{ withCredentials: true }
)
}, },
delete: (id: string) => { delete: (id: string) => {
return this.http.delete<Status>(environment.apiEndpoint+`/admin/clean/${id}`, {withCredentials: true}) return this.http.delete<Status>(
environment.apiEndpoint + `/admin/clean/${id}`,
{ withCredentials: true }
)
}, },
summary: { summary: {
getSummary: (start: DateTime, end: DateTime) => { getSummary: (start: DateTime, end: DateTime) => {
return this.http.get<{room: string, avg: number}[]>(environment.apiEndpoint+`/admin/clean/summary/${start.toISO()}/${end.toISO()}`, {withCredentials: true}) return this.http.get<{ room: string; avg: number }[]>(
} environment.apiEndpoint +
`/admin/clean/summary/${start.toISO()}/${end.toISO()}`,
{ withCredentials: true }
)
},
}, },
attendence: { attendence: {
getUsers: (room: string) => { getUsers: (room: string) => {
return this.http.get<{users: {fname: string, surname: string, _id: string}[], attendence?: {auto: {id: string, hour?: string}[], notes: string}}>(environment.apiEndpoint+`/admin/clean/attendence/${room}`, {withCredentials: true}) return this.http.get<{
users: { fname: string; surname: string; _id: string }[]
attendence?: { auto: { id: string; hour?: string }[]; notes: string }
}>(environment.apiEndpoint + `/admin/clean/attendence/${room}`, {
withCredentials: true,
})
}, },
postAttendence: (room: string, attendence: {auto: {id: string, hour?: string}[], notes: string}) => { postAttendence: (
return this.http.post<Status>(environment.apiEndpoint+`/admin/clean/attendence/${room}`, attendence, {withCredentials: true}) room: string,
attendence: { auto: { id: string; hour?: string }[]; notes: string }
) => {
return this.http.post<Status>(
environment.apiEndpoint + `/admin/clean/attendence/${room}`,
attendence,
{ withCredentials: true }
)
}, },
getSummary: () => { getSummary: () => {
return this.http.get<{room: string, hours: string[], notes: string, auto: boolean}[]>(environment.apiEndpoint+`/admin/clean/attendenceSummary`, {withCredentials: true}) return this.http.get<
{ room: string; hours: string[]; notes: string; auto: boolean }[]
>(environment.apiEndpoint + `/admin/clean/attendenceSummary`, {
withCredentials: true,
})
}, },
deleteRoom: (room: string) => { deleteRoom: (room: string) => {
return this.http.delete<Status>(environment.apiEndpoint+`/admin/clean/attendence/${room}`, {withCredentials: true}) return this.http.delete<Status>(
} environment.apiEndpoint + `/admin/clean/attendence/${room}`,
} { withCredentials: true }
)
},
},
} }
//#endregion //#endregion
//#region Settings //#region Settings
settings = { settings = {
getAll: () => { getAll: () => {
return this.http.get<IUSettings>(environment.apiEndpoint+`/admin/settings/`, {withCredentials: true}) return this.http.get<IUSettings>(
environment.apiEndpoint + `/admin/settings/`,
{ withCredentials: true }
)
}, },
post: (settings: IUSettings) => { post: (settings: IUSettings) => {
return this.http.post<Status>(environment.apiEndpoint+`/admin/settings/`, settings, {withCredentials: true}) return this.http.post<Status>(
environment.apiEndpoint + `/admin/settings/`,
settings,
{ withCredentials: true }
)
}, },
reload: () => { reload: () => {
return this.http.get<Status>(environment.apiEndpoint+`/admin/settings/reload/`, {withCredentials: true}) return this.http.get<Status>(
} environment.apiEndpoint + `/admin/settings/reload/`,
{ withCredentials: true }
)
},
} }
//#endregion //#endregion
//#region misc //#region misc
userFilter = (query: string) => { userFilter = (query: string) => {
return this.http.get<any[]>(environment.apiEndpoint+`/admin/usearch`, {params: {q: query}, withCredentials: true}) return this.http.get<any[]>(environment.apiEndpoint + `/admin/usearch`, {
params: { q: query },
withCredentials: true,
})
} }
//#endregion //#endregion
} }

View File

@@ -1,14 +1,15 @@
:host { :host {
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
mat-sidenav, mat-toolbar { mat-sidenav,
padding: 8pt mat-toolbar {
padding: 8pt;
} }
mat-sidenav-container { mat-sidenav-container {
flex-grow: 1; flex-grow: 1;
} }

View File

@@ -1,37 +1,45 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { AdminViewComponent } from './admin-view.component'; import { AdminViewComponent } from './admin-view.component'
import { MatToolbarModule } from '@angular/material/toolbar'; import { MatToolbarModule } from '@angular/material/toolbar'
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon'
import { MatDrawer, MatSidenavModule } from '@angular/material/sidenav'; import { MatDrawer, MatSidenavModule } from '@angular/material/sidenav'
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
import { MatListModule } from '@angular/material/list'; import { MatListModule } from '@angular/material/list'
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router'
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core'
@Component({ @Component({
selector: "app-toolbar", template: '', selector: 'app-toolbar',
standalone: false template: '',
standalone: false,
}) })
class ToolbarMock { class ToolbarMock {
@Input() drawer!: MatDrawer; @Input() drawer!: MatDrawer
} }
describe('AdminViewComponent', () => { describe('AdminViewComponent', () => {
let component: AdminViewComponent; let component: AdminViewComponent
let fixture: ComponentFixture<AdminViewComponent>; let fixture: ComponentFixture<AdminViewComponent>
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [AdminViewComponent, ToolbarMock], declarations: [AdminViewComponent, ToolbarMock],
imports: [MatToolbarModule, MatIconModule, MatSidenavModule, BrowserAnimationsModule, MatListModule, RouterModule.forRoot([])] imports: [
}); MatToolbarModule,
fixture = TestBed.createComponent(AdminViewComponent); MatIconModule,
component = fixture.componentInstance; MatSidenavModule,
fixture.detectChanges(); BrowserAnimationsModule,
}); MatListModule,
RouterModule.forRoot([]),
],
})
fixture = TestBed.createComponent(AdminViewComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,30 +1,78 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core'
import { Router } from '@angular/router'; import { Router } from '@angular/router'
import { LocalStorageService } from '../services/local-storage.service'; import { LocalStorageService } from '../services/local-storage.service'
import { Link } from '../types/link'; import { Link } from '../types/link'
@Component({ @Component({
selector: 'app-admin-view', selector: 'app-admin-view',
templateUrl: './admin-view.component.html', templateUrl: './admin-view.component.html',
styleUrls: ['./admin-view.component.scss'], styleUrls: ['./admin-view.component.scss'],
standalone: false standalone: false,
}) })
export class AdminViewComponent { export class AdminViewComponent {
private readonly _LINKS: Link[] = [ private readonly _LINKS: Link[] = [
{ title: "Wiadomości", icon: "newspaper", href: "news", enabled: this.ls.permChecker(1) && this.ls.capCheck(1) }, {
{ title: "Jadłospis", icon: "restaurant_menu", href: "menu", enabled: this.ls.permChecker(2) && this.ls.capCheck(2) }, title: 'Wiadomości',
{ title: "Wysyłanie powiadomień", icon: "notifications", href: "notifications", enabled: this.ls.permChecker(4) && this.ls.capCheck(4) }, icon: 'newspaper',
{ title: "Grupy", icon: "groups", href: "groups", enabled: this.ls.permChecker(8) && this.ls.capCheck(8) }, href: 'news',
{ title: "Zarządzanie kontami", icon: "manage_accounts", href: "accounts", enabled: this.ls.permChecker(16) }, enabled: this.ls.permChecker(1) && this.ls.capCheck(1),
{ title: "Klucze", icon: "key", href: "keys", enabled: this.ls.permChecker(64) && this.ls.capCheck(32) }, },
{ title: "Czystość", icon: "cleaning_services", href: "grades", enabled: this.ls.permChecker(128) && this.ls.capCheck(16) }, {
{ title: "Frekwencja", icon: "checklist", href: "attendence", enabled: false }, title: 'Jadłospis',
{ title: "Ustawienia", icon: "settings_applications", href: "settings", enabled: this.ls.permChecker(32) }, icon: 'restaurant_menu',
]; href: 'menu',
enabled: this.ls.permChecker(2) && this.ls.capCheck(2),
},
{
title: 'Wysyłanie powiadomień',
icon: 'notifications',
href: 'notifications',
enabled: this.ls.permChecker(4) && this.ls.capCheck(4),
},
{
title: 'Grupy',
icon: 'groups',
href: 'groups',
enabled: this.ls.permChecker(8) && this.ls.capCheck(8),
},
{
title: 'Zarządzanie kontami',
icon: 'manage_accounts',
href: 'accounts',
enabled: this.ls.permChecker(16),
},
{
title: 'Klucze',
icon: 'key',
href: 'keys',
enabled: this.ls.permChecker(64) && this.ls.capCheck(32),
},
{
title: 'Czystość',
icon: 'cleaning_services',
href: 'grades',
enabled: this.ls.permChecker(128) && this.ls.capCheck(16),
},
{
title: 'Frekwencja',
icon: 'checklist',
href: 'attendence',
enabled: false,
},
{
title: 'Ustawienia',
icon: 'settings_applications',
href: 'settings',
enabled: this.ls.permChecker(32),
},
]
public get LINKS(): Link[] { public get LINKS(): Link[] {
return this._LINKS.filter(v => v.enabled); return this._LINKS.filter(v => v.enabled)
} }
constructor(readonly router: Router, readonly ls: LocalStorageService) { } constructor(
readonly router: Router,
readonly ls: LocalStorageService
) {}
goNormal() { goNormal() {
this.router.navigateByUrl('app') this.router.navigateByUrl('app')
} }

View File

@@ -1,34 +1,34 @@
@use 'sass:list'; @use "sass:list";
#guide { #guide {
margin: 1em margin: 1em;
} }
#legend { #legend {
display: flex; display: flex;
justify-self: center; justify-self: center;
gap: 3ch; gap: 3ch;
* { * {
margin: 2px; margin: 2px;
} }
} }
.circle { .circle {
&::before { &::before {
border-radius: 7.5%; border-radius: 7.5%;
width: 2.5ch; width: 2.5ch;
height: 2.5ch; height: 2.5ch;
display: inline-block; display: inline-block;
content: ""; content: "";
vertical-align: middle; vertical-align: middle;
margin-right: 3px; margin-right: 3px;
} }
$list: (red, yellow, green); $list: (red, yellow, green);
@for $n from 1 through 3 { @for $n from 1 through 3 {
&:nth-of-type(#{$n})::before { &:nth-of-type(#{$n})::before {
background-color: list.nth($list, $n); background-color: list.nth($list, $n);
}
} }
} }
}

View File

@@ -1,42 +1,36 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { AttendenceSummaryComponent } from './attendence-summary.component'; import { AttendenceSummaryComponent } from './attendence-summary.component'
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router'
import { AdminCommService } from '../../admin-comm.service'; import { AdminCommService } from '../../admin-comm.service'
import { of } from 'rxjs'; import { of } from 'rxjs'
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table'
describe('AttendenceSummaryComponent', () => { describe('AttendenceSummaryComponent', () => {
let component: AttendenceSummaryComponent; let component: AttendenceSummaryComponent
let fixture: ComponentFixture<AttendenceSummaryComponent>; let fixture: ComponentFixture<AttendenceSummaryComponent>
let acMock let acMock
beforeEach(async () => { beforeEach(async () => {
acMock = { acMock = {
clean: { clean: {
attendence: { attendence: {
getSummary: jasmine.createSpy("getSummary").and.returnValue(of()) getSummary: jasmine.createSpy('getSummary').and.returnValue(of()),
} },
} },
} }
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [AttendenceSummaryComponent], declarations: [AttendenceSummaryComponent],
imports: [ imports: [RouterModule.forRoot([]), MatTableModule],
RouterModule.forRoot([]), providers: [{ provide: AdminCommService, useValue: acMock }],
MatTableModule }).compileComponents()
],
providers: [ fixture = TestBed.createComponent(AttendenceSummaryComponent)
{provide: AdminCommService, useValue: acMock} component = fixture.componentInstance
] fixture.detectChanges()
}) })
.compileComponents();
fixture = TestBed.createComponent(AttendenceSummaryComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,24 +1,38 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core'
import { ToolbarService } from '../../toolbar/toolbar.service'; import { ToolbarService } from '../../toolbar/toolbar.service'
import { Router, ActivatedRoute } from '@angular/router'; import { Router, ActivatedRoute } from '@angular/router'
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table'
import { AdminCommService } from '../../admin-comm.service'; import { AdminCommService } from '../../admin-comm.service'
@Component({ @Component({
selector: 'app-attendence-summary', selector: 'app-attendence-summary',
templateUrl: './attendence-summary.component.html', templateUrl: './attendence-summary.component.html',
styleUrl: './attendence-summary.component.scss', styleUrl: './attendence-summary.component.scss',
standalone: false standalone: false,
}) })
export class AttendenceSummaryComponent implements OnInit { export class AttendenceSummaryComponent implements OnInit {
data: MatTableDataSource<{
data: MatTableDataSource<{room: string, hours: string[], notes: string, auto: boolean}> = new MatTableDataSource<{room: string, hours: string[], notes: string, auto: boolean}>(); room: string
hours: string[]
notes: string
auto: boolean
}> = new MatTableDataSource<{
room: string
hours: string[]
notes: string
auto: boolean
}>()
collumns = ['room', 'hours', 'actions'] collumns = ['room', 'hours', 'actions']
constructor (private toolbar: ToolbarService, private router: Router, private route: ActivatedRoute, private ac: AdminCommService) { constructor(
private toolbar: ToolbarService,
private router: Router,
private route: ActivatedRoute,
private ac: AdminCommService
) {
this.toolbar.comp = this this.toolbar.comp = this
this.toolbar.menu = [ this.toolbar.menu = [
{check: true, title: "Ocenianie", fn: "goBack", icon: "arrow_back"} { check: true, title: 'Ocenianie', fn: 'goBack', icon: 'arrow_back' },
] ]
} }
@@ -35,6 +49,6 @@ export class AttendenceSummaryComponent implements OnInit {
} }
goBack() { goBack() {
this.router.navigate(['../'], {relativeTo: this.route}) this.router.navigate(['../'], { relativeTo: this.route })
} }
} }

View File

@@ -1,8 +1,8 @@
:host { :host {
display: inline; display: inline;
} }
span { span {
padding: 2px; padding: 2px;
border-radius: 7.5%; border-radius: 7.5%;
} }

View File

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

View File

@@ -1,27 +1,27 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core'
import { DateTime } from "luxon"; import { DateTime } from 'luxon'
@Component({ @Component({
selector: 'app-hour-display', selector: 'app-hour-display',
templateUrl: './hour-display.component.html', templateUrl: './hour-display.component.html',
styleUrl: './hour-display.component.scss', styleUrl: './hour-display.component.scss',
standalone: false standalone: false,
}) })
export class HourDisplayComponent { export class HourDisplayComponent {
@Input() value = ""; @Input() value = ''
style () { style() {
if (/(0+[0-9]|1[0-9]|2[0-3]):(0+[0-9]|[1-5][0-9])/g.test(this.value)) { if (/(0+[0-9]|1[0-9]|2[0-3]):(0+[0-9]|[1-5][0-9])/g.test(this.value)) {
var diff = DateTime.fromFormat(this.value, "HH:mm").diffNow("minutes") var diff = DateTime.fromFormat(this.value, 'HH:mm').diffNow('minutes')
if (diff.as("minutes") > 30) { if (diff.as('minutes') > 30) {
return { "background-color": "red" } return { 'background-color': 'red' }
} else if (diff.as("minutes") > 0) { } else if (diff.as('minutes') > 0) {
return { "background-color": "yellow", "color": "black"} return { 'background-color': 'yellow', color: 'black' }
} else { } else {
return { "background-color": "green"} return { 'background-color': 'green' }
} }
} else { } else {
return { "color": "gray"} return { color: 'gray' }
} }
} }
} }

View File

@@ -1,32 +1,36 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { AttendenceComponent } from './attendence.component'; import { AttendenceComponent } from './attendence.component'
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import {
import { AdminCommService } from '../../admin-comm.service'; MAT_DIALOG_DATA,
import { MatFormFieldModule } from '@angular/material/form-field'; MatDialogModule,
import { of } from 'rxjs'; MatDialogRef,
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; } from '@angular/material/dialog'
import { MatInputModule } from '@angular/material/input'; import { AdminCommService } from '../../admin-comm.service'
import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { MatFormFieldModule } from '@angular/material/form-field'
import { of } from 'rxjs'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { MatInputModule } from '@angular/material/input'
import { NoopAnimationsModule } from '@angular/platform-browser/animations'
describe('AttendenceComponent', () => { describe('AttendenceComponent', () => {
let component: AttendenceComponent; let component: AttendenceComponent
let fixture: ComponentFixture<AttendenceComponent>; let fixture: ComponentFixture<AttendenceComponent>
beforeEach(async () => { beforeEach(async () => {
const acMock = { const acMock = {
clean: { clean: {
attendence: { attendence: {
getUsers: jasmine.createSpy("getUsers").and.returnValue(of()) getUsers: jasmine.createSpy('getUsers').and.returnValue(of()),
} },
} },
} }
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [AttendenceComponent], declarations: [AttendenceComponent],
providers: [ providers: [
{provide: MAT_DIALOG_DATA, useValue: {}}, { provide: MAT_DIALOG_DATA, useValue: {} },
{provide: MatDialogRef, useValue: {}}, { provide: MatDialogRef, useValue: {} },
{provide: AdminCommService, useValue: acMock} { provide: AdminCommService, useValue: acMock },
], ],
imports: [ imports: [
MatDialogModule, MatDialogModule,
@@ -34,17 +38,16 @@ describe('AttendenceComponent', () => {
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
MatInputModule, MatInputModule,
NoopAnimationsModule NoopAnimationsModule,
] ],
}) }).compileComponents()
.compileComponents();
fixture = TestBed.createComponent(AttendenceComponent)
fixture = TestBed.createComponent(AttendenceComponent); component = fixture.componentInstance
component = fixture.componentInstance; fixture.detectChanges()
fixture.detectChanges(); })
});
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,29 +1,37 @@
import { Component, Inject, OnInit } from '@angular/core'; import { Component, Inject, OnInit } from '@angular/core'
import { FormArray, FormBuilder, FormGroup } from '@angular/forms'; import { FormArray, FormBuilder, FormGroup } from '@angular/forms'
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'
import { AdminCommService } from '../../admin-comm.service'; import { AdminCommService } from '../../admin-comm.service'
@Component({ @Component({
selector: 'app-attendence', selector: 'app-attendence',
templateUrl: './attendence.component.html', templateUrl: './attendence.component.html',
styleUrl: './attendence.component.scss', styleUrl: './attendence.component.scss',
standalone: false standalone: false,
}) })
export class AttendenceComponent implements OnInit { export class AttendenceComponent implements OnInit {
constructor(
constructor(private fb: FormBuilder, @Inject(MAT_DIALOG_DATA) public data: { room: string }, public dialogRef: MatDialogRef<AttendenceComponent>, private ac: AdminCommService) { } private fb: FormBuilder,
@Inject(MAT_DIALOG_DATA) public data: { room: string },
public dialogRef: MatDialogRef<AttendenceComponent>,
private ac: AdminCommService
) {}
ngOnInit(): void { ngOnInit(): void {
this.room = this.data.room this.room = this.data.room
this.ac.clean.attendence.getUsers(this.room).subscribe(query => { this.ac.clean.attendence.getUsers(this.room).subscribe(query => {
query.users.forEach(v => { query.users.forEach(v => {
var att = query.attendence ? query.attendence.auto.find(z => z.id == v._id) : false var att = query.attendence
this.users.push(this.fb.group({ ? query.attendence.auto.find(z => z.id == v._id)
id: v._id, : false
label: `${v.fname} ${v.surname}`, this.users.push(
att: this.fb.control(att), this.fb.group({
hour: this.fb.control(att ? att.hour : ""), id: v._id,
})) label: `${v.fname} ${v.surname}`,
att: this.fb.control(att),
hour: this.fb.control(att ? att.hour : ''),
})
)
}) })
this.form.get('notes')?.setValue(query.attendence?.notes) this.form.get('notes')?.setValue(query.attendence?.notes)
}) })
@@ -32,15 +40,15 @@ export class AttendenceComponent implements OnInit {
save() { save() {
this.dialogRef.close({ this.dialogRef.close({
room: this.room, room: this.room,
...this.form.value ...this.form.value,
}) })
} }
room: string = ""; room: string = ''
form: FormGroup = this.fb.group({ form: FormGroup = this.fb.group({
users: this.fb.array([]), users: this.fb.array([]),
notes: this.fb.control(""), notes: this.fb.control(''),
}) })
get users() { get users() {

View File

@@ -1,10 +1,10 @@
div#things { div#things {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
div#buttons { div#buttons {
* { * {
margin: 0 2px 0 2px; margin: 0 2px 0 2px;
} }
} }

View File

@@ -1,52 +1,52 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { GradesComponent } from './grades.component'; import { GradesComponent } from './grades.component'
import { AdminCommService } from '../admin-comm.service'; import { AdminCommService } from '../admin-comm.service'
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router'
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Component, EventEmitter, Input, Output } from '@angular/core'
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon'
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field'
import { of } from 'rxjs'; import { of } from 'rxjs'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input'
import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'
import { DateTime } from 'luxon'; import { DateTime } from 'luxon'
@Component({ @Component({
selector: "app-date-selector", template: '', selector: 'app-date-selector',
standalone: false template: '',
standalone: false,
}) })
class DateSelectorStub { class DateSelectorStub {
@Input() date: string = DateTime.now().toISODate(); @Input() date: string = DateTime.now().toISODate()
@Output() dateChange = new EventEmitter<string>(); @Output() dateChange = new EventEmitter<string>()
@Input() filter: (date: DateTime | null) => boolean = () => true @Input() filter: (date: DateTime | null) => boolean = () => true
} }
@Component({ @Component({
selector: "app-room-chooser", template: '', selector: 'app-room-chooser',
standalone: false template: '',
standalone: false,
}) })
class RoomSelectorStub { class RoomSelectorStub {
@Input() rooms: string[] = [] @Input() rooms: string[] = []
@Output() room: EventEmitter<string> = new EventEmitter<string>(); @Output() room: EventEmitter<string> = new EventEmitter<string>()
} }
describe('GradesComponent', () => { describe('GradesComponent', () => {
let component: GradesComponent; let component: GradesComponent
let fixture: ComponentFixture<GradesComponent>; let fixture: ComponentFixture<GradesComponent>
let acMock let acMock
beforeEach(async () => { beforeEach(async () => {
acMock = { acMock = {
clean: { clean: {
getConfig: jasmine.createSpy("getConfig").and.returnValue(of()) getConfig: jasmine.createSpy('getConfig').and.returnValue(of()),
} },
} }
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [GradesComponent, DateSelectorStub, RoomSelectorStub], declarations: [GradesComponent, DateSelectorStub, RoomSelectorStub],
providers: [ providers: [{ provide: AdminCommService, useValue: acMock }],
{provide: AdminCommService, useValue: acMock}
],
imports: [ imports: [
RouterModule.forRoot([]), RouterModule.forRoot([]),
MatIconModule, MatIconModule,
@@ -54,17 +54,16 @@ describe('GradesComponent', () => {
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
MatInputModule, MatInputModule,
NoopAnimationsModule NoopAnimationsModule,
] ],
}) }).compileComponents()
.compileComponents();
fixture = TestBed.createComponent(GradesComponent)
fixture = TestBed.createComponent(GradesComponent); component = fixture.componentInstance
component = fixture.componentInstance; fixture.detectChanges()
fixture.detectChanges(); })
});
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,46 +1,52 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core'
import { AdminCommService } from '../admin-comm.service'; import { AdminCommService } from '../admin-comm.service'
import { FormArray, FormBuilder } from '@angular/forms'; import { FormArray, FormBuilder } from '@angular/forms'
import { weekendFilter } from 'src/app/fd.da'; import { weekendFilter } from 'src/app/fd.da'
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar'
import { ToolbarService } from '../toolbar/toolbar.service'; import { ToolbarService } from '../toolbar/toolbar.service'
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router'
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog'
import { AttendenceComponent } from './attendence/attendence.component'; import { AttendenceComponent } from './attendence/attendence.component'
import { DateTime } from 'luxon'; import { DateTime } from 'luxon'
@Component({ @Component({
selector: 'app-grades', selector: 'app-grades',
templateUrl: './grades.component.html', templateUrl: './grades.component.html',
styleUrl: './grades.component.scss', styleUrl: './grades.component.scss',
standalone: false standalone: false,
}) })
export class GradesComponent implements OnInit, OnDestroy { export class GradesComponent implements OnInit, OnDestroy {
rooms!: string[] rooms!: string[]
room: string = "0"; room: string = '0'
protected _date: DateTime; protected _date: DateTime
public get date(): string { public get date(): string {
return this._date.toISODate()!; return this._date.toISODate()!
} }
public set date(value: string) { public set date(value: string) {
this._date = DateTime.fromISO(value); this._date = DateTime.fromISO(value)
} }
grade: number = 6 grade: number = 6
gradeDate?: DateTime; gradeDate?: DateTime
id?: string id?: string
filter = weekendFilter filter = weekendFilter
get notes(): { label: string, weight: number }[] { get notes(): { label: string; weight: number }[] {
var th = this.things.value as { cb: boolean, label: string, weight: number }[] var th = this.things.value as {
return th.filter((v) => v.cb).map((v) => { cb: boolean
return { ...v, cb: undefined } label: string
}) weight: number
}[]
return th
.filter(v => v.cb)
.map(v => {
return { ...v, cb: undefined }
})
} }
set notes(value: { label: string, weight: number }[]) { set notes(value: { label: string; weight: number }[]) {
var things = this.things.controls var things = this.things.controls
things.forEach((v) => { things.forEach(v => {
var thing = value.find((s) => s.label == v.get('label')?.value) var thing = value.find(s => s.label == v.get('label')?.value)
if (thing) { if (thing) {
v.get('cb')?.setValue(true) v.get('cb')?.setValue(true)
v.get('weight')?.setValue(thing.weight) v.get('weight')?.setValue(thing.weight)
@@ -51,22 +57,35 @@ export class GradesComponent implements OnInit, OnDestroy {
}) })
} }
constructor(private ac: AdminCommService, private fb: FormBuilder, private sb: MatSnackBar, private toolbar: ToolbarService, private router: Router, private route: ActivatedRoute, private dialog: MatDialog) { constructor(
private ac: AdminCommService,
private fb: FormBuilder,
private sb: MatSnackBar,
private toolbar: ToolbarService,
private router: Router,
private route: ActivatedRoute,
private dialog: MatDialog
) {
this._date = DateTime.now() this._date = DateTime.now()
// if (!this.filter(this.date)) this.date.isoWeekday(8); // if (!this.filter(this.date)) this.date.isoWeekday(8);
this.toolbar.comp = this this.toolbar.comp = this
this.toolbar.menu = [ this.toolbar.menu = [
{ title: "Pokoje do sprawdzenia", check: true, fn: "attendenceSummary", icon: "overview"}, {
{ title: "Podsumowanie", check: true, fn: "summary", icon: "analytics" }, title: 'Pokoje do sprawdzenia',
check: true,
fn: 'attendenceSummary',
icon: 'overview',
},
{ title: 'Podsumowanie', check: true, fn: 'summary', icon: 'analytics' },
] ]
this.form.valueChanges.subscribe((v) => { this.form.valueChanges.subscribe(v => {
this.calculate() this.calculate()
}) })
} }
form = this.fb.group({ form = this.fb.group({
things: this.fb.array([]), things: this.fb.array([]),
tips: this.fb.control("") tips: this.fb.control(''),
}) })
get things() { get things() {
@@ -74,21 +93,25 @@ export class GradesComponent implements OnInit, OnDestroy {
} }
summary() { summary() {
this.router.navigate(["summary"], { relativeTo: this.route }) this.router.navigate(['summary'], { relativeTo: this.route })
} }
attendenceSummary() { attendenceSummary() {
this.router.navigate(["attendenceSummary"], {relativeTo: this.route}) this.router.navigate(['attendenceSummary'], { relativeTo: this.route })
} }
ngOnInit(): void { ngOnInit(): void {
this.ac.clean.getConfig().subscribe((s) => { this.ac.clean.getConfig().subscribe(s => {
this.rooms = s.rooms this.rooms = s.rooms
s.things.forEach((s) => this.things.push(this.fb.group({ s.things.forEach(s =>
cb: this.fb.control(false), this.things.push(
label: this.fb.control(s), this.fb.group({
weight: this.fb.control(1) cb: this.fb.control(false),
}))) label: this.fb.control(s),
weight: this.fb.control(1),
})
)
)
}) })
} }
@@ -98,19 +121,19 @@ export class GradesComponent implements OnInit, OnDestroy {
} }
downloadData() { downloadData() {
this.ac.clean.getClean(this.date, this.room).subscribe((v) => { this.ac.clean.getClean(this.date, this.room).subscribe(v => {
if (v) { if (v) {
this.notes = v.notes this.notes = v.notes
this.gradeDate = DateTime.fromISO(v.gradeDate) this.gradeDate = DateTime.fromISO(v.gradeDate)
this.grade = v.grade this.grade = v.grade
this.id = v._id this.id = v._id
this.form.get("tips")?.setValue(v.tips) this.form.get('tips')?.setValue(v.tips)
} else { } else {
this.gradeDate = undefined this.gradeDate = undefined
this.grade = 6 this.grade = 6
this.notes = [] this.notes = []
this.id = undefined this.id = undefined
this.form.get("tips")?.setValue("") this.form.get('tips')?.setValue('')
} }
}) })
} }
@@ -139,7 +162,7 @@ export class GradesComponent implements OnInit, OnDestroy {
weight.setValue(weight.value - 1) weight.setValue(weight.value - 1)
} }
} }
} },
} }
save() { save() {
@@ -149,16 +172,16 @@ export class GradesComponent implements OnInit, OnDestroy {
date: this.date, date: this.date,
room: this.room, room: this.room,
notes: this.notes, notes: this.notes,
tips: this.form.get("tips")?.value tips: this.form.get('tips')?.value,
} }
this.ac.clean.postClean(obj).subscribe((s) => { this.ac.clean.postClean(obj).subscribe(s => {
this.sb.open("Zapisano!", undefined, { duration: 1500 }) this.sb.open('Zapisano!', undefined, { duration: 1500 })
this.downloadData() this.downloadData()
}) })
} }
remove() { remove() {
this.ac.clean.delete(this.id!).subscribe((s) => { this.ac.clean.delete(this.id!).subscribe(s => {
if (s.status == 200) { if (s.status == 200) {
this.downloadData() this.downloadData()
} }
@@ -171,22 +194,35 @@ export class GradesComponent implements OnInit, OnDestroy {
} }
attendence() { attendence() {
this.dialog.open(AttendenceComponent, {data: {room: this.room}}).afterClosed().subscribe((v: {room: string, users: {att: boolean, id: string, hour: string}[], notes: string}) => { this.dialog
if (!v) return .open(AttendenceComponent, { data: { room: this.room } })
let x: {room: string, users: {id: string, hour?: string}[]} = { .afterClosed()
room: v.room, .subscribe(
users: [] (v: {
} room: string
v.users.forEach(i => { users: { att: boolean; id: string; hour: string }[]
if (i.att && i.hour) { notes: string
x.users.push({id: i.id, hour: i.hour}) }) => {
if (!v) return
let x: { room: string; users: { id: string; hour?: string }[] } = {
room: v.room,
users: [],
}
v.users.forEach(i => {
if (i.att && i.hour) {
x.users.push({ id: i.id, hour: i.hour })
}
})
this.ac.clean.attendence
.postAttendence(x.room, { auto: x.users, notes: v.notes })
.subscribe(s => {
if (s.status == 200) {
this.sb.open('Zapisano obecność!', undefined, {
duration: 1500,
})
}
})
} }
}) )
this.ac.clean.attendence.postAttendence(x.room, {auto: x.users, notes: v.notes}).subscribe((s) => {
if (s.status == 200) {
this.sb.open("Zapisano obecność!", undefined, {duration: 1500})
}
})
})
} }
} }

View File

@@ -1,34 +1,34 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { SummaryComponent } from './summary.component'; import { SummaryComponent } from './summary.component'
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router'
import { AdminCommService } from '../../admin-comm.service'; import { AdminCommService } from '../../admin-comm.service'
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field'
import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatDatepickerModule } from '@angular/material/datepicker'
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon'
import { of } from 'rxjs'; import { of } from 'rxjs'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table'
import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'
import { provideLuxonDateAdapter } from '@angular/material-luxon-adapter'; import { provideLuxonDateAdapter } from '@angular/material-luxon-adapter'
describe('SummaryComponent', () => { describe('SummaryComponent', () => {
let component: SummaryComponent; let component: SummaryComponent
let fixture: ComponentFixture<SummaryComponent>; let fixture: ComponentFixture<SummaryComponent>
beforeEach(async () => { beforeEach(async () => {
const acMock = { const acMock = {
clean: { clean: {
summary: { summary: {
getSummary: jasmine.createSpy("getSummary").and.returnValue(of()) getSummary: jasmine.createSpy('getSummary').and.returnValue(of()),
} },
} },
} }
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [SummaryComponent], declarations: [SummaryComponent],
providers: [ providers: [
{ provide: AdminCommService, useValue: acMock }, { provide: AdminCommService, useValue: acMock },
provideLuxonDateAdapter() provideLuxonDateAdapter(),
], ],
imports: [ imports: [
RouterModule.forRoot([]), RouterModule.forRoot([]),
@@ -38,17 +38,16 @@ describe('SummaryComponent', () => {
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
MatTableModule, MatTableModule,
NoopAnimationsModule NoopAnimationsModule,
] ],
}) }).compileComponents()
.compileComponents();
fixture = TestBed.createComponent(SummaryComponent); fixture = TestBed.createComponent(SummaryComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,58 +1,68 @@
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'
import { ToolbarService } from '../../toolbar/toolbar.service'; import { ToolbarService } from '../../toolbar/toolbar.service'
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router'
import { AdminCommService } from '../../admin-comm.service'; import { AdminCommService } from '../../admin-comm.service'
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table'
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms'
import { MatSort } from '@angular/material/sort'; import { MatSort } from '@angular/material/sort'
import { DateTime } from 'luxon'; import { DateTime } from 'luxon'
@Component({ @Component({
selector: 'app-summary', selector: 'app-summary',
templateUrl: './summary.component.html', templateUrl: './summary.component.html',
styleUrl: './summary.component.scss', styleUrl: './summary.component.scss',
standalone: false standalone: false,
}) })
export class SummaryComponent implements OnInit, OnDestroy { export class SummaryComponent implements OnInit, OnDestroy {
data: MatTableDataSource<{ room: string; avg: number }> =
data: MatTableDataSource<{room: string, avg: number}> = new MatTableDataSource<{room: string, avg: number}>(); new MatTableDataSource<{ room: string; avg: number }>()
collumns = ['room', 'avg'] collumns = ['room', 'avg']
dateSelector = this.fb.group({ dateSelector = this.fb.group({
start: this.fb.control(DateTime.utc().startOf('day')), start: this.fb.control(DateTime.utc().startOf('day')),
end: this.fb.control(DateTime.utc().endOf('day')) end: this.fb.control(DateTime.utc().endOf('day')),
}) })
@ViewChild(MatSort, {static: false}) set content(sort: MatSort) { @ViewChild(MatSort, { static: false }) set content(sort: MatSort) {
this.data.sort = sort this.data.sort = sort
} }
constructor (private toolbar: ToolbarService, private router: Router, private route: ActivatedRoute, private ac: AdminCommService, private fb: FormBuilder) { constructor(
private toolbar: ToolbarService,
private router: Router,
private route: ActivatedRoute,
private ac: AdminCommService,
private fb: FormBuilder
) {
this.toolbar.comp = this this.toolbar.comp = this
this.toolbar.menu = [ this.toolbar.menu = [
{check: true, title: "Ocenianie", fn: "goBack", icon: "arrow_back"} { check: true, title: 'Ocenianie', fn: 'goBack', icon: 'arrow_back' },
] ]
this.dateSelector.valueChanges.subscribe((v) => { this.dateSelector.valueChanges.subscribe(v => {
this.download() this.download()
}) })
} }
ngOnInit(): void { ngOnInit(): void {
this.download() this.download()
} }
download() { download() {
this.ac.clean.summary.getSummary(this.dateSelector.get('start')?.value!.startOf('day')!, this.dateSelector.get('end')?.value!.endOf('day')!).subscribe((v) => { this.ac.clean.summary
this.data.data = v .getSummary(
}) this.dateSelector.get('start')?.value!.startOf('day')!,
this.dateSelector.get('end')?.value!.endOf('day')!
)
.subscribe(v => {
this.data.data = v
})
} }
goBack() { goBack() {
this.router.navigate(['../'], {relativeTo: this.route}) this.router.navigate(['../'], { relativeTo: this.route })
} }
ngOnDestroy(): void { ngOnDestroy(): void {
this.toolbar.comp = undefined this.toolbar.comp = undefined
this.toolbar.menu = undefined this.toolbar.menu = undefined
} }
} }

View File

@@ -1,12 +1,12 @@
:host { :host {
display: flex; display: flex;
} }
mat-card { mat-card {
margin: 15px; margin: 15px;
padding: 1ch; padding: 1ch;
} }
mat-card-title { mat-card-title {
font-size: 1.5rem; font-size: 1.5rem;
} }

View File

@@ -1,31 +1,29 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { GroupsComponent } from './groups.component'; import { GroupsComponent } from './groups.component'
import { AdminCommService } from '../admin-comm.service'; import { AdminCommService } from '../admin-comm.service'
import { of } from 'rxjs'; import { of } from 'rxjs'
describe('GroupsComponent', () => { describe('GroupsComponent', () => {
let component: GroupsComponent; let component: GroupsComponent
let fixture: ComponentFixture<GroupsComponent>; let fixture: ComponentFixture<GroupsComponent>
beforeEach(() => { beforeEach(() => {
const acMock = { const acMock = {
groups: { groups: {
getGroups: jasmine.createSpy("getGroups").and.returnValue(of()) getGroups: jasmine.createSpy('getGroups').and.returnValue(of()),
} },
} }
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [GroupsComponent], declarations: [GroupsComponent],
providers: [ providers: [{ provide: AdminCommService, useValue: acMock }],
{provide: AdminCommService, useValue: acMock} })
] fixture = TestBed.createComponent(GroupsComponent)
}); component = fixture.componentInstance
fixture = TestBed.createComponent(GroupsComponent); fixture.detectChanges()
component = fixture.componentInstance; })
fixture.detectChanges();
});
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,21 +1,24 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core'
import { AdminCommService } from '../admin-comm.service'; import { AdminCommService } from '../admin-comm.service'
import { Group } from 'src/app/types/group'; import { Group } from 'src/app/types/group'
import { Status } from 'src/app/types/status'; import { Status } from 'src/app/types/status'
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog'
import { RemoveConfirmComponent } from './remove-confirm/remove-confirm.component'; import { RemoveConfirmComponent } from './remove-confirm/remove-confirm.component'
@Component({ @Component({
selector: 'app-groups', selector: 'app-groups',
templateUrl: './groups.component.html', templateUrl: './groups.component.html',
styleUrls: ['./groups.component.scss'], styleUrls: ['./groups.component.scss'],
standalone: false standalone: false,
}) })
export class GroupsComponent implements OnInit { export class GroupsComponent implements OnInit {
groups?: Group[] groups?: Group[]
constructor (protected readonly acs: AdminCommService, private readonly dialog: MatDialog) {} constructor(
protected readonly acs: AdminCommService,
private readonly dialog: MatDialog
) {}
ngOnInit(): void { ngOnInit(): void {
this.acs.groups.getGroups().subscribe((v) => { this.acs.groups.getGroups().subscribe(v => {
this.groups = v this.groups = v
}) })
} }
@@ -26,36 +29,41 @@ export class GroupsComponent implements OnInit {
} }
} }
get groupOptions(): {id: string, text: string}[] { get groupOptions(): { id: string; text: string }[] {
return this.groups!.map((v)=> {return {id: v._id as string, text: v.name as string}}) return this.groups!.map(v => {
return { id: v._id as string, text: v.name as string }
})
} }
protected getId(g: Group[] | undefined) { protected getId(g: Group[] | undefined) {
if (!g) return undefined if (!g) return undefined
return g.map((v)=>v._id) return g.map(v => v._id)
} }
groupNames(groups: Group[]) { groupNames(groups: Group[]) {
return groups.flatMap((g) => g.name) return groups.flatMap(g => g.name)
} }
protected nameEdit(id: string, name: string | string[]) { protected nameEdit(id: string, name: string | string[]) {
name = name as string name = name as string
this.acs.groups.editName(id, name).subscribe((s) => this.refreshIfGood(s)) this.acs.groups.editName(id, name).subscribe(s => this.refreshIfGood(s))
} }
protected newGroup() { protected newGroup() {
let name = prompt("Nazwa grupy") let name = prompt('Nazwa grupy')
if (name) { if (name) {
this.acs.groups.newGroup(name).subscribe((s) => this.refreshIfGood(s)) this.acs.groups.newGroup(name).subscribe(s => this.refreshIfGood(s))
} }
} }
protected remove(id: string) { protected remove(id: string) {
this.dialog.open(RemoveConfirmComponent).afterClosed().subscribe((v) => { this.dialog
if (v) { .open(RemoveConfirmComponent)
this.acs.groups.remove(id).subscribe((s) => this.refreshIfGood(s)) .afterClosed()
} .subscribe(v => {
}) if (v) {
this.acs.groups.remove(id).subscribe(s => this.refreshIfGood(s))
}
})
} }
} }

View File

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

View File

@@ -1,11 +1,9 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core'
@Component({ @Component({
selector: 'app-remove-confirm', selector: 'app-remove-confirm',
templateUrl: './remove-confirm.component.html', templateUrl: './remove-confirm.component.html',
styleUrls: ['./remove-confirm.component.scss'], styleUrls: ['./remove-confirm.component.scss'],
standalone: false standalone: false,
}) })
export class RemoveConfirmComponent { export class RemoveConfirmComponent {}
}

View File

@@ -1,5 +1,5 @@
#upper-bar { #upper-bar {
display: flex; display: flex;
align-items: baseline; align-items: baseline;
gap: 4pt; gap: 4pt;
} }

View File

@@ -1,44 +1,51 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { AdminKeyComponent } from './key.component'; import { AdminKeyComponent } from './key.component'
import { of } from 'rxjs'; import { of } from 'rxjs'
import { AdminCommService } from '../admin-comm.service'; import { AdminCommService } from '../admin-comm.service'
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field'
import { MatChipsModule } from '@angular/material/chips'; import { MatChipsModule } from '@angular/material/chips'
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon'
import { MatPaginatorModule } from '@angular/material/paginator'; import { MatPaginatorModule } from '@angular/material/paginator'
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms'
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table'
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input'
import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'
describe('AdminKeyComponent', () => { describe('AdminKeyComponent', () => {
let component: AdminKeyComponent; let component: AdminKeyComponent
let fixture: ComponentFixture<AdminKeyComponent>; let fixture: ComponentFixture<AdminKeyComponent>
let acMock let acMock
beforeEach(async () => { beforeEach(async () => {
acMock = { acMock = {
keys: { keys: {
getKeys: jasmine.createSpy("getKeys").and.returnValue(of()) getKeys: jasmine.createSpy('getKeys').and.returnValue(of()),
} },
} }
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [AdminKeyComponent], declarations: [AdminKeyComponent],
providers: [ providers: [{ provide: AdminCommService, useValue: acMock }],
{provide: AdminCommService, useValue: acMock} imports: [
MatFormFieldModule,
MatChipsModule,
MatIconModule,
MatPaginatorModule,
FormsModule,
MatProgressSpinnerModule,
MatTableModule,
MatInputModule,
NoopAnimationsModule,
], ],
imports: [MatFormFieldModule, MatChipsModule, MatIconModule, MatPaginatorModule, FormsModule, MatProgressSpinnerModule, MatTableModule, MatInputModule, NoopAnimationsModule] }).compileComponents()
})
.compileComponents(); fixture = TestBed.createComponent(AdminKeyComponent)
component = fixture.componentInstance
fixture = TestBed.createComponent(AdminKeyComponent); fixture.detectChanges()
component = fixture.componentInstance; })
fixture.detectChanges();
});
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,46 +1,50 @@
import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core'; import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core'
import { MatPaginator } from '@angular/material/paginator'; import { MatPaginator } from '@angular/material/paginator'
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table'
import { AKey } from 'src/app/types/key'; import { AKey } from 'src/app/types/key'
import { AdminCommService } from '../admin-comm.service'; import { AdminCommService } from '../admin-comm.service'
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog'
import { NewKeyComponent } from './new-key/new-key.component'; import { NewKeyComponent } from './new-key/new-key.component'
import { catchError, throwError } from 'rxjs'; import { catchError, throwError } from 'rxjs'
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar'
@Component({ @Component({
selector: 'app-admin-key', selector: 'app-admin-key',
templateUrl: './key.component.html', templateUrl: './key.component.html',
styleUrl: './key.component.scss', styleUrl: './key.component.scss',
standalone: false standalone: false,
}) })
export class AdminKeyComponent implements AfterViewInit, OnInit { export class AdminKeyComponent implements AfterViewInit, OnInit {
keys: MatTableDataSource<AKey> = new MatTableDataSource<AKey>(); keys: MatTableDataSource<AKey> = new MatTableDataSource<AKey>()
pureData: AKey[] = [] pureData: AKey[] = []
private _filters: string[] = []; private _filters: string[] = []
public get filters(): string[] { public get filters(): string[] {
return this._filters; return this._filters
} }
collumns = ['room', 'whom', 'borrow', 'tb', 'actions'] collumns = ['room', 'whom', 'borrow', 'tb', 'actions']
public set filters(value: string[]) { public set filters(value: string[]) {
if (value.includes("showAll")) { if (value.includes('showAll')) {
this.collumns = ['room', 'whom', 'borrow', 'tb', 'actions'] this.collumns = ['room', 'whom', 'borrow', 'tb', 'actions']
} else { } else {
this.collumns = ['room', 'whom', 'borrow', 'actions'] this.collumns = ['room', 'whom', 'borrow', 'actions']
} }
this._filters = value; this._filters = value
this.transformData(); this.transformData()
} }
loading = true loading = true
@ViewChild(MatPaginator) paginator!: MatPaginator @ViewChild(MatPaginator) paginator!: MatPaginator
constructor (private ac: AdminCommService, private dialog: MatDialog, private sb: MatSnackBar) { constructor(
private ac: AdminCommService,
private dialog: MatDialog,
private sb: MatSnackBar
) {
this.filters = [] this.filters = []
} }
fetchData() { fetchData() {
this.loading = true this.loading = true
this.ac.keys.getKeys().subscribe((r) => { this.ac.keys.getKeys().subscribe(r => {
this.loading = false this.loading = false
this.pureData = r this.pureData = r
this.transformData() this.transformData()
@@ -49,15 +53,16 @@ export class AdminKeyComponent implements AfterViewInit, OnInit {
transformData() { transformData() {
var finalData: AKey[] = this.pureData var finalData: AKey[] = this.pureData
if (!this.filters.includes('showAll')) finalData = finalData.filter((v) => v.tb == undefined) if (!this.filters.includes('showAll'))
finalData = finalData.filter(v => v.tb == undefined)
this.keys.data = finalData this.keys.data = finalData
} }
filter(event: Event) { filter(event: Event) {
const value = (event.target as HTMLInputElement).value const value = (event.target as HTMLInputElement).value
this.keys.filter = value.toLowerCase().trim() this.keys.filter = value.toLowerCase().trim()
} }
ngAfterViewInit(): void { ngAfterViewInit(): void {
this.keys.paginator = this.paginator this.keys.paginator = this.paginator
} }
@@ -68,26 +73,36 @@ export class AdminKeyComponent implements AfterViewInit, OnInit {
// {room: "Kawiarenka", borrow: moment().subtract(15, "minutes"), whom: {_id: "test", room: 303, uname: "sk"}} // {room: "Kawiarenka", borrow: moment().subtract(15, "minutes"), whom: {_id: "test", room: 303, uname: "sk"}}
// ] // ]
} }
new() { new() {
this.dialog.open(NewKeyComponent).afterClosed().subscribe(v => { this.dialog
if (v) { .open(NewKeyComponent)
this.ac.keys.postKey(v.room, v.user).pipe(catchError((err,caught)=>{ .afterClosed()
if (err.status == 404) { .subscribe(v => {
this.sb.open("Nie znaleziono użytkownika", undefined, {duration: 2500}) if (v) {
} this.ac.keys
return throwError(() => new Error(err.message)) .postKey(v.room, v.user)
})).subscribe((s) => { .pipe(
if (s.status == 201) { catchError((err, caught) => {
this.fetchData() if (err.status == 404) {
} this.sb.open('Nie znaleziono użytkownika', undefined, {
}) duration: 2500,
} })
}) }
return throwError(() => new Error(err.message))
})
)
.subscribe(s => {
if (s.status == 201) {
this.fetchData()
}
})
}
})
} }
tb(id: string) { tb(id: string) {
this.ac.keys.returnKey(id).subscribe((r) => { this.ac.keys.returnKey(id).subscribe(r => {
if (r.status == 200) { if (r.status == 200) {
this.fetchData() this.fetchData()
} }

View File

@@ -1,4 +1,4 @@
form { form {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }

View File

@@ -1,42 +1,58 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NewKeyComponent } from './new-key.component'; import { NewKeyComponent } from './new-key.component'
import { AdminCommService } from '../../admin-comm.service'; import { AdminCommService } from '../../admin-comm.service'
import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'
import { MatFormFieldControl, MatFormFieldModule } from '@angular/material/form-field'; import {
import { MatSelectModule } from '@angular/material/select'; MatFormFieldControl,
import { Component, forwardRef, Optional, Self } from '@angular/core'; MatFormFieldModule,
import { Observable, of } from 'rxjs'; } from '@angular/material/form-field'
import { AbstractControlDirective, ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR, NgControl, ReactiveFormsModule } from '@angular/forms'; import { MatSelectModule } from '@angular/material/select'
import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { Component, forwardRef, Optional, Self } from '@angular/core'
import { Observable, of } from 'rxjs'
import {
AbstractControlDirective,
ControlValueAccessor,
FormsModule,
NG_VALUE_ACCESSOR,
NgControl,
ReactiveFormsModule,
} from '@angular/forms'
import { NoopAnimationsModule } from '@angular/platform-browser/animations'
@Component({ @Component({
selector: "app-user-search", template: '', providers: [{ selector: 'app-user-search',
provide: NG_VALUE_ACCESSOR, template: '',
useExisting: forwardRef(() => UserSearchStub), providers: [
multi: true, {
}, provide: NG_VALUE_ACCESSOR,
{ useExisting: forwardRef(() => UserSearchStub),
provide: MatFormFieldControl, multi: true,
useExisting: UserSearchStub },
}], {
standalone: false provide: MatFormFieldControl,
useExisting: UserSearchStub,
},
],
standalone: false,
}) })
class UserSearchStub implements ControlValueAccessor, MatFormFieldControl<never> { class UserSearchStub
value: null = null; implements ControlValueAccessor, MatFormFieldControl<never>
stateChanges: Observable<void> = of(); {
id: string = ""; value: null = null
placeholder: string = ""; stateChanges: Observable<void> = of()
ngControl: NgControl | AbstractControlDirective | null = null; id: string = ''
focused: boolean = false; placeholder: string = ''
empty: boolean = true; ngControl: NgControl | AbstractControlDirective | null = null
shouldLabelFloat: boolean = true; focused: boolean = false
required: boolean = false; empty: boolean = true
disabled: boolean = false; shouldLabelFloat: boolean = true
errorState: boolean = false; required: boolean = false
controlType?: string | undefined; disabled: boolean = false
autofilled?: boolean | undefined; errorState: boolean = false
userAriaDescribedBy?: string | undefined; controlType?: string | undefined
autofilled?: boolean | undefined
userAriaDescribedBy?: string | undefined
setDescribedByIds(ids: string[]): void {} setDescribedByIds(ids: string[]): void {}
onContainerClick(event: MouseEvent): void {} onContainerClick(event: MouseEvent): void {}
writeValue(obj: any): void {} writeValue(obj: any): void {}
@@ -46,21 +62,21 @@ class UserSearchStub implements ControlValueAccessor, MatFormFieldControl<never>
} }
describe('NewKeyComponent', () => { describe('NewKeyComponent', () => {
let component: NewKeyComponent; let component: NewKeyComponent
let fixture: ComponentFixture<NewKeyComponent>; let fixture: ComponentFixture<NewKeyComponent>
let acMock let acMock
beforeEach(async () => { beforeEach(async () => {
acMock = { acMock = {
keys: { keys: {
avalKeys: jasmine.createSpy("avalKeys").and.returnValue(of()) avalKeys: jasmine.createSpy('avalKeys').and.returnValue(of()),
} },
} }
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [NewKeyComponent, UserSearchStub], declarations: [NewKeyComponent, UserSearchStub],
providers: [ providers: [
{ provide: AdminCommService, useValue: acMock }, { provide: AdminCommService, useValue: acMock },
{ provide: MatDialogRef, useValue: {} } { provide: MatDialogRef, useValue: {} },
], ],
imports: [ imports: [
MatDialogModule, MatDialogModule,
@@ -68,17 +84,16 @@ describe('NewKeyComponent', () => {
MatSelectModule, MatSelectModule,
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
NoopAnimationsModule NoopAnimationsModule,
] ],
}) }).compileComponents()
.compileComponents();
fixture = TestBed.createComponent(NewKeyComponent); fixture = TestBed.createComponent(NewKeyComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,26 +1,29 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core'
import { AdminCommService } from '../../admin-comm.service'; import { AdminCommService } from '../../admin-comm.service'
import { MatDialogRef } from '@angular/material/dialog'; import { MatDialogRef } from '@angular/material/dialog'
import { FormControl, FormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms'
import { UserSearchResult } from 'src/app/commonComponents/user-search/user-search.component'; import { UserSearchResult } from 'src/app/commonComponents/user-search/user-search.component'
@Component({ @Component({
selector: 'app-new-key', selector: 'app-new-key',
templateUrl: './new-key.component.html', templateUrl: './new-key.component.html',
styleUrl: './new-key.component.scss', styleUrl: './new-key.component.scss',
standalone: false standalone: false,
}) })
export class NewKeyComponent implements OnInit { export class NewKeyComponent implements OnInit {
rooms: string[] = [] rooms: string[] = []
form = new FormGroup({ form = new FormGroup({
room: new FormControl<string>(""), room: new FormControl<string>(''),
user: new FormControl<UserSearchResult | null>(null) user: new FormControl<UserSearchResult | null>(null),
}) })
unames: any[] = [] unames: any[] = []
constructor ( private ac: AdminCommService, public dialogRef: MatDialogRef<NewKeyComponent> ) {} constructor(
private ac: AdminCommService,
public dialogRef: MatDialogRef<NewKeyComponent>
) {}
ngOnInit(): void { ngOnInit(): void {
this.ac.keys.avalKeys().subscribe((v) => { this.ac.keys.avalKeys().subscribe(v => {
this.rooms = v this.rooms = v
}) })
} }
@@ -28,7 +31,6 @@ export class NewKeyComponent implements OnInit {
send() { send() {
if (this.form.valid) { if (this.form.valid) {
this.dialogRef.close(this.form.value) this.dialogRef.close(this.form.value)
} }
} }
} }

View File

@@ -1,36 +1,39 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { MenuAddComponent } from './menu-add.component'; import { MenuAddComponent } from './menu-add.component'
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import {
import { MatRadioModule } from '@angular/material/radio'; MAT_DIALOG_DATA,
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; MatDialogModule,
MatDialogRef,
} from '@angular/material/dialog'
import { MatRadioModule } from '@angular/material/radio'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
describe('MenuAddComponent', () => { describe('MenuAddComponent', () => {
let component: MenuAddComponent; let component: MenuAddComponent
let fixture: ComponentFixture<MenuAddComponent>; let fixture: ComponentFixture<MenuAddComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [MenuAddComponent], declarations: [MenuAddComponent],
providers: [ providers: [
{provide: MAT_DIALOG_DATA, useValue: {}}, { provide: MAT_DIALOG_DATA, useValue: {} },
{provide: MatDialogRef, useValue: {}} { provide: MatDialogRef, useValue: {} },
], ],
imports: [ imports: [
MatDialogModule, MatDialogModule,
MatRadioModule, MatRadioModule,
ReactiveFormsModule, ReactiveFormsModule,
FormsModule FormsModule,
] ],
}) }).compileComponents()
.compileComponents();
fixture = TestBed.createComponent(MenuAddComponent)
fixture = TestBed.createComponent(MenuAddComponent); component = fixture.componentInstance
component = fixture.componentInstance; fixture.detectChanges()
fixture.detectChanges(); })
});
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,51 +1,60 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core'
import { MenuUploadComponent } from '../menu-upload/menu-upload.component'; import { MenuUploadComponent } from '../menu-upload/menu-upload.component'
import { MatDialog, MatDialogRef } from '@angular/material/dialog'; import { MatDialog, MatDialogRef } from '@angular/material/dialog'
import { FDSelection, weekendFilter } from 'src/app/fd.da'; import { FDSelection, weekendFilter } from 'src/app/fd.da'
import { FormControl, FormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms'
import { MAT_DATE_RANGE_SELECTION_STRATEGY } from '@angular/material/datepicker'; import { MAT_DATE_RANGE_SELECTION_STRATEGY } from '@angular/material/datepicker'
import { DateTime } from 'luxon'; import { DateTime } from 'luxon'
@Component({ @Component({
selector: 'app-menu-add', selector: 'app-menu-add',
templateUrl: './menu-add.component.html', templateUrl: './menu-add.component.html',
styleUrl: './menu-add.component.scss', styleUrl: './menu-add.component.scss',
providers: [ providers: [
{ provide: MAT_DATE_RANGE_SELECTION_STRATEGY, useClass: FDSelection } { provide: MAT_DATE_RANGE_SELECTION_STRATEGY, useClass: FDSelection },
], ],
standalone: false standalone: false,
}) })
export class MenuAddComponent { export class MenuAddComponent {
type: string | undefined; type: string | undefined
filter = weekendFilter filter = weekendFilter
day: string = DateTime.now().toISODate(); day: string = DateTime.now().toISODate()
range = new FormGroup({ range = new FormGroup({
start: new FormControl<DateTime|null>(null), start: new FormControl<DateTime | null>(null),
end: new FormControl<DateTime|null>(null), end: new FormControl<DateTime | null>(null),
}) })
constructor (public dialogRef: MatDialogRef<MenuAddComponent>, private dialog: MatDialog) { } constructor(
public dialogRef: MatDialogRef<MenuAddComponent>,
private dialog: MatDialog
) {}
submit() { submit() {
switch (this.type) { switch (this.type) {
case "day": case 'day':
this.dialogRef.close({type: "day", value: this.day}) this.dialogRef.close({ type: 'day', value: this.day })
break; break
case "week": case 'week':
this.dialogRef.close({type: "week", value: {start: this.range.value.start?.toISODate(), count: 5}}) this.dialogRef.close({
break; type: 'week',
value: { start: this.range.value.start?.toISODate(), count: 5 },
})
break
default: default:
break; break
} }
} }
activateUpload() { activateUpload() {
this.dialog.open(MenuUploadComponent).afterClosed().subscribe((data) => { this.dialog
if (data) { .open(MenuUploadComponent)
this.dialogRef.close({type: "file", ...data}); .afterClosed()
} .subscribe(data => {
}) if (data) {
this.dialogRef.close({ type: 'file', ...data })
}
})
} }
} }

View File

@@ -1,18 +1,18 @@
#upper-bar { #upper-bar {
display: flex; display: flex;
} }
mat-form-field { mat-form-field {
flex-grow: 1; flex-grow: 1;
} }
button[mat-icon-button] { button[mat-icon-button] {
margin-left: 4pt; margin-left: 4pt;
margin-right: 4pt; margin-right: 4pt;
margin-top: 4pt; margin-top: 4pt;
} }
.non-editable { .non-editable {
color: gray; color: gray;
font-style: italic; font-style: italic;
} }

View File

@@ -1,41 +1,52 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { MenuNewComponent } from './menu-new.component'; import { MenuNewComponent } from './menu-new.component'
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table'
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input'
import { MAT_DATE_RANGE_SELECTION_STRATEGY, MatDatepickerModule } from '@angular/material/datepicker'; import {
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; MAT_DATE_RANGE_SELECTION_STRATEGY,
import { FDSelection } from 'src/app/fd.da'; MatDatepickerModule,
import { ReactiveFormsModule } from '@angular/forms'; } from '@angular/material/datepicker'
import { AdminCommService } from '../admin-comm.service'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
import { of } from 'rxjs'; import { FDSelection } from 'src/app/fd.da'
import { MatDialogModule } from '@angular/material/dialog'; import { ReactiveFormsModule } from '@angular/forms'
import { MatIconModule } from '@angular/material/icon'; import { AdminCommService } from '../admin-comm.service'
import { provideLuxonDateAdapter } from '@angular/material-luxon-adapter'; import { of } from 'rxjs'
import { MatDialogModule } from '@angular/material/dialog'
import { MatIconModule } from '@angular/material/icon'
import { provideLuxonDateAdapter } from '@angular/material-luxon-adapter'
describe('MenuNewComponent', () => { describe('MenuNewComponent', () => {
let component: MenuNewComponent; let component: MenuNewComponent
let fixture: ComponentFixture<MenuNewComponent>; let fixture: ComponentFixture<MenuNewComponent>
beforeEach(() => { beforeEach(() => {
const acMock = jasmine.createSpyObj('AdminCommService', { const acMock = jasmine.createSpyObj('AdminCommService', {
getMenu: of() getMenu: of(),
}) })
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [MenuNewComponent], declarations: [MenuNewComponent],
imports: [MatTableModule, MatInputModule, MatDatepickerModule, BrowserAnimationsModule, ReactiveFormsModule, MatDialogModule, MatIconModule], imports: [
MatTableModule,
MatInputModule,
MatDatepickerModule,
BrowserAnimationsModule,
ReactiveFormsModule,
MatDialogModule,
MatIconModule,
],
providers: [ providers: [
provideLuxonDateAdapter(), provideLuxonDateAdapter(),
{provide: MAT_DATE_RANGE_SELECTION_STRATEGY, useClass: FDSelection}, { provide: MAT_DATE_RANGE_SELECTION_STRATEGY, useClass: FDSelection },
{provide: AdminCommService, useValue: acMock} { provide: AdminCommService, useValue: acMock },
], ],
}); })
fixture = TestBed.createComponent(MenuNewComponent); fixture = TestBed.createComponent(MenuNewComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,85 +1,105 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms'
import { MAT_DATE_RANGE_SELECTION_STRATEGY } from '@angular/material/datepicker'; import { MAT_DATE_RANGE_SELECTION_STRATEGY } from '@angular/material/datepicker'
import { FDSelection } from 'src/app/fd.da'; import { FDSelection } from 'src/app/fd.da'
import { Menu } from 'src/app/types/menu'; import { Menu } from 'src/app/types/menu'
import { AdminCommService } from '../admin-comm.service'; import { AdminCommService } from '../admin-comm.service'
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table'
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog'
import { MenuUploadComponent } from './menu-upload/menu-upload.component'; import { MenuUploadComponent } from './menu-upload/menu-upload.component'
import { Status } from 'src/app/types/status'; import { Status } from 'src/app/types/status'
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar'
import { MenuAddComponent } from './menu-add/menu-add.component'; import { MenuAddComponent } from './menu-add/menu-add.component'
import { LocalStorageService } from 'src/app/services/local-storage.service'; import { LocalStorageService } from 'src/app/services/local-storage.service'
import { DateTime } from 'luxon'; import { DateTime } from 'luxon'
@Component({ @Component({
selector: 'app-menu-new', selector: 'app-menu-new',
templateUrl: './menu-new.component.html', templateUrl: './menu-new.component.html',
styleUrls: ['./menu-new.component.scss'], styleUrls: ['./menu-new.component.scss'],
providers: [ providers: [
{ provide: MAT_DATE_RANGE_SELECTION_STRATEGY, useClass: FDSelection } { provide: MAT_DATE_RANGE_SELECTION_STRATEGY, useClass: FDSelection },
], ],
standalone: false standalone: false,
}) })
export class MenuNewComponent { export class MenuNewComponent {
dcols: string[] = ['day', 'sn', 'ob', 'kol'] dcols: string[] = ['day', 'sn', 'ob', 'kol']
dataSource: MatTableDataSource<Menu> = new MatTableDataSource<Menu>() dataSource: MatTableDataSource<Menu> = new MatTableDataSource<Menu>()
range = new FormGroup({ range = new FormGroup({
start: new FormControl<DateTime|null>(null), start: new FormControl<DateTime | null>(null),
end: new FormControl<DateTime|null>(null), end: new FormControl<DateTime | null>(null),
}) })
loading = false loading = false
public options: any; public options: any
constructor (private ac: AdminCommService, private dialog: MatDialog, private sb: MatSnackBar, readonly ls: LocalStorageService) { } constructor(
private ac: AdminCommService,
private dialog: MatDialog,
private sb: MatSnackBar,
readonly ls: LocalStorageService
) {}
print() { print() {
this.ac.menu.print(this.range.value.start, this.range.value.end)?.subscribe((r) => { this.ac.menu
if (r && r.length > 0) { .print(this.range.value.start, this.range.value.end)
var mywindow = window.open(undefined, 'Drukowanie', 'height=400,width=400') ?.subscribe(r => {
mywindow?.document.write(r) if (r && r.length > 0) {
mywindow?.print() var mywindow = window.open(
mywindow?.close() undefined,
} 'Drukowanie',
}) 'height=400,width=400'
)
mywindow?.document.write(r)
mywindow?.print()
mywindow?.close()
}
})
} }
addDate() { addDate() {
this.dialog.open(MenuAddComponent).afterClosed().subscribe((data) => { this.dialog
if (data) { .open(MenuAddComponent)
switch (data.type) { .afterClosed()
case "day": .subscribe(data => {
this.ac.menu.new.single(data.value).subscribe(s => this.refreshIfGood(s)) if (data) {
break; switch (data.type) {
case "week": case 'day':
this.ac.menu.new.range(data.value.start, data.value.count).subscribe(s => this.refreshIfGood(s)) this.ac.menu.new
break; .single(data.value)
case "file": .subscribe(s => this.refreshIfGood(s))
this.requestData() break
break; case 'week':
default: this.ac.menu.new
break; .range(data.value.start, data.value.count)
.subscribe(s => this.refreshIfGood(s))
break
case 'file':
this.requestData()
break
default:
break
}
} }
} })
})
} }
requestData() { requestData() {
this.loading = true this.loading = true
this.ac.menu.getOpts().subscribe((o) => { this.ac.menu.getOpts().subscribe(o => {
this.options = o; this.options = o
}) })
this.ac.menu.getMenu(this.range.value.start, this.range.value.end)?.subscribe((data) => { this.ac.menu
this.loading = false .getMenu(this.range.value.start, this.range.value.end)
this.dataSource.data = data.map((v) => { ?.subscribe(data => {
let newMenu: Menu = { this.loading = false
...v, this.dataSource.data = data.map(v => {
day: DateTime.fromISO(v.day) let newMenu: Menu = {
} ...v,
return newMenu day: DateTime.fromISO(v.day),
}
return newMenu
})
}) })
})
} }
private refreshIfGood(s: Status) { private refreshIfGood(s: Status) {
@@ -87,33 +107,52 @@ export class MenuNewComponent {
this.requestData() this.requestData()
} }
} }
activateUpload() { activateUpload() {
this.dialog.open(MenuUploadComponent).afterClosed().subscribe((data) => { this.dialog
if (data) { .open(MenuUploadComponent)
this.requestData() .afterClosed()
} .subscribe(data => {
}) if (data) {
this.requestData()
}
})
} }
editSn(id: string) { editSn(id: string) {
this.ac.menu.editSn(id, this.dataSource.data.find(v => v._id == id)!.sn).subscribe(s => this.refreshIfGood(s)) this.ac.menu
.editSn(id, this.dataSource.data.find(v => v._id == id)!.sn)
.subscribe(s => this.refreshIfGood(s))
} }
editOb(id: string) { editOb(id: string) {
this.ac.menu.editOb(id, this.dataSource.data.find(v => v._id == id)!.ob).subscribe(s => this.refreshIfGood(s)) this.ac.menu
.editOb(id, this.dataSource.data.find(v => v._id == id)!.ob)
.subscribe(s => this.refreshIfGood(s))
} }
editKol(id: string) { editKol(id: string) {
this.ac.menu.editKol(id, this.dataSource.data.find(v => v._id == id)?.kol).subscribe(s => this.refreshIfGood(s)) this.ac.menu
.editKol(id, this.dataSource.data.find(v => v._id == id)?.kol)
.subscribe(s => this.refreshIfGood(s))
} }
editTitle(id: string) { editTitle(id: string) {
this.ac.menu.editTitle(id, this.dataSource.data.find(v => v._id == id)?.dayTitle).subscribe(s => this.refreshIfGood(s)) this.ac.menu
.editTitle(id, this.dataSource.data.find(v => v._id == id)?.dayTitle)
.subscribe(s => this.refreshIfGood(s))
} }
getStat(day: DateTime, m: "ob" | "kol") { getStat(day: DateTime, m: 'ob' | 'kol') {
this.ac.menu.stat(day, m).subscribe((s) => this.sb.open(`${s.y} / ${s.y+s.n} = ${((s.y/(s.y+s.n))*100).toFixed(2)}%`, "Zamknij", {duration: 2500})) this.ac.menu
.stat(day, m)
.subscribe(s =>
this.sb.open(
`${s.y} / ${s.y + s.n} = ${((s.y / (s.y + s.n)) * 100).toFixed(2)}%`,
'Zamknij',
{ duration: 2500 }
)
)
} }
remove(id: string) { remove(id: string) {

View File

@@ -1,4 +1,4 @@
:host { :host {
margin: 8pt; margin: 8pt;
display: block; display: block;
} }

View File

@@ -1,29 +1,29 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { MenuUploadComponent } from './menu-upload.component'; import { MenuUploadComponent } from './menu-upload.component'
import { AdminCommService } from '../../admin-comm.service'; import { AdminCommService } from '../../admin-comm.service'
import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'
describe('MenuUploadComponent', () => { describe('MenuUploadComponent', () => {
let component: MenuUploadComponent; let component: MenuUploadComponent
let fixture: ComponentFixture<MenuUploadComponent>; let fixture: ComponentFixture<MenuUploadComponent>
beforeEach(() => { beforeEach(() => {
const acMock = jasmine.createSpyObj('AdminCommService', ['postMenu']) const acMock = jasmine.createSpyObj('AdminCommService', ['postMenu'])
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [MenuUploadComponent], declarations: [MenuUploadComponent],
providers: [ providers: [
{provide: AdminCommService, useValue: acMock}, { provide: AdminCommService, useValue: acMock },
{provide: MatDialogRef, useValue: {}} { provide: MatDialogRef, useValue: {} },
], ],
imports: [MatDialogModule] imports: [MatDialogModule],
}); })
fixture = TestBed.createComponent(MenuUploadComponent); fixture = TestBed.createComponent(MenuUploadComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,27 +1,30 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core'
import { AdminCommService } from '../../admin-comm.service'; import { AdminCommService } from '../../admin-comm.service'
import { MatDialogRef } from '@angular/material/dialog'; import { MatDialogRef } from '@angular/material/dialog'
@Component({ @Component({
selector: 'app-upload-edit', selector: 'app-upload-edit',
templateUrl: './menu-upload.component.html', templateUrl: './menu-upload.component.html',
styleUrls: ['./menu-upload.component.scss'], styleUrls: ['./menu-upload.component.scss'],
standalone: false standalone: false,
}) })
export class MenuUploadComponent { export class MenuUploadComponent {
constructor(private ac:AdminCommService, public dialogRef: MatDialogRef<MenuUploadComponent>) {} constructor(
protected file: File | undefined; private ac: AdminCommService,
public dialogRef: MatDialogRef<MenuUploadComponent>
) {}
protected file: File | undefined
onFileChange(event: Event) { onFileChange(event: Event) {
const file:File = (event.target as HTMLInputElement).files![0]; const file: File = (event.target as HTMLInputElement).files![0]
if (file) { if (file) {
this.file = file this.file = file
} else { } else {
this.file = undefined this.file = undefined
} }
} }
submit() { submit() {
this.ac.menu.postMenu(this.file!)?.subscribe((value) => { this.ac.menu.postMenu(this.file!)?.subscribe(value => {
this.dialogRef.close(value) this.dialogRef.close(value)
}) })
} }

View File

@@ -1,10 +1,10 @@
:host { :host {
padding: 8pt; padding: 8pt;
display: block; display: block;
} }
form { form {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: stretch; align-items: stretch;
} }

View File

@@ -1,31 +1,41 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NewPostComponent } from './edit-post.component'; import { NewPostComponent } from './edit-post.component'
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import {
import { MatFormFieldModule } from '@angular/material/form-field'; MAT_DIALOG_DATA,
import { MatInputModule } from '@angular/material/input'; MatDialogModule,
import { ReactiveFormsModule } from '@angular/forms'; MatDialogRef,
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; } from '@angular/material/dialog'
import { MatFormFieldModule } from '@angular/material/form-field'
import { MatInputModule } from '@angular/material/input'
import { ReactiveFormsModule } from '@angular/forms'
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
describe('NewPostComponent', () => { describe('NewPostComponent', () => {
let component: NewPostComponent; let component: NewPostComponent
let fixture: ComponentFixture<NewPostComponent>; let fixture: ComponentFixture<NewPostComponent>
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [NewPostComponent], declarations: [NewPostComponent],
imports: [MatDialogModule, MatFormFieldModule, MatInputModule, ReactiveFormsModule, BrowserAnimationsModule], imports: [
MatDialogModule,
MatFormFieldModule,
MatInputModule,
ReactiveFormsModule,
BrowserAnimationsModule,
],
providers: [ providers: [
{provide: MatDialogRef, useValue: {}}, { provide: MatDialogRef, useValue: {} },
{provide: MAT_DIALOG_DATA, useValue: {}} { provide: MAT_DIALOG_DATA, useValue: {} },
] ],
}); })
fixture = TestBed.createComponent(NewPostComponent); fixture = TestBed.createComponent(NewPostComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,32 +1,35 @@
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms'
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'
@Component({ @Component({
selector: 'app-edit-post', selector: 'app-edit-post',
templateUrl: './edit-post.component.html', templateUrl: './edit-post.component.html',
styleUrls: ['./edit-post.component.scss'], styleUrls: ['./edit-post.component.scss'],
standalone: false standalone: false,
}) })
export class NewPostComponent { export class NewPostComponent {
form: FormGroup; form: FormGroup
constructor (public dialogRef: MatDialogRef<NewPostComponent>, @Inject(MAT_DIALOG_DATA) public data: any) { constructor(
public dialogRef: MatDialogRef<NewPostComponent>,
@Inject(MAT_DIALOG_DATA) public data: any
) {
if (data == null) { if (data == null) {
data = { data = {
title:"", title: '',
content:"", content: '',
} }
} }
this.form = new FormGroup({ this.form = new FormGroup({
title: new FormControl(data.title), title: new FormControl(data.title),
content: new FormControl(data.content) content: new FormControl(data.content),
}) })
} }
protected makePost() { protected makePost() {
this.dialogRef.close({ this.dialogRef.close({
title: this.form.get('title')?.value, title: this.form.get('title')?.value,
content: this.form.get('content')?.value content: this.form.get('content')?.value,
}) })
} }
} }

View File

@@ -1,38 +1,38 @@
mat-card { mat-card {
margin: 15px; margin: 15px;
padding: 1ch; padding: 1ch;
width: 100%; width: 100%;
} }
mat-card-title { mat-card-title {
font-size: 1.5rem; font-size: 1.5rem;
} }
mat-card-footer p { mat-card-footer p {
font-size: 0.8rem; font-size: 0.8rem;
color: #4a4a4a; color: #4a4a4a;
margin-bottom: 0; margin-bottom: 0;
text-align: end; text-align: end;
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
color: #999999 color: #999999;
} }
} }
mat-card-content p { mat-card-content p {
white-space: pre-line; white-space: pre-line;
} }
mat-card p { mat-card p {
margin: 15px; margin: 15px;
} }
button { button {
margin-right: 4pt; margin-right: 4pt;
} }
:host { :host {
padding: 8pt; padding: 8pt;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
} }

View File

@@ -1,36 +1,34 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NewsEditComponent } from './news-edit.component'; import { NewsEditComponent } from './news-edit.component'
import { AdminCommService } from '../admin-comm.service'; import { AdminCommService } from '../admin-comm.service'
import { MatDialogModule } from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog'
import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatSnackBarModule } from '@angular/material/snack-bar'
import { of } from 'rxjs'; import { of } from 'rxjs'
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card'
describe('NewsEditComponent', () => { describe('NewsEditComponent', () => {
let component: NewsEditComponent; let component: NewsEditComponent
let fixture: ComponentFixture<NewsEditComponent>; let fixture: ComponentFixture<NewsEditComponent>
let acMock let acMock
beforeEach(() => { beforeEach(() => {
acMock = { acMock = {
news: { news: {
getNews: jasmine.createSpy('getNews').and.returnValue(of([])) getNews: jasmine.createSpy('getNews').and.returnValue(of([])),
} },
} }
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [NewsEditComponent], declarations: [NewsEditComponent],
providers: [ providers: [{ provide: AdminCommService, useValue: acMock }],
{provide: AdminCommService, useValue: acMock} imports: [MatDialogModule, MatSnackBarModule, MatCardModule],
], })
imports: [MatDialogModule, MatSnackBarModule, MatCardModule] fixture = TestBed.createComponent(NewsEditComponent)
}); component = fixture.componentInstance
fixture = TestBed.createComponent(NewsEditComponent); fixture.detectChanges()
component = fixture.componentInstance; })
fixture.detectChanges();
});
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,32 +1,38 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core'
import { AdminCommService } from '../admin-comm.service'; import { AdminCommService } from '../admin-comm.service'
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog'
import { NewPostComponent } from './new-post/edit-post.component'; import { NewPostComponent } from './new-post/edit-post.component'
import { catchError, throwError } from 'rxjs'; import { catchError, throwError } from 'rxjs'
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar'
import { News } from 'src/app/types/news'; import { News } from 'src/app/types/news'
import { marked } from 'marked'; import { marked } from 'marked'
@Component({ @Component({
selector: 'app-news-edit', selector: 'app-news-edit',
templateUrl: './news-edit.component.html', templateUrl: './news-edit.component.html',
styleUrls: ['./news-edit.component.scss'], styleUrls: ['./news-edit.component.scss'],
standalone: false standalone: false,
}) })
export class NewsEditComponent implements OnInit { export class NewsEditComponent implements OnInit {
news:Array<News & {formatted: string}> = new Array<News & {formatted: string}> news: Array<News & { formatted: string }> = new Array<
News & { formatted: string }
>()
loading = true loading = true
constructor(private ac:AdminCommService, private dialog:MatDialog, private sb:MatSnackBar) {} constructor(
private ac: AdminCommService,
private dialog: MatDialog,
private sb: MatSnackBar
) {}
ngOnInit() { ngOnInit() {
this.loading = true this.loading = true
this.ac.news.getNews().subscribe(data => { this.ac.news.getNews().subscribe(data => {
this.loading = false this.loading = false
this.news = data.map(v => { this.news = data.map(v => {
var nd: News & {formatted: string} = { var nd: News & { formatted: string } = {
...v, ...v,
formatted: marked.parse(v.content, {breaks: true}).toString() formatted: marked.parse(v.content, { breaks: true }).toString(),
} }
return nd return nd
}) })
@@ -34,35 +40,51 @@ export class NewsEditComponent implements OnInit {
} }
newPost() { newPost() {
this.dialog.open(NewPostComponent, {width: "90vw"}).afterClosed().subscribe(result=> { this.dialog
if (result == undefined) return .open(NewPostComponent, { width: '90vw' })
this.ac.news.postNews(result.title, result.content).pipe(catchError((err)=>{ .afterClosed()
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.") .subscribe(result => {
return throwError(() => new Error(err.message)) if (result == undefined) return
})).subscribe((data)=>{ this.ac.news
if (data.status == 201) { .postNews(result.title, result.content)
this.ngOnInit() .pipe(
} else { catchError(err => {
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.") this.sb.open('Wystąpił błąd. Skontaktuj się z obsługą programu.')
} return throwError(() => new Error(err.message))
})
)
.subscribe(data => {
if (data.status == 201) {
this.ngOnInit()
} else {
this.sb.open('Wystąpił błąd. Skontaktuj się z obsługą programu.')
}
})
}) })
})
} }
editPost(item: any) { editPost(item: any) {
this.dialog.open(NewPostComponent, {data: item, width: "90vh"}).afterClosed().subscribe(result=>{ this.dialog
if (result == undefined) return .open(NewPostComponent, { data: item, width: '90vh' })
this.ac.news.updateNews(item._id, result.title, result.content).pipe(catchError((err)=>{ .afterClosed()
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.") .subscribe(result => {
return throwError(()=> new Error(err.message)) if (result == undefined) return
})).subscribe((data)=> { this.ac.news
if (data.status == 200) { .updateNews(item._id, result.title, result.content)
this.ngOnInit() .pipe(
} else { catchError(err => {
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.") this.sb.open('Wystąpił błąd. Skontaktuj się z obsługą programu.')
} return throwError(() => new Error(err.message))
})
)
.subscribe(data => {
if (data.status == 200) {
this.ngOnInit()
} else {
this.sb.open('Wystąpił błąd. Skontaktuj się z obsługą programu.')
}
})
}) })
})
} }
delete(id: string) { delete(id: string) {
@@ -74,29 +96,39 @@ export class NewsEditComponent implements OnInit {
} }
visibleToggle(item: any) { visibleToggle(item: any) {
this.ac.news.toggleNews(item._id, item.visible).pipe(catchError((err)=>{ this.ac.news
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.") .toggleNews(item._id, item.visible)
return throwError(()=> new Error(err.message)) .pipe(
})).subscribe((data)=> { catchError(err => {
if (data.status == 200) { this.sb.open('Wystąpił błąd. Skontaktuj się z obsługą programu.')
this.ngOnInit() return throwError(() => new Error(err.message))
} else { })
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.") )
} .subscribe(data => {
}) if (data.status == 200) {
this.ngOnInit()
} else {
this.sb.open('Wystąpił błąd. Skontaktuj się z obsługą programu.')
}
})
} }
pinToggle(item:any) { pinToggle(item: any) {
console.log(item.pinned) console.log(item.pinned)
this.ac.news.togglePin(item._id, item.pinned).pipe(catchError((err)=>{ this.ac.news
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.") .togglePin(item._id, item.pinned)
return throwError(()=> new Error(err.message)) .pipe(
})).subscribe((data)=> { catchError(err => {
if (data.status == 200) { this.sb.open('Wystąpił błąd. Skontaktuj się z obsługą programu.')
this.ngOnInit() return throwError(() => new Error(err.message))
} else { })
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.") )
} .subscribe(data => {
}) if (data.status == 200) {
this.ngOnInit()
} else {
this.sb.open('Wystąpił błąd. Skontaktuj się z obsługą programu.')
}
})
} }
} }

View File

@@ -1,3 +1,3 @@
mat-radio-button { mat-radio-button {
display: block; display: block;
} }

View File

@@ -1,44 +1,60 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NotificationsComponent } from './notifications.component'; import { NotificationsComponent } from './notifications.component'
import { AdminCommService } from '../admin-comm.service'; import { AdminCommService } from '../admin-comm.service'
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router'
import { MatRadioModule } from '@angular/material/radio'; import { MatRadioModule } from '@angular/material/radio'
import { MatFormFieldControl, MatFormFieldModule } from '@angular/material/form-field'; import {
import { Component, forwardRef } from '@angular/core'; MatFormFieldControl,
import { MatIconModule } from '@angular/material/icon'; MatFormFieldModule,
import { Observable, of } from 'rxjs'; } from '@angular/material/form-field'
import { AbstractControlDirective, ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR, NgControl, ReactiveFormsModule } from '@angular/forms'; import { Component, forwardRef } from '@angular/core'
import { MatInputModule } from '@angular/material/input'; import { MatIconModule } from '@angular/material/icon'
import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { Observable, of } from 'rxjs'
import {
AbstractControlDirective,
ControlValueAccessor,
FormsModule,
NG_VALUE_ACCESSOR,
NgControl,
ReactiveFormsModule,
} from '@angular/forms'
import { MatInputModule } from '@angular/material/input'
import { NoopAnimationsModule } from '@angular/platform-browser/animations'
@Component({ @Component({
selector: "app-user-search", template: '', providers: [{ selector: 'app-user-search',
provide: NG_VALUE_ACCESSOR, template: '',
useExisting: forwardRef(() => UserSearchStub), providers: [
multi: true, {
}, provide: NG_VALUE_ACCESSOR,
{ useExisting: forwardRef(() => UserSearchStub),
provide: MatFormFieldControl, multi: true,
useExisting: UserSearchStub },
}], {
standalone: false provide: MatFormFieldControl,
useExisting: UserSearchStub,
},
],
standalone: false,
}) })
class UserSearchStub implements ControlValueAccessor, MatFormFieldControl<never> { class UserSearchStub
value: null = null; implements ControlValueAccessor, MatFormFieldControl<never>
stateChanges: Observable<void> = of(); {
id: string = ""; value: null = null
placeholder: string = ""; stateChanges: Observable<void> = of()
ngControl: NgControl | AbstractControlDirective | null = null; id: string = ''
focused: boolean = false; placeholder: string = ''
empty: boolean = true; ngControl: NgControl | AbstractControlDirective | null = null
shouldLabelFloat: boolean = true; focused: boolean = false
required: boolean = false; empty: boolean = true
disabled: boolean = false; shouldLabelFloat: boolean = true
errorState: boolean = false; required: boolean = false
controlType?: string | undefined; disabled: boolean = false
autofilled?: boolean | undefined; errorState: boolean = false
userAriaDescribedBy?: string | undefined; controlType?: string | undefined
autofilled?: boolean | undefined
userAriaDescribedBy?: string | undefined
setDescribedByIds(ids: string[]): void {} setDescribedByIds(ids: string[]): void {}
onContainerClick(event: MouseEvent): void {} onContainerClick(event: MouseEvent): void {}
writeValue(obj: any): void {} writeValue(obj: any): void {}
@@ -48,20 +64,18 @@ class UserSearchStub implements ControlValueAccessor, MatFormFieldControl<never>
} }
describe('NotificationsComponent', () => { describe('NotificationsComponent', () => {
let component: NotificationsComponent; let component: NotificationsComponent
let fixture: ComponentFixture<NotificationsComponent>; let fixture: ComponentFixture<NotificationsComponent>
beforeEach(() => { beforeEach(() => {
const acMock = { const acMock = {
notif: { notif: {
getGroups: jasmine.createSpy("getGroups").and.returnValue(of()) getGroups: jasmine.createSpy('getGroups').and.returnValue(of()),
} },
} }
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [NotificationsComponent, UserSearchStub], declarations: [NotificationsComponent, UserSearchStub],
providers: [ providers: [{ provide: AdminCommService, useValue: acMock }],
{provide: AdminCommService, useValue: acMock}
],
imports: [ imports: [
RouterModule.forRoot([]), RouterModule.forRoot([]),
MatRadioModule, MatRadioModule,
@@ -70,15 +84,15 @@ describe('NotificationsComponent', () => {
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
MatInputModule, MatInputModule,
NoopAnimationsModule NoopAnimationsModule,
] ],
}); })
fixture = TestBed.createComponent(NotificationsComponent); fixture = TestBed.createComponent(NotificationsComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,46 +1,52 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core'
import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; import { FormBuilder, FormControl, FormGroup } from '@angular/forms'
import { AdminCommService } from '../admin-comm.service'; import { AdminCommService } from '../admin-comm.service'
import { Notification } from 'src/app/types/notification'; import { Notification } from 'src/app/types/notification'
import { Group } from 'src/app/types/group'; import { Group } from 'src/app/types/group'
import { LocalStorageService } from 'src/app/services/local-storage.service'; import { LocalStorageService } from 'src/app/services/local-storage.service'
import { ToolbarService } from '../toolbar/toolbar.service'; import { ToolbarService } from '../toolbar/toolbar.service'
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router'
import { UserSearchResult } from 'src/app/commonComponents/user-search/user-search.component'; import { UserSearchResult } from 'src/app/commonComponents/user-search/user-search.component'
@Component({ @Component({
selector: 'app-notifications', selector: 'app-notifications',
templateUrl: './notifications.component.html', templateUrl: './notifications.component.html',
styleUrls: ['./notifications.component.scss'], styleUrls: ['./notifications.component.scss'],
standalone: false standalone: false,
}) })
export class NotificationsComponent implements OnInit, OnDestroy { export class NotificationsComponent implements OnInit, OnDestroy {
groups!: Group[] groups!: Group[]
form = this.fb.group({ form = this.fb.group({
recp: this.fb.group({ recp: this.fb.group({
uid: this.fb.control<UserSearchResult | null>(null), uid: this.fb.control<UserSearchResult | null>(null),
room: this.fb.control<string|null>(null), room: this.fb.control<string | null>(null),
group: this.fb.control<string>(''), group: this.fb.control<string>(''),
type: this.fb.control<"room" | "uname" | "group">('uname', {nonNullable: true}) type: this.fb.control<'room' | 'uname' | 'group'>('uname', {
nonNullable: true,
}),
}), }),
title: this.fb.control('', {nonNullable: true}), title: this.fb.control('', { nonNullable: true }),
body: this.fb.control('', {nonNullable: true}) body: this.fb.control('', { nonNullable: true }),
}) })
constructor (private readonly acs: AdminCommService, readonly ls: LocalStorageService, private toolbar: ToolbarService, private router: Router, private route: ActivatedRoute, private fb: FormBuilder ) { constructor(
private readonly acs: AdminCommService,
readonly ls: LocalStorageService,
private toolbar: ToolbarService,
private router: Router,
private route: ActivatedRoute,
private fb: FormBuilder
) {
this.toolbar.comp = this this.toolbar.comp = this
this.toolbar.menu = [ this.toolbar.menu = [{ title: 'Wysłane', fn: 'outbox', icon: 'outbox' }]
{ title: "Wysłane", fn: "outbox", icon: "outbox" }
]
} }
outbox() { outbox() {
this.router.navigate(["outbox"], { relativeTo: this.route }) this.router.navigate(['outbox'], { relativeTo: this.route })
} }
ngOnInit(): void { ngOnInit(): void {
this.acs.notif.getGroups().subscribe((v) => { this.acs.notif.getGroups().subscribe(v => {
this.groups = v this.groups = v
}) })
} }
@@ -50,16 +56,21 @@ export class NotificationsComponent implements OnInit, OnDestroy {
this.toolbar.menu = undefined this.toolbar.menu = undefined
} }
public inbox() { public inbox() {}
}
success?: { sent: number; possible: number; };
success?: { sent: number; possible: number }
submit() { submit() {
this.acs.notif.send({...this.form.value, recp: {...this.form.get("recp")?.value, uid: this.form.controls['recp'].controls['uid'].value?._id}} as Notification).subscribe((data) => { this.acs.notif
this.success = data .send({
}) ...this.form.value,
recp: {
...this.form.get('recp')?.value,
uid: this.form.controls['recp'].controls['uid'].value?._id,
},
} as Notification)
.subscribe(data => {
this.success = data
})
} }
} }

View File

@@ -1,3 +1,3 @@
mat-card-title { mat-card-title {
font-size: 24pt; font-size: 24pt;
} }

View File

@@ -1,35 +1,28 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { MessageComponent } from './message.component'; import { MessageComponent } from './message.component'
import { AdminCommService } from 'src/app/admin-view/admin-comm.service'; import { AdminCommService } from 'src/app/admin-view/admin-comm.service'
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card'
import { DateTime } from 'luxon'; import { DateTime } from 'luxon'
describe('MessageComponent', () => { describe('MessageComponent', () => {
let component: MessageComponent; let component: MessageComponent
let fixture: ComponentFixture<MessageComponent>; let fixture: ComponentFixture<MessageComponent>
beforeEach(async () => { beforeEach(async () => {
const acMock = { const acMock = {}
}
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [MessageComponent], declarations: [MessageComponent],
providers: [ providers: [{ provide: AdminCommService, useValue: acMock }],
{provide: AdminCommService, useValue: acMock} imports: [MatCardModule],
], }).compileComponents()
imports: [
MatCardModule fixture = TestBed.createComponent(MessageComponent)
] component = fixture.componentInstance
}) component.item = { _id: 'test', sentDate: DateTime.now(), title: 'Test' }
.compileComponents(); fixture.detectChanges()
})
fixture = TestBed.createComponent(MessageComponent);
component = fixture.componentInstance;
component.item = {_id: "test", sentDate: DateTime.now(), title: "Test"}
fixture.detectChanges();
});
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,19 +1,25 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core'
import { DateTime } from 'luxon'; import { DateTime } from 'luxon'
import { AdminCommService } from 'src/app/admin-view/admin-comm.service'; import { AdminCommService } from 'src/app/admin-view/admin-comm.service'
@Component({ @Component({
selector: 'app-message', selector: 'app-message',
templateUrl: './message.component.html', templateUrl: './message.component.html',
styleUrl: './message.component.scss', styleUrl: './message.component.scss',
standalone: false standalone: false,
}) })
export class MessageComponent { export class MessageComponent {
@Input() item!: {_id: string, sentDate: DateTime, title: string} @Input() item!: { _id: string; sentDate: DateTime; title: string }
body?: string body?: string
rcpts?: {_id: string, uname: string, room?: string, fname?: string, surname?: string}[] rcpts?: {
_id: string
uname: string
room?: string
fname?: string
surname?: string
}[]
loading: boolean = false loading: boolean = false
constructor (readonly acu: AdminCommService) {} constructor(readonly acu: AdminCommService) {}
getMessage() { getMessage() {
this.loading = true this.loading = true

View File

@@ -1,6 +1,6 @@
.cardContainer { .cardContainer {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 1ch; gap: 1ch;
margin: 1ch; margin: 1ch;
} }

View File

@@ -1,39 +1,34 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { OutboxComponent } from './outbox.component'; import { OutboxComponent } from './outbox.component'
import { AdminCommService } from '../../admin-comm.service'; import { AdminCommService } from '../../admin-comm.service'
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router'
import { of } from 'rxjs'; import { of } from 'rxjs'
describe('OutboxComponent', () => { describe('OutboxComponent', () => {
let component: OutboxComponent; let component: OutboxComponent
let fixture: ComponentFixture<OutboxComponent>; let fixture: ComponentFixture<OutboxComponent>
beforeEach(async () => { beforeEach(async () => {
const acMock = { const acMock = {
notif: { notif: {
outbox: { outbox: {
getSent: jasmine.createSpy("getSent").and.returnValue(of()) getSent: jasmine.createSpy('getSent').and.returnValue(of()),
} },
} },
} }
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [OutboxComponent], declarations: [OutboxComponent],
providers: [ providers: [{ provide: AdminCommService, useValue: acMock }],
{provide: AdminCommService, useValue: acMock} imports: [RouterModule.forRoot([])],
], }).compileComponents()
imports: [
RouterModule.forRoot([]) fixture = TestBed.createComponent(OutboxComponent)
] component = fixture.componentInstance
}) fixture.detectChanges()
.compileComponents(); })
fixture = TestBed.createComponent(OutboxComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,38 +1,41 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core'
import { AdminCommService } from '../../admin-comm.service'; import { AdminCommService } from '../../admin-comm.service'
import { Router, ActivatedRoute } from '@angular/router'; import { Router, ActivatedRoute } from '@angular/router'
import { ToolbarService } from '../../toolbar/toolbar.service'; import { ToolbarService } from '../../toolbar/toolbar.service'
import { DateTime } from 'luxon'; import { DateTime } from 'luxon'
@Component({ @Component({
selector: 'app-outbox', selector: 'app-outbox',
templateUrl: './outbox.component.html', templateUrl: './outbox.component.html',
styleUrl: './outbox.component.scss', styleUrl: './outbox.component.scss',
standalone: false standalone: false,
}) })
export class OutboxComponent implements OnInit { export class OutboxComponent implements OnInit {
messages!: { messages!: {
_id: string; _id: string
sentDate: DateTime; sentDate: DateTime
title: string; title: string
}[] }[]
constructor (private readonly acs: AdminCommService, private toolbar: ToolbarService, private router: Router, private route: ActivatedRoute ) { constructor(
private readonly acs: AdminCommService,
private toolbar: ToolbarService,
private router: Router,
private route: ActivatedRoute
) {
this.toolbar.comp = this this.toolbar.comp = this
this.toolbar.menu = [ this.toolbar.menu = [
{ title: "Powiadomienia", fn: "goBack", icon: "arrow_back" } { title: 'Powiadomienia', fn: 'goBack', icon: 'arrow_back' },
] ]
} }
goBack() { goBack() {
this.router.navigate(['../'], {relativeTo: this.route}) this.router.navigate(['../'], { relativeTo: this.route })
} }
ngOnInit(): void { ngOnInit(): void {
this.acs.notif.outbox.getSent().subscribe((v) => { this.acs.notif.outbox.getSent().subscribe(v => {
this.messages = v this.messages = v
}) })
} }
} }

View File

@@ -1,4 +1,4 @@
:host { :host {
margin: 15px; margin: 15px;
display: block; display: block;
} }

View File

@@ -1,42 +1,40 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { SettingsComponent } from './settings.component'; import { SettingsComponent } from './settings.component'
import { AdminCommService } from '../admin-comm.service'; import { AdminCommService } from '../admin-comm.service'
import { MatExpansionModule } from '@angular/material/expansion'; import { MatExpansionModule } from '@angular/material/expansion'
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core'
import { MatTabsModule } from '@angular/material/tabs'; import { MatTabsModule } from '@angular/material/tabs'
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon'
import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'
import { of } from 'rxjs'; import { of } from 'rxjs'
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input'
@Component({ @Component({
selector: 'app-list-editor', template: '', selector: 'app-list-editor',
standalone: false template: '',
standalone: false,
}) })
class ListEditorStub { class ListEditorStub {
@Input() converter?: any[]; @Input() converter?: any[]
@Input() list?: string[]; @Input() list?: string[]
} }
describe('SettingsComponent', () => { describe('SettingsComponent', () => {
let component: SettingsComponent; let component: SettingsComponent
let fixture: ComponentFixture<SettingsComponent>; let fixture: ComponentFixture<SettingsComponent>
beforeEach(async () => { beforeEach(async () => {
const acMock = { const acMock = {
settings: { settings: {
getAll: jasmine.createSpy("getAll").and.returnValue(of()) getAll: jasmine.createSpy('getAll').and.returnValue(of()),
} },
} }
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [SettingsComponent, ListEditorStub], declarations: [SettingsComponent, ListEditorStub],
providers: [ providers: [{ provide: AdminCommService, useValue: acMock }],
{provide: AdminCommService, useValue: acMock}
],
imports: [ imports: [
MatExpansionModule, MatExpansionModule,
MatTabsModule, MatTabsModule,
@@ -45,17 +43,16 @@ describe('SettingsComponent', () => {
ReactiveFormsModule, ReactiveFormsModule,
MatIconModule, MatIconModule,
NoopAnimationsModule, NoopAnimationsModule,
MatInputModule MatInputModule,
] ],
}) }).compileComponents()
.compileComponents();
fixture = TestBed.createComponent(SettingsComponent)
fixture = TestBed.createComponent(SettingsComponent); component = fixture.componentInstance
component = fixture.componentInstance; fixture.detectChanges()
fixture.detectChanges(); })
});
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,28 +1,38 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core'
import { AdminCommService } from '../admin-comm.service'; import { AdminCommService } from '../admin-comm.service'
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar'
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms'
@Component({ @Component({
selector: 'app-settings', selector: 'app-settings',
templateUrl: './settings.component.html', templateUrl: './settings.component.html',
styleUrl: './settings.component.scss', styleUrl: './settings.component.scss',
standalone: false standalone: false,
}) })
export class SettingsComponent implements OnInit { export class SettingsComponent implements OnInit {
usettings: IUSettings = {cleanThings: [], keyrooms: [], menu: {defaultItems: {kol: [], sn: []}}, rooms: [], security: {loginTimeout: {attempts: 0, lockout: 0, time: 0}}} usettings: IUSettings = {
reloadTimeout: boolean = false; cleanThings: [],
keyrooms: [],
menu: { defaultItems: { kol: [], sn: [] } },
rooms: [],
security: { loginTimeout: { attempts: 0, lockout: 0, time: 0 } },
}
reloadTimeout: boolean = false
constructor(
private readonly acu: AdminCommService,
private readonly sb: MatSnackBar,
private readonly fb: FormBuilder
) {}
constructor (private readonly acu: AdminCommService, private readonly sb: MatSnackBar, private readonly fb: FormBuilder) { }
accSec = this.fb.nonNullable.group({ accSec = this.fb.nonNullable.group({
attempts: this.fb.nonNullable.control(1), attempts: this.fb.nonNullable.control(1),
time: this.fb.nonNullable.control(1), time: this.fb.nonNullable.control(1),
lockout: this.fb.nonNullable.control(1), lockout: this.fb.nonNullable.control(1),
}) })
ngOnInit(): void { ngOnInit(): void {
this.acu.settings.getAll().subscribe((r) => { this.acu.settings.getAll().subscribe(r => {
this.usettings = r this.usettings = r
this.accSecTimeouts = r.security.loginTimeout this.accSecTimeouts = r.security.loginTimeout
}) })
@@ -59,23 +69,23 @@ export class SettingsComponent implements OnInit {
this.accSec.setValue({ this.accSec.setValue({
attempts: value.attempts, attempts: value.attempts,
lockout: value.lockout / 60, lockout: value.lockout / 60,
time: value.time / 60 time: value.time / 60,
}) })
} }
get accSecTimeouts(): IUSettings['security']['loginTimeout'] { get accSecTimeouts(): IUSettings['security']['loginTimeout'] {
return { return {
attempts: this.accSec.controls['attempts'].value, attempts: this.accSec.controls['attempts'].value,
lockout: this.accSec.controls['lockout'].value * 60, lockout: this.accSec.controls['lockout'].value * 60,
time: this.accSec.controls['time'].value * 60 time: this.accSec.controls['time'].value * 60,
} }
} }
send() { send() {
this.acu.settings.post(this.usettings).subscribe((s) => { this.acu.settings.post(this.usettings).subscribe(s => {
if (s.status == 200) { if (s.status == 200) {
this.sb.open("Zapisano!", undefined, { duration: 1000 }) this.sb.open('Zapisano!', undefined, { duration: 1000 })
} else { } else {
console.error(s); console.error(s)
} }
}) })
} }
@@ -87,32 +97,32 @@ export class SettingsComponent implements OnInit {
this.reloadTimeout = true this.reloadTimeout = true
setTimeout(() => { setTimeout(() => {
this.reloadTimeout = false this.reloadTimeout = false
}, 5000); }, 5000)
this.acu.settings.reload().subscribe((s) => { this.acu.settings.reload().subscribe(s => {
if (s.status == 200) { if (s.status == 200) {
this.sb.open("Przeładowano ustawienia!", undefined, { duration: 3000 }) this.sb.open('Przeładowano ustawienia!', undefined, { duration: 3000 })
} else { } else {
console.error(s); console.error(s)
} }
}) })
} }
} }
export interface IUSettings { export interface IUSettings {
keyrooms: string[]; keyrooms: string[]
rooms: string[]; rooms: string[]
cleanThings: string[]; cleanThings: string[]
menu: { menu: {
defaultItems: { defaultItems: {
sn: string[]; sn: string[]
kol: string[]; kol: string[]
}
};
security: {
loginTimeout: {
attempts: number;
time: number;
lockout: number;
} }
} }
} security: {
loginTimeout: {
attempts: number
time: number
lockout: number
}
}
}

View File

@@ -1,13 +1,13 @@
.main { .main {
margin-top: 8px; margin-top: 8px;
margin-left: 16px; margin-left: 16px;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 1ch; gap: 1ch;
} }
.icon { .icon {
width: fit-content; width: fit-content;
height: fit-content; height: fit-content;
font-size: 32pt; font-size: 32pt;
} }

View File

@@ -1,27 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { StartAdminComponent } from './start.component'; import { StartAdminComponent } from './start.component'
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon'
describe('StartAdminComponent', () => { describe('StartAdminComponent', () => {
let component: StartAdminComponent; let component: StartAdminComponent
let fixture: ComponentFixture<StartAdminComponent>; let fixture: ComponentFixture<StartAdminComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [StartAdminComponent], declarations: [StartAdminComponent],
imports: [ imports: [MatIconModule],
MatIconModule }).compileComponents()
]
}) fixture = TestBed.createComponent(StartAdminComponent)
.compileComponents(); component = fixture.componentInstance
fixture.detectChanges()
fixture = TestBed.createComponent(StartAdminComponent); })
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,11 +1,9 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core'
@Component({ @Component({
selector: 'app-start', selector: 'app-start',
templateUrl: './start.component.html', templateUrl: './start.component.html',
styleUrl: './start.component.scss', styleUrl: './start.component.scss',
standalone: false standalone: false,
}) })
export class StartAdminComponent { export class StartAdminComponent {}
}

View File

@@ -1,27 +1,26 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { ToolbarComponent } from './toolbar.component'; import { ToolbarComponent } from './toolbar.component'
import { MatToolbarModule } from '@angular/material/toolbar'; import { MatToolbarModule } from '@angular/material/toolbar'
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon'
import { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu'
describe('ToolbarComponent', () => { describe('ToolbarComponent', () => {
let component: ToolbarComponent; let component: ToolbarComponent
let fixture: ComponentFixture<ToolbarComponent>; let fixture: ComponentFixture<ToolbarComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ToolbarComponent], declarations: [ToolbarComponent],
imports: [MatToolbarModule, MatIconModule, MatMenuModule] imports: [MatToolbarModule, MatIconModule, MatMenuModule],
}) }).compileComponents()
.compileComponents();
fixture = TestBed.createComponent(ToolbarComponent)
fixture = TestBed.createComponent(ToolbarComponent); component = fixture.componentInstance
component = fixture.componentInstance; fixture.detectChanges()
fixture.detectChanges(); })
});
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,26 +1,27 @@
import { Component, Input, ViewChild } from '@angular/core'; import { Component, Input, ViewChild } from '@angular/core'
import { MatDrawer } from '@angular/material/sidenav'; import { MatDrawer } from '@angular/material/sidenav'
import { Title } from '@angular/platform-browser'; import { Title } from '@angular/platform-browser'
import { ToolbarService } from './toolbar.service'; import { ToolbarService } from './toolbar.service'
import { MatMenuTrigger } from '@angular/material/menu'; import { MatMenuTrigger } from '@angular/material/menu'
@Component({ @Component({
selector: 'app-toolbar', selector: 'app-toolbar',
templateUrl: './toolbar.component.html', templateUrl: './toolbar.component.html',
styleUrl: './toolbar.component.scss', styleUrl: './toolbar.component.scss',
standalone: false standalone: false,
}) })
export class ToolbarComponent { export class ToolbarComponent {
@Input() drawer!: MatDrawer; @Input() drawer!: MatDrawer
@ViewChild(MatMenuTrigger) trigger!: MatMenuTrigger; @ViewChild(MatMenuTrigger) trigger!: MatMenuTrigger
protected _menu?: typeof this.toolbar.menu protected _menu?: typeof this.toolbar.menu
constructor(readonly title: Title, protected toolbar: ToolbarService) { constructor(
readonly title: Title,
} protected toolbar: ToolbarService
) {}
openMenu () { openMenu() {
this._menu = this.toolbar.menu this._menu = this.toolbar.menu
this.trigger.openMenu() this.trigger.openMenu()
} }

View File

@@ -1,16 +1,16 @@
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing'
import { ToolbarService } from './toolbar.service'; import { ToolbarService } from './toolbar.service'
describe('ToolbarService', () => { describe('ToolbarService', () => {
let service: ToolbarService; let service: ToolbarService
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({}); TestBed.configureTestingModule({})
service = TestBed.inject(ToolbarService); service = TestBed.inject(ToolbarService)
}); })
it('should be created', () => { it('should be created', () => {
expect(service).toBeTruthy(); expect(service).toBeTruthy()
}); })
}); })

View File

@@ -1,11 +1,9 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core'
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root',
}) })
export class ToolbarService { export class ToolbarService {
public comp?: any
public comp?: any; public menu?: { title: string; check?: boolean; icon?: string; fn: string }[]
public menu?: {title: string, check?: boolean, icon?: string, fn: string}[]
} }

View File

@@ -1,17 +1,17 @@
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing'
import { CanActivateChildFn } from '@angular/router'; import { CanActivateChildFn } from '@angular/router'
import { adminGuard } from './admin.guard'; import { adminGuard } from './admin.guard'
describe('adminGuard', () => { describe('adminGuard', () => {
const executeGuard: CanActivateChildFn = (...guardParameters) => const executeGuard: CanActivateChildFn = (...guardParameters) =>
TestBed.runInInjectionContext(() => adminGuard(...guardParameters)); TestBed.runInInjectionContext(() => adminGuard(...guardParameters))
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({}); TestBed.configureTestingModule({})
}); })
it('should be created', () => { it('should be created', () => {
expect(executeGuard).toBeTruthy(); expect(executeGuard).toBeTruthy()
}); })
}); })

View File

@@ -1,9 +1,10 @@
import { inject } from '@angular/core'; import { inject } from '@angular/core'
import { CanActivateChildFn, RedirectCommand, Router } from '@angular/router'; import { CanActivateChildFn, RedirectCommand, Router } from '@angular/router'
import { LocalStorageService } from './services/local-storage.service'; import { LocalStorageService } from './services/local-storage.service'
export const adminGuard: CanActivateChildFn = (childRoute, state) => { export const adminGuard: CanActivateChildFn = (childRoute, state) => {
const router = inject(Router) const router = inject(Router)
if (inject(LocalStorageService).admin == undefined) return new RedirectCommand(router.parseUrl('/')) if (inject(LocalStorageService).admin == undefined)
return new RedirectCommand(router.parseUrl('/'))
return true return true
}; }

View File

@@ -1,58 +1,106 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core'
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router'
import { NewsComponent } from './app-view/news/news.component'; import { NewsComponent } from './app-view/news/news.component'
import { MenuComponent } from './app-view/menu/menu.component'; import { MenuComponent } from './app-view/menu/menu.component'
import { AppViewComponent } from './app-view/app-view.component'; import { AppViewComponent } from './app-view/app-view.component'
import { LoginComponent } from './login/login.component'; import { LoginComponent } from './login/login.component'
import { authGuard } from './auth.guard'; import { authGuard } from './auth.guard'
import { PersonalComponent } from './app-view/personal/personal.component'; import { PersonalComponent } from './app-view/personal/personal.component'
import { AdminViewComponent } from './admin-view/admin-view.component'; import { AdminViewComponent } from './admin-view/admin-view.component'
import { NewsEditComponent } from './admin-view/news-edit/news-edit.component'; import { NewsEditComponent } from './admin-view/news-edit/news-edit.component'
import { AccountMgmtComponent } from './admin-view/account-mgmt/account-mgmt.component'; import { AccountMgmtComponent } from './admin-view/account-mgmt/account-mgmt.component'
import { MenuNewComponent } from './admin-view/menu-new/menu-new.component'; import { MenuNewComponent } from './admin-view/menu-new/menu-new.component'
import { adminGuard } from './admin.guard'; import { adminGuard } from './admin.guard'
import { GroupsComponent } from './admin-view/groups/groups.component'; import { GroupsComponent } from './admin-view/groups/groups.component'
import { StartComponent } from './app-view/start/start.component'; import { StartComponent } from './app-view/start/start.component'
import { AdminKeyComponent } from './admin-view/key/key.component'; import { AdminKeyComponent } from './admin-view/key/key.component'
import { GradesComponent } from './admin-view/grades/grades.component'; import { GradesComponent } from './admin-view/grades/grades.component'
import { SummaryComponent } from './admin-view/grades/summary/summary.component'; import { SummaryComponent } from './admin-view/grades/summary/summary.component'
import { SettingsComponent } from './admin-view/settings/settings.component'; import { SettingsComponent } from './admin-view/settings/settings.component'
import { AttendenceSummaryComponent } from './admin-view/grades/attendence-summary/attendence-summary.component'; import { AttendenceSummaryComponent } from './admin-view/grades/attendence-summary/attendence-summary.component'
import { NotificationsComponent } from './admin-view/notifications/notifications.component'; import { NotificationsComponent } from './admin-view/notifications/notifications.component'
import { OutboxComponent } from './admin-view/notifications/outbox/outbox.component'; import { OutboxComponent } from './admin-view/notifications/outbox/outbox.component'
import { StartAdminComponent } from './admin-view/start/start.component'; import { StartAdminComponent } from './admin-view/start/start.component'
const routes: Routes = [ const routes: Routes = [
{path: "", redirectTo: "login", pathMatch: "full"}, { path: '', redirectTo: 'login', pathMatch: 'full' },
{path: "login", component: LoginComponent}, { path: 'login', component: LoginComponent },
{path: "app", component: AppViewComponent, title: "Internat", canActivateChild: [authGuard], children: [ {
{path: "", component: StartComponent, pathMatch: "full"}, path: 'app',
{path: "news",component: NewsComponent, title: "Wiadomości"}, component: AppViewComponent,
{path: "menu", component: MenuComponent, title: "Jadłospis"}, title: 'Internat',
{path: "grades", component: PersonalComponent, title: "Konto"} canActivateChild: [authGuard],
]}, children: [
{path: "admin", component: AdminViewComponent, title: "Panel administracyjny", canActivateChild: [authGuard, adminGuard], children: [ { path: '', component: StartComponent, pathMatch: 'full' },
{path: "", pathMatch: "full", component: StartAdminComponent}, { path: 'news', component: NewsComponent, title: 'Wiadomości' },
{path: "news", title: "Edytowanie wiadomości", component: NewsEditComponent}, { path: 'menu', component: MenuComponent, title: 'Jadłospis' },
{path: "menu", title: "Edytowanie jadłospisu", component: MenuNewComponent}, { path: 'grades', component: PersonalComponent, title: 'Konto' },
{path: "accounts", title: "Użytkownicy", component: AccountMgmtComponent}, ],
{path: "notifications", children: [ },
{path: "", pathMatch: "full", title: "Powiadomienia", component: NotificationsComponent}, {
{path: "outbox", title: "Wysłane", component: OutboxComponent} path: 'admin',
]}, component: AdminViewComponent,
{path: "groups", title: "Grupy", component: GroupsComponent}, title: 'Panel administracyjny',
{path: "keys", title: "Klucze", component: AdminKeyComponent}, canActivateChild: [authGuard, adminGuard],
{path: "grades", children: [ children: [
{path: "", pathMatch: "full", title: "Oceny", component: GradesComponent}, { path: '', pathMatch: 'full', component: StartAdminComponent },
{path: "summary", title: "Podsumowanie ocen", component: SummaryComponent}, {
{path: "attendenceSummary", title: "Obecność", component: AttendenceSummaryComponent} path: 'news',
]}, title: 'Edytowanie wiadomości',
{path: "settings", title: "Ustawienia", component: SettingsComponent} component: NewsEditComponent,
]} },
]; {
path: 'menu',
title: 'Edytowanie jadłospisu',
component: MenuNewComponent,
},
{
path: 'accounts',
title: 'Użytkownicy',
component: AccountMgmtComponent,
},
{
path: 'notifications',
children: [
{
path: '',
pathMatch: 'full',
title: 'Powiadomienia',
component: NotificationsComponent,
},
{ path: 'outbox', title: 'Wysłane', component: OutboxComponent },
],
},
{ path: 'groups', title: 'Grupy', component: GroupsComponent },
{ path: 'keys', title: 'Klucze', component: AdminKeyComponent },
{
path: 'grades',
children: [
{
path: '',
pathMatch: 'full',
title: 'Oceny',
component: GradesComponent,
},
{
path: 'summary',
title: 'Podsumowanie ocen',
component: SummaryComponent,
},
{
path: 'attendenceSummary',
title: 'Obecność',
component: AttendenceSummaryComponent,
},
],
},
{ path: 'settings', title: 'Ustawienia', component: SettingsComponent },
],
},
]
@NgModule({ @NgModule({
imports: [RouterModule.forRoot(routes)], imports: [RouterModule.forRoot(routes)],
exports: [RouterModule] exports: [RouterModule],
}) })
export class AppRoutingModule { } export class AppRoutingModule {}

View File

@@ -1,29 +1,29 @@
#bot-navigation { #bot-navigation {
width: 100%; width: 100%;
} }
#outlet { #outlet {
width: 100%; width: 100%;
flex: 1 1 auto; flex: 1 1 auto;
overflow-y: auto; overflow-y: auto;
} }
#scrollable { #scrollable {
overflow: auto; overflow: auto;
height: 100%; height: 100%;
position: relative; position: relative;
} }
:host { :host {
width: 100%; width: 100%;
height: 100vh; height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@supports (-webkit-touch-callout: none) { @supports (-webkit-touch-callout: none) {
height: 95vh; height: 95vh;
} }
} }
a { a {
text-decoration-line: none; text-decoration-line: none;
} }

View File

@@ -1,40 +1,40 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { AppViewComponent } from './app-view.component'; import { AppViewComponent } from './app-view.component'
import { AuthClient } from '../services/auth.client'; import { AuthClient } from '../services/auth.client'
import { SwPush } from '@angular/service-worker'; import { SwPush } from '@angular/service-worker'
import { UpdatesService } from '../services/updates.service'; import { UpdatesService } from '../services/updates.service'
import { MatTabsModule } from '@angular/material/tabs'; import { MatTabsModule } from '@angular/material/tabs'
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router'
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon'
import { of } from 'rxjs'; import { of } from 'rxjs'
describe('AppViewComponent', () => { describe('AppViewComponent', () => {
let component: AppViewComponent; let component: AppViewComponent
let fixture: ComponentFixture<AppViewComponent>; let fixture: ComponentFixture<AppViewComponent>
beforeEach(() => { beforeEach(() => {
const authSpy = jasmine.createSpyObj('AuthClient', ['check']) const authSpy = jasmine.createSpyObj('AuthClient', ['check'])
const pushSpy = jasmine.createSpyObj('SwPush', ['requestSubscription']) const pushSpy = jasmine.createSpyObj('SwPush', ['requestSubscription'])
const updatesSpy = jasmine.createSpyObj('UpdatesService', { const updatesSpy = jasmine.createSpyObj('UpdatesService', {
newsCheck: of() newsCheck: of(),
}) })
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [AppViewComponent], declarations: [AppViewComponent],
providers: [ providers: [
{provide: AuthClient, useValue: authSpy}, { provide: AuthClient, useValue: authSpy },
{provide: SwPush, useValue: pushSpy}, { provide: SwPush, useValue: pushSpy },
{provide: UpdatesService, useValue: updatesSpy} { provide: UpdatesService, useValue: updatesSpy },
], ],
imports: [MatTabsModule, RouterModule.forRoot([]), MatIconModule] imports: [MatTabsModule, RouterModule.forRoot([]), MatIconModule],
}); })
fixture = TestBed.createComponent(AppViewComponent); fixture = TestBed.createComponent(AppViewComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,49 +1,61 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core'
import { AuthClient } from '../services/auth.client'; import { AuthClient } from '../services/auth.client'
import { SwPush } from '@angular/service-worker'; import { SwPush } from '@angular/service-worker'
import { UpdatesService } from '../services/updates.service'; import { UpdatesService } from '../services/updates.service'
import { Link } from '../types/link'; import { Link } from '../types/link'
import { LocalStorageService } from '../services/local-storage.service'; import { LocalStorageService } from '../services/local-storage.service'
import { interval } from 'rxjs'; import { interval } from 'rxjs'
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar'
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog'
import { NotifDialogComponent } from './notif-dialog/notif-dialog.component'; import { NotifDialogComponent } from './notif-dialog/notif-dialog.component'
@Component({ @Component({
selector: 'app-app-view', selector: 'app-app-view',
templateUrl: './app-view.component.html', templateUrl: './app-view.component.html',
styleUrls: ['./app-view.component.scss'], styleUrls: ['./app-view.component.scss'],
standalone: false standalone: false,
}) })
export class AppViewComponent implements OnInit { export class AppViewComponent implements OnInit {
private readonly _LINKS: Link[] = [ private readonly _LINKS: Link[] = [
{ title: "Jadłospis", href: "menu", icon: "restaurant_menu", enabled: this.ls.capCheck(2) }, {
{ title: "Wiadomości", href: "news", icon: "newspaper", enabled: this.ls.capCheck(1) }, title: 'Jadłospis',
{ title: "Konto", href: "grades", icon: "account_circle", enabled: true } href: 'menu',
]; icon: 'restaurant_menu',
enabled: this.ls.capCheck(2),
},
{
title: 'Wiadomości',
href: 'news',
icon: 'newspaper',
enabled: this.ls.capCheck(1),
},
{ title: 'Konto', href: 'grades', icon: 'account_circle', enabled: true },
]
public get LINKS() { public get LINKS() {
return this._LINKS.filter((v) => { return this._LINKS.filter(v => {
return v.enabled return v.enabled
}); })
} }
constructor ( constructor(
private ac: AuthClient, private ac: AuthClient,
readonly swPush: SwPush, readonly swPush: SwPush,
private us: UpdatesService, private us: UpdatesService,
private ls: LocalStorageService, private ls: LocalStorageService,
private sb: MatSnackBar, private sb: MatSnackBar,
private dialog: MatDialog private dialog: MatDialog
) {} ) {}
subscribeToNotif() { subscribeToNotif() {
if (this.swPush.isEnabled && this.ls.capCheck(4)) { if (this.swPush.isEnabled && this.ls.capCheck(4)) {
this.swPush.requestSubscription({ this.swPush
serverPublicKey: this.ls.vapid .requestSubscription({
}).then(sub => { serverPublicKey: this.ls.vapid,
this.us.postNotif(sub) })
}) .then(sub => {
this.us.postNotif(sub)
})
} }
} }
@@ -56,18 +68,21 @@ export class AppViewComponent implements OnInit {
newsCheck() { newsCheck() {
if (this.ls.capCheck(4)) { if (this.ls.capCheck(4)) {
this.us.getNotifCheck().subscribe((s) => { this.us.getNotifCheck().subscribe(s => {
s.forEach(v => { s.forEach(v => {
this.dialog.open(NotifDialogComponent, {data: v}) this.dialog.open(NotifDialogComponent, { data: v })
}) })
}) })
} }
if (this.ls.newsflag) return; if (this.ls.newsflag) return
this.us.newsCheck().subscribe((s) => { this.us.newsCheck().subscribe(s => {
if (s.hash != this.ls.newsCheck.hash) { if (s.hash != this.ls.newsCheck.hash) {
this.ls.newsflag = this.ls.newsCheck.count - s.count this.ls.newsflag = this.ls.newsCheck.count - s.count
this.ls.newsCheck = s this.ls.newsCheck = s
this.sb.open("Nowe wiadomości", "Zamknij", {duration: 5000, verticalPosition: 'bottom'}) this.sb.open('Nowe wiadomości', 'Zamknij', {
duration: 5000,
verticalPosition: 'bottom',
})
} }
}) })
} }

View File

@@ -1,3 +1,3 @@
ol>li::marker { ol > li::marker {
font-weight: bold; font-weight: bold;
} }

View File

@@ -1,21 +1,21 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { AllergensComponent } from './allergens.component'; import { AllergensComponent } from './allergens.component'
describe('AllergensComponent', () => { describe('AllergensComponent', () => {
let component: AllergensComponent; let component: AllergensComponent
let fixture: ComponentFixture<AllergensComponent>; let fixture: ComponentFixture<AllergensComponent>
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [AllergensComponent] declarations: [AllergensComponent],
}); })
fixture = TestBed.createComponent(AllergensComponent); fixture = TestBed.createComponent(AllergensComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,11 +1,9 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core'
@Component({ @Component({
selector: 'app-allergens', selector: 'app-allergens',
templateUrl: './allergens.component.html', templateUrl: './allergens.component.html',
styleUrls: ['./allergens.component.scss'], styleUrls: ['./allergens.component.scss'],
standalone: false standalone: false,
}) })
export class AllergensComponent { export class AllergensComponent {}
}

View File

@@ -1,41 +1,41 @@
:host { :host {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: none; overflow: none;
height: 100%; height: 100%;
} }
#cards { #cards {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex-grow: 1; flex-grow: 1;
overflow: auto; overflow: auto;
} }
#alrg { #alrg {
align-self: center; align-self: center;
} }
mat-spinner { mat-spinner {
align-self: center; align-self: center;
} }
mat-card { mat-card {
margin: 15px; margin: 15px;
padding: 1ch; padding: 1ch;
} }
#no-data { #no-data {
color: #777; color: #777;
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
color: #AAA color: #aaa;
} }
} }
mat-card-title { mat-card-title {
font-size: 1.5rem; font-size: 1.5rem;
} }
app-date-selector { app-date-selector {
width: 100%; width: 100%;
} }

View File

@@ -1,55 +1,54 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { MenuComponent } from './menu.component'; import { MenuComponent } from './menu.component'
import { UpdatesService } from 'src/app/services/updates.service'; import { UpdatesService } from 'src/app/services/updates.service'
import { DateSelectorComponent } from '../../commonComponents/date-selector/date-selector.component'; import { DateSelectorComponent } from '../../commonComponents/date-selector/date-selector.component'
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon'
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field'
import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatDatepickerModule } from '@angular/material/datepicker'
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card'
import { ReactiveFormsModule } from '@angular/forms'; import { ReactiveFormsModule } from '@angular/forms'
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input'
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
import { MatDialogRef } from '@angular/material/dialog'; import { MatDialogRef } from '@angular/material/dialog'
import { MatBottomSheetModule } from '@angular/material/bottom-sheet'; import { MatBottomSheetModule } from '@angular/material/bottom-sheet'
import { of } from 'rxjs'; import { of } from 'rxjs'
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'
import { provideLuxonDateAdapter } from '@angular/material-luxon-adapter'; import { provideLuxonDateAdapter } from '@angular/material-luxon-adapter'
describe('MenuComponent', () => { describe('MenuComponent', () => {
let component: MenuComponent; let component: MenuComponent
let fixture: ComponentFixture<MenuComponent>; let fixture: ComponentFixture<MenuComponent>
beforeEach(async () => { beforeEach(async () => {
const updatesSpy = jasmine.createSpyObj('UpdatesService', { const updatesSpy = jasmine.createSpyObj('UpdatesService', {
getMenu: of() getMenu: of(),
}) })
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ MenuComponent, DateSelectorComponent], declarations: [MenuComponent, DateSelectorComponent],
providers: [ providers: [
{provide: UpdatesService, useValue: updatesSpy}, { provide: UpdatesService, useValue: updatesSpy },
provideLuxonDateAdapter() provideLuxonDateAdapter(),
], ],
imports: [ imports: [
MatIconModule, MatIconModule,
MatFormFieldModule, MatFormFieldModule,
MatDatepickerModule, MatDatepickerModule,
MatCardModule, MatCardModule,
ReactiveFormsModule, ReactiveFormsModule,
MatInputModule, MatInputModule,
BrowserAnimationsModule, BrowserAnimationsModule,
MatBottomSheetModule, MatBottomSheetModule,
MatProgressSpinnerModule MatProgressSpinnerModule,
] ],
}) }).compileComponents()
.compileComponents();
fixture = TestBed.createComponent(MenuComponent); fixture = TestBed.createComponent(MenuComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,42 +1,60 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core'
import { UpdatesService } from '../../services/updates.service'; import { UpdatesService } from '../../services/updates.service'
import { Menu } from '../../types/menu'; import { Menu } from '../../types/menu'
import { MatBottomSheet } from '@angular/material/bottom-sheet'; import { MatBottomSheet } from '@angular/material/bottom-sheet'
import { AllergensComponent } from './allergens/allergens.component'; import { AllergensComponent } from './allergens/allergens.component'
import { weekendFilter } from "../../fd.da"; import { weekendFilter } from '../../fd.da'
import { LocalStorageService } from 'src/app/services/local-storage.service'; import { LocalStorageService } from 'src/app/services/local-storage.service'
import { DateTime } from 'luxon'; import { DateTime } from 'luxon'
@Component({ @Component({
selector: 'app-menu', selector: 'app-menu',
templateUrl: './menu.component.html', templateUrl: './menu.component.html',
styleUrls: ['./menu.component.scss'], styleUrls: ['./menu.component.scss'],
standalone: false standalone: false,
}) })
export class MenuComponent { export class MenuComponent {
constructor(private uc: UpdatesService, readonly bs: MatBottomSheet, readonly ls: LocalStorageService) { constructor(
private uc: UpdatesService,
readonly bs: MatBottomSheet,
readonly ls: LocalStorageService
) {
this._day = DateTime.now().toISODate() this._day = DateTime.now().toISODate()
} }
loading = true loading = true
public filter = weekendFilter public filter = weekendFilter
private _day: string; private _day: string
public get day(): string { public get day(): string {
return this._day; return this._day
} }
public set day(value: string) { public set day(value: string) {
this._day = value; this._day = value
this.updateMenu() this.updateMenu()
} }
menu?: Menu; menu?: Menu
get getsn() { return (this.menu && this.checkIfAnyProperty(this.menu.sn)) ? this.menu.sn : null } get getsn() {
get getob() { return (this.menu && this.checkIfAnyProperty(this.menu.ob)) ? this.menu.ob : null } return this.menu && this.checkIfAnyProperty(this.menu.sn)
get getkol() { return (this.menu && this.menu.kol) ? this.menu.kol : null } ? this.menu.sn
get gettitle() { return (this.menu && this.menu.dayTitle && this.menu.dayTitle != "") ? this.menu.dayTitle : null } : null
}
get getob() {
return this.menu && this.checkIfAnyProperty(this.menu.ob)
? this.menu.ob
: null
}
get getkol() {
return this.menu && this.menu.kol ? this.menu.kol : null
}
get gettitle() {
return this.menu && this.menu.dayTitle && this.menu.dayTitle != ''
? this.menu.dayTitle
: null
}
private checkIfAnyProperty(obj: { [x: string]: string | string[]; }) { private checkIfAnyProperty(obj: { [x: string]: string | string[] }) {
for (let i in obj) { for (let i in obj) {
if (Array.isArray(obj[i])) { if (Array.isArray(obj[i])) {
if (obj[i].length > 0) return true if (obj[i].length > 0) return true
@@ -57,7 +75,7 @@ export class MenuComponent {
this.uc.getMenu(this.day).subscribe(m => { this.uc.getMenu(this.day).subscribe(m => {
this.loading = false this.loading = false
this.menu = m this.menu = m
console.log(m); console.log(m)
}) })
} }
@@ -66,14 +84,14 @@ export class MenuComponent {
} }
protected vegeColor(text: string) { protected vegeColor(text: string) {
if (text.startsWith("V: ")) { if (text.startsWith('V: ')) {
return "#43A047" return '#43A047'
} }
return "inherit" return 'inherit'
} }
vote(type: "ob" | "kol", vote: "-" | "+" | "n") { vote(type: 'ob' | 'kol', vote: '-' | '+' | 'n') {
this.uc.postVote(this.menu!.day.toISO()!, type, vote).subscribe((data) => { this.uc.postVote(this.menu!.day.toISO()!, type, vote).subscribe(data => {
this.updateMenu(true) this.updateMenu(true)
}) })
} }

View File

@@ -1,35 +1,35 @@
:host { :host {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
mat-card { mat-card {
margin: 15px; margin: 15px;
padding: 1ch; padding: 1ch;
} }
mat-spinner { mat-spinner {
align-self: center; align-self: center;
} }
mat-card-title { mat-card-title {
font-size: 1.5rem; font-size: 1.5rem;
} }
mat-card-footer p { mat-card-footer p {
font-size: 0.8rem; font-size: 0.8rem;
color: #4a4a4a; color: #4a4a4a;
margin-bottom: 0; margin-bottom: 0;
text-align: end; text-align: end;
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
color: #999999 color: #999999;
} }
} }
mat-card-content p { mat-card-content p {
white-space: pre-line; white-space: pre-line;
} }
mat-card p { mat-card p {
margin: 15px; margin: 15px;
} }

View File

@@ -1,44 +1,39 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NewsComponent } from './news.component'; import { NewsComponent } from './news.component'
import { UpdatesService } from 'src/app/services/updates.service'; import { UpdatesService } from 'src/app/services/updates.service'
import { of } from 'rxjs'; import { of } from 'rxjs'
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'
import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'
import { LocalStorageService } from 'src/app/services/local-storage.service'; import { LocalStorageService } from 'src/app/services/local-storage.service'
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card'
describe('NewsComponent', () => { describe('NewsComponent', () => {
let component: NewsComponent; let component: NewsComponent
let fixture: ComponentFixture<NewsComponent>; let fixture: ComponentFixture<NewsComponent>
beforeEach(async () => { beforeEach(async () => {
const updatesMock = jasmine.createSpyObj('UpdatesService', { const updatesMock = jasmine.createSpyObj('UpdatesService', {
getNews: of() getNews: of(),
}) })
const lsMock = { const lsMock = {
news: [] news: [],
} }
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ NewsComponent ], declarations: [NewsComponent],
providers: [ providers: [
{provide: UpdatesService, useValue: updatesMock}, { provide: UpdatesService, useValue: updatesMock },
{provide: LocalStorageService, useValue: lsMock} { provide: LocalStorageService, useValue: lsMock },
], ],
imports: [ imports: [MatProgressSpinnerModule, NoopAnimationsModule, MatCardModule],
MatProgressSpinnerModule, }).compileComponents()
NoopAnimationsModule,
MatCardModule
]
})
.compileComponents();
fixture = TestBed.createComponent(NewsComponent); fixture = TestBed.createComponent(NewsComponent)
component = fixture.componentInstance; component = fixture.componentInstance
fixture.detectChanges(); fixture.detectChanges()
}); })
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,19 +1,22 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core'
import { UpdatesService } from '../../services/updates.service'; import { UpdatesService } from '../../services/updates.service'
import { LocalStorageService } from 'src/app/services/local-storage.service'; import { LocalStorageService } from 'src/app/services/local-storage.service'
import { News } from 'src/app/types/news'; import { News } from 'src/app/types/news'
import { marked } from 'marked'; import { marked } from 'marked'
@Component({ @Component({
selector: 'app-news', selector: 'app-news',
templateUrl: './news.component.html', templateUrl: './news.component.html',
styleUrls: ['./news.component.scss'], styleUrls: ['./news.component.scss'],
standalone: false standalone: false,
}) })
export class NewsComponent implements OnInit { export class NewsComponent implements OnInit {
news:Array<News> = new Array<News> news: Array<News> = new Array<News>()
loading = true loading = true
constructor(private newsapi:UpdatesService, private ls: LocalStorageService) { } constructor(
private newsapi: UpdatesService,
private ls: LocalStorageService
) {}
ngOnInit() { ngOnInit() {
this.ls.newsflag = false this.ls.newsflag = false
@@ -22,9 +25,9 @@ export class NewsComponent implements OnInit {
this.newsapi.getNews().subscribe(data => { this.newsapi.getNews().subscribe(data => {
this.loading = false this.loading = false
this.news = data.map(v => { this.news = data.map(v => {
v.content = marked.parse(v.content, {breaks: true}).toString() v.content = marked.parse(v.content, { breaks: true }).toString()
return v return v
}); })
this.ls.news = this.news this.ls.news = this.news
}) })
} }

View File

@@ -1,37 +1,38 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NotifDialogComponent } from './notif-dialog.component'; import { NotifDialogComponent } from './notif-dialog.component'
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import {
import { UpdatesService } from 'src/app/services/updates.service'; MAT_DIALOG_DATA,
import { of } from 'rxjs'; MatDialogModule,
MatDialogRef,
} from '@angular/material/dialog'
import { UpdatesService } from 'src/app/services/updates.service'
import { of } from 'rxjs'
describe('NotifDialogComponent', () => { describe('NotifDialogComponent', () => {
let component: NotifDialogComponent; let component: NotifDialogComponent
let fixture: ComponentFixture<NotifDialogComponent>; let fixture: ComponentFixture<NotifDialogComponent>
beforeEach(async () => { beforeEach(async () => {
const uMock = jasmine.createSpyObj<UpdatesService>("UpdatesService", { const uMock = jasmine.createSpyObj<UpdatesService>('UpdatesService', {
postInfoAck: of() postInfoAck: of(),
}) })
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [NotifDialogComponent], declarations: [NotifDialogComponent],
providers: [ providers: [
{provide: MAT_DIALOG_DATA, useValue: {message: "Test"}}, { provide: MAT_DIALOG_DATA, useValue: { message: 'Test' } },
{provide: MatDialogRef, useValue: {}}, { provide: MatDialogRef, useValue: {} },
{provide: UpdatesService, useValue: uMock} { provide: UpdatesService, useValue: uMock },
], ],
imports: [ imports: [MatDialogModule],
MatDialogModule }).compileComponents()
]
}) fixture = TestBed.createComponent(NotifDialogComponent)
.compileComponents(); component = fixture.componentInstance
fixture.detectChanges()
fixture = TestBed.createComponent(NotifDialogComponent); })
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,30 +1,33 @@
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core'
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'
import { DateTime } from 'luxon'; import { DateTime } from 'luxon'
import { UpdatesService } from 'src/app/services/updates.service'; import { UpdatesService } from 'src/app/services/updates.service'
@Component({ @Component({
selector: 'app-notif-dialog', selector: 'app-notif-dialog',
templateUrl: './notif-dialog.component.html', templateUrl: './notif-dialog.component.html',
styleUrl: './notif-dialog.component.scss', styleUrl: './notif-dialog.component.scss',
standalone: false standalone: false,
}) })
export class NotifDialogComponent { export class NotifDialogComponent {
date: DateTime date: DateTime
constructor ( constructor(
@Inject(MAT_DIALOG_DATA) public data: {_id: string, message: {title: string, body: string}, sentDate: string}, @Inject(MAT_DIALOG_DATA)
public data: {
_id: string
message: { title: string; body: string }
sentDate: string
},
public dialogRef: MatDialogRef<NotifDialogComponent>, public dialogRef: MatDialogRef<NotifDialogComponent>,
private uc: UpdatesService private uc: UpdatesService
) { ) {
this.date = DateTime.fromISO(data.sentDate) this.date = DateTime.fromISO(data.sentDate)
} }
ack () { ack() {
this.uc.postInfoAck(this.data._id).subscribe((v) => { this.uc.postInfoAck(this.data._id).subscribe(v => {
this.dialogRef.close() this.dialogRef.close()
}) })
} }
} }

View File

@@ -1,29 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { AboutComponent } from './about.component'; import { AboutComponent } from './about.component'
import { MatDialogModule } from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog'
import { MatListModule } from '@angular/material/list'; import { MatListModule } from '@angular/material/list'
describe('AboutComponent', () => { describe('AboutComponent', () => {
let component: AboutComponent; let component: AboutComponent
let fixture: ComponentFixture<AboutComponent>; let fixture: ComponentFixture<AboutComponent>
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [AboutComponent], declarations: [AboutComponent],
imports: [ imports: [MatDialogModule, MatListModule],
MatDialogModule, }).compileComponents()
MatListModule
] fixture = TestBed.createComponent(AboutComponent)
}) component = fixture.componentInstance
.compileComponents(); fixture.detectChanges()
})
fixture = TestBed.createComponent(AboutComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,31 +1,31 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core'
import { Link } from 'src/app/types/link'; import { Link } from 'src/app/types/link'
@Component({ @Component({
selector: 'app-about', selector: 'app-about',
templateUrl: './about.component.html', templateUrl: './about.component.html',
styleUrl: './about.component.scss', styleUrl: './about.component.scss',
standalone: false standalone: false,
}) })
export class AboutComponent { export class AboutComponent {
LINKS: { title: string, info: string, icon: string, link: string }[] = [ LINKS: { title: string; info: string; icon: string; link: string }[] = [
{ {
title: "Autor", title: 'Autor',
info: "Jan Szumotalski", info: 'Jan Szumotalski',
icon: "person", icon: 'person',
link: "https://github.com/Slasherss1/" link: 'https://github.com/Slasherss1/',
}, },
{ {
title: 'Źrodło', title: 'Źrodło',
info: 'Aplikacja jest darmowa i może ją uruchomić każdy!', info: 'Aplikacja jest darmowa i może ją uruchomić każdy!',
icon: 'code', icon: 'code',
link: 'https://github.com/Slasherss1/ipwa-selfhosted' link: 'https://github.com/Slasherss1/ipwa-selfhosted',
}, },
{ {
title: "Licencja", title: 'Licencja',
info: 'GPL-3.0', info: 'GPL-3.0',
icon: 'license', icon: 'license',
link: 'https://www.gnu.org/licenses/gpl-3.0-standalone.html' link: 'https://www.gnu.org/licenses/gpl-3.0-standalone.html',
} },
] ]
} }

View File

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

View File

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

View File

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

View File

@@ -1,49 +1,52 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'
import { CleanComponent } from './clean.component'; import { CleanComponent } from './clean.component'
import { UpdatesService } from 'src/app/services/updates.service'; import { UpdatesService } from 'src/app/services/updates.service'
import { of } from 'rxjs'; import { of } from 'rxjs'
import { MatDialogModule } from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog'
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon'
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field'
import { MatDatepicker } from '@angular/material/datepicker'; import { MatDatepicker } from '@angular/material/datepicker'
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Component, EventEmitter, Input, Output } from '@angular/core'
import { DateTime } from 'luxon'; import { DateTime } from 'luxon'
@Component({ @Component({
selector: "app-date-selector", template: '', selector: 'app-date-selector',
standalone: false template: '',
standalone: false,
}) })
class DateSelectorStub { class DateSelectorStub {
@Input() date: string = DateTime.now().toISODate(); @Input() date: string = DateTime.now().toISODate()
@Output() dateChange = new EventEmitter<string>(); @Output() dateChange = new EventEmitter<string>()
@Input() filter: (date: DateTime | null) => boolean = () => true @Input() filter: (date: DateTime | null) => boolean = () => true
} }
describe('CleanComponent', () => { describe('CleanComponent', () => {
let component: CleanComponent; let component: CleanComponent
let fixture: ComponentFixture<CleanComponent>; let fixture: ComponentFixture<CleanComponent>
let updates: jasmine.SpyObj<UpdatesService> let updates: jasmine.SpyObj<UpdatesService>
beforeEach(async () => { beforeEach(async () => {
updates = jasmine.createSpyObj<UpdatesService>("UpdatesService", { updates = jasmine.createSpyObj<UpdatesService>('UpdatesService', {
getClean: of() getClean: of(),
}) })
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [CleanComponent, DateSelectorStub], declarations: [CleanComponent, DateSelectorStub],
providers: [ providers: [{ provide: UpdatesService, useValue: updates }],
{provide: UpdatesService, useValue: updates} imports: [
MatDialogModule,
MatIconModule,
MatFormFieldModule,
MatDatepicker,
], ],
imports: [MatDialogModule, MatIconModule, MatFormFieldModule, MatDatepicker] }).compileComponents()
})
.compileComponents(); fixture = TestBed.createComponent(CleanComponent)
component = fixture.componentInstance
fixture = TestBed.createComponent(CleanComponent); fixture.detectChanges()
component = fixture.componentInstance; })
fixture.detectChanges();
});
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy()
}); })
}); })

View File

@@ -1,32 +1,32 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core'
import { DateTime } from 'luxon'; import { DateTime } from 'luxon'
import { weekendFilter } from 'src/app/fd.da'; import { weekendFilter } from 'src/app/fd.da'
import { UpdatesService } from 'src/app/services/updates.service'; import { UpdatesService } from 'src/app/services/updates.service'
import { CleanNote } from 'src/app/types/clean-note'; import { CleanNote } from 'src/app/types/clean-note'
@Component({ @Component({
selector: 'app-clean', selector: 'app-clean',
templateUrl: './clean.component.html', templateUrl: './clean.component.html',
styleUrl: './clean.component.scss', styleUrl: './clean.component.scss',
standalone: false standalone: false,
}) })
export class CleanComponent implements OnInit { export class CleanComponent implements OnInit {
protected day: string protected day: string
grade: number | null = null grade: number | null = null
notes: CleanNote[] = [] notes: CleanNote[] = []
tips: string = "" tips: string = ''
filter = weekendFilter filter = weekendFilter
constructor (private updates: UpdatesService) { constructor(private updates: UpdatesService) {
this.day = DateTime.now().toISODate() this.day = DateTime.now().toISODate()
} }
ngOnInit(): void { ngOnInit(): void {
this.update() this.update()
} }
update() { update() {
this.updates.getClean(this.day).subscribe((v) => { this.updates.getClean(this.day).subscribe(v => {
if (v) { if (v) {
this.grade = v.grade this.grade = v.grade
this.notes = v.notes this.notes = v.notes
@@ -34,7 +34,7 @@ export class CleanComponent implements OnInit {
} else { } else {
this.grade = null this.grade = null
this.notes = [] this.notes = []
this.tips = "" this.tips = ''
} }
}) })
} }
@@ -42,19 +42,19 @@ export class CleanComponent implements OnInit {
protected gradeColor() { protected gradeColor() {
switch (this.grade) { switch (this.grade) {
case 1: case 1:
return { color: "red" } return { color: 'red' }
case 2: case 2:
return { color: "darkorange" } return { color: 'darkorange' }
case 3: case 3:
return { color: "orange" } return { color: 'orange' }
case 4: case 4:
return { color: "olive" } return { color: 'olive' }
case 5: case 5:
return { color: "green" } return { color: 'green' }
case 6: case 6:
return { color: "springgreen" } return { color: 'springgreen' }
default: default:
return { color: "inherit" } return { color: 'inherit' }
} }
} }
} }

Some files were not shown because too many files have changed in this diff Show More