Consolidated commit message to v22 signals

This commit is contained in:
2026-08-25 14:45:09 +00:00
parent 6403d25d3e
commit df289b20b8
443 changed files with 16058 additions and 17332 deletions
+79 -105
View File
@@ -1,6 +1,6 @@
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 { signal, Component, HostListener, inject, input, computed, linkedSignal } 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';
@@ -9,10 +9,8 @@ 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 { 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';
@@ -23,14 +21,19 @@ 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';
export interface IncentiveFormData {
date: moment.Moment;
incentives: { points: number }[];
}
@Component({
selector: 'app-incentive',
templateUrl: './incentive.component.html',
styleUrls: ['./incentive.component.css'],
imports: [
ReactiveFormsModule,
FormField,
FormRoot,
MatFormFieldModule,
MatInputModule,
MatDatepickerModule,
@@ -42,27 +45,31 @@ import { IncentiveDataSource } from './incentive-datasource';
LocalTimePipe,
],
})
export class IncentiveComponent implements OnInit {
private route = inject(ActivatedRoute);
export class IncentiveComponent {
id = input(null, { transform: (v: string | null | undefined) => v ?? null });
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>;
}>
>;
}>;
voucherResource = this.ser.getVoucher(this.id, signal('Incentive'), signal(null), signal(null));
voucher = computed(() => this.voucherResource.value() ?? new Voucher());
model = linkedSignal({
source: this.voucher,
computation: (v): IncentiveFormData => ({
date: moment(v.date, 'DD-MMM-YYYY'),
incentives: v.incentives.map((x) => ({ points: x.points })),
}),
});
form = createForm(this.model);
incentives = linkedSignal(() => this.voucher().incentives);
voucher: Voucher = new Voucher();
account: Account = new Account();
accBal: AccountBalance | null = null;
@@ -71,98 +78,68 @@ export class IncentiveComponent implements OnInit {
@HostListener('window:keydown.f2', ['$event'])
focusDate(event: Event) {
event.preventDefault();
this.dateElement.nativeElement.focus();
this.dateElement.nativeElement.select();
this.form().focusBoundControl();
}
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 '';
});
}
totalPoints = computed(() => {
return this.incentives()
.map((item: Incentive, i: number) => item.daysWorked * (this.model().incentives[i]?.points ?? 0))
.reduce((sum: number, item: number) => sum + item, 0);
});
ngOnInit() {
this.route.data.subscribe((value) => {
const data = value as { voucher: Voucher };
pointValue = computed(() => {
const tp = this.totalPoints();
if (!tp) return 0;
return Math.round(((this.voucher().incentive as number) * 100) / tp) / 100;
});
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,
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] = { ...arr[i], points: currentPoints - 1 };
}
return { ...m, incentives: arr };
});
}
}
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(i: number) {
const currentPoints = this.model().incentives[i]?.points ?? 0;
this.model.update((m) => {
const arr = [...m.incentives];
if (arr[i]) {
arr[i] = { ...arr[i], points: currentPoints + 1 };
}
return { ...m, incentives: arr };
});
}
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();
amount(row: Incentive, i: number) {
const currentPoints = this.model().incentives[i]?.points ?? 0;
return currentPoints * row.daysWorked * this.pointValue();
}
canSave() {
if (!this.voucher.id) {
if (!this.voucher().id) {
return true;
}
if (this.voucher.posted && this.auth.allowed('edit-posted-vouchers')) {
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");
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.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, 'Danger');
this.snackBar.open(error as string, 'Danger');
},
});
}
@@ -170,38 +147,35 @@ export class IncentiveComponent implements OnInit {
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]);
}
next: (result: Voucher) => {
this.router.navigate(['/incentive', (result as Voucher).id], { replaceUrl: true });
},
error: (error) => {
this.snackBar.open(error, 'Danger');
this.snackBar.open(error as string, 'Danger');
},
});
}
getVoucher(): Voucher {
const formModel = this.form.value;
this.voucher.date = moment(formModel.date).format('DD-MMM-YYYY');
const array = this.form.controls.incentives;
const formModel = this.model();
const v = this.voucher();
v.date = formModel.date.format('DD-MMM-YYYY');
const array = formModel.incentives;
this.voucher.incentives.forEach((item, index) => {
item.points = array.controls[index].value.points ?? 0;
v.incentives.forEach((item: Incentive, index: number) => {
item.points = array[index]?.points ?? 0;
});
return this.voucher;
return v;
}
delete() {
this.ser.delete(this.voucher.id as string).subscribe({
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');
this.snackBar.open(error as string, 'Danger');
},
});
}