Compare commits

...
Sign in to create a new pull request.

7 commits

Author SHA1 Message Date
856d3e44b9
Merge pull request 'Feature: Filter Dashboard - Simple Filter for visible values' (#21) from feature/filter-dashboard into trunk
All checks were successful
Quality Check / Linting (push) Successful in 22s
Reviewed-on: #21
2025-01-16 10:50:38 +00:00
Rajbir Singh
748df61dc6 Feature: Filter Dashboard - Simple Filter for visible values
All checks were successful
Quality Check / Linting (push) Successful in 27s
Quality Check / Linting (pull_request) Successful in 26s
2025-01-16 10:32:02 +01:00
c08df8c2d8
Merge pull request 'Feature: Delete Employee - function created' (#20) from feature/delete-employee into trunk
All checks were successful
Quality Check / Linting (push) Successful in 21s
Reviewed-on: #20
Reviewed-by: SZUT-Ole <ole.kueck@hmmh.de>
2025-01-15 12:50:30 +00:00
Rajbir Singh
9d3be76744 Feature: Delete Employee - function created
All checks were successful
Quality Check / Linting (push) Successful in 22s
Quality Check / Linting (pull_request) Successful in 21s
2025-01-15 12:53:25 +01:00
c43d73aadb
Merge pull request 'Feature: Dashboard' (#19) from feature/dashboard into trunk
All checks were successful
Quality Check / Linting (push) Successful in 21s
Reviewed-on: #19
Reviewed-by: SZUT-Ole <ole.kueck@hmmh.de>
2025-01-15 07:46:58 +00:00
Rajbir Singh
3bffc414ec Feature: Dashboard - layout, style
All checks were successful
Quality Check / Linting (push) Successful in 28s
Quality Check / Linting (pull_request) Successful in 27s
2025-01-15 08:40:03 +01:00
3a528b0a01
Merge pull request 'Feature: Employee Edit and Create' (#18) from feature/employee-edit into trunk
All checks were successful
Quality Check / Linting (push) Successful in 22s
Reviewed-on: #18
2025-01-13 17:16:13 +00:00
8 changed files with 277 additions and 15 deletions

View file

@ -0,0 +1,8 @@
interface DeleteModalData {
title: string,
description: string,
cancel: string,
ok: string,
}
export default DeleteModalData;

View file

@ -0,0 +1,8 @@
<h2 mat-dialog-title>{{ data.title}}</h2>
<mat-dialog-content>
<p>{{data.description}}</p>
</mat-dialog-content>
<mat-dialog-actions class="delete-modal">
<button mat-flat-button [mat-dialog-close]="false">{{data.cancel}}</button>
<button mat-flat-button [mat-dialog-close]="true" class="warn">{{data.ok}}</button>
</mat-dialog-actions>

View file

@ -0,0 +1,4 @@
.delete-modal {
display: flex;
justify-content: space-between;
}

View file

@ -0,0 +1,32 @@
import {Component, Inject} from '@angular/core';
import {MatButton} from '@angular/material/button';
import {
MAT_DIALOG_DATA,
MatDialogActions, MatDialogClose,
MatDialogContent,
MatDialogTitle
} from '@angular/material/dialog';
import DeleteModalData from '@app/delete-model/DeleteModalData';
@Component({
selector: 'app-delete-model',
imports: [
MatDialogContent,
MatDialogActions,
MatDialogTitle,
MatButton,
MatDialogClose
],
templateUrl: './delete-model.component.html',
styleUrl: './delete-model.component.scss'
})
export class DeleteModelComponent {
constructor(
@Inject(MAT_DIALOG_DATA) protected data: DeleteModalData,
) {
}
}

8
src/app/views/dashboard/Filter.d.ts vendored Normal file
View file

@ -0,0 +1,8 @@
import {Qualification} from '@core/ems';
interface Filter {
fuzzy: string,
qualification: Qualification|undefined
}
export default Filter;

View file

@ -1,4 +1,63 @@
<a mat-flat-button routerLink="/employee/new">New Employee</a>
<a mat-flat-button routerLink="/employee/1">Edit Employee 3</a>
<button mat-flat-button (click)="testInfo()">Test Info</button>
<button mat-flat-button (click)="testError()">Test Error</button>
<div class="dashboard">
@if (auth.$user|async; as user) {
<div class="dashboard__action-row">
<mat-form-field>
<mat-label>Search</mat-label>
<input matInput (keyup)="onFuzzyFilter($event)">
</mat-form-field>
<mat-form-field>
<mat-label>Qualification</mat-label>
<mat-select [(value)]="selectedQualificationFilter" (valueChange)="onFilterUpdate()">
<mat-option>None</mat-option>
@for (skill of ($qualifications|async); track skill) {
<mat-option [value]="skill">{{ skill.skill }}</mat-option>
}
</mat-select>
</mat-form-field>
<button mat-fab class="shadowless" routerLink="employee/new">
<mat-icon>add</mat-icon>
</button>
</div>
<mat-table [dataSource]="employeeDataSource" class="dashboard__employees">
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef>Id</th>
<td mat-cell *matCellDef="let employee">{{ employee.id }}</td>
</ng-container>
<ng-container matColumnDef="first-name">
<th mat-header-cell *matHeaderCellDef>First Name</th>
<td mat-cell *matCellDef="let employee">{{ employee.firstName }}</td>
</ng-container>
<ng-container matColumnDef="last-name">
<th mat-header-cell *matHeaderCellDef>Last Name</th>
<td mat-cell *matCellDef="let employee">{{ employee.lastName }}</td>
</ng-container>
<ng-container matColumnDef="skills">
<th mat-header-cell *matHeaderCellDef>Skills</th>
<td mat-cell *matCellDef="let employee">
<ul>
@for (skill of employee.skillSet; track skill) {
<li>{{ skill.skill }}</li>
}
</ul>
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let employee">
<button mat-mini-fab class="shadowless" routerLink="employee/{{employee.id}}">
<mat-icon>edit</mat-icon>
</button>
<button mat-mini-fab class="shadowless warn" (click)="onDelete(employee)">
<mat-icon>delete</mat-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="employeesDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: employeesDisplayedColumns;"></tr>
</mat-table>
} @else {
<p>Log in to see more!</p>
}
</div>

View file

@ -0,0 +1,49 @@
.dashboard {
display: flex;
flex-direction: column;
gap: 1rem;
&__action-row {
display: flex;
gap: 1rem;
:first-child {
flex-grow: 1;
}
}
&__employees {
width: 100%;
tr {
display: flex;
width: 100%;
.mat-column-id{
width: 4rem;
}
.mat-column-first-name, .mat-column-last-name, .mat-column-skills {
width: 0;
flex-grow: 1;
}
ul{
margin: 0;
}
th.mat-column-actions{
width: calc(40px * 2 + 1rem);
}
td.mat-column-actions{
gap: 1rem;
display: flex;
padding: 0;
padding-top: 0.25rem;
align-items: start;
}
}
}
}

View file

@ -1,20 +1,114 @@
import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { RouterLink } from '@angular/router';
import { NotificationService, NotificationType } from '@core/notification/notification.service';
import {AsyncPipe} from '@angular/common';
import {Component} from '@angular/core';
import {ReactiveFormsModule} from '@angular/forms';
import {MatButtonModule} from '@angular/material/button';
import {MatDialog} from '@angular/material/dialog';
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 {RouterLink} from '@angular/router';
import {DeleteModelComponent} from '@app/delete-model/delete-model.component';
import Filter from '@app/views/dashboard/Filter';
import {AuthService} from '@core/auth/auth.service';
import {Employee, EmployeeService, Qualification, QualificationService} from '@core/ems';
import {NotificationService, NotificationType} from '@core/notification/notification.service';
import {Observable} from 'rxjs';
@Component({
selector: 'app-dashboard',
imports: [MatButtonModule, RouterLink],
imports: [MatButtonModule, MatSelectModule, MatFormFieldModule, MatTableModule, ReactiveFormsModule, MatInputModule, MatIcon, RouterLink, AsyncPipe],
templateUrl: './dashboard.component.html',
styleUrl: './dashboard.component.scss'
})
export class DashboardComponent {
constructor(private notifications: NotificationService) { }
testInfo() {
this.notifications.publish('Cake', NotificationType.Information);
selectedQualificationFilter: Qualification | undefined;
fuzzyFilter: string = '';
employeeDataSource: MatTableDataSource<Employee> = new MatTableDataSource<Employee>([]);
employeesDisplayedColumns = ['id', 'first-name', 'last-name', 'skills', 'actions'];
$qualifications: Observable<Array<Qualification> | undefined>;
constructor(
protected auth: AuthService,
private employeeService: EmployeeService,
private qualificationService: QualificationService,
private dialog: MatDialog,
private notifications: NotificationService
) {
this.$qualifications = this.qualificationService.getAllQualifications();
this.employeeService.getAllEmployees().subscribe((employees) => {
this.employeeDataSource = new MatTableDataSource(employees);
this.employeeDataSource.filterPredicate = (employee: Employee, rawFilter: string): boolean => {
const filter = JSON.parse(rawFilter) as Filter;
if (filter.fuzzy == '' && filter.qualification == undefined) {
return true;
}
if (
filter.qualification != undefined
&& !employee.skillSet.map((skill) => skill.id).includes(filter.qualification.id)
) {
return false;
}
if (
filter.fuzzy != ''
&& !employee.id.toString().includes(filter.fuzzy)
&& !employee.firstName.toLowerCase().includes(filter.fuzzy)
&& !employee.lastName.toLowerCase().includes(filter.fuzzy)
) {
return false;
}
return true;
};
});
}
testError() {
this.notifications.publish('Cake', NotificationType.Error);
}}
onDelete(employee: Employee) {
const dialogRef = this.dialog.open(DeleteModelComponent, {
data: {
title: `Delete ${employee.firstName} ${employee.lastName}`,
description: `Do you really want to delete ${employee.firstName} ${employee.lastName}?`,
cancel: 'No',
ok: 'Yes',
}
});
dialogRef.afterClosed().subscribe((accepted: boolean) => {
if (!accepted) {
return;
}
this.employeeService.deleteEmployee({id: employee.id}).subscribe(() => {
const data = this.employeeDataSource.data;
const i = data.indexOf(employee);
if (i != -1) {
data.splice(i, 1);
this.employeeDataSource.data = data;
}
this.notifications.publish(`Deleted ${employee.firstName} ${employee.lastName}`, NotificationType.Information);
});
});
}
onFuzzyFilter(event: KeyboardEvent) {
this.fuzzyFilter = (event.target as HTMLInputElement).value;
this.onFilterUpdate();
}
onFilterUpdate() {
const skill = this.selectedQualificationFilter;
const fuzzy = this.fuzzyFilter;
if (skill == undefined && fuzzy == '') {
this.employeeDataSource.filter = '';
return;
}
this.employeeDataSource.filter = JSON.stringify({
qualification: skill,
fuzzy: fuzzy.toLowerCase()
} as Filter);
}
}