feat(inventories): implement inventory movement list and transfer functionality

- Added InventoryMovementListComponent to display stock movements with filtering options.
- Created InventoriesTransferFormComponent for transferring stock between inventories.
- Implemented product cardex view to show detailed inventory movements for specific products.
- Enhanced product listing with loading states and improved UI components.
- Introduced Jalali date formatting directive for better date representation.
- Added utility functions for Jalali date conversions and comparisons.
- Created reusable details info card component for displaying inventory details.
- Updated movement reference types and tags for better categorization of inventory movements.
This commit is contained in:
2025-12-14 10:16:14 +03:30
parent b46b8b83f9
commit 35be7e0298
65 changed files with 1543 additions and 80 deletions
+1
View File
@@ -2,6 +2,7 @@ export * from './copy/copy.component';
export * from './datepicker/datepicker.component';
export * from './duration-picker/duration-picker.component';
export * from './paginator.component';
export * from './uikit-counter.component';
export * from './uikit-emptystate.component';
export * from './uikit-field.component';
export * from './uikit-label.component';
@@ -0,0 +1,35 @@
<div class="flex items-center border rounded overflow-hidden bg-white" [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>
<input
class="text-center w-16 px-2 py-1 outline-none"
type="number"
[value]="value"
(input)="onInput($event)"
[min]="min ?? undefined"
[max]="max ?? undefined"
[step]="step"
[disabled]="disabled"
aria-label="quantity"
/>
<button
type="button"
pButton
class="p-button p-component flex items-center justify-center px-3"
(click)="increase()"
[disabled]="disabled"
aria-label="increase"
>
+
</button>
</div>
@@ -0,0 +1,14 @@
:host {
display: inline-block;
}
/* Tailwind handles the main layout. Keep a couple of small overrides for cross-browser input appearance. */
.uikit-counter__input {
-moz-appearance: textfield;
}
.uikit-counter__input::-webkit-outer-spin-button,
.uikit-counter__input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
+52
View File
@@ -0,0 +1,52 @@
import { CommonModule } from '@angular/common';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { ButtonModule } from 'primeng/button';
@Component({
selector: 'app-uikit-counter',
templateUrl: './uikit-counter.component.html',
styleUrls: ['./uikit-counter.component.scss'],
standalone: true,
imports: [CommonModule, ButtonModule],
})
export class UikitCounterComponent {
@Input() value = 0;
@Input() min: number | null = null;
@Input() max: number | null = null;
@Input() step = 1;
@Input() disabled = false;
@Output() valueChange = new EventEmitter<number>();
clamp(v: number): number {
if (this.min !== null && v < this.min) return this.min;
if (this.max !== null && v > this.max) return this.max;
return v;
}
setValue(v: number) {
const clamped = this.clamp(v);
if (clamped !== this.value) {
this.value = clamped;
this.valueChange.emit(this.value);
}
}
increase() {
if (this.disabled) return;
this.setValue(this.value + this.step);
}
decrease() {
if (this.disabled) return;
this.setValue(this.value - this.step);
}
onInput(e: Event) {
const v = (e.target as HTMLInputElement).value;
const n = Number(v);
if (!Number.isNaN(n)) {
this.setValue(n);
}
}
}