This commit is contained in:
2026-08-27 11:38:38 +00:00
parent 311464e593
commit ff537edb06
19 changed files with 456 additions and 393 deletions
@@ -13,7 +13,7 @@ import { ProductService } from '../../product/product.service';
import { BundleItem } from '../bundle';
export interface BundleDetailDialogFormData {
product: BundleItem | ProductQuery | string;
product: ProductQuery | string;
quantity: number;
salePrice: number;
printInBill: boolean;
@@ -44,7 +44,7 @@ export class BundleDetailDialogComponent {
formModel = linkedSignal<BundleItem, BundleDetailDialogFormData>({
source: () => this.data.item,
computation: (item) => ({
product: item,
product: item.sku ?? '',
quantity: Number(item.quantity ?? 1),
salePrice: Number(item.salePrice ?? 0),
printInBill: item.printInBill ?? false,
@@ -84,8 +84,7 @@ export class BundleDetailDialogComponent {
return;
}
this.data.item.itemId = product.id ?? '';
this.data.item.name = product.name ?? '';
this.data.item.sku = product;
this.data.item.quantity = quantity;
this.data.item.salePrice = salePrice;
this.data.item.printInBill = formValue.printInBill ?? false;
@@ -24,7 +24,7 @@
<mat-label>Menu Category</mat-label>
<mat-select [formField]="form.menuCategory">
@for (mc of menuCategories(); track mc) {
<mat-option [value]="mc.id">
<mat-option [value]="mc">
{{ mc.name }}
</mat-option>
}
@@ -35,7 +35,7 @@
<mat-label>Sale Category</mat-label>
<mat-select [formField]="form.saleCategory">
@for (sc of saleCategories(); track sc) {
<mat-option [value]="sc.id">
<mat-option [value]="sc">
{{ sc.name }}
</mat-option>
}
@@ -61,9 +61,9 @@
<input
type="text"
matInput
[formField]="form.addRow.itemId"
[formField]="form.addRow.sku"
[matAutocomplete]="autoItem"
[value]="displayFn(formModel().addRow.itemId)"
[value]="displayFn(formModel().addRow.sku)"
/>
<mat-autocomplete #autoItem="matAutocomplete" [displayWith]="displayFn">
@for (p of itemProducts(); track p) {
@@ -89,11 +89,11 @@
<div class="row-container wrapped">
<mat-table [dataSource]="formModel().items" aria-label="Bundle Items" class="flex-auto">
<!-- Name Column (name is "name (units)") -->
<!-- Name Column -->
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef>Item</mat-header-cell>
<mat-cell *matCellDef="let row">
{{ row.name }}
{{ row.sku?.name }}
</mat-cell>
</ng-container>
@@ -34,7 +34,7 @@ export interface BundleDetailFormData {
hasHappyHour: boolean;
isNotAvailable: boolean;
addRow: {
itemId: ProductQuery | string;
sku: ProductQuery | string;
quantity: number;
salePrice: number;
printInBill: boolean;
@@ -89,7 +89,7 @@ export class BundleDetailComponent {
isNotAvailable: itemVal.isNotAvailable ?? false,
items,
addRow: {
itemId: '',
sku: '',
quantity: 1,
salePrice: 0,
printInBill: true,
@@ -101,7 +101,7 @@ export class BundleDetailComponent {
form = form(this.formModel);
private itemProductSearch = computed(() => {
const v = this.form.addRow.itemId().value();
const v = this.form.addRow.sku().value();
return typeof v === 'string' ? v : null;
});
@@ -116,7 +116,7 @@ export class BundleDetailComponent {
constructor() {
effect(() => {
const v = this.form.addRow.itemId().value();
const v = this.form.addRow.sku().value();
if (typeof v === 'object' && v !== null) {
this.formModel.update((f) => ({
...f,
@@ -147,7 +147,7 @@ export class BundleDetailComponent {
this.formModel.update((f) => ({
...f,
addRow: {
itemId: '',
sku: '',
quantity: 1,
salePrice: 0,
printInBill: true,
@@ -158,7 +158,7 @@ export class BundleDetailComponent {
addRow() {
const formValue = this.form().value().addRow;
if (!formValue) return;
const itemProduct = formValue.itemId as ProductQuery;
const itemProduct = formValue.sku as ProductQuery;
if (!itemProduct || typeof itemProduct === 'string') {
this.snackBar.open('Please select a product', 'Error');
@@ -178,8 +178,7 @@ export class BundleDetailComponent {
}
const bi = new BundleItem({
itemId: itemProduct.id,
name: itemProduct.name ?? '',
sku: itemProduct,
quantity,
salePrice,
printInBill: formValue.printInBill || false,
@@ -72,7 +72,9 @@
<mat-icon>
{{ item.printInBill ? 'visibility' : 'visibility_off' }}
</mat-icon>
<div class="item-name">{{ item.name }} x {{ item.quantity }} @ {{ item.salePrice | currency: 'INR' }}</div>
<div class="item-name">
{{ item.sku?.name }} x {{ item.quantity }} @ {{ item.salePrice | currency: 'INR' }}
</div>
}
</div>
</mat-cell>
@@ -88,7 +88,7 @@ export class BundleListComponent {
...b,
menuCategory: b.menuCategory?.name ?? '',
items: (b.items ?? [])
.map((i) => `${i.name}${i.quantity && i.quantity !== 1 ? ` x${i.quantity}` : ''}`)
.map((i) => `${i.sku?.name ?? ''}${i.quantity && i.quantity !== 1 ? ` x${i.quantity}` : ''}`)
.join(' | '),
}));
+2 -3
View File
@@ -3,14 +3,13 @@ import { ProductQuery } from '../core/product-query';
import { SaleCategory } from '../core/sale-category';
export class BundleItem {
sku: ProductQuery | undefined;
name: string;
id?: string;
sku?: ProductQuery;
salePrice: number;
quantity: number;
printInBill: boolean;
public constructor(init?: Partial<BundleItem>) {
this.name = '';
this.salePrice = 0;
this.quantity = 0;
this.printInBill = true;
@@ -1,6 +1,6 @@
/* eslint-disable @angular-eslint/no-input-rename */
import { PercentPipe } from '@angular/common';
import { Component, inject, signal, effect, computed, debounced, afterNextRender, input } from '@angular/core';
import { Component, inject, linkedSignal, 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';
@@ -51,8 +51,11 @@ export class CustomerListComponent {
sortInput = input<string>('name', { alias: 'f' });
direction = input<string>('asc', { alias: 'd' });
formModel = signal<CustomerListFormData>({
filter: this.route.snapshot.queryParamMap.get('q') ?? '',
formModel = linkedSignal({
source: this.q,
computation: (q): CustomerListFormData => ({
filter: q,
}),
});
form = form(this.formModel);
@@ -1,4 +1,4 @@
import { Component, inject, computed, effect, signal } from '@angular/core';
import { Component, inject, computed, effect, input, linkedSignal } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatDatepickerModule } from '@angular/material/datepicker';
@@ -40,8 +40,21 @@ export class GuestBookListComponent {
private snackBar = inject(MatSnackBar);
private ser = inject(GuestBookService);
formModel = signal<GuestBookListFormData>({
date: this.initialDate(),
date = input(new Date(), {
transform: (v: string | null | undefined): Date => {
if (v) {
const parsed = moment(v, 'DD-MMM-YYYY', true);
if (parsed.isValid()) return parsed.toDate();
}
return new Date();
},
});
formModel = linkedSignal({
source: this.date,
computation: (d): GuestBookListFormData => ({
date: d,
}),
});
form = form(this.formModel);
@@ -65,13 +78,4 @@ export class GuestBookListComponent {
});
});
}
private initialDate(): Date {
const qp = this.route.snapshot.queryParamMap.get('date');
if (qp) {
const parsed = moment(qp, 'DD-MMM-YYYY', true);
if (parsed.isValid()) return parsed.toDate();
}
return new Date();
}
}
@@ -19,10 +19,7 @@ export class GuestBookService {
private http = inject(HttpClient);
private log = inject(ErrorLoggerService);
get(
id: Signal<string | null>,
type?: Signal<GuestBookType | string>,
): HttpResourceRef<GuestBook | undefined> {
get(id: Signal<string | null>, type?: Signal<GuestBookType | string>): HttpResourceRef<GuestBook | undefined> {
return httpResource<GuestBook>(() => {
const idVal = id();
const typeVal = type ? type() : '';
@@ -27,29 +27,27 @@
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Name</mat-label>
<input matInput [formField]="form.name" />
<input matInput [value]="formModel().name" readonly />
</mat-form-field>
</div>
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Address</mat-label>
<textarea matInput [formField]="form.address"> </textarea>
<textarea matInput [value]="formModel().address" readonly> </textarea>
</mat-form-field>
</div>
<div class="row-container">
<mat-checkbox [formField]="form.printInBill">Print in Bill?</mat-checkbox>
<mat-checkbox [checked]="formModel().printInBill" [disabled]="true">Print in Bill?</mat-checkbox>
</div>
<p></p>
<div class="discounts">
@for (r of item.discounts; track r; let i = $index) {
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-label>Discount on {{ r.name }}</mat-label>
<input matInput [formField]="form.discounts[i].discount" />
<span matTextSuffix>%</span>
</mat-form-field>
</div>
}
<ul>
@for (r of formModel().discounts; track r.id) {
@if (r.discount > 0) {
<li>{{ r.name }} - {{ r.discount | percent: '1.2-2' }}</li>
}
}
</ul>
</div>
</form>
</mat-dialog-content>
@@ -1,4 +1,5 @@
import { CdkScrollableModule } from '@angular/cdk/scrolling';
import { PercentPipe } from '@angular/common';
import { Component, inject, computed, debounced, linkedSignal, signal } from '@angular/core';
import { form, FormField } from '@angular/forms/signals';
import { MatAutocompleteSelectedEvent, MatAutocompleteModule } from '@angular/material/autocomplete';
@@ -8,9 +9,9 @@ import { MatOptionModule } from '@angular/material/core';
import { MAT_DIALOG_DATA, MatDialogRef, MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { round } from 'mathjs';
import { Customer } from '../../core/customer';
import { CustomerDiscount } from '../../core/customer-discount';
import { CustomerService } from '../../customers/customer.service';
export interface ChooseCustomerFormData {
@@ -19,9 +20,7 @@ export interface ChooseCustomerFormData {
phone: string;
address: string;
printInBill: boolean;
discounts: {
discount: number | null;
}[];
discounts: CustomerDiscount[];
}
@Component({
@@ -37,6 +36,7 @@ export interface ChooseCustomerFormData {
MatFormFieldModule,
MatInputModule,
MatOptionModule,
PercentPipe,
FormField,
],
})
@@ -55,9 +55,7 @@ export class ChooseCustomerComponent {
phone: data.phone,
address: data.address ?? '',
printInBill: data.printInBill,
discounts: data.discounts.map((x) => ({
discount: x.discount ? x.discount * 100 : null,
})),
discounts: data.discounts ?? [],
}),
});
@@ -70,7 +68,9 @@ export class ChooseCustomerComponent {
debouncedPhone = debounced(this.phoneSignal, 150);
customersResource = this.ser.autocomplete(this.debouncedPhone.value);
customers = computed(() => { return this.customersResource.value() ?? [] });
customers = computed(() => {
return this.customersResource.value() ?? [];
});
save() {
const customer = this.getItem();
@@ -92,7 +92,6 @@ export class ChooseCustomerComponent {
getItem(): Customer {
const formModel = this.formModel();
const array = formModel.discounts;
return new Customer({
id: formModel.id,
@@ -100,14 +99,7 @@ export class ChooseCustomerComponent {
phone: formModel.phone ?? '',
address: formModel.address ?? '',
printInBill: formModel.printInBill ?? false,
discounts: formModel.discounts.map((item, index) => {
const array_item = array?.[index];
return {
...this.item().discounts?.[index],
discount:
array_item && array_item?.discount ? Math.max(Math.min(round(array_item.discount / 100, 5), 100), 0) : 0,
};
}),
discounts: formModel.discounts.map((item) => ({ ...item })),
});
}
}
@@ -12,12 +12,15 @@ export class CustomerDiscountsService {
private injector = inject(Injector);
list(id: Signal<string | undefined>): HttpResourceRef<DiscountItem[] | undefined> {
return httpResource<DiscountItem[]>(() => {
const idVal = id();
return idVal === undefined ? url : `${url}/${idVal}`;
}, {
injector: this.injector,
});
return httpResource<DiscountItem[]>(
() => {
const idVal = id();
return idVal === undefined ? url : `${url}/${idVal}`;
},
{
injector: this.injector,
},
);
}
listForDiscount(): HttpResourceRef<{ name: string; discount: number; discountLimit: number }[] | undefined> {