feat: update environment configurations and add gold price management module with related components and services
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<app-card-data cardTitle="قیمت پیشفرض هر گرم طلا" class="w-full">
|
||||
<p-message variant="text" severity="info" icon="pi pi-info-circle">
|
||||
در این بخش میتوانید قیمت پیشفرض هر گرم طلا را وارد کنید.
|
||||
</p-message>
|
||||
<form [formGroup]="form" class="mt-6" (submit)="submit()">
|
||||
<field-unit-price [control]="form.controls.price" />
|
||||
<app-form-footer-actions (onCancel)="close()" />
|
||||
</form>
|
||||
</app-card-data>
|
||||
@@ -0,0 +1,55 @@
|
||||
import { AbstractForm } from '@/shared/abstractClasses';
|
||||
import { AppCardComponent, UnitPriceComponent } from '@/shared/components';
|
||||
import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component';
|
||||
import { fieldControl } from '@/shared/constants';
|
||||
import { Component, inject, signal } from '@angular/core';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { Message } from 'primeng/message';
|
||||
import { IPosConfigGoldPricePayload, IPosConfigGoldPriceResponse } from './models';
|
||||
import { PosConfigGoldPriceService } from './services/main.service';
|
||||
|
||||
@Component({
|
||||
selector: 'pos-config-gold-price-form',
|
||||
templateUrl: 'form.component.html',
|
||||
imports: [
|
||||
ReactiveFormsModule,
|
||||
FormFooterActionsComponent,
|
||||
AppCardComponent,
|
||||
Message,
|
||||
UnitPriceComponent,
|
||||
],
|
||||
})
|
||||
export class PosConfigGoldPriceFormComponent extends AbstractForm<
|
||||
IPosConfigGoldPricePayload,
|
||||
IPosConfigGoldPriceResponse
|
||||
> {
|
||||
private readonly service = inject(PosConfigGoldPriceService);
|
||||
|
||||
loading = signal(true);
|
||||
|
||||
initForm = () => {
|
||||
const form = this.fb.group({
|
||||
price: fieldControl.unit_price(),
|
||||
});
|
||||
|
||||
return form;
|
||||
};
|
||||
|
||||
form = this.initForm();
|
||||
|
||||
override async ngOnInit() {
|
||||
this.loading.set(true);
|
||||
const initialValues = await this.service.get();
|
||||
|
||||
console.log('initialValues.price', initialValues);
|
||||
|
||||
this.form.controls.price.setValue((initialValues || 0) + '');
|
||||
this.loading.set(false);
|
||||
}
|
||||
|
||||
override submitForm() {
|
||||
const formValue = this.form.value.price as IPosConfigGoldPricePayload;
|
||||
this.toastService.success({ text: 'تغییرات با موفقیت اعمال شد.' });
|
||||
return this.service.submit(formValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type IPosConfigGoldPriceResponse = number;
|
||||
|
||||
export type IPosConfigGoldPricePayload = number;
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { LOCAL_STORAGE_KEYS } from 'src/assets/constants';
|
||||
import { IPosConfigGoldPricePayload, IPosConfigGoldPriceResponse } from '../models';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PosConfigGoldPriceService {
|
||||
get(): IPosConfigGoldPriceResponse {
|
||||
const defaultPrice = Number(
|
||||
window.localStorage.getItem(LOCAL_STORAGE_KEYS.POS_CONFIG_GOLD_PRICE) || '0'
|
||||
);
|
||||
|
||||
this.submit(defaultPrice);
|
||||
return defaultPrice;
|
||||
}
|
||||
|
||||
submit(data: IPosConfigGoldPricePayload) {
|
||||
window.localStorage.setItem(LOCAL_STORAGE_KEYS.POS_CONFIG_GOLD_PRICE, data.toString());
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
<div class="flex h-full w-full items-center justify-center p-4">
|
||||
<div class="flex h-full w-full flex-col items-center justify-center gap-6 p-4">
|
||||
@if (info()?.guild!.code.toLocaleLowerCase() === 'gold') {
|
||||
<pos-config-gold-price-form class="w-full" (onClose)="returnToMainPage()" />
|
||||
}
|
||||
<app-card-data cardTitle="تنظیمات فاکتور" class="w-full">
|
||||
<p-message variant="text" severity="info" icon="pi pi-info-circle">
|
||||
هریک از موارد زیر را که میخواهید در چاپ فاکتور نمایش داده شوند را انتخاب کنید
|
||||
</p-message>
|
||||
<pos-config-print-form class="mt-6 block w-full" (onClose)="returnToMainPage()" (onSubmit)="returnToMainPage()" />
|
||||
<pos-config-print-form class="mt-6 block w-full" (onClose)="returnToMainPage()" />
|
||||
</app-card-data>
|
||||
</div>
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
import { PosInfoStore } from '@/domains/pos/store';
|
||||
import { AppCardComponent } from '@/shared/components';
|
||||
import { UikitFlatpickrJalaliComponent } from '@/uikit';
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { Component, computed, inject } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Message } from 'primeng/message';
|
||||
import { PosConfigGoldPriceFormComponent } from '../components/goldPrice/form.component';
|
||||
import { PosConfigPrintFormComponent } from '../components/print/form.component';
|
||||
|
||||
@Component({
|
||||
selector: 'pos-config-page',
|
||||
templateUrl: './root.component.html',
|
||||
imports: [PosConfigPrintFormComponent, AppCardComponent, Message, UikitFlatpickrJalaliComponent],
|
||||
imports: [
|
||||
PosConfigPrintFormComponent,
|
||||
AppCardComponent,
|
||||
Message,
|
||||
PosConfigGoldPriceFormComponent,
|
||||
],
|
||||
})
|
||||
export class PosConfigPageComponent {
|
||||
private readonly router = inject(Router);
|
||||
private readonly posInfoStore = inject(PosInfoStore);
|
||||
|
||||
readonly info = computed(() => this.posInfoStore.entity());
|
||||
|
||||
returnToMainPage() {
|
||||
this.router.navigateByUrl('/');
|
||||
}
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
<div class="flex flex-col min-h-full">
|
||||
<div
|
||||
class="bg-surface-card z-10 py-4 border-b border-surface-border shadow-[0_-4px_16px_rgba(0,0,0,0.08)] rounded-b-xl"
|
||||
>
|
||||
<div class="flex items-center justify-end mb-4 px-4">
|
||||
<div class="flex min-h-full flex-col">
|
||||
<div class="bg-surface-card border-surface-border z-10 rounded-b-xl border-b py-4">
|
||||
<div class="mb-4 flex items-center justify-end px-4">
|
||||
<!-- <div class="flex items-center gap-2 text-muted-color">
|
||||
<i class="pi pi-list"></i>
|
||||
<span class="text-base font-bold">لیست کالاها</span>
|
||||
</div> -->
|
||||
<div class="flex items-center gap-2 w-full">
|
||||
<app-search-input class="sm:w-auto w-full max-w-xs" (search)="onSearch($event)"></app-search-input>
|
||||
<div class="flex w-full items-center gap-2">
|
||||
<app-search-input class="w-full max-w-xs sm:w-auto" (search)="onSearch($event)"></app-search-input>
|
||||
<p-divider layout="vertical" />
|
||||
<p-selectbutton
|
||||
[options]="viewOptions"
|
||||
[(ngModel)]="viewType"
|
||||
optionValue="value"
|
||||
[allowEmpty]="false"
|
||||
class="shrink-0 border border-surface-border bg-surface-ground"
|
||||
>
|
||||
class="border-surface-border bg-surface-ground shrink-0 border">
|
||||
<ng-template #item let-item>
|
||||
<i [class]="item.icon"></i>
|
||||
</ng-template>
|
||||
@@ -25,13 +22,13 @@
|
||||
</div>
|
||||
<pos-good-categories (categoryChange)="onCategoryChange($event)" />
|
||||
</div>
|
||||
<div [class]="`p-4 ${!loading() && !goods()?.length ? ' grow flex items-center justify-center' : ''}`">
|
||||
<div [class]="`p-4 ${!loading() && !goods()?.length ? ' flex grow items-center justify-center' : ''}`">
|
||||
@if (!loading() && !goods()?.length) {
|
||||
<div class="flex flex-col items-center gap-4 mt-10">
|
||||
<i class="pi pi-box text-6xl! text-muted-color"></i>
|
||||
<span class="text-xl font-semibold text-muted-color">کالایی برای نمایش وجود ندارد.</span>
|
||||
<div class="mt-10 flex flex-col items-center gap-4">
|
||||
<i class="pi pi-box text-muted-color text-6xl!"></i>
|
||||
<span class="text-muted-color text-xl font-semibold">کالایی برای نمایش وجود ندارد.</span>
|
||||
</div>
|
||||
} @else if (viewType === "grid") {
|
||||
} @else if (viewType === 'grid') {
|
||||
<pos-good-grid-view (onAdd)="addGood($event)" />
|
||||
} @else {
|
||||
<pos-goods-list-view (onAdd)="addGood($event)" />
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
<div class="grid 2xl:grid-cols-8 xl:grid-cols-6 lg:grid-cols-4 grid-cols-2 gap-3 flex-wrap">
|
||||
<div class="grid grid-cols-2 flex-wrap gap-3 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-8">
|
||||
@if (loading()) {
|
||||
@for (_ of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15]; track $index) {
|
||||
<div class="flex-1 aspect-[0.9]">
|
||||
<div class="aspect-[0.9] flex-1">
|
||||
<p-skeleton width="100%" height="100%"></p-skeleton>
|
||||
</div>
|
||||
}
|
||||
} @else {
|
||||
@for (good of visibleGoods(); track good.id) {
|
||||
<div class="flex-1 bg-surface-card rounded-lg p-1 shadow-xs flex flex-col">
|
||||
<div class="bg-surface-card border-surface-border flex flex-1 flex-col rounded-lg border p-1">
|
||||
<img
|
||||
[src]="good.image_url || goodPlaceholder"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
class="w-full aspect-[1.3] object-cover rounded-md shrink-0"
|
||||
/>
|
||||
<div class="mt-2 text-center grow flex flex-col justify-between">
|
||||
<span class="text-lg font-bold text-center grow flex items-center justify-center w-full">
|
||||
class="aspect-[1.3] w-full shrink-0 rounded-md object-cover" />
|
||||
<div class="mt-2 flex grow flex-col justify-between text-center">
|
||||
<span class="flex w-full grow items-center justify-center text-center text-lg font-bold">
|
||||
{{ good.name }}
|
||||
@if (!good.is_default_guild_good) {
|
||||
<small class="text-xs text-muted-color">*</small>
|
||||
<small class="text-muted-color text-xs">*</small>
|
||||
}
|
||||
</span>
|
||||
<div class="flex items-center justify-between mt-1 w-full px-2 my-2 shrink-0 gap-1">
|
||||
<button pButton type="button" label="انتخاب" class="grow w-full" (click)="addProduct(good)"></button>
|
||||
<div class="my-2 mt-1 flex w-full shrink-0 items-center justify-between gap-1 px-2">
|
||||
<button pButton type="button" label="انتخاب" class="w-full grow" (click)="addProduct(good)"></button>
|
||||
<pos-good-favorite-cta [isFavorite]="good.is_favorite" [id]="good.id" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -31,7 +30,7 @@
|
||||
}
|
||||
</div>
|
||||
@if (!loading() && hasMore()) {
|
||||
<div class="flex justify-center mt-3">
|
||||
<div class="mt-3 flex justify-center">
|
||||
<button pButton type="button" outlined (click)="loadMore()">نمایش کالاهای بیشتر</button>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { greaterThanValidator } from '@/core/validators/greater.validator';
|
||||
import { PosConfigGoldPriceService } from '@/domains/pos/modules/configs/components/goldPrice/services/main.service';
|
||||
import { AbstractForm } from '@/shared/abstractClasses';
|
||||
import { EnumSelectComponent } from '@/shared/apiEnum/select.component';
|
||||
import { InputComponent } from '@/shared/components';
|
||||
import { AmountPercentageInputComponent } from '@/shared/components/amountPercentageInput/amount-percentage-input.component';
|
||||
import { TGoldKarat } from '@/shared/localEnum/constants/goldKarat';
|
||||
import { formatWithCurrency } from '@/utils';
|
||||
import { Component, computed, EventEmitter, Input, Output, signal } from '@angular/core';
|
||||
import { Component, computed, EventEmitter, inject, Input, Output, signal } from '@angular/core';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { ButtonDirective } from 'primeng/button';
|
||||
@@ -47,6 +48,8 @@ export class PosGoldPayloadFormComponent extends AbstractForm<
|
||||
@Output() onSubmitForm = new EventEmitter<IGoldPayload>();
|
||||
@Output() onChangeTotalAmount = new EventEmitter<number>();
|
||||
|
||||
private readonly posGoldConfig = inject(PosConfigGoldPriceService);
|
||||
|
||||
readonly discountTypeItems = [
|
||||
{
|
||||
label: 'از سود',
|
||||
@@ -66,11 +69,20 @@ export class PosGoldPayloadFormComponent extends AbstractForm<
|
||||
taxAmount = signal<number>(0);
|
||||
totalAmount = signal<number>(0);
|
||||
|
||||
goldDefaultUnitPrice = signal<number>(0);
|
||||
|
||||
preparedCTAText = computed(() => (this.editMode ? 'اعمال ویرایش' : 'افزودن به لیست خرید'));
|
||||
|
||||
private readonly initialForm = () => {
|
||||
const defaultPrice = this.posGoldConfig.get();
|
||||
|
||||
this.goldDefaultUnitPrice.set(defaultPrice);
|
||||
|
||||
const form = this.fb.group({
|
||||
unit_price: [this.initialValues?.unit_price || 0, [Validators.required]],
|
||||
unit_price: [
|
||||
this.initialValues?.unit_price || this.goldDefaultUnitPrice() || 0,
|
||||
[Validators.required],
|
||||
],
|
||||
quantity: [this.initialValues?.quantity || 0, [Validators.required, greaterThanValidator(0)]],
|
||||
discount: [this.initialValues?.discount || 0],
|
||||
discount_percentage: [0, [Validators.max(100), Validators.min(0)]],
|
||||
|
||||
@@ -5,4 +5,5 @@ export enum LOCAL_STORAGE_KEYS {
|
||||
LAST_LOGIN_TIME = 'lastLoginTime',
|
||||
LOGIN_ATTEMPTS = 'loginAttempts',
|
||||
POS_CONFIG_PRINT = 'posConfigPrint',
|
||||
POS_CONFIG_GOLD_PRICE = 'posConfigGoldPrice',
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user