feat: enhance product and purchase components

- Added new components for handling purchase receipts and payment wrappers.
- Updated product details view to include sales count and stock alerts.
- Refactored invoice payment form to use a template for better structure.
- Introduced confirmation dialog service for payment confirmations.
- Improved state card component for better visual representation of orders.
- Added loading indicators and error handling in various components.
- Updated routes to include new purchase functionality for suppliers.
- Enhanced stock alert component to visually indicate low stock levels.
This commit is contained in:
2025-12-30 21:03:39 +03:30
parent 85a9c8714d
commit 83c3d57866
48 changed files with 797 additions and 259 deletions
@@ -0,0 +1,78 @@
import {
ApplicationRef,
ComponentFactoryResolver,
ComponentRef,
Injectable,
Injector,
} from '@angular/core';
import { ConfirmationDialogComponent } from './confirmation-dialog.component';
@Injectable({
providedIn: 'root',
})
export class ConfirmationDialogService {
private componentRef: ComponentRef<ConfirmationDialogComponent> | null = null;
constructor(
private componentFactoryResolver: ComponentFactoryResolver,
private appRef: ApplicationRef,
private injector: Injector,
) {}
confirm(options: {
message: string;
header?: string;
acceptLabel?: string;
rejectLabel?: string;
accept?: () => void;
reject?: () => void;
}) {
// Create the component dynamically
const factory = this.componentFactoryResolver.resolveComponentFactory(
ConfirmationDialogComponent,
);
this.componentRef = factory.create(this.injector);
// Set inputs
this.componentRef.instance.message = options.message;
if (options.header) this.componentRef.instance.header = options.header;
if (options.acceptLabel) this.componentRef.instance.acceptLabel = options.acceptLabel;
if (options.rejectLabel) this.componentRef.instance.rejectLabel = options.rejectLabel;
// Subscribe to outputs and close after
if (options.accept) {
this.componentRef.instance.onAccept.subscribe(() => {
options.accept!();
this.close();
});
} else {
this.componentRef.instance.onAccept.subscribe(() => this.close());
}
if (options.reject) {
this.componentRef.instance.onReject.subscribe(() => {
options.reject!();
this.close();
});
} else {
this.componentRef.instance.onReject.subscribe(() => this.close());
}
// Attach to the app
this.appRef.attachView(this.componentRef.hostView);
// Append to body
document.body.appendChild(this.componentRef.location.nativeElement);
// Trigger change detection and show
this.componentRef.changeDetectorRef.detectChanges();
this.componentRef.instance.show();
}
close() {
if (this.componentRef) {
this.appRef.detachView(this.componentRef.hostView);
this.componentRef.destroy();
this.componentRef = null;
}
}
}