init to partner panel

This commit is contained in:
2026-04-13 15:47:50 +03:30
parent af3123e61e
commit 9eb5c35285
45 changed files with 806 additions and 12 deletions
@@ -5,7 +5,7 @@ export const CONSUMER_MENU_ITEMS = [
{ {
label: 'داشبورد', label: 'داشبورد',
icon: 'pi pi-fw pi-home', icon: 'pi pi-fw pi-home',
routerLink: ['/'], routerLink: ['/consumer'],
}, },
{ {
label: 'حساب‌های کاربری', label: 'حساب‌های کاربری',
@@ -0,0 +1,5 @@
const baseUrl = '/api/v1/partner';
export const PARTNER_API_ROUTES = {
info: () => `${baseUrl}`,
};
@@ -1 +1,2 @@
export * from './apiRoutes';
export * from './menuItems.const'; export * from './menuItems.const';
@@ -5,7 +5,17 @@ export const PARTNER_MENU_ITEMS = [
{ {
label: 'داشبورد', label: 'داشبورد',
icon: 'pi pi-fw pi-home', icon: 'pi pi-fw pi-home',
routerLink: ['/'], routerLink: ['/partner'],
},
{
label: 'حساب‌های کاربری',
icon: 'pi pi-fw pi-home',
routerLink: ['/partner/accounts'],
},
{
label: 'مشتریان',
icon: 'pi pi-fw pi-home',
routerLink: ['/partner/customers'],
}, },
], ],
}, },
@@ -1 +1,5 @@
@if (loading()) {
<shared-page-loading />
} @else {
<router-outlet></router-outlet> <router-outlet></router-outlet>
}
@@ -1,17 +1,25 @@
import { LayoutService } from '@/layout/service/layout.service'; import { LayoutService } from '@/layout/service/layout.service';
import { Component, inject } from '@angular/core'; import { PageLoadingComponent } from '@/shared/components/page-loading.component';
import { Component, computed, inject, TemplateRef, ViewChild } from '@angular/core';
import { RouterOutlet } from '@angular/router'; import { RouterOutlet } from '@angular/router';
import { PARTNER_MENU_ITEMS } from '../constants'; import { PARTNER_MENU_ITEMS } from '../constants';
import { PartnerStore } from '../store/main.store';
@Component({ @Component({
selector: 'partner-layout', selector: 'partner-layout',
templateUrl: './layout.component.html', templateUrl: './layout.component.html',
imports: [RouterOutlet], imports: [RouterOutlet, PageLoadingComponent],
}) })
export class LayoutComponent { export class LayoutComponent {
@ViewChild('topBarMoreAction') topBarMoreAction!: TemplateRef<any>;
private readonly layoutService = inject(LayoutService); private readonly layoutService = inject(LayoutService);
private readonly store = inject(PartnerStore);
readonly loading = computed(() => this.store.loading());
ngOnInit() { ngOnInit() {
this.layoutService.setMenuItems(PARTNER_MENU_ITEMS); this.layoutService.setMenuItems(PARTNER_MENU_ITEMS);
this.store.getData();
} }
} }
+1
View File
@@ -0,0 +1 @@
export * from './io';
+5
View File
@@ -0,0 +1,5 @@
export interface IPartnerInfoRawResponse {
id: string;
name: string;
}
export interface IPartnerInfoResponse extends IPartnerInfoRawResponse {}
@@ -0,0 +1,41 @@
<p-dialog
header="ویرایش گذرواژه"
[(visible)]="visible"
[modal]="true"
[style]="{ width: '500px' }"
[closable]="true"
(onHide)="close()"
>
<form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4">
<uikit-field label="رمز عبور" class="" [control]="form.controls.password">
<p-password
id="password1"
name="password"
formControlName="password"
autocomplete="password"
[toggleMask]="true"
[fluid]="true"
[feedback]="false"
[invalid]="form.controls.password.touched && form.controls.password.invalid"
/>
<span class="text-xs mt-1 text-muted-color">
رمز باید حداقل ۸ کاراکتر بوده و شامل حداقل یک حرف بزرگ، یک حرف کوچک، یک عدد و یک کاراکتر ویژه باشد.
</span>
</uikit-field>
<uikit-field label="تکرار رمز عبور" class="" [control]="form.controls.confirmPassword">
<p-password
id="confirmPassword"
name="confirmPassword"
formControlName="confirmPassword"
autocomplete="new-password"
[toggleMask]="true"
[fluid]="true"
[feedback]="false"
[invalid]="form.controls.confirmPassword.touched && form.controls.confirmPassword.invalid"
/>
</uikit-field>
<app-form-footer-actions [submitLabel]="'ذخیره'" [loading]="submitLoading()" (onCancel)="close()" />
</form>
</p-dialog>
@@ -0,0 +1,40 @@
import { MustMatch, password } from '@/core/validators';
import { AbstractFormDialog } from '@/shared/abstractClasses';
import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component';
import { UikitFieldComponent } from '@/uikit';
import { Component, inject, Input } from '@angular/core';
import { ReactiveFormsModule, Validators } from '@angular/forms';
import { Dialog } from 'primeng/dialog';
import { Password } from 'primeng/password';
import { IAccountRequest, IAccountResponse } from '../models';
import { AccountsService } from '../services/main.service';
@Component({
selector: 'account-password-form',
templateUrl: './form.component.html',
imports: [ReactiveFormsModule, Dialog, FormFooterActionsComponent, UikitFieldComponent, Password],
})
export class PartnerAccountPasswordFormComponent extends AbstractFormDialog<
IAccountRequest,
IAccountResponse
> {
@Input({ required: true }) accountId!: string;
private service = inject(AccountsService);
form = this.fb.group(
{
password: ['', [Validators.required, password()]],
confirmPassword: ['', [Validators.required]],
},
{
validators: [MustMatch('password', 'confirmPassword')],
},
);
override submitForm(payload: IAccountRequest) {
return this.service.update(this.accountId, payload);
// @ts-ignore
// const { confirmPassword, ...rest } = payload;
// return this.service.create(rest);
}
}
@@ -0,0 +1,5 @@
@if (loading()) {
<shared-page-loading />
} @else {
<router-outlet></router-outlet>
}
@@ -0,0 +1,26 @@
import { PageLoadingComponent } from '@/shared/components/page-loading.component';
import { Component, computed, inject, signal } from '@angular/core';
import { ActivatedRoute, RouterOutlet } from '@angular/router';
import { AccountStore } from '../store/account.store';
@Component({
selector: 'partner-account-layout',
templateUrl: './layout.component.html',
imports: [PageLoadingComponent, RouterOutlet],
})
export class PartnerAccountLayoutComponent {
private readonly route = inject(ActivatedRoute);
private readonly store = inject(AccountStore);
readonly accountId = signal<string>(this.route.snapshot.paramMap.get('accountId')!);
readonly loading = computed(() => this.store.loading());
getData() {
this.store.getData(this.accountId());
}
ngOnInit() {
this.getData();
}
}
@@ -0,0 +1,6 @@
const baseUrl = '/api/v1/partner/accounts';
export const PARTNER_ACCOUNTS_API_ROUTES = {
list: () => `${baseUrl}`,
single: (id: string) => `${baseUrl}/${id}`,
};
@@ -0,0 +1,2 @@
export * from './apiRoutes';
export * from './routes';
@@ -0,0 +1,40 @@
import { NamedRoutes } from '@/core';
import { Routes } from '@angular/router';
export type TPartnerAccountsRouteNames = 'accounts' | 'account';
export const partnerAccountsNamedRoutes: NamedRoutes<TPartnerAccountsRouteNames> = {
accounts: {
path: 'accounts',
loadComponent: () =>
import('../../views/list.component').then((m) => m.PartnerAccountsComponent),
meta: {
title: 'حساب‌های کاربری',
pagePath: () => '/partner/accounts',
},
},
account: {
path: 'accounts/:accountId',
loadComponent: () =>
import('../../views/single.component').then((m) => m.PartnerAccountComponent),
meta: {
title: 'حساب کاربری',
pagePath: (accountId: string) => `/partner/accounts/${accountId}`,
},
},
};
export const PARTNER_ACCOUNTS_ROUTES: Routes = [
partnerAccountsNamedRoutes.accounts,
{
path: partnerAccountsNamedRoutes.account.path,
loadComponent: () =>
import('../../components/layout.component').then((m) => m.PartnerAccountLayoutComponent),
children: [
{
...partnerAccountsNamedRoutes.account,
path: '',
},
],
},
];
@@ -0,0 +1 @@
export * from './io';
+20
View File
@@ -0,0 +1,20 @@
export interface IAccountRawResponse {
id: string;
role: string;
created_at: string;
account: Account;
}
export interface IAccountResponse extends IAccountRawResponse {}
export interface IAccountRequest {
name: string;
}
export interface IAccountPasswordRequest {
password: string;
}
interface Account {
username: string;
status: string;
}
@@ -0,0 +1,24 @@
import { IPaginatedResponse } from '@/core/models/service.model';
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { PARTNER_ACCOUNTS_API_ROUTES } from '../constants';
import { IAccountRawResponse, IAccountRequest, IAccountResponse } from '../models';
@Injectable({ providedIn: 'root' })
export class AccountsService {
constructor(private http: HttpClient) {}
private apiRoutes = PARTNER_ACCOUNTS_API_ROUTES;
getAll(): Observable<IPaginatedResponse<IAccountResponse>> {
return this.http.get<IPaginatedResponse<IAccountRawResponse>>(this.apiRoutes.list());
}
getSingle(accountId: string): Observable<IAccountResponse> {
return this.http.get<IAccountRawResponse>(this.apiRoutes.single(accountId));
}
update(accountId: string, userData: IAccountRequest): Observable<IAccountResponse> {
return this.http.patch<IAccountResponse>(this.apiRoutes.single(accountId), userData);
}
}
@@ -0,0 +1,67 @@
import { defaultBaseStateData, EntityState, EntityStore } from '@/core/state';
import { computed, inject, Injectable } from '@angular/core';
import { MenuItem } from 'primeng/api';
import { catchError, finalize } from 'rxjs';
import { partnerAccountsNamedRoutes } from '../constants';
import { IAccountResponse } from '../models';
import { AccountsService } from '../services/main.service';
interface AccountState extends EntityState<IAccountResponse> {
breadcrumbItems: MenuItem[];
}
@Injectable({
providedIn: 'root',
})
export class AccountStore extends EntityStore<IAccountResponse, AccountState> {
private readonly service = inject(AccountsService);
constructor() {
super({
...defaultBaseStateData,
breadcrumbItems: [],
});
}
breadcrumbItems = computed(() => this._state().breadcrumbItems);
private setBreadcrumb(accountId: string) {
this.patchState({
breadcrumbItems: [
{
...partnerAccountsNamedRoutes.accounts.meta,
routerLink: partnerAccountsNamedRoutes.accounts.meta.pagePath!(),
},
{
title: this.entity()?.account.username,
routerLink: partnerAccountsNamedRoutes.account.meta.pagePath!(accountId),
},
],
});
}
getData(accountId: string) {
this.patchState({ loading: true });
this.service
.getSingle(accountId)
.pipe(
finalize(() => {
this.patchState({ loading: false });
}),
catchError((error) => {
this.setError(error);
throw error;
}),
)
.subscribe((entity) => {
this.patchState({ entity });
this.setBreadcrumb(accountId);
});
}
override reset(): void {
this.setState({
...defaultBaseStateData,
breadcrumbItems: [],
});
}
}
@@ -0,0 +1,2 @@
export * from './list.component';
export * from './single.component';
@@ -0,0 +1,19 @@
<app-page-data-list
[pageTitle]="'لیست حساب‌های کاربری'"
[columns]="columns"
emptyPlaceholderTitle="حساب‌ کاربری‌ای یافت نشد"
[items]="items()"
[showDetails]="true"
[loading]="loading()"
(onDetails)="toSinglePage($event)"
/>
@if (selectedItemForEdit()) {
<account-password-form
[(visible)]="visibleForm"
[editMode]="editMode()"
[accountId]="selectedItemForEdit()?.id!"
[initialValues]="selectedItemForEdit() || undefined"
(onSubmit)="refresh()"
/>
}
@@ -0,0 +1,41 @@
// import { CatalogRoleTagComponent } from '@/shared/catalog/roles';
import { AbstractList } from '@/shared/abstractClasses/abstract-list';
import { PageDataListComponent } from '@/shared/components/pageDataList/page-data-list.component';
import { Component, inject } from '@angular/core';
import { Router } from '@angular/router';
import { PartnerAccountPasswordFormComponent } from '../components/form.component';
import { partnerAccountsNamedRoutes } from '../constants';
import { IAccountResponse } from '../models';
import { AccountsService } from '../services/main.service';
@Component({
selector: 'partner-accounts',
templateUrl: './list.component.html',
imports: [PageDataListComponent, PartnerAccountPasswordFormComponent],
})
export class PartnerAccountsComponent extends AbstractList<IAccountResponse> {
private readonly service = inject(AccountsService);
private readonly router = inject(Router);
override setColumns(): void {
this.columns = [
{ field: 'id', header: 'شناسه', type: 'id' },
{ field: 'username', header: 'نام کاربری', type: 'nested', nestedPath: 'account.username' },
{ field: 'role', header: 'نقش' },
{ field: 'status', header: 'وضعیت', type: 'nested', nestedPath: 'account.status' },
{
field: 'created_at',
header: 'تاریخ ایجاد',
type: 'date',
},
];
}
override getDataRequest() {
return this.service.getAll();
}
toSinglePage(account: IAccountResponse) {
this.router.navigateByUrl(partnerAccountsNamedRoutes.account.meta.pagePath!(account.id));
}
}
@@ -0,0 +1,11 @@
<div class="flex flex-col gap-6">
<app-card-data cardTitle="اطلاعات کاربری" [editable]="false" [(editMode)]="editMode">
<div class="flex flex-col gap-4">
<div class="grid grid-cols-3 gap-4 items-center">
<app-key-value label="نام کاربری" [value]="account()?.account?.username" />
<app-key-value label="نقش" [value]="account()?.role" />
<app-key-value label="وضعیت" [value]="account()?.account?.status" />
</div>
</div>
</app-card-data>
</div>
@@ -0,0 +1,39 @@
import { BreadcrumbService } from '@/core/services';
import { AppCardComponent, KeyValueComponent } from '@/shared/components';
import { Component, computed, effect, inject, signal } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { AccountStore } from '../store/account.store';
@Component({
selector: 'partner-account',
templateUrl: './single.component.html',
imports: [AppCardComponent, KeyValueComponent],
})
export class PartnerAccountComponent {
private readonly store = inject(AccountStore);
private readonly route = inject(ActivatedRoute);
private readonly breadcrumbService = inject(BreadcrumbService);
readonly accountId = signal<string>(this.route.snapshot.paramMap.get('accountId')!);
editMode = signal<boolean>(false);
readonly loading = computed(() => this.store.loading());
readonly account = computed(() => this.store.entity());
readonly accountBreadcrumb = computed(() => this.store.breadcrumbItems());
constructor() {
effect(() => {
if (this.account()?.id) {
this.setBreadcrumb();
}
});
}
getData() {
this.store.getData(this.accountId());
}
setBreadcrumb() {
this.breadcrumbService.setItems(this.accountBreadcrumb());
}
}
@@ -0,0 +1,5 @@
@if (loading()) {
<shared-page-loading />
} @else {
<router-outlet></router-outlet>
}
@@ -0,0 +1,26 @@
import { PageLoadingComponent } from '@/shared/components/page-loading.component';
import { Component, computed, inject, signal } from '@angular/core';
import { ActivatedRoute, RouterOutlet } from '@angular/router';
import { PartnerCustomerStore } from '../store/customer.store';
@Component({
selector: 'partner-customer-layout',
templateUrl: './layout.component.html',
imports: [PageLoadingComponent, RouterOutlet],
})
export class PartnerCustomerLayoutComponent {
private readonly route = inject(ActivatedRoute);
private readonly store = inject(PartnerCustomerStore);
readonly customerId = signal<string>(this.route.snapshot.paramMap.get('customerId')!);
readonly loading = computed(() => this.store.loading());
getData() {
this.store.getData(this.customerId());
}
ngOnInit() {
this.getData();
}
}
@@ -0,0 +1,11 @@
<app-page-data-list
[pageTitle]="'لیست مشتریان'"
[columns]="columns"
emptyPlaceholderTitle="مشتری‌ای یافت نشد"
[items]="items()"
[loading]="loading()"
[showEdit]="true"
[showDetails]="true"
(onEdit)="onEditClick($event)"
(onDetails)="toSinglePage($event)"
/>
@@ -0,0 +1,48 @@
// import { CatalogRoleTagComponent } from '@/shared/catalog/roles';
import { AbstractList } from '@/shared/abstractClasses/abstract-list';
import {
IColumn,
PageDataListComponent,
} from '@/shared/components/pageDataList/page-data-list.component';
import { Component, inject, Input } from '@angular/core';
import { Router } from '@angular/router';
import { partnerCustomersNamedRoutes } from '../constants';
import { ICustomerResponse } from '../models';
import { CustomersService } from '../services/main.service';
@Component({
selector: 'partner-customer-list',
templateUrl: './list.component.html',
imports: [PageDataListComponent],
})
export class PartnerCustomerListComponent extends AbstractList<ICustomerResponse> {
@Input() fullHeight?: boolean;
@Input() header: IColumn[] = [
{ field: 'id', header: 'شناسه', type: 'id' },
{
field: 'first_name',
header: 'عنوان',
},
{
field: 'created_at',
header: 'تاریخ ایجاد',
type: 'date',
},
];
private readonly service = inject(CustomersService);
private readonly router = inject(Router);
override setColumns(): void {
this.columns = this.header;
}
override getDataRequest() {
return this.service.getAll();
}
toSinglePage(item: ICustomerResponse) {
this.router.navigateByUrl(partnerCustomersNamedRoutes.customer.meta.pagePath!(item.id));
}
}
@@ -0,0 +1,6 @@
const baseUrl = '/api/v1/partner/customers';
export const PARTNER_CUSTOMERS_API_ROUTES = {
list: () => `${baseUrl}`,
single: (id: string) => `${baseUrl}/${id}`,
};
@@ -0,0 +1,2 @@
export * from './apiRoutes';
export * from './routes';
@@ -0,0 +1,40 @@
import { NamedRoutes } from '@/core';
import { Routes } from '@angular/router';
export type TPartnerCustomersRouteNames = 'customers' | 'customer';
export const partnerCustomersNamedRoutes: NamedRoutes<TPartnerCustomersRouteNames> = {
customers: {
path: 'customers',
loadComponent: () =>
import('../../views/list.component').then((m) => m.PartnerCustomersComponent),
meta: {
title: 'مشتریان',
pagePath: () => 'partner/customers',
},
},
customer: {
path: 'customers/:customerId',
loadComponent: () =>
import('../../views/single.component').then((m) => m.PartnerCustomerComponent),
meta: {
title: 'مشتری',
pagePath: (customerId: string) => `partner/customers/${customerId}`,
},
},
};
export const PARTNER_CUSTOMERS_ROUTES: Routes = [
partnerCustomersNamedRoutes.customers,
{
path: partnerCustomersNamedRoutes.customer.path,
loadComponent: () =>
import('../../components/layout.component').then((m) => m.PartnerCustomerLayoutComponent),
children: [
{
...partnerCustomersNamedRoutes.customer,
path: '',
},
],
},
];
@@ -0,0 +1 @@
export * from './io';
@@ -0,0 +1,8 @@
export interface ICustomerRawResponse {
id: string;
first_name: string;
last_name: string;
}
export interface ICustomerResponse extends ICustomerRawResponse {}
export interface ICustomerRequest {}
@@ -0,0 +1,20 @@
import { IPaginatedResponse } from '@/core/models/service.model';
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { PARTNER_CUSTOMERS_API_ROUTES } from '../constants';
import { ICustomerRawResponse, ICustomerResponse } from '../models';
@Injectable({ providedIn: 'root' })
export class CustomersService {
constructor(private http: HttpClient) {}
private apiRoutes = PARTNER_CUSTOMERS_API_ROUTES;
getAll(): Observable<IPaginatedResponse<ICustomerResponse>> {
return this.http.get<IPaginatedResponse<ICustomerRawResponse>>(this.apiRoutes.list());
}
getSingle(customerId: string): Observable<ICustomerResponse> {
return this.http.get<ICustomerRawResponse>(this.apiRoutes.single(customerId));
}
}
@@ -0,0 +1,75 @@
import { EntityState, EntityStore } from '@/core/state';
import { computed, inject, Injectable } from '@angular/core';
import { MenuItem } from 'primeng/api';
import { catchError, finalize } from 'rxjs';
import { partnerCustomersNamedRoutes } from '../constants';
import { ICustomerResponse } from '../models';
import { CustomersService } from '../services/main.service';
interface PartnerCustomerState extends EntityState<ICustomerResponse> {
breadcrumbItems: MenuItem[];
}
@Injectable({
providedIn: 'root',
})
export class PartnerCustomerStore extends EntityStore<ICustomerResponse, PartnerCustomerState> {
private readonly service = inject(CustomersService);
constructor() {
super({
loading: false,
error: null,
entity: null,
initialized: false,
isRefreshing: false,
breadcrumbItems: [],
});
}
breadcrumbItems = computed(() => this._state().breadcrumbItems);
private setBreadcrumb(customerId: string) {
this.patchState({
breadcrumbItems: [
{
...partnerCustomersNamedRoutes.customers.meta,
routerLink: partnerCustomersNamedRoutes.customers.meta.pagePath!(),
},
{
title: this.entity()?.first_name,
routerLink: partnerCustomersNamedRoutes.customer.meta.pagePath!(customerId),
},
],
});
}
getData(customerId: string) {
this.patchState({ loading: true });
this.service
.getSingle(customerId)
.pipe(
finalize(() => {
this.patchState({ loading: false });
}),
catchError((error) => {
this.setError(error);
throw error;
}),
)
.subscribe((entity) => {
this.patchState({ entity });
this.setBreadcrumb(customerId);
});
}
override reset(): void {
this.setState({
loading: false,
error: null,
entity: null,
initialized: false,
isRefreshing: false,
breadcrumbItems: [],
});
}
}
@@ -0,0 +1,2 @@
export * from './list.component';
export * from './single.component';
@@ -0,0 +1 @@
<partner-customer-list />
@@ -0,0 +1,10 @@
// import { CatalogRoleTagComponent } from '@/shared/catalog/roles';
import { Component } from '@angular/core';
import { PartnerCustomerListComponent } from '../components/list.component';
@Component({
selector: 'partner-customers',
templateUrl: './list.component.html',
imports: [PartnerCustomerListComponent],
})
export class PartnerCustomersComponent {}
@@ -0,0 +1,9 @@
<div class="flex flex-col gap-6">
<app-card-data cardTitle="اطلاعات مشتری" [editable]="true" [(editMode)]="editMode">
<div class="flex flex-col gap-4">
<div class="grid grid-cols-3 gap-4 items-center">
<app-key-value label="عنوان" [value]="customer()?.first_name" />
</div>
</div>
</app-card-data>
</div>
@@ -0,0 +1,39 @@
import { BreadcrumbService } from '@/core/services';
import { AppCardComponent, KeyValueComponent } from '@/shared/components';
import { Component, computed, effect, inject, signal } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { PartnerCustomerStore } from '../store/customer.store';
@Component({
selector: 'partner-customer',
templateUrl: './single.component.html',
imports: [AppCardComponent, KeyValueComponent],
})
export class PartnerCustomerComponent {
private readonly store = inject(PartnerCustomerStore);
private readonly route = inject(ActivatedRoute);
private readonly breadcrumbService = inject(BreadcrumbService);
readonly customerId = signal<string>(this.route.snapshot.paramMap.get('customerId')!);
editMode = signal<boolean>(false);
readonly loading = computed(() => this.store.loading());
readonly customer = computed(() => this.store.entity());
readonly customerBreadcrumb = computed(() => this.store.breadcrumbItems());
constructor() {
effect(() => {
if (this.customer()?.id) {
this.setBreadcrumb();
}
});
}
getData() {
this.store.getData(this.customerId());
}
setBreadcrumb() {
this.breadcrumbService.setItems(this.customerBreadcrumb());
}
}
@@ -1,3 +1 @@
<div class="flex items-center justify-center h-svh w-svw"> <div class="grid grid-cols-2 gap-10">به پنل مدیریتی شریک تجاری خوش آمدید.</div>
<span class="text-center"> به پنل کسب و کار خوش آمدید </span>
</div>
@@ -4,6 +4,4 @@ import { Component } from '@angular/core';
selector: 'partner-dashboard', selector: 'partner-dashboard',
templateUrl: './index.component.html', templateUrl: './index.component.html',
}) })
export class DashboardComponent { export class DashboardComponent {}
constructor() {}
}
+5 -1
View File
@@ -1,13 +1,17 @@
import { Route } from '@angular/router'; import { Route } from '@angular/router';
import { PARTNER_ACCOUNTS_ROUTES } from './modules/accounts/constants';
import { PARTNER_CUSTOMERS_ROUTES } from './modules/customers/constants';
export const PARTNER_ROUTES = { export const PARTNER_ROUTES = {
path: 'partner', path: 'partner',
component: undefined, loadComponent: () => import('./layouts/layout.component').then((m) => m.LayoutComponent),
children: [ children: [
{ {
path: '', path: '',
loadComponent: () => loadComponent: () =>
import('./modules/dashboard/views/index.component').then((m) => m.DashboardComponent), import('./modules/dashboard/views/index.component').then((m) => m.DashboardComponent),
}, },
...PARTNER_ACCOUNTS_ROUTES,
...PARTNER_CUSTOMERS_ROUTES,
], ],
} as Route; } as Route;
@@ -0,0 +1,16 @@
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';
@Injectable({ providedIn: 'root' })
export class PartnerService {
constructor(private http: HttpClient) {}
private apiRoutes = PARTNER_API_ROUTES;
getInfo(): Observable<IPartnerInfoResponse> {
return this.http.get<IPartnerInfoRawResponse>(this.apiRoutes.info());
}
}
@@ -0,0 +1,57 @@
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 { PartnerService } from '../services/main.service';
interface PartnerState extends EntityState<IPartnerInfoResponse> {}
@Injectable({
providedIn: 'root',
})
export class PartnerStore extends EntityStore<IPartnerInfoResponse, PartnerState> {
private readonly service = inject(PartnerService);
private readonly layoutService = inject(LayoutService);
constructor() {
super({
loading: false,
error: null,
entity: null,
initialized: false,
isRefreshing: false,
});
}
getData() {
this.patchState({ loading: true });
this.service
.getInfo()
.pipe(
finalize(() => {
this.patchState({ loading: false });
}),
catchError((error) => {
this.setError(error);
throw error;
}),
)
.subscribe((entity) => {
this.layoutService.setPanelInfo({
title: `پنل مدیریتی ${entity.name}`,
});
this.setEntity(entity);
});
}
override reset(): void {
this.setState({
loading: false,
error: null,
entity: null,
initialized: false,
isRefreshing: false,
});
}
}