diff --git a/src/app/domains/consumer/modules/businessActivities/components/goods/form.component.html b/src/app/domains/consumer/modules/businessActivities/components/goods/form.component.html
new file mode 100644
index 0000000..053da93
--- /dev/null
+++ b/src/app/domains/consumer/modules/businessActivities/components/goods/form.component.html
@@ -0,0 +1,17 @@
+
+
+
diff --git a/src/app/domains/consumer/modules/businessActivities/components/goods/form.component.ts b/src/app/domains/consumer/modules/businessActivities/components/goods/form.component.ts
new file mode 100644
index 0000000..50f5dd3
--- /dev/null
+++ b/src/app/domains/consumer/modules/businessActivities/components/goods/form.component.ts
@@ -0,0 +1,55 @@
+import { AbstractFormDialog } from '@/shared/abstractClasses';
+import { InputComponent } from '@/shared/components';
+import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component';
+import { Component, inject, Input } from '@angular/core';
+import { ReactiveFormsModule, Validators } from '@angular/forms';
+import { Dialog } from 'primeng/dialog';
+
+import { EnumSelectComponent } from '@/shared/apiEnum/select.component';
+import { CatalogGuildGoodCategoriesSelectComponent } from '@/shared/catalog/guildGoodCategories';
+import { IConsumerComplexGoodRequest, IConsumerComplexGoodResponse } from '../../models/goods_io';
+import { GuildGoodsService } from '../../services/goods.service';
+
+@Component({
+ selector: 'consumer-complex-good-form',
+ templateUrl: './form.component.html',
+ imports: [
+ ReactiveFormsModule,
+ Dialog,
+ InputComponent,
+ FormFooterActionsComponent,
+ EnumSelectComponent,
+ CatalogGuildGoodCategoriesSelectComponent,
+ ],
+})
+export class ConsumerComplexGoodFormComponent extends AbstractFormDialog<
+ IConsumerComplexGoodRequest,
+ IConsumerComplexGoodResponse
+> {
+ @Input({ required: true }) businessId!: string;
+ @Input({ required: true }) complexId!: string;
+ @Input({ required: true }) guildId!: string;
+ @Input() categoryId?: string;
+
+ private service = inject(GuildGoodsService);
+
+ form = this.fb.group({
+ name: [this.initialValues?.name || '', [Validators.required]],
+ sku: [this.initialValues?.sku || '', [Validators.required]],
+ unit_type: [this.initialValues?.unit_type || '', [Validators.required]],
+ pricing_model: [this.initialValues?.pricing_model || '', [Validators.required]],
+ category_id: [this.initialValues?.category.id || '', [Validators.required]],
+ description: [this.initialValues?.description || ''],
+ });
+
+ get preparedTitle() {
+ return `${this.editMode ? 'ویرایش' : 'ایجاد'} کالا`;
+ }
+
+ override submitForm(payload: IConsumerComplexGoodRequest) {
+ if (this.editMode) {
+ return this.service.update(this.businessId, this.complexId, this.categoryId!, payload);
+ }
+ return this.service.create(this.businessId, this.complexId, payload);
+ }
+}
diff --git a/src/app/domains/consumer/modules/businessActivities/components/goods/list.component.html b/src/app/domains/consumer/modules/businessActivities/components/goods/list.component.html
new file mode 100644
index 0000000..505d1f7
--- /dev/null
+++ b/src/app/domains/consumer/modules/businessActivities/components/goods/list.component.html
@@ -0,0 +1,25 @@
+
+
diff --git a/src/app/domains/consumer/modules/businessActivities/components/goods/list.component.ts b/src/app/domains/consumer/modules/businessActivities/components/goods/list.component.ts
new file mode 100644
index 0000000..dc11ea0
--- /dev/null
+++ b/src/app/domains/consumer/modules/businessActivities/components/goods/list.component.ts
@@ -0,0 +1,42 @@
+// import { CatalogRoleTagComponent } from '@/shared/catalog/roles';
+import { AbstractList } from '@/shared/abstractClasses/abstract-list';
+import {
+ IColumn,
+ PageDataListComponent,
+} from '@/shared/components/pageDataList/page-data-list.component';
+import { Component, inject, Input } from '@angular/core';
+import { IConsumerComplexGoodResponse } from '../../models/goods_io';
+import { GuildGoodsService } from '../../services/goods.service';
+import { ConsumerComplexGoodFormComponent } from './form.component';
+
+@Component({
+ selector: 'consumer-complex-goods-list',
+ templateUrl: './list.component.html',
+ imports: [PageDataListComponent, ConsumerComplexGoodFormComponent],
+})
+export class ConsumerComplexGoodsListComponent extends AbstractList {
+ @Input({ required: true }) businessId!: string;
+ @Input({ required: true }) complexId!: string;
+ @Input({ required: true }) guildId!: string;
+ @Input() fullHeight?: boolean;
+ @Input() header: IColumn[] = [
+ { field: 'id', header: 'شناسه' },
+ { field: 'name', header: 'عنوان' },
+ { field: 'sku', header: 'شناسهی عمومی' },
+ { field: 'category', header: 'دستهبندی', type: 'nested', nestedPath: 'category.name' },
+ {
+ field: 'created_at',
+ header: 'تاریخ ایجاد',
+ type: 'date',
+ },
+ ];
+ service = inject(GuildGoodsService);
+
+ override setColumns(): void {
+ this.columns = this.header;
+ }
+
+ override getDataRequest() {
+ return this.service.getAll(this.businessId, this.complexId);
+ }
+}
diff --git a/src/app/domains/consumer/modules/businessActivities/constants/apiRoutes/complexGoods.ts b/src/app/domains/consumer/modules/businessActivities/constants/apiRoutes/complexGoods.ts
new file mode 100644
index 0000000..4ad6919
--- /dev/null
+++ b/src/app/domains/consumer/modules/businessActivities/constants/apiRoutes/complexGoods.ts
@@ -0,0 +1,8 @@
+const baseUrl = (businessId: string, complexId: string) =>
+ `/api/v1/consumer/business_activities/${businessId}/complexes/${complexId}/goods`;
+
+export const CONSUMER_COMPLEX_GOODS_API_ROUTES = {
+ list: (businessId: string, complexId: string) => `${baseUrl(businessId, complexId)}`,
+ single: (businessId: string, complexId: string, id: string) =>
+ `${baseUrl(businessId, complexId)}/${id}`,
+};
diff --git a/src/app/domains/consumer/modules/businessActivities/constants/routes/goods.ts b/src/app/domains/consumer/modules/businessActivities/constants/routes/goods.ts
new file mode 100644
index 0000000..ca881ad
--- /dev/null
+++ b/src/app/domains/consumer/modules/businessActivities/constants/routes/goods.ts
@@ -0,0 +1,48 @@
+import { NamedRoutes } from '@/core';
+import { Routes } from '@angular/router';
+
+export type TGoodsRouteNames = 'goods';
+
+const baseUrl = (businessId: string, complexId: string) =>
+ `/consumer/business_activities/${businessId}/complexes/${complexId}/goods`;
+
+export const consumerComplexGoodsNamedRoutes: NamedRoutes = {
+ goods: {
+ path: 'goods',
+ loadComponent: () =>
+ import('../../views/goods/list.component').then((m) => m.ConsumerComplexGoodsComponent),
+ // @ts-ignore
+ meta: {
+ title: 'کالاها',
+ pagePath: (businessId: string, complexId: string) => baseUrl(businessId, complexId),
+ },
+ },
+ // good: {
+ // path: 'goods/:goodId',
+ // loadComponent: () =>
+ // import('../../views/goods/single.component').then((m) => m.SuperAdminUserPosComponent),
+ // // @ts-ignore
+ // meta: {
+ // title: 'پایانهی فروش',
+ // pagePath: (businessId: string, complexId: string, goodId: string) =>
+ // `${baseUrl(businessId, complexId)}/${goodId}`,
+ // },
+ // },
+};
+
+export const CONSUMER_COMPLEX_GOODS_ROUTES: Routes = [
+ consumerComplexGoodsNamedRoutes.goods,
+ // {
+ // path: consumerComplexGoodsNamedRoutes.good.path,
+ // loadComponent: () =>
+ // import('../../components/goods/layout.component').then(
+ // (m) => m.SuperAdminConsumerPosLayoutComponent,
+ // ),
+ // children: [
+ // {
+ // path: '',
+ // loadComponent: consumerComplexGoodsNamedRoutes.good.loadComponent,
+ // },
+ // ],
+ // },
+];
diff --git a/src/app/domains/consumer/modules/businessActivities/models/goods_io.d.ts b/src/app/domains/consumer/modules/businessActivities/models/goods_io.d.ts
new file mode 100644
index 0000000..017bb49
--- /dev/null
+++ b/src/app/domains/consumer/modules/businessActivities/models/goods_io.d.ts
@@ -0,0 +1,23 @@
+import ISummary from '@/core/models';
+
+export interface IConsumerComplexGoodRawResponse {
+ id: string;
+ name: string;
+ sku: string;
+ category: ISummary;
+ unit_type: string;
+ pricing_model: string;
+ description?: string;
+ created_at: string;
+ updated_at: string;
+}
+export interface IConsumerComplexGoodResponse extends IConsumerComplexGoodRawResponse {}
+
+export interface IConsumerComplexGoodRequest {
+ name: string;
+ sku: string;
+ category_id: string;
+ unit_type: string;
+ pricing_model: string;
+ description?: string;
+}
diff --git a/src/app/domains/consumer/modules/businessActivities/services/goods.service.ts b/src/app/domains/consumer/modules/businessActivities/services/goods.service.ts
new file mode 100644
index 0000000..3c339ab
--- /dev/null
+++ b/src/app/domains/consumer/modules/businessActivities/services/goods.service.ts
@@ -0,0 +1,58 @@
+import { IPaginatedResponse } from '@/core/models/service.model';
+import { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs';
+import { CONSUMER_COMPLEX_GOODS_API_ROUTES } from '../constants/apiRoutes/complexGoods';
+import {
+ IConsumerComplexGoodRawResponse,
+ IConsumerComplexGoodRequest,
+ IConsumerComplexGoodResponse,
+} from '../models/goods_io';
+
+@Injectable({ providedIn: 'root' })
+export class GuildGoodsService {
+ constructor(private http: HttpClient) {}
+
+ private apiRoutes = CONSUMER_COMPLEX_GOODS_API_ROUTES;
+
+ getAll(
+ businessId: string,
+ complexId: string,
+ ): Observable> {
+ return this.http.get>(
+ this.apiRoutes.list(businessId, complexId),
+ );
+ }
+ getSingle(
+ businessId: string,
+ complexId: string,
+ id: string,
+ ): Observable {
+ return this.http.get(
+ this.apiRoutes.single(businessId, complexId, id),
+ );
+ }
+
+ create(
+ businessId: string,
+ complexId: string,
+ data: IConsumerComplexGoodRequest,
+ ): Observable {
+ return this.http.post(
+ this.apiRoutes.list(businessId, complexId),
+ data,
+ );
+ }
+
+ update(
+ businessId: string,
+ complexId: string,
+ id: string,
+ data: IConsumerComplexGoodRequest,
+ ): Observable {
+ return this.http.patch(
+ this.apiRoutes.single(businessId, complexId, id),
+ data,
+ );
+ }
+}
diff --git a/src/app/domains/consumer/modules/businessActivities/views/complexes/single.component.html b/src/app/domains/consumer/modules/businessActivities/views/complexes/single.component.html
index b7a361f..2a8f962 100644
--- a/src/app/domains/consumer/modules/businessActivities/views/complexes/single.component.html
+++ b/src/app/domains/consumer/modules/businessActivities/views/complexes/single.component.html
@@ -10,6 +10,7 @@
+
this.store.loading());
readonly complex = computed(() => this.store.entity());
+ readonly business = computed(() => this.businessStore.entity());
constructor() {
effect(() => {
diff --git a/src/app/domains/consumer/modules/businessActivities/views/goods/list.component.html b/src/app/domains/consumer/modules/businessActivities/views/goods/list.component.html
new file mode 100644
index 0000000..8c1d313
--- /dev/null
+++ b/src/app/domains/consumer/modules/businessActivities/views/goods/list.component.html
@@ -0,0 +1 @@
+
diff --git a/src/app/domains/consumer/modules/businessActivities/views/goods/list.component.ts b/src/app/domains/consumer/modules/businessActivities/views/goods/list.component.ts
new file mode 100644
index 0000000..1d8fcff
--- /dev/null
+++ b/src/app/domains/consumer/modules/businessActivities/views/goods/list.component.ts
@@ -0,0 +1,44 @@
+// import { CatalogRoleTagComponent } from '@/shared/catalog/roles';
+import { BreadcrumbService } from '@/core/services';
+import pageParamsUtils from '@/utils/page-params.utils';
+import { Component, computed, inject, signal } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { ConsumerComplexGoodsListComponent } from '../../components/goods/list.component';
+import { consumerComplexGoodsNamedRoutes } from '../../constants/routes/goods';
+import { BusinessActivityStore } from '../../store/businessActivity.store';
+import { ConsumerComplexStore } from '../../store/complex.store';
+
+@Component({
+ selector: 'consumer-complex-goods',
+ templateUrl: './list.component.html',
+ imports: [ConsumerComplexGoodsListComponent],
+})
+export class ConsumerComplexGoodsComponent {
+ private readonly route = inject(ActivatedRoute);
+ private readonly breadcrumbService = inject(BreadcrumbService);
+
+ private readonly businessStore = inject(BusinessActivityStore);
+ private readonly complexStore = inject(ConsumerComplexStore);
+
+ pageParams = computed(() => pageParamsUtils(this.route));
+ businessId = signal(this.pageParams()['businessActivityId']);
+ complexId = signal(this.pageParams()['complexId']);
+
+ ngOnInit() {
+ this.setBreadcrumb();
+ }
+
+ setBreadcrumb() {
+ this.breadcrumbService.setItems([
+ ...this.businessStore.breadcrumbItems(),
+ ...this.complexStore.breadcrumbItems(),
+ {
+ title: consumerComplexGoodsNamedRoutes.goods.meta.title,
+ routerLink: consumerComplexGoodsNamedRoutes.goods.meta.pagePath!(
+ this.businessId(),
+ this.complexId(),
+ ),
+ },
+ ]);
+ }
+}
diff --git a/src/app/shared/catalog/apiRoutes.ts b/src/app/shared/catalog/apiRoutes.ts
index 851b6b6..ccff4bc 100644
--- a/src/app/shared/catalog/apiRoutes.ts
+++ b/src/app/shared/catalog/apiRoutes.ts
@@ -2,6 +2,7 @@ const baseUrl = '/api/v1/catalog';
export const CATALOG_API_ROUTES = {
guilds: () => `${baseUrl}/guilds`,
+ guildGoodCategories: (guildId: string) => `${baseUrl}/guilds/${guildId}/good_categories`,
providers: () => `${baseUrl}/providers`,
devices: () => `${baseUrl}/devices`,
deviceBrands: () => `${baseUrl}/device_brands`,
diff --git a/src/app/shared/catalog/guildGoodCategories/components/select.component.html b/src/app/shared/catalog/guildGoodCategories/components/select.component.html
new file mode 100644
index 0000000..3a5ed61
--- /dev/null
+++ b/src/app/shared/catalog/guildGoodCategories/components/select.component.html
@@ -0,0 +1,13 @@
+
+
+
diff --git a/src/app/shared/catalog/guildGoodCategories/components/select.component.ts b/src/app/shared/catalog/guildGoodCategories/components/select.component.ts
new file mode 100644
index 0000000..b138386
--- /dev/null
+++ b/src/app/shared/catalog/guildGoodCategories/components/select.component.ts
@@ -0,0 +1,31 @@
+import { IListingResponse } from '@/core/models/service.model';
+import ISummary from '@/core/models/summary';
+import { AbstractSelectComponent } from '@/shared/abstractClasses';
+import { UikitFieldComponent } from '@/uikit';
+import { Component, inject, Input } from '@angular/core';
+import { ReactiveFormsModule } from '@angular/forms';
+import { Select } from 'primeng/select';
+import { CatalogsService } from '../../service';
+
+@Component({
+ selector: 'catalog-guild-goodCategories-select',
+ templateUrl: './select.component.html',
+ imports: [UikitFieldComponent, Select, ReactiveFormsModule],
+})
+export class CatalogGuildGoodCategoriesSelectComponent extends AbstractSelectComponent<
+ ISummary,
+ IListingResponse
+> {
+ @Input({ required: true }) guildId!: string;
+ @Input() override showClear: boolean = false;
+ @Input() label: string = '';
+ private readonly service = inject(CatalogsService);
+
+ ngOnInit() {
+ this.getData();
+ }
+
+ getDataService() {
+ return this.service.getGuildGoodCategories(this.guildId);
+ }
+}
diff --git a/src/app/shared/catalog/guildGoodCategories/index.ts b/src/app/shared/catalog/guildGoodCategories/index.ts
new file mode 100644
index 0000000..0ac8688
--- /dev/null
+++ b/src/app/shared/catalog/guildGoodCategories/index.ts
@@ -0,0 +1 @@
+export * from './components/select.component';
diff --git a/src/app/shared/catalog/service.ts b/src/app/shared/catalog/service.ts
index d187a2e..5e89c18 100644
--- a/src/app/shared/catalog/service.ts
+++ b/src/app/shared/catalog/service.ts
@@ -15,6 +15,10 @@ export class CatalogsService {
return this.http.get>(this.apiRoutes.guilds());
}
+ getGuildGoodCategories(guildId: string): Observable> {
+ return this.http.get>(this.apiRoutes.guildGoodCategories(guildId));
+ }
+
getProviders(): Observable> {
return this.http.get>(this.apiRoutes.providers());
}