Compare commits
10 Commits
db0a5b52ab
...
b4826cab2c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4826cab2c | ||
|
|
9ea2229f5a | ||
|
|
2eafa21baf | ||
|
|
ea947caf54 | ||
|
|
22796196a7 | ||
|
|
fe64d141b7 | ||
|
|
007d9a9d5b | ||
|
|
50c43c3da2 | ||
|
|
5a7ef383fe | ||
|
|
1ff84df9b2 |
@@ -1,3 +1,6 @@
|
|||||||
|
# Application
|
||||||
|
DEVELOP_MODE=false
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
MYSQL_USER=db_user
|
MYSQL_USER=db_user
|
||||||
MYSQL_PASSWORD=PAssword123
|
MYSQL_PASSWORD=PAssword123
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { DatabaseModule } from 'src/shared/database/database.module';
|
|||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
import { HttpModule } from '@nestjs/axios';
|
import { HttpModule } from '@nestjs/axios';
|
||||||
import { JwtModule, JwtService } from '@nestjs/jwt';
|
import { JwtModule, JwtService } from '@nestjs/jwt';
|
||||||
|
import { LogModule } from '../log/log.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
controllers: [AuthController],
|
controllers: [AuthController],
|
||||||
@@ -21,6 +22,7 @@ import { JwtModule, JwtService } from '@nestjs/jwt';
|
|||||||
signOptions: { expiresIn: config.get('JWT_EXPIRES_IN') },
|
signOptions: { expiresIn: config.get('JWT_EXPIRES_IN') },
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
LogModule
|
||||||
],
|
],
|
||||||
exports: [JwtModule, AuthService],
|
exports: [JwtModule, AuthService],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { ConfigService } from '@nestjs/config';
|
|||||||
import { JwtService } from '@nestjs/jwt';
|
import { JwtService } from '@nestjs/jwt';
|
||||||
import { IExternalAccessPayload, IPayload } from 'src/model/interface';
|
import { IExternalAccessPayload, IPayload } from 'src/model/interface';
|
||||||
import { User } from 'src/model/entitites';
|
import { User } from 'src/model/entitites';
|
||||||
|
import { LogService, LogType } from '../log/log.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
@@ -15,9 +16,12 @@ export class AuthService {
|
|||||||
private readonly http: HttpService,
|
private readonly http: HttpService,
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
private jwt: JwtService,
|
private jwt: JwtService,
|
||||||
|
private log: LogService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
register(register: CreateUserDto) {
|
register(register: CreateUserDto) {
|
||||||
return this.userRepo.createUser(register);
|
const user = this.userRepo.createUser(register);
|
||||||
|
this.log.log(LogType.Auth, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
async registerOrLoginWithAuthCode(auth: AuthCodeDto): Promise<User> {
|
async registerOrLoginWithAuthCode(auth: AuthCodeDto): Promise<User> {
|
||||||
@@ -31,6 +35,7 @@ export class AuthService {
|
|||||||
return resolve(null);
|
return resolve(null);
|
||||||
}
|
}
|
||||||
this.generateTokens(user);
|
this.generateTokens(user);
|
||||||
|
this.log.log(LogType.Auth, user);
|
||||||
resolve(user);
|
resolve(user);
|
||||||
},
|
},
|
||||||
error: () => {
|
error: () => {
|
||||||
@@ -116,6 +121,7 @@ export class AuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getUserById(id: string): Promise<User> {
|
getUserById(id: string): Promise<User> {
|
||||||
|
this.log.log(LogType.Auth, null);
|
||||||
return this.userRepo.findById(id);
|
return this.userRepo.findById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ import { CylinderService } from './cylinder.service';
|
|||||||
import { AuthModule } from '../auth/auth.module';
|
import { AuthModule } from '../auth/auth.module';
|
||||||
import { DatabaseModule } from 'src/shared/database/database.module';
|
import { DatabaseModule } from 'src/shared/database/database.module';
|
||||||
import { SharedServiceModule } from 'src/shared/service/shared.service.module';
|
import { SharedServiceModule } from 'src/shared/service/shared.service.module';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
controllers: [CylinderController],
|
controllers: [CylinderController],
|
||||||
providers: [CylinderService],
|
providers: [CylinderService, ConfigService],
|
||||||
imports: [AuthModule, DatabaseModule, SharedServiceModule],
|
imports: [AuthModule, DatabaseModule, SharedServiceModule],
|
||||||
})
|
})
|
||||||
export class CylinderModule {}
|
export class CylinderModule {}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { Cylinder, User } from 'src/model/entitites';
|
import { Cylinder, User } from 'src/model/entitites';
|
||||||
import { ActivityRepository, CylinderRepository, KeyRepository } from 'src/model/repositories';
|
import { ActivityRepository, CylinderRepository, KeyRepository } from 'src/model/repositories';
|
||||||
import { HelperService } from 'src/shared/service/system.helper.service';
|
import { HelperService } from 'src/shared/service/system.helper.service';
|
||||||
@@ -9,15 +10,24 @@ export class CylinderService {
|
|||||||
private readonly cylinderRepo: CylinderRepository,
|
private readonly cylinderRepo: CylinderRepository,
|
||||||
private readonly keyRepo: KeyRepository,
|
private readonly keyRepo: KeyRepository,
|
||||||
private systemActivityRepo: ActivityRepository,
|
private systemActivityRepo: ActivityRepository,
|
||||||
private readonly helper: HelperService
|
private readonly helper: HelperService,
|
||||||
|
private readonly configService: ConfigService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
get isDevelopMode(): boolean {
|
||||||
|
return (this.configService.get('DEVELOP_MODE') || '').toLowerCase() == 'true';
|
||||||
|
}
|
||||||
|
|
||||||
async getCylinders(user: User): Promise<Cylinder[]> {
|
async getCylinders(user: User): Promise<Cylinder[]> {
|
||||||
const c = await this.cylinderRepo.find({
|
let c = await this.cylinderRepo.find({
|
||||||
where: { system: { managers: { id: user.id } } },
|
where: { system: { managers: { id: user.id } } },
|
||||||
order: { name: { direction: 'ASC' } },
|
order: { name: { direction: 'ASC' } },
|
||||||
relations: ['system', 'keys'],
|
relations: ['system', 'keys'],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this.isDevelopMode) {
|
||||||
|
c = c.filter(c => c.name.toLowerCase().includes('develop') || c.system.name.toLocaleLowerCase().includes('develop'))
|
||||||
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ import { KeyService } from './key.service';
|
|||||||
import { DatabaseModule } from 'src/shared/database/database.module';
|
import { DatabaseModule } from 'src/shared/database/database.module';
|
||||||
import { AuthModule } from '../auth/auth.module';
|
import { AuthModule } from '../auth/auth.module';
|
||||||
import { SharedServiceModule } from 'src/shared/service/shared.service.module';
|
import { SharedServiceModule } from 'src/shared/service/shared.service.module';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
controllers: [KeyController],
|
controllers: [KeyController],
|
||||||
providers: [KeyService],
|
providers: [KeyService, ConfigService],
|
||||||
imports: [DatabaseModule, AuthModule, SharedServiceModule],
|
imports: [DatabaseModule, AuthModule, SharedServiceModule],
|
||||||
})
|
})
|
||||||
export class KeyModule {}
|
export class KeyModule {}
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ import {
|
|||||||
import { KeyHandoutRepository } from 'src/model/repositories/key-handout.repository';
|
import { KeyHandoutRepository } from 'src/model/repositories/key-handout.repository';
|
||||||
import { ActivityHelperService } from 'src/shared/service/activity.logger.service';
|
import { ActivityHelperService } from 'src/shared/service/activity.logger.service';
|
||||||
import { HelperService } from 'src/shared/service/system.helper.service';
|
import { HelperService } from 'src/shared/service/system.helper.service';
|
||||||
import { IsNull, Not } from 'typeorm';
|
import { FindOperator, IsNull, Not } from 'typeorm';
|
||||||
|
import { faker } from '@faker-js/faker';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class KeyService {
|
export class KeyService {
|
||||||
@@ -18,27 +20,39 @@ export class KeyService {
|
|||||||
private readonly handoverRepo: KeyHandoutRepository,
|
private readonly handoverRepo: KeyHandoutRepository,
|
||||||
private readonly activityService: ActivityHelperService,
|
private readonly activityService: ActivityHelperService,
|
||||||
private readonly helper: HelperService,
|
private readonly helper: HelperService,
|
||||||
|
private readonly configService: ConfigService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
get isDevelopMode(): boolean {
|
||||||
|
return (this.configService.get('DEVELOP_MODE') || '').toLowerCase() == 'true';
|
||||||
|
}
|
||||||
|
|
||||||
async getUsersKeys(user: User): Promise<Key[]> {
|
async getUsersKeys(user: User): Promise<Key[]> {
|
||||||
const keys = await this.keyrepository.find({
|
let keys = await this.keyrepository.find({
|
||||||
where: { cylinder: { system: { managers: { id: user.id } } }, keyLost: IsNull() },
|
where: { cylinder: { system: { managers: { id: user.id } } }, keyLost: IsNull() },
|
||||||
relations: ['cylinder', 'cylinder.system', 'customer'],
|
relations: ['cylinder', 'cylinder.system', 'customer'],
|
||||||
});
|
});
|
||||||
for (let k of keys) {
|
for (let k of keys) {
|
||||||
k.customer = await this.getCustomerOfLastHandout(user, k.id);
|
k.customer = await this.getCustomerOfLastHandout(user, k.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isDevelopMode) {
|
||||||
|
keys = keys.filter(k => k.cylinder.some(c => c.name.toLowerCase().includes('develop') || c.system.name.toLowerCase().includes('develop')))
|
||||||
|
}
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getLostKeys(user: User): Promise<Key[]> {
|
async getLostKeys(user: User): Promise<Key[]> {
|
||||||
const keys = await this.keyrepository.find({
|
let keys = await this.keyrepository.find({
|
||||||
where: { cylinder: { system: { managers: { id: user.id } } }, keyLost: Not(IsNull()) },
|
where: { cylinder: { system: { managers: { id: user.id } } }, keyLost: Not(IsNull()) },
|
||||||
relations: ['cylinder', 'cylinder.system', 'customer'],
|
relations: ['cylinder', 'cylinder.system', 'customer'],
|
||||||
});
|
});
|
||||||
for (let k of keys) {
|
for (let k of keys) {
|
||||||
k.customer = await this.getCustomerOfLastHandout(user, k.id);
|
k.customer = await this.getCustomerOfLastHandout(user, k.id);
|
||||||
}
|
}
|
||||||
|
if (this.isDevelopMode) {
|
||||||
|
keys = keys.filter(k => k.cylinder.some(c => c.name.toLowerCase().includes('develop') || c.system.name.toLowerCase().includes('develop')))
|
||||||
|
}
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { User } from 'src/model/entitites';
|
||||||
import { EmailLogRepository } from 'src/model/repositories/log';
|
import { EmailLogRepository } from 'src/model/repositories/log';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -11,6 +12,8 @@ export class LogService {
|
|||||||
log(type: LogType, data: any) {
|
log(type: LogType, data: any) {
|
||||||
if (type == LogType.Mail) {
|
if (type == LogType.Mail) {
|
||||||
return this.logEmail(data);
|
return this.logEmail(data);
|
||||||
|
} else if (type == LogType.Auth) {
|
||||||
|
return this.logAuthEvent(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,10 +21,15 @@ export class LogService {
|
|||||||
const log = this.emailLogRepo.create(data);
|
const log = this.emailLogRepo.create(data);
|
||||||
const logEntry = await this.emailLogRepo.save(log);
|
const logEntry = await this.emailLogRepo.save(log);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async logAuthEvent(data: User) {
|
||||||
|
console.error("auth logging not implemented")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum LogType {
|
export enum LogType {
|
||||||
Mail
|
Mail,
|
||||||
|
Auth
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum EmailEvent {
|
export enum EmailEvent {
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { MailerService } from "@nestjs-modules/mailer";
|
|||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
import { EmailEvent, LogService, LogType } from "../log/log.service";
|
import { EmailEvent, LogService, LogType } from "../log/log.service";
|
||||||
|
import { KeySystem } from "src/model/entitites/system.entity";
|
||||||
|
import { User } from "src/model/entitites";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MailService {
|
export class MailService {
|
||||||
@@ -12,56 +14,60 @@ export class MailService {
|
|||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendAccessGrantedMail({to, firstName, systemName}: {to: string, firstName: string, systemName: string}) {
|
async sendAccessGrantedMail({to, system}: {to: User, system: KeySystem}) {
|
||||||
this.mailserService.sendMail({
|
this.mailserService.sendMail({
|
||||||
template: './access',
|
template: './access',
|
||||||
to: to,
|
to: to.username,
|
||||||
from: this.configService.get<string>('MAILER_FROM'),
|
from: this.configService.get<string>('MAILER_FROM'),
|
||||||
subject: 'Zugriff gewährt',
|
subject: 'Zugriff gewährt',
|
||||||
context: {
|
context: {
|
||||||
firstName,
|
firstName: to.firstName,
|
||||||
systemName
|
systemName: system.name
|
||||||
}
|
}
|
||||||
}).then(v => {
|
}).then(v => {
|
||||||
this.logService.log(LogType.Mail, {
|
this.logService.log(LogType.Mail, {
|
||||||
to,
|
to,
|
||||||
success: true,
|
success: true,
|
||||||
message: v.response,
|
message: v.response,
|
||||||
type: EmailEvent.GrantSystemAccess
|
type: EmailEvent.GrantSystemAccess,
|
||||||
|
system
|
||||||
})
|
})
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
this.logService.log(LogType.Mail, {
|
this.logService.log(LogType.Mail, {
|
||||||
to,
|
to,
|
||||||
success: false,
|
success: false,
|
||||||
message: e.response,
|
message: e.response,
|
||||||
type: EmailEvent.GrantSystemAccess
|
type: EmailEvent.GrantSystemAccess,
|
||||||
|
system
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
sendAccessRemovedMail({to, firstName, systemName}: {to: string, firstName: string, systemName: string}) {
|
sendAccessRemovedMail({to, system}: {to: User, system: KeySystem}) {
|
||||||
this.mailserService.sendMail({
|
this.mailserService.sendMail({
|
||||||
template: './access-removed',
|
template: './access-removed',
|
||||||
to: to,
|
to: to.username,
|
||||||
from: this.configService.get<string>('MAILER_FROM'),
|
from: this.configService.get<string>('MAILER_FROM'),
|
||||||
subject: 'Zugriff entzogen',
|
subject: 'Zugriff entzogen',
|
||||||
context: {
|
context: {
|
||||||
firstName,
|
firstName: to.firstName,
|
||||||
systemName
|
systemName: system.name
|
||||||
}
|
}
|
||||||
}).then(v => {
|
}).then(v => {
|
||||||
this.logService.log(LogType.Mail, {
|
this.logService.log(LogType.Mail, {
|
||||||
to,
|
to,
|
||||||
success: true,
|
success: true,
|
||||||
message: v.response,
|
message: v.response,
|
||||||
type: EmailEvent.RemoveSystemAccess
|
type: EmailEvent.RemoveSystemAccess,
|
||||||
|
system
|
||||||
})
|
})
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
this.logService.log(LogType.Mail, {
|
this.logService.log(LogType.Mail, {
|
||||||
to,
|
to,
|
||||||
success: false,
|
success: false,
|
||||||
message: e.response,
|
message: e.response,
|
||||||
type: EmailEvent.RemoveSystemAccess
|
type: EmailEvent.RemoveSystemAccess,
|
||||||
|
system
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ import { SystemController } from './system.controller';
|
|||||||
import { AuthModule } from '../auth/auth.module';
|
import { AuthModule } from '../auth/auth.module';
|
||||||
import { DatabaseModule } from 'src/shared/database/database.module';
|
import { DatabaseModule } from 'src/shared/database/database.module';
|
||||||
import { MailModule } from '../mail/mail.module';
|
import { MailModule } from '../mail/mail.module';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
controllers: [SystemController],
|
controllers: [SystemController],
|
||||||
providers: [SystemService],
|
providers: [SystemService, ConfigService],
|
||||||
imports: [AuthModule, DatabaseModule, MailModule],
|
imports: [AuthModule, DatabaseModule, MailModule],
|
||||||
})
|
})
|
||||||
export class SystemModule {}
|
export class SystemModule {}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { ActivityRepository, KeySystemRepository, UserRepository } from 'src/mod
|
|||||||
import { User } from 'src/model/entitites';
|
import { User } from 'src/model/entitites';
|
||||||
import { IUser } from 'src/model/interface';
|
import { IUser } from 'src/model/interface';
|
||||||
import { MailService } from '../mail/mail.service';
|
import { MailService } from '../mail/mail.service';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SystemService {
|
export class SystemService {
|
||||||
@@ -12,9 +13,14 @@ export class SystemService {
|
|||||||
private systemRepo: KeySystemRepository,
|
private systemRepo: KeySystemRepository,
|
||||||
private userRepo: UserRepository,
|
private userRepo: UserRepository,
|
||||||
private systemActivityRepo: ActivityRepository,
|
private systemActivityRepo: ActivityRepository,
|
||||||
private mailService: MailService
|
private mailService: MailService,
|
||||||
|
private readonly configService: ConfigService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
get isDevelopMode(): boolean {
|
||||||
|
return (this.configService.get('DEVELOP_MODE') || '').toLowerCase() == 'true';
|
||||||
|
}
|
||||||
|
|
||||||
async create(user: User, createSystemDto: CreateSystemDto) {
|
async create(user: User, createSystemDto: CreateSystemDto) {
|
||||||
const sys = this.systemRepo.create(createSystemDto);
|
const sys = this.systemRepo.create(createSystemDto);
|
||||||
sys.managers = [user];
|
sys.managers = [user];
|
||||||
@@ -34,11 +40,17 @@ export class SystemService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll(user: User) {
|
async findAll(user: User) {
|
||||||
return this.systemRepo.find({
|
let systems = await this.systemRepo.find({
|
||||||
where: { managers: { id: user.id } },
|
where: { managers: { id: user.id } },
|
||||||
order: { name: { direction: 'ASC' } },
|
order: { name: { direction: 'ASC' } },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this.isDevelopMode) {
|
||||||
|
systems = systems.filter(s => s.name.toLocaleLowerCase().includes('develop'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return systems;
|
||||||
}
|
}
|
||||||
|
|
||||||
findOne(id: string) {
|
findOne(id: string) {
|
||||||
@@ -87,7 +99,7 @@ export class SystemService {
|
|||||||
relations: ['settings']
|
relations: ['settings']
|
||||||
});
|
});
|
||||||
if (user.settings.sendSystemAccessMails) {
|
if (user.settings.sendSystemAccessMails) {
|
||||||
this.mailService.sendAccessRemovedMail({to: manageObject.email, firstName: manageObject.email, systemName: sys.name})
|
this.mailService.sendAccessRemovedMail({to: user, system: sys})
|
||||||
}
|
}
|
||||||
return sys.managers;
|
return sys.managers;
|
||||||
}
|
}
|
||||||
@@ -107,7 +119,7 @@ export class SystemService {
|
|||||||
sys.managers.push(user);
|
sys.managers.push(user);
|
||||||
await this.systemRepo.save(sys);
|
await this.systemRepo.save(sys);
|
||||||
if (user.settings.sendSystemAccessMails) {
|
if (user.settings.sendSystemAccessMails) {
|
||||||
this.mailService.sendAccessGrantedMail({to: user.username, firstName: user.firstName, systemName: sys.name})
|
this.mailService.sendAccessGrantedMail({to: user, system: sys})
|
||||||
}
|
}
|
||||||
return sys.managers;
|
return sys.managers;
|
||||||
|
|
||||||
|
|||||||
@@ -88,10 +88,39 @@
|
|||||||
},
|
},
|
||||||
"defaultConfiguration": "development"
|
"defaultConfiguration": "development"
|
||||||
},
|
},
|
||||||
|
"test": {
|
||||||
|
"builder": "@angular/build:unit-test"
|
||||||
|
},
|
||||||
"extract-i18n": {
|
"extract-i18n": {
|
||||||
"builder": "@angular/build:extract-i18n"
|
"builder": "@angular/build:extract-i18n"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"schematics": {
|
||||||
|
"@schematics/angular:component": {
|
||||||
|
"type": "component"
|
||||||
|
},
|
||||||
|
"@schematics/angular:directive": {
|
||||||
|
"type": "directive"
|
||||||
|
},
|
||||||
|
"@schematics/angular:service": {
|
||||||
|
"type": "service"
|
||||||
|
},
|
||||||
|
"@schematics/angular:guard": {
|
||||||
|
"typeSeparator": "."
|
||||||
|
},
|
||||||
|
"@schematics/angular:interceptor": {
|
||||||
|
"typeSeparator": "."
|
||||||
|
},
|
||||||
|
"@schematics/angular:module": {
|
||||||
|
"typeSeparator": "."
|
||||||
|
},
|
||||||
|
"@schematics/angular:pipe": {
|
||||||
|
"typeSeparator": "."
|
||||||
|
},
|
||||||
|
"@schematics/angular:resolver": {
|
||||||
|
"typeSeparator": "."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
import type { Config } from 'jest';
|
|
||||||
|
|
||||||
const jestConfig: Config = {
|
|
||||||
preset: 'jest-preset-angular',
|
|
||||||
setupFilesAfterEnv: ['<rootDir>/setup-jest.ts'],
|
|
||||||
moduleNameMapper: {
|
|
||||||
'@ngxpert/hot-toast': '<rootDir>/mocks/modules/hot-toast',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default jestConfig;
|
|
||||||
16132
client/package-lock.json
generated
16132
client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,38 +12,40 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ag-grid-community/locale": "^32.1.0",
|
"@ag-grid-community/locale": "^35.1.0",
|
||||||
"@angular/animations": "^19.2.18",
|
"@angular/animations": "^21.1.4",
|
||||||
"@angular/cdk": "^19.2.19",
|
"@angular/cdk": "^21.1.4",
|
||||||
"@angular/common": "^19.2.18",
|
"@angular/common": "^21.1.4",
|
||||||
"@angular/compiler": "^19.2.18",
|
"@angular/compiler": "^21.1.4",
|
||||||
"@angular/core": "^19.2.18",
|
"@angular/core": "^21.1.4",
|
||||||
"@angular/forms": "^19.2.18",
|
"@angular/forms": "^21.1.4",
|
||||||
"@angular/material": "^19.2.19",
|
"@angular/material": "^21.1.4",
|
||||||
"@angular/material-moment-adapter": "^19.2.19",
|
"@angular/material-moment-adapter": "^21.1.4",
|
||||||
"@angular/platform-browser": "^19.2.18",
|
"@angular/platform-browser": "^21.1.4",
|
||||||
"@angular/platform-browser-dynamic": "^19.2.18",
|
"@angular/platform-browser-dynamic": "^21.1.4",
|
||||||
"@angular/router": "^19.2.18",
|
"@angular/router": "^21.1.4",
|
||||||
"@ngneat/overview": "^6.0.0",
|
"@ngneat/overview": "^7.0.0",
|
||||||
"@ngxpert/hot-toast": "^3.0.1",
|
"@ngxpert/hot-toast": "^6.1.0",
|
||||||
"ag-grid-angular": "^32.1.0",
|
"ag-grid-angular": "^35.1.0",
|
||||||
"ag-grid-community": "^32.1.0",
|
"ag-grid-community": "^35.1.0",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "~0.15.1"
|
"zone.js": "~0.15.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular/build": "^19.2.20",
|
"@analogjs/vitest-angular": "^2.2.3",
|
||||||
"@angular/cli": "^19.2.20",
|
"@angular/build": "^21.1.4",
|
||||||
"@angular/compiler-cli": "^19.2.18",
|
"@angular/cli": "^21.1.4",
|
||||||
|
"@angular/compiler-cli": "^21.1.4",
|
||||||
"@faker-js/faker": "^9.0.3",
|
"@faker-js/faker": "^9.0.3",
|
||||||
"@types/jest": "^29.5.14",
|
"@vitest/coverage-v8": "^4.0.18",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"jest": "^29.7.0",
|
"jsdom": "^28.0.0",
|
||||||
"jest-preset-angular": "^14.4.0",
|
|
||||||
"postcss": "^8.4.49",
|
"postcss": "^8.4.49",
|
||||||
"tailwindcss": "^3.4.16",
|
"tailwindcss": "^3.4.16",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "~5.8.3"
|
"typescript": "~5.9.3",
|
||||||
|
"vite": "^7.3.1",
|
||||||
|
"vitest": "^4.0.18"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,22 +1,14 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { AppComponent } from './app.component';
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
import { provideHttpClient } from '@angular/common/http';
|
|
||||||
|
|
||||||
describe('AppComponent', () => {
|
describe('MathService', () => {
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [AppComponent],
|
beforeEach(() => {
|
||||||
providers: [
|
TestBed.configureTestingModule({});
|
||||||
provideHttpClient(),
|
|
||||||
provideHttpClient(),
|
|
||||||
]
|
|
||||||
}).compileComponents();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create the app', () => {
|
it('should add two numbers', () => {
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
expect(5).toEqual(5)
|
||||||
const app = fixture.componentInstance;
|
|
||||||
expect(app).toBeTruthy();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -1,6 +1,16 @@
|
|||||||
import { Component, LOCALE_ID } from '@angular/core';
|
import { Component, LOCALE_ID } from '@angular/core';
|
||||||
import { MAT_DATE_LOCALE } from '@angular/material/core';
|
import { MAT_DATE_LOCALE } from '@angular/material/core';
|
||||||
import { RouterOutlet } from '@angular/router';
|
import { RouterOutlet } from '@angular/router';
|
||||||
|
import { ModuleRegistry, AllCommunityModule } from 'ag-grid-community';
|
||||||
|
|
||||||
|
ModuleRegistry.registerModules([ AllCommunityModule ]);
|
||||||
|
|
||||||
|
import { provideGlobalGridOptions } from 'ag-grid-community';
|
||||||
|
|
||||||
|
// Mark all grids as using legacy themes
|
||||||
|
provideGlobalGridOptions({
|
||||||
|
theme: "legacy",
|
||||||
|
});
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
|
|||||||
@@ -12,14 +12,14 @@
|
|||||||
|
|
||||||
<mat-drawer-container class="example-container" autosize>
|
<mat-drawer-container class="example-container" autosize>
|
||||||
<mat-drawer #drawer class="main_sidenav" mode="side" opened="true" style="border-right: 1px solid #dfdfdf">
|
<mat-drawer #drawer class="main_sidenav" mode="side" opened="true" style="border-right: 1px solid #dfdfdf">
|
||||||
<button mat-button routerLink="/" routerLinkActive="mat-elevation-z1" [routerLinkActiveOptions]="{exact: true}">Home</button>
|
<button matButton routerLink="/" routerLinkActive="mat-elevation-z1" [routerLinkActiveOptions]="{exact: true}">Home</button>
|
||||||
<button mat-button routerLink="/keys" routerLinkActive="mat-elevation-z1">Schlüssel</button>
|
<button matButton routerLink="/keys" routerLinkActive="mat-elevation-z1">Schlüssel</button>
|
||||||
<button mat-button routerLink="/cylinders" routerLinkActive="mat-elevation-z1">Zylinder</button>
|
<button matButton routerLink="/cylinders" routerLinkActive="mat-elevation-z1">Zylinder</button>
|
||||||
<button mat-button routerLink="/systems" routerLinkActive="mat-elevation-z1">Schließanlagen</button>
|
<button matButton routerLink="/systems" routerLinkActive="mat-elevation-z1">Schließanlagen</button>
|
||||||
@if (isAdmin) {
|
@if (isAdmin) {
|
||||||
<button mat-button routerLink="/users" routerLinkActive="mat-elevation-z1">Alle User</button>
|
<button matButton routerLink="/users" routerLinkActive="mat-elevation-z1">Alle User</button>
|
||||||
}
|
}
|
||||||
<button mat-button (click)="openSidebar()">Einstellungen</button>
|
<button matButton (click)="openSidebar()">Einstellungen</button>
|
||||||
|
|
||||||
</mat-drawer>
|
</mat-drawer>
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
|
|
||||||
<!-- <div class="example-sidenav-content">
|
<!-- <div class="example-sidenav-content">
|
||||||
<button type="button" mat-button (click)="drawer.toggle()">
|
<button type="button" matButton (click)="drawer.toggle()">
|
||||||
Toggle sidenav
|
Toggle sidenav
|
||||||
</button>
|
</button>
|
||||||
</div> -->
|
</div> -->
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
Du bist nicht eingeloggt. Logge dich ein um zu Beginnen.
|
Du bist nicht eingeloggt. Logge dich ein um zu Beginnen.
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<button mat-raised-button color="primary" (click)="authService.routeToLogin()">Einloggen!</button>
|
<button matButton="elevated" color="primary" (click)="authService.routeToLogin()">Einloggen!</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -25,8 +25,8 @@
|
|||||||
</form>
|
</form>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions>
|
<mat-dialog-actions>
|
||||||
<button mat-button mat-dialog-close color="warn">Abbrechen</button>
|
<button matButton mat-dialog-close>Abbrechen</button>
|
||||||
<button mat-raised-button (click)="save()" [disabled]="createForm.disabled || createForm.invalid" color="accent">
|
<button matButton="elevated" (click)="save()" [disabled]="createForm.disabled || createForm.invalid" class="btn-primary">
|
||||||
<mat-icon>save</mat-icon>
|
<mat-icon>save</mat-icon>
|
||||||
Speichern
|
Speichern
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { CreateCylinderComponent } from './create-cylinder.component';
|
|
||||||
import { MatDialogRef } from '@angular/material/dialog';
|
|
||||||
import { HotToastService } from '@ngxpert/hot-toast';
|
|
||||||
import { ApiService } from '../../../../shared/api.service';
|
|
||||||
import { MockApiService } from '../../../../../../mocks/services/mock.api.service';
|
|
||||||
import { MockHotToastService } from '../../../../../../mocks/services/mock.hottoast.service';
|
|
||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
|
||||||
|
|
||||||
describe('CreateCylinderComponent', () => {
|
|
||||||
let component: CreateCylinderComponent;
|
|
||||||
let fixture: ComponentFixture<CreateCylinderComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [CreateCylinderComponent, NoopAnimationsModule],
|
|
||||||
providers: [
|
|
||||||
{ provide: ApiService, useClass: MockApiService },
|
|
||||||
{ provide: MatDialogRef, useValue: [] },
|
|
||||||
{ provide: HotToastService, useClass: MockHotToastService }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(CreateCylinderComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -3,7 +3,7 @@ import { FormGroup, FormControl, Validators, ReactiveFormsModule, FormsModule }
|
|||||||
import { MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
import { MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { HotToastService } from '@ngxpert/hot-toast';
|
import { HotToastService } from '@ngxpert/hot-toast';
|
||||||
import { ApiService } from '../../../../shared/api.service';
|
import { ApiService } from '../../../../shared/api.service';
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatSelectModule } from '@angular/material/select';
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
@@ -12,7 +12,7 @@ import { MatIconModule } from '@angular/material/icon';
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-create-cylinder',
|
selector: 'app-create-cylinder',
|
||||||
imports: [CommonModule, MatFormFieldModule, MatInputModule, MatDialogModule, ReactiveFormsModule, FormsModule, MatSelectModule, MatButtonModule, MatIconModule],
|
imports: [MatFormFieldModule, MatInputModule, MatDialogModule, ReactiveFormsModule, FormsModule, MatSelectModule, MatButtonModule, MatIconModule],
|
||||||
templateUrl: './create-cylinder.component.html',
|
templateUrl: './create-cylinder.component.html',
|
||||||
styleUrl: './create-cylinder.component.scss'
|
styleUrl: './create-cylinder.component.scss'
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<h2 mat-dialog-title>Zylinder <u>{{cylinder.name}}</u> löschen?</h2>
|
<h2 mat-dialog-title>Zylinder <u>{{cylinder.name}}</u> löschen?</h2>
|
||||||
<mat-dialog-content>
|
<mat-dialog-content>
|
||||||
<div class="warning-message">
|
<div class="warning-message">
|
||||||
<mat-icon color="warn">warning</mat-icon>
|
<mat-icon>warning</mat-icon>
|
||||||
<p>
|
<p>
|
||||||
<b>{{cylinder.name}}</b> wirklich entfernen?
|
<b>{{cylinder.name}}</b> wirklich entfernen?
|
||||||
Alle Schlüssel die nur diesen Zylinder haben werden ebenfalls entfernt.
|
Alle Schlüssel die nur diesen Zylinder haben werden ebenfalls entfernt.
|
||||||
@@ -13,8 +13,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions align="end">
|
<mat-dialog-actions align="end">
|
||||||
<button mat-button [mat-dialog-close]="false">Abbrechen</button>
|
<button matButton [mat-dialog-close]="false">Abbrechen</button>
|
||||||
<button mat-raised-button color="warn" [mat-dialog-close]="true">
|
<button matButton="elevated" [mat-dialog-close]="true">
|
||||||
<mat-icon>delete</mat-icon>
|
<mat-icon>delete</mat-icon>
|
||||||
Entfernen
|
Entfernen
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { Component, inject, LOCALE_ID } from '@angular/core';
|
import { Component, inject, LOCALE_ID } from '@angular/core';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
||||||
@@ -7,7 +7,7 @@ import { ICylinder } from '../../../../model/interface/cylinder.interface';
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-delete-cylinder',
|
selector: 'app-delete-cylinder',
|
||||||
imports: [MatDialogModule, MatButtonModule, CommonModule, MatIconModule],
|
imports: [MatDialogModule, MatButtonModule, MatIconModule],
|
||||||
providers: [{ provide: LOCALE_ID, useValue: 'de-DE' },],
|
providers: [{ provide: LOCALE_ID, useValue: 'de-DE' },],
|
||||||
templateUrl: './delete-cylinder.component.html',
|
templateUrl: './delete-cylinder.component.html',
|
||||||
styleUrl: './delete-cylinder.component.scss'
|
styleUrl: './delete-cylinder.component.scss'
|
||||||
|
|||||||
@@ -4,6 +4,6 @@
|
|||||||
[gridOptions]="gridOptions!"
|
[gridOptions]="gridOptions!"
|
||||||
/>
|
/>
|
||||||
<div class="floating-btn-container">
|
<div class="floating-btn-container">
|
||||||
<button mat-flat-button class="btn-create mat-elevation-z8" (click)="openCreateCylinder()" color="accent" >Zylinder anlegen</button>
|
<button mat-flat-button class="btn-create mat-elevation-z8" (click)="openCreateCylinder()" >Zylinder anlegen</button>
|
||||||
<button mat-mini-fab disabled><mat-icon>inventory_2</mat-icon></button>
|
<button mat-mini-fab disabled><mat-icon>inventory_2</mat-icon></button>
|
||||||
</div>
|
</div>
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<p>Aktive Schlüssel</p>
|
<p>Aktive Schlüssel</p>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
<mat-card-actions>
|
<mat-card-actions>
|
||||||
<button mat-button routerLink="/keys">Verwalten</button>
|
<button matButton routerLink="/keys">Verwalten</button>
|
||||||
</mat-card-actions>
|
</mat-card-actions>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
<p>Registrierte Zylinder</p>
|
<p>Registrierte Zylinder</p>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
<mat-card-actions>
|
<mat-card-actions>
|
||||||
<button mat-button routerLink="/cylinders">Verwalten</button>
|
<button matButton routerLink="/cylinders">Verwalten</button>
|
||||||
</mat-card-actions>
|
</mat-card-actions>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
<p>Aktive Schließanlagen</p>
|
<p>Aktive Schließanlagen</p>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
<mat-card-actions>
|
<mat-card-actions>
|
||||||
<button mat-button routerLink="/systems">Verwalten</button>
|
<button matButton routerLink="/systems">Verwalten</button>
|
||||||
</mat-card-actions>
|
</mat-card-actions>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
<p>Derzeit ausgegebene Schlüssel</p>
|
<p>Derzeit ausgegebene Schlüssel</p>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
<mat-card-actions>
|
<mat-card-actions>
|
||||||
<button mat-button routerLink="/keys">Verwalten</button>
|
<button matButton routerLink="/keys">Verwalten</button>
|
||||||
</mat-card-actions>
|
</mat-card-actions>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,5 +8,5 @@
|
|||||||
|
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions>
|
<mat-dialog-actions>
|
||||||
<button mat-button [mat-dialog-close]="dataChanged">Schließen</button>
|
<button matButton [mat-dialog-close]="dataChanged">Schließen</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
import { HotToastService, provideHotToastConfig } from '@ngxpert/hot-toast';
|
|
||||||
import { from, map, of } from 'rxjs';
|
|
||||||
import { GridReadyEvent } from 'ag-grid-community';
|
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
|
||||||
import { ArchiveComponent } from './archive.component';
|
|
||||||
import { ApiService } from '../../../../shared/api.service';
|
|
||||||
import { MockApiService } from '../../../../../../mocks/services/mock.api.service';
|
|
||||||
|
|
||||||
// Mocking the dependencies
|
|
||||||
jest.mock('@ngxpert/hot-toast', () => ({
|
|
||||||
HotToastService: jest.fn(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('ArchiveComponent', () => {
|
|
||||||
let component: ArchiveComponent;
|
|
||||||
let fixture: ComponentFixture<ArchiveComponent>;
|
|
||||||
|
|
||||||
const mockGridReadyEvent: GridReadyEvent = {
|
|
||||||
api: { setGridOption: jest.fn(), addEventListener: jest.fn() },
|
|
||||||
columnApi: { someColumnApiMethod: jest.fn() },
|
|
||||||
type: 'gridReady',
|
|
||||||
} as any;
|
|
||||||
|
|
||||||
const mockHotToastService = {
|
|
||||||
observe: jest.fn().mockImplementation(() => ({
|
|
||||||
loading: 'speichern...',
|
|
||||||
success: 'Änderungen gespeichert',
|
|
||||||
error: 'Änderungen konnten nicht gespeichert werden!',
|
|
||||||
subscribe: jest.fn().mockReturnValue(of([]))
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [ArchiveComponent, ],
|
|
||||||
providers: [
|
|
||||||
{ provide: HotToastService, useValue: mockHotToastService },
|
|
||||||
{ provide: ApiService, useClass: MockApiService },
|
|
||||||
]
|
|
||||||
}).compileComponents();
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(ArchiveComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
component.onGridReady(mockGridReadyEvent);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create the archive', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should load the data on start', () => {
|
|
||||||
expect(component['api'].getKeyArchive).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<h2 mat-dialog-title>Schlüssel <u>{{key.name}}</u> löschen?</h2>
|
<h2 mat-dialog-title>Schlüssel <u>{{key.name}}</u> löschen?</h2>
|
||||||
<mat-dialog-content>
|
<mat-dialog-content>
|
||||||
<div class="warning-message">
|
<div class="warning-message">
|
||||||
<mat-icon color="warn">warning</mat-icon>
|
<mat-icon>warning</mat-icon>
|
||||||
<p>
|
<p>
|
||||||
<b>{{key.name}}</b> wirklich entfernen?
|
<b>{{key.name}}</b> wirklich entfernen?
|
||||||
</p>
|
</p>
|
||||||
@@ -12,8 +12,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions align="end">
|
<mat-dialog-actions align="end">
|
||||||
<button mat-button [mat-dialog-close]="false">Abbrechen</button>
|
<button matButton [mat-dialog-close]="false">Abbrechen</button>
|
||||||
<button mat-raised-button color="warn" [mat-dialog-close]="true">
|
<button matButton="elevated" [mat-dialog-close]="true" class="btn-warning">
|
||||||
<mat-icon>delete</mat-icon>
|
<mat-icon>delete</mat-icon>
|
||||||
Entfernen
|
Entfernen
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -43,8 +43,11 @@
|
|||||||
</form>
|
</form>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions>
|
<mat-dialog-actions>
|
||||||
<button mat-button mat-dialog-close color="warn" >Schließen</button>
|
<button matButton mat-dialog-close class="btn-warning">Schließen</button>
|
||||||
<button mat-button (click)="save()" [disabled]="handoverForm.invalid">Speichern</button>
|
<button matButton="elevated" (click)="save()" class="btn-primary" [disabled]="handoverForm.invalid || handoverForm.pristine">
|
||||||
|
<mat-icon>save</mat-icon>
|
||||||
|
Speichern
|
||||||
|
</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
|
|
||||||
@@ -59,7 +62,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions>
|
<mat-dialog-actions>
|
||||||
<button mat-button mat-dialog-close color="warn" >Schließen</button>
|
<button matButton mat-dialog-close >Schließen</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
</mat-tab-group>
|
</mat-tab-group>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
|
|||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MAT_DATE_LOCALE, provideNativeDateAdapter } from '@angular/material/core';
|
import { MAT_DATE_LOCALE, provideNativeDateAdapter } from '@angular/material/core';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { CommonModule, DatePipe } from '@angular/common';
|
import { AsyncPipe, DatePipe } from '@angular/common';
|
||||||
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||||
import { map, Observable, startWith, timestamp } from 'rxjs';
|
import { map, Observable, startWith, timestamp } from 'rxjs';
|
||||||
import {
|
import {
|
||||||
@@ -24,10 +24,11 @@ import {MatTabsModule} from '@angular/material/tabs';
|
|||||||
import { GridApi, GridOptions, GridReadyEvent } from 'ag-grid-community';
|
import { GridApi, GridOptions, GridReadyEvent } from 'ag-grid-community';
|
||||||
import { AG_GRID_LOCALE_DE } from '@ag-grid-community/locale';
|
import { AG_GRID_LOCALE_DE } from '@ag-grid-community/locale';
|
||||||
import { AgGridAngular } from 'ag-grid-angular';
|
import { AgGridAngular } from 'ag-grid-angular';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-handover-dialog',
|
selector: 'app-handover-dialog',
|
||||||
imports: [FormsModule, MatTabsModule, AgGridAngular, ReactiveFormsModule, MatDatepickerModule, MatFormFieldModule, MatInputModule, MatButtonModule, MatDialogModule, CommonModule, MatAutocompleteModule, MatProgressSpinnerModule, MatRadioModule],
|
imports: [FormsModule, MatTabsModule, AgGridAngular, ReactiveFormsModule, MatDatepickerModule, MatFormFieldModule, MatInputModule, MatButtonModule, MatDialogModule, MatAutocompleteModule, MatProgressSpinnerModule, MatRadioModule, AsyncPipe, MatIconModule],
|
||||||
providers: [
|
providers: [
|
||||||
provideNativeDateAdapter(),
|
provideNativeDateAdapter(),
|
||||||
{ provide: LOCALE_ID, useValue: 'de-DE' },
|
{ provide: LOCALE_ID, useValue: 'de-DE' },
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<mat-dialog-content>
|
<mat-dialog-content>
|
||||||
<div class="warning-message">
|
<div class="warning-message">
|
||||||
@if(key.keyLost != null) {
|
@if(key.keyLost != null) {
|
||||||
<mat-icon color="accent">report</mat-icon>
|
<mat-icon >report</mat-icon>
|
||||||
<p>
|
<p>
|
||||||
<b>{{key.name}}</b> wirklich als gefunden markieren?
|
<b>{{key.name}}</b> wirklich als gefunden markieren?
|
||||||
</p>
|
</p>
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
<small>Die Information, dass er am {{ key.keyLost| date:'shortDate' }} verloren wurde, wird gelöscht!</small>
|
<small>Die Information, dass er am {{ key.keyLost| date:'shortDate' }} verloren wurde, wird gelöscht!</small>
|
||||||
</p>
|
</p>
|
||||||
} @else {
|
} @else {
|
||||||
<mat-icon color="warn">warning</mat-icon>
|
<mat-icon>warning</mat-icon>
|
||||||
<p>
|
<p>
|
||||||
<b>{{key.name}}</b> wirklich als verloren markieren?
|
<b>{{key.name}}</b> wirklich als verloren markieren?
|
||||||
</p>
|
</p>
|
||||||
@@ -25,15 +25,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions align="end">
|
<mat-dialog-actions align="end">
|
||||||
<button mat-button [mat-dialog-close]="null">Abbrechen</button>
|
<button matButton [mat-dialog-close]="null">Abbrechen</button>
|
||||||
|
|
||||||
@if(key.keyLost != null) {
|
@if(key.keyLost != null) {
|
||||||
<button mat-raised-button color="accent" (click)="closeFound()">
|
<button matButton="elevated" (click)="closeFound()" class="btn-primary">
|
||||||
<mat-icon>report</mat-icon>
|
<mat-icon>report</mat-icon>
|
||||||
Als gefunden melden
|
Als gefunden melden
|
||||||
</button>
|
</button>
|
||||||
} @else {
|
} @else {
|
||||||
<button mat-raised-button color="warn" (click)="closeWithData()">
|
<button matButton="elevated" (click)="closeWithData()" class="btn-warning">
|
||||||
<mat-icon>report_problem</mat-icon>
|
<mat-icon>report_problem</mat-icon>
|
||||||
Als verloren melden
|
Als verloren melden
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { LostKeyComponent } from './lost-key.component';
|
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
|
||||||
|
|
||||||
describe('LostKeyComponent', () => {
|
|
||||||
let component: LostKeyComponent;
|
|
||||||
let fixture: ComponentFixture<LostKeyComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [LostKeyComponent],
|
|
||||||
providers: [
|
|
||||||
{ provide: MatDialogRef, useValue: [] },
|
|
||||||
{ provide: MAT_DIALOG_DATA, useValue: [] }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(LostKeyComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -7,5 +7,5 @@
|
|||||||
/>
|
/>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions>
|
<mat-dialog-actions>
|
||||||
<button mat-button [mat-dialog-close]="dataChanged">Schließen</button>
|
<button matButton [mat-dialog-close]="dataChanged">Schließen</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { LostKeysComponent } from './lost-keys.component';
|
|
||||||
import { ApiService } from '../../../../shared/api.service';
|
|
||||||
import { MockApiService } from '../../../../../../mocks/services/mock.api.service';
|
|
||||||
import { HotToastService } from '@ngxpert/hot-toast';
|
|
||||||
|
|
||||||
describe('LostKeysComponent', () => {
|
|
||||||
let component: LostKeysComponent;
|
|
||||||
let fixture: ComponentFixture<LostKeysComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [LostKeysComponent],
|
|
||||||
providers: [
|
|
||||||
{ provide: ApiService, useClass: MockApiService },
|
|
||||||
HotToastService
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(LostKeysComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { CommonModule, DatePipe } from '@angular/common';
|
import { DatePipe } from '@angular/common';
|
||||||
import { Component, inject } from '@angular/core';
|
import { Component, inject } from '@angular/core';
|
||||||
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
|
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
|
||||||
import { HotToastService } from '@ngxpert/hot-toast';
|
import { HotToastService } from '@ngxpert/hot-toast';
|
||||||
@@ -13,7 +13,7 @@ import { MatButtonModule } from '@angular/material/button';
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-lost-keys',
|
selector: 'app-lost-keys',
|
||||||
imports: [MatDialogModule, AgGridAngular, CommonModule, MatButtonModule],
|
imports: [MatDialogModule, AgGridAngular, MatButtonModule],
|
||||||
providers: [DatePipe],
|
providers: [DatePipe],
|
||||||
templateUrl: './lost-keys.component.html',
|
templateUrl: './lost-keys.component.html',
|
||||||
styleUrl: './lost-keys.component.scss'
|
styleUrl: './lost-keys.component.scss'
|
||||||
|
|||||||
@@ -41,8 +41,8 @@
|
|||||||
</form>
|
</form>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions>
|
<mat-dialog-actions>
|
||||||
<button mat-button mat-dialog-close color="warn">Abbrechen</button>
|
<button matButton mat-dialog-close>Abbrechen</button>
|
||||||
<button (click)="save()" [disabled]="createForm.disabled || createForm.invalid" mat-raised-button color="accent">
|
<button (click)="save()" [disabled]="createForm.disabled || createForm.invalid" matButton="elevated" class="btn-primary">
|
||||||
<mat-icon>save</mat-icon>
|
<mat-icon>save</mat-icon>
|
||||||
Speichern
|
Speichern
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -10,6 +10,6 @@
|
|||||||
|
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions>
|
<mat-dialog-actions>
|
||||||
<button mat-button color="warn" [mat-dialog-close]="null">Abbrechen</button>
|
<button matButton [mat-dialog-close]="null">Abbrechen</button>
|
||||||
<button mat-button color="accent" [mat-dialog-close]="selectedCylinders">Übernehmen</button>
|
<button matButton [mat-dialog-close]="selectedCylinders">Übernehmen</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { SelectKeyCylinderComponent } from './select-key-cylinder.component';
|
|
||||||
import { HotToastService } from '@ngxpert/hot-toast';
|
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
|
||||||
import { MockHotToastService } from '../../../../../../mocks/services/mock.hottoast.service';
|
|
||||||
|
|
||||||
describe('SelectKeyCylinderComponent', () => {
|
|
||||||
let component: SelectKeyCylinderComponent;
|
|
||||||
let fixture: ComponentFixture<SelectKeyCylinderComponent>;
|
|
||||||
|
|
||||||
const mockHotToastService = {
|
|
||||||
info: jest.fn(),
|
|
||||||
error: jest.fn(),
|
|
||||||
success: jest.fn(),
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [SelectKeyCylinderComponent],
|
|
||||||
providers: [
|
|
||||||
{ provide: HotToastService, useClass: MockHotToastService },
|
|
||||||
{ provide: MatDialogRef, useValue: {} },
|
|
||||||
{ provide: MAT_DIALOG_DATA, useValue: [] }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(SelectKeyCylinderComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="floating-btn-container">
|
<div class="floating-btn-container">
|
||||||
<button mat-flat-button class="btn-create mat-elevation-z8" (click)="openCreateKey()" color="accent" >Schlüssel anlegen</button>
|
<button mat-flat-button class="btn-create mat-elevation-z8" (click)="openCreateKey()" >Schlüssel anlegen</button>
|
||||||
<button mat-mini-fab (click)="openArchive()" matTooltip="Archiv"><mat-icon>inventory_2</mat-icon></button>
|
<button mat-mini-fab (click)="openArchive()" matTooltip="Archiv"><mat-icon>inventory_2</mat-icon></button>
|
||||||
<button mat-mini-fab (click)="openLostKeys()" class="lost icon-btn-xs" style="background-repeat: no-repeat; background-position: center;" matTooltip="Verlorene Schlüssel"></button>
|
<button mat-mini-fab (click)="openLostKeys()" class="lost icon-btn-xs" style="background-repeat: no-repeat; background-position: center;" matTooltip="Verlorene Schlüssel"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,109 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
import { KeysComponent } from './keys.component';
|
|
||||||
import { HotToastService } from '@ngxpert/hot-toast';
|
|
||||||
import { of } from 'rxjs';
|
|
||||||
import { ApiService } from '../../shared/api.service';
|
|
||||||
import { GridReadyEvent } from 'ag-grid-community';
|
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
|
||||||
import { MockApiService } from '../../../../mocks/services/mock.api.service';
|
|
||||||
|
|
||||||
|
|
||||||
describe('KeysComponent', () => {
|
|
||||||
let component: KeysComponent;
|
|
||||||
let fixture: ComponentFixture<KeysComponent>;
|
|
||||||
|
|
||||||
const mockGridReadyEvent: GridReadyEvent = {
|
|
||||||
api: { setGridOption: jest.fn(), addEventListener: jest.fn(), getGridOption: jest.fn() },
|
|
||||||
columnApi: { someColumnApiMethod: jest.fn() },
|
|
||||||
type: 'gridReady',
|
|
||||||
} as any;
|
|
||||||
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [KeysComponent, ],
|
|
||||||
providers: [
|
|
||||||
{ provide: ApiService, useClass: MockApiService },
|
|
||||||
{ provide: MatDialog, useClass: MockMatDialog },
|
|
||||||
HotToastService
|
|
||||||
]
|
|
||||||
}).compileComponents();
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(KeysComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
component.onGridReady(mockGridReadyEvent);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create the keyscomponent', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call getCylinders on ngOnInit', () => {
|
|
||||||
component.ngOnInit();
|
|
||||||
expect(component['api'].getCylinders).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call getKeys and set rowData when loadKeys is called', () => {
|
|
||||||
component.loadKeys();
|
|
||||||
expect(component['api'].getKeys).toHaveBeenCalled();
|
|
||||||
expect(component.gridApi.setGridOption).toHaveBeenCalledWith('rowData', [{ name: 'Key 1' }]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call deleteKey when deleteKey is triggered', () => {
|
|
||||||
const keyId = '123';
|
|
||||||
component.deleteKey(keyId);
|
|
||||||
expect(component['api'].deleteKey).toHaveBeenCalledWith(keyId);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call updateKey on cellEditEnd when a value is changed', () => {
|
|
||||||
|
|
||||||
const mockEvent = {
|
|
||||||
data: { id: '1', name: 'Old Name' },
|
|
||||||
oldValue: 'Old Name',
|
|
||||||
newValue: 'New Name',
|
|
||||||
valueChanged: true,
|
|
||||||
};
|
|
||||||
component.cellEditEnd(mockEvent as any);
|
|
||||||
expect(component['api'].updateKey).toHaveBeenCalledWith(mockEvent.data);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not call updateKey on cellEditEnd if value is not changed', () => {
|
|
||||||
const mockEvent = {
|
|
||||||
data: { id: '1', name: 'Old Name' },
|
|
||||||
oldValue: 'Old Name',
|
|
||||||
newValue: 'Old Name',
|
|
||||||
valueChanged: false,
|
|
||||||
};
|
|
||||||
component.cellEditEnd(mockEvent as any);
|
|
||||||
expect(component['api'].updateKey).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reload Keys after creation', () => {
|
|
||||||
component['dialog'].open = jest.fn().mockReturnValue({
|
|
||||||
afterClosed: jest.fn().mockReturnValue(of(true)),
|
|
||||||
})
|
|
||||||
component.openCreateKey();
|
|
||||||
expect(component['dialog'].open).toHaveBeenCalled();
|
|
||||||
expect(component['api'].getKeys).toHaveBeenCalledTimes(2)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should not reload Keys after cancellation', () => {
|
|
||||||
component['dialog'].open = jest.fn().mockReturnValue({
|
|
||||||
afterClosed: jest.fn().mockReturnValue(of(null)),
|
|
||||||
})
|
|
||||||
component.openCreateKey();
|
|
||||||
expect(component['dialog'].open).toHaveBeenCalled();
|
|
||||||
expect(component['api'].getKeys).toHaveBeenCalledTimes(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
class MockMatDialog {
|
|
||||||
open = jest.fn().mockReturnValue({
|
|
||||||
afterClosed: jest.fn().mockReturnValue(of(true)),
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
@@ -47,9 +47,7 @@ export class KeysComponent {
|
|||||||
valueFormatter: (data: any) => { return data; },
|
valueFormatter: (data: any) => { return data; },
|
||||||
cellRenderer: (data: any) => {return data.value?.map((m: ICylinder) => m.name).join(', ')},
|
cellRenderer: (data: any) => {return data.value?.map((m: ICylinder) => m.name).join(', ')},
|
||||||
tooltipValueGetter: (data: any) => data.value?.map((m: ICylinder) => m.name).join(','),
|
tooltipValueGetter: (data: any) => data.value?.map((m: ICylinder) => m.name).join(','),
|
||||||
onCellDoubleClicked(event) {
|
onCellDoubleClicked(event) {},
|
||||||
|
|
||||||
},
|
|
||||||
cellEditorPopup: true,
|
cellEditorPopup: true,
|
||||||
filterValueGetter: (params: any) => {return params.data.cylinder?.map((m: ICylinder) => m.name).join(', ')},
|
filterValueGetter: (params: any) => {return params.data.cylinder?.map((m: ICylinder) => m.name).join(', ')},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,12 +3,16 @@
|
|||||||
}
|
}
|
||||||
<div class="sidebar" [class.open]="isOpen">
|
<div class="sidebar" [class.open]="isOpen">
|
||||||
<div class="sidebar-header">
|
<div class="sidebar-header">
|
||||||
|
<div class="title_text">Einstellungen</div>
|
||||||
<button class="close-btn" (click)="closeSidebar()">✕</button>
|
<button class="close-btn" (click)="closeSidebar()">✕</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content" style="flex: 1 1 auto" >
|
<div class="content" style="flex: 1 1 auto" >
|
||||||
|
|
||||||
<h4>Einstellungen</h4>
|
<div class="px-4">
|
||||||
|
<div class="text-2xl">Allgemeine Einstellungen</div>
|
||||||
|
<div>Name, Login...:</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form [formGroup]="userData" class="flex flex-col p-4">
|
<form [formGroup]="userData" class="flex flex-col p-4">
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
@@ -23,12 +27,14 @@
|
|||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
<mat-label>Email</mat-label>
|
<mat-label>Email</mat-label>
|
||||||
<input type="text" matInput formControlName="userName">
|
<input type="text" matInput formControlName="userName">
|
||||||
|
<mat-hint>Wird zum Login benötigt</mat-hint>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<button mat-button [disabled]="userData.invalid" (click)="saveUser()">Speichern</button>
|
<div class="spacer-y16"></div>
|
||||||
|
<button matButton="elevated" [disabled]="userData.invalid" (click)="saveUser()">Speichern</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<div class="spacer-y32"></div>
|
||||||
<div class=" px-4">
|
<div class="px-4">
|
||||||
<div class="text-2xl">Emailbenachrichtigungen</div>
|
<div class="text-2xl">Emailbenachrichtigungen</div>
|
||||||
<div>Sende Emails bei: </div>
|
<div>Sende Emails bei: </div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -35,7 +35,10 @@
|
|||||||
.sidebar-header {
|
.sidebar-header {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: space-between;
|
||||||
|
.title_text {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.close-btn {
|
.close-btn {
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { SettingsComponent } from './settings.component';
|
|
||||||
import { ApiService } from '../../shared/api.service';
|
|
||||||
import { MockApiService } from '../../../../mocks/services/mock.api.service';
|
|
||||||
import { AuthService } from '../../core/auth/auth.service';
|
|
||||||
import { MockAuthService } from '../../../../mocks/services/mock.auth.service';
|
|
||||||
import { HotToastService } from '@ngxpert/hot-toast';
|
|
||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
|
||||||
|
|
||||||
describe('SettingsComponent', () => {
|
|
||||||
let component: SettingsComponent;
|
|
||||||
let fixture: ComponentFixture<SettingsComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [SettingsComponent, NoopAnimationsModule],
|
|
||||||
providers: [
|
|
||||||
{ provide: ApiService, useClass: MockApiService },
|
|
||||||
{ provide: AuthService, useClass: MockAuthService },
|
|
||||||
HotToastService
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(SettingsComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -44,6 +44,9 @@ export class SettingsComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
closeSidebar() {
|
closeSidebar() {
|
||||||
|
if (this.isDirty()) {
|
||||||
|
this.toast.warning('Ungespeicherte Änderungen wurden verworfen.');
|
||||||
|
}
|
||||||
this.isOpen = false;
|
this.isOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,11 +86,13 @@ export class SettingsComponent {
|
|||||||
next: () => {
|
next: () => {
|
||||||
this.toast.success('Gespeichert')
|
this.toast.success('Gespeichert')
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
|
this.userSettings.markAsPristine();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
saveUser() {
|
saveUser() {
|
||||||
|
this.isDirty();
|
||||||
const user = this.authService.user;
|
const user = this.authService.user;
|
||||||
user.firstName = this.$userData.firstName.value!;
|
user.firstName = this.$userData.firstName.value!;
|
||||||
user.lastName = this.$userData.lastName.value!;
|
user.lastName = this.$userData.lastName.value!;
|
||||||
@@ -95,8 +100,14 @@ export class SettingsComponent {
|
|||||||
|
|
||||||
this.api.saveUser(user).subscribe({
|
this.api.saveUser(user).subscribe({
|
||||||
next: () => {
|
next: () => {
|
||||||
this.toast.success('Gespeichert')
|
this.toast.success('Gespeichert');
|
||||||
|
this.userSettings.markAsPristine()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isDirty(): boolean {
|
||||||
|
|
||||||
|
return this.userData.dirty || this.userSettings.dirty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<h2 mat-dialog-title>Manager entfernen</h2>
|
<h2 mat-dialog-title>Manager entfernen</h2>
|
||||||
<mat-dialog-content>
|
<mat-dialog-content>
|
||||||
<div class="warning-message">
|
<div class="warning-message">
|
||||||
<mat-icon color="warn">warning</mat-icon>
|
<mat-icon>warning</mat-icon>
|
||||||
<p>
|
<p>
|
||||||
<b>{{ manager.firstName }} {{ manager.lastName }}</b> wirklich entfernen?
|
<b>{{ manager.firstName }} {{ manager.lastName }}</b> wirklich entfernen?
|
||||||
Der Benutzer hat dann keinen Zugriff mehr auf diese Schließanlage mit allen ihren Daten.
|
Der Benutzer hat dann keinen Zugriff mehr auf diese Schließanlage mit allen ihren Daten.
|
||||||
@@ -14,8 +14,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions align="end">
|
<mat-dialog-actions align="end">
|
||||||
<button mat-button [mat-dialog-close]="false">Abbrechen</button>
|
<button matButton [mat-dialog-close]="false">Abbrechen</button>
|
||||||
<button mat-raised-button color="warn" [mat-dialog-close]="true">
|
<button matButton="elevated" [mat-dialog-close]="true">
|
||||||
<mat-icon>delete</mat-icon>
|
<mat-icon>delete</mat-icon>
|
||||||
Entfernen
|
Entfernen
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { RemoveManagerPopupComponent } from './remove-manager-popup.component';
|
|
||||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
|
||||||
|
|
||||||
describe('RemoveManagerPopupComponent', () => {
|
|
||||||
let component: RemoveManagerPopupComponent;
|
|
||||||
let fixture: ComponentFixture<RemoveManagerPopupComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [RemoveManagerPopupComponent],
|
|
||||||
providers: [
|
|
||||||
{
|
|
||||||
provide: MatDialogRef,
|
|
||||||
useValue: []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: MAT_DIALOG_DATA,
|
|
||||||
useValue: ''
|
|
||||||
},
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(RemoveManagerPopupComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
<mat-hint>Emailadresse des neuen Users eingeben</mat-hint>
|
<mat-hint>Emailadresse des neuen Users eingeben</mat-hint>
|
||||||
<mat-icon matPrefix class="text-gray-400 mr-2">email</mat-icon>
|
<mat-icon matPrefix class="text-gray-400 mr-2">email</mat-icon>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<button mat-raised-button
|
<button matButton="elevated"
|
||||||
color="primary"
|
color="primary"
|
||||||
(click)="addManagerByEmail()"
|
(click)="addManagerByEmail()"
|
||||||
[disabled]="email == '' || email == null">
|
[disabled]="email == '' || email == null">
|
||||||
@@ -29,5 +29,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions>
|
<mat-dialog-actions>
|
||||||
<button mat-button [mat-dialog-close]="true">Schließen</button>
|
<button matButton [mat-dialog-close]="true">Schließen</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
@@ -6,26 +6,3 @@
|
|||||||
--ag-selected-row-background-color: rgb(229 231 235);
|
--ag-selected-row-background-color: rgb(229 231 235);
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-4 {
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-gray-50 {
|
|
||||||
background-color: rgb(249 250 251);
|
|
||||||
}
|
|
||||||
|
|
||||||
.rounded-md {
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shadow-sm {
|
|
||||||
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-gray-400 {
|
|
||||||
color: rgb(156 163 175);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mr-2 {
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { SystemManagerComponent } from './system-manager.component';
|
|
||||||
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
|
||||||
import { AgGridAngular } from 'ag-grid-angular';
|
|
||||||
import { ApiService } from '../../../../shared/api.service';
|
|
||||||
import { HotToastService } from '@ngxpert/hot-toast';
|
|
||||||
import { MockApiService } from '../../../../../../mocks/services/mock.api.service';
|
|
||||||
import { GridReadyEvent } from 'ag-grid-community';
|
|
||||||
import { AuthService } from '../../../../core/auth/auth.service';
|
|
||||||
import { MockAuthService } from '../../../../../../mocks/services/mock.auth.service';
|
|
||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
describe('SystemManagerComponent', () => {
|
|
||||||
let component: SystemManagerComponent;
|
|
||||||
let fixture: ComponentFixture<SystemManagerComponent>;
|
|
||||||
let api: ApiService;
|
|
||||||
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [SystemManagerComponent, AgGridAngular, MatDialogModule, NoopAnimationsModule],
|
|
||||||
providers: [
|
|
||||||
HotToastService,
|
|
||||||
{ provide: ApiService, useClass: MockApiService },
|
|
||||||
{
|
|
||||||
provide: MatDialogRef,
|
|
||||||
useValue: []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: MAT_DIALOG_DATA,
|
|
||||||
useValue: []
|
|
||||||
},
|
|
||||||
{ provide: AuthService, useClass: MockAuthService }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(SystemManagerComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
api = component['api']
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should initialize gridApi and gridColumnApi on gridReady and fill data', () => {
|
|
||||||
// Mock des GridReadyEvent
|
|
||||||
let mockData = [{ id: 1, name: 'Test' }];
|
|
||||||
const mockGridReadyEvent: GridReadyEvent = {
|
|
||||||
api: { setGridOption: jest.fn() },
|
|
||||||
columnApi: { someColumnApiMethod: jest.fn() },
|
|
||||||
type: 'gridReady',
|
|
||||||
} as any;
|
|
||||||
|
|
||||||
// Methode aufrufen
|
|
||||||
component.onGridReady(mockGridReadyEvent);
|
|
||||||
|
|
||||||
// Assertions
|
|
||||||
expect(component.gridApi).toBe(mockGridReadyEvent.api);
|
|
||||||
expect(api.getSystemManagers).toHaveBeenCalled();
|
|
||||||
expect(component.gridApi.setGridOption).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -8,7 +8,7 @@ import { ApiService } from '../../../../shared/api.service';
|
|||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { AuthService } from '../../../../core/auth/auth.service';
|
import { AuthService } from '../../../../core/auth/auth.service';
|
||||||
import { HttpErrorResponse } from '@angular/common/http';
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
@@ -18,7 +18,7 @@ import { MatIconModule } from '@angular/material/icon';
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-system-manager',
|
selector: 'app-system-manager',
|
||||||
imports: [AgGridAngular, MatDialogModule, MatButtonModule, MatInputModule, MatFormFieldModule, CommonModule, FormsModule, MatIconModule],
|
imports: [AgGridAngular, MatDialogModule, MatButtonModule, MatInputModule, MatFormFieldModule, FormsModule, MatIconModule],
|
||||||
templateUrl: './system-manager.component.html',
|
templateUrl: './system-manager.component.html',
|
||||||
styleUrl: './system-manager.component.scss'
|
styleUrl: './system-manager.component.scss'
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -14,8 +14,8 @@
|
|||||||
</form>
|
</form>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions>
|
<mat-dialog-actions>
|
||||||
<button mat-button mat-dialog-close color="warn">Abbrechen</button>
|
<button matButton mat-dialog-close>Abbrechen</button>
|
||||||
<button mat-raised-button (click)="save()" [disabled]="createForm.disabled" color="accent">
|
<button matButton="elevated" (click)="save()" [disabled]="createForm.disabled" >
|
||||||
<mat-icon>save</mat-icon>
|
<mat-icon>save</mat-icon>
|
||||||
Speichern
|
Speichern
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
|
||||||
import { CreateSystemComponent } from './create.component';
|
|
||||||
import { ApiService } from '../../../shared/api.service';
|
|
||||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
|
||||||
import { of, throwError } from 'rxjs';
|
|
||||||
import { HotToastService } from '@ngxpert/hot-toast';
|
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
||||||
import { MockApiService } from '../../../../../mocks/services/mock.api.service';
|
|
||||||
|
|
||||||
describe('CreateComponent', () => {
|
|
||||||
let component: CreateSystemComponent;
|
|
||||||
let fixture: ComponentFixture<CreateSystemComponent>;
|
|
||||||
let apiService: ApiService;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [CreateSystemComponent, NoopAnimationsModule, FormsModule, ReactiveFormsModule],
|
|
||||||
providers: [
|
|
||||||
HotToastService,
|
|
||||||
{ provide: ApiService, useClass: MockApiService },
|
|
||||||
{
|
|
||||||
provide: MatDialogRef,
|
|
||||||
useValue: []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: MAT_DIALOG_DATA,
|
|
||||||
useValue: []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(CreateSystemComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
apiService = component['api'];
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call apiService.createSystem when createSystem is called', () => {
|
|
||||||
expect(apiService.createSystem).not.toHaveBeenCalled();
|
|
||||||
component.createForm.setValue({ name: 'Test System' });
|
|
||||||
component.save();
|
|
||||||
expect(apiService.createSystem).toHaveBeenCalledWith({ name: 'Test System' });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle success response correctly', () => {
|
|
||||||
jest.spyOn(apiService, 'createSystem').mockReturnValue(of({}));
|
|
||||||
const toastSpy = jest.spyOn(component['toast'], 'observe');
|
|
||||||
component.createForm.setValue({ name: 'Test System' });
|
|
||||||
component.save();
|
|
||||||
expect(toastSpy).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle error response correctly', () => {
|
|
||||||
jest.spyOn(apiService, 'createSystem').mockReturnValue(throwError(() => new Error('Test Error')));
|
|
||||||
const toastSpy = jest.spyOn(component['toast'], 'observe');
|
|
||||||
component.save();
|
|
||||||
expect(toastSpy).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
@@ -5,5 +5,5 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="floating-btn-container">
|
<div class="floating-btn-container">
|
||||||
<button mat-flat-button class="btn-create mat-elevation-z8" (click)="openCreateSystem()" color="accent" >Schließanlage anlegen</button>
|
<button mat-flat-button class="btn-create mat-elevation-z8" (click)="openCreateSystem()" >Schließanlage anlegen</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
|
||||||
import { SystemComponent } from './system.component';
|
|
||||||
import { ApiService } from '../../shared/api.service';
|
|
||||||
import { GridReadyEvent } from 'ag-grid-community';
|
|
||||||
import { of } from 'rxjs';
|
|
||||||
|
|
||||||
describe('SystemcomponentComponent', () => {
|
|
||||||
let component: SystemComponent;
|
|
||||||
let mockApiService: MockApiService;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
mockApiService = new MockApiService();
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [SystemComponent],
|
|
||||||
providers: [
|
|
||||||
{ provide: ApiService, useValue: mockApiService }
|
|
||||||
]
|
|
||||||
}).compileComponents();
|
|
||||||
const fixture = TestBed.createComponent(SystemComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create the SystemComponent', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should initialize gridApi and gridColumnApi on gridReady and fill data', () => {
|
|
||||||
// Mock des GridReadyEvent
|
|
||||||
let mockData = [{ id: 1, name: 'Test' }];
|
|
||||||
mockApiService.getSystems.mockReturnValue(of(mockData));
|
|
||||||
const mockGridReadyEvent: GridReadyEvent = {
|
|
||||||
api: { setGridOption: jest.fn() },
|
|
||||||
columnApi: { someColumnApiMethod: jest.fn() },
|
|
||||||
type: 'gridReady',
|
|
||||||
} as any;
|
|
||||||
|
|
||||||
// Methode aufrufen
|
|
||||||
component.onGridReady(mockGridReadyEvent);
|
|
||||||
|
|
||||||
// Assertions
|
|
||||||
expect(component.gridApi).toBe(mockGridReadyEvent.api);
|
|
||||||
expect(mockApiService.getSystems).toHaveBeenCalled();
|
|
||||||
expect(component.gridApi.setGridOption).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
class MockApiService {
|
|
||||||
getSystems = jest.fn();
|
|
||||||
}
|
|
||||||
@@ -79,10 +79,7 @@ export class AgDeleteKeyComponent implements ICellRendererAngularComp {
|
|||||||
})
|
})
|
||||||
).subscribe({
|
).subscribe({
|
||||||
next: () => {
|
next: () => {
|
||||||
let data = this.params.api.getGridOption("rowData");
|
this.setData();
|
||||||
data = data?.filter(d => d.id != this.key.id);
|
|
||||||
this.params.api.setGridOption("rowData", data);
|
|
||||||
this.params.api.setGridOption("loading", false);
|
|
||||||
},
|
},
|
||||||
error: () => {
|
error: () => {
|
||||||
this.params.api.setGridOption("loading", false);
|
this.params.api.setGridOption("loading", false);
|
||||||
@@ -122,8 +119,16 @@ export class AgDeleteKeyComponent implements ICellRendererAngularComp {
|
|||||||
this.key.keyLost = n;
|
this.key.keyLost = n;
|
||||||
this.params.api.refreshCells();
|
this.params.api.refreshCells();
|
||||||
this.api.updateKey(this.key).subscribe();
|
this.api.updateKey(this.key).subscribe();
|
||||||
|
this.setData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setData() {
|
||||||
|
let data = this.params.api.getGridOption("rowData");
|
||||||
|
data = data?.filter(d => d.id != this.key.id);
|
||||||
|
this.params.api.setGridOption("rowData", data);
|
||||||
|
this.params.api.setGridOption("loading", false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { AgSystemManagerComponent } from './ag-system-manager.component';
|
|
||||||
import { MatDialogModule } from '@angular/material/dialog';
|
|
||||||
import { AgGridAngular } from 'ag-grid-angular';
|
|
||||||
import { ApiService } from '../../../api.service';
|
|
||||||
import { of } from 'rxjs';
|
|
||||||
import { HotToastService } from '@ngxpert/hot-toast';
|
|
||||||
|
|
||||||
describe('AgSystemManagerComponent', () => {
|
|
||||||
let component: AgSystemManagerComponent;
|
|
||||||
let fixture: ComponentFixture<AgSystemManagerComponent>;
|
|
||||||
let mockApiService: MockApiService;
|
|
||||||
|
|
||||||
const mockHotToastService = {
|
|
||||||
observe: jest.fn().mockImplementation(() => ({
|
|
||||||
loading: 'speichern...',
|
|
||||||
success: 'Änderungen gespeichert',
|
|
||||||
error: 'Änderungen konnten nicht gespeichert werden!',
|
|
||||||
subscribe: jest.fn().mockReturnValue(of([]))
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
mockApiService = new MockApiService();
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [AgSystemManagerComponent, AgGridAngular, MatDialogModule],
|
|
||||||
providers: [
|
|
||||||
{ provide: ApiService, useValue: mockApiService },
|
|
||||||
{ provide: HotToastService, useValue: mockHotToastService },
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(AgSystemManagerComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
class MockApiService {
|
|
||||||
getSystems = jest.fn();
|
|
||||||
}
|
|
||||||
@@ -158,3 +158,59 @@ div.ag-row {
|
|||||||
{
|
{
|
||||||
border-right: none;
|
border-right: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.p-4 {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-gray-50 {
|
||||||
|
background-color: rgb(249 250 251);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rounded-md {
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadow-sm {
|
||||||
|
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-gray-400 {
|
||||||
|
color: rgb(156 163 175);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mr-2 {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer-y8 {
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer-y16 {
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer-y32 {
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-button, .mat-mdc-raised-button:not(.mat-mdc-button-disabled) {
|
||||||
|
.mdc-button__label, .mat-icon {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn-warning:not(.mat-mdc-button-disabled) {
|
||||||
|
.mdc-button__label, .mat-icon {
|
||||||
|
color: rgb(197, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:not(.mat-mdc-button-disabled).mat-mdc-raised-button {
|
||||||
|
.mdc-button__label, .mat-icon {
|
||||||
|
color: rgb(32, 100, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
client/src/test-setup.ts
Normal file
12
client/src/test-setup.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import 'zone.js/testing';
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import {
|
||||||
|
BrowserDynamicTestingModule,
|
||||||
|
platformBrowserDynamicTesting,
|
||||||
|
} from '@angular/platform-browser-dynamic/testing';
|
||||||
|
|
||||||
|
// Wichtig: Initialisiert Angulars Test-Environment (wie früher in test.ts)
|
||||||
|
TestBed.initTestEnvironment(
|
||||||
|
BrowserDynamicTestingModule,
|
||||||
|
platformBrowserDynamicTesting()
|
||||||
|
);
|
||||||
@@ -17,11 +17,7 @@
|
|||||||
"importHelpers": true,
|
"importHelpers": true,
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"module": "ES2022",
|
"module": "ES2022",
|
||||||
"useDefineForClassFields": false,
|
"useDefineForClassFields": false
|
||||||
"lib": [
|
|
||||||
"ES2022",
|
|
||||||
"dom"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"enableI18nLegacyMessageIdFormat": false,
|
"enableI18nLegacyMessageIdFormat": false,
|
||||||
|
|||||||
@@ -1,13 +1,5 @@
|
|||||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
|
||||||
{
|
{
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./out-tsc/spec",
|
"types": ["vitest/globals", "node"]
|
||||||
"module": "CommonJS",
|
}
|
||||||
"types": ["jest"]
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"src/**/*.spec.ts",
|
|
||||||
"src/**/*.d.ts"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
15
client/vitest.config.ts
Normal file
15
client/vitest.config.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
globals: true,
|
||||||
|
environment: 'jsdom',
|
||||||
|
setupFiles: ['src/test-setup.ts'],
|
||||||
|
include: ['src/**/*.spec.ts'],
|
||||||
|
coverage: {
|
||||||
|
provider: 'v8',
|
||||||
|
reporter: ['text', 'html'],
|
||||||
|
reportsDirectory: './coverage'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user