Compare commits

..

10 commits

Author SHA1 Message Date
212a6f8d26 chore(deps): lock file maintenance
Some checks failed
Playwright Tests / test (pull_request) Failing after 45s
2025-01-22 11:03:32 +00:00
ebd4508743 Merge pull request 'feat(navigation): add navigation bar to employee views' (#79) from feature/add-sidebar into main
All checks were successful
Playwright Tests / test (push) Successful in 2m3s
Playwright Tests / test (pull_request) Successful in 2m39s
Reviewed-on: #79
2025-01-22 09:23:07 +00:00
5cc396f552
feat(navigation): add navigation bar to employee views
All checks were successful
Playwright Tests / test (pull_request) Successful in 2m16s
2025-01-22 10:16:08 +01:00
f3738ff631 Merge pull request 'refactor: remove unused employee fields from template' (#77) from fix/remove-extra-collumns-from-employees into main
All checks were successful
Playwright Tests / test (push) Successful in 2m10s
Reviewed-on: #77
2025-01-22 08:32:14 +00:00
fbec66debc Merge pull request 'feat(qualifikationsverwaltung): add error handling for delete skill' (#76) from fix/skill-deletion-logi into main
All checks were successful
Playwright Tests / test (push) Successful in 2m25s
Reviewed-on: #76
2025-01-22 08:29:36 +00:00
4dc253b508
refactor: remove unused employee fields from template
All checks were successful
Playwright Tests / test (pull_request) Successful in 2m45s
2025-01-22 09:28:17 +01:00
5f18aa207d
feat(qualifikationsverwaltung): add error handling for delete skill
All checks were successful
Playwright Tests / test (pull_request) Successful in 2m12s
2025-01-22 09:26:46 +01:00
2a337f3586 Merge pull request 'feat: add qualification creation component and route' (#75) from feature/create-skill into main
All checks were successful
Playwright Tests / test (push) Successful in 2m13s
Reviewed-on: #75
2025-01-22 08:09:43 +00:00
471a675117
fix: correct selector and template URLs in component
All checks were successful
Playwright Tests / test (pull_request) Successful in 2m2s
2025-01-22 09:07:13 +01:00
9b80777fc3
feat: add qualification creation component and route
Some checks failed
Playwright Tests / test (pull_request) Failing after 1m19s
2025-01-22 09:03:52 +01:00
15 changed files with 204 additions and 122 deletions

18
package-lock.json generated
View file

@ -5503,9 +5503,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001692",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz",
"integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==",
"version": "1.0.30001695",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz",
"integrity": "sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==",
"dev": true,
"funding": [
{
@ -6430,9 +6430,9 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
"version": "1.5.83",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.83.tgz",
"integrity": "sha512-LcUDPqSt+V0QmI47XLzZrz5OqILSMGsPFkDYus22rIbgorSvBYEFqq854ltTmUdHkY92FSdAAvsh4jWEULMdfQ==",
"version": "1.5.84",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.84.tgz",
"integrity": "sha512-I+DQ8xgafao9Ha6y0qjHHvpZ9OfyA1qKlkHkjywxzniORU2awxyz7f/iVJcULmrF2yrM3nHQf+iDjJtbbexd/g==",
"dev": true,
"license": "ISC"
},
@ -7063,9 +7063,9 @@
"license": "MIT"
},
"node_modules/fast-uri": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.5.tgz",
"integrity": "sha512-5JnBCWpFlMo0a3ciDy/JckMzzv1U9coZrIhedq+HXxxUfDTAiS0LA8OKVao4G9BxmCVck/jtA5r3KAtRWEyD8Q==",
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
"integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==",
"dev": true,
"funding": [
{

View file

@ -7,6 +7,7 @@ import { MitarbeiterBearbeitenViewComponent } from "./components/mitarbeiter-bea
import { AuthGuard } from "./service/auth.service";
import { MitarbeiterErstellenComponent } from "./components/mitarbeiter-erstellen/mitarbeiter-erstellen.component";
import { QualifikationsverwaltungComponent } from "./components/qualifikationsverwaltung/qualifikationsverwaltung.component";
import { QulifikationErstellenComponent } from "./components/qualifikation-erstellen/qualifikation-erstellen.component";
export const routes: Routes = [
{
@ -18,6 +19,11 @@ export const routes: Routes = [
component: MitarbeiterverwaltungViewComponent,
canActivate: [AuthGuard],
},
{
path: "qualifikationerstellen",
component: QulifikationErstellenComponent,
canActivate: [AuthGuard],
},
{
path: "mitarbeitererstellen",
component: MitarbeiterErstellenComponent,

View file

@ -1,57 +1,52 @@
<div class="container">
<div class="header">
<div class="dropdown position-absolute top-0 end-0 m-3">
<button class="btn align-items-center d-flex" type="button" id="userDropdown" data-bs-toggle="dropdown"
aria-expanded="false">
<img src="user.svg" alt="User Icon" class="rounded-circle" style="width: 30px; height: 30px;">
</button>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
<li><a class="dropdown-item" href="/logout">Log out</a></li>
</ul>
</div>
<h1>Employees</h1>
</div>
<div class="header-actions">
<form [formGroup]="searchForm">
<div class="search-bar">
<input type="text" placeholder="Search employee" formControlName="search">
<button (click)="submit()">Search</button>
<div class="d-flex flex-row">
<app-navigation-bar [route]="'employee'" class="row" style="height: 100vh;"></app-navigation-bar>
<div class="container">
<div class="header">
<div class="dropdown position-absolute top-0 end-0 m-3">
<button class="btn align-items-center d-flex" type="button" id="userDropdown" data-bs-toggle="dropdown"
aria-expanded="false">
<img src="user.svg" alt="User Icon" class="rounded-circle" style="width: 30px; height: 30px;">
</button>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
<li><a class="dropdown-item" href="/logout">Log out</a></li>
</ul>
</div>
<p class="text-body-tertiary">Search for a propertiy of an Employee. eg. First Name</p>
</form>
<button (click)="createEmployee()" class="add-button">Add employee</button>
</div>
<h1>Employees</h1>
</div>
<table class="employee-table">
<thead>
<tr>
<th><span class="sortable">First Name</span></th>
<th><span class="sortable">Last Name</span></th>
<th>Street</th>
<th>Postcode</th>
<th>City</th>
<th>Phone</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@if (employees) {
@for (employee of employees; track employee) {
<tr>
<td>{{ employee.firstName }}</td>
<td>{{ employee.lastName }}</td>
<td>{{ employee.street }}</td>
<td>{{ employee.postcode }}</td>
<td>{{ employee.city }}</td>
<td>{{ employee.phone }}</td>
<td>
<button class="btn btn-primary me-2" (click)="editEmployee(employee.id)">Edit</button>
<button class="btn btn-danger" (click)="deleteEmployee(employee.id)">Delete</button>
</td>
</tr>
}
}
</tbody>
</table>
<div class="header-actions">
<form [formGroup]="searchForm">
<div class="search-bar">
<input type="text" placeholder="Search employee" formControlName="search">
<button (click)="submit()">Search</button>
</div>
<p class="text-body-tertiary">Search for a propertiy of an Employee. eg. First Name</p>
</form>
<button (click)="createEmployee()" class="add-button">Add employee</button>
</div>
<table class="employee-table">
<thead>
<tr>
<th><span class="sortable">First Name</span></th>
<th><span class="sortable">Last Name</span></th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@if (employees) {
@for (employee of employees; track employee) {
<tr>
<td>{{ employee.firstName }}</td>
<td>{{ employee.lastName }}</td>
<td>
<button class="btn btn-primary me-2" (click)="editEmployee(employee.id)">Edit</button>
<button class="btn btn-danger" (click)="deleteEmployee(employee.id)">Delete</button>
</td>
</tr>
}
}
</tbody>
</table>
</div>
</div>

View file

@ -5,13 +5,14 @@ import { EmployeeResponseDTO } from '../../models/mitarbeiter';
import { EmployeeService } from '../../service/employee.service';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { NavigationBarComponent } from '../navigation-bar/navigation-bar.component';
@Component({
selector: 'app-mitarbeiterverwaltung-view',
templateUrl: './mitarbeiterverwaltung-view.component.html',
styleUrls: ['./mitarbeiterverwaltung-view.component.css'],
standalone: true,
imports: [CommonModule, ReactiveFormsModule]
imports: [CommonModule, ReactiveFormsModule, NavigationBarComponent]
})
export class MitarbeiterverwaltungViewComponent implements OnInit {
employees: Array<EmployeeResponseDTO> = [];

View file

@ -5,18 +5,13 @@
</a>
<hr>
<ul class="nav nav-pills flex-column mb-auto">
<li class="nav-item">
<a href="#" class="nav-link active text-black" aria-current="page">
Dashboard
</a>
</li>
<li>
<a href="#" class="nav-link text-black">
<a [routerLink]="['/mitarbeiter']" (click)="employees()" [class.active]="route == 'employee'" class="nav-link text-black" aria-current="page">
Mitarbeiterverwaltung
</a>
</li>
<li>
<a href="#" class="nav-link text-black">
<a [routerLink]="['/qualifikationsverwaltung']" [class.active]="route == 'skill'" class="nav-link text-black">
Qualifikationsverwaltung
</a>
</li>

View file

@ -1,12 +1,18 @@
import { Component } from '@angular/core';
import { Component, Input } from '@angular/core';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
@Component({
selector: 'app-navigation-bar',
standalone: true,
imports: [],
imports: [RouterLink],
templateUrl: './navigation-bar.component.html',
styleUrl: './navigation-bar.component.css'
})
export class NavigationBarComponent {
constructor(private router: Router) { }
@Input() route: string = "";
employees() {
this.router.navigate(['mitarbeiter']);
}
}

View file

@ -0,0 +1 @@
<app-qualifikation-form [(skill)]="skill" (skillChange)="submitted($event)"></app-qualifikation-form>

View file

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

View file

@ -0,0 +1,29 @@
import { Component } from '@angular/core';
import { QualifikationFormComponent } from '../qualifikation-form/qualifikation-form.component';
import { QualificationGetDTO } from '../../models/skill';
import { SkillService } from '../../service/skill.service';
@Component({
selector: 'app-qulifikation-erstellen',
standalone: true,
imports: [QualifikationFormComponent],
templateUrl: './qualifikation-erstellen.component.html',
styleUrl: './qualifikation-erstellen.component.css'
})
export class QulifikationErstellenComponent {
public skill!: QualificationGetDTO;
constructor(private skillService: SkillService) { }
submitted(skill: QualificationGetDTO) {
console.log(skill);
this.skillService.createSkill(skill);
}
ngOnInit(): void {
this.skill = {
id: 0,
skill: '',
};
}
}

View file

@ -115,7 +115,7 @@ export class QualifikationFormComponent {
this.skill.skill = this.skillForm.get("name")?.value;
this.skillChange.emit(this.skill);
this.router.navigate(["/qualifikationen"]);
this.router.navigate(["/qualifikationsverwaltung"]);
}
ngOnChanges(): void {

View file

@ -1,47 +1,60 @@
<div class="container">
<div class="header">
<div class="dropdown position-absolute top-0 end-0 m-3">
<button class="btn align-items-center d-flex" type="button" id="userDropdown" data-bs-toggle="dropdown"
aria-expanded="false">
<img src="user.svg" alt="User Icon" class="rounded-circle" style="width: 30px; height: 30px;">
</button>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
<li><a class="dropdown-item" href="/logout">Log out</a></li>
</ul>
</div>
<h1>Qualifications</h1>
</div>
<div class="header-actions">
<form [formGroup]="searchForm">
<div class="search-bar">
<input type="text" placeholder="Search employee" formControlName="search">
<button (click)="submit()">Search</button>
<div class="d-flex flex-row">
<app-navigation-bar [route]="'skill'" class="row" style="height: 100vh;"></app-navigation-bar>
<div class="container">
<div class="header">
<div class="dropdown position-absolute top-0 end-0 m-3">
<button class="btn align-items-center d-flex" type="button" id="userDropdown" data-bs-toggle="dropdown"
aria-expanded="false">
<img src="user.svg" alt="User Icon" class="rounded-circle" style="width: 30px; height: 30px;">
</button>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
<li><a class="dropdown-item" href="/logout">Log out</a></li>
</ul>
</div>
<p class="text-body-tertiary">Search for a propertiy of a Qualification. eg. Name</p>
</form>
<button (click)="createSkill()" class="add-button">Add qualification</button>
</div>
<h1>Qualifications</h1>
</div>
<table class="employee-table">
<thead>
<tr>
<th><span class="sortable">Name</span></th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@if (skills) {
@for (skill of skills; track skill) {
<tr>
<td>{{ skill.skill }}</td>
<td>
<button class="btn btn-primary me-2" (click)="editSkill(skill.id)">Edit</button>
<button class="btn btn-danger" (click)="deleteSkill(skill.id)">Delete</button>
</td>
</tr>
<div class="d-flex flex-column">
<div class="row header-actions">
<form [formGroup]="searchForm">
<div class="search-bar">
<input type="text" placeholder="Search employee" formControlName="search">
<button (click)="submit()">Search</button>
</div>
<p class="text-body-tertiary">Search for a propertiy of a Qualification. eg. Name</p>
</form>
<div class="d-flex">
<button (click)="createSkill()" class="add-button me-auto">Add qualification</button>
</div>
</div>
@if (errorDeletingSkill) {
<div class="row">
<p class="alert alert-danger">This Qualification can not be deleted because it still has employees.</p>
</div>
}
}
</tbody>
</table>
</div>
<table class="employee-table">
<thead>
<tr>
<th><span class="sortable">Name</span></th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@if (skills) {
@for (skill of skills; track skill) {
<tr>
<td>{{ skill.skill }}</td>
<td>
<button class="btn btn-primary me-2" (click)="editSkill(skill.id)">Edit</button>
<button class="btn btn-danger" (click)="deleteSkill(skill.id)">Delete</button>
</td>
</tr>
}
}
</tbody>
</table>
</div>
</div>

View file

@ -3,19 +3,21 @@ import { QualificationGetDTO } from '../../models/skill';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { SkillService } from '../../service/skill.service';
import { Router } from '@angular/router';
import { NavigationBarComponent } from '../navigation-bar/navigation-bar.component';
@Component({
selector: 'app-qualifikationsverwaltung',
standalone: true,
imports: [ReactiveFormsModule],
imports: [ReactiveFormsModule, NavigationBarComponent],
templateUrl: './qualifikationsverwaltung.component.html',
styleUrl: './qualifikationsverwaltung.component.css'
})
export class QualifikationsverwaltungComponent {
skills: Array<QualificationGetDTO> = [];
public searchForm!: FormGroup;
public errorDeletingSkill = false;
constructor(private skillService: SkillService, private router: Router) {}
constructor(private skillService: SkillService, private router: Router) { }
submit() {
const searchTerm = this.searchForm.get("search")?.value || '';
@ -44,8 +46,15 @@ export class QualifikationsverwaltungComponent {
}
deleteSkill(id: number) {
this.skillService.deleteSkill(id);
this.skills = this.skills.filter(skill => skill.id != id);
this.skillService.getEmployeesBySkill(id).subscribe(employees => {
this.errorDeletingSkill = false;
if (employees.employees.length == 0) {
this.skillService.deleteSkill(id);
this.skills = this.skills.filter(skill => skill.id != id);
} else {
this.errorDeletingSkill = true;
}
});
}
ngOnInit(): void {

View file

@ -15,7 +15,7 @@ import { ActivatedRoute } from '@angular/router';
export class QualifikatonBearbeitenViewComponent {
public skill!: QualificationGetDTO;
constructor(private skillService: SkillService, private route: ActivatedRoute) {}
constructor(private skillService: SkillService, private route: ActivatedRoute) { }
submitted(skill: QualificationGetDTO) {
this.skillService.updateSkill(skill);

View file

@ -28,6 +28,10 @@ export class SkillService {
}
}
createSkill(skill: QualificationGetDTO) {
this.http.post(`${SkillService.BASE_URL}/qualifications`, this.getToPutDto(skill)).subscribe();
}
constructor(private http: HttpClient, private employeeService: EmployeeService) {
}