Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c135e1a85f | |||
| 79c00e0149 |
@@ -197,11 +197,7 @@ export abstract class EntityStore<
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
// @TODO: check to familiar with ts
|
// @TODO: check to familiar with ts
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-2
@@ -1,9 +1,31 @@
|
|||||||
|
import { IEnumTranslate } from '@/shared/models/enum_translate.type';
|
||||||
|
|
||||||
export interface IConsumerInfoRawResponse {
|
export interface IConsumerInfoRawResponse {
|
||||||
id: string;
|
id: string;
|
||||||
|
partner: Partner;
|
||||||
|
type: IEnumTranslate<'INDIVIDUAL' | 'LEGAL'>;
|
||||||
|
status: IEnumTranslate<'ACTIVE' | 'INACTIVE'>;
|
||||||
|
legal?: Legal;
|
||||||
|
individual?: Individual;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
export interface IConsumerInfoResponse extends IConsumerInfoRawResponse {}
|
||||||
|
|
||||||
|
interface Individual {
|
||||||
first_name: string;
|
first_name: string;
|
||||||
last_name: string;
|
last_name: string;
|
||||||
mobile_number: string;
|
mobile_number: string;
|
||||||
status: string;
|
national_code: string;
|
||||||
fullname: string;
|
fullname: string;
|
||||||
}
|
}
|
||||||
export interface IConsumerInfoResponse extends IConsumerInfoRawResponse {}
|
|
||||||
|
interface Legal {
|
||||||
|
company_name: string;
|
||||||
|
registration_number: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Partner {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
code: string;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EntityState, EntityStore } from '@/core/state';
|
import { defaultBaseStateData, EntityState, EntityStore } from '@/core/state';
|
||||||
import { computed, inject, Injectable } from '@angular/core';
|
import { computed, inject, Injectable } from '@angular/core';
|
||||||
import { MenuItem } from 'primeng/api';
|
import { MenuItem } from 'primeng/api';
|
||||||
import { catchError, finalize } from 'rxjs';
|
import { catchError, finalize } from 'rxjs';
|
||||||
@@ -17,11 +17,7 @@ export class AccountStore extends EntityStore<IAccountResponse, AccountState> {
|
|||||||
private readonly service = inject(AccountsService);
|
private readonly service = inject(AccountsService);
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -64,11 +60,7 @@ export class AccountStore extends EntityStore<IAccountResponse, AccountState> {
|
|||||||
|
|
||||||
override reset(): void {
|
override reset(): void {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-11
@@ -1,4 +1,4 @@
|
|||||||
import { EntityState, EntityStore } from '@/core/state';
|
import { defaultBaseStateData, EntityState, EntityStore } from '@/core/state';
|
||||||
import { computed, inject, Injectable } from '@angular/core';
|
import { computed, inject, Injectable } from '@angular/core';
|
||||||
import { MenuItem } from 'primeng/api';
|
import { MenuItem } from 'primeng/api';
|
||||||
import { catchError, finalize } from 'rxjs';
|
import { catchError, finalize } from 'rxjs';
|
||||||
@@ -20,11 +20,7 @@ export class BusinessActivityStore extends EntityStore<
|
|||||||
private readonly service = inject(BusinessActivitiesService);
|
private readonly service = inject(BusinessActivitiesService);
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -68,11 +64,7 @@ export class BusinessActivityStore extends EntityStore<
|
|||||||
|
|
||||||
override reset(): void {
|
override reset(): void {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EntityState, EntityStore } from '@/core/state';
|
import { defaultBaseStateData, EntityState, EntityStore } from '@/core/state';
|
||||||
import { computed, inject, Injectable } from '@angular/core';
|
import { computed, inject, Injectable } from '@angular/core';
|
||||||
import { MenuItem } from 'primeng/api';
|
import { MenuItem } from 'primeng/api';
|
||||||
import { catchError, finalize } from 'rxjs';
|
import { catchError, finalize } from 'rxjs';
|
||||||
@@ -17,11 +17,7 @@ export class ConsumerComplexStore extends EntityStore<IComplexResponse, ComplexS
|
|||||||
private readonly service = inject(ConsumerComplexesService);
|
private readonly service = inject(ConsumerComplexesService);
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -63,11 +59,7 @@ export class ConsumerComplexStore extends EntityStore<IComplexResponse, ComplexS
|
|||||||
|
|
||||||
override reset(): void {
|
override reset(): void {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EntityState, EntityStore } from '@/core/state';
|
import { defaultBaseStateData, EntityState, EntityStore } from '@/core/state';
|
||||||
import { computed, inject, Injectable } from '@angular/core';
|
import { computed, inject, Injectable } from '@angular/core';
|
||||||
import { MenuItem } from 'primeng/api';
|
import { MenuItem } from 'primeng/api';
|
||||||
import { catchError, finalize } from 'rxjs';
|
import { catchError, finalize } from 'rxjs';
|
||||||
@@ -17,11 +17,7 @@ export class PosStore extends EntityStore<IPosResponse, PosState> {
|
|||||||
private readonly service = inject(ConsumerPosesService);
|
private readonly service = inject(ConsumerPosesService);
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -64,11 +60,7 @@ export class PosStore extends EntityStore<IPosResponse, PosState> {
|
|||||||
|
|
||||||
override reset(): void {
|
override reset(): void {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EntityState, EntityStore } from '@/core/state';
|
import { defaultBaseStateData, EntityState, EntityStore } from '@/core/state';
|
||||||
import { computed, inject, Injectable } from '@angular/core';
|
import { computed, inject, Injectable } from '@angular/core';
|
||||||
import { MenuItem } from 'primeng/api';
|
import { MenuItem } from 'primeng/api';
|
||||||
import { catchError, finalize } from 'rxjs';
|
import { catchError, finalize } from 'rxjs';
|
||||||
@@ -17,11 +17,7 @@ export class ConsumerCustomerStore extends EntityStore<ICustomerResponse, Consum
|
|||||||
private readonly service = inject(CustomersService);
|
private readonly service = inject(CustomersService);
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -67,11 +63,7 @@ export class ConsumerCustomerStore extends EntityStore<ICustomerResponse, Consum
|
|||||||
|
|
||||||
override reset(): void {
|
override reset(): void {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EntityState, EntityStore } from '@/core/state';
|
import { defaultBaseStateData, EntityState, EntityStore } from '@/core/state';
|
||||||
import { computed, inject, Injectable } from '@angular/core';
|
import { computed, inject, Injectable } from '@angular/core';
|
||||||
import { MenuItem } from 'primeng/api';
|
import { MenuItem } from 'primeng/api';
|
||||||
import { catchError, finalize } from 'rxjs';
|
import { catchError, finalize } from 'rxjs';
|
||||||
@@ -20,11 +20,7 @@ export class ConsumerCustomerSaleInvoiceStore extends EntityStore<
|
|||||||
private readonly service = inject(CustomerSaleInvoicesService);
|
private readonly service = inject(CustomerSaleInvoicesService);
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -71,11 +67,7 @@ export class ConsumerCustomerSaleInvoiceStore extends EntityStore<
|
|||||||
|
|
||||||
override reset(): void {
|
override reset(): void {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EntityState, EntityStore } from '@/core/state';
|
import { defaultBaseStateData, EntityState, EntityStore } from '@/core/state';
|
||||||
import { computed, inject, Injectable } from '@angular/core';
|
import { computed, inject, Injectable } from '@angular/core';
|
||||||
import { MenuItem } from 'primeng/api';
|
import { MenuItem } from 'primeng/api';
|
||||||
import { catchError, finalize } from 'rxjs';
|
import { catchError, finalize } from 'rxjs';
|
||||||
@@ -17,11 +17,7 @@ export class ConsumerPosStore extends EntityStore<IPosResponse, ConsumerPosState
|
|||||||
private readonly service = inject(ConsumerPosesService);
|
private readonly service = inject(ConsumerPosesService);
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -64,11 +60,7 @@ export class ConsumerPosStore extends EntityStore<IPosResponse, ConsumerPosState
|
|||||||
|
|
||||||
override reset(): void {
|
override reset(): void {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<shared-dialog
|
||||||
|
header="ویرایش اطلاعات پروفایل"
|
||||||
|
[(visible)]="visible"
|
||||||
|
[modal]="true"
|
||||||
|
[style]="{ width: '500px' }"
|
||||||
|
[closable]="true"
|
||||||
|
(onHide)="close()"
|
||||||
|
>
|
||||||
|
<form [formGroup]="form" (submit)="submit()">
|
||||||
|
@if (profileType === "LEGAL") {
|
||||||
|
<field-company-name [control]="form.controls.company_name" />
|
||||||
|
<field-registration-number [control]="form.controls.registration_number" />
|
||||||
|
} @else {
|
||||||
|
<field-first-name [control]="form.controls.first_name" />
|
||||||
|
<field-last-name [control]="form.controls.last_name" />
|
||||||
|
<field-mobile-number [control]="form.controls.mobile_number" />
|
||||||
|
<field-national-code [control]="form.controls.national_code" />
|
||||||
|
}
|
||||||
|
<app-form-footer-actions [loading]="submitLoading()" (onCancel)="close()" />
|
||||||
|
</form>
|
||||||
|
</shared-dialog>
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import { AbstractFormDialog } from '@/shared/abstractClasses';
|
||||||
|
import {
|
||||||
|
CompanyNameComponent,
|
||||||
|
FirstNameComponent,
|
||||||
|
LastNameComponent,
|
||||||
|
MobileNumberComponent,
|
||||||
|
NationalCodeComponent,
|
||||||
|
RegistrationNumberComponent,
|
||||||
|
SharedDialogComponent,
|
||||||
|
} from '@/shared/components';
|
||||||
|
import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component';
|
||||||
|
import { fieldControl } from '@/shared/constants';
|
||||||
|
import { Component, inject, Input } from '@angular/core';
|
||||||
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { IProfileRequestPayload, IProfileResponse } from '../models';
|
||||||
|
import { ProfileService } from '../services/main.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'consumer-profile-form',
|
||||||
|
templateUrl: 'form.component.html',
|
||||||
|
imports: [
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FormFooterActionsComponent,
|
||||||
|
FirstNameComponent,
|
||||||
|
LastNameComponent,
|
||||||
|
NationalCodeComponent,
|
||||||
|
MobileNumberComponent,
|
||||||
|
CompanyNameComponent,
|
||||||
|
RegistrationNumberComponent,
|
||||||
|
SharedDialogComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class ConsumerProfileFormComponent extends AbstractFormDialog<
|
||||||
|
IProfileRequestPayload,
|
||||||
|
IProfileResponse,
|
||||||
|
IProfileRequestPayload
|
||||||
|
> {
|
||||||
|
@Input({ required: true }) profileType!: 'INDIVIDUAL' | 'LEGAL';
|
||||||
|
|
||||||
|
private readonly service = inject(ProfileService);
|
||||||
|
|
||||||
|
initForm = () => {
|
||||||
|
const form = this.fb.group({
|
||||||
|
first_name: fieldControl.first_name(this.initialValues?.first_name),
|
||||||
|
last_name: fieldControl.last_name(this.initialValues?.last_name),
|
||||||
|
mobile_number: fieldControl.mobile_number(this.initialValues?.mobile_number),
|
||||||
|
national_code: fieldControl.national_code(this.initialValues?.national_code),
|
||||||
|
company_name: fieldControl.company_name(this.initialValues?.company_name),
|
||||||
|
registration_number: fieldControl.registration_number(
|
||||||
|
this.initialValues?.registration_number,
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.profileType === 'LEGAL') {
|
||||||
|
// @ts-ignore
|
||||||
|
form.removeControl('first_name');
|
||||||
|
// @ts-ignore
|
||||||
|
form.removeControl('last_name');
|
||||||
|
// @ts-ignore
|
||||||
|
form.removeControl('mobile_number');
|
||||||
|
// @ts-ignore
|
||||||
|
form.removeControl('national_code');
|
||||||
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
|
form.removeControl('company_name');
|
||||||
|
// @ts-ignore
|
||||||
|
form.removeControl('registration_number');
|
||||||
|
}
|
||||||
|
return form;
|
||||||
|
};
|
||||||
|
override form = this.initForm();
|
||||||
|
|
||||||
|
override submitForm(payload: IProfileRequestPayload) {
|
||||||
|
return this.service.updateProfile(payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
<app-card-data cardTitle="تغییر گذرواژه" [editable]="false">
|
||||||
|
<form [formGroup]="form" (submit)="submit()" class="max-w-lg mx-auto">
|
||||||
|
<shared-password-input
|
||||||
|
[passwordControl]="form.controls.password"
|
||||||
|
[confirmPasswordControl]="form.controls.confirmPassword"
|
||||||
|
/>
|
||||||
|
<button type="submit" pButton [disabled]="form.invalid || loading()" class="w-full max-w-xs mx-auto">
|
||||||
|
تغییر گذرواژه
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</app-card-data>
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { ToastService } from '@/core/services/toast.service';
|
||||||
|
import { MustMatch } from '@/core/validators';
|
||||||
|
import { AppCardComponent, SharedPasswordInputComponent } from '@/shared/components';
|
||||||
|
import { fieldControl } from '@/shared/constants';
|
||||||
|
import { Component, inject, signal } from '@angular/core';
|
||||||
|
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { ButtonDirective } from 'primeng/button';
|
||||||
|
import { finalize } from 'rxjs';
|
||||||
|
import { ProfileService } from '../services/main.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'consumer-reset-password-card',
|
||||||
|
templateUrl: './reset-password-card.component.html',
|
||||||
|
imports: [ReactiveFormsModule, AppCardComponent, SharedPasswordInputComponent, ButtonDirective],
|
||||||
|
})
|
||||||
|
export class ConsumerResetPasswordCardComponent {
|
||||||
|
private readonly service = inject(ProfileService);
|
||||||
|
private readonly fb = inject(FormBuilder);
|
||||||
|
private readonly toastService = inject(ToastService);
|
||||||
|
|
||||||
|
readonly loading = signal(false);
|
||||||
|
|
||||||
|
form = this.fb.group(
|
||||||
|
{
|
||||||
|
password: fieldControl.password(),
|
||||||
|
confirmPassword: fieldControl.confirmPassword(),
|
||||||
|
},
|
||||||
|
{ validators: [MustMatch('password', 'confirmPassword')] },
|
||||||
|
);
|
||||||
|
|
||||||
|
submit() {
|
||||||
|
if (this.form.invalid) {
|
||||||
|
this.form.markAllAsTouched();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading.set(true);
|
||||||
|
this.service
|
||||||
|
.resetPassword({ password: this.form.value.password as string })
|
||||||
|
.pipe(finalize(() => this.loading.set(false)))
|
||||||
|
.subscribe(() => {
|
||||||
|
this.form.reset();
|
||||||
|
this.toastService.success({ text: 'گذرواژه با موفقیت بهروز شد.' });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
const baseUrl = '/api/v1/consumer';
|
||||||
|
|
||||||
|
export const CONSUMER_PROFILE_API_ROUTES = {
|
||||||
|
info: () => `${baseUrl}`,
|
||||||
|
updateInfo: () => `${baseUrl}`,
|
||||||
|
resetPassword: () => `${baseUrl}/update-password`,
|
||||||
|
};
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './apiRoutes';
|
||||||
|
export * from './routes';
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { NamedRoutes } from '@/core';
|
||||||
|
import { Routes } from '@angular/router';
|
||||||
|
|
||||||
|
export type TConsumerProfileRouteNames = 'profile';
|
||||||
|
|
||||||
|
export const consumerProfileNamedRoutes: NamedRoutes<TConsumerProfileRouteNames> = {
|
||||||
|
profile: {
|
||||||
|
path: 'profile',
|
||||||
|
loadComponent: () => import('../../views/single.component').then((m) => m.ConsumerProfileComponent),
|
||||||
|
meta: {
|
||||||
|
title: 'پروفایل',
|
||||||
|
pagePath: () => '/consumer/profile',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CONSUMER_PROFILE_ROUTES: Routes = [consumerProfileNamedRoutes.profile];
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './io.d';
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { IConsumerInfoResponse } from '@/domains/consumer/models';
|
||||||
|
|
||||||
|
export interface IProfileResponse extends IConsumerInfoResponse {}
|
||||||
|
|
||||||
|
export interface IProfileRequestPayload {
|
||||||
|
first_name?: string;
|
||||||
|
last_name?: string;
|
||||||
|
mobile_number?: string;
|
||||||
|
national_code?: string;
|
||||||
|
company_name?: string;
|
||||||
|
registration_number?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IResetPasswordRequest {
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { CONSUMER_PROFILE_API_ROUTES } from '../constants';
|
||||||
|
import { IProfileRequestPayload, IProfileResponse, IResetPasswordRequest } from '../models';
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class ProfileService {
|
||||||
|
constructor(private readonly http: HttpClient) {}
|
||||||
|
|
||||||
|
getInfo(): Observable<IProfileResponse> {
|
||||||
|
return this.http.get<IProfileResponse>(CONSUMER_PROFILE_API_ROUTES.info());
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProfile(payload: IProfileRequestPayload): Observable<IProfileResponse> {
|
||||||
|
return this.http.patch<IProfileResponse>(CONSUMER_PROFILE_API_ROUTES.updateInfo(), payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
resetPassword(payload: IResetPasswordRequest): Observable<unknown> {
|
||||||
|
return this.http.put(CONSUMER_PROFILE_API_ROUTES.resetPassword(), payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<div class="flex flex-col gap-4">
|
||||||
|
<app-card-data cardTitle="اطلاعات پروفایل" [editable]="true" [(editMode)]="editing">
|
||||||
|
@if (isLegal()) {
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||||
|
<app-key-value label="نام شرکت" [value]="profile()?.legal?.company_name || '-'" />
|
||||||
|
<app-key-value label="شماره ثبت" [value]="profile()?.legal?.registration_number" />
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||||
|
<app-key-value label="نام" [value]="profile()?.individual?.first_name || '-'" />
|
||||||
|
<app-key-value label="نام خانوادگی" [value]="profile()?.individual?.last_name || '-'" />
|
||||||
|
<app-key-value label="کد ملی" [value]="profile()?.individual?.national_code" />
|
||||||
|
<app-key-value label="شماره موبایل" [value]="profile()?.individual?.mobile_number" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</app-card-data>
|
||||||
|
|
||||||
|
<consumer-reset-password-card />
|
||||||
|
|
||||||
|
@if (editing()) {
|
||||||
|
<consumer-profile-form
|
||||||
|
[(visible)]="editing"
|
||||||
|
[profileType]="profile()!.type!.value"
|
||||||
|
[initialValues]="profile()?.legal || profile()?.individual"
|
||||||
|
(onSubmit)="onEditSuccess()"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { ConsumerStore } from '@/domains/consumer/store/main.store';
|
||||||
|
import { AppCardComponent, KeyValueComponent } from '@/shared/components';
|
||||||
|
import { Component, computed, inject, signal } from '@angular/core';
|
||||||
|
import { ConsumerProfileFormComponent } from '../components/form.component';
|
||||||
|
import { ConsumerResetPasswordCardComponent } from '../components/reset-password-card.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'consumer-profile',
|
||||||
|
templateUrl: './single.component.html',
|
||||||
|
imports: [
|
||||||
|
AppCardComponent,
|
||||||
|
KeyValueComponent,
|
||||||
|
ConsumerResetPasswordCardComponent,
|
||||||
|
ConsumerProfileFormComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class ConsumerProfileComponent {
|
||||||
|
private readonly store = inject(ConsumerStore);
|
||||||
|
|
||||||
|
readonly profile = computed(() => this.store.entity());
|
||||||
|
readonly isLegal = computed(() => this.profile()?.type?.value === 'LEGAL');
|
||||||
|
|
||||||
|
editing = signal(false);
|
||||||
|
|
||||||
|
edit() {
|
||||||
|
this.editing.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
onEditSuccess() {
|
||||||
|
this.store.getData();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import { CONSUMER_ACCOUNTS_ROUTES } from './modules/accounts/constants';
|
|||||||
import { CONSUMER_BUSINESS_ACTIVITIES_ROUTES } from './modules/businessActivities/constants';
|
import { CONSUMER_BUSINESS_ACTIVITIES_ROUTES } from './modules/businessActivities/constants';
|
||||||
import { CONSUMER_CUSTOMERS_ROUTES } from './modules/customers/constants';
|
import { CONSUMER_CUSTOMERS_ROUTES } from './modules/customers/constants';
|
||||||
import { CONSUMER_POSES_ROUTES } from './modules/poses/constants';
|
import { CONSUMER_POSES_ROUTES } from './modules/poses/constants';
|
||||||
|
import { CONSUMER_PROFILE_ROUTES } from './modules/profile/constants';
|
||||||
import { CONSUMER_SALE_INVOICES_ROUTES } from './modules/saleInvoices/constants';
|
import { CONSUMER_SALE_INVOICES_ROUTES } from './modules/saleInvoices/constants';
|
||||||
|
|
||||||
export const CONSUMER_ROUTES = {
|
export const CONSUMER_ROUTES = {
|
||||||
@@ -19,5 +20,6 @@ export const CONSUMER_ROUTES = {
|
|||||||
...CONSUMER_CUSTOMERS_ROUTES,
|
...CONSUMER_CUSTOMERS_ROUTES,
|
||||||
...CONSUMER_SALE_INVOICES_ROUTES,
|
...CONSUMER_SALE_INVOICES_ROUTES,
|
||||||
...CONSUMER_POSES_ROUTES,
|
...CONSUMER_POSES_ROUTES,
|
||||||
|
...CONSUMER_PROFILE_ROUTES,
|
||||||
],
|
],
|
||||||
} as Route;
|
} as Route;
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { EntityState, EntityStore } from '@/core/state';
|
import { defaultBaseStateData, EntityState, EntityStore } from '@/core/state';
|
||||||
import { LayoutService } from '@/layout/service/layout.service';
|
import { LayoutService } from '@/layout/service/layout.service';
|
||||||
import { inject, Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { catchError, finalize } from 'rxjs';
|
import { catchError, finalize } from 'rxjs';
|
||||||
import { IConsumerInfoResponse } from '../models';
|
import { IConsumerInfoResponse } from '../models';
|
||||||
|
import { consumerProfileNamedRoutes } from '../modules/profile/constants';
|
||||||
import { ConsumerService } from '../services/main.service';
|
import { ConsumerService } from '../services/main.service';
|
||||||
|
|
||||||
interface ConsumerState extends EntityState<IConsumerInfoResponse> {}
|
interface ConsumerState extends EntityState<IConsumerInfoResponse> {}
|
||||||
@@ -16,11 +17,7 @@ export class ConsumerStore extends EntityStore<IConsumerInfoResponse, ConsumerSt
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,17 +38,16 @@ export class ConsumerStore extends EntityStore<IConsumerInfoResponse, ConsumerSt
|
|||||||
this.layoutService.setPanelInfo({
|
this.layoutService.setPanelInfo({
|
||||||
title: 'پنل مدیریت صورتحسابهای مالیاتی',
|
title: 'پنل مدیریت صورتحسابهای مالیاتی',
|
||||||
});
|
});
|
||||||
|
this.layoutService.setProfilePageRoute(
|
||||||
|
consumerProfileNamedRoutes.profile.meta!.pagePath!(),
|
||||||
|
);
|
||||||
this.patchState({ entity });
|
this.patchState({ entity });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
override reset(): void {
|
override reset(): void {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-7
@@ -1,12 +1,9 @@
|
|||||||
import ISummary from '@/core/models/summary';
|
|
||||||
|
|
||||||
export interface IPartnerRawResponse {
|
export interface IPartnerRawResponse {
|
||||||
id: string;
|
id: string;
|
||||||
role: string;
|
name: string;
|
||||||
partner: ISummary;
|
code: string;
|
||||||
account: {
|
logo_url?: string;
|
||||||
username: string;
|
account: Account;
|
||||||
};
|
|
||||||
}
|
}
|
||||||
export interface IPartnerResponse extends IPartnerRawResponse {}
|
export interface IPartnerResponse extends IPartnerRawResponse {}
|
||||||
|
|
||||||
@@ -26,3 +23,9 @@ interface LicensesStatus {
|
|||||||
total_activated: number;
|
total_activated: number;
|
||||||
total_expired: number;
|
total_expired: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Account {
|
||||||
|
role: string;
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EntityState, EntityStore } from '@/core/state';
|
import { defaultBaseStateData, EntityState, EntityStore } from '@/core/state';
|
||||||
import { computed, inject, Injectable } from '@angular/core';
|
import { computed, inject, Injectable } from '@angular/core';
|
||||||
import { MenuItem } from 'primeng/api';
|
import { MenuItem } from 'primeng/api';
|
||||||
import { catchError, finalize } from 'rxjs';
|
import { catchError, finalize } from 'rxjs';
|
||||||
@@ -20,11 +20,7 @@ export class BusinessActivityStore extends EntityStore<
|
|||||||
private readonly service = inject(PartnerConsumerBusinessActivitiesService);
|
private readonly service = inject(PartnerConsumerBusinessActivitiesService);
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -43,8 +39,10 @@ export class BusinessActivityStore extends EntityStore<
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: this.entity()?.name,
|
title: this.entity()?.name,
|
||||||
routerLink: partnerConsumerBusinessActivitiesNamedRoutes.businessActivity.meta
|
routerLink: partnerConsumerBusinessActivitiesNamedRoutes.businessActivity.meta.pagePath!(
|
||||||
.pagePath!(consumerId, businessId),
|
consumerId,
|
||||||
|
businessId,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@@ -71,11 +69,7 @@ export class BusinessActivityStore extends EntityStore<
|
|||||||
|
|
||||||
override reset(): void {
|
override reset(): void {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EntityState, EntityStore } from '@/core/state';
|
import { defaultBaseStateData, EntityState, EntityStore } from '@/core/state';
|
||||||
import { computed, inject, Injectable } from '@angular/core';
|
import { computed, inject, Injectable } from '@angular/core';
|
||||||
import { MenuItem } from 'primeng/api';
|
import { MenuItem } from 'primeng/api';
|
||||||
import { catchError, finalize } from 'rxjs';
|
import { catchError, finalize } from 'rxjs';
|
||||||
@@ -17,11 +17,7 @@ export class ComplexStore extends EntityStore<IComplexResponse, ComplexState> {
|
|||||||
private readonly service = inject(PartnerComplexesService);
|
private readonly service = inject(PartnerComplexesService);
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -71,11 +67,7 @@ export class ComplexStore extends EntityStore<IComplexResponse, ComplexState> {
|
|||||||
|
|
||||||
override reset(): void {
|
override reset(): void {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EntityState, EntityStore } from '@/core/state';
|
import { defaultBaseStateData, EntityState, EntityStore } from '@/core/state';
|
||||||
import { computed, inject, Injectable } from '@angular/core';
|
import { computed, inject, Injectable } from '@angular/core';
|
||||||
import { MenuItem } from 'primeng/api';
|
import { MenuItem } from 'primeng/api';
|
||||||
import { catchError, finalize } from 'rxjs';
|
import { catchError, finalize } from 'rxjs';
|
||||||
@@ -17,11 +17,7 @@ export class ConsumerStore extends EntityStore<IPartnerConsumerResponse, Consume
|
|||||||
private readonly service = inject(ConsumersService);
|
private readonly service = inject(ConsumersService);
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EntityState, EntityStore } from '@/core/state';
|
import { defaultBaseStateData, EntityState, EntityStore } from '@/core/state';
|
||||||
import { computed, inject, Injectable } from '@angular/core';
|
import { computed, inject, Injectable } from '@angular/core';
|
||||||
import { MenuItem } from 'primeng/api';
|
import { MenuItem } from 'primeng/api';
|
||||||
import { catchError, finalize } from 'rxjs';
|
import { catchError, finalize } from 'rxjs';
|
||||||
@@ -17,11 +17,7 @@ export class PosStore extends EntityStore<IPosResponse, PosState> {
|
|||||||
private readonly service = inject(PartnerPosesService);
|
private readonly service = inject(PartnerPosesService);
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -74,11 +70,7 @@ export class PosStore extends EntityStore<IPosResponse, PosState> {
|
|||||||
|
|
||||||
override reset(): void {
|
override reset(): void {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EntityState, EntityStore } from '@/core/state';
|
import { defaultBaseStateData, EntityState, EntityStore } from '@/core/state';
|
||||||
import { computed, inject, Injectable } from '@angular/core';
|
import { computed, inject, Injectable } from '@angular/core';
|
||||||
import { MenuItem } from 'primeng/api';
|
import { MenuItem } from 'primeng/api';
|
||||||
import { catchError, finalize } from 'rxjs';
|
import { catchError, finalize } from 'rxjs';
|
||||||
@@ -17,11 +17,7 @@ export class PartnerCustomerStore extends EntityStore<ICustomerResponse, Partner
|
|||||||
private readonly service = inject(CustomersService);
|
private readonly service = inject(CustomersService);
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -64,11 +60,7 @@ export class PartnerCustomerStore extends EntityStore<ICustomerResponse, Partner
|
|||||||
|
|
||||||
override reset(): void {
|
override reset(): void {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
breadcrumbItems: [],
|
breadcrumbItems: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<shared-dialog
|
||||||
|
header="ویرایش اطلاعات پروفایل"
|
||||||
|
[(visible)]="visible"
|
||||||
|
[modal]="true"
|
||||||
|
[style]="{ width: '500px' }"
|
||||||
|
[closable]="true"
|
||||||
|
(onHide)="close()"
|
||||||
|
>
|
||||||
|
<form [formGroup]="form" (submit)="submit()">
|
||||||
|
<app-shared-upload-file
|
||||||
|
accept="image/*"
|
||||||
|
name="image"
|
||||||
|
[initial_file_url]="initialValues?.logo_url || ''"
|
||||||
|
(onSelect)="changeFile($event)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<field-name [control]="form.controls.name" />
|
||||||
|
<field-code [control]="form.controls.code" />
|
||||||
|
<app-form-footer-actions [loading]="submitLoading()" (onCancel)="close()" />
|
||||||
|
</form>
|
||||||
|
</shared-dialog>
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import { Maybe } from '@/core';
|
||||||
|
import { AbstractFormDialog } from '@/shared/abstractClasses';
|
||||||
|
import { CodeComponent, NameComponent, SharedDialogComponent } from '@/shared/components';
|
||||||
|
import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component';
|
||||||
|
import { onSelectFileArgs } from '@/shared/components/uploadFile/model';
|
||||||
|
import { SharedUploadFileComponent } from '@/shared/components/uploadFile/upload-file.component';
|
||||||
|
import { fieldControl } from '@/shared/constants';
|
||||||
|
import { buildFormData } from '@/utils';
|
||||||
|
import { Component, inject, Input, signal } from '@angular/core';
|
||||||
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { IProfileRequestPayload, IProfileResponse } from '../models';
|
||||||
|
import { ProfileService } from '../services/main.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'partner-profile-form',
|
||||||
|
templateUrl: 'form.component.html',
|
||||||
|
imports: [
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FormFooterActionsComponent,
|
||||||
|
SharedDialogComponent,
|
||||||
|
NameComponent,
|
||||||
|
CodeComponent,
|
||||||
|
SharedUploadFileComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class PartnerProfileFormComponent extends AbstractFormDialog<
|
||||||
|
IProfileRequestPayload,
|
||||||
|
IProfileResponse
|
||||||
|
> {
|
||||||
|
@Input() partnerId?: string;
|
||||||
|
private service = inject(ProfileService);
|
||||||
|
|
||||||
|
private initForm() {
|
||||||
|
const formConfig = {
|
||||||
|
name: fieldControl.name(this.initialValues?.name || ''),
|
||||||
|
code: fieldControl.code(this.initialValues?.code || ''),
|
||||||
|
};
|
||||||
|
|
||||||
|
const form = this.fb.group(formConfig);
|
||||||
|
|
||||||
|
return form;
|
||||||
|
}
|
||||||
|
|
||||||
|
logo = signal<Maybe<File>>(null);
|
||||||
|
|
||||||
|
form = this.initForm();
|
||||||
|
|
||||||
|
changeFile(payload: onSelectFileArgs) {
|
||||||
|
this.logo.set(payload.file);
|
||||||
|
}
|
||||||
|
|
||||||
|
override ngOnChanges() {
|
||||||
|
this.form = this.initForm();
|
||||||
|
this.form.patchValue((this.initialValues ?? {}) as Partial<Record<string, unknown>>);
|
||||||
|
if (this.editMode && !this.partnerId) {
|
||||||
|
throw 'missing some arguments';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override submitForm(payload: IProfileRequestPayload) {
|
||||||
|
const formData = buildFormData(this.form, { logo: this.logo() });
|
||||||
|
|
||||||
|
return this.service.updateProfile(formData);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<app-card-data cardTitle="تغییر گذرواژه" [editable]="false">
|
||||||
|
<form [formGroup]="form" (submit)="submit()" class="max-w-lg mx-auto">
|
||||||
|
<shared-password-input
|
||||||
|
[passwordControl]="form.controls.password"
|
||||||
|
[confirmPasswordControl]="form.controls.confirmPassword"
|
||||||
|
/>
|
||||||
|
<button type="submit" pButton [disabled]="form.invalid || loading()" class="w-full max-w-xs mx-auto">
|
||||||
|
تغییر گذرواژه
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</app-card-data>
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { ToastService } from '@/core/services/toast.service';
|
||||||
|
import { MustMatch } from '@/core/validators';
|
||||||
|
import { AppCardComponent, SharedPasswordInputComponent } from '@/shared/components';
|
||||||
|
import { fieldControl } from '@/shared/constants';
|
||||||
|
import { Component, inject, signal } from '@angular/core';
|
||||||
|
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { ButtonDirective } from 'primeng/button';
|
||||||
|
import { finalize } from 'rxjs';
|
||||||
|
import { ProfileService } from '../services/main.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'partner-reset-password-card',
|
||||||
|
templateUrl: './reset-password-card.component.html',
|
||||||
|
imports: [ReactiveFormsModule, AppCardComponent, SharedPasswordInputComponent, ButtonDirective],
|
||||||
|
})
|
||||||
|
export class PartnerResetPasswordCardComponent {
|
||||||
|
private readonly service = inject(ProfileService);
|
||||||
|
private readonly fb = inject(FormBuilder);
|
||||||
|
private readonly toastService = inject(ToastService);
|
||||||
|
|
||||||
|
readonly loading = signal(false);
|
||||||
|
|
||||||
|
form = this.fb.group(
|
||||||
|
{
|
||||||
|
password: fieldControl.password(),
|
||||||
|
confirmPassword: fieldControl.confirmPassword(),
|
||||||
|
},
|
||||||
|
{ validators: [MustMatch('password', 'confirmPassword')] },
|
||||||
|
);
|
||||||
|
|
||||||
|
submit() {
|
||||||
|
if (this.form.invalid) {
|
||||||
|
this.form.markAllAsTouched();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading.set(true);
|
||||||
|
this.service
|
||||||
|
.resetPassword({ password: this.form.value.password as string })
|
||||||
|
.pipe(finalize(() => this.loading.set(false)))
|
||||||
|
.subscribe(() => {
|
||||||
|
this.form.reset();
|
||||||
|
this.toastService.success({ text: 'گذرواژه با موفقیت بهروز شد.' });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
const baseUrl = '/api/v1/partner';
|
||||||
|
|
||||||
|
export const PARTNER_PROFILE_API_ROUTES = {
|
||||||
|
info: () => `${baseUrl}`,
|
||||||
|
updateInfo: () => `${baseUrl}`,
|
||||||
|
resetPassword: () => `${baseUrl}/update-password`,
|
||||||
|
};
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './apiRoutes';
|
||||||
|
export * from './routes';
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { NamedRoutes } from '@/core';
|
||||||
|
import { Routes } from '@angular/router';
|
||||||
|
|
||||||
|
export type TPartnerProfileRouteNames = 'profile';
|
||||||
|
|
||||||
|
export const partnerProfileNamedRoutes: NamedRoutes<TPartnerProfileRouteNames> = {
|
||||||
|
profile: {
|
||||||
|
path: 'profile',
|
||||||
|
loadComponent: () =>
|
||||||
|
import('../../views/single.component').then((m) => m.PartnerProfileComponent),
|
||||||
|
meta: {
|
||||||
|
title: 'پروفایل',
|
||||||
|
pagePath: () => '/partner/profile',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PARTNER_PROFILE_ROUTES: Routes = [partnerProfileNamedRoutes.profile];
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './io.d';
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { IPartnerRawResponse } from '@/domains/partner/models';
|
||||||
|
|
||||||
|
export interface IProfileResponse extends IPartnerRawResponse {}
|
||||||
|
|
||||||
|
export interface IProfileRequestPayload {
|
||||||
|
first_name?: string;
|
||||||
|
last_name?: string;
|
||||||
|
mobile_number?: string;
|
||||||
|
national_code?: string;
|
||||||
|
company_name?: string;
|
||||||
|
registration_number?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IResetPasswordRequest {
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { PARTNER_PROFILE_API_ROUTES } from '../constants';
|
||||||
|
import { IProfileResponse, IResetPasswordRequest } from '../models';
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class ProfileService {
|
||||||
|
constructor(private readonly http: HttpClient) {}
|
||||||
|
|
||||||
|
getInfo(): Observable<IProfileResponse> {
|
||||||
|
return this.http.get<IProfileResponse>(PARTNER_PROFILE_API_ROUTES.info());
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProfile(payload: FormData): Observable<IProfileResponse> {
|
||||||
|
return this.http.patch<IProfileResponse>(PARTNER_PROFILE_API_ROUTES.updateInfo(), payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
resetPassword(payload: IResetPasswordRequest): Observable<unknown> {
|
||||||
|
return this.http.put(PARTNER_PROFILE_API_ROUTES.resetPassword(), payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<div class="flex flex-col gap-4">
|
||||||
|
<app-card-data cardTitle="اطلاعات پروفایل" [editable]="true" [(editMode)]="editing">
|
||||||
|
<div class="listKeyValue">
|
||||||
|
<app-key-value label="تصویر" [value]="profile()?.logo_url" type="thumbnail" />
|
||||||
|
<app-key-value label="عنوان" [value]="profile()?.name" />
|
||||||
|
<app-key-value label="کد" [value]="profile()?.code" />
|
||||||
|
</div>
|
||||||
|
</app-card-data>
|
||||||
|
|
||||||
|
<partner-reset-password-card />
|
||||||
|
|
||||||
|
@if (editing()) {
|
||||||
|
<partner-profile-form [(visible)]="editing" [initialValues]="profile()!" (onSubmit)="onEditSuccess()" />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { PartnerStore } from '@/domains/partner/store/main.store';
|
||||||
|
import { AppCardComponent, KeyValueComponent } from '@/shared/components';
|
||||||
|
import { Component, computed, inject, signal } from '@angular/core';
|
||||||
|
import { PartnerProfileFormComponent } from '../components/form.component';
|
||||||
|
import { PartnerResetPasswordCardComponent } from '../components/reset-password-card.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'partner-profile',
|
||||||
|
templateUrl: './single.component.html',
|
||||||
|
imports: [
|
||||||
|
AppCardComponent,
|
||||||
|
KeyValueComponent,
|
||||||
|
PartnerResetPasswordCardComponent,
|
||||||
|
PartnerProfileFormComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class PartnerProfileComponent {
|
||||||
|
private readonly store = inject(PartnerStore);
|
||||||
|
|
||||||
|
readonly profile = computed(() => this.store.entity());
|
||||||
|
|
||||||
|
editing = signal(false);
|
||||||
|
|
||||||
|
edit() {
|
||||||
|
this.editing.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
onEditSuccess() {
|
||||||
|
this.store.getData();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import { Route } from '@angular/router';
|
|||||||
import { PARTNER_ACCOUNTS_ROUTES } from './modules/accounts/constants';
|
import { PARTNER_ACCOUNTS_ROUTES } from './modules/accounts/constants';
|
||||||
import { PARTNER_CONSUMERS_ROUTES } from './modules/consumers/constants';
|
import { PARTNER_CONSUMERS_ROUTES } from './modules/consumers/constants';
|
||||||
import { PARTNER_LICENSES_ROUTES } from './modules/licenses/constants';
|
import { PARTNER_LICENSES_ROUTES } from './modules/licenses/constants';
|
||||||
|
import { PARTNER_PROFILE_ROUTES } from './modules/profile/constants';
|
||||||
|
|
||||||
export const PARTNER_ROUTES = {
|
export const PARTNER_ROUTES = {
|
||||||
path: 'partner',
|
path: 'partner',
|
||||||
@@ -15,5 +16,6 @@ export const PARTNER_ROUTES = {
|
|||||||
...PARTNER_ACCOUNTS_ROUTES,
|
...PARTNER_ACCOUNTS_ROUTES,
|
||||||
...PARTNER_CONSUMERS_ROUTES,
|
...PARTNER_CONSUMERS_ROUTES,
|
||||||
...PARTNER_LICENSES_ROUTES,
|
...PARTNER_LICENSES_ROUTES,
|
||||||
|
...PARTNER_PROFILE_ROUTES,
|
||||||
],
|
],
|
||||||
} as Route;
|
} as Route;
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { EntityState, EntityStore } from '@/core/state';
|
import { defaultBaseStateData, EntityState, EntityStore } from '@/core/state';
|
||||||
import { LayoutService } from '@/layout/service/layout.service';
|
import { LayoutService } from '@/layout/service/layout.service';
|
||||||
import { inject, Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { catchError, finalize } from 'rxjs';
|
import { catchError, finalize } from 'rxjs';
|
||||||
import { IPartnerResponse } from '../models';
|
import { IPartnerResponse } from '../models';
|
||||||
|
import { partnerProfileNamedRoutes } from '../modules/profile/constants';
|
||||||
import { PartnerService } from '../services/main.service';
|
import { PartnerService } from '../services/main.service';
|
||||||
|
|
||||||
interface PartnerState extends EntityState<IPartnerResponse> {}
|
interface PartnerState extends EntityState<IPartnerResponse> {}
|
||||||
@@ -16,11 +17,7 @@ export class PartnerStore extends EntityStore<IPartnerResponse, PartnerState> {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,19 +36,17 @@ export class PartnerStore extends EntityStore<IPartnerResponse, PartnerState> {
|
|||||||
)
|
)
|
||||||
.subscribe((entity) => {
|
.subscribe((entity) => {
|
||||||
this.layoutService.setPanelInfo({
|
this.layoutService.setPanelInfo({
|
||||||
title: `پنل مدیریتی ${entity.partner.name}`,
|
title: `پنل مدیریتی ${entity.name}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.layoutService.setProfilePageRoute(partnerProfileNamedRoutes.profile.meta!.pagePath!());
|
||||||
this.setEntity(entity);
|
this.setEntity(entity);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
override reset(): void {
|
override reset(): void {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EntityState, EntityStore } from '@/core/state';
|
import { defaultBaseStateData, EntityState, EntityStore } from '@/core/state';
|
||||||
import { inject, Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { catchError, finalize } from 'rxjs';
|
import { catchError, finalize } from 'rxjs';
|
||||||
import { IPartnerResponse } from '../models';
|
import { IPartnerResponse } from '../models';
|
||||||
@@ -13,11 +13,7 @@ export class PartnerStore extends EntityStore<IPartnerResponse, PartnerState> {
|
|||||||
private readonly service = inject(PartnersService);
|
private readonly service = inject(PartnersService);
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EntityState, EntityStore } from '@/core/state';
|
import { defaultBaseStateData, EntityState, EntityStore } from '@/core/state';
|
||||||
import { inject, Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { catchError, finalize } from 'rxjs';
|
import { catchError, finalize } from 'rxjs';
|
||||||
import { IUserResponse } from '../models';
|
import { IUserResponse } from '../models';
|
||||||
@@ -13,11 +13,7 @@ export class UserStore extends EntityStore<IUserResponse, UserState> {
|
|||||||
private readonly service = inject(UsersService);
|
private readonly service = inject(UsersService);
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
loading: false,
|
...defaultBaseStateData,
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,14 +34,4 @@ export class UserStore extends EntityStore<IUserResponse, UserState> {
|
|||||||
this.patchState({ entity });
|
this.patchState({ entity });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
override reset(): void {
|
|
||||||
this.setState({
|
|
||||||
loading: false,
|
|
||||||
error: null,
|
|
||||||
entity: null,
|
|
||||||
initialized: false,
|
|
||||||
isRefreshing: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
</p-button>
|
</p-button>
|
||||||
|
|
||||||
<div class="">
|
<div class="">
|
||||||
<p-menu #menu [model]="profileMenuItems" [popup]="true" />
|
<p-menu #menu [model]="profileMenuItems()" [popup]="true" />
|
||||||
<p-button (click)="menu.toggle($event)" icon="pi pi-user" text severity="contrast" size="large" />
|
<p-button (click)="menu.toggle($event)" icon="pi pi-user" text severity="contrast" size="large" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export class AppTopbar {
|
|||||||
@Input() centerTemplate?: TemplateRef<any> | null = null;
|
@Input() centerTemplate?: TemplateRef<any> | null = null;
|
||||||
@Input() endTemplate?: TemplateRef<any> | null = null;
|
@Input() endTemplate?: TemplateRef<any> | null = null;
|
||||||
|
|
||||||
constructor(public layoutService: LayoutService) {}
|
readonly layoutService = inject(LayoutService);
|
||||||
private authService: AuthService = inject(AuthService);
|
private authService: AuthService = inject(AuthService);
|
||||||
|
|
||||||
readonly logo = images.logo;
|
readonly logo = images.logo;
|
||||||
@@ -34,20 +34,20 @@ export class AppTopbar {
|
|||||||
this.layoutService.layoutConfig.update((state) => ({ ...state, darkTheme: !state.darkTheme }));
|
this.layoutService.layoutConfig.update((state) => ({ ...state, darkTheme: !state.darkTheme }));
|
||||||
}
|
}
|
||||||
|
|
||||||
username = computed(() => this.authService.currentAccount()?.username || 'کاربر ناشناس');
|
readonly username = computed(() => this.authService.currentAccount()?.username || 'کاربر ناشناس');
|
||||||
panelTitle = computed(
|
readonly profilePageRoute = computed(() => this.layoutService.profilePageRoute());
|
||||||
() => this.layoutService.panelInfo()?.title || 'پنل مدیریت صورتحسابهای مالیاتی',
|
readonly panelTitle = computed(() => this.layoutService.panelInfo()?.title);
|
||||||
);
|
|
||||||
|
|
||||||
logout = () => {
|
readonly logout = () => {
|
||||||
this.authService.logout();
|
this.authService.logout();
|
||||||
};
|
};
|
||||||
|
|
||||||
profileMenuItems: MenuItem[] = [
|
profileMenuItems = computed<MenuItem[]>(() => [
|
||||||
{
|
{
|
||||||
label: this.username(),
|
label: this.username(),
|
||||||
icon: 'pi pi-user',
|
icon: 'pi pi-user',
|
||||||
disabled: true,
|
disabled: !this.profilePageRoute(),
|
||||||
|
routerLink: this.profilePageRoute(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'راهنمای سامانه',
|
label: 'راهنمای سامانه',
|
||||||
@@ -59,5 +59,5 @@ export class AppTopbar {
|
|||||||
icon: 'pi pi-sign-out',
|
icon: 'pi pi-sign-out',
|
||||||
command: this.logout,
|
command: this.logout,
|
||||||
},
|
},
|
||||||
];
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ interface LayoutState {
|
|||||||
menuHoverActive?: boolean;
|
menuHoverActive?: boolean;
|
||||||
isFixedContentSize?: boolean;
|
isFixedContentSize?: boolean;
|
||||||
isFullPage?: boolean;
|
isFullPage?: boolean;
|
||||||
|
profilePageRoute?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MenuChangeEvent {
|
interface MenuChangeEvent {
|
||||||
@@ -51,6 +52,7 @@ export class LayoutService {
|
|||||||
menuHoverActive: false,
|
menuHoverActive: false,
|
||||||
isFixedContentSize: true,
|
isFixedContentSize: true,
|
||||||
isFullPage: false,
|
isFullPage: false,
|
||||||
|
profilePageRoute: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
layoutConfig = signal<layoutConfig>(this._config);
|
layoutConfig = signal<layoutConfig>(this._config);
|
||||||
@@ -68,7 +70,7 @@ export class LayoutService {
|
|||||||
public menuItems = signal<MenuItem[]>([]);
|
public menuItems = signal<MenuItem[]>([]);
|
||||||
|
|
||||||
public panelInfo = signal<IPanelInfo>({
|
public panelInfo = signal<IPanelInfo>({
|
||||||
title: 'پنل مدیریت صورتحسابهای مالیاتی',
|
title: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
menuSource$ = this.menuSource.asObservable();
|
menuSource$ = this.menuSource.asObservable();
|
||||||
@@ -95,6 +97,7 @@ export class LayoutService {
|
|||||||
|
|
||||||
isFixedContentSize = computed(() => this.layoutState().isFixedContentSize);
|
isFixedContentSize = computed(() => this.layoutState().isFixedContentSize);
|
||||||
isFullPage = computed(() => this.layoutState().isFullPage);
|
isFullPage = computed(() => this.layoutState().isFullPage);
|
||||||
|
profilePageRoute = computed(() => this.layoutState().profilePageRoute || '');
|
||||||
|
|
||||||
transitionComplete = signal<boolean>(false);
|
transitionComplete = signal<boolean>(false);
|
||||||
|
|
||||||
@@ -264,4 +267,10 @@ export class LayoutService {
|
|||||||
setTopbarEndSlot(tpl: TemplateRef<any> | null) {
|
setTopbarEndSlot(tpl: TemplateRef<any> | null) {
|
||||||
this.topbarEndSlot.next(tpl);
|
this.topbarEndSlot.next(tpl);
|
||||||
}
|
}
|
||||||
|
setProfilePageRoute(route: string) {
|
||||||
|
this.layoutState.update((prev) => ({
|
||||||
|
...prev,
|
||||||
|
profilePageRoute: route,
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ import { AbstractForm } from './abstract-form';
|
|||||||
template: '',
|
template: '',
|
||||||
imports: [ReactiveFormsModule],
|
imports: [ReactiveFormsModule],
|
||||||
})
|
})
|
||||||
export abstract class AbstractFormDialog<Request, Response> extends AbstractForm<
|
export abstract class AbstractFormDialog<
|
||||||
Request,
|
Request,
|
||||||
Response
|
Response,
|
||||||
> {
|
initialValue = Response,
|
||||||
|
> extends AbstractForm<Request, Response, initialValue> {
|
||||||
@Input()
|
@Input()
|
||||||
set visible(v: boolean) {
|
set visible(v: boolean) {
|
||||||
this.visibleSignal.set(!!v);
|
this.visibleSignal.set(!!v);
|
||||||
|
|||||||
@@ -10,7 +10,13 @@
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<ng-content>
|
<ng-content>
|
||||||
@if (valueType() === "tag") {
|
@if (type === "thumbnail") {
|
||||||
|
<div class="w-20 h-20 rounded-2xl overflow-hidden bg-surface-100 cursor-pointer">
|
||||||
|
@if (valueToShow()) {
|
||||||
|
<img [src]="valueToShow()" class="w-full h-full object-cover" />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
} @else if (valueType() === "tag") {
|
||||||
<p-tag [value]="valueToShow()?.toString()" [severity]="value ? 'contrast' : 'danger'" />
|
<p-tag [value]="valueToShow()?.toString()" [severity]="value ? 'contrast' : 'danger'" />
|
||||||
} @else {
|
} @else {
|
||||||
<span class="text-text-color text-base font-bold grow"> {{ valueToShow() }}</span>
|
<span class="text-text-color text-base font-bold grow"> {{ valueToShow() }}</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user