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()); }