This commit is contained in:
tanshu committed 2026-08-27 05:38:32 +00:00
1 parent 2a29dc9195
commit 43204e8441
78 files changed
+904 -1235

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "bookie",
"version": "14.5.0",
"version": "14.5.1",
"scripts": {
"ng": "ng",
"start": "ng serve",
+1 -1
View File
@@ -1,7 +1,7 @@
export const environment = {
production: true,
ACCESS_TOKEN_REFRESH_MINUTES: 10, // refresh token 10 minutes before expiry
version: '14.5.0',
version: '14.5.1',
};
export const dateFormat = {
@@ -3,14 +3,14 @@
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Username</mat-label>
<input matInput #nameElement [formField]="form.username" />
<input matInput [formField]="form.username" />
</mat-form-field>
</div>
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Password</mat-label>
<input matInput [formField]="form.password" [type]="hide ? 'password' : 'text'" (keyup.enter)="login()" />
<mat-icon matSuffix (click)="hide = !hide">{{ hide ? 'visibility' : 'visibility_off' }}</mat-icon>
<input matInput [formField]="form.password" [type]="hide() ? 'password' : 'text'" (keyup.enter)="login()" />
<mat-icon matSuffix (click)="hide.set(!hide())">{{ hide() ? 'visibility' : 'visibility_off' }}</mat-icon>
</mat-form-field>
</div>
@if (unregisteredDevice()) {
+14 -37
View File
@@ -1,13 +1,4 @@
import {
AfterViewInit,
ChangeDetectorRef,
Component,
ElementRef,
OnInit,
ViewChild,
inject,
signal,
} from '@angular/core';
import { Component, afterNextRender, inject, input, signal } from '@angular/core';
import { form, FormField, required } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatDividerModule } from '@angular/material/divider';
@@ -15,7 +6,7 @@ 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 { ActivatedRoute, Router } from '@angular/router';
import { Router } from '@angular/router';
import { CookieService } from '../../shared/cookie.service';
import { AuthService } from '../auth.service';
@@ -31,14 +22,15 @@ export interface LoginFormData {
styleUrls: ['./login.component.css'],
imports: [MatButtonModule, MatDividerModule, MatFormFieldModule, MatIconModule, MatInputModule, FormField],
})
export class LoginComponent implements OnInit, AfterViewInit {
@ViewChild('nameElement', { static: true }) nameElement?: ElementRef;
private route = inject(ActivatedRoute);
export class LoginComponent {
private auth = inject(AuthService);
private router = inject(Router);
private snackBar = inject(MatSnackBar);
private cs = inject(CookieService);
private cd = inject(ChangeDetectorRef);
returnUrl = input<string | null | undefined, string>('/', {
transform: (v: string | null | undefined): string => v ?? '/',
});
formModel = signal<LoginFormData>({
username: '',
@@ -50,28 +42,14 @@ export class LoginComponent implements OnInit, AfterViewInit {
required(schemaPath.password);
});
hide: boolean;
unregisteredDevice = signal<boolean | undefined>(undefined);
deviceName = signal<string | undefined>(undefined);
private returnUrl: string;
hide = signal<boolean>(true);
unregisteredDevice = signal<boolean>(false);
deviceName = signal<string>('');
constructor() {
this.hide = true;
this.unregisteredDevice.set(false);
this.deviceName.set('');
this.returnUrl = '';
}
ngOnInit() {
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] ?? '/';
}
ngAfterViewInit() {
setTimeout(() => {
if (this.nameElement !== undefined) {
this.nameElement.nativeElement.focus();
}
}, 0);
afterNextRender(() => {
this.form().focusBoundControl();
});
}
login(): void {
@@ -80,13 +58,12 @@ export class LoginComponent implements OnInit, AfterViewInit {
const password = formModel.password ?? '';
this.auth.login(username.trim(), password.trim()).subscribe({
next: () => {
this.router.navigateByUrl(this.returnUrl);
this.router.navigateByUrl(this.returnUrl() ?? '/');
},
error: (error) => {
if (error.status === 401 && error.error.detail === 'Device is not registered') {
this.unregisteredDevice.set(true);
this.deviceName.set(this.cs.getCookie('device'));
this.cd.detectChanges();
}
this.snackBar.open(error.error.detail, 'Error');
},
@@ -1,4 +1,4 @@
import { Component, OnInit, inject } from '@angular/core';
import { Component, inject } from '@angular/core';
import { AuthService } from '../auth.service';
@@ -7,10 +7,10 @@ import { AuthService } from '../auth.service';
template: '',
standalone: true,
})
export class LogoutComponent implements OnInit {
export class LogoutComponent {
private auth = inject(AuthService);
ngOnInit() {
constructor() {
this.auth.logout();
}
}
@@ -5,7 +5,7 @@
<mat-form-field class="w-full">
<mat-label>Item</mat-label>
<input matInput type="text" [formField]="form.product" [matAutocomplete]="autoP" />
<mat-autocomplete #autoP="matAutocomplete" [displayWith]="displayFn" (optionSelected)="productSelected($event)">
<mat-autocomplete #autoP="matAutocomplete" [displayWith]="displayFn">
@for (p of products(); track p) {
<mat-option [value]="p">{{ p.name }}</mat-option>
}
@@ -1,7 +1,7 @@
import { CommonModule } from '@angular/common';
import { Component, OnInit, inject, signal, computed, effect, debounced } from '@angular/core';
import { Component, inject, computed, debounced, linkedSignal } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatAutocompleteModule, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
@@ -13,7 +13,7 @@ import { ProductService } from '../../product/product.service';
import { BundleItem } from '../bundle';
export interface BundleDetailDialogFormData {
product: ProductQuery | string;
product: BundleItem | ProductQuery | string;
quantity: number;
salePrice: number;
printInBill: boolean;
@@ -35,19 +35,20 @@ export interface BundleDetailDialogFormData {
FormField,
],
})
export class BundleDetailDialogComponent implements OnInit {
export class BundleDetailDialogComponent {
private readonly dialogRef = inject<MatDialogRef<BundleDetailDialogComponent>>(MatDialogRef);
private readonly productSer = inject(ProductService);
data = inject<{ item: BundleItem }>(MAT_DIALOG_DATA);
selectedProduct = signal<ProductQuery | null>(null);
formModel = signal<BundleDetailDialogFormData>({
product: '',
quantity: 1,
salePrice: 0,
printInBill: true,
formModel = linkedSignal<BundleItem, BundleDetailDialogFormData>({
source: () => this.data.item,
computation: (item) => ({
product: item,
quantity: Number(item.quantity ?? 1),
salePrice: Number(item.salePrice ?? 0),
printInBill: item.printInBill ?? false,
}),
});
form = form(this.formModel);
@@ -59,62 +60,35 @@ export class BundleDetailDialogComponent implements OnInit {
debouncedProduct = debounced(this.productSignal, 150);
productsResource = this.productSer.query(computed(() => this.debouncedProduct.value()));
productsResource = this.productSer.query(this.debouncedProduct.value);
products = computed(() => {
if (!this.debouncedProduct.value()) return [];
return this.productsResource.value() ?? [];
});
constructor() {
// typing after selecting invalidates prior selection
effect(() => {
const x = this.formModel().product;
if (typeof x === 'string') this.selectedProduct.set(null);
});
}
ngOnInit(): void {
// preload display text using existing name
this.formModel.set({
product: this.data.item.name ?? '',
quantity: Number(this.data.item.quantity ?? 1),
salePrice: Number(this.data.item.salePrice ?? 0),
printInBill: this.data.item.printInBill,
});
this.selectedProduct.set(null);
}
displayFn(product?: ProductQuery | string): string {
return !product ? '' : typeof product === 'string' ? product : product.name;
}
productSelected(event: MatAutocompleteSelectedEvent): void {
const p = event.option.value as ProductQuery;
this.selectedProduct.set(p);
}
accept(): void {
const v = this.formModel();
const formValue = this.form().value();
const quantity = Number(v.quantity ?? 0);
const quantity = Number(formValue.quantity ?? 0);
if (Number.isNaN(quantity) || quantity <= 0) return;
const salePrice = Number(v.salePrice ?? 0);
const salePrice = Number(formValue.salePrice ?? 0);
if (Number.isNaN(salePrice) || salePrice < 0) return;
// If a new product was selected, update itemId + name
if (this.selectedProduct()?.id) {
this.data.item.itemId = this.selectedProduct()?.id ?? '';
this.data.item.name = this.selectedProduct()?.name ?? '';
} else {
// If no selection, don't allow changing SKU silently
// but still allow quantity/price edit
const product = typeof formValue.product === 'string' ? null : formValue.product;
if (!product || !product.id) {
return;
}
this.data.item.itemId = product.id ?? '';
this.data.item.name = product.name ?? '';
this.data.item.quantity = quantity;
this.data.item.salePrice = salePrice;
this.data.item.printInBill = v.printInBill || false;
this.data.item.printInBill = formValue.printInBill ?? false;
this.dialogRef.close(this.data.item);
}
@@ -4,7 +4,7 @@
<div class="row-container">
<mat-form-field class="basis-3-4">
<mat-label>Name</mat-label>
<input matInput #name [formField]="form.name" />
<input matInput [formField]="form.name" />
</mat-form-field>
<mat-form-field class="basis-1-4">
@@ -65,7 +65,7 @@
[matAutocomplete]="autoItem"
[value]="displayFn(formModel().addRow.itemId)"
/>
<mat-autocomplete #autoItem="matAutocomplete" [displayWith]="displayFn" (optionSelected)="bundleSelected($event)">
<mat-autocomplete #autoItem="matAutocomplete" [displayWith]="displayFn">
@for (p of itemProducts(); track p) {
<mat-option [value]="p">{{ p.name }}</mat-option>
}
@@ -88,7 +88,7 @@
</form>
<div class="row-container wrapped">
<mat-table [dataSource]="dataSource()" aria-label="Bundle Items" class="flex-auto">
<mat-table [dataSource]="formModel().items" aria-label="Bundle Items" class="flex-auto">
<!-- Name Column (name is "name (units)") -->
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef>Item</mat-header-cell>
@@ -143,7 +143,7 @@
<div class="row-container">
<button type="button" mat-raised-button color="primary" (click)="save()">Save</button>
@if (!!item.id) {
@if (!!item().id) {
<button type="button" mat-raised-button color="warn" (click)="confirmDelete()">Delete</button>
}
</div>
@@ -1,19 +1,7 @@
import { CurrencyPipe } from '@angular/common';
import {
AfterViewInit,
Component,
ElementRef,
ViewChild,
inject,
computed,
signal,
effect,
debounced,
linkedSignal,
input,
} from '@angular/core';
import { Component, inject, computed, effect, debounced, linkedSignal, input, afterNextRender } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatAutocompleteModule, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatOptionModule } from '@angular/material/core';
@@ -24,7 +12,7 @@ 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 { ActivatedRoute, Router } from '@angular/router';
import { Router } from '@angular/router';
import { MenuCategory } from '../../core/menu-category';
import { ProductQuery } from '../../core/product-query';
@@ -41,8 +29,8 @@ export interface BundleDetailFormData {
name: string;
units: string;
salePrice: number;
menuCategory: string;
saleCategory: string;
menuCategory: MenuCategory | null;
saleCategory: SaleCategory | null;
hasHappyHour: boolean;
isNotAvailable: boolean;
addRow: {
@@ -51,6 +39,7 @@ export interface BundleDetailFormData {
salePrice: number;
printInBill: boolean;
};
items: BundleItem[];
}
@Component({
@@ -71,53 +60,34 @@ export interface BundleDetailFormData {
FormField,
],
})
export class BundleDetailComponent implements AfterViewInit {
@ViewChild('name', { static: true }) nameElement?: ElementRef;
export class BundleDetailComponent {
private menuCategoryService = inject(MenuCategoryService);
private saleCategoryService = inject(SaleCategoryService);
private route = inject(ActivatedRoute);
private router = inject(Router);
private dialog = inject(MatDialog);
private snackBar = inject(MatSnackBar);
private ser = inject(BundleService);
private productSer = inject(ProductService);
id = input(null, { transform: (v: string | null | undefined) => v ?? null });
saleCategoriesResource = this.saleCategoryService.list();
menuCategoriesResource = this.menuCategoryService.list(undefined);
menuCategories = computed(() => this.menuCategoriesResource.value() ?? []);
saleCategoriesResource = this.saleCategoryService.list();
saleCategories = computed(() => this.saleCategoriesResource.value() ?? []);
id = input(null, { transform: (v: string | null | undefined) => v ?? null });
itemResource = this.ser.get(this.id);
item = computed(() => this.itemResource.value() ?? new Bundle());
formModel = linkedSignal({
source: () => this.menuCategoriesResource.value(),
computation: (menuCategoriesVal): BundleDetailFormData => {
if (menuCategoriesVal) {
return {
name: this.item.name ?? '',
units: this.item.units ?? '',
salePrice: Number(this.item.salePrice ?? 0),
menuCategory: this.item.menuCategory?.id ?? '',
saleCategory: this.item.saleCategory?.id ?? '',
hasHappyHour: this.item.hasHappyHour ?? false,
isNotAvailable: this.item.isNotAvailable ?? false,
addRow: {
itemId: '',
quantity: 1,
salePrice: 0,
printInBill: true,
},
};
}
source: this.item,
computation: (itemVal): BundleDetailFormData => {
const items = [...(itemVal.items ?? [])];
return {
name: '',
units: '',
salePrice: 0,
menuCategory: '',
saleCategory: '',
hasHappyHour: false,
isNotAvailable: false,
name: itemVal.name ?? '',
units: itemVal.units ?? '',
salePrice: Number(itemVal.salePrice ?? 0),
menuCategory: itemVal.menuCategory ?? null,
saleCategory: itemVal.saleCategory ?? null,
hasHappyHour: itemVal.hasHappyHour ?? false,
isNotAvailable: itemVal.isNotAvailable ?? false,
items,
addRow: {
itemId: '',
quantity: 1,
@@ -130,25 +100,15 @@ export class BundleDetailComponent implements AfterViewInit {
form = form(this.formModel);
menuCategories = signal<MenuCategory[]>([]);
saleCategories = signal<SaleCategory[]>([]);
items = signal<BundleItem[]>([]);
dataSource = computed(() => this.items());
item: Bundle = new Bundle();
itemProduct = signal<ProductQuery | null>(null);
itemIdSignal = computed(() => {
const x = this.formModel().addRow?.itemId;
return typeof x === 'string' ? x.trim() : '';
private itemProductSearch = computed(() => {
const v = this.form.addRow.itemId().value();
return typeof v === 'string' ? v : null;
});
debouncedItemId = debounced(this.itemIdSignal, 150);
debouncedItemProduct = debounced(this.itemProductSearch, 150);
itemProductsResource = this.productSer.query(computed(() => this.debouncedItemId.value()));
itemProductsResource = this.productSer.query(this.debouncedItemProduct.value);
itemProducts = computed(() => {
if (!this.debouncedItemId.value()) return [];
return this.itemProductsResource.value() ?? [];
});
@@ -156,25 +116,26 @@ export class BundleDetailComponent implements AfterViewInit {
constructor() {
effect(() => {
const x = this.formModel().addRow?.itemId;
if (typeof x === 'string') {
this.itemProduct.set(null);
const v = this.form.addRow.itemId().value();
if (typeof v === 'object' && v !== null) {
this.formModel.update((f) => ({
...f,
addRow: {
...f.addRow,
salePrice: v.price ?? 0,
},
}));
}
});
effect(() => {
const menuCategoriesVal = this.menuCategoriesResource.value();
if (menuCategoriesVal) this.menuCategories.set(menuCategoriesVal);
const saleCategoriesVal = this.saleCategoriesResource.value();
if (saleCategoriesVal) this.saleCategories.set(saleCategoriesVal);
const itemVal = this.itemResource.value();
if (itemVal) {
this.item = itemVal;
this.itemProduct.set(null);
this.items.set(this.item.items ?? []);
this.recalcBundleSalePrice();
}
const items = this.form.items().value();
this.formModel.update((f) => ({
...f,
salePrice: Number(items.reduce((sum, x) => sum + x.quantity * x.salePrice, 0).toFixed(2)),
}));
});
afterNextRender(() => {
this.form().focusBoundControl();
});
}
@@ -182,40 +143,6 @@ export class BundleDetailComponent implements AfterViewInit {
return !product ? '' : typeof product === 'string' ? product : product.name;
}
bundleSelected(event: MatAutocompleteSelectedEvent): void {
const product = event.option.value as ProductQuery;
this.itemProduct.set(product);
this.formModel.update((f) => ({
...f,
addRow: {
...f.addRow,
salePrice: product.price ?? 0,
},
}));
}
private recalcBundleSalePrice(): void {
const total = (this.item.items ?? []).reduce((sum, x) => {
const qty = Number(x.quantity ?? 0);
const price = Number(x.salePrice ?? 0);
if (Number.isNaN(qty) || Number.isNaN(price)) return sum;
return sum + qty * price;
}, 0);
// keep model + form in sync
this.item.salePrice = Number(total.toFixed(2));
this.formModel.update((f) => ({ ...f, salePrice: this.item.salePrice }));
}
ngAfterViewInit() {
setTimeout(() => {
if (this.nameElement !== undefined) {
this.nameElement.nativeElement.focus();
}
}, 0);
}
resetAddRow() {
this.formModel.update((f) => ({
...f,
@@ -226,47 +153,43 @@ export class BundleDetailComponent implements AfterViewInit {
printInBill: true,
},
}));
this.itemProduct.set(null);
}
addRow() {
const v = this.formModel().addRow;
if (!v) {
return;
}
const formValue = this.form().value().addRow;
if (!formValue) return;
const itemProduct = formValue.itemId as ProductQuery;
if (!this.itemProduct()?.id) {
if (!itemProduct || typeof itemProduct === 'string') {
this.snackBar.open('Please select a product', 'Error');
return;
}
const quantity = Number(v.quantity ?? 0);
const quantity = Number(formValue.quantity ?? 0);
if (Number.isNaN(quantity) || quantity <= 0) {
this.snackBar.open('Quantity has to be > 0', 'Error');
return;
}
const salePrice = Number(v.salePrice ?? 0);
const salePrice = Number(formValue.salePrice ?? 0);
if (Number.isNaN(salePrice) || salePrice < 0) {
this.snackBar.open('Sale Price has to be >= 0', 'Error');
return;
}
// name is expected from backend as "name (units)" on GET;
// for new rows we keep it blank (or you can set placeholder)
const bi = new BundleItem({
itemId: this.itemProduct()?.id,
name: this.itemProduct()?.name ?? '',
itemId: itemProduct.id,
name: itemProduct.name ?? '',
quantity,
salePrice,
printInBill: v.printInBill || false,
printInBill: formValue.printInBill || false,
});
this.item.items.push(bi);
this.items.set(this.item.items);
this.formModel.update((m) => ({
...m,
items: [...m.items, bi],
}));
this.resetAddRow();
this.recalcBundleSalePrice();
}
editRow(row: BundleItem) {
@@ -281,25 +204,28 @@ export class BundleDetailComponent implements AfterViewInit {
if (!result) {
return;
}
Object.assign(row, result as BundleItem);
this.items.set(this.item.items);
const updated = result as BundleItem;
this.formModel.update((m) => ({
...m,
items: m.items.map((item) => (item === row ? Object.assign(new BundleItem(item), updated) : item)),
}));
this.resetAddRow();
this.recalcBundleSalePrice();
});
}
deleteRow(row: BundleItem) {
this.item.items.splice(this.item.items.indexOf(row), 1);
this.items.set(this.item.items);
this.recalcBundleSalePrice();
this.formModel.update((m) => ({
...m,
items: m.items.filter((item) => item !== row),
}));
}
save() {
if (!this.item.items || this.item.items.length === 0) {
this.snackBar.open('Bundle must contain at least one item', 'Error');
const item = this.getItem();
if (!item) {
return;
}
this.ser.saveOrUpdate(this.getItem()).subscribe({
this.ser.saveOrUpdate(item).subscribe({
next: () => {
this.snackBar.open('', 'Success');
this.router.navigateByUrl('/bundles');
@@ -311,7 +237,7 @@ export class BundleDetailComponent implements AfterViewInit {
}
delete() {
this.ser.delete(this.item.id as string).subscribe({
this.ser.delete(this.item().id as string).subscribe({
next: () => {
this.snackBar.open('', 'Success');
this.router.navigateByUrl('/bundles');
@@ -335,47 +261,33 @@ export class BundleDetailComponent implements AfterViewInit {
});
}
getItem(): Bundle {
getItem(): Bundle | null {
const v = this.formModel();
this.item.name = v.name ?? '';
this.item.units = v.units ?? '';
this.item.salePrice = Number(v.salePrice ?? 0);
this.item.hasHappyHour = v.hasHappyHour ?? false;
this.item.isNotAvailable = v.isNotAvailable ?? false;
this.item.sortOrder = this.item.sortOrder ?? 0;
// menu category
const menuCategoryId = v.menuCategory ?? '';
if (!menuCategoryId) {
// keep it as-is; backend will 422 anyway, but we can show UI error too
if (!v.menuCategory) {
this.snackBar.open('Menu Category is required', 'Error');
return this.item;
return null;
}
if (this.item.menuCategory === null || this.item.menuCategory === undefined) {
this.item.menuCategory = new MenuCategory();
}
this.item.menuCategory.id = menuCategoryId;
// sale category
const saleCategoryId = v.saleCategory ?? '';
if (!saleCategoryId) {
// keep it as-is; backend will 422 anyway, but we can show UI error too
if (!v.saleCategory) {
this.snackBar.open('Menu Category is required', 'Error');
return this.item;
return null;
}
if (this.item.saleCategory === null || this.item.saleCategory === undefined) {
this.item.saleCategory = new SaleCategory();
if (!v.items.length) {
this.snackBar.open('Bundle must contain at least one item', 'Error');
return null;
}
this.item.saleCategory.id = saleCategoryId;
// ensure items array exists
if (!this.item.items) {
this.item.items = [];
}
return this.item;
return new Bundle({
...this.item(),
name: v.name ?? '',
units: v.units ?? '',
salePrice: Number(v.salePrice ?? 0),
hasHappyHour: v.hasHappyHour ?? false,
isNotAvailable: v.isNotAvailable ?? false,
sortOrder: this.item().sortOrder ?? 0,
menuCategory: v.menuCategory,
saleCategory: v.saleCategory,
items: v.items ?? [],
});
}
}
@@ -3,7 +3,7 @@
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Name</mat-label>
<input matInput #nameElement [formField]="form.name" />
<input matInput [formField]="form.name" />
</mat-form-field>
</div>
<div class="row-container">
@@ -1,5 +1,5 @@
import { PercentPipe } from '@angular/common';
import { AfterViewInit, Component, ElementRef, ViewChild, computed, inject, linkedSignal, input } from '@angular/core';
import { Component, computed, inject, linkedSignal, input, afterNextRender } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
@@ -39,8 +39,7 @@ export interface CustomerDetailFormData {
FormField,
],
})
export class CustomerDetailComponent implements AfterViewInit {
@ViewChild('nameElement', { static: true }) nameElement?: ElementRef;
export class CustomerDetailComponent {
private route = inject(ActivatedRoute);
private router = inject(Router);
private snackBar = inject(MatSnackBar);
@@ -68,12 +67,10 @@ export class CustomerDetailComponent implements AfterViewInit {
hide = true;
ngAfterViewInit() {
setTimeout(() => {
if (this.nameElement !== undefined) {
this.nameElement.nativeElement.focus();
}
}, 0);
constructor() {
afterNextRender(() => {
this.form().focusBoundControl();
});
}
save() {
@@ -9,11 +9,11 @@
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Filter</mat-label>
<input type="text" matInput #filterElement [formField]="form.filter" autocomplete="off" />
<input type="text" matInput [formField]="form.filter" autocomplete="off" />
</mat-form-field>
</div>
</form>
<mat-table #table [dataSource]="dataSource().items" matSort aria-label="Elements">
<mat-table #table [dataSource]="dataSource().items" matSort (matSortChange)="sortData($event)" aria-label="Elements">
<!-- Name Column -->
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef mat-sort-header>Name</mat-header-cell>
@@ -1,21 +1,12 @@
/* eslint-disable @angular-eslint/no-input-rename */
import { PercentPipe } from '@angular/common';
import {
AfterViewInit,
Component,
ElementRef,
ViewChild,
inject,
signal,
effect,
computed,
debounced,
} from '@angular/core';
import { Component, inject, signal, effect, computed, debounced, afterNextRender, input } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator';
import { MatSort, MatSortModule } from '@angular/material/sort';
import { MatPaginatorModule, PageEvent } from '@angular/material/paginator';
import { MatSortModule, Sort } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
@@ -42,13 +33,23 @@ export interface CustomerListFormData {
FormField,
],
})
export class CustomerListComponent implements AfterViewInit {
@ViewChild('filterElement', { static: true }) filterElement!: ElementRef<HTMLInputElement>;
@ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator;
@ViewChild(MatSort, { static: true }) sort!: MatSort;
export class CustomerListComponent {
private route = inject(ActivatedRoute);
private router = inject(Router);
private customerService = inject(CustomerService);
q = input('', { transform: (v: string | null | undefined) => v ?? '' });
pageSize = input(50, {
transform: (v: string | number | null | undefined): number => (v ? Number(v) : 50),
alias: 's',
});
pageIndex = input(0, {
transform: (v: string | number | null | undefined): number => (v ? Number(v) : 0),
alias: 'i',
});
sortInput = input<string>('name', { alias: 'f' });
direction = input<string>('asc', { alias: 'd' });
formModel = signal<CustomerListFormData>({
filter: this.route.snapshot.queryParamMap.get('q') ?? '',
@@ -56,23 +57,10 @@ export class CustomerListComponent implements AfterViewInit {
form = form(this.formModel);
filterSignal = computed(() => this.formModel().filter);
filterSignal = computed(() => this.formModel().filter ?? null);
debouncedFilter = debounced(this.filterSignal, 150);
pageIndex = signal(+(this.route.snapshot.queryParamMap.get('p') ?? '0'));
pageSize = signal(+(this.route.snapshot.queryParamMap.get('s') ?? '50'));
sortField = signal(this.route.snapshot.queryParamMap.get('f') ?? 'name');
sortDirection = signal(this.route.snapshot.queryParamMap.get('d') ?? 'asc');
queryParams = computed(() => ({
q: this.debouncedFilter.value() ?? '',
p: this.pageIndex(),
s: this.pageSize(),
f: this.sortField(),
d: this.sortDirection(),
}));
listResource = this.customerService.query(this.queryParams);
listResource = this.customerService.query(this.q, this.pageIndex, this.pageSize, this.sortInput, this.direction);
dataSource = computed(() => {
const data = this.listResource.value();
@@ -84,32 +72,37 @@ export class CustomerListComponent implements AfterViewInit {
displayedColumns = ['name', 'phone', 'address', 'printInBill', 'discounts'];
constructor() {
// Sync query string whenever params change
effect(() => {
const p = this.queryParams();
const q = this.debouncedFilter.value();
this.router.navigate([], {
relativeTo: this.route,
queryParams: { q: p.q, p: p.p, s: p.s, f: p.f, d: p.d },
queryParams: { q: q },
queryParamsHandling: 'merge',
replaceUrl: true,
});
});
afterNextRender(() => {
this.form().focusBoundControl();
});
}
ngAfterViewInit() {
this.sort.sortChange.subscribe(() => {
this.sortField.set(this.sort.active);
this.sortDirection.set(this.sort.direction);
this.pageIndex.set(0);
});
handlePageEvent(e: PageEvent) {
if (e.pageSize !== this.pageSize() || e.pageIndex !== this.pageIndex()) {
this.router.navigate([], {
relativeTo: this.route,
queryParams: { s: e.pageSize, p: e.pageIndex },
queryParamsHandling: 'merge',
replaceUrl: true,
});
}
}
this.paginator.page.subscribe(() => {
this.pageSize.set(this.paginator.pageSize);
this.pageIndex.set(this.paginator.pageIndex);
sortData(sort: Sort) {
this.router.navigate([], {
relativeTo: this.route,
queryParams: { f: sort.active || null, d: sort.direction || null },
queryParamsHandling: 'merge',
replaceUrl: true,
});
setTimeout(() => {
this.filterElement.nativeElement.focus();
}, 0);
}
}
+10 -7
View File
@@ -41,18 +41,21 @@ export class CustomerService {
}
query(
params: Signal<{ q: string; p: number; s: number; f: string; d: string }>,
q: Signal<string>,
p: Signal<number>,
s: Signal<number>,
f: Signal<string>,
d: Signal<string>,
): HttpResourceRef<PagedResult<Customer> | undefined> {
return httpResource<PagedResult<Customer>>(() => {
const p = params();
return {
url: `${url}/list`,
params: {
...(p.q ? { q: p.q } : {}),
p: p.p,
s: p.s,
f: p.f,
d: p.d,
q: q(),
p: p(),
s: s(),
f: f(),
d: d(),
},
};
});
@@ -3,7 +3,7 @@
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Name</mat-label>
<input matInput #nameElement [formField]="form.name" (keyup.enter)="save()" />
<input matInput [formField]="form.name" (keyup.enter)="save()" />
</mat-form-field>
</div>
<div class="row-container">
@@ -1,4 +1,4 @@
import { AfterViewInit, Component, ElementRef, ViewChild, computed, inject, linkedSignal, input } from '@angular/core';
import { Component, computed, inject, linkedSignal, input, afterNextRender } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
@@ -38,8 +38,7 @@ export interface DeviceDetailFormData {
FormField,
],
})
export class DeviceDetailComponent implements AfterViewInit {
@ViewChild('nameElement', { static: true }) nameElement?: ElementRef;
export class DeviceDetailComponent {
private sectionService = inject(SectionService);
private route = inject(ActivatedRoute);
private router = inject(Router);
@@ -68,12 +67,10 @@ export class DeviceDetailComponent implements AfterViewInit {
sections = computed(() => this.sectionsResource.value() ?? []);
ngAfterViewInit() {
setTimeout(() => {
if (this.nameElement !== undefined) {
this.nameElement.nativeElement.focus();
}
}, 0);
constructor() {
afterNextRender(() => {
this.form().focusBoundControl();
});
}
save() {
@@ -3,13 +3,13 @@
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Name</mat-label>
<input matInput #name [formField]="form.name" />
<input matInput [formField]="form.name" />
</mat-form-field>
</div>
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Phone</mat-label>
<input matInput #phone type="text" [formField]="form.phone" [matAutocomplete]="auto" autocomplete="off" />
<input matInput type="text" [formField]="form.phone" [matAutocomplete]="auto" autocomplete="off" />
</mat-form-field>
<mat-autocomplete
#auto="matAutocomplete"
@@ -1,14 +1,4 @@
import {
AfterViewInit,
Component,
ElementRef,
ViewChild,
computed,
inject,
debounced,
linkedSignal,
input,
} from '@angular/core';
import { Component, computed, inject, debounced, linkedSignal, input, afterNextRender } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatAutocompleteSelectedEvent, MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
@@ -53,8 +43,7 @@ export interface GuestBookDetailFormData {
FormField,
],
})
export class GuestBookDetailComponent implements AfterViewInit {
@ViewChild('phone', { static: true }) phoneElement?: ElementRef;
export class GuestBookDetailComponent {
private route = inject(ActivatedRoute);
private router = inject(Router);
private dialog = inject(MatDialog);
@@ -111,12 +100,10 @@ export class GuestBookDetailComponent implements AfterViewInit {
return this.customersResource.value() ?? [];
});
ngAfterViewInit() {
setTimeout(() => {
if (this.phoneElement !== undefined) {
this.phoneElement.nativeElement.focus();
}
}, 0);
constructor() {
afterNextRender(() => {
this.form().focusBoundControl();
});
}
save() {
@@ -1,4 +1,4 @@
import { Component, ElementRef, ViewChild, computed, inject, linkedSignal, input } from '@angular/core';
import { Component, computed, inject, linkedSignal, input } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatOptionModule } from '@angular/material/core';
@@ -33,7 +33,6 @@ export interface HeaderFooterFormData {
],
})
export class HeaderFooterComponent {
@ViewChild('section', { static: true }) sectionElement?: ElementRef;
private route = inject(ActivatedRoute);
private router = inject(Router);
private snackBar = inject(MatSnackBar);
@@ -3,7 +3,7 @@
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Name</mat-label>
<input matInput #nameElement [formField]="form.name" />
<input matInput [formField]="form.name" />
</mat-form-field>
</div>
<div class="row-container">
@@ -1,4 +1,4 @@
import { AfterViewInit, Component, ElementRef, ViewChild, computed, inject, linkedSignal, input } from '@angular/core';
import { Component, computed, inject, linkedSignal, input, afterNextRender } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
@@ -23,8 +23,7 @@ export interface MenuCategoryDetailFormData {
styleUrls: ['./menu-category-detail.component.css'],
imports: [MatButtonModule, MatCheckboxModule, MatFormFieldModule, MatInputModule, FormField],
})
export class MenuCategoryDetailComponent implements AfterViewInit {
@ViewChild('nameElement', { static: true }) nameElement?: ElementRef;
export class MenuCategoryDetailComponent {
private route = inject(ActivatedRoute);
private router = inject(Router);
private dialog = inject(MatDialog);
@@ -47,12 +46,10 @@ export class MenuCategoryDetailComponent implements AfterViewInit {
form = form(this.formModel);
ngAfterViewInit() {
setTimeout(() => {
if (this.nameElement !== undefined) {
this.nameElement.nativeElement.focus();
}
}, 0);
constructor() {
afterNextRender(() => {
this.form().focusBoundControl();
});
}
save() {
@@ -40,7 +40,7 @@
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Filter</mat-label>
<input type="text" matInput #filterElement [formField]="form.filter" autocomplete="off" />
<input type="text" matInput [formField]="form.filter" autocomplete="off" />
<mat-hint
>n: Name, sc: Sale Category, mc: Menu Category, q:(=/&lt;=/&gt;=/&lt;/&gt;)Quantity,
s:(=/&lt;=/&gt;=/&lt;/&gt;)Sales</mat-hint
@@ -1,5 +1,5 @@
import { DecimalPipe, PercentPipe } from '@angular/common';
import { Component, ViewChild, ElementRef, inject, computed, linkedSignal, input } from '@angular/core';
import { Component, inject, computed, linkedSignal, input, afterNextRender } from '@angular/core';
import { toObservable } from '@angular/core/rxjs-interop';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
@@ -7,9 +7,9 @@ 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 { MatPaginator, MatPaginatorModule } from '@angular/material/paginator';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatSnackBar } from '@angular/material/snack-bar';
import { MatSort, MatSortModule } from '@angular/material/sort';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { ActivatedRoute, Router } from '@angular/router';
import moment from 'moment';
@@ -45,9 +45,6 @@ export interface MenuEngineeringReportFormData {
],
})
export class MenuEngineeringReportComponent {
@ViewChild('filterElement', { static: true }) filterElement?: ElementRef;
@ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator;
@ViewChild(MatSort, { static: true }) sort?: MatSort;
private route = inject(ActivatedRoute);
private router = inject(Router);
private toCsv = inject(ToCsvService);
@@ -89,6 +86,9 @@ export class MenuEngineeringReportComponent {
// Listen to Filter Change
const filterSignal = computed(() => this.formModel().filter);
this.filter = toObservable(filterSignal).pipe(debounceTime(150), distinctUntilChanged());
afterNextRender(() => {
this.form().focusBoundControl();
});
}
show() {
@@ -3,7 +3,7 @@
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Name</mat-label>
<input matInput #nameElement [formField]="form.name" />
<input matInput [formField]="form.name" />
</mat-form-field>
</div>
<div class="row-container">
@@ -1,4 +1,4 @@
import { AfterViewInit, Component, ElementRef, ViewChild, effect, inject, input, signal } from '@angular/core';
import { Component, effect, inject, input, signal, afterNextRender } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
@@ -48,8 +48,7 @@ export interface ModifierCategoryDetailFormData {
FormField,
],
})
export class ModifierCategoryDetailComponent implements AfterViewInit {
@ViewChild('nameElement', { static: true }) nameElement?: ElementRef;
export class ModifierCategoryDetailComponent {
private route = inject(ActivatedRoute);
private router = inject(Router);
private snackBar = inject(MatSnackBar);
@@ -108,14 +107,9 @@ export class ModifierCategoryDetailComponent implements AfterViewInit {
});
}
});
}
ngAfterViewInit() {
setTimeout(() => {
if (this.nameElement !== undefined) {
this.nameElement.nativeElement.focus();
}
}, 0);
afterNextRender(() => {
this.form().focusBoundControl();
});
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -3,7 +3,7 @@
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Name</mat-label>
<input matInput #name [formField]="form.name" />
<input matInput [formField]="form.name" />
</mat-form-field>
</div>
<div class="row-container">
@@ -1,4 +1,4 @@
import { AfterViewInit, Component, ElementRef, ViewChild, computed, inject, linkedSignal, input } from '@angular/core';
import { Component, computed, inject, linkedSignal, input, afterNextRender } from '@angular/core';
import { form, FormField, required } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
@@ -38,8 +38,7 @@ export interface ModifierDetailFormData {
FormField,
],
})
export class ModifierDetailComponent implements AfterViewInit {
@ViewChild('name', { static: true }) nameElement?: ElementRef;
export class ModifierDetailComponent {
private modifierCategoryService = inject(ModifierCategoryService);
private route = inject(ActivatedRoute);
private router = inject(Router);
@@ -71,12 +70,10 @@ export class ModifierDetailComponent implements AfterViewInit {
required(schema.modifierCategory, { message: 'Modifier Category is required' });
});
ngAfterViewInit() {
setTimeout(() => {
if (this.nameElement !== undefined) {
this.nameElement.nativeElement.focus();
}
}, 0);
constructor() {
afterNextRender(() => {
this.form().focusBoundControl();
});
}
save() {
@@ -3,7 +3,7 @@
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Name</mat-label>
<input matInput #nameElement [formField]="form.name" />
<input matInput [formField]="form.name" />
</mat-form-field>
</div>
<div class="row-container">
@@ -1,4 +1,4 @@
import { AfterViewInit, Component, ElementRef, ViewChild, computed, inject, linkedSignal, input } from '@angular/core';
import { Component, computed, inject, linkedSignal, input, afterNextRender } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatDialog } from '@angular/material/dialog';
@@ -23,8 +23,7 @@ export interface PrinterDetailFormData {
styleUrls: ['./printer-detail.component.css'],
imports: [MatButtonModule, MatFormFieldModule, MatInputModule, FormField],
})
export class PrinterDetailComponent implements AfterViewInit {
@ViewChild('nameElement', { static: true }) nameElement?: ElementRef;
export class PrinterDetailComponent {
private route = inject(ActivatedRoute);
private router = inject(Router);
private dialog = inject(MatDialog);
@@ -48,12 +47,10 @@ export class PrinterDetailComponent implements AfterViewInit {
form = form(this.formModel);
ngAfterViewInit() {
setTimeout(() => {
if (this.nameElement !== undefined) {
this.nameElement.nativeElement.focus();
}
}, 0);
constructor() {
afterNextRender(() => {
this.form().focusBoundControl();
});
}
save() {
@@ -9,7 +9,7 @@
</button>
</h2>
<form class="flex-col">
<form class="flex-col" [formRoot]="form">
<div class="row-container sm:max-lg:flex-col">
<mat-form-field class="flex-auto basis-1-3">
<mat-label>Start Date</mat-label>
@@ -1,6 +1,6 @@
import { DecimalPipe } from '@angular/common';
import { Component, inject, computed, linkedSignal, input } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { form, FormField, FormRoot } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatFormFieldModule } from '@angular/material/form-field';
@@ -37,6 +37,7 @@ export interface ProductSaleReportFormData {
MatSelectModule,
MatTableModule,
FormField,
FormRoot,
],
})
export class ProductSaleReportComponent {
@@ -3,7 +3,7 @@
<div class="row-container">
<mat-form-field class="basis-3-4">
<mat-label>Name</mat-label>
<input matInput #name [formField]="form.name" />
<input matInput [formField]="form.name" />
</mat-form-field>
<mat-form-field class="basis-1-4">
<mat-label>Fraction Units</mat-label>
@@ -1,5 +1,5 @@
import { CurrencyPipe } from '@angular/common';
import { AfterViewInit, Component, ElementRef, ViewChild, inject, computed, linkedSignal, input } from '@angular/core';
import { Component, inject, computed, linkedSignal, input, afterNextRender } from '@angular/core';
import { form, FormField, required, min, max } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
@@ -57,8 +57,7 @@ export interface ProductDetailFormData {
FormField,
],
})
export class ProductDetailComponent implements AfterViewInit {
@ViewChild('name', { static: true }) nameElement?: ElementRef;
export class ProductDetailComponent {
private menuCategoryService = inject(MenuCategoryService);
private saleCategoryService = inject(SaleCategoryService);
private route = inject(ActivatedRoute);
@@ -123,12 +122,10 @@ export class ProductDetailComponent implements AfterViewInit {
'action',
];
ngAfterViewInit() {
setTimeout(() => {
if (this.nameElement !== undefined) {
this.nameElement.nativeElement.focus();
}
}, 0);
constructor() {
afterNextRender(() => {
this.form().focusBoundControl();
});
}
addRow() {
+2 -2
View File
@@ -44,9 +44,9 @@ export class ProductService {
});
}
query(query: Signal<string | null> | string | null): HttpResourceRef<ProductQuery[] | undefined> {
query(query: Signal<string | null>): HttpResourceRef<ProductQuery[] | undefined> {
return httpResource<ProductQuery[]>(() => {
const qVal = typeof query === 'function' ? query() : query;
const qVal = query();
return `${url}/query?q=${qVal ?? ''}`;
});
}
@@ -3,7 +3,7 @@
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Name</mat-label>
<input matInput #nameElement [formField]="form.name" (keyup.enter)="save()" />
<input matInput [formField]="form.name" (keyup.enter)="save()" />
</mat-form-field>
</div>
<div class="row-container">
@@ -1,4 +1,4 @@
import { AfterViewInit, Component, ElementRef, ViewChild, computed, inject, linkedSignal, input } from '@angular/core';
import { Component, computed, inject, linkedSignal, input, afterNextRender } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatDialog } from '@angular/material/dialog';
@@ -23,8 +23,7 @@ export interface RegimeDetailFormData {
styleUrls: ['./regime-detail.component.css'],
imports: [MatButtonModule, MatFormFieldModule, MatInputModule, FormField],
})
export class RegimeDetailComponent implements AfterViewInit {
@ViewChild('nameElement', { static: true }) nameElement?: ElementRef;
export class RegimeDetailComponent {
private route = inject(ActivatedRoute);
private router = inject(Router);
private dialog = inject(MatDialog);
@@ -48,12 +47,10 @@ export class RegimeDetailComponent implements AfterViewInit {
form = form(this.formModel);
ngAfterViewInit() {
setTimeout(() => {
if (this.nameElement !== undefined) {
this.nameElement.nativeElement.focus();
}
}, 0);
constructor() {
afterNextRender(() => {
this.form().focusBoundControl();
});
}
save() {
@@ -3,7 +3,7 @@
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Name</mat-label>
<input matInput #nameElement [formField]="form.name" />
<input matInput [formField]="form.name" />
</mat-form-field>
</div>
@@ -1,4 +1,4 @@
import { AfterViewInit, Component, ElementRef, ViewChild, computed, inject, linkedSignal, input } from '@angular/core';
import { Component, computed, inject, linkedSignal, input, afterNextRender } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
@@ -24,8 +24,7 @@ export interface RoleDetailFormData {
styleUrls: ['./role-detail.component.css'],
imports: [MatButtonModule, MatCheckboxModule, MatFormFieldModule, MatInputModule, FormField],
})
export class RoleDetailComponent implements AfterViewInit {
@ViewChild('nameElement', { static: true }) nameElement?: ElementRef;
export class RoleDetailComponent {
private route = inject(ActivatedRoute);
private router = inject(Router);
private snackBar = inject(MatSnackBar);
@@ -60,12 +59,10 @@ export class RoleDetailComponent implements AfterViewInit {
return implied;
});
ngAfterViewInit() {
setTimeout(() => {
if (this.nameElement !== undefined) {
this.nameElement.nativeElement.focus();
}
}, 0);
constructor() {
afterNextRender(() => {
this.form().focusBoundControl();
});
}
save() {
@@ -3,7 +3,7 @@
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Name</mat-label>
<input matInput #nameElement [formField]="form.name" />
<input matInput [formField]="form.name" />
</mat-form-field>
</div>
<div class="row-container">
@@ -1,5 +1,5 @@
import { CurrencyPipe } from '@angular/common';
import { AfterViewInit, Component, ElementRef, ViewChild, inject, computed, linkedSignal, input } from '@angular/core';
import { Component, inject, computed, linkedSignal, input, afterNextRender } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatOptionModule } from '@angular/material/core';
@@ -43,8 +43,7 @@ export interface SaleCategoryDetailFormData {
RouterLink,
],
})
export class SaleCategoryDetailComponent implements AfterViewInit {
@ViewChild('nameElement', { static: true }) nameElement?: ElementRef;
export class SaleCategoryDetailComponent {
private taxService = inject(TaxService);
private productService = inject(ProductService);
private route = inject(ActivatedRoute);
@@ -80,12 +79,10 @@ export class SaleCategoryDetailComponent implements AfterViewInit {
dataSource = computed(() => this.products());
displayedColumns = ['name', 'price', 'menuCategory'];
ngAfterViewInit() {
setTimeout(() => {
if (this.nameElement !== undefined) {
this.nameElement.nativeElement.focus();
}
}, 0);
constructor() {
afterNextRender(() => {
this.form().focusBoundControl();
});
}
save() {
@@ -20,7 +20,6 @@ export class SaleCategoryListComponent {
private ser = inject(SaleCategoryService);
listResource = this.ser.list();
// @ViewChild('table', { static: true }) table?: MatTable<SaleCategory>;
dataSource = computed(() => this.listResource.value() ?? []);
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
@@ -1,5 +1,5 @@
import { CdkScrollableModule } from '@angular/cdk/scrolling';
import { Component, ElementRef, ViewChild, inject, computed, signal } from '@angular/core';
import { Component, inject, computed, signal } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MAT_DIALOG_DATA, MatDialogRef, MatDialogModule } from '@angular/material/dialog';
@@ -26,7 +26,6 @@ export interface ReasonFormData {
],
})
export class ReasonComponent {
@ViewChild('son', { static: true }) son?: ElementRef;
dialogRef = inject<MatDialogRef<ReasonComponent>>(MatDialogRef);
private data = inject<{
title: string;
@@ -1,6 +1,6 @@
import { CdkScrollableModule } from '@angular/cdk/scrolling';
import { CurrencyPipe } from '@angular/common';
import { Component, ElementRef, ViewChild, inject, signal, computed } from '@angular/core';
import { Component, inject, signal, computed } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MAT_DIALOG_DATA, MatDialogRef, MatDialogModule } from '@angular/material/dialog';
@@ -38,7 +38,6 @@ export interface ReceivePaymentFormData {
],
})
export class ReceivePaymentComponent {
@ViewChild('son', { static: true }) son?: ElementRef;
private ser = inject(SettleOptionService);
dialogRef = inject<MatDialogRef<ReceivePaymentComponent>>(MatDialogRef);
data = inject<{
@@ -1,4 +1,4 @@
import { Component, ElementRef, ViewChild, computed, inject, linkedSignal, input } from '@angular/core';
import { Component, computed, inject, linkedSignal, input } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatOptionModule } from '@angular/material/core';
@@ -43,7 +43,6 @@ export interface SectionPrinterFormData {
],
})
export class SectionPrinterComponent {
@ViewChild('section', { static: true }) sectionElement?: ElementRef;
private printerService = inject(PrinterService);
private sectionService = inject(SectionService);
private route = inject(ActivatedRoute);
@@ -3,7 +3,7 @@
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Name</mat-label>
<input matInput #nameElement [formField]="form.name" (keyup.enter)="save()" />
<input matInput [formField]="form.name" (keyup.enter)="save()" />
</mat-form-field>
</div>
</form>
@@ -1,4 +1,4 @@
import { AfterViewInit, Component, ElementRef, ViewChild, computed, inject, linkedSignal, input } from '@angular/core';
import { Component, computed, inject, linkedSignal, input, afterNextRender } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatDialog } from '@angular/material/dialog';
@@ -21,8 +21,7 @@ export interface SectionDetailFormData {
styleUrls: ['./section-detail.component.css'],
imports: [MatButtonModule, MatFormFieldModule, MatInputModule, FormField],
})
export class SectionDetailComponent implements AfterViewInit {
@ViewChild('nameElement', { static: true }) nameElement?: ElementRef;
export class SectionDetailComponent {
private route = inject(ActivatedRoute);
private router = inject(Router);
private dialog = inject(MatDialog);
@@ -44,12 +43,10 @@ export class SectionDetailComponent implements AfterViewInit {
form = form(this.formModel);
ngAfterViewInit() {
setTimeout(() => {
if (this.nameElement !== undefined) {
this.nameElement.nativeElement.focus();
}
}, 0);
constructor() {
afterNextRender(() => {
this.form().focusBoundControl();
});
}
save() {
@@ -3,7 +3,7 @@
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Name</mat-label>
<input matInput #nameElement [formField]="form.name" />
<input matInput [formField]="form.name" />
</mat-form-field>
</div>
<div class="row-container">
@@ -1,4 +1,4 @@
import { AfterViewInit, Component, ElementRef, ViewChild, computed, inject, linkedSignal, input } from '@angular/core';
import { Component, computed, inject, linkedSignal, input, afterNextRender } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
@@ -37,8 +37,7 @@ export interface SettleOptionDetailFormData {
FormField,
],
})
export class SettleOptionDetailComponent implements AfterViewInit {
@ViewChild('nameElement', { static: true }) nameElement?: ElementRef;
export class SettleOptionDetailComponent {
private route = inject(ActivatedRoute);
private router = inject(Router);
private dialog = inject(MatDialog);
@@ -73,14 +72,9 @@ export class SettleOptionDetailComponent implements AfterViewInit {
this.reportingLevel = Object.keys(ReportingLevel)
.filter((e) => !isNaN(+e))
.map((o) => ({ id: +o, name: ReportingLevel[+o] as string }));
}
ngAfterViewInit() {
setTimeout(() => {
if (this.nameElement !== undefined) {
this.nameElement.nativeElement.focus();
}
}, 0);
afterNextRender(() => {
this.form().focusBoundControl();
});
}
save() {
@@ -3,7 +3,7 @@
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Name</mat-label>
<input matInput #nameElement [formField]="form.name" (keyup.enter)="save()" />
<input matInput [formField]="form.name" (keyup.enter)="save()" />
</mat-form-field>
</div>
<div class="row-container">
@@ -1,4 +1,4 @@
import { AfterViewInit, Component, ElementRef, ViewChild, computed, inject, linkedSignal, input } from '@angular/core';
import { Component, computed, inject, linkedSignal, input, afterNextRender } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
@@ -37,8 +37,7 @@ export interface TableDetailFormData {
FormField,
],
})
export class TableDetailComponent implements AfterViewInit {
@ViewChild('nameElement', { static: true }) nameElement?: ElementRef;
export class TableDetailComponent {
private sectionService = inject(SectionService);
private route = inject(ActivatedRoute);
private router = inject(Router);
@@ -68,12 +67,10 @@ export class TableDetailComponent implements AfterViewInit {
sections = computed(() => this.sectionsResource.value() ?? []);
ngAfterViewInit() {
setTimeout(() => {
if (this.nameElement !== undefined) {
this.nameElement.nativeElement.focus();
}
}, 0);
constructor() {
afterNextRender(() => {
this.form().focusBoundControl();
});
}
save() {
@@ -3,7 +3,7 @@
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Name</mat-label>
<input matInput #nameElement [formField]="form.name" (keyup.enter)="save()" />
<input matInput [formField]="form.name" (keyup.enter)="save()" />
<mat-hint> Format for GST: ST GST &#64; x% (1/2) ; CGST &#64; x% (1/2) </mat-hint>
</mat-form-field>
</div>
@@ -1,4 +1,4 @@
import { AfterViewInit, Component, ElementRef, ViewChild, computed, inject, linkedSignal, input } from '@angular/core';
import { Component, computed, inject, linkedSignal, input, afterNextRender } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatOptionModule } from '@angular/material/core';
@@ -28,8 +28,7 @@ export interface TaxDetailFormData {
styleUrls: ['./tax-detail.component.css'],
imports: [MatButtonModule, MatFormFieldModule, MatInputModule, MatOptionModule, MatSelectModule, FormField],
})
export class TaxDetailComponent implements AfterViewInit {
@ViewChild('nameElement', { static: true }) nameElement?: ElementRef;
export class TaxDetailComponent {
private regimeService = inject(RegimeService);
private route = inject(ActivatedRoute);
private router = inject(Router);
@@ -58,12 +57,10 @@ export class TaxDetailComponent implements AfterViewInit {
regimes = computed(() => this.regimesResource.value() ?? []);
ngAfterViewInit() {
setTimeout(() => {
if (this.nameElement !== undefined) {
this.nameElement.nativeElement.focus();
}
}, 0);
constructor() {
afterNextRender(() => {
this.form().focusBoundControl();
});
}
save() {
@@ -6,7 +6,7 @@
<div class="row-container">
<mat-form-field class="flex-auto basis-3-4">
<mat-label>Name</mat-label>
<input matInput #name [formField]="productForm.name" />
<input matInput [formField]="productForm.name" />
</mat-form-field>
<mat-form-field class="flex-auto basis-1-4">
<mat-label>Fraction Units</mat-label>
@@ -1,14 +1,4 @@
import {
AfterViewInit,
Component,
ElementRef,
ViewChild,
computed,
inject,
signal,
linkedSignal,
input,
} from '@angular/core';
import { Component, computed, inject, signal, linkedSignal, input, afterNextRender } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
@@ -74,8 +64,7 @@ export interface TemporalSkuFormData {
FormField,
],
})
export class TemporalProductDetailComponent implements AfterViewInit {
@ViewChild('name', { static: true }) nameElement?: ElementRef;
export class TemporalProductDetailComponent {
private menuCategoryService = inject(MenuCategoryService);
private saleCategoryService = inject(SaleCategoryService);
private route = inject(ActivatedRoute);
@@ -175,12 +164,10 @@ export class TemporalProductDetailComponent implements AfterViewInit {
//
// // }
ngAfterViewInit() {
setTimeout(() => {
if (this.nameElement !== undefined) {
this.nameElement.nativeElement.focus();
}
}, 0);
constructor() {
afterNextRender(() => {
this.productForm().focusBoundControl();
});
}
editProduct(p: Product) {
@@ -188,7 +175,7 @@ export class TemporalProductDetailComponent implements AfterViewInit {
console.log(p);
setTimeout(() => this.nameElement?.nativeElement?.focus?.(), 0);
this.productForm().focusBoundControl();
}
updateProduct() {
@@ -3,7 +3,7 @@
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Name</mat-label>
<input matInput #nameElement [formField]="form.name" />
<input matInput [formField]="form.name" />
</mat-form-field>
</div>
<div class="row-container">
@@ -1,4 +1,4 @@
import { AfterViewInit, Component, ElementRef, ViewChild, computed, inject, linkedSignal, input } from '@angular/core';
import { Component, computed, inject, linkedSignal, input, afterNextRender } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
@@ -35,8 +35,7 @@ export interface UserDetailFormData {
FormField,
],
})
export class UserDetailComponent implements AfterViewInit {
@ViewChild('nameElement', { static: true }) nameElement?: ElementRef;
export class UserDetailComponent {
private route = inject(ActivatedRoute);
private router = inject(Router);
private snackBar = inject(MatSnackBar);
@@ -64,15 +63,10 @@ export class UserDetailComponent implements AfterViewInit {
hide = true;
constructor() {
this.hide = true;
}
ngAfterViewInit() {
setTimeout(() => {
if (this.nameElement !== undefined) {
this.nameElement.nativeElement.focus();
}
}, 0);
afterNextRender(() => {
this.form().focusBoundControl();
// this.formModel().root.focus("name");
});
}
save() {
+438 -451
View File
File diff suppressed because it is too large. Load diff