diff --git a/public/favicon/site.webmanifest b/public/favicon/site.webmanifest index ea30e22..d2499ae 100644 --- a/public/favicon/site.webmanifest +++ b/public/favicon/site.webmanifest @@ -1,21 +1,21 @@ { - "name": "مدیریت کسب‌وکار", - "short_name": "مدیریت کسب‌وکار", + "background_color": "#ffffff", + "display": "standalone", "icons": [ { - "src": "/web-app-manifest-192x192.png", + "purpose": "maskable", "sizes": "192x192", - "type": "image/png", - "purpose": "maskable" + "src": "/web-app-manifest-192x192.png", + "type": "image/png" }, { - "src": "/web-app-manifest-512x512.png", + "purpose": "maskable", "sizes": "512x512", - "type": "image/png", - "purpose": "maskable" + "src": "/web-app-manifest-512x512.png", + "type": "image/png" } ], - "theme_color": "#ffffff", - "background_color": "#ffffff", - "display": "standalone" + "name": "مدیریت صورت‌حساب‌های مالیاتی", + "short_name": "مدیریت صورت‌حساب‌های مالیاتی", + "theme_color": "#ffffff" } diff --git a/src/app/components/index.ts b/src/app/components/index.ts new file mode 100644 index 0000000..bdb2ea0 --- /dev/null +++ b/src/app/components/index.ts @@ -0,0 +1 @@ +export * from './pos-display'; diff --git a/src/app/components/pos-display/README.md b/src/app/components/pos-display/README.md new file mode 100644 index 0000000..e6a4e78 --- /dev/null +++ b/src/app/components/pos-display/README.md @@ -0,0 +1,133 @@ +# POS Display Component + +A reusable component for displaying Point-of-Sale (POS) terminal information with configurable variants. + +## Overview + +The `PosDisplayComponent` displays POS entity information in a card-based layout with support for different visibility variants ('consumer', 'partner', 'full'). + +## Features + +- **Variant Support**: Choose from 'consumer', 'partner', or 'full' display modes +- **Conditional Field Visibility**: Fields are dynamically shown/hidden based on variant +- **Edit Mode**: Toggle between view and edit modes +- **Responsive Grid**: Displays up to 3 columns with gap-4 spacing +- **More Actions**: Optional button for additional actions + +## Usage + +### Basic Usage + +```typescript +import { PosDisplayComponent, IPosEntity } from '@/app/components'; + +@Component({ + selector: 'app-my-component', + template: ` + + `, + standalone: true, + imports: [PosDisplayComponent], +}) +export class MyComponent { + posData = signal({ + id: '123', + name: 'Terminal 1', + pos_type: 'PSP', + serial_number: 'SN123', + model: 'Model X', + status: 'active', + complex: { id: 'c1', name: 'Complex 1' }, + device: { id: 'd1', name: 'Device A' }, + provider: { id: 'p1', name: 'Provider A' }, + }); + + isEditing = signal(false); + + handleMoreAction(): void { + console.log('More action clicked'); + } + + editModeChange(editing: boolean): void { + this.isEditing.set(editing); + } +} +``` + +## Variants + +### Consumer Variant +Displays: name, pos_type, serial_number, device, model, provider (6 fields) + +### Partner Variant +Displays: name, pos_type, serial_number (3 fields) + +### Full Variant +Displays: name, pos_type, serial_number, device, model, provider (6 fields - same as consumer) + +## Inputs + +| Input | Type | Default | Description | +|-------|------|---------|-------------| +| `variant` | 'consumer' \| 'partner' \| 'full' | 'full' | Display variant mode | +| `pos` | Signal | undefined | POS entity data | +| `editMode` | Signal | false | Edit mode state | +| `cardTitle` | string | 'اطلاعات پایانه‌ی فروش' | Card header title | +| `showMoreActions` | boolean | true | Show more actions button | + +## Outputs + +| Output | Payload | Description | +|--------|---------|-------------| +| `editModeChange` | boolean | Emitted when edit mode toggled | +| `onMoreAction` | void | Emitted when more actions button clicked | +| `onSubmit` | void | Emitted on form submission | + +## Model (IPosEntity) + +```typescript +interface IPosEntity { + id: string; + name: string; + serial_number: string; + model?: string; + status: string; + pos_type: string; + complex: ISummary; + device?: ISummary; + provider?: ISummary; +} +``` + +## Integration in Existing Component + +To replace the inline display in your POS single view: + +```typescript +// Before: Manual field rendering + + +// ... more fields + +// After: Use the component + +``` + +## Files + +- `pos-display.component.ts` - Component logic +- `pos-display.component.html` - Template +- `pos-display.component.scss` - Styles +- `models/posEntity.model.ts` - TypeScript interfaces and types +- `index.ts` - Barrel export diff --git a/src/app/components/pos-display/index.ts b/src/app/components/pos-display/index.ts new file mode 100644 index 0000000..926d83c --- /dev/null +++ b/src/app/components/pos-display/index.ts @@ -0,0 +1,2 @@ +export * from './models/posEntity.model'; +export * from './pos-display.component'; diff --git a/src/app/components/pos-display/models/posEntity.model.ts b/src/app/components/pos-display/models/posEntity.model.ts new file mode 100644 index 0000000..6f68bd3 --- /dev/null +++ b/src/app/components/pos-display/models/posEntity.model.ts @@ -0,0 +1,27 @@ +import ISummary from '@/core/models/summary'; + +export type PosVariant = 'consumer' | 'partner' | 'full'; + +export interface IPosEntity { + id: string; + name: string; + serial_number: string; + model?: string; + status: string; + pos_type: string; + complex: ISummary; + device?: ISummary; + provider?: ISummary; +} + +export interface IPosDisplayConfig { + variant: PosVariant; + editable?: boolean; + showMoreActions?: boolean; +} + +export const POS_VARIANT_FIELDS: Record> = { + consumer: ['name', 'pos_type', 'serial_number', 'device', 'model', 'provider'], + partner: ['name', 'pos_type', 'serial_number'], + full: ['name', 'pos_type', 'serial_number', 'device', 'model', 'provider'], +}; diff --git a/src/app/components/pos-display/pos-display.component.html b/src/app/components/pos-display/pos-display.component.html new file mode 100644 index 0000000..28bee2f --- /dev/null +++ b/src/app/components/pos-display/pos-display.component.html @@ -0,0 +1,17 @@ + + + @if (showMoreActions) { + ورود به پایانه + } + + +
+
+ @for (field of visibleFields(); track field) { + @if (isFieldVisible(field)) { + + } + } +
+
+
diff --git a/src/app/components/pos-display/pos-display.component.ts b/src/app/components/pos-display/pos-display.component.ts new file mode 100644 index 0000000..a371bc4 --- /dev/null +++ b/src/app/components/pos-display/pos-display.component.ts @@ -0,0 +1,78 @@ +import { AppCardComponent, KeyValueComponent } from '@/shared/components'; +import { CommonModule } from '@angular/common'; +import { Component, effect, EventEmitter, Input, Output, signal } from '@angular/core'; +import { Button } from 'primeng/button'; +import { IPosEntity, POS_VARIANT_FIELDS, PosVariant } from './models/posEntity.model'; + +@Component({ + selector: 'app-pos-entity-display', + standalone: true, + imports: [CommonModule, AppCardComponent, Button, KeyValueComponent], + templateUrl: './pos-display.component.html', +}) +export class PosDisplayComponent { + @Input() variant: PosVariant = 'full'; + @Input() pos = signal(undefined); + @Input() editMode = signal(false); + @Input() cardTitle = 'اطلاعات پایانه‌ی فروش'; + @Input() showMoreActions = true; + + @Output() editModeChange = new EventEmitter(); + @Output() onMoreAction = new EventEmitter(); + @Output() onSubmit = new EventEmitter(); + + visibleFields = signal>([]); + isEditing = signal(false); + + constructor() { + effect(() => { + this.visibleFields.set(POS_VARIANT_FIELDS[this.variant] || POS_VARIANT_FIELDS.full); + }); + + effect(() => { + this.isEditing.set(this.editMode()); + }); + } + + get fieldLabels(): Record { + return { + name: 'عنوان', + pos_type: 'نوع پایانه', + serial_number: 'شماره سریال', + device: 'نوع دستگاه', + model: 'مدل دستگاه', + provider: 'ارایه‌دهنده', + }; + } + + isFieldVisible(field: keyof IPosEntity): boolean { + return this.visibleFields().includes(field); + } + + getFieldValue(field: keyof IPosEntity): any { + const posData = this.pos(); + if (!posData) return null; + + if (field === 'device' && posData.device) { + return posData.device.name; + } + if (field === 'provider' && posData.provider) { + return posData.provider.name; + } + return posData[field]; + } + + toggleEditMode(): void { + const newMode = !this.isEditing(); + this.isEditing.set(newMode); + this.editModeChange.emit(newMode); + } + + handleMoreAction(): void { + this.onMoreAction.emit(); + } + + handleSubmit(): void { + this.onSubmit.emit(); + } +} diff --git a/src/app/domains/consumer/components/invoices/shared-saleInvoice.component.html b/src/app/domains/consumer/components/invoices/shared-saleInvoice.component.html index 5797451..66ff9ca 100644 --- a/src/app/domains/consumer/components/invoices/shared-saleInvoice.component.html +++ b/src/app/domains/consumer/components/invoices/shared-saleInvoice.component.html @@ -58,6 +58,7 @@ [items]="invoice()?.items" [loading]="loading" pageTitle="" + (onRefresh)="refresh()" /> 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 23c62ef..5368a24 100644 --- a/src/app/domains/consumer/components/invoices/shared-saleInvoice.component.ts +++ b/src/app/domains/consumer/components/invoices/shared-saleInvoice.component.ts @@ -4,7 +4,7 @@ import { PageDataListComponent, } from '@/shared/components/pageDataList/page-data-list.component'; import { getGoodUnitTypeProperties } from '@/utils'; -import { Component, Input } from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Badge } from 'primeng/badge'; import { ButtonDirective } from 'primeng/button'; import { Divider } from 'primeng/divider'; @@ -30,6 +30,8 @@ export class ConsumerSaleInvoiceSharedComponent { @Input({ required: true }) invoice!: any; @Input() variant: TConsumerSaleInvoice = 'full'; + @Output() onRefresh = new EventEmitter(); + printInvoice = () => { window.print(); }; @@ -86,4 +88,8 @@ export class ConsumerSaleInvoiceSharedComponent { ]; expandedRows: { [key: string]: boolean } = {}; + + refresh() { + this.onRefresh.emit(); + } } diff --git a/src/app/domains/consumer/modules/accounts/views/list.component.html b/src/app/domains/consumer/modules/accounts/views/list.component.html index f7429d0..704076d 100644 --- a/src/app/domains/consumer/modules/accounts/views/list.component.html +++ b/src/app/domains/consumer/modules/accounts/views/list.component.html @@ -4,6 +4,7 @@ emptyPlaceholderTitle="حساب‌ کاربری‌ای یافت نشد" [items]="items()" [loading]="loading()" + (onRefresh)="refresh()" /> @if (selectedItemForEdit()) { diff --git a/src/app/domains/consumer/modules/businessActivities/components/complexes/list.component.html b/src/app/domains/consumer/modules/businessActivities/components/complexes/list.component.html index 91b1d51..181a497 100644 --- a/src/app/domains/consumer/modules/businessActivities/components/complexes/list.component.html +++ b/src/app/domains/consumer/modules/businessActivities/components/complexes/list.component.html @@ -13,6 +13,7 @@ (onAdd)="openAddForm()" (onEdit)="onEditClick($event)" (onDetails)="toSinglePage($event)" + (onRefresh)="refresh()" > + @if (form.controls.pos_type.value === "PSP") { + + + + } + @if (!editMode) { اطلاعات ورود @@ -51,12 +57,6 @@ } - @if (form.controls.pos_type.value === "PSP") { - - - - } - diff --git a/src/app/domains/consumer/modules/businessActivities/components/poses/list.component.html b/src/app/domains/consumer/modules/businessActivities/components/poses/list.component.html index b64d630..bb278ec 100644 --- a/src/app/domains/consumer/modules/businessActivities/components/poses/list.component.html +++ b/src/app/domains/consumer/modules/businessActivities/components/poses/list.component.html @@ -12,6 +12,7 @@ (onAdd)="openAddForm()" (onEdit)="onEditClick($event)" (onDetails)="toSinglePage($event)" + (onRefresh)="refresh()" > diff --git a/src/app/domains/consumer/modules/customers/components/list.component.html b/src/app/domains/consumer/modules/customers/components/list.component.html index d54dc5c..916930c 100644 --- a/src/app/domains/consumer/modules/customers/components/list.component.html +++ b/src/app/domains/consumer/modules/customers/components/list.component.html @@ -8,6 +8,7 @@ [showDetails]="true" (onEdit)="onEditClick($event)" (onDetails)="toSinglePage($event)" + (onRefresh)="refresh()" /> @if (selectedItemForEdit()) { diff --git a/src/app/domains/consumer/modules/customers/components/saleInvoices/list.component.html b/src/app/domains/consumer/modules/customers/components/saleInvoices/list.component.html index a264dff..f0dfa43 100644 --- a/src/app/domains/consumer/modules/customers/components/saleInvoices/list.component.html +++ b/src/app/domains/consumer/modules/customers/components/saleInvoices/list.component.html @@ -6,4 +6,5 @@ [loading]="loading()" [showDetails]="true" (onDetails)="toSinglePage($event)" + (onRefresh)="refresh()" /> diff --git a/src/app/domains/consumer/modules/dashboard/components/statistics/latestInvoices/latest-Invoices.component.html b/src/app/domains/consumer/modules/dashboard/components/statistics/latestInvoices/latest-Invoices.component.html index 7634913..71339d7 100644 --- a/src/app/domains/consumer/modules/dashboard/components/statistics/latestInvoices/latest-Invoices.component.html +++ b/src/app/domains/consumer/modules/dashboard/components/statistics/latestInvoices/latest-Invoices.component.html @@ -6,6 +6,7 @@ [showDetails]="true" [loading]="loading()" (onDetails)="toSinglePage($event)" + (onRefresh)="refresh()" > تمامی فاکتورها diff --git a/src/app/domains/consumer/modules/poses/components/list.component.html b/src/app/domains/consumer/modules/poses/components/list.component.html index cb921f8..2cc804a 100644 --- a/src/app/domains/consumer/modules/poses/components/list.component.html +++ b/src/app/domains/consumer/modules/poses/components/list.component.html @@ -8,6 +8,7 @@ [showEdit]="true" (onEdit)="onEditClick($event)" (onDetails)="toSinglePage($event)" + (onRefresh)="refresh()" > ورود به پایانه diff --git a/src/app/domains/consumer/modules/saleInvoices/components/list.component.html b/src/app/domains/consumer/modules/saleInvoices/components/list.component.html index a264dff..f0dfa43 100644 --- a/src/app/domains/consumer/modules/saleInvoices/components/list.component.html +++ b/src/app/domains/consumer/modules/saleInvoices/components/list.component.html @@ -6,4 +6,5 @@ [loading]="loading()" [showDetails]="true" (onDetails)="toSinglePage($event)" + (onRefresh)="refresh()" /> diff --git a/src/app/domains/consumer/store/main.store.ts b/src/app/domains/consumer/store/main.store.ts index b6275db..72e18d1 100644 --- a/src/app/domains/consumer/store/main.store.ts +++ b/src/app/domains/consumer/store/main.store.ts @@ -39,7 +39,7 @@ export class ConsumerStore extends EntityStore { this.layoutService.setPanelInfo({ - title: 'پنل مدیریت کسب و کار', + title: 'پنل مدیریت صورت‌حساب‌های مالیاتی', }); this.patchState({ entity }); }); diff --git a/src/app/domains/partner/constants/apiRoutes/index.ts b/src/app/domains/partner/constants/apiRoutes/index.ts index 8e4bf96..7ba6922 100644 --- a/src/app/domains/partner/constants/apiRoutes/index.ts +++ b/src/app/domains/partner/constants/apiRoutes/index.ts @@ -1,5 +1,6 @@ const baseUrl = '/api/v1/partner'; export const PARTNER_API_ROUTES = { - info: () => `${baseUrl}`, + me: () => `${baseUrl}`, + info: () => `${baseUrl}/info`, }; diff --git a/src/app/domains/partner/models/io.d.ts b/src/app/domains/partner/models/io.d.ts index c4d9f58..79fdc14 100644 --- a/src/app/domains/partner/models/io.d.ts +++ b/src/app/domains/partner/models/io.d.ts @@ -1,5 +1,14 @@ -export interface IPartnerInfoRawResponse { +import ISummary from '@/core/models/summary'; + +export interface IPartnerRawResponse { id: string; - name: string; + role: string; + partner: ISummary; + account: { + username: string; + }; } +export interface IPartnerResponse extends IPartnerRawResponse {} + +export interface IPartnerInfoRawResponse extends ISummary {} export interface IPartnerInfoResponse extends IPartnerInfoRawResponse {} diff --git a/src/app/domains/partner/modules/accounts/views/list.component.html b/src/app/domains/partner/modules/accounts/views/list.component.html index e41a2a1..ed61b67 100644 --- a/src/app/domains/partner/modules/accounts/views/list.component.html +++ b/src/app/domains/partner/modules/accounts/views/list.component.html @@ -6,6 +6,7 @@ [showDetails]="true" [loading]="loading()" (onDetails)="toSinglePage($event)" + (onRefresh)="refresh()" /> @if (selectedItemForEdit()) { diff --git a/src/app/domains/partner/modules/consumers/components/accounts/list.component.html b/src/app/domains/partner/modules/consumers/components/accounts/list.component.html index 63600a9..7a1070f 100644 --- a/src/app/domains/partner/modules/consumers/components/accounts/list.component.html +++ b/src/app/domains/partner/modules/consumers/components/accounts/list.component.html @@ -1,15 +1,10 @@ + + @if (!editMode) { + اطلاعات لایسنس + + } + diff --git a/src/app/domains/partner/modules/consumers/components/businessActivities/form.component.ts b/src/app/domains/partner/modules/consumers/components/businessActivities/form.component.ts index 2b5913d..544dc99 100644 --- a/src/app/domains/partner/modules/consumers/components/businessActivities/form.component.ts +++ b/src/app/domains/partner/modules/consumers/components/businessActivities/form.component.ts @@ -1,11 +1,14 @@ // import { CatalogRolesComponent } from '@/shared/catalog/roles'; +import { MustMatch } from '@/core/validators'; import { AbstractFormDialog } from '@/shared/abstractClasses'; import { CatalogGuildSelectComponent } from '@/shared/catalog/guild/components/select.component'; import { InputComponent } from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; +import { UikitFlatpickrJalaliComponent } from '@/uikit'; import { Component, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; import { Dialog } from 'primeng/dialog'; +import { Divider } from 'primeng/divider'; import { IBusinessActivityRequest, IBusinessActivityResponse } from '../../models'; import { PartnerConsumerBusinessActivitiesService } from '../../services/businessActivities.service'; @@ -18,6 +21,8 @@ import { PartnerConsumerBusinessActivitiesService } from '../../services/busines InputComponent, FormFooterActionsComponent, CatalogGuildSelectComponent, + Divider, + UikitFlatpickrJalaliComponent, ], }) export class ConsumerBusinessActivitiesFormComponent extends AbstractFormDialog< @@ -30,11 +35,28 @@ export class ConsumerBusinessActivitiesFormComponent extends AbstractFormDialog< @Input() businessActivityId!: string; initForm = () => { - return this.fb.group({ + const form = this.fb.group({ name: [this.initialValues?.name || '', [Validators.required]], economic_code: [this.initialValues?.economic_code || '', [Validators.required]], guild_id: [this.initialValues?.guild?.id || '', [Validators.required]], + license_starts_at: [''], }); + + if (this.editMode) { + // @ts-ignore + form.removeControl('license_starts_at'); + } else { + form.addControl( + 'license_starts_at', + this.fb.control('', { + nonNullable: true, + validators: [Validators.required], + }), + ); + form.addValidators([MustMatch('password', 'confirmPassword')]); + } + + return form; }; form = this.initForm(); @@ -48,10 +70,11 @@ export class ConsumerBusinessActivitiesFormComponent extends AbstractFormDialog< } submitForm() { - const formValue = this.form.value as IBusinessActivityRequest; + const formValue = this.form.value as IBusinessActivityRequest & { confirmPassword?: string }; + const { confirmPassword, ...rest } = formValue; if (this.editMode) { - return this.service.update(this.consumerId, this.businessActivityId, formValue); + return this.service.update(this.consumerId, this.businessActivityId, rest); } - return this.service.create(this.consumerId, formValue); + return this.service.create(this.consumerId, rest); } } diff --git a/src/app/domains/partner/modules/consumers/components/businessActivities/list.component.html b/src/app/domains/partner/modules/consumers/components/businessActivities/list.component.html index 0733330..a5e9c41 100644 --- a/src/app/domains/partner/modules/consumers/components/businessActivities/list.component.html +++ b/src/app/domains/partner/modules/consumers/components/businessActivities/list.component.html @@ -13,6 +13,7 @@ (onAdd)="openAddForm()" (onEdit)="onEditClick($event)" (onDetails)="toSinglePage($event)" + (onRefresh)="refresh()" > + @if (form.controls.pos_type.value === "PSP") { + + + + } + @if (!editMode) { + اطلاعات ورود } - @if (form.controls.pos_type.value === "PSP") { - - - - } diff --git a/src/app/domains/partner/modules/consumers/components/poses/form.component.ts b/src/app/domains/partner/modules/consumers/components/poses/form.component.ts index ad1ad08..3fba465 100644 --- a/src/app/domains/partner/modules/consumers/components/poses/form.component.ts +++ b/src/app/domains/partner/modules/consumers/components/poses/form.component.ts @@ -10,6 +10,7 @@ import { UikitFieldComponent } from '@/uikit'; import { Component, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; import { Dialog } from 'primeng/dialog'; +import { Divider } from 'primeng/divider'; import { Password } from 'primeng/password'; import { IPosRequest, IPosResponse } from '../../models'; import { PartnerPosesService } from '../../services/poses.service'; @@ -27,6 +28,7 @@ import { PartnerPosesService } from '../../services/poses.service'; EnumSelectComponent, UikitFieldComponent, Password, + Divider, ], }) export class ConsumerPosFormComponent extends AbstractFormDialog { diff --git a/src/app/domains/partner/modules/consumers/components/poses/list.component.html b/src/app/domains/partner/modules/consumers/components/poses/list.component.html index 621fb24..933ff65 100644 --- a/src/app/domains/partner/modules/consumers/components/poses/list.component.html +++ b/src/app/domains/partner/modules/consumers/components/poses/list.component.html @@ -2,17 +2,16 @@ pageTitle="مدیریت پایانه‌های فروش" [addNewCtaLabel]="'افزودن پایانه‌ی فروش جدید'" [columns]="columns" - [showAdd]="true" emptyPlaceholderTitle="پایانه‌ی فروشی یافت نشد." emptyPlaceholderDescription="برای افزودن پایانه‌ی فروش جدید، روی دکمهٔ بالا کلیک کنید." [items]="items()" [loading]="loading()" - [showDetails]="true" [showAdd]="true" [showEdit]="true" (onAdd)="openAddForm()" (onEdit)="onEditClick($event)" (onDetails)="toSinglePage($event)" + (onRefresh)="refresh()" > { @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: 'serial_number', header: 'شماره سریال' }, + // { field: 'device', header: 'دستگاه', type: 'nested', nestedOption: { path: 'device.name' } }, + { field: 'pos_type', header: 'نوع پایانه' }, { - field: 'provider', - header: 'ارایه‌دهنده', + field: 'account', + header: 'کاربر', type: 'nested', - nestedOption: { path: 'provider.name' }, - }, - { field: 'status', header: 'وضعیت' }, - { - field: 'created_at', - header: 'تاریخ ایجاد', - type: 'date', + nestedOption: { path: 'account.username' }, }, + // { + // field: 'provider', + // header: 'ارایه‌دهنده', + // type: 'nested', + // nestedOption: { path: 'provider.name' }, + // }, + // { field: 'status', header: 'وضعیت' }, + // { + // field: 'created_at', + // header: 'تاریخ ایجاد', + // type: 'date', + // }, ]; private readonly service = inject(PartnerPosesService); diff --git a/src/app/domains/partner/modules/consumers/models/businessActivities_io.d.ts b/src/app/domains/partner/modules/consumers/models/businessActivities_io.d.ts index 29c3708..9c590bf 100644 --- a/src/app/domains/partner/modules/consumers/models/businessActivities_io.d.ts +++ b/src/app/domains/partner/modules/consumers/models/businessActivities_io.d.ts @@ -14,6 +14,8 @@ export interface IBusinessActivityRequest { name: string; economic_code: string; guild_id: string; + username?: string; + password?: string; } interface LicenseInfo { diff --git a/src/app/domains/partner/modules/consumers/models/poses_io.d.ts b/src/app/domains/partner/modules/consumers/models/poses_io.d.ts index 894d903..b2598d9 100644 --- a/src/app/domains/partner/modules/consumers/models/poses_io.d.ts +++ b/src/app/domains/partner/modules/consumers/models/poses_io.d.ts @@ -10,6 +10,9 @@ export interface IPosRawResponse { complex: ISummary; device?: ISummary; provider?: ISummary; + account: { + username: string; + }; } export interface IPosResponse extends IPosRawResponse {} diff --git a/src/app/domains/partner/modules/consumers/views/list.component.html b/src/app/domains/partner/modules/consumers/views/list.component.html index fd238c8..d99e857 100644 --- a/src/app/domains/partner/modules/consumers/views/list.component.html +++ b/src/app/domains/partner/modules/consumers/views/list.component.html @@ -13,6 +13,7 @@ (onAdd)="openAddForm()" (onDetails)="toSinglePage($event)" (onEdit)="onEditClick($event)" + (onRefresh)="refresh()" /> { { field: 'id', header: 'شناسه', type: 'id' }, { field: 'fullname', header: 'نام' }, { field: 'mobile_number', header: 'شماره موبایل' }, - { field: 'status', header: 'وضعیت' }, - { - field: 'license_expires_at', - header: 'تاریخ انقضای لایسنس', - customDataModel(item) { - if (item.license_info) { - return formatJalali(item.license_info.expires_at); - } - return 'بدون لایسنس'; - }, - }, + { field: 'national_code', header: 'کد ملی' }, + // { field: 'status', header: 'وضعیت' }, + // { + // field: 'license_expires_at', + // header: 'تاریخ انقضای لایسنس', + // customDataModel(item) { + // if (item.license_info) { + // return formatJalali(item.license_info.expires_at); + // } + // return 'بدون لایسنس'; + // }, + // }, { field: 'created_at', header: 'تاریخ ایجاد', diff --git a/src/app/domains/partner/modules/customers/components/list.component.html b/src/app/domains/partner/modules/customers/components/list.component.html index db56c99..b759c74 100644 --- a/src/app/domains/partner/modules/customers/components/list.component.html +++ b/src/app/domains/partner/modules/customers/components/list.component.html @@ -8,4 +8,5 @@ [showDetails]="true" (onEdit)="onEditClick($event)" (onDetails)="toSinglePage($event)" + (onRefresh)="refresh()" /> diff --git a/src/app/domains/partner/modules/dashboard/store/main.store.ts b/src/app/domains/partner/modules/dashboard/store/main.store.ts new file mode 100644 index 0000000..ebbfb8e --- /dev/null +++ b/src/app/domains/partner/modules/dashboard/store/main.store.ts @@ -0,0 +1,42 @@ +import { defaultBaseStateData, EntityState, EntityStore } from '@/core/state'; +import { IPartnerInfoResponse } from '@/domains/partner/models'; +import { PartnerService } from '@/domains/partner/services/main.service'; +import { LayoutService } from '@/layout/service/layout.service'; +import { inject, Injectable } from '@angular/core'; +import { catchError, finalize } from 'rxjs'; + +interface PartnerInfoState extends EntityState {} + +@Injectable({ + providedIn: 'root', +}) +export class PartnerInfoStore extends EntityStore { + private readonly service = inject(PartnerService); + private readonly layoutService = inject(LayoutService); + + constructor() { + super(defaultBaseStateData); + } + + getData() { + this.patchState({ loading: true }); + this.service + .getInfo() + .pipe( + finalize(() => { + this.patchState({ loading: false }); + }), + catchError((error) => { + this.setError(error); + throw error; + }), + ) + .subscribe((entity) => { + this.setEntity(entity); + }); + } + + override reset(): void { + this.setState({ ...defaultBaseStateData }); + } +} diff --git a/src/app/domains/partner/modules/dashboard/views/index.component.ts b/src/app/domains/partner/modules/dashboard/views/index.component.ts index 8428f5a..4c4b7c8 100644 --- a/src/app/domains/partner/modules/dashboard/views/index.component.ts +++ b/src/app/domains/partner/modules/dashboard/views/index.component.ts @@ -1,7 +1,18 @@ -import { Component } from '@angular/core'; +import { Component, computed, inject } from '@angular/core'; +import { PartnerInfoStore } from '../store/main.store'; @Component({ selector: 'partner-dashboard', templateUrl: './index.component.html', }) -export class DashboardComponent {} +export class DashboardComponent { + private readonly store = inject(PartnerInfoStore); + + readonly loading = computed(() => this.store.loading()); + readonly entity = computed(() => this.store.entity()); + readonly error = computed(() => this.store.error()); + + ngOnInit() { + this.store.getData(); + } +} diff --git a/src/app/domains/partner/services/main.service.ts b/src/app/domains/partner/services/main.service.ts index 5e91166..209da8d 100644 --- a/src/app/domains/partner/services/main.service.ts +++ b/src/app/domains/partner/services/main.service.ts @@ -2,7 +2,12 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { PARTNER_API_ROUTES } from '../constants'; -import { IPartnerInfoRawResponse, IPartnerInfoResponse } from '../models'; +import { + IPartnerInfoRawResponse, + IPartnerInfoResponse, + IPartnerRawResponse, + IPartnerResponse, +} from '../models'; @Injectable({ providedIn: 'root' }) export class PartnerService { @@ -10,6 +15,10 @@ export class PartnerService { private apiRoutes = PARTNER_API_ROUTES; + getMe(): Observable { + return this.http.get(this.apiRoutes.me()); + } + getInfo(): Observable { return this.http.get(this.apiRoutes.info()); } diff --git a/src/app/domains/partner/store/main.store.ts b/src/app/domains/partner/store/main.store.ts index 0a41df2..1f3d736 100644 --- a/src/app/domains/partner/store/main.store.ts +++ b/src/app/domains/partner/store/main.store.ts @@ -2,15 +2,15 @@ import { EntityState, EntityStore } from '@/core/state'; import { LayoutService } from '@/layout/service/layout.service'; import { inject, Injectable } from '@angular/core'; import { catchError, finalize } from 'rxjs'; -import { IPartnerInfoResponse } from '../models'; +import { IPartnerResponse } from '../models'; import { PartnerService } from '../services/main.service'; -interface PartnerState extends EntityState {} +interface PartnerState extends EntityState {} @Injectable({ providedIn: 'root', }) -export class PartnerStore extends EntityStore { +export class PartnerStore extends EntityStore { private readonly service = inject(PartnerService); private readonly layoutService = inject(LayoutService); @@ -27,7 +27,7 @@ export class PartnerStore extends EntityStore { this.patchState({ loading: false }); @@ -39,7 +39,7 @@ export class PartnerStore extends EntityStore { this.layoutService.setPanelInfo({ - title: `پنل مدیریتی ${entity.name}`, + title: `پنل مدیریتی ${entity.partner.name}`, }); this.setEntity(entity); }); diff --git a/src/app/domains/superAdmin/modules/consumers/components/accounts/list.component.html b/src/app/domains/superAdmin/modules/consumers/components/accounts/list.component.html index 323355e..e6375a6 100644 --- a/src/app/domains/superAdmin/modules/consumers/components/accounts/list.component.html +++ b/src/app/domains/superAdmin/modules/consumers/components/accounts/list.component.html @@ -10,6 +10,7 @@ [showDetails]="true" [showAdd]="true" (onAdd)="openAddForm()" + (onRefresh)="refresh()" > diff --git a/src/app/domains/superAdmin/modules/licenses/views/list.component.html b/src/app/domains/superAdmin/modules/licenses/views/list.component.html index 6fe7c72..c6b966d 100644 --- a/src/app/domains/superAdmin/modules/licenses/views/list.component.html +++ b/src/app/domains/superAdmin/modules/licenses/views/list.component.html @@ -4,6 +4,7 @@ emptyPlaceholderTitle="لایسنسی یافت نشد" [items]="items()" [loading]="loading()" + (onRefresh)="refresh()" > diff --git a/src/app/pages/notfound/notfound.component.html b/src/app/pages/notfound/notfound.component.html index 17607f5..6f233bc 100644 --- a/src/app/pages/notfound/notfound.component.html +++ b/src/app/pages/notfound/notfound.component.html @@ -2,7 +2,7 @@
- مدیریت کسب‌وکار + مدیریت صورت‌حساب‌های مالیاتی
- @if (pageTitle || showAdd || filter || caption) { + @if (pageTitle || showAdd || filter || caption || showRefresh || moreActions) {
{{ pageTitle }}
- @if (showAdd || filter || moreActions) { + @if (showAdd || filter || showRefresh || moreActions) {
@if (filter) { @@ -31,6 +31,9 @@ (click)="openFilter()" > } + @if (showRefresh) { + + } @if (showAdd) { } diff --git a/src/app/shared/components/pageDataList/page-data-list.component.ts b/src/app/shared/components/pageDataList/page-data-list.component.ts index c8d3d2e..1555674 100644 --- a/src/app/shared/components/pageDataList/page-data-list.component.ts +++ b/src/app/shared/components/pageDataList/page-data-list.component.ts @@ -89,6 +89,7 @@ export class PageDataListComponent { @Input() showDelete: boolean = false; @Input() showDetails: boolean = false; @Input() showAdd: boolean = false; + @Input() showRefresh: boolean = true; @Input() isFiltered: boolean = false; @Input() fullHeight?: boolean = false; @Input() height: string = ''; @@ -108,6 +109,7 @@ export class PageDataListComponent { @Output() onAdd = new EventEmitter(); @Output() pageChange = new EventEmitter(); @Output() onThumbnailClick = new EventEmitter(); + @Output() onRefresh = new EventEmitter(); filterDrawerVisible = signal(false); expandedRows: { [key: string]: boolean } = {}; @@ -258,4 +260,8 @@ export class PageDataListComponent { if (this.showDetails) totalCount += 1; return totalCount; }); + + refresh() { + this.onRefresh.emit(); + } } diff --git a/src/index.html b/src/index.html index 0002c06..17efe0d 100644 --- a/src/index.html +++ b/src/index.html @@ -2,7 +2,7 @@ - pos + پنل مدیریت صورت‌حساب‌های مالیاتی