Refactor code structure for improved readability and maintainability
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 35 KiB |
@@ -5,20 +5,20 @@
|
||||
{
|
||||
"purpose": "maskable",
|
||||
"sizes": "192x192",
|
||||
"src": "/favicon/web-app-manifest-192x192.png",
|
||||
"src": "/web-app-manifest-192x192.png",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"purpose": "maskable",
|
||||
"sizes": "512x512",
|
||||
"src": "/favicon/web-app-manifest-512x512.png",
|
||||
"src": "/web-app-manifest-512x512.png",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"id": "/",
|
||||
"name": "مدیریت صورتحسابهای مالیاتی",
|
||||
"name": "نرم افزار صورتحسابهای مالیاتی پاژن",
|
||||
"scope": "/",
|
||||
"short_name": "مدیریت صورتحسابهای مالیاتی",
|
||||
"short_name": "پاژن",
|
||||
"start_url": "/",
|
||||
"theme_color": "#ffffff"
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 44 KiB |
@@ -64,6 +64,10 @@ export class FormErrorsService {
|
||||
if (errors['invalidMobile']) {
|
||||
out.push({ key: 'invalidMobile', message: `${label} معتبر نیست.` });
|
||||
}
|
||||
if (errors['invalidUsername']) {
|
||||
const info = errors['invalidUsername'];
|
||||
out.push({ key: 'invalidUsername', message: info.message ?? `${label} معتبر نیست.` });
|
||||
}
|
||||
// fallback: include any other error keys
|
||||
Object.keys(errors).forEach((k) => {
|
||||
if (
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import { Maybe } from '../models';
|
||||
import { ToastService } from './toast.service';
|
||||
|
||||
@@ -42,17 +41,22 @@ export class NativeBridgeService {
|
||||
}
|
||||
|
||||
isEnabled(): boolean {
|
||||
if (!this.host) return false;
|
||||
const hostEnabled = this.host?.isEnabled;
|
||||
if (typeof hostEnabled === 'function') {
|
||||
try {
|
||||
return !!hostEnabled();
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// @ts-ignore
|
||||
return !!window.NativeBridge;
|
||||
// if (window.NativeBridge) {
|
||||
|
||||
return !!environment.enableNativeBridge && !!this.host;
|
||||
// }
|
||||
// if (!this.host) return false;
|
||||
// const hostEnabled = this.host?.isEnabled;
|
||||
// if (typeof hostEnabled === 'function') {
|
||||
// try {
|
||||
// return !!hostEnabled();
|
||||
// } catch {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// return !!environment.enableNativeBridge && !!this.host;
|
||||
}
|
||||
|
||||
canPay(): boolean {
|
||||
@@ -64,22 +68,40 @@ export class NativeBridgeService {
|
||||
}
|
||||
|
||||
pay(request: INativePayRequest): any {
|
||||
this.toastService.info({ text: 'در حال پردازش پرداخت...', life: 3000 });
|
||||
const fn = this.host?.pay;
|
||||
if (typeof fn !== 'function') {
|
||||
this.toastService.error({ text: 'متاسفانه پرداخت امکانپذیر نیست.', life: 3000 });
|
||||
return { success: false, error: 'متاسفانه پرداخت امکانپذیر نیست.' };
|
||||
if (request.amount <= 10_000) {
|
||||
const errorMessage = 'برای مقادیر زیر ۱۰۰ هزار ریال، پرداخت ممکن نیست.';
|
||||
this.toastService.error({ text: errorMessage, life: 3000 });
|
||||
return {
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
};
|
||||
}
|
||||
|
||||
this.toastService.info({ text: 'در حال پردازش پرداخت...', life: 3000 });
|
||||
try {
|
||||
fn(request.amount, request.id);
|
||||
|
||||
// @ts-ignore
|
||||
window.NativeBridge.pay(request.amount, request.id || '');
|
||||
return { success: true };
|
||||
|
||||
// return { success: true, data: parsed ?? raw };
|
||||
} catch (error) {
|
||||
this.toastService.info({ text: (error as Error).message, life: 30000 });
|
||||
return { success: false, error: (error as Error).message };
|
||||
}
|
||||
// const fn = window.NativeBridge.pay(123, 'test');
|
||||
// if (typeof fn !== 'function') {
|
||||
// this.toastService.error({ text: 'متاسفانه پرداخت امکانپذیر نیست.', life: 3000 });
|
||||
// return { success: false, error: 'متاسفانه پرداخت امکانپذیر نیست.' };
|
||||
// }
|
||||
|
||||
// try {
|
||||
// fn(123, 'test');
|
||||
// // fn(request.amount, request.id || '');
|
||||
|
||||
// return { success: true };
|
||||
|
||||
// // return { success: true, data: parsed ?? raw };
|
||||
// } catch (error) {
|
||||
// this.toastService.info({ text: (error as Error).message, life: 30000 });
|
||||
// return { success: false, error: (error as Error).message };
|
||||
// }
|
||||
}
|
||||
|
||||
print(request: INativePrintRequest): INativeBridgeResult {
|
||||
@@ -87,17 +109,13 @@ export class NativeBridgeService {
|
||||
}
|
||||
|
||||
private invokePrint(payload: INativePrintRequest): INativeBridgeResult {
|
||||
const fn = this.host?.print;
|
||||
if (typeof fn !== 'function') {
|
||||
return { success: false, error: 'متاسفانه چاپ امکانپذیر نیست.' };
|
||||
}
|
||||
|
||||
try {
|
||||
fn(payload);
|
||||
|
||||
// @ts-ignore
|
||||
window.NativeBridge.print(JSON.stringify([payload]));
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
return { success: false, error: 'متاسفانه چاپ امکانپذیر نیست.' };
|
||||
this.toastService.info({ text: (error as Error).message, life: 30000 });
|
||||
return { success: false, error: (error as Error).message };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,3 +5,4 @@ export * from './mobile.validator';
|
||||
export * from './must-match.validator';
|
||||
export * from './password.validator';
|
||||
export * from './postal-code.validator';
|
||||
export * from './username.validator';
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { ValidatorFn } from '@angular/forms';
|
||||
|
||||
export function usernameValidator(): ValidatorFn {
|
||||
return (control) => {
|
||||
if (control.value === null || control.value === undefined || control.value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (control.value.length < 6) {
|
||||
return {
|
||||
minlength: {
|
||||
requiredLength: 6,
|
||||
actualLength: control.value.length,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const pattern = /^[a-zA-Z0-9_-]*$/;
|
||||
|
||||
if (!pattern.test(control.value)) {
|
||||
return {
|
||||
invalidUsername: {
|
||||
value: control.value,
|
||||
message:
|
||||
'نام کاربری فقط میتواند شامل حروف انگلیسی، اعداد، خط تیره (-) و زیرخط (_) باشد',
|
||||
},
|
||||
};
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Maybe } from '@/core';
|
||||
import { NativeBridgeService } from '@/core/services';
|
||||
import { CatalogTaxProviderStatusTagComponent } from '@/shared/catalog';
|
||||
import { AppCardComponent, KeyValueComponent } from '@/shared/components';
|
||||
@@ -46,7 +47,7 @@ export class ConsumerSaleInvoiceSharedComponent {
|
||||
private readonly nativeBridge = inject(NativeBridgeService);
|
||||
|
||||
@Input({ required: true }) loading!: boolean;
|
||||
@Input({ required: true }) invoice!: ISaleInvoiceFullResponse;
|
||||
@Input() invoice!: Maybe<ISaleInvoiceFullResponse>;
|
||||
@Input() variant: TConsumerSaleInvoice = 'full';
|
||||
@Input() backRoute?: UrlTree | string | any[];
|
||||
|
||||
@@ -55,7 +56,7 @@ export class ConsumerSaleInvoiceSharedComponent {
|
||||
@ViewChild('totalAmount', { static: false }) totalAmount!: TemplateRef<any>;
|
||||
|
||||
printInvoice = () => {
|
||||
if (this.nativeBridge.isEnabled()) {
|
||||
if (this.nativeBridge.isEnabled() && this.invoice) {
|
||||
const result = this.nativeBridge.print({
|
||||
title: 'salam',
|
||||
items: [
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
@case (1) {
|
||||
<partner-consumer-businessActivities-form-content
|
||||
[consumerId]="consumerId"
|
||||
[visible]="visible"
|
||||
(onSubmit)="onBusinessActivitySubmit($event)"
|
||||
(onClose)="close()"
|
||||
/>
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
[businessActivityId]="businessActivityId"
|
||||
[initialValues]="initialValues"
|
||||
[editMode]="editMode"
|
||||
[visible]="visible"
|
||||
(onSubmit)="onFormSubmit($event)"
|
||||
(onClose)="close()"
|
||||
/>
|
||||
|
||||
@@ -38,7 +38,6 @@ export class ConsumerBusinessActivitiesFormComponent extends AbstractForm<
|
||||
|
||||
@Input({ required: true }) consumerId!: string;
|
||||
@Input() businessActivityId!: string;
|
||||
@Input({ required: true }) visible!: boolean;
|
||||
|
||||
initForm = () => {
|
||||
const form = this.fb.group({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// import { CatalogRolesComponent } from '@/shared/catalog/roles';
|
||||
import { MustMatch } from '@/core/validators';
|
||||
import { MustMatch, usernameValidator } from '@/core/validators';
|
||||
import { AbstractForm } from '@/shared/abstractClasses';
|
||||
import {
|
||||
DeviceIdComponent,
|
||||
@@ -49,9 +49,9 @@ export class ConsumerPosFormContentComponent extends AbstractForm<IPosRequest, I
|
||||
serial_number: fieldControl.serial_number(this.initialValues?.serial_number || ''),
|
||||
device_id: fieldControl.device_id(this.initialValues?.device?.id || ''),
|
||||
provider_id: fieldControl.provider_id(this.initialValues?.provider?.id || ''),
|
||||
username: [''],
|
||||
password: [''],
|
||||
confirmPassword: [''],
|
||||
username: fieldControl.username(),
|
||||
password: fieldControl.password(),
|
||||
confirmPassword: fieldControl.confirmPassword(),
|
||||
});
|
||||
|
||||
form.controls.pos_type.valueChanges.subscribe((value) => {
|
||||
@@ -89,30 +89,8 @@ export class ConsumerPosFormContentComponent extends AbstractForm<IPosRequest, I
|
||||
form.removeControl('confirmPassword');
|
||||
form.removeValidators([MustMatch('password', 'confirmPassword')]);
|
||||
} else {
|
||||
// @ts-ignore
|
||||
form.addControl(
|
||||
'username',
|
||||
this.fb.control<string>('', {
|
||||
nonNullable: true,
|
||||
validators: fieldControl.username('')[1],
|
||||
}),
|
||||
);
|
||||
// @ts-ignore
|
||||
form.addControl(
|
||||
'password',
|
||||
this.fb.control<string>('', {
|
||||
nonNullable: true,
|
||||
validators: [Validators.required],
|
||||
}),
|
||||
);
|
||||
// @ts-ignore
|
||||
form.addControl(
|
||||
'confirmPassword',
|
||||
this.fb.control<string>('', {
|
||||
nonNullable: true,
|
||||
validators: [Validators.required],
|
||||
}),
|
||||
);
|
||||
form.controls.username.setValidators([Validators.required, usernameValidator()]);
|
||||
form.controls.username.updateValueAndValidity({ emitEvent: false });
|
||||
form.addValidators([MustMatch('password', 'confirmPassword')]);
|
||||
}
|
||||
|
||||
|
||||
@@ -102,10 +102,21 @@ export class PosLayoutComponent implements AfterViewInit {
|
||||
}
|
||||
|
||||
doPay() {
|
||||
this.nativeBridgeService.pay({
|
||||
amount: 1000,
|
||||
id: '1',
|
||||
// this.nativeBridgeService.pay({
|
||||
// amount: 10_000,
|
||||
// id: '1',
|
||||
// });
|
||||
this.nativeBridgeService.print({
|
||||
title: 'salam',
|
||||
items: [
|
||||
{
|
||||
label: 'مجموع قیمت',
|
||||
value: '10_000',
|
||||
},
|
||||
],
|
||||
});
|
||||
// @ts-ignore
|
||||
// window.NativeBridge.pay(12312, 'test');
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<field-mobile-number [control]="form.controls.mobile_number" />
|
||||
<field-national-id [control]="form.controls.national_id" />
|
||||
<field-economic-code [control]="form.controls.economic_code" />
|
||||
<field-postal-code [control]="form.controls.postal_code" />
|
||||
|
||||
<app-form-footer-actions submitLabel="ثبت مشتری و ادامه" (onCancel)="close()" (onSubmit)="submit()" />
|
||||
</form>
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
LastNameComponent,
|
||||
MobileNumberComponent,
|
||||
NationalIdComponent,
|
||||
PostalCodeComponent,
|
||||
} from '@/shared/components';
|
||||
import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component';
|
||||
import { fieldControl } from '@/shared/constants';
|
||||
@@ -26,6 +27,7 @@ import { PosLandingStore } from '../../../store/main.store';
|
||||
NationalIdComponent,
|
||||
EconomicCodeComponent,
|
||||
MobileNumberComponent,
|
||||
PostalCodeComponent,
|
||||
],
|
||||
})
|
||||
export class CustomerIndividualFormComponent extends AbstractForm<
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
}
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span>مالیات (۱۰٪)</span>
|
||||
<span>ارزش افزوده</span>
|
||||
<span [appPriceMask]="taxAmount"></span>
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
@@ -132,9 +132,14 @@ export class PosGoldPayloadFormComponent extends AbstractForm<
|
||||
const totalAmountBeforeProfit = unitWithQuantity + wageAmount + commissionAmount;
|
||||
const baseTotalAmount = totalAmountBeforeProfit + profitAmount;
|
||||
const baseAmountForDiscountCalculation =
|
||||
this.discountType() === 1 ? profitAmount : baseTotalAmount;
|
||||
this.discountType() === 1 ? profitAmount : unitWithQuantity;
|
||||
|
||||
const taxAmount = (profitAmount + commissionAmount + wageAmount - discountAmount) * 0.1;
|
||||
const taxAmount =
|
||||
(profitAmount +
|
||||
commissionAmount +
|
||||
wageAmount -
|
||||
(this.discountType() === 1 ? discountAmount : 0)) *
|
||||
0.1;
|
||||
const totalAmount = baseTotalAmount - discountAmount + taxAmount;
|
||||
|
||||
this.unitWithQuantity.set(unitWithQuantity);
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
/>
|
||||
<app-input label="شناسه اقتصادی" [control]="form.controls.customer_economic_code" name="customer_economic_code" />
|
||||
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<!-- <div class="grid grid-cols-2 gap-2">
|
||||
<uikit-datepicker label="از تاریخ فاکتور" [control]="form.controls.invoice_date_from" name="invoice_date_from" />
|
||||
<uikit-datepicker label="تا تاریخ فاکتور" [control]="form.controls.invoice_date_to" name="invoice_date_to" />
|
||||
</div>
|
||||
@@ -31,7 +31,7 @@
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<uikit-datepicker label="از تاریخ صدور" [control]="form.controls.created_at_from" name="created_at_from" />
|
||||
<uikit-datepicker label="تا تاریخ صدور" [control]="form.controls.created_at_to" name="created_at_to" />
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<app-input label="از مبلغ" [control]="form.controls.total_amount_from" name="total_amount_from" type="price" />
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Component, computed, inject, signal } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Button, ButtonDirective } from 'primeng/button';
|
||||
import { Chip } from 'primeng/chip';
|
||||
import { Paginator } from 'primeng/paginator';
|
||||
import { Skeleton } from 'primeng/skeleton';
|
||||
import { finalize } from 'rxjs';
|
||||
import { posSaleInvoicesNamedRoutes } from '../constants/routes';
|
||||
@@ -27,7 +26,6 @@ import { SaleInvoiceCardComponent } from './sale-invoice-card.component';
|
||||
Button,
|
||||
Chip,
|
||||
SaleInvoiceCardComponent,
|
||||
Paginator,
|
||||
],
|
||||
})
|
||||
export class PosSaleInvoiceListComponent {
|
||||
|
||||
@@ -51,7 +51,7 @@ export class SaleInvoiceCardComponent {
|
||||
|
||||
if (customer) {
|
||||
const { legal, individual } = customer;
|
||||
let text = 'الکترونیکی نوع اول - ';
|
||||
let text = 'نوع اول - ';
|
||||
console.log(customer);
|
||||
if (legal) {
|
||||
text += `حقوقی (${legal.company_name})`;
|
||||
@@ -61,7 +61,7 @@ export class SaleInvoiceCardComponent {
|
||||
}
|
||||
return text;
|
||||
}
|
||||
return 'الکترونیکی نوع دوم (بدون اطلاعات خریدار)';
|
||||
return 'نوع دوم (بدون اطلاعات خریدار)';
|
||||
});
|
||||
|
||||
sendInvoice() {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
emptyPlaceholderTitle="حساب کاربریای یافت نشد."
|
||||
[items]="items()"
|
||||
[loading]="loading()"
|
||||
[showDetails]="true"
|
||||
[showIndex]="false"
|
||||
(onRefresh)="refresh()"
|
||||
>
|
||||
</app-page-data-list>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
[pageTitle]="'مدیریت دستهبندیهای کالا'"
|
||||
[addNewCtaLabel]="'افزودن دستهبندی'"
|
||||
[columns]="columns"
|
||||
[showAdd]="true"
|
||||
emptyPlaceholderTitle="دستهبندی یافت نشد"
|
||||
emptyPlaceholderDescription="برای افزودن دستهبندی، روی دکمهٔ بالا کلیک کنید."
|
||||
[items]="items()"
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<shared-dialog header="فرم صنف" [(visible)]="visible" [modal]="true" [style]="{ width: '500px' }" [closable]="true" ()>
|
||||
<form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4">
|
||||
<app-input label="عنوان" [control]="form.controls.name" name="name" />
|
||||
<app-input label="کد" [control]="form.controls.code" name="code" />
|
||||
<field-name [control]="form.controls.name" />
|
||||
<field-code [control]="form.controls.code" />
|
||||
<field-invoice-templates [control]="form.controls.invoice_template" />
|
||||
|
||||
<app-form-footer-actions [submitLabel]="'ذخیره'" [loading]="submitLoading()" (onCancel)="close()" />
|
||||
</form>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { AbstractFormDialog } from '@/shared/abstractClasses';
|
||||
import { InputComponent } from '@/shared/components';
|
||||
import { CodeComponent, FieldInvoiceTemplatesComponent, NameComponent } from '@/shared/components';
|
||||
import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component';
|
||||
import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component';
|
||||
import { fieldControl } from '@/shared/constants';
|
||||
import { Component, inject, Input } from '@angular/core';
|
||||
import { ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { IGuildRequest, IGuildResponse } from '../models';
|
||||
import { GuildsService } from '../services/main.service';
|
||||
import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component';
|
||||
|
||||
interface T {
|
||||
name: string;
|
||||
@@ -15,21 +16,23 @@ interface T {
|
||||
@Component({
|
||||
selector: 'admin-guild-form',
|
||||
templateUrl: './form.component.html',
|
||||
imports: [ReactiveFormsModule, SharedDialogComponent, InputComponent, FormFooterActionsComponent],
|
||||
imports: [
|
||||
ReactiveFormsModule,
|
||||
SharedDialogComponent,
|
||||
FormFooterActionsComponent,
|
||||
NameComponent,
|
||||
CodeComponent,
|
||||
FieldInvoiceTemplatesComponent,
|
||||
],
|
||||
})
|
||||
export class GuildFormComponent extends AbstractFormDialog<IGuildRequest, IGuildResponse> {
|
||||
@Input() guildId?: string;
|
||||
private service = inject(GuildsService);
|
||||
|
||||
form = this.fb.group({
|
||||
name: [
|
||||
{ value: this.initialValues?.name, disabled: this.submitLoading() },
|
||||
Validators.required,
|
||||
],
|
||||
code: [
|
||||
{ value: this.initialValues?.code, disabled: this.submitLoading() },
|
||||
Validators.required,
|
||||
],
|
||||
name: fieldControl.name(this.initialValues?.name, true),
|
||||
code: fieldControl.code(this.initialValues?.code, true),
|
||||
invoice_template: fieldControl.invoice_template(this.initialValues?.invoice_template.id, true),
|
||||
});
|
||||
|
||||
override ngOnChanges() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<app-page-data-list
|
||||
pageTitle="مدیریت کالاها"
|
||||
[addNewCtaLabel]="'افزودن کالای'"
|
||||
[addNewCtaLabel]="'افزودن کالا'"
|
||||
[columns]="columns"
|
||||
[showAdd]="true"
|
||||
emptyPlaceholderTitle="کالایی یافت نشد"
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import ISummary from '@/core/models/summary';
|
||||
|
||||
export interface IGuildRawResponse {
|
||||
id: string;
|
||||
name: string;
|
||||
code: string;
|
||||
invoice_template: ISummary;
|
||||
created_at: string;
|
||||
}
|
||||
export interface IGuildResponse extends IGuildRawResponse {}
|
||||
@@ -9,7 +12,7 @@ export interface IGuildResponse extends IGuildRawResponse {}
|
||||
export interface IGuildRequest {
|
||||
name: string;
|
||||
code: string;
|
||||
invoice_template_type: string;
|
||||
invoice_template: string;
|
||||
}
|
||||
|
||||
export interface IGoodCategoriesRawResponse {
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
[pageTitle]="'مدیریت اصناف'"
|
||||
[addNewCtaLabel]="'افزودن صنف'"
|
||||
[columns]="columns"
|
||||
[showAdd]="true"
|
||||
emptyPlaceholderTitle="صنفی یافت نشد"
|
||||
emptyPlaceholderDescription="برای افزودن صنف، روی دکمهٔ بالا کلیک کنید."
|
||||
[items]="items()"
|
||||
[loading]="loading()"
|
||||
[showDetails]="true"
|
||||
[showAdd]="true"
|
||||
[showEdit]="true"
|
||||
(onAdd)="openAddForm()"
|
||||
(onDetails)="toSinglePage($event)"
|
||||
(onRefresh)="refresh()"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
emptyPlaceholderDescription="برای افزودن حساب کاربری، روی دکمهٔ بالا کلیک کنید."
|
||||
[items]="items()"
|
||||
[loading]="loading()"
|
||||
[showIndex]="false"
|
||||
(onAdd)="openAddForm()"
|
||||
(onRefresh)="refresh()"
|
||||
>
|
||||
|
||||
@@ -8,12 +8,11 @@
|
||||
>
|
||||
<form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4">
|
||||
<app-shared-upload-file accept="image/*" name="image" (onSelect)="changeFile($event)" />
|
||||
<app-input label="عنوان" [control]="form.controls.name" name="name" />
|
||||
<app-input label="کد" [control]="form.controls.code" name="code" />
|
||||
<field-name [control]="form.controls.name" />
|
||||
<field-code [control]="form.controls.code" />
|
||||
@if (!editMode) {
|
||||
<p-divider align="center"> اطلاعات ورود </p-divider>
|
||||
<!-- @ts-ignore -->
|
||||
<app-input label="نام کاربری" [control]="form.controls.username" name="username" [isLtrInput]="true" />
|
||||
<field-username [control]="form.controls.username" />
|
||||
<shared-password-input
|
||||
[passwordControl]="form.controls.password"
|
||||
[confirmPasswordControl]="form.controls.confirmPassword"
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import { Maybe } from '@/core';
|
||||
import { MustMatch } from '@/core/validators';
|
||||
import { MustMatch, usernameValidator } from '@/core/validators';
|
||||
import { AbstractFormDialog } from '@/shared/abstractClasses';
|
||||
import { InputComponent, SharedPasswordInputComponent } from '@/shared/components';
|
||||
import {
|
||||
CodeComponent,
|
||||
NameComponent,
|
||||
SharedPasswordInputComponent,
|
||||
UsernameComponent,
|
||||
} from '@/shared/components';
|
||||
import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component';
|
||||
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, computed, inject, Input, signal } from '@angular/core';
|
||||
import { ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
@@ -19,11 +25,13 @@ import { PartnersService } from '../services/main.service';
|
||||
imports: [
|
||||
ReactiveFormsModule,
|
||||
SharedDialogComponent,
|
||||
InputComponent,
|
||||
FormFooterActionsComponent,
|
||||
Divider,
|
||||
SharedPasswordInputComponent,
|
||||
SharedUploadFileComponent,
|
||||
UsernameComponent,
|
||||
NameComponent,
|
||||
CodeComponent,
|
||||
],
|
||||
})
|
||||
export class GuildFormComponent extends AbstractFormDialog<IPartnerRequest, IPartnerResponse> {
|
||||
@@ -32,35 +40,16 @@ export class GuildFormComponent extends AbstractFormDialog<IPartnerRequest, IPar
|
||||
|
||||
private initForm() {
|
||||
const form = this.fb.group({
|
||||
name: [this.initialValues?.name, [Validators.required]],
|
||||
code: [this.initialValues?.code, [Validators.required]],
|
||||
username: [''],
|
||||
password: [''],
|
||||
confirmPassword: [''],
|
||||
name: fieldControl.name(this.initialValues?.name || ''),
|
||||
code: fieldControl.code(this.initialValues?.code || ''),
|
||||
username: fieldControl.username(),
|
||||
password: fieldControl.password(),
|
||||
confirmPassword: fieldControl.confirmPassword(),
|
||||
});
|
||||
|
||||
if (!this.editMode) {
|
||||
form.addControl(
|
||||
'username',
|
||||
this.fb.control<string>('', {
|
||||
nonNullable: true,
|
||||
validators: [Validators.required],
|
||||
}),
|
||||
);
|
||||
form.addControl(
|
||||
'password',
|
||||
this.fb.control<string>('', {
|
||||
nonNullable: true,
|
||||
validators: [Validators.required],
|
||||
}),
|
||||
);
|
||||
form.addControl(
|
||||
'confirmPassword',
|
||||
this.fb.control<string>('', {
|
||||
nonNullable: true,
|
||||
validators: [Validators.required],
|
||||
}),
|
||||
);
|
||||
form.controls.username.setValidators([Validators.required, usernameValidator()]);
|
||||
form.controls.username.updateValueAndValidity({ emitEvent: false });
|
||||
form.addValidators([MustMatch('password', 'confirmPassword')]);
|
||||
} else {
|
||||
// @ts-ignore
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<p-avatar
|
||||
[image]="item.logo_url"
|
||||
[title]="item.name"
|
||||
[label]="item.logo_url ? '' : item.code.slice(0, 2)"
|
||||
[label]="item.logo_url ? '' : item.name.slice(0, 2)"
|
||||
shape="square"
|
||||
size="large"
|
||||
></p-avatar>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { MustMatch } from '@/core/validators';
|
||||
import { MustMatch, usernameValidator } from '@/core/validators';
|
||||
import { AbstractFormDialog } from '@/shared/abstractClasses';
|
||||
import { InputComponent, SharedPasswordInputComponent } from '@/shared/components';
|
||||
import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component';
|
||||
import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component';
|
||||
import { fieldControl } from '@/shared/constants';
|
||||
import { Component, computed, inject, Input } from '@angular/core';
|
||||
import { ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { Divider } from 'primeng/divider';
|
||||
@@ -27,35 +28,16 @@ export class ProviderFormComponent extends AbstractFormDialog<IProviderRequest,
|
||||
|
||||
private initForm() {
|
||||
const form = this.fb.group({
|
||||
name: [this.initialValues?.name, [Validators.required]],
|
||||
code: [this.initialValues?.code, [Validators.required]],
|
||||
username: [''],
|
||||
password: [''],
|
||||
confirmPassword: [''],
|
||||
name: fieldControl.name(this.initialValues?.name || ''),
|
||||
code: fieldControl.code(this.initialValues?.code || ''),
|
||||
username: fieldControl.username(),
|
||||
password: fieldControl.password(),
|
||||
confirmPassword: fieldControl.confirmPassword(),
|
||||
});
|
||||
|
||||
if (!this.editMode) {
|
||||
form.addControl(
|
||||
'username',
|
||||
this.fb.control<string>('', {
|
||||
nonNullable: true,
|
||||
validators: [Validators.required],
|
||||
}),
|
||||
);
|
||||
form.addControl(
|
||||
'password',
|
||||
this.fb.control<string>('', {
|
||||
nonNullable: true,
|
||||
validators: [Validators.required],
|
||||
}),
|
||||
);
|
||||
form.addControl(
|
||||
'confirmPassword',
|
||||
this.fb.control<string>('', {
|
||||
nonNullable: true,
|
||||
validators: [Validators.required],
|
||||
}),
|
||||
);
|
||||
form.controls.username.setValidators([Validators.required, usernameValidator()]);
|
||||
form.controls.username.updateValueAndValidity({ emitEvent: false });
|
||||
form.addValidators([MustMatch('password', 'confirmPassword')]);
|
||||
} else {
|
||||
// @ts-ignore
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
pageTitle="مدیریت حسابهای کاربری"
|
||||
[addNewCtaLabel]="'افزودن حساب کاربری'"
|
||||
[columns]="columns"
|
||||
[showAdd]="true"
|
||||
emptyPlaceholderTitle="حساب کاربریای یافت نشد."
|
||||
emptyPlaceholderDescription="برای افزودن حساب کاربری، روی دکمهٔ بالا کلیک کنید."
|
||||
[items]="items()"
|
||||
[loading]="loading()"
|
||||
[showDetails]="true"
|
||||
[showAdd]="true"
|
||||
[showIndex]="false"
|
||||
(onAdd)="openAddForm()"
|
||||
(onRefresh)="refresh()"
|
||||
>
|
||||
|
||||
@@ -3,8 +3,12 @@
|
||||
<app-menu></app-menu>
|
||||
</div>
|
||||
<div class="shrink-0 sticky bottom-0">
|
||||
<div class="py-2 w-full text-center">
|
||||
<span class="text-muted-color text-sm font-medium">ارایه شده توسط (برند نرمافزار)</span>
|
||||
<p-divider class="mb-1!" />
|
||||
<div class="py-2 w-full text-center flex items-center gap-2">
|
||||
<div class="w-10 h-10">
|
||||
<img [src]="logo" alt="Logo" class="object-contain" />
|
||||
</div>
|
||||
<span class="text-muted-color text-sm font-medium">مدیریت صورتحسابهای مالیاتی پاژن</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { Component, ElementRef } from '@angular/core';
|
||||
import { Divider } from 'primeng/divider';
|
||||
import images from 'src/assets/images';
|
||||
import { AppMenu } from './app.menu.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-sidebar',
|
||||
standalone: true,
|
||||
imports: [AppMenu],
|
||||
imports: [AppMenu, Divider],
|
||||
templateUrl: './app.sidebar.component.html',
|
||||
})
|
||||
export class AppSidebar {
|
||||
constructor(public el: ElementRef) {}
|
||||
|
||||
logo = images.logo;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
>
|
||||
<div class="flex flex-col gap-4 text-center mb-10 items-center justify-center">
|
||||
<img [src]="logo" alt="مدیریت صورتحسابهای مالیاتی" class="w-20 h-auto" />
|
||||
<span class="text-lg font-bold"> به پنل مدیریت صورتحسابهای مالیاتی خوش آمدید. </span>
|
||||
<span class="text-lg font-bold"> به پنل مدیریت صورتحسابهای مالیاتی پاژن خوش آمدید. </span>
|
||||
</div>
|
||||
|
||||
<!-- @if (activeStep() === "login") { -->
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
import { IAuthResponse, TRoles } from '@/core';
|
||||
import { ToastService } from '@/core/services/toast.service';
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
Output,
|
||||
signal,
|
||||
inject,
|
||||
} from '@angular/core';
|
||||
import { Component, EventEmitter, inject, Input, Output, signal } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { ButtonDirective } from 'primeng/button';
|
||||
import images from 'src/assets/images';
|
||||
import { LoginComponent } from './login/login.component';
|
||||
|
||||
@@ -18,7 +10,7 @@ type TSteps = 'login' | 'otp' | 'modifyLoginInfo' | 'signup';
|
||||
@Component({
|
||||
selector: 'app-auth',
|
||||
templateUrl: './auth.component.html',
|
||||
imports: [LoginComponent, ButtonDirective],
|
||||
imports: [LoginComponent],
|
||||
})
|
||||
export class AuthComponent {
|
||||
@Input() redirectUrl!: string;
|
||||
@@ -91,5 +83,4 @@ export class AuthComponent {
|
||||
}
|
||||
this.router.navigateByUrl(redirectUrl);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
}
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span>مالیات (۱۰٪)</span>
|
||||
<span>ارزش افزوده</span>
|
||||
<span [appPriceMask]="priceInfo().taxAmount"></span>
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
@@ -20,4 +20,5 @@ export const ENUMS_API_ROUTES = {
|
||||
goodPricingModel: `${baseUrl}/good_pricing_model`,
|
||||
consumerRole: `${baseUrl}/consumer_role`,
|
||||
fiscalResponseStatus: `${baseUrl}/tsp_provider_response_status`,
|
||||
invoice_template_type: `${baseUrl}/invoice_templates`,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'abstract-field',
|
||||
template: '',
|
||||
imports: [ReactiveFormsModule],
|
||||
})
|
||||
export abstract class AbstractFieldComponent<T = string> {
|
||||
@Input({ required: true }) control!: FormControl<T>;
|
||||
@Input({ required: true }) name!: string;
|
||||
@Input({ required: true }) label!: string;
|
||||
}
|
||||
@@ -10,6 +10,7 @@ export * from './email.component';
|
||||
export * from './first_name.component';
|
||||
export * from './fiscal_id.component';
|
||||
export * from './guild_id.component';
|
||||
export * from './invoice_templates.component';
|
||||
export * from './last_name.component';
|
||||
export * from './legal_name.component';
|
||||
export * from './license_starts_at.component';
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { EnumSelectComponent } from '@/shared/apiEnum/select.component';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'field-invoice-templates',
|
||||
template: `<app-enum-select
|
||||
type="invoice_template_type"
|
||||
[customLabel]="label"
|
||||
[control]="control"
|
||||
[name]="name"
|
||||
/>`,
|
||||
imports: [ReactiveFormsModule, EnumSelectComponent],
|
||||
})
|
||||
export class FieldInvoiceTemplatesComponent {
|
||||
@Input({ required: true }) control = new FormControl<string>('');
|
||||
@Input() name = 'invoice-template';
|
||||
@Input() label = 'قالب فاکتور';
|
||||
}
|
||||
@@ -1,4 +1,10 @@
|
||||
import { fiscalCodeValidator, mobileValidator, postalCodeValidator } from '@/core/validators';
|
||||
import {
|
||||
fiscalCodeValidator,
|
||||
mobileValidator,
|
||||
password,
|
||||
postalCodeValidator,
|
||||
usernameValidator,
|
||||
} from '@/core/validators';
|
||||
import { nationalIdValidator } from '@/core/validators/national-id.validator';
|
||||
import { ValidatorFn, Validators } from '@angular/forms';
|
||||
|
||||
@@ -7,6 +13,10 @@ type ControlConfig = [any, ValidatorFn[]];
|
||||
|
||||
export const fieldControl = {
|
||||
name: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
invoice_template: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
code: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
description: (value = '', isRequired = false): ControlConfig => [
|
||||
value,
|
||||
@@ -61,11 +71,14 @@ export const fieldControl = {
|
||||
value,
|
||||
isRequired ? [Validators.required, nationalIdValidator()] : [nationalIdValidator()],
|
||||
],
|
||||
postal_code: (value = '', isRequired = true): ControlConfig => [
|
||||
postal_code: (value = '', isRequired = false): ControlConfig => [
|
||||
value,
|
||||
isRequired ? [Validators.required, postalCodeValidator()] : [postalCodeValidator()],
|
||||
],
|
||||
username: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
username: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? [...required(), usernameValidator()] : [usernameValidator()],
|
||||
],
|
||||
economic_code: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
@@ -174,4 +187,13 @@ export const fieldControl = {
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
|
||||
password: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? [...required(), password()] : [password()],
|
||||
],
|
||||
confirmPassword: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Export the public URL for static assets so bundlers don't need a loader for binary imports.
|
||||
// The Angular CLI / build serves files in `src/assets` at `/assets`.
|
||||
import errors from './errors';
|
||||
import login from './login.webp';
|
||||
import login from './login.jpg';
|
||||
import logo from './logo.png';
|
||||
import { placeholders } from './placeholders';
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 586 KiB |
|
Before Width: | Height: | Size: 524 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 32 KiB |
@@ -2,7 +2,7 @@
|
||||
<html lang="fa" dir="rtl">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>پنل مدیریت صورتحسابهای مالیاتی</title>
|
||||
<title>مدیریت صورتحسابهای مالیاتی پاژن</title>
|
||||
<base href="/" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||