diff --git a/agents.md b/agents.md index be795b8..8e4e3a0 100644 --- a/agents.md +++ b/agents.md @@ -111,6 +111,37 @@ example: (value = '', isRequired = true): ControlConfig => [ - `` - For numeric/price behavior, use `app-input` `type="number"` or `type="price"` and optional `[fixed]`. +## List Component Configs + +- Centralized list metadata is stored in `src/app/shared/constants/list-configs/` — **never** in domain modules. +- Structure by data type, not domain (e.g. `good-list.const.ts`, `sku-list.const.ts`, `category-list.const.ts`). +- Each config implements `IListConfig` (defined in `list-config.model.ts`): + - `pageTitle`: display title for the list page + - `addNewCtaLabel`: call-to-action button label + - `emptyPlaceholderTitle` and `emptyPlaceholderDescription`: empty state messaging + - `columns`: array of `IColumn[]` definitions +- Usage in list components: + ```ts + @Input() header: IColumn[] = goodListConfig.columns; + listConfig = goodListConfig; + ``` +- Any domain needing a list config imports from `@/shared/constants/list-configs` — no cross-domain dependencies. +- **Do not** define configs inside domains or duplicate them across modules. + +## Breadcrumb Usage in Stores & Views + +- Entity stores expose `breadcrumbItems` as a computed signal. +- Call `store.breadcrumbItems()` in view components and extend with current page: + ```ts + setBreadcrumb() { + this.breadcrumbService.setItems([ + ...this.store.breadcrumbItems(), + { title: 'Current Page' }, + ]); + } + ``` +- Root page breadcrumbs are set in the store's `getData()` method once entity is loaded. + ## Validation Checklist - For TypeScript-only changes, run: diff --git a/src/app/core/services/native-bridge.service.ts b/src/app/core/services/native-bridge.service.ts index 45b3fa6..cddd8e9 100644 --- a/src/app/core/services/native-bridge.service.ts +++ b/src/app/core/services/native-bridge.service.ts @@ -109,7 +109,7 @@ export class NativeBridgeService { // } } - print(request: INativePrintRequest): INativeBridgeResult { + print(request: INativePrintRequest[]): INativeBridgeResult { return this.invokePrint(request); } @@ -126,7 +126,7 @@ export class NativeBridgeService { } } - private invokePrint(payload: INativePrintRequest): INativeBridgeResult { + private invokePrint(payload: INativePrintRequest[]): INativeBridgeResult { if (!this.isEnabled() || !this.canPrint()) { this.toastService.warn({ text: 'متاسفانه ارتباط با چاپگر برقرار نیست.' }); return { success: false, error: 'متاسفانه ارتباط با چاپگر برقرار نیست.' }; diff --git a/src/app/domains/consumer/components/invoices/shared-saleInvoice.component.ts b/src/app/domains/consumer/components/invoices/shared-saleInvoice.component.ts index 64b87de..ce8cc4d 100644 --- a/src/app/domains/consumer/components/invoices/shared-saleInvoice.component.ts +++ b/src/app/domains/consumer/components/invoices/shared-saleInvoice.component.ts @@ -68,17 +68,19 @@ export class ConsumerSaleInvoiceSharedComponent { printInvoice = () => { if (this.invoice) { - const printResult = this.nativeBridge.print({ - title: `فروشگاه ${this.posName()}`, - items: [ - { - label: 'شماره فاکتور', - value: this.invoice?.code, - }, - { label: 'تاریخ', value: formatJalali(this.invoice.invoice_date, 'YYYY/MM/DD') }, - { label: 'مبلغ کل', value: this.invoice.total_amount }, - ], - }); + const printResult = this.nativeBridge.print([ + { + title: `فروشگاه ${this.posName()}`, + items: [ + { + label: 'شماره فاکتور', + value: this.invoice?.code, + }, + { label: 'تاریخ', value: formatJalali(this.invoice.invoice_date, 'YYYY/MM/DD') }, + { label: 'مبلغ کل', value: this.invoice.total_amount }, + ], + }, + ]); } }; diff --git a/src/app/domains/consumer/modules/accounts/views/list.component.ts b/src/app/domains/consumer/modules/accounts/views/list.component.ts index afd4804..3716f54 100644 --- a/src/app/domains/consumer/modules/accounts/views/list.component.ts +++ b/src/app/domains/consumer/modules/accounts/views/list.component.ts @@ -5,6 +5,7 @@ import { IChangePasswordSubmitPayload, } from '@/shared/components'; import { PageDataListComponent } from '@/shared/components/pageDataList/page-data-list.component'; +import { accountListConfig } from '@/shared/constants/list-configs'; import { Component, inject, signal } from '@angular/core'; import { Router } from '@angular/router'; import { finalize } from 'rxjs'; @@ -23,27 +24,7 @@ export class ConsumerAccountsComponent extends AbstractList { passwordSubmitLoading = signal(false); override setColumns(): void { - this.columns = [ - // { field: 'id', header: 'شناسه', type: 'id' }, - { - field: 'username', - header: 'نام کاربری', - type: 'nested', - nestedOption: { path: 'account.username' }, - }, - { - field: 'pos', - header: 'پایانه', - customDataModel(item) { - return `${item.pos.name} - ${item.pos.complex.name} - ${item.pos.complex.business_activity.name}`; - }, - }, - { - field: 'created_at', - header: 'تاریخ ایجاد', - type: 'date', - }, - ]; + this.columns = accountListConfig.columns; } override getDataRequest() { diff --git a/src/app/domains/consumer/modules/businessActivities/components/complexes/list.component.ts b/src/app/domains/consumer/modules/businessActivities/components/complexes/list.component.ts index c03d363..c9b9a04 100644 --- a/src/app/domains/consumer/modules/businessActivities/components/complexes/list.component.ts +++ b/src/app/domains/consumer/modules/businessActivities/components/complexes/list.component.ts @@ -4,6 +4,7 @@ import { IColumn, PageDataListComponent, } from '@/shared/components/pageDataList/page-data-list.component'; +import { complexListConfig } from '@/shared/constants/list-configs'; import { Component, inject, Input } from '@angular/core'; import { Router } from '@angular/router'; import { consumerComplexesNamedRoutes } from '../../constants/routes/complexes'; @@ -19,16 +20,7 @@ import { ConsumerComplexFormComponent } from './form.component'; export class ConsumerComplexListComponent extends AbstractList { @Input({ required: true }) businessId!: string; @Input() fullHeight?: boolean; - @Input() header: IColumn[] = [ - // { field: 'id', header: 'شناسه', type: 'id' }, - { field: 'name', header: 'عنوان' }, - { field: 'branch_code', header: 'کد شعبه' }, - { - field: 'created_at', - header: 'تاریخ ایجاد', - type: 'date', - }, - ]; + @Input() header: IColumn[] = complexListConfig.columns; private readonly service = inject(ConsumerComplexesService); private readonly router = inject(Router); 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 index 327b81d..a61552d 100644 --- a/src/app/domains/consumer/modules/businessActivities/components/goods/list.component.ts +++ b/src/app/domains/consumer/modules/businessActivities/components/goods/list.component.ts @@ -4,6 +4,7 @@ import { IColumn, PageDataListComponent, } from '@/shared/components/pageDataList/page-data-list.component'; +import { goodListConfig } from '@/shared/constants/list-configs'; import { Component, inject, Input } from '@angular/core'; import { IConsumerBusinessActivityGoodResponse } from '../../models/goods_io'; import { BusinessActivityGoodsService } from '../../services/goods.service'; @@ -18,22 +19,9 @@ export class ConsumerBusinessActivityGoodsListComponent extends AbstractList { @Input() fullHeight?: boolean; - @Input() header: IColumn[] = [ - { field: 'name', header: 'عنوان' }, - { field: 'guild', header: 'صنف', type: 'nested', nestedOption: { path: 'guild.name' } }, - { field: 'economic_code', header: 'کد اقتصادی' }, - { - field: 'license_info', - header: 'محدودیت کاربر', - customDataModel(item) { - const licenseInfo = (item as IBusinessActivityResponse).license_info; - if (!licenseInfo) { - return 'بدون مجوز'; - } - return `${licenseInfo.accounts_limit} کاربر`; - }, - }, - { - field: 'license_info', - header: 'انقضا مجوز', - type: 'nested', - nestedOption: { path: 'license_info.expires_at', type: 'date' }, - }, - { - field: 'created_at', - header: 'تاریخ ایجاد', - type: 'date', - }, - ]; + @Input() header: IColumn[] = businessActivityListConfig.columns; private readonly service = inject(BusinessActivitiesService); private readonly router = inject(Router); diff --git a/src/app/domains/consumer/modules/customers/components/list.component.ts b/src/app/domains/consumer/modules/customers/components/list.component.ts index 1e44862..35f179d 100644 --- a/src/app/domains/consumer/modules/customers/components/list.component.ts +++ b/src/app/domains/consumer/modules/customers/components/list.component.ts @@ -4,6 +4,8 @@ import { IColumn, PageDataListComponent, } from '@/shared/components/pageDataList/page-data-list.component'; + +import { customerListConfig } from '@/shared/constants/list-configs/customer-list.const'; import { Component, inject, Input } from '@angular/core'; import { Router } from '@angular/router'; import { ConsumerBusinessActivityFormComponent } from '../components/form-dialog.component'; @@ -18,42 +20,7 @@ import { CustomersService } from '../services/main.service'; }) export class ConsumerCustomerListComponent extends AbstractList { @Input() fullHeight?: boolean; - @Input() header: IColumn[] = [ - // { field: 'id', header: 'شناسه', type: 'id' }, - { - field: 'type', - header: 'نوع مشتری', - customDataModel(item) { - return item.type === 'LEGAL' ? 'حقوقی' : 'حقیقی'; - }, - }, - - { - field: 'name', - header: 'عنوان', - customDataModel(item: ICustomerResponse) { - if (item.type === 'LEGAL') { - return item.legal!.company_name; - } - return `${item.individual!.first_name} ${item.individual!.last_name}`; - }, - }, - { - field: 'economic_code', - header: 'کد اقتصادی', - customDataModel(item: ICustomerResponse) { - if (item.type === 'LEGAL') { - return item.legal!.economic_code; - } - return item.individual!.economic_code; - }, - }, - { - field: 'created_at', - header: 'تاریخ ایجاد', - type: 'date', - }, - ]; + @Input() header: IColumn[] = customerListConfig.columns; private readonly service = inject(CustomersService); private readonly router = inject(Router); diff --git a/src/app/domains/consumer/modules/customers/views/saleInvoices/single.component.html b/src/app/domains/consumer/modules/customers/views/saleInvoices/single.component.html index f8a671a..4ff73ad 100644 --- a/src/app/domains/consumer/modules/customers/views/saleInvoices/single.component.html +++ b/src/app/domains/consumer/modules/customers/views/saleInvoices/single.component.html @@ -1 +1 @@ - + diff --git a/src/app/domains/consumer/modules/customers/views/saleInvoices/single.component.ts b/src/app/domains/consumer/modules/customers/views/saleInvoices/single.component.ts index ffd0be2..f728a61 100644 --- a/src/app/domains/consumer/modules/customers/views/saleInvoices/single.component.ts +++ b/src/app/domains/consumer/modules/customers/views/saleInvoices/single.component.ts @@ -1,5 +1,5 @@ import { BreadcrumbService } from '@/core/services'; -import { ConsumerSaleInvoiceSharedComponent } from '@/domains/consumer/components/invoices/shared-saleInvoice.component'; +import { SharedSaleInvoiceSingleViewComponent } from '@/shared/components/invoices/sale-invoice-single-view.component'; import pageParamsUtils from '@/utils/page-params.utils'; import { Component, computed, effect, inject, signal } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @@ -9,7 +9,7 @@ import { ConsumerCustomerSaleInvoiceStore } from '../../store/saleInvoice.store' @Component({ selector: 'consumer-customer-saleInvoice', templateUrl: './single.component.html', - imports: [ConsumerSaleInvoiceSharedComponent], + imports: [SharedSaleInvoiceSingleViewComponent], }) export class ConsumerCustomerSaleInvoiceComponent { private readonly route = inject(ActivatedRoute); diff --git a/src/app/domains/consumer/modules/poses/components/list.component.ts b/src/app/domains/consumer/modules/poses/components/list.component.ts index 3817eac..a958342 100644 --- a/src/app/domains/consumer/modules/poses/components/list.component.ts +++ b/src/app/domains/consumer/modules/poses/components/list.component.ts @@ -1,10 +1,10 @@ // import { CatalogRoleTagComponent } from '@/shared/catalog/roles'; -import { CONSUMER_COMPONENTS_CONST } from '@/domains/consumer/constants'; import { AbstractList } from '@/shared/abstractClasses/abstract-list'; import { IColumn, PageDataListComponent, } from '@/shared/components/pageDataList/page-data-list.component'; +import { posListConfig } from '@/shared/constants/list-configs'; import { Component, inject, Input, TemplateRef, ViewChild } from '@angular/core'; import { Router } from '@angular/router'; import { CookieService } from 'ngx-cookie-service'; @@ -22,7 +22,7 @@ import { ConsumerPosFormComponent } from './form.component'; }) export class ConsumerPosListComponent extends AbstractList { @Input() fullHeight?: boolean; - @Input() header: IColumn[] = CONSUMER_COMPONENTS_CONST.pos.columns; + @Input() header: IColumn[] = posListConfig.columns; private readonly service = inject(ConsumerPosesService); private readonly cookieService = inject(CookieService); diff --git a/src/app/domains/consumer/modules/saleInvoices/components/list.component.ts b/src/app/domains/consumer/modules/saleInvoices/components/list.component.ts index 4bbc099..07f6fc8 100644 --- a/src/app/domains/consumer/modules/saleInvoices/components/list.component.ts +++ b/src/app/domains/consumer/modules/saleInvoices/components/list.component.ts @@ -4,6 +4,7 @@ import { IColumn, PageDataListComponent, } from '@/shared/components/pageDataList/page-data-list.component'; +import { saleInvoiceListConfig } from '@/shared/constants/list-configs'; import { Component, inject, Input } from '@angular/core'; import { Router } from '@angular/router'; import { consumerSaleInvoicesNamedRoutes } from '../constants/routes'; @@ -17,37 +18,7 @@ import { ConsumerSaleInvoicesService } from '../services/main.service'; }) export class ConsumerSaleInvoiceListComponent extends AbstractList { @Input() fullHeight?: boolean; - @Input() header: IColumn[] = [ - { field: 'code', header: 'کد رهگیری' }, - { - field: 'total_amount', - header: 'قیمت نهایی', - type: 'price', - }, - { - field: 'pos', - header: 'پایانه', - customDataModel(item: IConsumerSaleInvoicesResponse) { - return `${item.pos.name} (${item.pos.complex.name} - ${item.pos.complex.business_activity.name})`; - }, - }, - { - field: 'consumer_account', - header: 'ایجاد شده توسط', - type: 'nested', - nestedOption: { path: 'consumer_account.account.username' }, - }, - { - field: 'invoice_date', - header: 'تاریخ فاکتور', - type: 'date', - }, - { - field: 'created_at', - header: 'تاریخ ایجاد', - type: 'date', - }, - ]; + @Input() header: IColumn[] = saleInvoiceListConfig.columns; private readonly service = inject(ConsumerSaleInvoicesService); private readonly router = inject(Router); diff --git a/src/app/domains/consumer/modules/saleInvoices/views/single.component.html b/src/app/domains/consumer/modules/saleInvoices/views/single.component.html index 66c3cb3..d6c951d 100644 --- a/src/app/domains/consumer/modules/saleInvoices/views/single.component.html +++ b/src/app/domains/consumer/modules/saleInvoices/views/single.component.html @@ -1 +1 @@ - + diff --git a/src/app/domains/consumer/modules/saleInvoices/views/single.component.ts b/src/app/domains/consumer/modules/saleInvoices/views/single.component.ts index d563c3e..92d60d1 100644 --- a/src/app/domains/consumer/modules/saleInvoices/views/single.component.ts +++ b/src/app/domains/consumer/modules/saleInvoices/views/single.component.ts @@ -1,5 +1,5 @@ import { BreadcrumbService } from '@/core/services'; -import { ConsumerSaleInvoiceSharedComponent } from '@/domains/consumer/components/invoices/shared-saleInvoice.component'; +import { SharedSaleInvoiceSingleViewComponent } from '@/shared/components/invoices/sale-invoice-single-view.component'; import pageParamsUtils from '@/utils/page-params.utils'; import { Component, computed, effect, inject, signal } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @@ -8,7 +8,7 @@ import { ConsumerSaleInvoiceStore } from '../store/main.store'; @Component({ selector: 'consumer-saleInvoice', templateUrl: './single.component.html', - imports: [ConsumerSaleInvoiceSharedComponent], + imports: [SharedSaleInvoiceSingleViewComponent], }) export class ConsumerSaleInvoiceComponent { private readonly route = inject(ActivatedRoute); diff --git a/src/app/domains/partner/modules/accounts/views/list.component.ts b/src/app/domains/partner/modules/accounts/views/list.component.ts index 889d59d..5950128 100644 --- a/src/app/domains/partner/modules/accounts/views/list.component.ts +++ b/src/app/domains/partner/modules/accounts/views/list.component.ts @@ -5,6 +5,7 @@ import { IChangePasswordSubmitPayload, } from '@/shared/components'; import { PageDataListComponent } from '@/shared/components/pageDataList/page-data-list.component'; +import { partnerAccountListConfig } from '@/shared/constants/list-configs'; import { Component, inject, signal } from '@angular/core'; import { Router } from '@angular/router'; import { finalize } from 'rxjs'; @@ -23,27 +24,7 @@ export class PartnerAccountsComponent extends AbstractList { passwordSubmitLoading = signal(false); override setColumns(): void { - this.columns = [ - // { field: 'id', header: 'شناسه', type: 'id' }, - { - field: 'username', - header: 'نام کاربری', - type: 'nested', - nestedOption: { path: 'account.username' }, - }, - { field: 'role', header: 'نقش' }, - { - field: 'status', - header: 'وضعیت', - type: 'nested', - nestedOption: { path: 'account.status' }, - }, - { - field: 'created_at', - header: 'تاریخ ایجاد', - type: 'date', - }, - ]; + this.columns = partnerAccountListConfig.columns; } override getDataRequest() { diff --git a/src/app/domains/partner/modules/consumers/components/accounts/list.component.ts b/src/app/domains/partner/modules/consumers/components/accounts/list.component.ts index f7c05da..1e030c8 100644 --- a/src/app/domains/partner/modules/consumers/components/accounts/list.component.ts +++ b/src/app/domains/partner/modules/consumers/components/accounts/list.component.ts @@ -4,6 +4,7 @@ import { IColumn, PageDataListComponent, } from '@/shared/components/pageDataList/page-data-list.component'; +import { partnerConsumerAccountListConfig } from '@/shared/constants/list-configs'; import { Component, inject, Input } from '@angular/core'; import { IConsumerAccountResponse } from '../../models'; import { PartnerConsumerAccountsService } from '../../services/accounts.service'; @@ -17,37 +18,7 @@ import { ConsumerAccountFormComponent } from './form.component'; export class ConsumerAccountListComponent extends AbstractList { @Input({ required: true }) consumerId!: string; @Input() fullHeight?: boolean; - @Input() header: IColumn[] = [ - { - field: 'account', - header: 'نام کاربری', - type: 'nested', - nestedOption: { path: 'account.username' }, - }, - { field: 'role', header: 'نوع حساب', type: 'nested', nestedOption: { path: 'role.translate' } }, - { - field: 'pos', - header: 'پایانه‌', - customDataModel(item: IConsumerAccountResponse) { - if (item.pos) { - return `${item.pos.name} (${item.pos.complex.name} - ${item.pos.complex.business_activity.name})`; - } - return '-'; - }, - }, - { - field: 'status', - header: 'وضعیت', - type: 'nested', - nestedOption: { path: 'account.status.translate' }, - variant: 'tag', - }, - // { - // field: 'created_at', - // header: 'تاریخ ایجاد', - // type: 'date', - // }, - ]; + @Input() header: IColumn[] = partnerConsumerAccountListConfig.columns; private readonly service = inject(PartnerConsumerAccountsService); override setColumns(): void { diff --git a/src/app/domains/partner/modules/consumers/components/businessActivities/list.component.ts b/src/app/domains/partner/modules/consumers/components/businessActivities/list.component.ts index bdad311..ba58898 100644 --- a/src/app/domains/partner/modules/consumers/components/businessActivities/list.component.ts +++ b/src/app/domains/partner/modules/consumers/components/businessActivities/list.component.ts @@ -4,6 +4,7 @@ import { IColumn, PageDataListComponent, } from '@/shared/components/pageDataList/page-data-list.component'; +import { businessActivityListConfig } from '@/shared/constants/list-configs'; import { Component, inject, Input, signal } from '@angular/core'; import { Router } from '@angular/router'; import { partnerConsumerBusinessActivitiesNamedRoutes } from '../../constants/routes/businessActivities'; @@ -24,28 +25,7 @@ import { ConsumerBusinessActivitiesFormDialogComponent } from './form-dialog.com export class ConsumerBusinessActivitiesComponent extends AbstractList { @Input({ required: true }) consumerId!: string; @Input() fullHeight?: boolean; - @Input() header: IColumn[] = [ - { field: 'name', header: 'عنوان' }, - { field: 'guild', header: 'صنف', type: 'nested', nestedOption: { path: 'guild.name' } }, - { field: 'economic_code', header: 'کد اقتصادی' }, - { - field: 'license_info', - header: 'محدودیت کاربر', - customDataModel(item) { - const licenseInfo = (item as IBusinessActivityResponse).license_info; - if (!licenseInfo) { - return 'بدون مجوز'; - } - return `${licenseInfo.accounts_limit} کاربر`; - }, - }, - { - field: 'license_info', - header: 'انقضا مجوز', - type: 'nested', - nestedOption: { path: 'license_info.expires_at', type: 'date' }, - }, - ]; + @Input() header: IColumn[] = businessActivityListConfig.columns; private readonly service = inject(PartnerConsumerBusinessActivitiesService); private readonly router = inject(Router); diff --git a/src/app/domains/partner/modules/consumers/components/complexes/list.component.ts b/src/app/domains/partner/modules/consumers/components/complexes/list.component.ts index dcf3075..f6f6582 100644 --- a/src/app/domains/partner/modules/consumers/components/complexes/list.component.ts +++ b/src/app/domains/partner/modules/consumers/components/complexes/list.component.ts @@ -4,6 +4,7 @@ import { IColumn, PageDataListComponent, } from '@/shared/components/pageDataList/page-data-list.component'; +import { complexListConfig } from '@/shared/constants/list-configs'; import { Component, inject, Input, signal } from '@angular/core'; import { Router } from '@angular/router'; import { partnerConsumerComplexesNamedRoutes } from '../../constants/routes/complexes'; @@ -25,17 +26,7 @@ export class ConsumerComplexesComponent extends AbstractList { @Input() consumerId!: string; @Input() businessId!: string; @Input() fullHeight?: boolean; - @Input() header: IColumn[] = [ - // { field: 'id', header: 'شناسه', type: 'id' }, - { field: 'name', header: 'عنوان' }, - { field: 'branch_code', header: 'کد شعبه' }, - { field: 'pos_count', header: 'تعداد پایانه‌ها' }, - { - field: 'created_at', - header: 'تاریخ ایجاد', - type: 'date', - }, - ]; + @Input() header: IColumn[] = complexListConfig.columns; private readonly service = inject(PartnerComplexesService); private readonly router = inject(Router); diff --git a/src/app/domains/partner/modules/consumers/components/poses/list.component.ts b/src/app/domains/partner/modules/consumers/components/poses/list.component.ts index d98d037..30900b4 100644 --- a/src/app/domains/partner/modules/consumers/components/poses/list.component.ts +++ b/src/app/domains/partner/modules/consumers/components/poses/list.component.ts @@ -4,6 +4,7 @@ import { IColumn, PageDataListComponent, } from '@/shared/components/pageDataList/page-data-list.component'; +import { posListConfig } from '@/shared/constants/list-configs'; import { Component, inject, Input } from '@angular/core'; import { Router } from '@angular/router'; import { partnerConsumerPosesNamedRoutes } from '../../constants/routes/poses'; @@ -21,31 +22,7 @@ export class ConsumerPosesComponent extends AbstractList { @Input({ required: true }) businessId!: string; @Input({ required: true }) complexId!: string; @Input() fullHeight?: boolean; - @Input() header: IColumn[] = [ - // { field: 'id', header: 'شناسه', type: 'id' }, - { field: 'name', header: 'عنوان' }, - // { field: 'serial_number', header: 'شماره سریال' }, - // { field: 'device', header: 'دستگاه', type: 'nested', nestedOption: { path: 'device.name' } }, - { field: 'pos_type', header: 'نوع پایانه' }, - { - field: 'account', - header: 'کاربر', - type: 'nested', - nestedOption: { path: 'account.username' }, - }, - // { - // field: 'provider', - // header: 'ارایه‌دهنده', - // type: 'nested', - // nestedOption: { path: 'provider.name' }, - // }, - // { field: 'status', header: 'وضعیت' }, - // { - // field: 'created_at', - // header: 'تاریخ ایجاد', - // type: 'date', - // }, - ]; + @Input() header: IColumn[] = posListConfig.columns; private readonly service = inject(PartnerPosesService); private readonly router = inject(Router); diff --git a/src/app/domains/partner/modules/consumers/views/list.component.ts b/src/app/domains/partner/modules/consumers/views/list.component.ts index f1ab23f..3fed2d9 100644 --- a/src/app/domains/partner/modules/consumers/views/list.component.ts +++ b/src/app/domains/partner/modules/consumers/views/list.component.ts @@ -1,6 +1,7 @@ // import { CatalogRoleTagComponent } from '@/shared/catalog/roles'; import { AbstractList } from '@/shared/abstractClasses/abstract-list'; import { PageDataListComponent } from '@/shared/components/pageDataList/page-data-list.component'; +import { consumerListConfig } from '@/shared/constants/list-configs'; import { Component, inject, signal } from '@angular/core'; import { Router } from '@angular/router'; import { CreateConsumerWrapperComponent } from '../components/createConsumerWrapper/create-consumer-wrapper.component'; @@ -20,17 +21,7 @@ export class ConsumersComponent extends AbstractList { visibleCreateForm = signal(false); override setColumns(): void { - this.columns = [ - { field: 'name', header: 'نام' }, - { field: 'type', header: 'نوع', type: 'nested', nestedOption: { path: 'type.translate' } }, - { field: 'business_counts', header: 'تعداد فعالیت‌ اقتصادی فعال' }, - - { - field: 'created_at', - header: 'تاریخ ایجاد', - type: 'date', - }, - ]; + this.columns = consumerListConfig.columns; } override getDataRequest() { diff --git a/src/app/domains/partner/modules/customers/components/list.component.ts b/src/app/domains/partner/modules/customers/components/list.component.ts index 64da073..b5f7450 100644 --- a/src/app/domains/partner/modules/customers/components/list.component.ts +++ b/src/app/domains/partner/modules/customers/components/list.component.ts @@ -4,6 +4,7 @@ import { IColumn, PageDataListComponent, } from '@/shared/components/pageDataList/page-data-list.component'; +import { partnerCustomerListConfig } from '@/shared/constants/list-configs'; import { Component, inject, Input } from '@angular/core'; import { Router } from '@angular/router'; import { partnerCustomersNamedRoutes } from '../constants'; @@ -17,19 +18,7 @@ import { CustomersService } from '../services/main.service'; }) export class PartnerCustomerListComponent extends AbstractList { @Input() fullHeight?: boolean; - @Input() header: IColumn[] = [ - // { field: 'id', header: 'شناسه', type: 'id' }, - - { - field: 'first_name', - header: 'عنوان', - }, - { - field: 'created_at', - header: 'تاریخ ایجاد', - type: 'date', - }, - ]; + @Input() header: IColumn[] = partnerCustomerListConfig.columns; private readonly service = inject(CustomersService); private readonly router = inject(Router); diff --git a/src/app/domains/pos/layouts/mainMenuSidebar/main-menu-sidebar.component.html b/src/app/domains/pos/layouts/mainMenuSidebar/main-menu-sidebar.component.html index 67e0132..9ec4abd 100644 --- a/src/app/domains/pos/layouts/mainMenuSidebar/main-menu-sidebar.component.html +++ b/src/app/domains/pos/layouts/mainMenuSidebar/main-menu-sidebar.component.html @@ -16,50 +16,19 @@
diff --git a/src/app/domains/pos/layouts/mainMenuSidebar/main-menu-sidebar.component.ts b/src/app/domains/pos/layouts/mainMenuSidebar/main-menu-sidebar.component.ts index 1a0d762..c4e94f1 100644 --- a/src/app/domains/pos/layouts/mainMenuSidebar/main-menu-sidebar.component.ts +++ b/src/app/domains/pos/layouts/mainMenuSidebar/main-menu-sidebar.component.ts @@ -7,6 +7,11 @@ import { Button } from 'primeng/button'; import { Drawer } from 'primeng/drawer'; import { Ripple } from 'primeng/ripple'; import { filter } from 'rxjs'; +import config from 'src/config'; +import { posAboutNamedRoutes } from '../../modules/about/constants'; +import { posConfigNamedRoutes } from '../../modules/configs/constants'; +import { posSaleInvoicesNamedRoutes } from '../../modules/saleInvoices/constants'; +import { posSupportNamedRoutes } from '../../modules/support/constants'; import { PosInfoStore } from '../../store'; @Component({ @@ -31,9 +36,36 @@ export class PosMainMenuSidebarComponent extends AbstractDialog { return ''; }); + readonly menuItems = [ + { + label: 'فروش', + routerLink: config.isPosApplication ? '/' : '/pos', + icon: 'pi pi-fw pi-home', + }, + { + label: 'فاکتورها', + routerLink: posSaleInvoicesNamedRoutes.saleInvoices.meta.pagePath!(), + icon: 'pi pi-fw pi-receipt', + }, + { + label: 'تنظیمات', + routerLink: posConfigNamedRoutes.config.meta.pagePath!(), + icon: 'pi pi-fw pi-cog', + }, + { + label: 'درباره ما', + routerLink: posAboutNamedRoutes.about.meta.pagePath!(), + icon: 'pi pi-fw pi-info-circle', + }, + { + label: 'پشتیبانی', + routerLink: posSupportNamedRoutes.support.meta.pagePath!(), + icon: 'pi pi-fw pi-phone', + }, + ]; + ngOnInit() { this.isPwaBuild = this.swUpdate.isEnabled; - console.log(this.swUpdate); if (!this.isPwaBuild) return; @@ -42,7 +74,6 @@ export class PosMainMenuSidebarComponent extends AbstractDialog { .then((data: { appData?: { appVersion?: string } }) => { this.appVersion = data?.appData?.appVersion || this.appVersion; this.toastService.info({ text: this.appVersion }); - console.log('appVersion:', data?.appData); }) .catch((err) => { console.log('err', err); diff --git a/src/app/domains/pos/models/good.io.ts b/src/app/domains/pos/models/good.io.ts index b600838..bf0a955 100644 --- a/src/app/domains/pos/models/good.io.ts +++ b/src/app/domains/pos/models/good.io.ts @@ -4,7 +4,11 @@ import { UnitType } from '@/utils'; export interface IGoodRawResponse { id: string; name: string; - sku: string; + sku: { + id: string; + code: string; + name: string; + }; category: ISummary; unit_type: UnitType; pricing_model: string; diff --git a/src/app/domains/pos/modules/configs/components/print/form.component.html b/src/app/domains/pos/modules/configs/components/print/form.component.html new file mode 100644 index 0000000..fc7ddd9 --- /dev/null +++ b/src/app/domains/pos/modules/configs/components/print/form.component.html @@ -0,0 +1,30 @@ +
+ + + + + + + + + + + + + + + + + diff --git a/src/app/domains/pos/modules/configs/components/print/form.component.ts b/src/app/domains/pos/modules/configs/components/print/form.component.ts new file mode 100644 index 0000000..c9469dc --- /dev/null +++ b/src/app/domains/pos/modules/configs/components/print/form.component.ts @@ -0,0 +1,47 @@ +import { AbstractForm } from '@/shared/abstractClasses'; +import { AppCheckboxComponent } from '@/shared/components/checkbox/checkbox.component'; +import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; +import { Component, inject } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; +import { IPosConfigPrintRequestPayload, IPosConfigPrintResponse } from './models'; +import { PosConfigPrintService } from './services/main.service'; + +@Component({ + selector: 'pos-config-print-form', + templateUrl: 'form.component.html', + imports: [ReactiveFormsModule, AppCheckboxComponent, FormFooterActionsComponent], +}) +export class PosConfigPrintFormComponent extends AbstractForm< + IPosConfigPrintRequestPayload, + IPosConfigPrintResponse +> { + private readonly service = inject(PosConfigPrintService); + + initForm = () => { + const form = this.fb.group({ + business_name: [true, []], + complex_name: [false], + pos_name: [false], + invoice_template: [false], + fiscal_id: [false], + economic_code: [false], + customer_name: [false], + customer_mobile: [false], + customer_national_id: [false], + customer_postal_code: [false], + customer_economic_code: [false], + payment_type: [false], + show_payment_info: [false], + show_items: [false], + }); + + return form; + }; + + form = this.initForm(); + + submitForm() { + const formValue = this.form.value as IPosConfigPrintRequestPayload; + return this.service.submit(formValue); + } +} diff --git a/src/app/domains/pos/modules/configs/components/print/models/index.ts b/src/app/domains/pos/modules/configs/components/print/models/index.ts new file mode 100644 index 0000000..0d5edfd --- /dev/null +++ b/src/app/domains/pos/modules/configs/components/print/models/index.ts @@ -0,0 +1,31 @@ +export interface IPosConfigPrintResponse extends Partial {} + +export interface IPosConfigPrintRequestPayload { + business_name: boolean; + complex_name: boolean; + pos_name: boolean; + invoice_template: boolean; + fiscal_id: boolean; + economic_code: boolean; + customer_name: boolean; + customer_mobile: boolean; + customer_national_id: boolean; + customer_postal_code: boolean; + customer_economic_code: boolean; + payment_type: boolean; + show_payment_info: boolean; + show_items: boolean; + items: { + name: boolean; + sku: boolean; + quantity: boolean; + base_amount: boolean; + tax: boolean; + discount: boolean; + karat: boolean; + profit: boolean; + commission: boolean; + wage: boolean; + total_amount: boolean; + }; +} diff --git a/src/app/domains/pos/modules/configs/components/print/services/main.service.ts b/src/app/domains/pos/modules/configs/components/print/services/main.service.ts new file mode 100644 index 0000000..f8f1e49 --- /dev/null +++ b/src/app/domains/pos/modules/configs/components/print/services/main.service.ts @@ -0,0 +1,48 @@ +import { Injectable } from '@angular/core'; +import { LOCAL_STORAGE_KEYS } from 'src/assets/constants'; +import { IPosConfigPrintRequestPayload, IPosConfigPrintResponse } from '../models'; + +@Injectable({ providedIn: 'root' }) +export class PosConfigPrintService { + get(): IPosConfigPrintResponse { + const data = window.localStorage.getItem(LOCAL_STORAGE_KEYS.POS_CONFIG_PRINT); + if (data) { + return JSON.parse(data) as IPosConfigPrintResponse; + } + const defaultData = { + business_name: true, + complex_name: true, + pos_name: true, + invoice_template: true, + fiscal_id: true, + economic_code: true, + customer_name: true, + customer_mobile: true, + customer_national_id: true, + customer_postal_code: true, + customer_economic_code: true, + payment_type: true, + show_payment_info: true, + show_items: false, + items: { + name: false, + sku: false, + quantity: false, + base_amount: false, + tax: false, + discount: false, + karat: false, + profit: false, + commission: false, + wage: false, + total_amount: false, + }, + }; + this.submit(defaultData); + return defaultData; + } + + submit(data: IPosConfigPrintRequestPayload) { + window.localStorage.setItem(LOCAL_STORAGE_KEYS.POS_CONFIG_PRINT, JSON.stringify(data)); + } +} diff --git a/src/app/domains/pos/modules/configs/constants/index.ts b/src/app/domains/pos/modules/configs/constants/index.ts new file mode 100644 index 0000000..442ce07 --- /dev/null +++ b/src/app/domains/pos/modules/configs/constants/index.ts @@ -0,0 +1 @@ +export * from './routes/index'; diff --git a/src/app/domains/pos/modules/configs/constants/routes/index.ts b/src/app/domains/pos/modules/configs/constants/routes/index.ts new file mode 100644 index 0000000..07f6fbf --- /dev/null +++ b/src/app/domains/pos/modules/configs/constants/routes/index.ts @@ -0,0 +1,19 @@ +import { NamedRoutes } from '@/core'; +import { Routes } from '@angular/router'; +import config from 'src/config'; + +export type TPosConfigRouteNames = 'config'; +const baseRoute = `${config.isPosApplication ? '' : '/pos'}/config`; + +export const posConfigNamedRoutes: NamedRoutes = { + config: { + path: 'config', + loadComponent: () => import('../../views/root.component').then((m) => m.PosConfigPageComponent), + meta: { + title: 'تنظیمات', + pagePath: () => baseRoute, + }, + }, +}; + +export const POS_CONFIG_ROUTES: Routes = [posConfigNamedRoutes.config]; diff --git a/src/app/domains/pos/modules/configs/views/index.ts b/src/app/domains/pos/modules/configs/views/index.ts new file mode 100644 index 0000000..8ca0c48 --- /dev/null +++ b/src/app/domains/pos/modules/configs/views/index.ts @@ -0,0 +1 @@ +export * from './root.component'; diff --git a/src/app/domains/pos/modules/configs/views/root.component.html b/src/app/domains/pos/modules/configs/views/root.component.html new file mode 100644 index 0000000..764a239 --- /dev/null +++ b/src/app/domains/pos/modules/configs/views/root.component.html @@ -0,0 +1,8 @@ +
+ + + هریک از موارد زیر را که می‌خواهید در چاپ فاکتور نمایش داده‌ شوند را انتخاب کنید + + + +
diff --git a/src/app/domains/pos/modules/configs/views/root.component.ts b/src/app/domains/pos/modules/configs/views/root.component.ts new file mode 100644 index 0000000..7b29dc8 --- /dev/null +++ b/src/app/domains/pos/modules/configs/views/root.component.ts @@ -0,0 +1,17 @@ +import { AppCardComponent } from '@/shared/components'; +import { Component, inject } from '@angular/core'; +import { Router } from '@angular/router'; +import { Message } from 'primeng/message'; +import { PosConfigPrintFormComponent } from '../components/print/form.component'; + +@Component({ + selector: 'pos-config-page', + templateUrl: './root.component.html', + imports: [PosConfigPrintFormComponent, AppCardComponent, Message], +}) +export class PosConfigPageComponent { + private readonly router = inject(Router); + returnToMainPage() { + this.router.navigateByUrl('/'); + } +} diff --git a/src/app/domains/pos/modules/landing/components/categories.component.html b/src/app/domains/pos/modules/landing/components/categories.component.html index 650f017..df83d2f 100644 --- a/src/app/domains/pos/modules/landing/components/categories.component.html +++ b/src/app/domains/pos/modules/landing/components/categories.component.html @@ -1,5 +1,5 @@
-
+
@if (loading()) { @for (i of [1, 2, 3, 4, 5]; track i) { diff --git a/src/app/domains/pos/modules/landing/components/goods.component.html b/src/app/domains/pos/modules/landing/components/goods.component.html index 7faa050..cf7da88 100644 --- a/src/app/domains/pos/modules/landing/components/goods.component.html +++ b/src/app/domains/pos/modules/landing/components/goods.component.html @@ -1,6 +1,8 @@
-
-
+
+
+ } + + + +
+} diff --git a/src/app/shared/components/invoices/sale-invoice-single-view.component.ts b/src/app/shared/components/invoices/sale-invoice-single-view.component.ts new file mode 100644 index 0000000..558fe73 --- /dev/null +++ b/src/app/shared/components/invoices/sale-invoice-single-view.component.ts @@ -0,0 +1,202 @@ +import { Maybe } from '@/core'; +import { NativeBridgeService } from '@/core/services'; +import { PosInfoStore } from '@/domains/pos/store'; +import { CatalogTaxProviderStatusTagComponent } from '@/shared/catalog'; +import { AppCardComponent, KeyValueComponent } from '@/shared/components'; +import { ISaleInvoiceFullResponse } from '@/shared/components/invoices/sale-invoice-full-response.model'; +import { PageLoadingComponent } from '@/shared/components/page-loading.component'; +import { + IColumn, + PageDataListComponent, +} from '@/shared/components/pageDataList/page-data-list.component'; +import { PriceMaskDirective } from '@/shared/directives'; +import { UikitEmptyStateComponent } from '@/uikit'; +import { + Component, + computed, + EventEmitter, + inject, + Input, + Output, + TemplateRef, + ViewChild, +} from '@angular/core'; +import { UrlTree } from '@angular/router'; +import { ButtonDirective } from 'primeng/button'; +import { Divider } from 'primeng/divider'; +import { TableModule } from 'primeng/table'; + +export type TSaleInvoiceSingleViewVariant = 'full' | 'customer' | 'pos'; + +@Component({ + selector: 'shared-sale-invoice-single-view', + templateUrl: './sale-invoice-single-view.component.html', + imports: [ + AppCardComponent, + KeyValueComponent, + Divider, + ButtonDirective, + PageDataListComponent, + TableModule, + PageLoadingComponent, + UikitEmptyStateComponent, + PriceMaskDirective, + CatalogTaxProviderStatusTagComponent, + ], +}) +export class SharedSaleInvoiceSingleViewComponent { + private readonly nativeBridge = inject(NativeBridgeService); + private readonly posInfoStore = inject(PosInfoStore); + + @Input({ required: true }) loading!: boolean; + @Input() invoice!: Maybe; + @Input() variant: TSaleInvoiceSingleViewVariant = 'full'; + @Input() backRoute?: UrlTree | string | any[]; + + @Output() onRefresh = new EventEmitter(); + + @ViewChild('totalAmount', { static: false }) totalAmount!: TemplateRef; + + readonly posName = computed(() => { + if (this.posInfoStore.entity()) { + const { name, businessActivity, complex } = this.posInfoStore.entity()!; + return `${name} (${businessActivity.name} - ${complex.name})`; + } + return ''; + }); + + printInvoice = () => { + if (this.invoice) { + this.nativeBridge.print([ + { + title: 'اطلاعات صورت‌حساب', + items: [ + { + label: 'شماره منحصر به فرد مالیاتی', + value: 'this.invoice', + }, + { + label: 'شماره صورت‌حساب', + value: this.invoice.code, + }, + { + label: 'موضوع صورت‌حساب', + value: 'this.invoice', + }, + { + label: 'نوع صورت‌حساب', + value: 'this.invoice', + }, + { + label: 'تاریخ و ساعت', + value: this.invoice.invoice_date, + }, + { + label: 'وضعیت صورت‌حساب مالیاتی', + value: this.invoice.status.translate, + }, + ], + }, + { + title: `اطلاعات فروشنده`, + items: [ + { + label: 'عنوان فروشگاه', + value: this.invoice.pos.complex.business_activity.name, + }, + { + label: 'عنوان شعبه', + value: this.invoice.pos.complex.name, + }, + { + label: 'عنوان پایانه‌ فروش', + value: this.invoice.pos.name, + }, + { + label: 'شماره اقتصادی', + value: 'this.invoice.pos.complex.business_activity.economic_code', + }, + ], + }, + { + title: `اطلاعات خریدار`, + items: [ + { + label: 'نام خریدار', + value: + (this.invoice.customer + ? this.invoice.customer.type === 'LEGAL' + ? this.invoice.customer.legal?.company_name + : `${this.invoice.customer.individual?.first_name} ${this.invoice.customer.individual?.last_name}` + : this.invoice.unknown_customer?.name) || '-', + }, + { + label: 'شماره اقتصادی', + value: this.invoice.customer + ? this.invoice.customer.type === 'LEGAL' + ? this.invoice.customer.legal?.economic_code + : this.invoice.customer.individual?.economic_code || '-' + : '-', + }, + ], + }, + ]); + } + }; + + columns: IColumn[] = [ + { + field: 'name', + header: 'عنوان', + type: 'nested', + nestedOption: { + path: 'good.name', + }, + }, + { + field: 'sku_code', + header: 'شناسه کالا', + }, + { + field: 'unit_price', + header: 'قیمت واحد', + type: 'price', + }, + { + field: 'quantity', + header: 'مقدار', + customDataModel(item) { + return `${item.quantity} ${item.measure_unit_text}`; + }, + }, + { + field: 'total_amount', + header: 'قیمت نهایی', + type: 'price', + }, + ]; + + expandableColumns: IColumn[] = [ + { + field: 'commission', + header: 'کارمزد', + type: 'price', + }, + { + field: 'wages', + header: 'اجرت', + type: 'price', + }, + { + field: 'profit', + header: 'سود', + type: 'price', + }, + ]; + + expandedRows: { [key: string]: boolean } = {}; + + refresh() { + this.onRefresh.emit(); + } +} diff --git a/src/app/shared/components/pageDataList/page-data-list-table-view.component.html b/src/app/shared/components/pageDataList/page-data-list-table-view.component.html index f8aa0fa..e4d9627 100644 --- a/src/app/shared/components/pageDataList/page-data-list-table-view.component.html +++ b/src/app/shared/components/pageDataList/page-data-list-table-view.component.html @@ -168,7 +168,7 @@ - + diff --git a/src/app/shared/components/pageDataList/page-data-list.component.html b/src/app/shared/components/pageDataList/page-data-list.component.html index 4362ccf..5183304 100644 --- a/src/app/shared/components/pageDataList/page-data-list.component.html +++ b/src/app/shared/components/pageDataList/page-data-list.component.html @@ -1,4 +1,4 @@ -
+
@if (pageTitle || showAdd || filter || showRefresh || moreActions) { diff --git a/src/app/shared/constants/list-configs/account-list.const.ts b/src/app/shared/constants/list-configs/account-list.const.ts new file mode 100644 index 0000000..3cf2277 --- /dev/null +++ b/src/app/shared/constants/list-configs/account-list.const.ts @@ -0,0 +1,28 @@ +import { IListConfig } from './list-config.model'; + +export const accountListConfig: IListConfig = { + pageTitle: 'مدیریت حساب‌های کاربری', + addNewCtaLabel: 'افزودن حساب', + emptyPlaceholderTitle: 'حسابی یافت نشد', + emptyPlaceholderDescription: 'برای افزودن حساب کاربری، روی دکمهٔ بالا کلیک کنید.', + columns: [ + { + field: 'username', + header: 'نام کاربری', + type: 'nested', + nestedOption: { path: 'account.username' }, + }, + { + field: 'pos', + header: 'پایانه', + customDataModel(item: any) { + return `${item.pos.name} - ${item.pos.complex.name} - ${item.pos.complex.business_activity.name}`; + }, + }, + { + field: 'created_at', + header: 'تاریخ ایجاد', + type: 'date', + }, + ], +}; diff --git a/src/app/shared/constants/list-configs/business-activity-list.const.ts b/src/app/shared/constants/list-configs/business-activity-list.const.ts new file mode 100644 index 0000000..2dff64f --- /dev/null +++ b/src/app/shared/constants/list-configs/business-activity-list.const.ts @@ -0,0 +1,35 @@ +import { IListConfig } from './list-config.model'; + +export const businessActivityListConfig: IListConfig = { + pageTitle: 'مدیریت کسب و کار', + addNewCtaLabel: 'افزودن کسب و کار', + emptyPlaceholderTitle: 'کسب و کاری یافت نشد', + emptyPlaceholderDescription: 'برای افزودن کسب و کار، روی دکمهٔ بالا کلیک کنید.', + columns: [ + { field: 'name', header: 'عنوان' }, + { field: 'guild', header: 'صنف', type: 'nested', nestedOption: { path: 'guild.name' } }, + { field: 'economic_code', header: 'کد اقتصادی' }, + { + field: 'license_info', + header: 'محدودیت کاربر', + customDataModel(item: any) { + const licenseInfo = item.license_info; + if (!licenseInfo) { + return 'بدون مجوز'; + } + return `${licenseInfo.accounts_limit} کاربر`; + }, + }, + { + field: 'license_info', + header: 'انقضا مجوز', + type: 'nested', + nestedOption: { path: 'license_info.expires_at', type: 'date' }, + }, + { + field: 'created_at', + header: 'تاریخ ایجاد', + type: 'date', + }, + ], +}; diff --git a/src/app/shared/constants/list-configs/category-list.const.ts b/src/app/shared/constants/list-configs/category-list.const.ts new file mode 100644 index 0000000..dbe0739 --- /dev/null +++ b/src/app/shared/constants/list-configs/category-list.const.ts @@ -0,0 +1,12 @@ +import { IListConfig } from './list-config.model'; + +export const categoryListConfig: IListConfig = { + pageTitle: 'مدیریت دسته‌بندی‌های کالا', + addNewCtaLabel: 'افزودن دسته‌بندی', + emptyPlaceholderTitle: 'دسته‌بندی‌ یافت نشد', + emptyPlaceholderDescription: 'برای افزودن دسته‌بندی، روی دکمهٔ بالا کلیک کنید.', + columns: [ + { field: 'image_url', header: 'تصویر', type: 'thumbnail' }, + { field: 'name', header: 'عنوان' }, + ], +}; diff --git a/src/app/shared/constants/list-configs/complex-list.const.ts b/src/app/shared/constants/list-configs/complex-list.const.ts new file mode 100644 index 0000000..398af0f --- /dev/null +++ b/src/app/shared/constants/list-configs/complex-list.const.ts @@ -0,0 +1,17 @@ +import { IListConfig } from './list-config.model'; + +export const complexListConfig: IListConfig = { + pageTitle: 'مدیریت شعب', + addNewCtaLabel: 'افزودن شعبه', + emptyPlaceholderTitle: 'شعبه‌ای یافت نشد', + emptyPlaceholderDescription: 'برای افزودن شعبه، روی دکمهٔ بالا کلیک کنید.', + columns: [ + { field: 'name', header: 'عنوان' }, + { field: 'branch_code', header: 'کد شعبه' }, + { + field: 'created_at', + header: 'تاریخ ایجاد', + type: 'date', + }, + ], +}; diff --git a/src/app/shared/constants/list-configs/consumer-list.const.ts b/src/app/shared/constants/list-configs/consumer-list.const.ts new file mode 100644 index 0000000..5189102 --- /dev/null +++ b/src/app/shared/constants/list-configs/consumer-list.const.ts @@ -0,0 +1,35 @@ +import { IListConfig } from './list-config.model'; + +export const consumerListConfig: IListConfig = { + pageTitle: 'مدیریت مشتری‌ها', + addNewCtaLabel: 'افزودن مشتری', + emptyPlaceholderTitle: 'مشتری‌ای یافت نشد', + emptyPlaceholderDescription: 'برای افزودن مشتری، روی دکمهٔ بالا کلیک کنید.', + columns: [ + { field: 'name', header: 'عنوان' }, + { + field: 'business_counts', + header: 'تعداد کسب‌و‌کارها', + customDataModel(item: any) { + return item.business_counts || 0; + }, + }, + { + field: 'partner', + header: 'ارایه‌دهنده', + type: 'nested', + nestedOption: { path: 'partner.name' }, + }, + { + field: 'status', + header: 'وضعیت', + type: 'nested', + nestedOption: { path: 'status.translate' }, + }, + { + field: 'created_at', + header: 'تاریخ ایجاد', + type: 'date', + }, + ], +}; diff --git a/src/app/shared/constants/list-configs/customer-list.const.ts b/src/app/shared/constants/list-configs/customer-list.const.ts new file mode 100644 index 0000000..9af6ebb --- /dev/null +++ b/src/app/shared/constants/list-configs/customer-list.const.ts @@ -0,0 +1,42 @@ +import { IListConfig } from './list-config.model'; + +export const customerListConfig: IListConfig = { + pageTitle: 'مدیریت مشتری‌ها', + addNewCtaLabel: 'افزودن مشتری', + emptyPlaceholderTitle: 'مشتری‌ای یافت نشد', + emptyPlaceholderDescription: 'برای افزودن مشتری، روی دکمهٔ بالا کلیک کنید.', + columns: [ + { + field: 'type', + header: 'نوع مشتری', + customDataModel(item: any) { + return item.type === 'LEGAL' ? 'حقوقی' : 'حقیقی'; + }, + }, + { + field: 'name', + header: 'عنوان', + customDataModel(item: any) { + if (item.type === 'LEGAL') { + return item.legal?.company_name; + } + return `${item.individual?.first_name} ${item.individual?.last_name}`; + }, + }, + { + field: 'economic_code', + header: 'کد اقتصادی', + customDataModel(item: any) { + if (item.type === 'LEGAL') { + return item.legal?.economic_code; + } + return item.individual?.economic_code; + }, + }, + { + field: 'created_at', + header: 'تاریخ ایجاد', + type: 'date', + }, + ], +}; diff --git a/src/app/shared/constants/list-configs/device-brand-list.const.ts b/src/app/shared/constants/list-configs/device-brand-list.const.ts new file mode 100644 index 0000000..79f8815 --- /dev/null +++ b/src/app/shared/constants/list-configs/device-brand-list.const.ts @@ -0,0 +1,16 @@ +import { IListConfig } from './list-config.model'; + +export const deviceBrandListConfig: IListConfig = { + pageTitle: 'مدیریت برندهای دستگاه', + addNewCtaLabel: 'افزودن برند', + emptyPlaceholderTitle: 'برندی یافت نشد', + emptyPlaceholderDescription: 'برای افزودن برند دستگاه، روی دکمهٔ بالا کلیک کنید.', + columns: [ + { field: 'name', header: 'عنوان' }, + { + field: 'created_at', + header: 'تاریخ ایجاد', + type: 'date', + }, + ], +}; diff --git a/src/app/shared/constants/list-configs/device-list.const.ts b/src/app/shared/constants/list-configs/device-list.const.ts new file mode 100644 index 0000000..6d2b1d0 --- /dev/null +++ b/src/app/shared/constants/list-configs/device-list.const.ts @@ -0,0 +1,17 @@ +import { IListConfig } from './list-config.model'; + +export const deviceListConfig: IListConfig = { + pageTitle: 'مدیریت دستگاه‌ها', + addNewCtaLabel: 'افزودن دستگاه', + emptyPlaceholderTitle: 'دستگاه‌ای یافت نشد', + emptyPlaceholderDescription: 'برای افزودن دستگاه، روی دکمهٔ بالا کلیک کنید.', + columns: [ + { field: 'name', header: 'عنوان' }, + { field: 'brand', header: 'برند', type: 'nested', nestedOption: { path: 'brand.name' } }, + { + field: 'created_at', + header: 'تاریخ ایجاد', + type: 'date', + }, + ], +}; diff --git a/src/app/shared/constants/list-configs/good-list.const.ts b/src/app/shared/constants/list-configs/good-list.const.ts new file mode 100644 index 0000000..9c9d6e0 --- /dev/null +++ b/src/app/shared/constants/list-configs/good-list.const.ts @@ -0,0 +1,25 @@ +import { IListConfig } from './list-config.model'; + +export const goodListConfig: IListConfig = { + pageTitle: 'مدیریت کالاها', + addNewCtaLabel: 'افزودن کالا', + emptyPlaceholderTitle: 'کالایی یافت نشد', + emptyPlaceholderDescription: 'برای افزودن کالای، روی دکمهٔ بالا کلیک کنید.', + columns: [ + { field: 'image_url', header: 'تصویر', type: 'thumbnail' }, + { field: 'name', header: 'عنوان' }, + { field: 'sku', header: 'شناسه کالا', type: 'nested', nestedOption: { path: 'sku.code' } }, + { + field: 'measure_unit', + header: 'واحد اندازه‌گیری', + type: 'nested', + nestedOption: { path: 'measure_unit.name' }, + }, + { + field: 'category', + header: 'دسته‌بندی', + type: 'nested', + nestedOption: { path: 'category.name' }, + }, + ], +}; diff --git a/src/app/shared/constants/list-configs/index.ts b/src/app/shared/constants/list-configs/index.ts new file mode 100644 index 0000000..544735c --- /dev/null +++ b/src/app/shared/constants/list-configs/index.ts @@ -0,0 +1,20 @@ +export * from './account-list.const'; +export * from './business-activity-list.const'; +export * from './category-list.const'; +export * from './complex-list.const'; +export * from './consumer-list.const'; +export * from './device-brand-list.const'; +export * from './device-list.const'; +export * from './good-list.const'; +export * from './license-list.const'; +export * from './list-config.model'; +export * from './partner-account-list.const'; +export * from './partner-consumer-account-list.const'; +export * from './partner-customer-list.const'; +export * from './partner-list.const'; +export * from './pos-list.const'; +export * from './provider-list.const'; +export * from './sale-invoice-list.const'; +export * from './sku-list.const'; +export * from './superAdmin-consumer-account-list.const'; +export * from './user-list.const'; diff --git a/src/app/shared/constants/list-configs/license-list.const.ts b/src/app/shared/constants/list-configs/license-list.const.ts new file mode 100644 index 0000000..9079a90 --- /dev/null +++ b/src/app/shared/constants/list-configs/license-list.const.ts @@ -0,0 +1,26 @@ +import { IListConfig } from './list-config.model'; + +export const licenseListConfig: IListConfig = { + pageTitle: 'مدیریت لایسنس‌ها', + addNewCtaLabel: 'افزودن لایسنس', + emptyPlaceholderTitle: 'لایسنسی یافت نشد', + emptyPlaceholderDescription: 'برای افزودن لایسنس، روی دکمهٔ بالا کلیک کنید.', + columns: [ + { + field: 'consumer', + header: 'مشتری', + customDataModel(item: any) { + return `${item.consumer.first_name} ${item.consumer.last_name}`; + }, + }, + { + field: 'partner', + header: 'ارایه شده توسط', + customDataModel(item: any) { + return item.license?.charged_license_transaction?.partner?.name || 'برند نرم‌افزار'; + }, + }, + { field: 'expires_at', header: 'تاریخ انقضا', type: 'date' }, + { field: 'created_at', header: 'تاریخ ایجاد', type: 'date' }, + ], +}; diff --git a/src/app/shared/constants/list-configs/list-config.model.ts b/src/app/shared/constants/list-configs/list-config.model.ts new file mode 100644 index 0000000..710d1f5 --- /dev/null +++ b/src/app/shared/constants/list-configs/list-config.model.ts @@ -0,0 +1,9 @@ +import { IColumn } from '@/shared/components/pageDataList/page-data-list.component'; + +export interface IListConfig { + pageTitle: string; + addNewCtaLabel: string; + emptyPlaceholderTitle: string; + emptyPlaceholderDescription: string; + columns: IColumn[]; +} diff --git a/src/app/shared/constants/list-configs/partner-account-list.const.ts b/src/app/shared/constants/list-configs/partner-account-list.const.ts new file mode 100644 index 0000000..139ca34 --- /dev/null +++ b/src/app/shared/constants/list-configs/partner-account-list.const.ts @@ -0,0 +1,28 @@ +import { IListConfig } from './list-config.model'; + +export const partnerAccountListConfig: IListConfig = { + pageTitle: 'مدیریت حساب‌های کاربری', + addNewCtaLabel: 'افزودن حساب', + emptyPlaceholderTitle: 'حسابی یافت نشد', + emptyPlaceholderDescription: 'برای افزودن حساب کاربری، روی دکمهٔ بالا کلیک کنید.', + columns: [ + { + field: 'username', + header: 'نام کاربری', + type: 'nested', + nestedOption: { path: 'account.username' }, + }, + { field: 'role', header: 'نقش' }, + { + field: 'status', + header: 'وضعیت', + type: 'nested', + nestedOption: { path: 'account.status' }, + }, + { + field: 'created_at', + header: 'تاریخ ایجاد', + type: 'date', + }, + ], +}; diff --git a/src/app/shared/constants/list-configs/partner-consumer-account-list.const.ts b/src/app/shared/constants/list-configs/partner-consumer-account-list.const.ts new file mode 100644 index 0000000..d453090 --- /dev/null +++ b/src/app/shared/constants/list-configs/partner-consumer-account-list.const.ts @@ -0,0 +1,34 @@ +import { IListConfig } from './list-config.model'; + +export const partnerConsumerAccountListConfig: IListConfig = { + pageTitle: 'مدیریت حساب‌های مشتری', + addNewCtaLabel: 'افزودن حساب', + emptyPlaceholderTitle: 'حسابی یافت نشد', + emptyPlaceholderDescription: 'برای افزودن حساب، روی دکمهٔ بالا کلیک کنید.', + columns: [ + { + field: 'account', + header: 'نام کاربری', + type: 'nested', + nestedOption: { path: 'account.username' }, + }, + { field: 'role', header: 'نوع حساب', type: 'nested', nestedOption: { path: 'role.translate' } }, + { + field: 'pos', + header: 'پایانه‌', + customDataModel(item: any) { + if (item.pos) { + return `${item.pos.name} (${item.pos.complex.name} - ${item.pos.complex.business_activity.name})`; + } + return '-'; + }, + }, + { + field: 'status', + header: 'وضعیت', + type: 'nested', + nestedOption: { path: 'account.status.translate' }, + variant: 'tag', + }, + ], +}; diff --git a/src/app/shared/constants/list-configs/partner-customer-list.const.ts b/src/app/shared/constants/list-configs/partner-customer-list.const.ts new file mode 100644 index 0000000..dc86c5b --- /dev/null +++ b/src/app/shared/constants/list-configs/partner-customer-list.const.ts @@ -0,0 +1,16 @@ +import { IListConfig } from './list-config.model'; + +export const partnerCustomerListConfig: IListConfig = { + pageTitle: 'مدیریت مشتریان', + addNewCtaLabel: 'افزودن مشتری', + emptyPlaceholderTitle: 'مشتری‌ای یافت نشد', + emptyPlaceholderDescription: 'برای افزودن مشتری، روی دکمهٔ بالا کلیک کنید.', + columns: [ + { field: 'first_name', header: 'نام' }, + { + field: 'created_at', + header: 'تاریخ ایجاد', + type: 'date', + }, + ], +}; diff --git a/src/app/shared/constants/list-configs/partner-list.const.ts b/src/app/shared/constants/list-configs/partner-list.const.ts new file mode 100644 index 0000000..e22e6bb --- /dev/null +++ b/src/app/shared/constants/list-configs/partner-list.const.ts @@ -0,0 +1,42 @@ +import { IListConfig } from './list-config.model'; + +export const partnerListConfig: IListConfig = { + pageTitle: 'مدیریت ارایه‌دهندگان', + addNewCtaLabel: 'افزودن ارایه‌دهنده', + emptyPlaceholderTitle: 'ارایه‌دهندگان‌ای یافت نشد', + emptyPlaceholderDescription: 'برای افزودن ارایه‌دهنده، روی دکمهٔ بالا کلیک کنید.', + columns: [ + { field: 'name', header: 'عنوان' }, + { field: 'code', header: 'کد' }, + { + field: 'licenses', + header: 'تعداد لایسنس‌ها', + customDataModel(item: any) { + return item.licenses || 0; + }, + }, + { + field: 'account_quota', + header: 'تعداد کاربرهای خریداری شده', + customDataModel(item: any) { + return item.account_quota || 0; + }, + }, + { + field: 'license_renew', + header: 'تعداد لایسنس تمدیدی', + customDataModel(item: any) { + return item.license_renew || 0; + }, + }, + { + field: 'status', + header: 'وضعیت', + type: 'nested', + variant: 'tag', + nestedOption: { + path: 'status.translate', + }, + }, + ], +}; diff --git a/src/app/shared/constants/list-configs/pos-list.const.ts b/src/app/shared/constants/list-configs/pos-list.const.ts new file mode 100644 index 0000000..0263c5d --- /dev/null +++ b/src/app/shared/constants/list-configs/pos-list.const.ts @@ -0,0 +1,31 @@ +import { IListConfig } from './list-config.model'; + +export const posListConfig: IListConfig = { + pageTitle: 'مدیریت پایانه‌های فروش', + addNewCtaLabel: 'افزودن پایانه', + emptyPlaceholderTitle: 'پایانه‌ای یافت نشد', + emptyPlaceholderDescription: 'برای افزودن پایانه، روی دکمهٔ بالا کلیک کنید.', + columns: [ + { field: 'name', header: 'عنوان' }, + { field: 'serial_number', header: 'شماره سریال' }, + { field: 'pos_type', header: 'نوع دستگاه' }, + { field: 'device', header: 'دستگاه', type: 'nested', nestedOption: { path: 'device.name' } }, + { + field: 'provider', + header: 'ارایه‌دهنده', + type: 'nested', + nestedOption: { path: 'provider.name' }, + }, + { + field: 'account', + header: 'کاربر', + type: 'nested', + nestedOption: { path: 'account.username' }, + }, + { + field: 'created_at', + header: 'تاریخ ایجاد', + type: 'date', + }, + ], +}; diff --git a/src/app/shared/constants/list-configs/provider-list.const.ts b/src/app/shared/constants/list-configs/provider-list.const.ts new file mode 100644 index 0000000..bb5cdce --- /dev/null +++ b/src/app/shared/constants/list-configs/provider-list.const.ts @@ -0,0 +1,16 @@ +import { IListConfig } from './list-config.model'; + +export const providerListConfig: IListConfig = { + pageTitle: 'مدیریت تامین‌کنندگان', + addNewCtaLabel: 'افزودن تامین‌کننده', + emptyPlaceholderTitle: 'تامین‌کننده‌ای یافت نشد', + emptyPlaceholderDescription: 'برای افزودن تامین‌کننده، روی دکمهٔ بالا کلیک کنید.', + columns: [ + { field: 'name', header: 'عنوان' }, + { + field: 'created_at', + header: 'تاریخ ایجاد', + type: 'date', + }, + ], +}; diff --git a/src/app/shared/constants/list-configs/sale-invoice-list.const.ts b/src/app/shared/constants/list-configs/sale-invoice-list.const.ts new file mode 100644 index 0000000..7a73eaa --- /dev/null +++ b/src/app/shared/constants/list-configs/sale-invoice-list.const.ts @@ -0,0 +1,39 @@ +import { IListConfig } from './list-config.model'; + +export const saleInvoiceListConfig: IListConfig = { + pageTitle: 'مدیریت فاکتورهای فروش', + addNewCtaLabel: 'افزودن فاکتور', + emptyPlaceholderTitle: 'فاکتوری یافت نشد', + emptyPlaceholderDescription: 'برای افزودن فاکتور، روی دکمهٔ بالا کلیک کنید.', + columns: [ + { field: 'code', header: 'کد رهگیری' }, + { + field: 'total_amount', + header: 'قیمت نهایی', + type: 'price', + }, + { + field: 'pos', + header: 'پایانه', + customDataModel(item: any) { + return `${item.pos.name} (${item.pos.complex.name} - ${item.pos.complex.business_activity.name})`; + }, + }, + { + field: 'consumer_account', + header: 'ایجاد شده توسط', + type: 'nested', + nestedOption: { path: 'consumer_account.account.username' }, + }, + { + field: 'invoice_date', + header: 'تاریخ فاکتور', + type: 'date', + }, + { + field: 'created_at', + header: 'تاریخ ایجاد', + type: 'date', + }, + ], +}; diff --git a/src/app/shared/constants/list-configs/sku-list.const.ts b/src/app/shared/constants/list-configs/sku-list.const.ts new file mode 100644 index 0000000..e8b0c04 --- /dev/null +++ b/src/app/shared/constants/list-configs/sku-list.const.ts @@ -0,0 +1,13 @@ +import { IListConfig } from './list-config.model'; + +export const skuListConfig: IListConfig = { + pageTitle: 'مدیریت شناسه کالاها', + addNewCtaLabel: 'افزودن شناسه کالا', + emptyPlaceholderTitle: 'شناسه کالایی یافت نشد', + emptyPlaceholderDescription: 'برای افزودن شناسه کالا، روی دکمهٔ بالا کلیک کنید.', + columns: [ + { field: 'code', header: 'کد' }, + { field: 'name', header: 'عنوان' }, + { field: 'VAT', header: 'مالیات ارزش افزوده' }, + ], +}; diff --git a/src/app/shared/constants/list-configs/superAdmin-consumer-account-list.const.ts b/src/app/shared/constants/list-configs/superAdmin-consumer-account-list.const.ts new file mode 100644 index 0000000..802b4ba --- /dev/null +++ b/src/app/shared/constants/list-configs/superAdmin-consumer-account-list.const.ts @@ -0,0 +1,35 @@ +import { IListConfig } from './list-config.model'; + +export const superAdminConsumerAccountListConfig: IListConfig = { + pageTitle: 'مدیریت حساب‌های مشتری', + addNewCtaLabel: 'افزودن حساب', + emptyPlaceholderTitle: 'حسابی یافت نشد', + emptyPlaceholderDescription: 'برای افزودن حساب، روی دکمهٔ بالا کلیک کنید.', + columns: [ + { + field: 'account', + header: 'نام کاربری', + type: 'nested', + nestedOption: { path: 'account.username' }, + }, + { field: 'role', header: 'نوع حساب', type: 'nested', nestedOption: { path: 'role.translate' } }, + { + field: 'pos', + header: 'پایانه', + customDataModel(item: any) { + return `${item.pos.name} (${item.pos.complex.name} - ${item.pos.complex.business_activity.name})`; + }, + }, + { + field: 'status', + header: 'وضعیت', + type: 'nested', + nestedOption: { path: 'account.status.translate' }, + }, + { + field: 'created_at', + header: 'تاریخ ایجاد', + type: 'date', + }, + ], +}; diff --git a/src/app/shared/constants/list-configs/user-list.const.ts b/src/app/shared/constants/list-configs/user-list.const.ts new file mode 100644 index 0000000..34b8329 --- /dev/null +++ b/src/app/shared/constants/list-configs/user-list.const.ts @@ -0,0 +1,18 @@ +import { IListConfig } from './list-config.model'; + +export const userListConfig: IListConfig = { + pageTitle: 'مدیریت کاربران', + addNewCtaLabel: 'افزودن کاربر', + emptyPlaceholderTitle: 'کاربری یافت نشد', + emptyPlaceholderDescription: 'برای افزودن کاربر، روی دکمهٔ بالا کلیک کنید.', + columns: [ + { field: 'fullname', header: 'نام' }, + { field: 'mobile_number', header: 'شماره موبایل' }, + { field: 'national_code', header: 'کد ملی' }, + { + field: 'created_at', + header: 'تاریخ ایجاد', + type: 'date', + }, + ], +}; diff --git a/src/assets/constants/localStorageKeys.const.ts b/src/assets/constants/localStorageKeys.const.ts index fd65074..68b2dcc 100644 --- a/src/assets/constants/localStorageKeys.const.ts +++ b/src/assets/constants/localStorageKeys.const.ts @@ -4,4 +4,5 @@ export enum LOCAL_STORAGE_KEYS { USER_DATA = 'userData', LAST_LOGIN_TIME = 'lastLoginTime', LOGIN_ATTEMPTS = 'loginAttempts', + POS_CONFIG_PRINT = 'posConfigPrint', }