Files
brewman/overlord/src/app/incentive/incentive.component.ts
T
tanshu 1e7476c5d9 Moved to uv from poetry
Updated ruff and mypy, accepted all changes
Central exception management and injected the Session.
This removed a lot of duplicated boilerplate code.
Added health check
Updated to Angular 21
2026-02-24 03:08:05 +00:00

222 lines
6.8 KiB
TypeScript

import { CurrencyPipe, DecimalPipe } from '@angular/common';
import { Component, ElementRef, HostListener, inject, OnInit, ViewChild } from '@angular/core';
import { FormArray, FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialog } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatSnackBar } from '@angular/material/snack-bar';
import { MatTableModule } from '@angular/material/table';
import { ActivatedRoute, Router } from '@angular/router';
import moment from 'moment';
import { BehaviorSubject } from 'rxjs';
import { map } from 'rxjs/operators';
import { AuthService } from '../auth/auth.service';
import { Account } from '../core/account';
import { AccountBalance } from '../core/account-balance';
import { Incentive } from '../core/incentive';
import { User } from '../core/user';
import { Voucher } from '../core/voucher';
import { VoucherService } from '../core/voucher.service';
import { ConfirmDialogComponent } from '../shared/confirm-dialog/confirm-dialog.component';
import { LocalTimePipe } from '../shared/local-time.pipe';
import { IncentiveDataSource } from './incentive-datasource';
@Component({
selector: 'app-incentive',
templateUrl: './incentive.component.html',
styleUrls: ['./incentive.component.css'],
imports: [
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule,
MatDatepickerModule,
MatTableModule,
MatIconModule,
MatButtonModule,
DecimalPipe,
CurrencyPipe,
LocalTimePipe,
],
})
export class IncentiveComponent implements OnInit {
private route = inject(ActivatedRoute);
private router = inject(Router);
private dialog = inject(MatDialog);
private snackBar = inject(MatSnackBar);
auth = inject(AuthService);
private ser = inject(VoucherService);
@ViewChild('dateElement', { static: true }) dateElement!: ElementRef<HTMLInputElement>;
public incentiveObservable = new BehaviorSubject<Incentive[]>([]);
dataSource: IncentiveDataSource = new IncentiveDataSource(this.incentiveObservable);
form: FormGroup<{
date: FormControl<moment.Moment>;
incentives: FormArray<
FormGroup<{
points: FormControl<number>;
}>
>;
}>;
voucher: Voucher = new Voucher();
account: Account = new Account();
accBal: AccountBalance | null = null;
displayedColumns = ['name', 'designation', 'department', 'daysWorked', 'points', 'amount'];
@HostListener('window:keydown.f2', ['$event'])
focusDate(event: Event) {
event.preventDefault();
this.dateElement.nativeElement.focus();
this.dateElement.nativeElement.select();
}
constructor() {
this.form = new FormGroup({
date: new FormControl(moment(new Date()), { nonNullable: true }),
incentives: new FormArray<FormGroup<{ points: FormControl<number> }>>([]),
});
// Listen to Date Change
this.form.controls.date.valueChanges.pipe(map((x) => moment(x).format('DD-MMM-YYYY'))).subscribe((x) => {
if (x !== this.voucher.date && !this.voucher.id) {
return this.ser.getIncentive(x).subscribe((voucher: Voucher) => {
this.loadVoucher(voucher);
});
}
return '';
});
}
ngOnInit() {
this.route.data.subscribe((value) => {
const data = value as { voucher: Voucher };
this.loadVoucher(data.voucher);
});
}
loadVoucher(voucher: Voucher) {
this.voucher = voucher;
this.form.controls.date.setValue(moment(this.voucher.date, 'DD-MMM-YYYY'));
this.form.controls.incentives.clear();
this.voucher.incentives.forEach((x) =>
this.form.controls.incentives.push(
new FormGroup({
points: new FormControl(x.points, { nonNullable: true }),
}),
),
);
this.dataSource = new IncentiveDataSource(this.incentiveObservable);
this.incentiveObservable.next(this.voucher.incentives);
}
totalPoints() {
return this.voucher.incentives.map((item) => item.daysWorked * item.points).reduce((sum, item) => sum + item);
}
pointValue() {
return Math.round(((this.voucher.incentive as number) * 100) / this.totalPoints()) / 100;
}
less(row: Incentive, i: number) {
if (row.points >= 1) {
row.points -= 1;
this.form.controls.incentives.controls[i].setValue({
points: row.points,
});
}
}
change(row: Incentive, i: number) {
row.points = this.form.controls.incentives.controls[i].value.points ?? 0;
this.form.controls.incentives.controls[i].setValue({ points: row.points });
}
more(row: Incentive, i: number) {
row.points += 1;
this.form.controls.incentives.controls[i].setValue({ points: row.points });
}
amount(row: Incentive) {
return row.points * row.daysWorked * this.pointValue();
}
canSave() {
if (!this.voucher.id) {
return true;
}
if (this.voucher.posted && this.auth.allowed('edit-posted-vouchers')) {
return true;
}
return this.voucher.user.id === (this.auth.user as User).id || this.auth.allowed("edit-other-user's-vouchers");
}
post() {
this.ser.post(this.voucher.id as string).subscribe({
next: (result) => {
this.loadVoucher(result);
this.snackBar.open('Voucher Posted', 'Success');
},
error: (error) => {
this.snackBar.open(error, 'Danger');
},
});
}
save() {
const voucher: Voucher = this.getVoucher();
this.ser.saveOrUpdate(voucher).subscribe({
next: (result) => {
if (voucher.id === result.id) {
this.loadVoucher(result);
} else {
this.router.navigate(['/incentive', result.id]);
}
},
error: (error) => {
this.snackBar.open(error, 'Danger');
},
});
}
getVoucher(): Voucher {
const formModel = this.form.value;
this.voucher.date = moment(formModel.date).format('DD-MMM-YYYY');
const array = this.form.controls.incentives;
this.voucher.incentives.forEach((item, index) => {
item.points = array.controls[index].value.points ?? 0;
});
return this.voucher;
}
delete() {
this.ser.delete(this.voucher.id as string).subscribe({
next: () => {
this.snackBar.open('', 'Success');
this.router.navigate(['/incentive'], { replaceUrl: true });
},
error: (error) => {
this.snackBar.open(error, 'Danger');
},
});
}
confirmDelete(): void {
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
width: '250px',
data: { title: 'Delete Voucher?', content: 'Are you sure? This cannot be undone.' },
});
dialogRef.afterClosed().subscribe((result: boolean) => {
if (result) {
this.delete();
}
});
}
}