feat: add qualification editing functionality and styles

This commit is contained in:
Jan Gleytenhoover 2025-01-15 15:57:31 +01:00
parent 61597627c6
commit c11772f08b
Signed by: jank
GPG key ID: 50620ADD22CD330B
8 changed files with 296 additions and 173 deletions

View file

@ -32,8 +32,9 @@ export const routes: Routes = [
component: EmployeeDetailComponent, component: EmployeeDetailComponent,
}, },
{ {
path: "qualifikationbearbeiten", path: "qualifikationbearbeiten/:id",
component: QualifikatonBearbeitenViewComponent, component: QualifikatonBearbeitenViewComponent,
canActivate: [AuthGuard],
}, },
{ {
path: "**", path: "**",

View file

@ -0,0 +1,131 @@
body {
font-family: sans-serif;
margin: 0;
padding: 20px;
background-color: #f0f0f0;
}
.container {
width: 100%;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #fff;
}
h1, h2 {
font-size: 1.8rem;
margin-bottom: 15px;
}
.back-button {
background-color: #ccc;
color: #333;
border: none;
padding: 8px 12px;
border-radius: 3px;
margin-bottom: 15px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
}
input[type="text"] {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 1px;
}
.employee-list {
list-style: none;
padding: 0;
}
.employee-list li {
display: flex;
align-items: center;
margin-bottom: 5px;
}
.employee-icon {
margin-right: 5px;
}
.add-employee-section {
margin-top: 15px;
}
.add-employee-section select {
padding: 10px;
border: 1px solid #ddd;
border-radius: 3px;
margin-right: 10px;
}
.add-employee-button {
background-color: #06a63b;
color: #fff;
padding: 10px 15px;
border: none;
border-radius: 3px;
cursor: pointer;
margin-top: 10px;
}
.save-button {
background-color: #007bff;
color: #fff;
padding: 10px 15px;
border: none;
border-radius: 3px;
cursor: pointer;
margin-top: 20px;
display: block;
width: 100%;
}
.delete-skill-button {
color: #fff;
padding: 5px 8px;
border: none;
cursor: pointer;
margin-left: 4px;
margin-right: 4px;
border-radius: 3px;
}
.delete-skill-button img {
width: 15px;
height: 15px;
}
.add-employee-section {
margin-top: 15px;
}
.add-employee-section label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.add-employee-section input[type="text"] {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 3px;
}
.employee-container {
border: 1px solid #ccc;
padding: 20px;
border-radius: 1px;
margin-bottom: 20px;
}

View file

@ -1 +1,49 @@
<p>qualifikation-form works!</p> <div class="container">
<form [formGroup]="skillForm">
<button class="back-button">Back</button>
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" formControlName="name">
</div>
<div class="employee-container">
<h2>Employees possessing the qualification</h2>
<ul class="employee-list">
@for (employee of (employees | async)?.employees; track employee) {
@if (!isEmployeeHidden(employee.id)) {
<li>
<button (click)="removeEmployee(employee.id)" class="delete-skill-button">
<img src="Delete-button.svg" alt="Delete">
</button>
<span class="employee-name">{{employee.firstName}} {{employee.lastName}}</span>
</li>
}
}
@for (employee of addedEmployees | async; track employee) {
<li>
<button (click)="removeEmployee(employee.id)" class="delete-skill-button">
<img src="Delete-button.svg" alt="Delete">
</button>
<span class="employee-name">{{employee.firstName}} {{employee.lastName}}</span>
</li>
}
</ul>
<div class="add-employee-section">
<select formControlName="newEmployee">
@for (employee of allEmployees | async; track employee) {
@if (!employeeHasSkill(employee)) {
<option value="{{employee.id}}">{{employee.firstName}} {{employee.lastName}}</option>
}
}
</select>
<button class="add-employee-button" (click)="addEmployee()">Add employee</button>
</div>
</div>
<button class="save-button">Save</button>
</form>
</div>

View file

@ -1,12 +1,90 @@
import { Component } from '@angular/core'; import { Component, EventEmitter, Input, Output } from '@angular/core';
import { EmployeesForAQualificationDTO, QualificationGetDTO } from '../../models/skill';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { SkillService } from '../../service/skill.service';
import { EmployeeService } from '../../service/employee.service';
import { Observable, of, tap } from 'rxjs';
import { EmployeeNameDataDTO, EmployeeResponseDTO } from '../../models/mitarbeiter';
import { AsyncPipe } from '@angular/common';
@Component({ @Component({
selector: 'app-qualifikation-form', selector: 'app-qualifikation-form',
standalone: true, standalone: true,
imports: [], imports: [ReactiveFormsModule, AsyncPipe],
templateUrl: './qualifikation-form.component.html', templateUrl: './qualifikation-form.component.html',
styleUrl: './qualifikation-form.component.css' styleUrl: './qualifikation-form.component.css'
}) })
export class QualifikationFormComponent { export class QualifikationFormComponent {
@Input() skill!: QualificationGetDTO;
@Output() skillChange = new EventEmitter<QualificationGetDTO>();
public skillForm!: FormGroup;
public employees: Observable<EmployeesForAQualificationDTO> = of();
public allEmployees: Observable<Array<EmployeeResponseDTO>> = of([]);
public hiddenEmployees: Array<number> = [];
public addedEmployees: Observable<Array<EmployeeResponseDTO>> = of([]);
public addedEmployeesIds: Array<number> = [];
errorMessages: Record<string, string> = {};
constructor(private skillService: SkillService, private employeeService: EmployeeService) { }
removeEmployee(id: number) {
this.hiddenEmployees.push(id);
}
isEmployeeHidden(id: number) {
for (const employeeId of this.hiddenEmployees) {
if (id == employeeId) {
return true
}
}
return false;
}
employeeHasSkill(employee: EmployeeResponseDTO) {
for (const id of this.addedEmployeesIds) {
console.log(id, employee.id);
if (id == employee.id) {
return true;
}
}
for (const employeeSkill of employee.skillSet || []) {
if (employeeSkill.id == this.skill.id) {
return true;
}
}
return false;
}
addEmployee() {
console.log("a");
const employeeId = this.skillForm.get("newEmployee")?.value;
this.employeeService.getEmployeeById(employeeId).subscribe(employee => {
this.addedEmployees.pipe(tap(employeeList => {
employeeList.push(employee);
this.addedEmployeesIds.push(employee.id);
})).subscribe();
});
}
setUpForm() {
this.skillForm = new FormGroup({
name: new FormControl(this.skill.skill, Validators.required),
newEmployee: new FormControl(),
});
}
ngOnChanges(): void {
this.setUpForm();
this.employees = this.skillService.getEmployeesBySkill(this.skill.id);
}
ngOnInit() {
this.setUpForm();
this.allEmployees = this.employeeService.getAllEmployees();
}
} }

View file

@ -1,131 +0,0 @@
body {
font-family: sans-serif;
margin: 0;
padding: 20px;
background-color: #f0f0f0;
}
.container {
width: 100%;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #fff;
}
h1, h2 {
font-size: 1.8rem;
margin-bottom: 15px;
}
.back-button {
background-color: #ccc;
color: #333;
border: none;
padding: 8px 12px;
border-radius: 3px;
margin-bottom: 15px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
}
input[type="text"] {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 1px;
}
.employee-list {
list-style: none;
padding: 0;
}
.employee-list li {
display: flex;
align-items: center;
margin-bottom: 5px;
}
.employee-icon {
margin-right: 5px;
}
.add-employee-section {
margin-top: 15px;
}
.add-employee-section select {
padding: 10px;
border: 1px solid #ddd;
border-radius: 3px;
margin-right: 10px;
}
.add-employee-button {
background-color: #06a63b;
color: #fff;
padding: 10px 15px;
border: none;
border-radius: 3px;
cursor: pointer;
margin-top: 10px;
}
.save-button {
background-color: #007bff;
color: #fff;
padding: 10px 15px;
border: none;
border-radius: 3px;
cursor: pointer;
margin-top: 20px;
display: block;
width: 100%;
}
.delete-skill-button {
color: #fff;
padding: 5px 8px;
border: none;
cursor: pointer;
margin-left: 4px;
margin-right: 4px;
border-radius: 3px;
}
.delete-skill-button img {
width: 15px;
height: 15px;
}
.add-employee-section {
margin-top: 15px;
}
.add-employee-section label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.add-employee-section input[type="text"] {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 3px;
}
.employee-container {
border: 1px solid #ccc;
padding: 20px;
border-radius: 1px;
margin-bottom: 20px;
}

View file

@ -1,36 +1 @@
<div class="container"> <app-qualifikation-form [(skill)]="skill" (skillChange)="submitted($event)"></app-qualifikation-form>
<button class="back-button">Back</button>
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" value="(Hier kommt name der gewählten qualification hin)">
</div>
<div class="employee-container">
<h2>Employees possessing the qualification</h2>
<ul class="employee-list">
<li>
<button class="delete-skill-button">
<img src="Delete-button.svg" alt="Delete">
</button>
<span class="employee-name">Max Mustermann</span>
</li>
<li>
<button class="delete-skill-button">
<img src="Delete-button.svg" alt="Delete">
</button>
<span class="employee-name">Mehdi Boudjoudi</span>
</li>
</ul>
<div class="add-employee-section">
<label for="employeeSearch">Search for employee</label>
<input type="text" id="employeeSearch" placeholder="Last name of employee">
<button class="add-employee-button">Add employee</button>
</div>
</div>
<button class="save-button">Save</button>
</div>

View file

@ -1,12 +1,38 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { QualifikationFormComponent } from '../qualifikation-form/qualifikation-form.component';
import { SkillService } from '../../service/skill.service';
import { FormGroup } from '@angular/forms';
import { QualificationGetDTO } from '../../models/skill';
import { ActivatedRoute } from '@angular/router';
@Component({ @Component({
selector: 'app-qualifikaton-bearbeiten-view', selector: 'app-qualifikaton-bearbeiten-view',
standalone: true, standalone: true,
imports: [], imports: [QualifikationFormComponent],
templateUrl: './qualifikaton-bearbeiten-view.component.html', templateUrl: './qualifikaton-bearbeiten-view.component.html',
styleUrl: './qualifikaton-bearbeiten-view.component.css' styleUrl: './qualifikaton-bearbeiten-view.component.css'
}) })
export class QualifikatonBearbeitenViewComponent { export class QualifikatonBearbeitenViewComponent {
public skill!: QualificationGetDTO;
constructor(private skillService: SkillService, private route: ActivatedRoute) {}
submitted(skill: QualificationGetDTO) {
}
ngOnInit(): void {
this.skill = {
id: 0,
skill: '',
};
this.skillService.getAllSkills().subscribe(skills => {
for (const skill of skills) {
if (skill.id == Number(this.route.snapshot.params['id'])) {
this.skill = skill;
}
}
});
}
} }

View file

@ -1,7 +1,8 @@
import { HttpClient } from "@angular/common/http"; import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { QualificationGetDTO } from "../models/skill"; import { EmployeesForAQualificationDTO, QualificationGetDTO } from "../models/skill";
import { Observable } from "rxjs"; import { Observable } from "rxjs";
import { EmployeeNameDataDTO } from "../models/mitarbeiter";
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -17,4 +18,8 @@ export class SkillService {
getAllSkills(): Observable<Array<QualificationGetDTO>> { getAllSkills(): Observable<Array<QualificationGetDTO>> {
return this.http.get<Array<QualificationGetDTO>>(`${SkillService.BASE_URL}/qualifications`); return this.http.get<Array<QualificationGetDTO>>(`${SkillService.BASE_URL}/qualifications`);
} }
getEmployeesBySkill(id: number): Observable<EmployeesForAQualificationDTO> {
return this.http.get<EmployeesForAQualificationDTO>(`${SkillService.BASE_URL}/qualifications/${id}/employees`);
}
} }