Files
brewman/overlord/src/app/incentive/incentive.component.ts
T

200 lines
6.1 KiB
TypeScript

import { CurrencyPipe, DecimalPipe } from '@angular/common';
import { signal, Component, inject, input, computed, linkedSignal, effect } from '@angular/core';
import { form as createForm, FormField, FormRoot } from '@angular/forms/signals';
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 { Router } from '@angular/router';
import moment from 'moment';
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';
export interface IncentiveFormData {
date: moment.Moment;
incentives: Incentive[];
}
@Component({
selector: 'app-incentive',
templateUrl: './incentive.component.html',
styleUrls: ['./incentive.component.css'],
host: {
'(window:keydown.f2)': 'focusDate($event)',
},
imports: [
FormField,
FormRoot,
MatFormFieldModule,
MatInputModule,
MatDatepickerModule,
MatTableModule,
MatIconModule,
MatButtonModule,
DecimalPipe,
CurrencyPipe,
LocalTimePipe,
],
})
export class IncentiveComponent {
private router = inject(Router);
private dialog = inject(MatDialog);
private snackBar = inject(MatSnackBar);
auth = inject(AuthService);
private ser = inject(VoucherService);
id = input(null, { transform: (v: string | null | undefined) => v ?? null });
d = input(null, { transform: (v: string | null | undefined): string | null => v ?? null });
voucherResource = this.ser.getVoucher(this.id, signal('Incentive'), signal(null), this.d);
voucher = computed(() => this.voucherResource.value() ?? new Voucher({ date: this.d() as string }));
model = linkedSignal({
source: this.voucher,
computation: (v): IncentiveFormData => ({
date: v.date ? moment(v.date, 'DD-MMM-YYYY') : moment(new Date()),
incentives: v.incentives.map((x) => new Incentive({ ...x })),
}),
});
form = createForm(this.model);
constructor() {
effect(async () => {
const date = this.form.date().value().format('DD-MMM-YYYY');
if (date !== this.d() && !this.id()) {
this.router.navigate(['/incentive'], { queryParams: { d: date }, replaceUrl: true });
}
});
}
account: Account = new Account();
accBal: AccountBalance | null = null;
displayedColumns = ['name', 'designation', 'department', 'daysWorked', 'points', 'amount'];
focusDate(event: Event) {
event.preventDefault();
this.form().focusBoundControl();
}
totalPoints = computed(() => {
return this.model().incentives.reduce(
(sum: number, item: Incentive) => sum + item.daysWorked * (item.points ?? 0),
0,
);
});
pointValue = computed(() => {
const tp = this.totalPoints();
if (!tp) return 0;
return Math.round(((this.voucher().incentive as number) * 100) / tp) / 100;
});
less(i: number) {
const currentPoints = this.model().incentives[i]?.points ?? 0;
if (currentPoints >= 1) {
this.model.update((m) => {
const arr = [...m.incentives];
if (arr[i]) {
arr[i] = new Incentive({ ...arr[i], points: currentPoints - 1 });
}
return { ...m, incentives: arr };
});
}
}
more(i: number) {
const currentPoints = this.model().incentives[i]?.points ?? 0;
this.model.update((m) => {
const arr = [...m.incentives];
if (arr[i]) {
arr[i] = new Incentive({ ...arr[i], points: currentPoints + 1 });
}
return { ...m, incentives: arr };
});
}
amount(row: Incentive, i: number) {
const currentPoints = this.model().incentives[i]?.points ?? 0;
return currentPoints * 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: Voucher) => {
this.snackBar.open('Voucher Posted', 'Success');
this.router.navigate(['/incentive', (result as Voucher).id], { replaceUrl: true });
},
error: (error) => {
this.snackBar.open(error as string, 'Danger');
},
});
}
save() {
const voucher: Voucher = this.getVoucher();
this.ser.saveOrUpdate(voucher).subscribe({
next: (result: Voucher) => {
this.router.navigate(['/incentive', (result as Voucher).id], { replaceUrl: true });
},
error: (error) => {
this.snackBar.open(error as string, 'Danger');
},
});
}
getVoucher(): Voucher {
const formModel = this.model();
const v = this.voucher();
v.date = moment(formModel.date).format('DD-MMM-YYYY');
v.incentives = formModel.incentives;
return v;
}
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 as string, '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();
}
});
}
}