200 lines
6.1 KiB
TypeScript
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();
|
|
}
|
|
});
|
|
}
|
|
}
|