diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 087641c..87d779e 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -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] } +]; diff --git a/src/app/views/employee-detail/employee-detail.component.html b/src/app/views/employee-detail/employee-detail.component.html new file mode 100644 index 0000000..3b9955b --- /dev/null +++ b/src/app/views/employee-detail/employee-detail.component.html @@ -0,0 +1,91 @@ +
+
+
+

Info

+
+ + First Name + + First Name can not be Empty + + + Last Name + + Last Name can not be Empty + + + Postcode + money + + Postcode has to be exactly 5 long + + + City + location_city + + City can not be Empty + + + Street + house + + Street can not be Empty + + + Phone + phone + + Phone can not be Empty + + +
+
+
+

Skills

+
+ + Skill + + @for (skill of ($unusedSkills|async); track skill) { + {{skill.skill}} + } + + + +
+ + + Id + {{skill.id}} + + + Skill + +

{{skill.skill}} + + + + + + + + + + +

+
+
+ Abort + @if (isNewEmployee) { + + } + @else { + + } +
+ +
diff --git a/src/app/views/employee-detail/employee-detail.component.scss b/src/app/views/employee-detail/employee-detail.component.scss new file mode 100644 index 0000000..32145d6 --- /dev/null +++ b/src/app/views/employee-detail/employee-detail.component.scss @@ -0,0 +1,91 @@ +@use '@angular/material' as mat; + +.employee-detail { + display: flex; + flex-direction: column; + gap: 4rem; + + // Calculate Height and Spacing based on the Form Fields + $inner-form-height: 56px; + $form-height: 75.5px; + $inner-form-spacing: 4.5px; + $form-conter-padding: calc(($form-height + $inner-form-spacing - $inner-form-height) / 2); + $form-conter-padding-fab: calc(($form-conter-padding - $inner-form-spacing) / 2); + + &__info-view { + display: flex; + gap: 4rem; + + >* { + min-width: 0; + flex-basis: 0; + flex-grow: 1; + } + + &__base { + display: flex; + flex-direction: column; + + &__content { + display: flex; + flex-direction: column; + gap: $inner-form-spacing; + + 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; + } + } + + tr { + td.mat-column-skill { + width: 100%; + justify-self: center; + padding: $form-conter-padding 0; + + p { + display: flex; + height: $inner-form-height; + margin: 0; + align-items: center; + font-size: var(--mat-sys-body-large-size); + } + } + + td.mat-column-action { + width: 0; + vertical-align: middle; + padding-right: 8px; // Half the difference to the Width of a FAB + } + + &:first-of-type { + td.mat-column-skill { + padding-top: $inner-form-spacing; + } + + td.mat-column-action { + padding-bottom: $form-conter-padding-fab; + } + } + } + } + } + + &__action-row { + display: flex; + justify-content: space-between; + } +} diff --git a/src/app/views/employee-detail/employee-detail.component.ts b/src/app/views/employee-detail/employee-detail.component.ts new file mode 100644 index 0000000..3f0445b --- /dev/null +++ b/src/app/views/employee-detail/employee-detail.component.ts @@ -0,0 +1,137 @@ +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 { MatSelect, MatSelectModule } from '@angular/material/select'; +import { MatTableDataSource, MatTableModule } from '@angular/material/table'; +import { ActivatedRoute, Router, RouterLink } from '@angular/router'; +import { Employee, EmployeeService, NewEmployee, Qualification, QualificationService, UpdatedEmployee } from '@core/ems'; +import { NotificationService, NotificationType } from '@core/notification/notification.service'; +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 = new MatTableDataSource([]); + skillsDisplayedColumns = ['skill', 'action']; + $unusedSkills: Observable>; + + constructor( + private route: ActivatedRoute, + private router: Router, + private employeeService: EmployeeService, + private qualificationService: QualificationService, + private formBuilder: FormBuilder, + private notificationService: NotificationService + ) { + const idParam = this.route.snapshot.paramMap.get('id'); + this.isNewEmployee = idParam == null; + + this.employeeForm = this.formBuilder.group({ + 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); + this.skillsDataSource = new MatTableDataSource(employee.skillSet); + this.$unusedSkills = this.qualificationService.getAllQualifications().pipe( + map((allSkills: Array) => { + const usedSkillIds = new Set(employee.skillSet.map(skill => skill.id)); + return allSkills?.filter(skill => !usedSkillIds.has(skill.id)); + }) + ); + }); + } + + onCreate() { + if (!this.employeeForm.valid) { + return; + } + + const employee: NewEmployee = this.employeeForm.value; + employee.skillSet = this.skillsDataSource.data.map((qualification) => qualification.id); + this.employeeService.createEmployee({ requestBody: employee }).subscribe((response) => { + this.notificationService.publish(`${response.firstName} ${response.lastName} was created`, NotificationType.Information); + this.router.navigate(['']); + }); + } + + onSave() { + if (!this.employeeForm.valid) { + return; + } + + const id = this.employeeForm.value.id; + const employee: UpdatedEmployee = this.employeeForm.value; + employee.skillSet = this.skillsDataSource.data.map((qualification) => qualification.id); + + if (employee.skillSet.length == 0) { + this.employeeService.getAllEmployeeQualifications({ id }).subscribe((result) => { + result.skillSet?.forEach((skill) => { + this.employeeService.removeQualificationFromEmployee({ employeeId: id, qualificationId: skill.id }).subscribe(() => { }); + }); + }); + } + + this.employeeService.updateEmployee({ id: id, requestBody: employee }).subscribe((response) => { + this.notificationService.publish(`${response.firstName} ${response.lastName} was updated`, NotificationType.Information); + this.router.navigate(['']); + }); + } + + onAddSkill(select: MatSelect) { + if (select.value == undefined) { + return; + } + const newSkill = select.value as Qualification; + + const skills = this.skillsDataSource.data; + skills.push(newSkill); + this.skillsDataSource.data = skills; + + this.$unusedSkills = this.$unusedSkills.pipe( + map((skills) => skills.filter(skill => skill.id != newSkill.id)) + ); + } + + onRemoveSkill(skill: Qualification) { + const skills = this.skillsDataSource.data; + this.skillsDataSource.data = skills.filter(filterSkill => filterSkill.id != skill.id); + this.$unusedSkills = this.$unusedSkills.pipe( + map(skills => [...skills, skill]) + ); + } +}