diff --git a/src/app/domains/consumer/models/io.d.ts b/src/app/domains/consumer/models/io.d.ts
index bd4bc85..d6267b2 100644
--- a/src/app/domains/consumer/models/io.d.ts
+++ b/src/app/domains/consumer/models/io.d.ts
@@ -1,9 +1,31 @@
+import { IEnumTranslate } from '@/shared/models/enum_translate.type';
+
export interface IConsumerInfoRawResponse {
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;
last_name: string;
mobile_number: string;
- status: string;
+ national_code: string;
fullname: string;
}
-export interface IConsumerInfoResponse extends IConsumerInfoRawResponse {}
+
+interface Legal {
+ company_name: string;
+ registration_number: string;
+}
+
+interface Partner {
+ id: string;
+ name: string;
+ code: string;
+}
diff --git a/src/app/domains/consumer/modules/profile/components/form.component.html b/src/app/domains/consumer/modules/profile/components/form.component.html
new file mode 100644
index 0000000..8f0dd94
--- /dev/null
+++ b/src/app/domains/consumer/modules/profile/components/form.component.html
@@ -0,0 +1,21 @@
+
+
+
diff --git a/src/app/domains/consumer/modules/profile/components/form.component.ts b/src/app/domains/consumer/modules/profile/components/form.component.ts
new file mode 100644
index 0000000..0721ba2
--- /dev/null
+++ b/src/app/domains/consumer/modules/profile/components/form.component.ts
@@ -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);
+ }
+}
diff --git a/src/app/domains/consumer/modules/profile/components/reset-password-card.component.html b/src/app/domains/consumer/modules/profile/components/reset-password-card.component.html
new file mode 100644
index 0000000..70ce531
--- /dev/null
+++ b/src/app/domains/consumer/modules/profile/components/reset-password-card.component.html
@@ -0,0 +1,11 @@
+
+
+
diff --git a/src/app/domains/consumer/modules/profile/components/reset-password-card.component.ts b/src/app/domains/consumer/modules/profile/components/reset-password-card.component.ts
new file mode 100644
index 0000000..114f35b
--- /dev/null
+++ b/src/app/domains/consumer/modules/profile/components/reset-password-card.component.ts
@@ -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: 'گذرواژه با موفقیت بهروز شد.' });
+ });
+ }
+}
diff --git a/src/app/domains/consumer/modules/profile/constants/apiRoutes/index.ts b/src/app/domains/consumer/modules/profile/constants/apiRoutes/index.ts
new file mode 100644
index 0000000..95e8587
--- /dev/null
+++ b/src/app/domains/consumer/modules/profile/constants/apiRoutes/index.ts
@@ -0,0 +1,7 @@
+const baseUrl = '/api/v1/consumer';
+
+export const CONSUMER_PROFILE_API_ROUTES = {
+ info: () => `${baseUrl}`,
+ updateInfo: () => `${baseUrl}`,
+ resetPassword: () => `${baseUrl}/update-password`,
+};
diff --git a/src/app/domains/consumer/modules/profile/constants/index.ts b/src/app/domains/consumer/modules/profile/constants/index.ts
new file mode 100644
index 0000000..ee61bd7
--- /dev/null
+++ b/src/app/domains/consumer/modules/profile/constants/index.ts
@@ -0,0 +1,2 @@
+export * from './apiRoutes';
+export * from './routes';
diff --git a/src/app/domains/consumer/modules/profile/constants/routes/index.ts b/src/app/domains/consumer/modules/profile/constants/routes/index.ts
new file mode 100644
index 0000000..0d82fb2
--- /dev/null
+++ b/src/app/domains/consumer/modules/profile/constants/routes/index.ts
@@ -0,0 +1,17 @@
+import { NamedRoutes } from '@/core';
+import { Routes } from '@angular/router';
+
+export type TConsumerProfileRouteNames = 'profile';
+
+export const consumerProfileNamedRoutes: NamedRoutes = {
+ 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];
diff --git a/src/app/domains/consumer/modules/profile/models/index.ts b/src/app/domains/consumer/modules/profile/models/index.ts
new file mode 100644
index 0000000..d7b593e
--- /dev/null
+++ b/src/app/domains/consumer/modules/profile/models/index.ts
@@ -0,0 +1 @@
+export * from './io.d';
diff --git a/src/app/domains/consumer/modules/profile/models/io.d.ts b/src/app/domains/consumer/modules/profile/models/io.d.ts
new file mode 100644
index 0000000..23061ca
--- /dev/null
+++ b/src/app/domains/consumer/modules/profile/models/io.d.ts
@@ -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;
+}
diff --git a/src/app/domains/consumer/modules/profile/services/main.service.ts b/src/app/domains/consumer/modules/profile/services/main.service.ts
new file mode 100644
index 0000000..7ce19de
--- /dev/null
+++ b/src/app/domains/consumer/modules/profile/services/main.service.ts
@@ -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 {
+ return this.http.get(CONSUMER_PROFILE_API_ROUTES.info());
+ }
+
+ updateProfile(payload: IProfileRequestPayload): Observable {
+ return this.http.patch(CONSUMER_PROFILE_API_ROUTES.updateInfo(), payload);
+ }
+
+ resetPassword(payload: IResetPasswordRequest): Observable {
+ return this.http.put(CONSUMER_PROFILE_API_ROUTES.resetPassword(), payload);
+ }
+}
diff --git a/src/app/domains/consumer/modules/profile/views/single.component.html b/src/app/domains/consumer/modules/profile/views/single.component.html
new file mode 100644
index 0000000..ed3c129
--- /dev/null
+++ b/src/app/domains/consumer/modules/profile/views/single.component.html
@@ -0,0 +1,28 @@
+
+
+ @if (isLegal()) {
+
+ } @else {
+
+ }
+
+
+
+
+ @if (editing()) {
+
+ }
+
diff --git a/src/app/domains/consumer/modules/profile/views/single.component.ts b/src/app/domains/consumer/modules/profile/views/single.component.ts
new file mode 100644
index 0000000..8520b4a
--- /dev/null
+++ b/src/app/domains/consumer/modules/profile/views/single.component.ts
@@ -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();
+ }
+}
diff --git a/src/app/domains/consumer/routes.ts b/src/app/domains/consumer/routes.ts
index 8fd52a3..f6fc5ec 100644
--- a/src/app/domains/consumer/routes.ts
+++ b/src/app/domains/consumer/routes.ts
@@ -3,6 +3,7 @@ import { CONSUMER_ACCOUNTS_ROUTES } from './modules/accounts/constants';
import { CONSUMER_BUSINESS_ACTIVITIES_ROUTES } from './modules/businessActivities/constants';
import { CONSUMER_CUSTOMERS_ROUTES } from './modules/customers/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';
export const CONSUMER_ROUTES = {
@@ -19,5 +20,6 @@ export const CONSUMER_ROUTES = {
...CONSUMER_CUSTOMERS_ROUTES,
...CONSUMER_SALE_INVOICES_ROUTES,
...CONSUMER_POSES_ROUTES,
+ ...CONSUMER_PROFILE_ROUTES,
],
} as Route;
diff --git a/src/app/shared/abstractClasses/abstract-form-dialog.component.ts b/src/app/shared/abstractClasses/abstract-form-dialog.component.ts
index b685f8c..cf094d8 100644
--- a/src/app/shared/abstractClasses/abstract-form-dialog.component.ts
+++ b/src/app/shared/abstractClasses/abstract-form-dialog.component.ts
@@ -7,10 +7,11 @@ import { AbstractForm } from './abstract-form';
template: '',
imports: [ReactiveFormsModule],
})
-export abstract class AbstractFormDialog extends AbstractForm<
+export abstract class AbstractFormDialog<
Request,
- Response
-> {
+ Response,
+ initialValue = Response,
+> extends AbstractForm {
@Input()
set visible(v: boolean) {
this.visibleSignal.set(!!v);