set own datepicker and change فاکتور to صورت‌حساب

This commit is contained in:
2026-05-31 13:54:34 +03:30
parent f50219a094
commit 4ec6143068
53 changed files with 557 additions and 13514 deletions
+152 -57
View File
@@ -1,75 +1,170 @@
import { Maybe } from '@/core';
import { nowJalali } from '@/utils';
import { CommonModule } from '@angular/common';
import {
AfterViewInit,
Component,
ElementRef,
EventEmitter,
Input,
OnDestroy,
Output,
ViewChild,
} from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { Component, EventEmitter, Input, Output, ViewChild, computed, signal } from '@angular/core';
import { FormControl } from '@angular/forms';
import dayjs from 'dayjs';
import flatpickr from 'flatpickr-wrap';
import { Persian } from 'flatpickr-wrap/dist/l10n/fa';
import { BaseOptions, DateOption } from 'flatpickr-wrap/dist/types/options';
import { InputTextModule } from 'primeng/inputtext';
import jalaliday from 'jalaliday';
import { Maybe } from '@/core';
import { formatJalali } from '@/utils';
import { CommonModule } from '@angular/common';
import { InputText } from 'primeng/inputtext';
import { Popover } from 'primeng/popover';
import { UikitFieldComponent } from '../uikit-field.component';
import {
MonthInfo,
MonthlyWeekDate,
daysName,
prepareMonthInfo,
prepareWeekDayDates,
} from './monthly-calendar.helper.util';
dayjs.extend(jalaliday);
@Component({
selector: 'uikit-datepicker',
selector: 'app-datepicker',
standalone: true,
imports: [CommonModule, ReactiveFormsModule, InputTextModule, UikitFieldComponent],
templateUrl: './datepicker.component.html',
imports: [Popover, UikitFieldComponent, InputText, CommonModule],
})
export class UikitFlatpickrJalaliComponent implements AfterViewInit, OnDestroy {
@Input() control?: FormControl<Maybe<string>>;
@Input() placeholder = 'انتخاب تاریخ';
@Input() label = 'تاریخ';
@Input() disabled = false;
@Input() name: string = 'datepicker';
@Input() options: Partial<BaseOptions> = {};
@Input() showLabel: boolean = true;
@Input() showErrors: boolean = true;
@Input() hint?: string;
@Input() minDate?: DateOption;
@Input() maxDate?: DateOption;
export class MiniMonthlyCalendarComponent {
@Input() control!: FormControl<Maybe<string>>;
@Input() alignment?: 'vertical' | 'horizontal' = 'vertical';
@Input() name?: string = 'date';
@Input() label?: string = 'تاریخ';
@Input() closeOnSelect?: boolean = true;
@Input() min?: string;
@Input() max?: string;
@Input() closedDays: number[] = [];
@Output() clickOnDayEvent = new EventEmitter<MonthlyWeekDate>();
@Output() valueChange = new EventEmitter<string>();
@ViewChild('wrapper', { static: true }) wrapperRef!: ElementRef<HTMLElement>;
@ViewChild('op') op!: Popover;
private fpInstance: any | null = null;
readonly daysName = daysName;
ngAfterViewInit(): void {
this.fpInstance = flatpickr(this.wrapperRef.nativeElement, {
wrap: true,
...this.options,
locale: Persian,
disableMobile: true,
clickOpens: !this.disabled,
minDate: this.minDate,
maxDate: this.maxDate,
now: nowJalali(),
altInputClass: 'flatpicker-field w-full',
altInput: true,
dateFormat: 'Y-m-d',
altFormat: 'Y/m/d',
/**
* Current displayed month
* Gregorian YYYY-MM-DD
*/
private readonly activeDateSignal = signal(dayjs().format('YYYY-MM-DD'));
onChange: (selectedDates: Date[], dateStr: string) => {
this.valueChange.emit(dateStr);
if (this.control) this.control.setValue(dayjs(dateStr).toISOString());
},
});
/**
* Selected value
* Gregorian YYYY-MM-DD
*/
private readonly selectedDateSignal = signal<Maybe<string>>(null);
disabled = false;
monthInfo = computed<MonthInfo>(() => prepareMonthInfo(this.activeDateSignal()));
weekDaysDates = computed(() => prepareWeekDayDates(this.monthInfo(), this.min, this.max));
// -----------------------
// Getters
// -----------------------
get value(): Maybe<string> {
return this.selectedDateSignal();
}
ngOnDestroy(): void {
if (this.fpInstance?.destroy) {
this.fpInstance.destroy();
this.fpInstance = null;
get valueToShow(): Maybe<string> {
return this.selectedDateSignal() && formatJalali(this.selectedDateSignal()!);
}
get activeDate(): string {
return this.activeDateSignal();
}
// -----------------------
// Setters
// -----------------------
set value(date: Maybe<string>) {
this.selectedDateSignal.set(date);
this.control.setValue(date);
if (date) {
this.activeDateSignal.set(date);
}
}
set activeDate(date: string) {
this.activeDateSignal.set(date);
}
// -----------------------
// Navigation
// -----------------------
changeMonth(months: number): void {
const newDate = dayjs(this.activeDateSignal()).add(months, 'month').format('YYYY-MM-DD');
this.activeDateSignal.set(newDate);
}
// -----------------------
// Selection
// -----------------------
selectDate(day: MonthlyWeekDate): void {
if (this.disabled) {
return;
}
if (day.isDisabled) {
return;
}
this.value = day.gregorianDate;
this.onChange(day.gregorianDate);
this.onTouched();
if (this.closeOnSelect) {
this.op.toggle(false);
}
this.clickOnDayEvent.emit(day);
this.valueChange.emit(day.gregorianDate);
}
isSelected(day: MonthlyWeekDate): boolean {
return !!this.value && this.value === day.gregorianDate;
}
// -----------------------
// CVA
// -----------------------
private onChange: (value: Maybe<string>) => void = () => {};
private onTouched: () => void = () => {};
writeValue(value: Maybe<string>): void {
this.value = value;
}
registerOnChange(fn: (value: Maybe<string>) => void): void {
this.onChange = fn;
}
registerOnTouched(fn: () => void): void {
this.onTouched = fn;
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}
// -----------------------
// Helpers
// -----------------------
trackByIndex(index: number): number {
return index;
}
}