Feature: Employee Edit and Create #18

Merged
SZUT-Dominik merged 2 commits from feature/employee-edit into trunk 2025-01-13 17:16:13 +00:00
4 changed files with 257 additions and 3 deletions
Showing only changes of commit f6664e1d8a - Show all commits

View file

@ -1,4 +1,11 @@
import {Routes} from '@angular/router';
import {DashboardComponent} from '@app/views/dashboard/dashboard.component';
import { Routes } from '@angular/router';
import { DashboardComponent } from '@app/views/dashboard/dashboard.component';
import { AuthService } from '@core/auth/auth.service';
export const routes: Routes = [{path: '', component: DashboardComponent, title: 'Home'}];
import { EmployeeDetailComponent } from './views/employee-detail/employee-detail.component';
export const routes: Routes = [
{ path: '', component: DashboardComponent, title: 'Home' },
{ path: 'employee/new', component: EmployeeDetailComponent, title: 'New Employee', canActivate: [AuthService] },
{ path: 'employee/:id', component: EmployeeDetailComponent, title: 'Edit Employee', canActivate: [AuthService] }
Review

Wenn der AuthServices als wert für canActivate gesetzt ist, dann wird der Nutzer, wenn er diese Seiten besucht, ohne eingeloggt zu sein, auf die Home Seite umgeleitet.

Wenn der `AuthServices` als wert für `canActivate` gesetzt ist, dann wird der Nutzer, wenn er diese Seiten besucht, ohne eingeloggt zu sein, auf die Home Seite umgeleitet.
];

View file

@ -0,0 +1,82 @@
<form class="employee-detail" [formGroup]="employeeForm" appearance="outline">
<div class="employee-detail__info-view">
<div class="employee-detail__info-view__base">
<h2>Info</h2>
<mat-form-field>
<mat-label>First Name</mat-label>
<input matInput formControlName="firstName">
</mat-form-field>
<mat-form-field>
<mat-label>Last Name</mat-label>
<input matInput formControlName="lastName">
</mat-form-field>
<mat-form-field>
<mat-label>Postcode</mat-label>
<mat-icon matSuffix>money</mat-icon>
<input matInput formControlName="postcode">
</mat-form-field>
<mat-form-field>
<mat-label>City</mat-label>
<mat-icon matSuffix>location_city</mat-icon>
<input matInput formControlName="city">
</mat-form-field>
<mat-form-field>
<mat-label>Street</mat-label>
<mat-icon matSuffix>house</mat-icon>
<input matInput formControlName="street">
</mat-form-field>
<mat-form-field>
<mat-label>Phone</mat-label>
<mat-icon matSuffix>phone</mat-icon>
<input matInput formControlName="phone">
</mat-form-field>
</div>
<div class="employee-detail__info-view__skills">
<h2>Skills</h2>
<div class="employee-detail__info-view__skills__action-row">
<mat-form-field>
<mat-label>Skill</mat-label>
<mat-select [disabled]="!($unusedSkills | async)?.length">
@for (skill of ($unusedSkills|async); track skill) {
<mat-option [value]="skill.skill">{{skill.skill}}</mat-option>
}
</mat-select>
</mat-form-field>
<button mat-fab class="shadowless">
<mat-icon>add</mat-icon>
</button>
</div>
<mat-table [dataSource]="skillsDataSource">
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef>Id</th>
<td mat-cell *matCellDef="let skill">{{skill.id}}</td>
</ng-container>
<ng-container matColumnDef="skill">
<th mat-header-cell *matHeaderCellDef>Skill</th>
<td mat-cell *matCellDef="let skill">
<p>{{skill.skill}}
</td>
</ng-container>
<ng-container matColumnDef="action">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let row">
<button mat-mini-fab class="warn shadowless">
<mat-icon>delete</mat-icon>
</button>
</td>
</ng-container>
<tr mat-row *matRowDef="let row; columns: skillsDisplayedColumns;"></tr>
</mat-table>
</div>
</div>
<div class="employee-detail__action-row">
<a mat-flat-button class="abort" routerLink="/">Abort</a>
@if (isNewEmployee) {
<button mat-flat-button (click)="onCreate()">Create</button>
}
@else {
<button mat-flat-button (click)="onSave()">Save</button>
}
</div>
</form>

View file

@ -0,0 +1,82 @@
@use '@angular/material' as mat;
.employee-detail {
display: flex;
flex-direction: column;
gap: 1rem;
&__info-view {
display: flex;
gap: 2rem;
>* {
min-width: 0;
flex-basis: 0;
flex-grow: 1;
}
&__base {
display: flex;
flex-direction: column;
input {
font-size: var(--mat-sys-body-large-size);
}
}
&__skills {
height: fit-content;
width: 100%;
&__action-row {
display: flex;
gap: 1rem;
mat-form-field:first-of-type {
flex-grow: 1;
}
}
p {
font-size: var(--mat-sys-body-large-size);
margin: 0;
}
tr {
height: 76px;
td.mat-column-skill {
width: 100%;
padding-top: 28px;
padding-bottom: 28px;
}
td.mat-column-action {
width: 0;
vertical-align: middle;
padding-left: 24px;
padding-right: 8px;
}
&:first-of-type {
height: 66px;
td.mat-column-skill {
padding-top: 0;
padding-bottom: 0;
}
td.mat-column-action {
padding-top: 10px;
vertical-align: unset;
}
}
}
}
}
&__action-row {
display: flex;
justify-content: space-between;
}
}

View file

@ -0,0 +1,83 @@
import { AsyncPipe } from '@angular/common';
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIcon } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { ActivatedRoute, RouterLink } from '@angular/router';
import { Employee, EmployeeService, Qualification, QualificationService } from '@core/ems';
import { map, Observable } from 'rxjs';
@Component({
selector: 'app-employee-detail',
imports: [
AsyncPipe,
MatIcon,
RouterLink,
MatButtonModule,
MatInputModule,
MatSelectModule,
MatTableModule,
MatFormFieldModule,
ReactiveFormsModule,
],
templateUrl: './employee-detail.component.html',
styleUrl: './employee-detail.component.scss'
})
export class EmployeeDetailComponent {
isNewEmployee: boolean;
employeeForm: FormGroup;
skillsDataSource: MatTableDataSource<Qualification> = new MatTableDataSource<Qualification>([]);
skillsDisplayedColumns = ['skill', 'action'];
$unusedSkills: Observable<Array<Qualification> | undefined>;
constructor(
private route: ActivatedRoute,
private employeeService: EmployeeService,
private qualificationService: QualificationService,
private formBuilder: FormBuilder
) {
const idParam = this.route.snapshot.paramMap.get('id');
this.isNewEmployee = idParam == null;
this.employeeForm = this.formBuilder.group<Employee>({
id: 0,
firstName: '',
lastName: '',
postcode: '',
city: '',
street: '',
phone: '',
skillSet: []
});
this.$unusedSkills = this.qualificationService.getAllQualifications();
if (this.isNewEmployee) {
return;
}
const id = parseInt(idParam as string);
this.employeeService.getEmployee({ id }).subscribe((employee) => {
this.employeeForm = this.formBuilder.group<Employee>(employee);
this.skillsDataSource = new MatTableDataSource<Qualification>(employee.skillSet);
this.$unusedSkills = this.qualificationService.getAllQualifications().pipe(
map((allSkills: Array<Qualification>) => {
const usedSkillIds = new Set(employee.skillSet.map(skill => skill.id));
return allSkills?.filter(skill => !usedSkillIds.has(skill.id));
})
);
});
}
onCreate() {
}
onSave() {
}
}