diff --git a/src/app/core/state/base-store.ts b/src/app/core/state/base-store.ts index f83d4be..e625df1 100644 --- a/src/app/core/state/base-store.ts +++ b/src/app/core/state/base-store.ts @@ -13,6 +13,7 @@ export interface BaseState { items?: T; meta?: Maybe; isRefreshing?: boolean; + initialized: boolean; } /** @@ -77,6 +78,7 @@ export abstract class BaseStore { readonly state: Signal; readonly loading: Signal; readonly error: Signal>; + readonly initialized: Signal; // Observable for RxJS compatibility readonly state$: Observable; @@ -89,6 +91,7 @@ export abstract class BaseStore { this.state = this._state.asReadonly(); this.loading = computed(() => this._state().loading); this.error = computed(() => this._state().error); + this.initialized = computed(() => this._state().initialized); // Observable for RxJS compatibility this.state$ = this._stateSubject.asObservable(); diff --git a/src/app/core/state/global/global.store.ts b/src/app/core/state/global/global.store.ts index 2d8afa1..de87c6e 100644 --- a/src/app/core/state/global/global.store.ts +++ b/src/app/core/state/global/global.store.ts @@ -85,6 +85,7 @@ export class GlobalStore extends BaseStore { constructor() { super({ + initialized: false, loading: false, error: null, sidebarCollapsed: false, @@ -300,6 +301,7 @@ export class GlobalStore extends BaseStore { */ reset(): void { this.setState({ + initialized: false, loading: false, error: null, sidebarCollapsed: false, diff --git a/src/app/modules/auth/state/auth.store.ts b/src/app/modules/auth/state/auth.store.ts index 45c2183..ff55cd8 100644 --- a/src/app/modules/auth/state/auth.store.ts +++ b/src/app/modules/auth/state/auth.store.ts @@ -48,6 +48,7 @@ export class AuthStore extends BaseStore { constructor() { super({ + initialized: false, loading: false, error: null, user: null, @@ -385,6 +386,7 @@ export class AuthStore extends BaseStore { */ reset(): void { this.setState({ + initialized: false, loading: false, error: null, user: null, diff --git a/src/app/modules/cardex/components/filters/filters.component.html b/src/app/modules/cardex/components/filters/filters.component.html index b134a27..992f8b4 100644 --- a/src/app/modules/cardex/components/filters/filters.component.html +++ b/src/app/modules/cardex/components/filters/filters.component.html @@ -1,11 +1,11 @@
- - + +
- +
diff --git a/src/app/modules/cardex/components/filters/filters.component.ts b/src/app/modules/cardex/components/filters/filters.component.ts index 71ab15c..d77b414 100644 --- a/src/app/modules/cardex/components/filters/filters.component.ts +++ b/src/app/modules/cardex/components/filters/filters.component.ts @@ -1,10 +1,17 @@ +import { Maybe } from '@/core'; import { InventoriesSelectComponent } from '@/modules/inventories/components'; import { ProductsSelectComponent } from '@/modules/products/components/select/select.component'; import { UikitFlatpickrJalaliComponent } from '@/uikit'; -import { Component, inject } from '@angular/core'; +import { Component, EventEmitter, inject, Output } from '@angular/core'; import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { ButtonDirective } from 'primeng/button'; +import { + ICardexInventorySummary, + ICardexProductSummary, + ICardexRequestPayload, +} from '../../models'; +import { CardexStore } from '../../store/main.store'; @Component({ selector: 'cardex-filters', @@ -20,12 +27,32 @@ import { ButtonDirective } from 'primeng/button'; export class CardexFiltersComponent { private route = inject(ActivatedRoute); private fb = inject(FormBuilder); - constructor() {} + private store = inject(CardexStore); + @Output() onSearch = new EventEmitter(); + + filters = {} as Partial; + + constructor(private router: Router) { + this.filters = this.store.filters(); + } form = this.fb.group({ - inventoryId: [Number(this.route.snapshot.queryParams['inventoryId']) || 0], - productId: [Number(this.route.snapshot.queryParams['productId']) || 0], - startDate: [this.route.snapshot.queryParams['startDate'] || ''], - endDate: [this.route.snapshot.queryParams['endDate'] || ''], + inventoryId: [this.filters.inventoryId || 0], + productId: [this.filters.productId || 0], + startDate: [this.filters.startDate || ''], + endDate: [this.filters.endDate || ''], }); + + onChangeInventory(inventory: Maybe) { + this.store.setInventory(inventory); + } + onChangeProduct(product: Maybe) { + this.store.setProduct(product); + } + + search() { + this.store.updateFilter(this.form.value as ICardexRequestPayload); + + this.store.getCardex(); + } } diff --git a/src/app/modules/cardex/components/templates/inventory.component.html b/src/app/modules/cardex/components/templates/inventory.component.html new file mode 100644 index 0000000..9492d25 --- /dev/null +++ b/src/app/modules/cardex/components/templates/inventory.component.html @@ -0,0 +1,73 @@ + + + + ردیف + تاریخ + شرح + شماره + طرف حساب + کالا + ورودی + خروجی + مانده + + + تعداد + فی + تعداد + فی + تعداد + + + + + + + {{ i + 1 }} + + + + + {{ item.referenceId || "-" }} + + {{ (item.referenceType === "SALES" ? item.customer?.name : item.supplier?.name) || "-" }} + + {{ item.product.name || "-" }} + + {{ item.type === "IN" ? item.quantity : 0 }} + + + @if (item.type === "IN") { + + } @else { + 0 + } + + + {{ item.type === "OUT" ? item.quantity : 0 }} + + + @if (item.type === "OUT") { + + } @else { + 0 + } + + {{ item.remainedInStock }} + + + + diff --git a/src/app/modules/cardex/components/templates/inventory.component.ts b/src/app/modules/cardex/components/templates/inventory.component.ts new file mode 100644 index 0000000..367530e --- /dev/null +++ b/src/app/modules/cardex/components/templates/inventory.component.ts @@ -0,0 +1,22 @@ +import { CatalogMovementReferenceTagComponent } from '@/shared/catalog/movementReferenceTypes'; +import { JalaliDateDirective, PriceMaskDirective } from '@/shared/directives'; +import { Component, Input } from '@angular/core'; +import { TableModule } from 'primeng/table'; +import { IInventoryCardex } from './types'; + +@Component({ + selector: 'cardex-templates-inventory', + templateUrl: './inventory.component.html', + imports: [ + TableModule, + JalaliDateDirective, + CatalogMovementReferenceTagComponent, + PriceMaskDirective, + ], + hostDirectives: [PriceMaskDirective], +}) +export class CardexComponent { + @Input() items: IInventoryCardex[] = []; + @Input() loading: boolean = false; + constructor() {} +} diff --git a/src/app/modules/cardex/components/templates/types.ts b/src/app/modules/cardex/components/templates/types.ts new file mode 100644 index 0000000..9a0837d --- /dev/null +++ b/src/app/modules/cardex/components/templates/types.ts @@ -0,0 +1,3 @@ +import { ICardexResponse } from '../../models'; + +export interface IInventoryCardex extends ICardexResponse {} diff --git a/src/app/modules/cardex/models/io.d.ts b/src/app/modules/cardex/models/io.d.ts index d213166..47d0430 100644 --- a/src/app/modules/cardex/models/io.d.ts +++ b/src/app/modules/cardex/models/io.d.ts @@ -1,66 +1,44 @@ -import { - IInventoryInfo, - IInventoryMovement, - IInventoryStockProduct, - IInventoryTransferRequestItem, -} from './types'; +import { ICardexInventorySummary, ICardexProductSummary } from './types'; -export interface IInventorySummaryRawResponse { +export interface ICardexRawResponse { id: number; - name: string; - location: string; - isActive: boolean; - isPointOfSale: boolean; - createdAt: string; - updatedAt: string; - deletedAt: Maybe; -} - -export interface IInventorySummaryResponse extends IInventorySummaryRawResponse {} - -export interface IInventoryDetailRawResponse extends IInventorySummaryRawResponse { - availableProductTypes: number; - availableProductCount: number; - availableProductsCost: number; -} - -export interface IInventoryDetailResponse extends IInventoryDetailRawResponse {} - -export interface IInventoryRequest { - name: string; - location: string; - isActive: boolean; -} - -export interface IInventoryStockMovementRawResponse { - receiptId: string; - count: number; - info: IInventoryInfo; - movements: IInventoryMovement[]; -} - -export interface IInventoryStockMovementResponse extends IInventoryStockMovementRawResponse {} - -export interface IInventoryTransferRequest { - code: string; - description: string; - fromInventoryId: number; - toInventoryId: number; - items: IInventoryTransferRequestItem[]; -} - -export interface IInventoryStockRawResponse { + type: string; quantity: number; + fee: number; + totalCost: number; + referenceType: string; + referenceId: string; + createdAt: string; avgCost: number; - product: IInventoryStockProduct; + product: ICardexProductSummary; + inventory: ICardexInventorySummary; + supplier?: { + id: number; + firstName: string; + lastName: string; + }; + customer?: { + id: number; + firstName: string; + lastName: string; + }; + counterInventory?: ICardexInventorySummary; } -export interface IInventoryStockResponse extends IInventoryStockRawResponse {} - -export interface IInventoryStockQuery { - isAvailable?: boolean; +export interface ICardexResponse extends ICardexRawResponse { + supplier?: { + id: number; + name: string; + }; + customer?: { + id: number; + name: string; + }; } -export interface IInventoryProductCardexRawResponse extends IInventoryStockMovementRawResponse {} - -export interface IInventoryProductCardexResponse extends IInventoryProductCardexRawResponse {} +export interface ICardexRequestPayload { + startDate?: string; + endDate?: string; + inventoryId?: number; + productId?: number; +} diff --git a/src/app/modules/cardex/models/types.ts b/src/app/modules/cardex/models/types.ts index 2093824..9eb1ca3 100644 --- a/src/app/modules/cardex/models/types.ts +++ b/src/app/modules/cardex/models/types.ts @@ -1,42 +1,10 @@ -import { IProductRawResponse } from '@/modules/products/models'; - -export interface IInventoryMovement { - id: number; - quantity: number; - fee: number; - totalCost: number; - avgCost: number; - product: IInventoryProduct; - remainedInStock: number; -} - -export interface IInventoryProduct { - id: number; +export interface ICardexInventorySummary { + name: string; + id: number; +} + +export interface ICardexProductSummary { name: string; - description: null; sku: string; - barcode: string; - createdAt: string; - updatedAt: string; - deletedAt: null; - brandId: number; - categoryId: number; + id: number; } - -export interface IInventoryInfo { - date: string; - type: string; - quantity: number; - fee: number; - totalCost: number; - referenceType: string; - referenceId: string; - createdAt: string; -} - -export interface IInventoryTransferRequestItem { - productId: number; - count: number; -} - -export interface IInventoryStockProduct extends IProductRawResponse {} diff --git a/src/app/modules/cardex/services/main.service.ts b/src/app/modules/cardex/services/main.service.ts new file mode 100644 index 0000000..f6407bd --- /dev/null +++ b/src/app/modules/cardex/services/main.service.ts @@ -0,0 +1,43 @@ +import { IPaginatedResponse } from '@/core/models/service.model'; +import { getFullName } from '@/utils'; +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { map, Observable } from 'rxjs'; +import { CARDEX_API_ROUTES } from '../constants'; +import { ICardexRawResponse, ICardexRequestPayload, ICardexResponse } from '../models'; + +@Injectable({ providedIn: 'root' }) +export class CardexService { + constructor(private http: HttpClient) {} + + private apiRoutes = CARDEX_API_ROUTES; + + getCardex(params: ICardexRequestPayload): Observable> { + return this.http + .get>(this.apiRoutes.cardex(), { + params: { ...params }, + }) + .pipe( + map((res) => { + return { + meta: res.meta, + data: res.data.map((item) => ({ + ...item, + supplier: item.supplier + ? { + ...item.supplier, + name: getFullName(item.supplier), + } + : undefined, + customer: item.customer + ? { + ...item.customer, + name: getFullName(item.customer), + } + : undefined, + })), + }; + }), + ); + } +} diff --git a/src/app/modules/cardex/store/main.store.ts b/src/app/modules/cardex/store/main.store.ts new file mode 100644 index 0000000..728bd1a --- /dev/null +++ b/src/app/modules/cardex/store/main.store.ts @@ -0,0 +1,187 @@ +import { Maybe } from '@/core'; +import { ToastService } from '@/core/services/toast.service'; +import { PaginatedState, PaginatedStore } from '@/core/state'; +import { computed, Injectable } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { + ICardexInventorySummary, + ICardexProductSummary, + ICardexRequestPayload, + ICardexResponse, +} from '../models'; +import { CardexService } from '../services/main.service'; + +export interface CardexState extends PaginatedState { + filters: ICardexRequestPayload; + selectedInventory: Maybe; + selectedProduct: Maybe; + variant: 'inventory' | 'product' | 'both'; + canChangeVariant: boolean; +} + +@Injectable({ + providedIn: 'root', +}) +export class CardexStore extends PaginatedStore { + private readonly _cardexState = { + isRefreshing: false, + filters: {}, + selectedInventory: null, + selectedProduct: null, + variant: 'both', + canChangeVariant: true, + }; + + constructor( + private router: Router, + private activeRoute: ActivatedRoute, + private service: CardexService, + private toastService: ToastService, + ) { + const queryParams = activeRoute.snapshot.queryParams; + + super({ + loading: false, + initialized: false, + error: null, + isRefreshing: false, + items: [], + totalCount: 0, + currentPage: Number(queryParams['page']) || 1, + pageSize: Number(queryParams['pageSize']) || 30, + totalPages: 0, + hasMore: false, + filters: { + inventoryId: queryParams['inventoryId'], + productId: queryParams['productId'], + startDate: queryParams['startDate'], + endDate: queryParams['endDate'], + }, + selectedInventory: null, + selectedProduct: null, + variant: 'both', + canChangeVariant: true, + }); + this.initial(); + } + + readonly filters = computed(() => this._state().filters); + readonly variant = computed(() => this._state().variant); + readonly canChangeVariant = computed(() => this._state().canChangeVariant); + readonly cardexTitle = computed(() => { + switch (this._state().variant) { + case 'product': + return `کاردکس کالای ${this._state().selectedProduct?.name}`; + case 'inventory': + return `کاردکس انبار ${this._state().selectedInventory?.name}`; + default: + return 'کاردکس'; + } + }); + + /** + * Reset state to initial values + */ + reset(): void { + const queryParams = this.activeRoute.snapshot.queryParams; + this.setState({ + loading: false, + initialized: false, + error: null, + isRefreshing: false, + items: [], + totalCount: 0, + currentPage: Number(queryParams['page']) || 1, + pageSize: Number(queryParams['pageSize']) || 30, + totalPages: 0, + hasMore: false, + filters: { + inventoryId: queryParams['inventoryId'], + productId: queryParams['productId'], + startDate: queryParams['startDate'], + endDate: queryParams['endDate'], + }, + selectedInventory: null, + selectedProduct: null, + variant: 'both', + canChangeVariant: this.state().canChangeVariant, + }); + } + + initial() { + this.getCardex(); + } + + getCardex() { + const filters = this.validateFilters(); + if (filters) { + this.patchState({ initialized: true }); + this.setLoading(true); + this.service.getCardex(filters).subscribe({ + next: (res) => { + this.setItems(res.data, res.meta); + }, + error: (err) => { + this.setError(err); + }, + }); + } + } + + private validateFilters() { + const { productId, inventoryId, startDate, endDate } = this.filters(); + const filters = {} as Partial; + Object.entries(this.filters()).forEach(([key, value]) => { + if (value) { + filters[key as keyof ICardexRequestPayload] = value; + } + }); + if (!productId && !inventoryId) { + this.toastService.warn({ text: 'برای مشاهده‌ی کاردکس حداقل انبار و یا کالا را مشخص کنید' }); + return false; + } + return filters; + } + + onPageChange(page: number) { + this.setCurrentPage(page); + this.getCardex(); + } + + updateFilter(filters: ICardexRequestPayload) { + this.updateVariant(); + console.log(filters); + + this.router.navigate([], { + queryParams: filters, + skipLocationChange: true, + }); + this.patchState({ filters }); + } + + setProduct(product: Maybe) { + this.patchState({ selectedProduct: product }); + } + setInventory(inventory: Maybe) { + this.patchState({ selectedInventory: inventory }); + } + + setCanChangeVariant(status: boolean) { + this.patchState({ canChangeVariant: status }); + } + + private updateVariant() { + const inventoryIsSelected = Boolean(this._state().selectedInventory); + const productIsSelected = Boolean(this._state().selectedProduct); + let variant = 'both' as 'product' | 'inventory' | 'both'; + if (inventoryIsSelected) { + if (!productIsSelected) { + variant = 'inventory'; + } + } else if (productIsSelected) { + variant = 'product'; + } + + this.patchState({ variant }); + } +} diff --git a/src/app/modules/cardex/views/cardex.component.html b/src/app/modules/cardex/views/cardex.component.html index 1fc4011..d2b23bb 100644 --- a/src/app/modules/cardex/views/cardex.component.html +++ b/src/app/modules/cardex/views/cardex.component.html @@ -3,6 +3,13 @@ - + @if (!initialized) { + + } @else { +

{{ cardexTitle }}

+ @if (variant === "inventory") { + + } + }
diff --git a/src/app/modules/cardex/views/cardex.component.ts b/src/app/modules/cardex/views/cardex.component.ts index c940f9c..dec75eb 100644 --- a/src/app/modules/cardex/views/cardex.component.ts +++ b/src/app/modules/cardex/views/cardex.component.ts @@ -2,12 +2,30 @@ import { UikitEmptyStateComponent } from '@/uikit'; import { Component } from '@angular/core'; import { Card } from 'primeng/card'; import { CardexFiltersComponent } from '../components'; +import { CardexComponent } from '../components/templates/inventory.component'; +import { CardexStore } from '../store/main.store'; @Component({ selector: 'app-cardex', templateUrl: './cardex.component.html', - imports: [Card, CardexFiltersComponent, UikitEmptyStateComponent], + imports: [Card, CardexFiltersComponent, UikitEmptyStateComponent, CardexComponent], }) export class CardexPageComponent { - constructor() {} + constructor(private store: CardexStore) {} + + get cardexTitle() { + return this.store.cardexTitle(); + } + get initialized() { + return this.store.initialized(); + } + get loading() { + return this.store.loading(); + } + get variant() { + return this.store.variant(); + } + get items() { + return this.store.items(); + } } diff --git a/src/app/modules/inventories/components/select/select.component.html b/src/app/modules/inventories/components/select/select.component.html index 4151fd3..17849a5 100644 --- a/src/app/modules/inventories/components/select/select.component.html +++ b/src/app/modules/inventories/components/select/select.component.html @@ -8,6 +8,7 @@ [showClear]="true" [filter]="true" appendTo="body" + (onChange)="change($event.value)" > diff --git a/src/app/modules/products/components/select/select.component.html b/src/app/modules/products/components/select/select.component.html index 8632922..a08301c 100644 --- a/src/app/modules/products/components/select/select.component.html +++ b/src/app/modules/products/components/select/select.component.html @@ -8,6 +8,7 @@ [showClear]="true" [filter]="true" appendTo="body" + (onChange)="change($event.value)" > diff --git a/src/app/modules/products/models/io.d.ts b/src/app/modules/products/models/io.d.ts index 5ff6772..026fc7a 100644 --- a/src/app/modules/products/models/io.d.ts +++ b/src/app/modules/products/models/io.d.ts @@ -9,7 +9,7 @@ export interface IProductRawResponse { name: string; description?: string; barcode?: Maybe; - sku?: Maybe; + sku: string; // productType: string; brand?: IProductBrand; category?: IProductCategory; diff --git a/src/app/modules/products/store/main.ts b/src/app/modules/products/store/main.ts index eb2eb30..57a27b3 100644 --- a/src/app/modules/products/store/main.ts +++ b/src/app/modules/products/store/main.ts @@ -22,6 +22,7 @@ export class ProductsStore extends PaginatedStore { const queryParams = activeRoute.snapshot.queryParams; super({ + initialized: false, loading: false, error: null, isRefreshing: false, @@ -40,6 +41,7 @@ export class ProductsStore extends PaginatedStore { */ reset(): void { this.setState({ + initialized: false, loading: false, error: null, isRefreshing: false, diff --git a/src/app/shared/catalog/movementReferenceTypes/tag.component.ts b/src/app/shared/catalog/movementReferenceTypes/tag.component.ts index c2c4302..2fb3e40 100644 --- a/src/app/shared/catalog/movementReferenceTypes/tag.component.ts +++ b/src/app/shared/catalog/movementReferenceTypes/tag.component.ts @@ -12,10 +12,11 @@ import { getMovementReferenceTypeColor, getMovementReferenceTypeText } from './u export class CatalogMovementReferenceTagComponent { @Input() type!: MovementReferenceType; @Input() movementType?: MovementType; + @Input() counterName?: string; constructor() {} get text() { - return getMovementReferenceTypeText(this.type, this.movementType); + return getMovementReferenceTypeText(this.type, this.movementType, this.counterName); } get color() { return getMovementReferenceTypeColor(this.type); diff --git a/src/app/shared/catalog/movementReferenceTypes/utils.ts b/src/app/shared/catalog/movementReferenceTypes/utils.ts index efbc0d8..420872b 100644 --- a/src/app/shared/catalog/movementReferenceTypes/utils.ts +++ b/src/app/shared/catalog/movementReferenceTypes/utils.ts @@ -5,6 +5,7 @@ import { MovementReferenceType } from './types'; export const getMovementReferenceTypeText = ( movementType: MovementReferenceType, type?: MovementType, + counterName?: string, ): string => { switch (movementType) { case MovementReferenceType.PURCHASE: @@ -15,9 +16,9 @@ export const getMovementReferenceTypeText = ( return 'تعدیل'; case MovementReferenceType.INVENTORY_TRANSFER: if (type === MovementType.IN) { - return 'دریافت از انبار دیگر'; + return `دریافت از انبار ${counterName ?? 'دیگر'}`; } else { - return 'ارسال به انبار دیگر'; + return `ارسال به انبار ${counterName ?? 'دیگر'}`; } default: return 'انتقال بین انبارها'; diff --git a/src/app/shared/components/abstract-select.component.ts b/src/app/shared/components/abstract-select.component.ts index 422a2ec..d114c68 100644 --- a/src/app/shared/components/abstract-select.component.ts +++ b/src/app/shared/components/abstract-select.component.ts @@ -1,5 +1,5 @@ import { Maybe } from '@/core'; -import { Component, Input, model, signal } from '@angular/core'; +import { Component, EventEmitter, Input, model, Output, signal } from '@angular/core'; import { FormControl } from '@angular/forms'; export interface ISelectItem { @@ -18,6 +18,7 @@ export abstract class AbstractSelectComponent { @Input() showErrors: boolean = true; @Input() isFullDataOptionValue: boolean = false; @Input() optionValue = 'id'; + @Output() onChange = new EventEmitter>(); loading = signal(false); items = signal>(null); @@ -50,4 +51,13 @@ export abstract class AbstractSelectComponent { } return this.optionValue; } + + change(value: Maybe) { + if (typeof value !== 'object') { + // @ts-ignore + this.onChange.emit(this.items()?.find((item) => item[this.optionValue] === value)); + } else { + this.onChange.emit(value); + } + } } diff --git a/src/app/uikit/datepicker/datepicker.component.html b/src/app/uikit/datepicker/datepicker.component.html index 6a3421b..f729401 100644 --- a/src/app/uikit/datepicker/datepicker.component.html +++ b/src/app/uikit/datepicker/datepicker.component.html @@ -5,22 +5,10 @@ [showErrors]="showErrors" class="w-full datepicker" > -
- +
+
+ +
+
-
- - diff --git a/src/app/uikit/datepicker/datepicker.component.ts b/src/app/uikit/datepicker/datepicker.component.ts index 5c63462..8f525a5 100644 --- a/src/app/uikit/datepicker/datepicker.component.ts +++ b/src/app/uikit/datepicker/datepicker.component.ts @@ -41,11 +41,13 @@ export class UikitFlatpickrJalaliComponent implements OnInit { ngOnInit(): void { this.fpInstance = flatpickr(this.wrapperRef.nativeElement, { ...this.options, - now: dayjs().calendar('jalali').format('YYYY/MM/DD'), + locale: 'fa', altInputClass: 'p-inputtext w-full', + dateFormat: 'Y-m-d', + altFormat: 'Y-m-d', onChange: (selectedDates: Date[], dateStr: string) => { this.valueChange.emit(dateStr); - if (this.control) this.control.setValue(dateStr); + if (this.control) this.control.setValue(dayjs(dateStr).toISOString()); }, }); }