feat: implement pos goods management module; add create, update, and list components with API integration

This commit is contained in:
2026-05-20 20:22:38 +03:30
parent c5e1fab09b
commit 1b4ac0789c
28 changed files with 437 additions and 65 deletions
@@ -0,0 +1,18 @@
<shared-dialog
[header]="preparedTitle"
[(visible)]="visible"
[modal]="true"
[style]="{ width: '500px' }"
[closable]="true"
(onHide)="close()">
@if (visible) {
<shared-good-form
[guildId]="guildId"
[initialValues]="initialValues"
[editMode]="editMode"
[createFn]="createFn"
[updateFn]="updateFn"
(onSubmit)="onFormSubmit($event)"
(onClose)="close()" />
}
</shared-dialog>
@@ -0,0 +1,30 @@
import { AbstractDialog } from '@/shared/abstractClasses/abstract-dialog';
import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Observable } from 'rxjs';
import { SharedGoodFormComponent } from './form.component';
import { IGoodResponse } from './types';
@Component({
selector: 'shared-good-form-dialog',
templateUrl: './form-dialog.component.html',
imports: [SharedDialogComponent, SharedGoodFormComponent],
})
export class ConsumerUserFormDialogComponent extends AbstractDialog {
@Output() onSubmit = new EventEmitter<IGoodResponse>();
@Input() initialValues?: IGoodResponse;
@Input() editMode?: boolean;
@Input({ required: true }) guildId!: string;
@Input() createFn?: (payload: FormData) => Observable<IGoodResponse>;
@Input() updateFn?: (payload: FormData) => Observable<IGoodResponse>;
get preparedTitle() {
return `${this.editMode ? 'ویرایش' : 'افزودن'} کالا`;
}
onFormSubmit(response: IGoodResponse) {
this.onSubmit.emit(response);
this.close();
}
}
@@ -1,19 +1,22 @@
<shared-dialog [header]="preparedTitle" [(visible)]="visible" [modal]="true" [style]="{ width: '500px' }"
[closable]="true">
<form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4">
@if (visible) {
<app-shared-upload-file accept="image/*" name="image" [initial_file_url]="initialValues?.image_url || ''"
(onSelect)="changeFile($event)" />
}
<field-name [control]="form.controls.name" name="name" />
<field-sku label="شناسه کالا" [control]="form.controls.sku_id" name="sku_id" />
<catalog-guild-goodCategories-select [guildId]="guildId" label="دسته‌بندی" [control]="form.controls.category_id"
name="category_id" />
<catalog-measure-units-select label="واحد اندازه‌گیری" [control]="form.controls.measure_unit_id"
name="measure_unit_id" />
<app-enum-select type="goodPricingModel" [control]="form.controls.pricing_model" name="pricing_model" />
<field-description [control]="form.controls.description" />
<form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4">
<app-shared-upload-file
accept="image/*"
name="image"
[initial_file_url]="initialValues?.image_url || ''"
(onSelect)="changeFile($event)" />
<field-name [control]="form.controls.name" name="name" />
<field-sku label="شناسه کالا" [control]="form.controls.sku_id" name="sku_id" />
<catalog-guild-goodCategories-select
[guildId]="guildId"
label="دسته‌بندی"
[control]="form.controls.category_id"
name="category_id" />
<catalog-measure-units-select
label="واحد اندازه‌گیری"
[control]="form.controls.measure_unit_id"
name="measure_unit_id" />
<app-enum-select type="goodPricingModel" [control]="form.controls.pricing_model" name="pricing_model" />
<field-description [control]="form.controls.description" />
<app-form-footer-actions [submitLabel]="'ذخیره'" [loading]="submitLoading()" (onCancel)="close()" />
</form>
</shared-dialog>
<app-form-footer-actions [submitLabel]="'ذخیره'" [loading]="submitLoading()" (onCancel)="close()" />
</form>
@@ -1,4 +1,4 @@
import { AbstractFormDialog } from '@/shared/abstractClasses';
import { AbstractForm } from '@/shared/abstractClasses';
import { DescriptionComponent, NameComponent, SkuComponent } from '@/shared/components';
import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component';
import { Component, Input, signal } from '@angular/core';
@@ -8,7 +8,6 @@ import { Maybe } from '@/core';
import { EnumSelectComponent } from '@/shared/apiEnum/select.component';
import { CatalogGuildGoodCategoriesSelectComponent } from '@/shared/catalog/guildGoodCategories';
import { CatalogMeasureUnitsSelectComponent } from '@/shared/catalog/measureUnits';
import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component';
import { onSelectFileArgs } from '@/shared/components/uploadFile/model';
import { fieldControl } from '@/shared/constants';
import { buildFormData } from '@/utils';
@@ -21,7 +20,6 @@ import { IGoodRequestPayload, IGoodResponse } from './types';
templateUrl: './form.component.html',
imports: [
ReactiveFormsModule,
SharedDialogComponent,
FormFooterActionsComponent,
EnumSelectComponent,
SkuComponent,
@@ -32,13 +30,10 @@ import { IGoodRequestPayload, IGoodResponse } from './types';
CatalogGuildGoodCategoriesSelectComponent,
],
})
export class SharedGoodFormComponent extends AbstractFormDialog<
IGoodRequestPayload,
IGoodResponse
> {
export class SharedGoodFormComponent extends AbstractForm<IGoodRequestPayload, IGoodResponse> {
@Input({ required: true }) guildId!: string;
@Input({ required: true }) createFn!: (payload: FormData) => Observable<IGoodResponse>;
@Input({ required: true }) updateFn!: (payload: FormData) => Observable<IGoodResponse>;
@Input() createFn?: (payload: FormData) => Observable<IGoodResponse>;
@Input() updateFn?: (payload: FormData) => Observable<IGoodResponse>;
goodImage = signal<Maybe<File>>(null);
@@ -69,8 +64,8 @@ export class SharedGoodFormComponent extends AbstractFormDialog<
override submitForm() {
const formData = buildFormData(this.form, { image: this.goodImage() });
if (this.editMode) {
return this.updateFn(formData);
return this.updateFn?.(formData);
}
return this.createFn(formData);
return this.createFn?.(formData);
}
}