From f18d7a1f0450d14f8e98654218bf6ec911d344b9 Mon Sep 17 00:00:00 2001 From: ahasani194 Date: Sun, 24 May 2026 10:44:35 +0330 Subject: [PATCH] feat: implement change password and send to fiscal activation modules with forms and services --- .../main-menu-sidebar.component.ts | 4 +- .../changePassword/form.component.html | 11 ++++ .../changePassword/form.component.ts | 51 ++++++++++++++++++ .../components/changePassword/model/index.ts | 3 ++ .../changePassword/services/main.service.ts | 15 ++++++ .../components/goldPrice/form.component.html | 16 +++--- .../components/goldPrice/form.component.ts | 54 ++++++------------- .../rapidInvoice/form.component.html | 19 +++---- .../components/rapidInvoice/form.component.ts | 54 ++++++------------- .../form.component.html | 9 ++++ .../sendToFiscalActivation/form.component.ts | 33 ++++++++++++ .../sendToFiscalActivation/models/index.ts | 3 ++ .../services/main.service.ts | 26 +++++++++ .../modules/configs/views/root.component.html | 18 +++++-- .../modules/configs/views/root.component.ts | 4 ++ .../components/fields/unit_price.component.ts | 9 +++- .../components/input/input.component.html | 16 +++--- .../constants/localStorageKeys.const.ts | 1 + src/assets/styles.scss | 16 ++++-- src/main.ts | 2 +- 20 files changed, 247 insertions(+), 117 deletions(-) create mode 100644 src/app/domains/pos/modules/configs/components/changePassword/form.component.html create mode 100644 src/app/domains/pos/modules/configs/components/changePassword/form.component.ts create mode 100644 src/app/domains/pos/modules/configs/components/changePassword/model/index.ts create mode 100644 src/app/domains/pos/modules/configs/components/changePassword/services/main.service.ts create mode 100644 src/app/domains/pos/modules/configs/components/sendToFiscalActivation/form.component.html create mode 100644 src/app/domains/pos/modules/configs/components/sendToFiscalActivation/form.component.ts create mode 100644 src/app/domains/pos/modules/configs/components/sendToFiscalActivation/models/index.ts create mode 100644 src/app/domains/pos/modules/configs/components/sendToFiscalActivation/services/main.service.ts diff --git a/src/app/domains/pos/layouts/mainMenuSidebar/main-menu-sidebar.component.ts b/src/app/domains/pos/layouts/mainMenuSidebar/main-menu-sidebar.component.ts index 589dc18..0441232 100644 --- a/src/app/domains/pos/layouts/mainMenuSidebar/main-menu-sidebar.component.ts +++ b/src/app/domains/pos/layouts/mainMenuSidebar/main-menu-sidebar.component.ts @@ -43,12 +43,12 @@ export class PosMainMenuSidebarComponent extends AbstractDialog { icon: 'pi pi-fw pi-home', }, { - label: 'فروش', + label: 'ایجاد صورت‌حساب', routerLink: PosShopNamedRoutes.shop.meta.pagePath!(), icon: 'pi pi-fw pi-cart-arrow-down', }, { - label: 'فاکتورها', + label: 'صورت‌حساب‌ها', routerLink: posSaleInvoicesNamedRoutes.saleInvoices.meta.pagePath!(), icon: 'pi pi-fw pi-receipt', }, diff --git a/src/app/domains/pos/modules/configs/components/changePassword/form.component.html b/src/app/domains/pos/modules/configs/components/changePassword/form.component.html new file mode 100644 index 0000000..8ff522d --- /dev/null +++ b/src/app/domains/pos/modules/configs/components/changePassword/form.component.html @@ -0,0 +1,11 @@ + +
+ + +
+ +
+ +
diff --git a/src/app/domains/pos/modules/configs/components/changePassword/form.component.ts b/src/app/domains/pos/modules/configs/components/changePassword/form.component.ts new file mode 100644 index 0000000..24cb9b7 --- /dev/null +++ b/src/app/domains/pos/modules/configs/components/changePassword/form.component.ts @@ -0,0 +1,51 @@ +import { MustMatch } from '@/core/validators'; +import { AbstractForm } from '@/shared/abstractClasses'; +import { AppCardComponent } from '@/shared/components'; +import { SharedPasswordInputComponent } from '@/shared/components/passwordInput/password-input.component'; +import { fieldControl } from '@/shared/constants'; +import { CommonModule } from '@angular/common'; +import { Component, inject } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; +import { ButtonDirective } from 'primeng/button'; +import { tap } from 'rxjs'; +import { IChangePasswordSubmitPayload } from './model'; +import { PosChangePasswordService } from './services/main.service'; + +@Component({ + selector: 'pos-change-password-form', + templateUrl: './form.component.html', + imports: [ + ReactiveFormsModule, + SharedPasswordInputComponent, + CommonModule, + ButtonDirective, + AppCardComponent, + ], +}) +export class PosChangePasswordFormDialogComponent extends AbstractForm< + IChangePasswordSubmitPayload, + any +> { + private readonly changePasswordService = inject(PosChangePasswordService); + + form = this.fb.group( + { + password: fieldControl.password(), + confirmPassword: fieldControl.confirmPassword(), + }, + { + validators: [MustMatch('password', 'confirmPassword')], + } + ); + + override submitForm() { + const payload = this.form.getRawValue().password!; + return this.changePasswordService.changePassword({ password: payload }).pipe( + tap({ + next: (res) => { + this.form.reset(); + }, + }) + ); + } +} diff --git a/src/app/domains/pos/modules/configs/components/changePassword/model/index.ts b/src/app/domains/pos/modules/configs/components/changePassword/model/index.ts new file mode 100644 index 0000000..22abae3 --- /dev/null +++ b/src/app/domains/pos/modules/configs/components/changePassword/model/index.ts @@ -0,0 +1,3 @@ +export interface IChangePasswordSubmitPayload { + password: string; +} diff --git a/src/app/domains/pos/modules/configs/components/changePassword/services/main.service.ts b/src/app/domains/pos/modules/configs/components/changePassword/services/main.service.ts new file mode 100644 index 0000000..0496867 --- /dev/null +++ b/src/app/domains/pos/modules/configs/components/changePassword/services/main.service.ts @@ -0,0 +1,15 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { IChangePasswordSubmitPayload } from '../model'; + +@Injectable({ + providedIn: 'root', +}) +export class PosChangePasswordService { + constructor(private readonly http: HttpClient) {} + + changePassword(payload: IChangePasswordSubmitPayload): Observable { + return this.http.put('/api/v1/pos/update-password', payload); + } +} diff --git a/src/app/domains/pos/modules/configs/components/goldPrice/form.component.html b/src/app/domains/pos/modules/configs/components/goldPrice/form.component.html index 3e54b90..a4e2707 100644 --- a/src/app/domains/pos/modules/configs/components/goldPrice/form.component.html +++ b/src/app/domains/pos/modules/configs/components/goldPrice/form.component.html @@ -1,9 +1,7 @@ - - - در این بخش می‌توانید قیمت پیش‌فرض هر گرم طلا را وارد کنید. - -
- - - -
+ diff --git a/src/app/domains/pos/modules/configs/components/goldPrice/form.component.ts b/src/app/domains/pos/modules/configs/components/goldPrice/form.component.ts index 0b796ca..eba001b 100644 --- a/src/app/domains/pos/modules/configs/components/goldPrice/form.component.ts +++ b/src/app/domains/pos/modules/configs/components/goldPrice/form.component.ts @@ -1,53 +1,31 @@ -import { AbstractForm } from '@/shared/abstractClasses'; -import { AppCardComponent, UnitPriceComponent } from '@/shared/components'; -import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; -import { fieldControl } from '@/shared/constants'; -import { Component, inject, signal } from '@angular/core'; -import { ReactiveFormsModule } from '@angular/forms'; -import { Message } from 'primeng/message'; -import { IPosConfigGoldPricePayload, IPosConfigGoldPriceResponse } from './models'; +import { InputComponent } from '@/shared/components'; +import { Component, inject } from '@angular/core'; +import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; +import { debounce, timer } from 'rxjs'; import { PosConfigGoldPriceService } from './services/main.service'; @Component({ selector: 'pos-config-gold-price-form', templateUrl: 'form.component.html', - imports: [ - ReactiveFormsModule, - FormFooterActionsComponent, - AppCardComponent, - Message, - UnitPriceComponent, - ], + imports: [ReactiveFormsModule, InputComponent], }) -export class PosConfigGoldPriceFormComponent extends AbstractForm< - IPosConfigGoldPricePayload, - IPosConfigGoldPriceResponse -> { +export class PosConfigGoldPriceFormComponent { private readonly service = inject(PosConfigGoldPriceService); + private readonly fb = inject(FormBuilder); - loading = signal(true); + goldPrice = this.fb.control(0, { nonNullable: true }); - initForm = () => { - const form = this.fb.group({ - price: fieldControl.unit_price(), + ngOnInit() { + const initialValues = this.service.get(); + this.goldPrice.setValue(initialValues); + + this.goldPrice.valueChanges.pipe(debounce(() => timer(300))).subscribe((value) => { + this.submit(); }); - - return form; - }; - - form = this.initForm(); - - override async ngOnInit() { - this.loading.set(true); - const initialValues = await this.service.get(); - - this.form.controls.price.setValue((initialValues || 0) + ''); - this.loading.set(false); } - override submitForm() { - const formValue = this.form.value.price as IPosConfigGoldPricePayload; - this.toastService.success({ text: 'تغییرات با موفقیت اعمال شد.' }); + submit() { + const formValue = this.goldPrice.value; return this.service.submit(formValue); } } diff --git a/src/app/domains/pos/modules/configs/components/rapidInvoice/form.component.html b/src/app/domains/pos/modules/configs/components/rapidInvoice/form.component.html index 3d8f896..ae16d8b 100644 --- a/src/app/domains/pos/modules/configs/components/rapidInvoice/form.component.html +++ b/src/app/domains/pos/modules/configs/components/rapidInvoice/form.component.html @@ -1,12 +1,9 @@ - - +
+
+ ثبت سریع صورت‌حساب + +
+ در صورتی که فروش شما تک محصولی / خدماتی است، این قابلیت را فعال کنید. - -
-
- ثبت سریع صورت‌حساب - -
- - - +
+
diff --git a/src/app/domains/pos/modules/configs/components/rapidInvoice/form.component.ts b/src/app/domains/pos/modules/configs/components/rapidInvoice/form.component.ts index d05a364..d14dd88 100644 --- a/src/app/domains/pos/modules/configs/components/rapidInvoice/form.component.ts +++ b/src/app/domains/pos/modules/configs/components/rapidInvoice/form.component.ts @@ -1,55 +1,33 @@ -import { AbstractForm } from '@/shared/abstractClasses'; -import { AppCardComponent } from '@/shared/components'; -import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { UikitLabelComponent } from '@/uikit'; -import { Component, inject, signal } from '@angular/core'; -import { ReactiveFormsModule } from '@angular/forms'; -import { Message } from 'primeng/message'; +import { Component, inject } from '@angular/core'; +import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; import { ToggleSwitch } from 'primeng/toggleswitch'; -import { IPosConfigRapidInvoicePayload, IPosConfigRapidInvoiceResponse } from './models'; +import { debounce, timer } from 'rxjs'; import { PosConfigRapidInvoiceService } from './services/main.service'; @Component({ selector: 'pos-config-rapid-invoice-form', templateUrl: 'form.component.html', - imports: [ - ReactiveFormsModule, - FormFooterActionsComponent, - AppCardComponent, - Message, - UikitLabelComponent, - ToggleSwitch, - ], + imports: [ReactiveFormsModule, UikitLabelComponent, ToggleSwitch], }) -export class PosConfigRapidInvoiceFormComponent extends AbstractForm< - IPosConfigRapidInvoicePayload, - IPosConfigRapidInvoiceResponse -> { +export class PosConfigRapidInvoiceFormComponent { private readonly service = inject(PosConfigRapidInvoiceService); - loading = signal(true); + private readonly fb = inject(FormBuilder); - initForm = () => { - const form = this.fb.group({ - rapidInvoice: [false], + rapidInvoice = this.fb.control(false, { nonNullable: true }); + + ngOnInit() { + const initialValues = this.service.get(); + this.rapidInvoice.setValue(initialValues); + + this.rapidInvoice.valueChanges.pipe(debounce(() => timer(300))).subscribe((value) => { + this.submit(); }); - - return form; - }; - - form = this.initForm(); - - override async ngOnInit() { - this.loading.set(true); - const initialValues = await this.service.get(); - - this.form.controls.rapidInvoice.setValue(initialValues || false); - this.loading.set(false); } - override submitForm() { - const formValue = this.form.value.rapidInvoice as IPosConfigRapidInvoicePayload; - this.toastService.success({ text: 'تغییرات با موفقیت اعمال شد.' }); + submit() { + const formValue = this.rapidInvoice.value; return this.service.submit(formValue); } } diff --git a/src/app/domains/pos/modules/configs/components/sendToFiscalActivation/form.component.html b/src/app/domains/pos/modules/configs/components/sendToFiscalActivation/form.component.html new file mode 100644 index 0000000..7e23bbc --- /dev/null +++ b/src/app/domains/pos/modules/configs/components/sendToFiscalActivation/form.component.html @@ -0,0 +1,9 @@ +
+
+ ارسال سریع صورت‌حساب به مالیات + +
+ + در صورتی که می‌خوا‌هید همزمان با ساخت صورت‌حساب، اطلاعات آن به سازمان مالیاتی ارسال شود این قابلیت را فعال کنید. + +
diff --git a/src/app/domains/pos/modules/configs/components/sendToFiscalActivation/form.component.ts b/src/app/domains/pos/modules/configs/components/sendToFiscalActivation/form.component.ts new file mode 100644 index 0000000..32e62e5 --- /dev/null +++ b/src/app/domains/pos/modules/configs/components/sendToFiscalActivation/form.component.ts @@ -0,0 +1,33 @@ +import { UikitLabelComponent } from '@/uikit'; +import { Component, inject } from '@angular/core'; +import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; +import { ToggleSwitch } from 'primeng/toggleswitch'; +import { debounce, timer } from 'rxjs'; +import { PosConfigSendToFiscalActivationService } from './services/main.service'; + +@Component({ + selector: 'pos-config-send-to-fiscal-activation-form', + templateUrl: 'form.component.html', + imports: [ReactiveFormsModule, UikitLabelComponent, ToggleSwitch], +}) +export class PosConfigSendToFiscalActivationFormComponent { + private readonly service = inject(PosConfigSendToFiscalActivationService); + + private readonly fb = inject(FormBuilder); + + sendToFiscalActivation = this.fb.control(false, { nonNullable: true }); + + ngOnInit() { + const initialValues = this.service.get(); + this.sendToFiscalActivation.setValue(initialValues); + + this.sendToFiscalActivation.valueChanges.pipe(debounce(() => timer(300))).subscribe((value) => { + this.submit(); + }); + } + + submit() { + const formValue = this.sendToFiscalActivation.value; + return this.service.submit(formValue); + } +} diff --git a/src/app/domains/pos/modules/configs/components/sendToFiscalActivation/models/index.ts b/src/app/domains/pos/modules/configs/components/sendToFiscalActivation/models/index.ts new file mode 100644 index 0000000..0008ff7 --- /dev/null +++ b/src/app/domains/pos/modules/configs/components/sendToFiscalActivation/models/index.ts @@ -0,0 +1,3 @@ +export type IPosConfigSendToFiscalActivationResponse = boolean; + +export type IPosConfigSendToFiscalActivationPayload = boolean; diff --git a/src/app/domains/pos/modules/configs/components/sendToFiscalActivation/services/main.service.ts b/src/app/domains/pos/modules/configs/components/sendToFiscalActivation/services/main.service.ts new file mode 100644 index 0000000..b932355 --- /dev/null +++ b/src/app/domains/pos/modules/configs/components/sendToFiscalActivation/services/main.service.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@angular/core'; +import { LOCAL_STORAGE_KEYS } from 'src/assets/constants'; +import { + IPosConfigSendToFiscalActivationPayload, + IPosConfigSendToFiscalActivationResponse, +} from '../models'; + +@Injectable({ providedIn: 'root' }) +export class PosConfigSendToFiscalActivationService { + get(): IPosConfigSendToFiscalActivationResponse { + const defaultPrice = Boolean( + window.localStorage.getItem(LOCAL_STORAGE_KEYS.POS_CONFIG_SEND_TO_FISCAL_ACTIVATION) === + 'true' + ); + + this.submit(defaultPrice); + return defaultPrice; + } + + submit(data: IPosConfigSendToFiscalActivationPayload) { + window.localStorage.setItem( + LOCAL_STORAGE_KEYS.POS_CONFIG_SEND_TO_FISCAL_ACTIVATION, + data.toString() + ); + } +} diff --git a/src/app/domains/pos/modules/configs/views/root.component.html b/src/app/domains/pos/modules/configs/views/root.component.html index 34d38ea..117bc37 100644 --- a/src/app/domains/pos/modules/configs/views/root.component.html +++ b/src/app/domains/pos/modules/configs/views/root.component.html @@ -1,9 +1,17 @@
- @if (info()?.guild!.code.toLocaleLowerCase() === 'gold') { - - } - - + +
+ @if (info()?.guild!.code.toLocaleLowerCase() === 'gold') { + +
+ } + +
+ +
+
+ + هریک از موارد زیر را که می‌خواهید در چاپ فاکتور نمایش داده‌ شوند را انتخاب کنید diff --git a/src/app/domains/pos/modules/configs/views/root.component.ts b/src/app/domains/pos/modules/configs/views/root.component.ts index b4911d1..470260b 100644 --- a/src/app/domains/pos/modules/configs/views/root.component.ts +++ b/src/app/domains/pos/modules/configs/views/root.component.ts @@ -3,9 +3,11 @@ import { AppCardComponent } from '@/shared/components'; import { Component, computed, inject } from '@angular/core'; import { Router } from '@angular/router'; import { Message } from 'primeng/message'; +import { PosChangePasswordFormDialogComponent } from '../components/changePassword/form.component'; import { PosConfigGoldPriceFormComponent } from '../components/goldPrice/form.component'; import { PosConfigPrintFormComponent } from '../components/print/form.component'; import { PosConfigRapidInvoiceFormComponent } from '../components/rapidInvoice/form.component'; +import { PosConfigSendToFiscalActivationFormComponent } from '../components/sendToFiscalActivation/form.component'; @Component({ selector: 'pos-config-page', @@ -16,6 +18,8 @@ import { PosConfigRapidInvoiceFormComponent } from '../components/rapidInvoice/f Message, PosConfigGoldPriceFormComponent, PosConfigRapidInvoiceFormComponent, + PosConfigSendToFiscalActivationFormComponent, + PosChangePasswordFormDialogComponent, ], }) export class PosConfigPageComponent { diff --git a/src/app/shared/components/fields/unit_price.component.ts b/src/app/shared/components/fields/unit_price.component.ts index 48c9ab5..56c770c 100644 --- a/src/app/shared/components/fields/unit_price.component.ts +++ b/src/app/shared/components/fields/unit_price.component.ts @@ -4,10 +4,17 @@ import { InputComponent } from '../input/input.component'; @Component({ selector: 'field-unit-price', - template: ``, + template: ``, imports: [ReactiveFormsModule, InputComponent], }) export class UnitPriceComponent { @Input({ required: true }) control = new FormControl(''); @Input() name = 'unit_price'; + @Input() hint = ''; } diff --git a/src/app/shared/components/input/input.component.html b/src/app/shared/components/input/input.component.html index 23d1f6b..d98eb66 100644 --- a/src/app/shared/components/input/input.component.html +++ b/src/app/shared/components/input/input.component.html @@ -1,7 +1,7 @@ @if (labelSuffix) { -
+
@if (labelTextView) { @@ -13,12 +13,12 @@
} - @if (type === "switch") { + @if (type === 'switch') { } @else { - @if (type === "price") { + @if (type === 'price') { + (onBlur)="blur.emit()" /> } @else { + (input)="onInput($event)" /> } @if (suffixTemp) { @@ -69,6 +67,6 @@ } @if (hint) { - {{ hint }} + {{ hint }} } diff --git a/src/assets/constants/localStorageKeys.const.ts b/src/assets/constants/localStorageKeys.const.ts index 53e9007..160a375 100644 --- a/src/assets/constants/localStorageKeys.const.ts +++ b/src/assets/constants/localStorageKeys.const.ts @@ -5,6 +5,7 @@ export enum LOCAL_STORAGE_KEYS { LAST_LOGIN_TIME = 'lastLoginTime', LOGIN_ATTEMPTS = 'loginAttempts', POS_CONFIG_RAPID_INVOICE = 'posConfigRapidInvoice', + POS_CONFIG_SEND_TO_FISCAL_ACTIVATION = 'posConfigSendToFiscalActivation', POS_CONFIG_PRINT = 'posConfigPrint', POS_CONFIG_GOLD_PRICE = 'posConfigGoldPrice', } diff --git a/src/assets/styles.scss b/src/assets/styles.scss index 4c3e44f..9788151 100644 --- a/src/assets/styles.scss +++ b/src/assets/styles.scss @@ -15,17 +15,27 @@ form, gap: 1rem; } -body.tenant-tis .p-drawer { +body.application-pos .p-drawer { will-change: transform; transform: translateZ(0); backface-visibility: hidden; + box-shadow: none !important; } -body.tenant-tis .p-drawer-mask { +body.application-pos .p-drawer-mask { will-change: opacity; } -body.tenant-tis .p-drawer .p-drawer-content { +body.application-pos .p-drawer .p-drawer-content { -webkit-overflow-scrolling: touch; overscroll-behavior: contain; } + +body.application-pos .p-card { + box-shadow: none !important; + border: 1px solid var(--surface-border) !important; +} + +body.application-pos .p-select { + box-shadow: none !important; +} diff --git a/src/main.ts b/src/main.ts index dafc4cf..9d997c6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -32,7 +32,7 @@ if (themeMeta) { } if (config.isPosApplication) { - document.body.classList.add('tenant-tis'); + document.body.classList.add('application-pos'); } bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));