import { computed, Component, inject, debounced, input, linkedSignal, afterNextRender } from '@angular/core'; import { form as createForm, FormField, FormRoot } from '@angular/forms/signals'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatChipsModule } from '@angular/material/chips'; import { MatOptionModule } from '@angular/material/core'; import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; import { MatSnackBar } from '@angular/material/snack-bar'; import { MatTableModule } from '@angular/material/table'; import { Router } from '@angular/router'; import moment from 'moment'; import { AttendanceTypeService } from '../attendance/attendance-type.service'; import { Employee } from '../employee/employee'; import { EmployeeService } from '../employee/employee.service'; import { ErrorStateComponent } from '../shared/error-state/error-state.component'; import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component'; import { EmployeeAttendance } from './employee-attendance'; import { EmployeeAttendanceService } from './employee-attendance.service'; export interface EmployeeAttendanceFormData { startDate: Date; finishDate: Date; employee: string | Employee | null; attendances: { attendanceType: number | null }[]; } @Component({ selector: 'app-employee-attendance', templateUrl: './employee-attendance.component.html', styleUrls: ['./employee-attendance.component.css'], host: { '(window:keydown.f2)': 'focusDate($event)', }, imports: [ FormField, FormRoot, MatFormFieldModule, MatInputModule, MatDatepickerModule, MatAutocompleteModule, MatOptionModule, MatButtonModule, MatTableModule, MatSelectModule, MatIconModule, MatChipsModule, SkeletonLoaderComponent, ErrorStateComponent, ], }) export class EmployeeAttendanceComponent { private router = inject(Router); private snackBar = inject(MatSnackBar); private ser = inject(EmployeeAttendanceService); private employeeSer = inject(EmployeeService); private attendanceTypeSer = inject(AttendanceTypeService); id = input(null, { transform: (v: string | null | undefined) => v ?? null }); startDate = input(null, { transform: (v: string | null | undefined) => v ?? null }); finishDate = input(null, { transform: (v: string | null | undefined) => v ?? null }); infoResource = this.ser.get(this.id, this.startDate, this.finishDate); info = computed(() => this.infoResource.value() ?? new EmployeeAttendance()); attendanceTypesResource = this.attendanceTypeSer.list(); attendanceTypes = computed(() => this.attendanceTypesResource.value() ?? []); model = linkedSignal({ source: this.info, computation: (info): EmployeeAttendanceFormData => ({ startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(), finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(), employee: info.employee ?? null, attendances: info.body.map((x) => ({ attendanceType: x.attendanceType.id, })), }), }); form = createForm(this.model); dataSource = computed(() => this.info().body); displayedColumns = ['date', 'status', 'prints']; employeesSearch = computed(() => { const x = this.model().employee; return typeof x === 'string' ? x : null; }); debouncedEmployeeSearch = debounced(this.employeesSearch, 150); employeesResource = this.employeeSer.autocomplete(computed(() => this.debouncedEmployeeSearch.value() ?? '')); employees = computed(() => this.employeesResource.value() ?? []); focusDate(event: Event) { event.preventDefault(); this.form().focusBoundControl(); } constructor() { afterNextRender(() => { this.form().focusBoundControl(); }); } displayFn(employee?: Employee | string): string { return !employee ? '' : typeof employee === 'string' ? employee : employee.name; } getClass(index: number) { const array = this.model().attendances; if (!array || !array[index]) return ''; const id = array[index].attendanceType; const typeObj = this.attendanceTypes().filter((x) => x.id === id)[0]; if (!typeObj) return ''; return typeObj.name.toLowerCase().replace(/(\s+\+\s+)|(\s+)/g, '-'); } show() { const formValue = this.model(); const currentInfo = this.info(); currentInfo.startDate = moment(formValue.startDate).format('DD-MMM-YYYY'); currentInfo.finishDate = moment(formValue.finishDate).format('DD-MMM-YYYY'); currentInfo.employee = typeof formValue.employee !== 'string' && formValue.employee ? formValue.employee : undefined; if (currentInfo.employee) { this.router.navigate(['/employee-attendance', currentInfo.employee.id], { queryParams: { startDate: currentInfo.startDate, finishDate: currentInfo.finishDate, }, }); } } save() { this.ser.save(this.getEmployeeAttendance()).subscribe({ next: () => { this.snackBar.open('', 'Success'); }, error: (error) => { this.snackBar.open(error as string, 'Danger'); }, }); } getEmployeeAttendance(): EmployeeAttendance { const formValue = this.model(); const currentInfo = this.info(); currentInfo.startDate = moment(formValue.startDate).format('DD-MMM-YYYY'); currentInfo.finishDate = moment(formValue.finishDate).format('DD-MMM-YYYY'); currentInfo.employee = typeof formValue.employee !== 'string' && formValue.employee ? formValue.employee : undefined; const array = formValue.attendances; if (array) { currentInfo.body.forEach((item, index) => { if (array[index]) { item.attendanceType.id = array[index].attendanceType ?? 0; } }); } return currentInfo; } }