feat: Implement price info card component and product categories with grid view

- Added `price-info-card.component` to display total amount, tax, and payable amount.
- Integrated price masking directive for formatted price display.
- Created `categories.component` to list product categories with loading skeletons.
- Implemented `grid-view.component` for displaying products in a grid layout.
- Enhanced `products.component` to manage category selection and product display.
- Introduced new models for product categories and stock responses.
- Updated `main.service.ts` to fetch product categories and stock data.
- Refactored `POSStore` to manage state for products, categories, and order items.
- Added utility functions for price formatting and number normalization.
- Included placeholder images for product and logo.
This commit is contained in:
2025-12-14 20:34:15 +03:30
parent 35be7e0298
commit 17fa65407c
43 changed files with 894 additions and 132 deletions
+22 -19
View File
@@ -1,35 +1,38 @@
<div class="flex items-center border rounded overflow-hidden bg-white" [class.opacity-60]="disabled">
<div class="flex items-center w-full" [class.opacity-60]="disabled">
<button
type="button"
pButton
class="p-button p-component flex items-center justify-center px-3"
(click)="decrease()"
[disabled]="disabled"
aria-label="decrease"
>
-
</button>
icon="pi pi-plus"
aria-label="increase"
size="small"
class="shrink-0"
outlined
(click)="increase()"
></button>
<input
class="text-center w-16 px-2 py-1 outline-none"
<p-inputnumber
type="number"
[value]="value"
(input)="onInput($event)"
[min]="min ?? undefined"
[max]="max ?? undefined"
[formControl]="control"
[min]="min"
[max]="max"
[step]="step"
size="small"
[disabled]="disabled"
[(ngModel)]="value"
aria-label="quantity"
inputStyleClass="text-center! outline-none border-none! grow w-12 px-1! appearance-none!"
/>
<button
type="button"
pButton
class="p-button p-component flex items-center justify-center px-3"
(click)="increase()"
[disabled]="disabled"
aria-label="increase"
>
+
</button>
icon="pi pi-minus"
aria-label="decrease"
size="small"
class="shrink-0"
outlined
(click)="decrease()"
></button>
</div>
+11 -9
View File
@@ -1,22 +1,24 @@
import { CommonModule } from '@angular/common';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Component, Input, model } from '@angular/core';
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ButtonModule } from 'primeng/button';
import { InputNumber } from 'primeng/inputnumber';
@Component({
selector: 'app-uikit-counter',
templateUrl: './uikit-counter.component.html',
styleUrls: ['./uikit-counter.component.scss'],
standalone: true,
imports: [CommonModule, ButtonModule],
imports: [CommonModule, ButtonModule, FormsModule, ReactiveFormsModule, FormsModule, InputNumber],
})
export class UikitCounterComponent {
@Input() value = 0;
@Input() control = new FormControl<number>(0);
@Input() min: number | null = null;
@Input() max: number | null = null;
@Input() step = 1;
@Input() disabled = false;
@Output() valueChange = new EventEmitter<number>();
value = model(0);
clamp(v: number): number {
if (this.min !== null && v < this.min) return this.min;
@@ -26,20 +28,20 @@ export class UikitCounterComponent {
setValue(v: number) {
const clamped = this.clamp(v);
if (clamped !== this.value) {
this.value = clamped;
this.valueChange.emit(this.value);
if (clamped !== this.value()) {
this.control?.setValue(clamped);
this.value.set(clamped);
}
}
increase() {
if (this.disabled) return;
this.setValue(this.value + this.step);
this.setValue(this.value() + this.step);
}
decrease() {
if (this.disabled) return;
this.setValue(this.value - this.step);
this.setValue(this.value() - this.step);
}
onInput(e: Event) {