diff --git a/src/app/components/select-field/select-field.component.ts b/src/app/components/select-field/select-field.component.ts index 18e06106..39c71e8d 100644 --- a/src/app/components/select-field/select-field.component.ts +++ b/src/app/components/select-field/select-field.component.ts @@ -1,5 +1,9 @@ import { Component, HostListener, inject, OnDestroy } from '@angular/core'; -import { TranslatePipe } from '@ngx-translate/core'; +import { FormGroup } from '@angular/forms'; +import { Condition } from '@decaf-ts/core'; +import { OperationKeys } from '@decaf-ts/db-decorators'; +import { Model } from '@decaf-ts/decorator-validation'; +import { ComponentEventNames } from '@decaf-ts/ui-decorators'; import { IonButton, IonItem, @@ -10,21 +14,17 @@ import { SelectChangeEventDetail, SelectCustomEvent, } from '@ionic/angular/standalone'; -import { FormGroup } from '@angular/forms'; -import { Condition } from '@decaf-ts/core'; -import { Model } from '@decaf-ts/decorator-validation'; -import { OperationKeys } from '@decaf-ts/db-decorators'; -import { ComponentEventNames } from '@decaf-ts/ui-decorators'; -import { RouterService } from 'src/app/services/router.service'; +import { TranslatePipe } from '@ngx-translate/core'; import { Batch } from 'src/app/ew/fabric/Batch'; -import { getDocumentTypes } from 'src/app/ew/utils/helpers'; import { Product } from 'src/app/ew/fabric/Product'; -import { ForAngularCommonModule } from 'src/lib/for-angular-common.module'; -import { IconComponent } from 'src/lib/components/icon/icon.component'; +import { getDocumentTypes } from 'src/app/ew/utils/helpers'; import { CrudFieldComponent } from 'src/lib/components/crud-field/crud-field.component'; -import { getOnWindow, setOnWindow, windowEventEmitter } from 'src/lib/utils/helpers'; +import { IconComponent } from 'src/lib/components/icon/icon.component'; import { Dynamic } from 'src/lib/engine/decorators'; import { getModelAndRepository } from 'src/lib/engine/helpers'; +import { ForAngularCommonModule } from 'src/lib/for-angular-common.module'; +import { NgxRouterService } from 'src/lib/services'; +import { getOnWindow, setOnWindow, windowEventEmitter } from 'src/lib/utils/helpers'; @Dynamic() @Component({ @@ -46,7 +46,7 @@ import { getModelAndRepository } from 'src/lib/engine/helpers'; standalone: true, }) export class AppSelectFieldComponent extends CrudFieldComponent implements OnDestroy { - routerService: RouterService = inject(RouterService); + override routerService: NgxRouterService = inject(NgxRouterService); override async onDestroy(): Promise { super.onDestroy(); diff --git a/src/app/ew/fabric/forms/BatchForm.ts b/src/app/ew/fabric/forms/BatchForm.ts index f59a71b6..9d0f2f54 100644 --- a/src/app/ew/fabric/forms/BatchForm.ts +++ b/src/app/ew/fabric/forms/BatchForm.ts @@ -1,43 +1,31 @@ -import { Model, ModelArg } from '@decaf-ts/decorator-validation'; -import { date, list, minlength, model, pattern, required } from '@decaf-ts/decorator-validation'; -import { column, index, OrderDirection, pk, table } from '@decaf-ts/core'; -import { BlockOperations, composed, OperationKeys, readonly } from '@decaf-ts/db-decorators'; +import { column, index, OrderDirection, table } from '@decaf-ts/core'; +import { readonly } from '@decaf-ts/db-decorators'; import { description } from '@decaf-ts/decoration'; -import { Cacheable } from '../Cacheable'; +import { date, Model, model, ModelArg, required } from '@decaf-ts/decorator-validation'; import { - ComponentEventNames, DecafComponent, hidden, HTML5InputTypes, - TransactionHooks, - uichild, uielement, - uihandlers, uilayout, uilayoutprop, uilistmodel, - uilistprop, - uimodel, uionclick, uionrender, uiorder, uitablecol, } from '@decaf-ts/ui-decorators'; -import { DatePattern, TableNames } from '../constants'; -import { audit } from '../utils'; +import { Batch } from '../Batch'; import { Product } from '../Product'; -import { DatamatrixModalHandler } from '../handlers/DatamatrixModalHandler'; +import { DatePattern, TableNames } from '../constants'; import { BatchHandler } from '../handlers/BatchHandler'; -import { Batch } from '../Batch'; +import { DatamatrixModalHandler } from '../handlers/DatamatrixModalHandler'; //@uses(FabricFlavour) @table(TableNames.Batch) @uilistmodel('ngx-decaf-list-item', { icon: 'ti-package' }) @uilayout('ngx-decaf-crud-form', true, 1, { empty: { showButton: false } }) -@uihandlers({ - [ComponentEventNames.Render]: BatchHandler, -}) @model() export class BatchForm extends Batch { //only for ui (table view) @@ -162,7 +150,6 @@ export class BatchForm extends Batch { type: HTML5InputTypes.TEXT, }) @uilayoutprop('half') - @uionrender(() => BatchHandler) @uitablecol(4, (instance: DecafComponent, prop: 'expiryDate', value: string) => { return value; }) diff --git a/src/app/pages/model/model.page.ts b/src/app/pages/model/model.page.ts index 0fc0aefc..daa9bead 100644 --- a/src/app/pages/model/model.page.ts +++ b/src/app/pages/model/model.page.ts @@ -1,16 +1,16 @@ import { Component, OnInit } from '@angular/core'; +import { Model } from '@decaf-ts/decorator-validation'; +import { ComponentEventNames } from '@decaf-ts/ui-decorators'; import { IonContent } from '@ionic/angular/standalone'; import { TranslatePipe } from '@ngx-translate/core'; -import { ComponentEventNames } from '@decaf-ts/ui-decorators'; -import { ModelRendererComponent } from 'src/lib/components/model-renderer/model-renderer.component'; -import { HeaderComponent } from 'src/app/components/header/header.component'; import { ContainerComponent } from 'src/app/components/container/container.component'; -import { ListComponent } from 'src/lib/components/list/list.component'; -import { NgxModelPageDirective } from 'src/lib/engine/NgxModelPageDirective'; -import { CardComponent, EmptyStateComponent } from 'src/lib/components'; +import { HeaderComponent } from 'src/app/components/header/header.component'; import { getNgxToastComponent } from 'src/app/utils/NgxToastComponent'; +import { CardComponent, EmptyStateComponent } from 'src/lib/components'; +import { ListComponent } from 'src/lib/components/list/list.component'; +import { ModelRendererComponent } from 'src/lib/components/model-renderer/model-renderer.component'; import { ICrudFormEvent, IModelComponentSubmitEvent } from 'src/lib/engine/interfaces'; -import { Model } from '@decaf-ts/decorator-validation'; +import { NgxModelPageDirective } from 'src/lib/engine/NgxModelPageDirective'; import { CrudEvent } from 'src/lib/engine/types'; /** @@ -125,8 +125,9 @@ export class ModelPage extends NgxModelPageDirective implements OnInit { // } async ngOnInit(): Promise { - this.enableCrudOperations(); await super.initialize(); + this.enableCrudOperations(); + console.log(this.modelId); } @@ -140,7 +141,7 @@ export class ModelPage extends NgxModelPageDirective implements OnInit { if (name === ComponentEventNames.Submit) { const { success, message } = (await super.submit( event as CrudEvent, - true, + true )) as IModelComponentSubmitEvent; const toast = getNgxToastComponent(); await toast.show({ diff --git a/src/lib/components/list/list.component.ts b/src/lib/components/list/list.component.ts index b77fd4cd..e915ff1a 100644 --- a/src/lib/components/list/list.component.ts +++ b/src/lib/components/list/list.component.ts @@ -9,17 +9,13 @@ * @link {@link ListComponent} */ -import { - Component, - OnInit, - EventEmitter, - Output, - Input, - HostListener, - OnDestroy, -} from '@angular/core'; +import { Component, EventEmitter, HostListener, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { Condition, Observer, Paginator } from '@decaf-ts/core'; +import { OperationKeys } from '@decaf-ts/db-decorators'; +import { Constructor, Metadata } from '@decaf-ts/decoration'; +import { Model, Primitives } from '@decaf-ts/decorator-validation'; +import { ComponentEventNames, UIFunctionLike } from '@decaf-ts/ui-decorators'; import { InfiniteScrollCustomEvent, RefresherCustomEvent, SpinnerTypes } from '@ionic/angular'; -import { TranslatePipe } from '@ngx-translate/core'; import { IonButton, IonInfiniteScroll, @@ -33,34 +29,26 @@ import { IonText, IonThumbnail, } from '@ionic/angular/standalone'; -import { OperationKeys } from '@decaf-ts/db-decorators'; -import { Model, Primitives } from '@decaf-ts/decorator-validation'; -import { Condition, Observer, Paginator } from '@decaf-ts/core'; +import { TranslatePipe } from '@ngx-translate/core'; import { debounceTime, shareReplay, Subject, takeUntil } from 'rxjs'; import { NgxComponentDirective } from '../../engine/NgxComponentDirective'; +import { ComponentsTagNames, DefaultListEmptyOptions, ListComponentsTypes } from '../../engine/constants'; import { Dynamic } from '../../engine/decorators'; -import { KeyValue, FunctionLike, DecafRepository } from '../../engine/types'; -import { - ComponentsTagNames, - DefaultListEmptyOptions, - ListComponentsTypes, -} from '../../engine/constants'; import { IBaseCustomEvent, - IListItemCustomEvent, - IPaginationCustomEvent, IFilterQuery, IFilterQueryItem, IListEmptyOptions, + IListItemCustomEvent, + IPaginationCustomEvent, } from '../../engine/interfaces'; -import { stringToBoolean, formatDate, isValidDate } from '../../utils/helpers'; -import { SearchbarComponent } from '../searchbar/searchbar.component'; -import { EmptyStateComponent } from '../empty-state/empty-state.component'; +import { DecafRepository, FunctionLike, KeyValue } from '../../engine/types'; +import { formatDate, isValidDate, stringToBoolean } from '../../utils/helpers'; import { ComponentRendererComponent } from '../component-renderer/component-renderer.component'; -import { PaginationComponent } from '../pagination/pagination.component'; +import { EmptyStateComponent } from '../empty-state/empty-state.component'; import { FilterComponent } from '../filter/filter.component'; -import { Constructor, Metadata } from '@decaf-ts/decoration'; -import { ComponentEventNames, UIFunctionLike } from '@decaf-ts/ui-decorators'; +import { PaginationComponent } from '../pagination/pagination.component'; +import { SearchbarComponent } from '../searchbar/searchbar.component'; /** * @description A versatile list component that supports various data display modes. @@ -499,8 +487,9 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe * @type {Subject} * @memberOf ListComponent */ - private clickItemSubject: Subject = - new Subject(); + private clickItemSubject: Subject = new Subject< + CustomEvent | IListItemCustomEvent | IBaseCustomEvent + >(); /** * @description Initializes a new instance of the ListComponent. @@ -548,11 +537,7 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe * @memberOf ListComponent */ async ngOnInit(): Promise { - this.initialized = false; - this.repositoryObserver = { - refresh: async (...args: unknown[]): Promise => this.handleRepositoryRefresh(...args), - }; - + // this.initialized = false; // this.router.events.subscribe(async event => { // if (event instanceof NavigationStart) // await super.ngOnDestroy(); @@ -564,12 +549,6 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe .pipe(debounceTime(100), shareReplay(1), takeUntil(this.destroySubscriptions$)) .subscribe((event) => this.clickEventEmit(event as IListItemCustomEvent | IBaseCustomEvent)); - this.repositoryObserverSubject - .pipe(debounceTime(100), shareReplay(1), takeUntil(this.destroySubscriptions$)) - .subscribe(([modelInstance, event, uid]) => - this.handleObserveEvent(modelInstance, event, uid), - ); - this.limit = Number(this.limit); this.start = Number(this.start); @@ -580,8 +559,7 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe this.showSearchbar = stringToBoolean(this.showSearchbar); this.disableSort = stringToBoolean(this.disableSort); - if (!this.operations || !this.operations.includes(OperationKeys.CREATE)) - this.createButton = false; + if (!this.operations || !this.operations.includes(OperationKeys.CREATE)) this.createButton = false; if (typeof this.item?.['tag'] === 'boolean' && this.item?.['tag'] === true) this.item['tag'] = ComponentsTagNames.LIST_ITEM as string; if (!this.initialized) this.parseProps(this); @@ -622,12 +600,15 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe if (this._repository && this.repositoryObserver) { const repo = this._repository as DecafRepository; //TODO: fix check observerHandler - const observeHandler = repo['observerHandler'] as { observers: Observer[] } | undefined; - if (observeHandler && observeHandler?.observers?.length) { - try { - repo.unObserve(this.repositoryObserver); - } catch (error: unknown) { - this.log.info((error as Error)?.message); + if (repo) { + const observeHandler = repo['observerHandler'] as { observers: Observer[] } | undefined; + if (observeHandler && observeHandler?.observers?.length) { + try { + repo.unObserve(this.repositoryObserver); + this.log.info(`Repository observer detached successfully for repository ${repo.class.name}`); + } catch (error: unknown) { + this.log.info((error as Error)?.message); + } } } } @@ -713,9 +694,8 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe * * @memberOf ListComponent */ - override async refresh( - event: InfiniteScrollCustomEvent | RefresherCustomEvent | boolean = false, - ): Promise { + override async refresh(event: InfiniteScrollCustomEvent | RefresherCustomEvent | boolean = false): Promise { + await super.refresh(); // if (typeof force !== 'boolean' && force.type === ComponentEventNames.BackButtonClickEvent) { // const {refresh} = (force as CustomEvent).detail; // if (!refresh) @@ -731,8 +711,7 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe this.refreshEventEmit(this.data); if (this.type === ListComponentsTypes.INFINITE) { if (this.page === this.pages) { - if ((event as InfiniteScrollCustomEvent)?.target) - (event as InfiniteScrollCustomEvent).target.complete(); + if ((event as InfiniteScrollCustomEvent)?.target) (event as InfiniteScrollCustomEvent).target.complete(); this.loadMoreData = false; } else { this.page += 1; @@ -764,19 +743,22 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe * @returns {Promise} * @memberOf ListComponent */ - override async handleObserveEvent( - clazz: UIFunctionLike, + override async handleObserveEvent( + data: M, + constructor: UIFunctionLike, event: OperationKeys, - uid: string | number, + uid: string | number ): Promise { if (event === OperationKeys.CREATE) { if (uid) { - await this.handleCreate(uid); + await this.handleCreate(data, uid); } else { await this.refresh(true); } } else { - if (event === OperationKeys.UPDATE) await this.handleUpdate(uid); + if (event === OperationKeys.UPDATE) { + await this.handleUpdate(data, uid); + } if (event === OperationKeys.DELETE) this.handleDelete(uid); this.refreshEventEmit(); } @@ -808,9 +790,11 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe * @param {string | number} uid - The ID of the item to create. * @returns {Promise} A promise that resolves when the item is created and added to the list. */ - async handleCreate(uid: string | number): Promise { - const result = await this._repository?.read(uid); - const item = (await this.mapResults([result as KeyValue]))[0]; + async handleCreate(data: Model, uid: string | number): Promise { + if (!data || !Object.keys(data).length) { + data = (await this._repository?.read(uid)) as Model; + } + const item = (await this.mapResults([data]))[0]; this.items = this.data = [item, ...(this.items || [])]; } @@ -823,8 +807,11 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe * @private * @memberOf ListComponent */ - async handleUpdate(uid: string | number): Promise { - const item = await this._repository?.read(uid); + async handleUpdate(item: Model, uid: string | number): Promise { + if (!item || !Object.keys(item).length) { + item = (await this._repository?.read(uid)) as Model; + } + // const item: KeyValue = this.itemMapper((await this._repository?.read(uid)) || {}, this.mapper); this.data = []; this.changeDetectorRef.detectChanges(); @@ -1044,7 +1031,7 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe const filtered = results.filter((item: KeyValue) => Object.values(item).some((v) => { if (`${v}`.toLowerCase().includes((search as string)?.toLowerCase())) return v; - }), + }) ); return filtered; } @@ -1065,12 +1052,7 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe async getFromRequest(force: boolean = false, start: number, limit: number): Promise { let data: KeyValue[] = [...(this.data || [])]; - if ( - !this.data?.length || - force || - (this.searchValue as string)?.length || - !!(this.searchValue as IFilterQuery) - ) { + if (!this.data?.length || force || (this.searchValue as string)?.length || !!(this.searchValue as IFilterQuery)) { // (self.data as ListItem[]) = []; if (!(this.searchValue as string)?.length && !(this.searchValue as IFilterQuery)) { if (!this.source && !this.data?.length) { @@ -1090,23 +1072,18 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe ? [...(this.items || []).concat([...data.slice(start, limit)])] : [...(data.slice(start, limit) as KeyValue[])]; } else { - data = await this.parseResult( - this.parseSearchResults(this.data as [], this.searchValue as string), - ); + data = await this.parseResult(this.parseSearchResults(this.data as [], this.searchValue as string)); } this.items = [...data]; if (this.isModalChild) this.changeDetectorRef.detectChanges(); } else { const data = [...(await this.parseResult(this.data as []))]; this.items = - this.type === ListComponentsTypes.INFINITE - ? [...(this.items || []), ...(data || [])] - : [...(data || [])]; + this.type === ListComponentsTypes.INFINITE ? [...(this.items || []), ...(data || [])] : [...(data || [])]; if (this.isModalChild) this.changeDetectorRef.detectChanges(); } - if (this.loadMoreData && this.type === ListComponentsTypes.PAGINATED) - this.getMoreData(this.data?.length || 0); + if (this.loadMoreData && this.type === ListComponentsTypes.PAGINATED) this.getMoreData(this.data?.length || 0); return this.data || ([] as KeyValue[]); } @@ -1130,34 +1107,11 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe if (!this._repository) { this._repository = this.repository; } - if (!this.repositoryObserver) { - this.repositoryObserver = { - refresh: async (...args) => this.handleRepositoryRefresh(...args), - }; - } - - try { - // const observerHandler = (this._repository as DecafRepository)['observerHandler']; - // if (!observerHandler) - // (this._repository as DecafRepository).observe(this.repositoryObserver); - const observerHandler = (this._repository as DecafRepository)['observerHandler'] as - | { observers: Observer[] } - | undefined; - if (!observerHandler?.observers?.length) - (this._repository as DecafRepository).observe(this.repositoryObserver); - } catch (error: unknown) { - this.log.info((error as Error)?.message); - } const repo = this._repository as DecafRepository; if (!this.indexes) { this.indexes = Object.keys(Model.indexes(this.model as Model) || {}); } - if ( - !this.data?.length || - force || - (this.searchValue as string)?.length || - !!(this.searchValue as IFilterQuery) - ) { + if (!this.data?.length || force || (this.searchValue as string)?.length || !!(this.searchValue as IFilterQuery)) { try { if (!(this.searchValue as string)?.length && !(this.searchValue as IFilterQuery)) { (this.data as KeyValue[]) = []; @@ -1174,17 +1128,15 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe .select() .where(this.parseConditions(this.searchValue as string | number | IFilterQuery)) .orderBy((this.sortBy || this.pk) as keyof Model, this.sortDirection) - .execute(), + .execute() ); data = []; this.changeDetectorRef.detectChanges(); } - data = - this.type === ListComponentsTypes.INFINITE ? [...data.concat(request)] : [...request]; + data = this.type === ListComponentsTypes.INFINITE ? [...data.concat(request)] : [...request]; } catch (error: unknown) { this.log.error( - (error as Error)?.message || - `Unable to find ${this.model} on registry. Return empty array from component`, + (error as Error)?.message || `Unable to find ${this.model} on registry. Return empty array from component` ); } } @@ -1228,7 +1180,7 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe const model = this.model as Model; if (typeof value === Primitives.STRING) { _condition = Condition.attribute(this.pk as keyof Model).eq( - this.pkType.toLocaleLowerCase() === Primitives.STRING ? value : Number(value), + this.pkType.toLocaleLowerCase() === Primitives.STRING ? value : Number(value) ); for (const index of this.indexes as (keyof Model)[]) { if (index === this.pk) continue; @@ -1257,10 +1209,7 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe if (index === this.pk || !isNaN(val as number)) { val = Number(val); } - const type = Metadata.type( - model.constructor as Constructor, - index as keyof Model, - ).name; + const type = Metadata.type(model.constructor as Constructor, index as keyof Model).name; if (type === Date.name) { val = new Date(val as string); } @@ -1273,9 +1222,7 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe orCondition = Condition.attribute(index as keyof Model).dif(val); break; case 'Not Contains': - orCondition = !Condition.attribute(index as keyof Model).regexp( - new RegExp(`^(?!.*${val}).*$`), - ); + orCondition = !Condition.attribute(index as keyof Model).regexp(new RegExp(`^(?!.*${val}).*$`)); break; case 'Contains': orCondition = Condition.attribute(index as keyof Model).regexp(val as string); @@ -1313,13 +1260,13 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe protected async parseResult(result: KeyValue[] | Paginator): Promise { if (!Array.isArray(result) && 'page' in result && 'total' in result) { const paginator = result as Paginator; + try { result = await paginator.page(this.page); this.getMoreData(paginator.total || this.pages); } catch (error: unknown) { this.log.info( - (error as Error)?.message || - 'Unable to get page from paginator. Return empty array from component', + (error as Error)?.message || 'Unable to get page from paginator. Return empty array from component' ); result = []; } @@ -1342,7 +1289,9 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe */ getMoreData(length: number): void { if (this.type === ListComponentsTypes.INFINITE) { - if (this.paginator) length = length * this.limit; + if (this.paginator) { + length = length * this.limit; + } if (length <= this.limit) { this.loadMoreData = false; } else { @@ -1385,9 +1334,7 @@ export class ListComponent extends NgxComponentDirective implements OnInit, OnDe let val; for (const _value of arrayValue) - val = !val - ? item[_value] - : (typeof val === Primitives.STRING ? JSON.parse(val) : val)[_value]; + val = !val ? item[_value] : (typeof val === Primitives.STRING ? JSON.parse(val) : val)[_value]; if (isValidDate(new Date(val))) val = `${formatDate(val)}`; diff --git a/src/lib/components/table/table.component.ts b/src/lib/components/table/table.component.ts index 5635720f..3bffc9cb 100644 --- a/src/lib/components/table/table.component.ts +++ b/src/lib/components/table/table.component.ts @@ -1,19 +1,12 @@ -import { Component, inject, Input, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { TranslatePipe } from '@ngx-translate/core'; -import { IonSelect, IonSelectOption } from '@ionic/angular/standalone'; +import { Component, inject, Input, OnInit } from '@angular/core'; import { OrderDirection } from '@decaf-ts/core'; -import { Model, Primitives } from '@decaf-ts/decorator-validation'; import { CrudOperations, OperationKeys } from '@decaf-ts/db-decorators'; +import { Model, Primitives } from '@decaf-ts/decorator-validation'; import { ComponentEventNames, UIFunctionLike, UIKeys } from '@decaf-ts/ui-decorators'; -import { SearchbarComponent } from '../searchbar/searchbar.component'; -import { IconComponent } from '../icon/icon.component'; -import { PaginationComponent } from '../pagination/pagination.component'; -import { ListComponent } from '../list/list.component'; -import { getNgxSelectOptionsModal } from '../modal/modal.component'; -import { EmptyStateComponent } from '../empty-state/empty-state.component'; -import { NgxRouterService } from '../../services/NgxRouterService'; -import { FunctionLike, KeyValue, SelectOption } from '../../engine/types'; +import { IonSelect, IonSelectOption } from '@ionic/angular/standalone'; +import { TranslatePipe } from '@ngx-translate/core'; +import { debounceTime, shareReplay, takeUntil } from 'rxjs'; import { ActionRoles, DefaultListEmptyOptions, @@ -21,9 +14,16 @@ import { SelectFieldInterfaces, } from '../../engine/constants'; import { Dynamic } from '../../engine/decorators'; -import { IBaseCustomEvent, IFilterQuery } from '../../engine/interfaces'; import { getModelAndRepository } from '../../engine/helpers'; -import { debounceTime, shareReplay, takeUntil } from 'rxjs'; +import { IBaseCustomEvent, IFilterQuery } from '../../engine/interfaces'; +import { FunctionLike, KeyValue, SelectOption } from '../../engine/types'; +import { NgxRouterService } from '../../services/NgxRouterService'; +import { EmptyStateComponent } from '../empty-state/empty-state.component'; +import { IconComponent } from '../icon/icon.component'; +import { ListComponent } from '../list/list.component'; +import { getNgxSelectOptionsModal } from '../modal/modal.component'; +import { PaginationComponent } from '../pagination/pagination.component'; +import { SearchbarComponent } from '../searchbar/searchbar.component'; @Dynamic() @Component({ @@ -64,7 +64,7 @@ export class TableComponent extends ListComponent implements OnInit { @Input() allowOperations: boolean = true; - routerService: NgxRouterService = inject(NgxRouterService); + override routerService: NgxRouterService = inject(NgxRouterService); private get _cols(): string[] { this.mapper = this._mapper; @@ -79,11 +79,7 @@ export class TableComponent extends ListComponent implements OnInit { if (aWeight !== bWeight) { return aWeight - bWeight; } - if ( - aWeight === 1 && - typeof aSequence === Primitives.NUMBER && - typeof bSequence === Primitives.NUMBER - ) { + if (aWeight === 1 && typeof aSequence === Primitives.NUMBER && typeof bSequence === Primitives.NUMBER) { return aSequence - bSequence; } return 0; @@ -112,9 +108,7 @@ export class TableComponent extends ListComponent implements OnInit { this.empty = Object.assign({}, DefaultListEmptyOptions, this.empty); this.repositoryObserverSubject .pipe(debounceTime(100), shareReplay(1), takeUntil(this.destroySubscriptions$)) - .subscribe(([modelInstance, event, uid]) => - this.handleObserveEvent(modelInstance, event, uid), - ); + .subscribe(([model, action, uid, data]) => this.handleObserveEvent(model, action, uid, data)); this.cols = this._cols as string[]; this.getOperations(); @@ -145,8 +139,7 @@ export class TableComponent extends ListComponent implements OnInit { getOperations() { if (this.allowOperations) { - this.allowOperations = - this.isAllowed(OperationKeys.UPDATE) || this.isAllowed(OperationKeys.DELETE); + this.allowOperations = this.isAllowed(OperationKeys.UPDATE) || this.isAllowed(OperationKeys.DELETE); } else { this.operations = []; } @@ -174,16 +167,12 @@ export class TableComponent extends ListComponent implements OnInit { } } - protected override async itemMapper( - item: KeyValue, - mapper: KeyValue, - props: KeyValue = {}, - ): Promise { + protected override async itemMapper(item: KeyValue, mapper: KeyValue, props: KeyValue = {}): Promise { this.model = item as Model; const mapped = super.itemMapper( item, this.cols.filter((c) => c !== 'actions'), - props, + props ); const { children } = (this.props as KeyValue) || []; const entries = Object.entries(mapped); @@ -229,9 +218,7 @@ export class TableComponent extends ListComponent implements OnInit { } return value; } catch (error) { - this.log - .for(this.itemMapper) - .error(`Error mapping child events. ${(error as Error)?.message || error}`); + this.log.for(this.itemMapper).error(`Error mapping child events. ${(error as Error)?.message || error}`); } }; const name = this.cols[Number(curr)]; @@ -253,7 +240,7 @@ export class TableComponent extends ListComponent implements OnInit { if (!data || !data.length) return []; return await Promise.all( - data.map(async (curr) => await this.itemMapper(curr, this.mapper, { uid: curr[this.pk] })), + data.map(async (curr) => await this.itemMapper(curr, this.mapper, { uid: curr[this.pk] })) ); } @@ -261,7 +248,7 @@ export class TableComponent extends ListComponent implements OnInit { event: IBaseCustomEvent, handler: UIFunctionLike | undefined, uid: string, - action: CrudOperations, + action: CrudOperations ): Promise { if (handler) { const handlerFn = await handler(this, event, uid); @@ -269,11 +256,7 @@ export class TableComponent extends ListComponent implements OnInit { } await this.handleRedirect(event, uid, action); } - async handleRedirect( - event: Event | IBaseCustomEvent, - uid: string, - action: CrudOperations, - ): Promise { + async handleRedirect(event: Event | IBaseCustomEvent, uid: string, action: CrudOperations): Promise { if (event instanceof Event) { event.preventDefault(); event.stopImmediatePropagation(); @@ -284,17 +267,12 @@ export class TableComponent extends ListComponent implements OnInit { } async openFilterSelectOptions(event: Event): Promise { - const type = - this.filterOptions.length > 10 ? SelectFieldInterfaces.MODAL : SelectFieldInterfaces.POPOVER; + const type = this.filterOptions.length > 10 ? SelectFieldInterfaces.MODAL : SelectFieldInterfaces.POPOVER; if (type === SelectFieldInterfaces.MODAL) { event.preventDefault(); event.stopImmediatePropagation(); const title = await this.translate(`${this.locale}.filter_by`); - const modal = await getNgxSelectOptionsModal( - title, - this.filterOptions as SelectOption[], - this.injector, - ); + const modal = await getNgxSelectOptionsModal(title, this.filterOptions as SelectOption[], this.injector); this.changeDetectorRef.detectChanges(); const { data, role } = await modal.onWillDismiss(); if (role === ActionRoles.confirm && data !== this.filterValue) { diff --git a/src/lib/engine/NgxComponentDirective.ts b/src/lib/engine/NgxComponentDirective.ts index 6cf16856..9b8f8b9c 100644 --- a/src/lib/engine/NgxComponentDirective.ts +++ b/src/lib/engine/NgxComponentDirective.ts @@ -31,8 +31,9 @@ import { Model, ModelConstructor, ModelKeys, Primitives } from '@decaf-ts/decora import { ComponentEventNames, DecafEventHandler, UIFunctionLike, UIKeys } from '@decaf-ts/ui-decorators'; import { OverlayBaseController } from '@ionic/angular/common'; import { LoadingController, LoadingOptions } from '@ionic/angular/standalone'; -import { shareReplay, Subject, takeUntil } from 'rxjs'; +import { shareReplay, takeUntil } from 'rxjs'; import { getLocaleContext } from '../i18n/Loader'; +import { NgxRouterService } from '../services'; import { NgxMediaService } from '../services/NgxMediaService'; import { NgxTranslateService } from '../services/NgxTranslateService'; import { generateRandomValue, getWindow, setOnWindow } from '../utils'; @@ -381,6 +382,8 @@ export abstract class NgxComponentDirective extends NgxRepositoryDirective(); - /** * @description Constructor for NgxComponentDirective. * @summary Initializes the directive by setting up the component name, locale root, @@ -639,7 +640,9 @@ export abstract class NgxComponentDirective extends NgxRepositoryDirective = M | M[] | PrimaryKeyType | PrimaryKeyType[] | undefined; @@ -199,6 +194,8 @@ export class NgxRepositoryDirective extends DecafComponent { */ protected repositoryObserver!: Observer; + protected destroySubscriptions$ = new Subject(); + override async initialize(): Promise { if (this.repository) { if (this.filter) { @@ -240,12 +237,39 @@ export class NgxRepositoryDirective extends DecafComponent { } override async refresh(model?: unknown): Promise { + if (this.repository) { + this.observe(); + } if (model && Model.isModel(model)) { this.model = Model.build(model as M, this.modelName); this._data = model; } } + observe() { + if (!this.repositoryObserver) { + this.repositoryObserver = { + refresh: async (...args) => this.handleRepositoryRefresh(...args), + }; + try { + // const observerHandler = (this._repository as DecafRepository)['observerHandler']; + // if (!observerHandler) + // (this._repository as DecafRepository).observe(this.repositoryObserver); + const observerHandler = this.repository?.['observerHandler'] as { observers: Observer[] } | undefined; + if (!observerHandler?.observers?.length) { + this.repository?.observe(this.repositoryObserver); + this.log.for(this.observe).info(`Registered repository observer for model ${this.modelName}`); + + this.repositoryObserverSubject + .pipe(debounceTime(100), shareReplay(1), takeUntil(this.destroySubscriptions$)) + .subscribe(([model, action, uid, data]) => this.handleObserveEvent(data, model, action, uid)); + } + } catch (error: unknown) { + this.log.info((error as Error)?.message); + } + } + } + protected buildCondition(attr?: keyof M): Condition { if (!attr) { attr = (this.filterBy || this.pk) as keyof M; @@ -262,7 +286,7 @@ export class NgxRepositoryDirective extends DecafComponent { return condition.eq( [Primitives.NUMBER, Primitives.BIGINT].includes(type as Primitives) ? Number(this.modelId) - : (this.modelId as PrimaryKeyType), + : (this.modelId as PrimaryKeyType) ); } return condition.dif(null); @@ -277,9 +301,7 @@ export class NgxRepositoryDirective extends DecafComponent { if (pk && this._context) { pk = Model.pk(this._context.class) as PrimaryKeyType; } - data = data - .map((item) => (item as KeyValue)[pk as string | number]) - .filter(Boolean) as PrimaryKeyType[]; + data = data.map((item) => (item as KeyValue)[pk as string | number]).filter(Boolean) as PrimaryKeyType[]; } else { data = [data] as PrimaryKeyType[]; } @@ -289,7 +311,7 @@ export class NgxRepositoryDirective extends DecafComponent { async query( condition?: Condition, sortBy: keyof M = (this.sortBy || this.pk) as keyof M, - sortDirection: OrderDirection = this.sortDirection, + sortDirection: OrderDirection = this.sortDirection ): Promise { if (!condition) { condition = this.buildCondition(); @@ -314,7 +336,7 @@ export class NgxRepositoryDirective extends DecafComponent { async paginate( limit: number = this.limit, sortDirection: OrderDirection = this.sortDirection, - condition?: Condition, + condition?: Condition ): Promise> { if (!condition) { condition = this.buildCondition(); @@ -329,7 +351,7 @@ export class NgxRepositoryDirective extends DecafComponent { protected async transactionBegin( data: M, repository: DecafRepository, - operation: CrudOperations, + operation: CrudOperations ): Promise { try { const hook = `before${operation.charAt(0).toUpperCase() + operation.slice(1)}`; @@ -351,7 +373,7 @@ export class NgxRepositoryDirective extends DecafComponent { protected async transactionEnd( model: M, repository: DecafRepository, - operation: CrudOperations, + operation: CrudOperations ): Promise> { try { const hook = `after${operation.charAt(0).toUpperCase() + operation.slice(1)}`; @@ -384,19 +406,16 @@ export class NgxRepositoryDirective extends DecafComponent { protected buildTransactionModel( data: KeyValue | KeyValue[], repository: DecafRepository, - operation?: OperationKeys, + operation?: OperationKeys ): TransactionResponse { if (!operation) { operation = this.operation as OperationKeys; } operation = ( - [OperationKeys.READ, OperationKeys.DELETE].includes(operation) - ? OperationKeys.DELETE - : operation.toLowerCase() + [OperationKeys.READ, OperationKeys.DELETE].includes(operation) ? OperationKeys.DELETE : operation.toLowerCase() ) as OperationKeys; - if (Array.isArray(data)) - return data.map((item) => this.buildTransactionModel(item, repository, operation)) as M[]; + if (Array.isArray(data)) return data.map((item) => this.buildTransactionModel(item, repository, operation)) as M[]; const pk = Model.pk(repository.class as Constructor) as string; const pkType = Metadata.type(repository.class as Constructor, pk as string).name; @@ -408,21 +427,13 @@ export class NgxRepositoryDirective extends DecafComponent { if (operation !== OperationKeys.DELETE) { const properties = Metadata.properties(repository.class as Constructor) as string[]; const relation = - pk === this.pk - ? {} - : properties.includes(this.pk) && !data[this.pk] - ? { [this.pk]: modelId } - : {}; + pk === this.pk ? {} : properties.includes(this.pk) && !data[this.pk] ? { [this.pk]: modelId } : {}; if (!String(data?.[pk] || '').trim().length) { data[pk] = undefined; } return Model.build( - Object.assign( - data || {}, - relation, - modelId && !data[this.pk] ? { [this.pk]: modelId } : {}, - ), - repository.class.name, + Object.assign(data || {}, relation, modelId && !data[this.pk] ? { [this.pk]: modelId } : {}), + repository.class.name ) as M; } return uid as PrimaryKeyType; @@ -434,9 +445,7 @@ export class NgxRepositoryDirective extends DecafComponent { } protected parsePkValue(value: PrimaryKeyType, type: string): PrimaryKeyType { - return [Primitives.NUMBER, Primitives.BIGINT].includes(type.toLowerCase() as Primitives) - ? Number(value) - : value; + return [Primitives.NUMBER, Primitives.BIGINT].includes(type.toLowerCase() as Primitives) ? Number(value) : value; } protected getModelConstrutor(model: string | Model): Constructor | undefined { @@ -451,12 +460,8 @@ export class NgxRepositoryDirective extends DecafComponent { try { return Metadata.type(constructor as Constructor, prop as string)?.name; } catch (error: unknown) { - this.log - .for(this) - .info(`${(error as Error)?.message || String(error)}. Tryng get with table Name`); - constructor = Model.get( - Model.tableName(this.repository.class as Constructor) as string, - ) as Constructor; + this.log.for(this).info(`${(error as Error)?.message || String(error)}. Tryng get with table Name`); + constructor = Model.get(Model.tableName(this.repository.class as Constructor) as string) as Constructor; return Metadata.type(constructor, prop as string)?.name; } } @@ -475,9 +480,9 @@ export class NgxRepositoryDirective extends DecafComponent { * @returns {Promise} */ async handleRepositoryRefresh(...args: unknown[]): Promise { - const [modelInstance, event, uid] = args; - if ([OperationKeys.CREATE, OperationKeys.DELETE].includes(event as OperationKeys)) - return this.handleObserveEvent(modelInstance, event, uid as string | number); + const [model, action, uid, data] = args; + if ([OperationKeys.CREATE, OperationKeys.DELETE].includes(action as OperationKeys)) + return this.handleObserveEvent(data, model, action, uid as string | number, data); return this.repositoryObserverSubject.next(args); } diff --git a/src/lib/engine/helpers.ts b/src/lib/engine/helpers.ts index c66fff40..aa8c0c37 100644 --- a/src/lib/engine/helpers.ts +++ b/src/lib/engine/helpers.ts @@ -1,14 +1,14 @@ -import { Provider, EnvironmentProviders, provideEnvironmentInitializer } from '@angular/core'; -import { Logger, Logging } from '@decaf-ts/logging'; -import { getOnWindow, getWindowDocument, setOnWindow } from '../utils/helpers'; -import { DecafRepository, FunctionLike, KeyValue } from './types'; -import { Model, Primitives } from '@decaf-ts/decorator-validation'; +import { EnvironmentProviders, provideEnvironmentInitializer, Provider } from '@angular/core'; import { Repository } from '@decaf-ts/core'; import { Constructor, Metadata, uses } from '@decaf-ts/decoration'; +import { Model, Primitives } from '@decaf-ts/decorator-validation'; +import { Logger, Logging } from '@decaf-ts/logging'; import { AnimationController, provideIonicAngular } from '@ionic/angular/standalone'; +import { getOnWindow, getWindowDocument, setOnWindow } from '../utils/helpers'; import { NgxComponentDirective } from './NgxComponentDirective'; import { DB_ADAPTER_FLAVOUR_TOKEN, DB_ADAPTER_PROVIDER_TOKEN } from './constants'; import { IRepositoryModelProps } from './interfaces'; +import { DecafRepository, FunctionLike, KeyValue } from './types'; export function getDbAdapterFlavour(): string { return (getOnWindow(DB_ADAPTER_FLAVOUR_TOKEN) || '') as string; @@ -53,16 +53,12 @@ export function provideDecafDynamicComponents(...components: unknown[]): Constru */ export function getModelAndRepository( model: M | string, - clazz?: NgxComponentDirective, + clazz?: NgxComponentDirective ): IRepositoryModelProps | undefined { if (!model) return undefined; try { - const modelName = ( - typeof model === Primitives.STRING ? model : (model as Model).constructor.name - ) as string; - const constructor = Model.get( - (modelName.charAt(0).toUpperCase() + modelName.slice(1)) as string, - ); + const modelName = (typeof model === Primitives.STRING ? model : (model as Model).constructor.name) as string; + const constructor = Model.get((modelName.charAt(0).toUpperCase() + modelName.slice(1)) as string); if (!constructor) return undefined; const dbAdapterFlavour = getOnWindow(DB_ADAPTER_FLAVOUR_TOKEN) || undefined; if (dbAdapterFlavour) uses(dbAdapterFlavour as string)(constructor); @@ -109,13 +105,11 @@ export function getModelAndRepository( export function provideDecafDbAdapter( clazz: Constructor, options: KeyValue = {}, - flavour?: string, + flavour?: string ): Provider { const adapter = new clazz(options); if (!flavour) flavour = adapter.flavour; - getLogger(provideDecafDbAdapter).info( - `Using ${adapter.constructor.name} ${flavour} as Db Provider`, - ); + getLogger(provideDecafDbAdapter).info(`Using ${adapter.constructor.name} ${flavour} as Db Provider`); setOnWindow(DB_ADAPTER_FLAVOUR_TOKEN, flavour); return { provide: DB_ADAPTER_PROVIDER_TOKEN, diff --git a/src/lib/engine/overrides.ts b/src/lib/engine/overrides.ts index e3a9acda..3435d512 100644 --- a/src/lib/engine/overrides.ts +++ b/src/lib/engine/overrides.ts @@ -17,7 +17,7 @@ export class DecafAxiosHttpAdapter extends AxiosHttpAdapter { alias: string = AxiosFlavour, protected enableCredentials: boolean = true ) { - super(config, alias); + super({ eventsListenerPath: '/events', ...config }, alias); } parseStatementURL(url: string): string { diff --git a/src/lib/services/NgxRouterService.ts b/src/lib/services/NgxRouterService.ts index 0c09a862..e8eed50d 100644 --- a/src/lib/services/NgxRouterService.ts +++ b/src/lib/services/NgxRouterService.ts @@ -1,12 +1,12 @@ -import { inject, Injectable } from '@angular/core'; import { Location } from '@angular/common'; +import { inject, Injectable } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { NavController } from '@ionic/angular/standalone'; -import { NavigationOptions } from '@ionic/angular/common/providers/nav-controller'; import { Primitives } from '@decaf-ts/decorator-validation'; -import { KeyValue } from '../engine/types'; -import { RouteDirections } from '../engine/constants'; import { ComponentEventNames } from '@decaf-ts/ui-decorators'; +import { NavigationOptions } from '@ionic/angular/common/providers/nav-controller'; +import { NavController } from '@ionic/angular/standalone'; +import { RouteDirections } from '../engine/constants'; +import { KeyValue } from '../engine/types'; /** * @description Service for handling routing operations in the application. @@ -303,11 +303,8 @@ export class NgxRouterService { * @memberOf RouterService */ getPreviousUrl(): string { - const currentNavigation = this.router.getCurrentNavigation(); - if ( - !!currentNavigation && - currentNavigation.previousNavigation?.finalUrl?.toString() !== undefined - ) + const currentNavigation = this.router.currentNavigation() || this.router.getCurrentNavigation(); + if (!!currentNavigation && currentNavigation.previousNavigation?.finalUrl?.toString() !== undefined) this.previousUrl = currentNavigation.previousNavigation?.finalUrl?.toString(); return this.previousUrl as string; } @@ -339,7 +336,7 @@ export class NgxRouterService { composed: true, cancelable: false, detail: { refres: true }, - }), + }) ); this.location.back(); } @@ -376,11 +373,10 @@ export class NgxRouterService { async navigateTo( page: string, direction: RouteDirections = RouteDirections.FORWARD, - options?: NavigationOptions, + options?: NavigationOptions ): Promise { if (direction === RouteDirections.ROOT) return this.navController.navigateRoot(page, options); - if (direction === RouteDirections.FORWARD) - return await this.navController.navigateForward(page, options); + if (direction === RouteDirections.FORWARD) return await this.navController.navigateForward(page, options); return await this.navController.navigateBack(page, options); } } diff --git a/src/lib/utils/helpers.ts b/src/lib/utils/helpers.ts index f69e61d3..476badbe 100644 --- a/src/lib/utils/helpers.ts +++ b/src/lib/utils/helpers.ts @@ -11,12 +11,11 @@ */ import { isDevMode } from '@angular/core'; -import { InjectableRegistryImp, InjectablesRegistry } from '@decaf-ts/injectable-decorators'; import { Primitives } from '@decaf-ts/decorator-validation'; -import { KeyValue, StringOrBoolean } from '../engine/types'; -import { FunctionLike } from '../engine/types'; -import { IMenuItem } from '../engine/interfaces'; +import { InjectableRegistryImp, InjectablesRegistry } from '@decaf-ts/injectable-decorators'; import { getLogger } from '../engine/helpers'; +import { IMenuItem } from '../engine/interfaces'; +import { FunctionLike, KeyValue, StringOrBoolean } from '../engine/types'; let injectableRegistry: InjectablesRegistry; @@ -51,13 +50,15 @@ export function getInjectablesRegistry(): InjectablesRegistry { * @memberOf module:for-angular */ export function isDevelopmentMode(context: string = 'localhost'): boolean { - if (!context) return isDevMode(); + const devMode = isDevMode(); const win = getWindow(); - return ( - isDevMode() || - win?.['env']?.['CONTEXT'].toLowerCase() !== context.toLowerCase() || - win?.['location']?.hostname?.includes(context) - ); + const env = win?.['env'] || win?.['ENV']; + const { protocol } = win.location || ({} as Location); + if (!env || !context) { + if (protocol.includes('https')) return false; + return devMode; + } + return env?.['CONTEXT'].toLowerCase() !== context.toLowerCase() || win?.['location']?.hostname?.includes(context); } /** @@ -82,7 +83,7 @@ export function windowEventEmitter(name: string, detail: unknown, props?: object cancelable: false, detail: detail, }, - props || {}, + props || {} ); (getWindow() as Window).dispatchEvent(new CustomEvent(name, data)); } @@ -217,10 +218,7 @@ export function isNotUndefined(prop: StringOrBoolean | undefined): boolean { * @function getLocaleFromClassName * @memberOf module:for-angular */ -export function getLocaleFromClassName( - instance: string | FunctionLike | KeyValue, - suffix?: string, -): string { +export function getLocaleFromClassName(instance: string | FunctionLike | KeyValue, suffix?: string): string { if (typeof instance !== Primitives.STRING) instance = (instance as FunctionLike).name || (instance as object)?.constructor?.name; @@ -276,9 +274,7 @@ export function getLocaleLanguage(): string { * @memberOf module:for-angular */ export function generateRandomValue(length: number = 8, onlyNumbers: boolean = false): string { - const chars = onlyNumbers - ? '0123456789' - : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + const chars = onlyNumbers ? '0123456789' : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let result = ''; for (let i = 0; i < length; i++) result += chars.charAt(Math.floor(Math.random() * chars.length)); @@ -325,8 +321,7 @@ export function isValidDate(date: string | Date | number): boolean { (date instanceof Date && !isNaN(date as unknown as number)) || (() => { const testRegex = new RegExp(/^\d{4}-\d{2}-\d{2}$/).test(date as string); - if (typeof date !== Primitives.STRING || (!(date as string)?.includes('T') && !testRegex)) - return false; + if (typeof date !== Primitives.STRING || (!(date as string)?.includes('T') && !testRegex)) return false; date = (date as string).split('T')[0]; if (!new RegExp(/^\d{4}-\d{2}-\d{2}$/).test(date)) return false; @@ -357,10 +352,7 @@ export function isValidDate(date: string | Date | number): boolean { * @function formatDate * @memberOf module:lib/helpers/utils */ -export function formatDate( - date: string | Date | number, - locale?: string | undefined, -): Date | string { +export function formatDate(date: string | Date | number, locale?: string | undefined): Date | string { if (!locale) locale = getLocaleLanguage(); if (typeof date === Primitives.STRING || typeof date === 'number') { @@ -407,9 +399,7 @@ export function itemMapper(item: KeyValue, mapper: KeyValue, props?: KeyValue): // accum[key] = item?.[value as string] || (value !== key ? value : ""); } else { for (const propValue of arrayValue) - value = !value - ? item[propValue] - : (typeof value === 'string' ? JSON.parse(value) : value)[propValue]; + value = !value ? item[propValue] : (typeof value === 'string' ? JSON.parse(value) : value)[propValue]; if (isValidDate(new Date(value))) value = `${formatDate(value)}`; @@ -516,17 +506,11 @@ export async function isDarkMode(): Promise { * @function filterString * @memberOf module:lib/helpers/utils */ -export function filterString( - original: string | string[], - value: string, - contain: boolean = true, -): string { +export function filterString(original: string | string[], value: string, contain: boolean = true): string { if (typeof original === Primitives.STRING) original = (original as string).split(' '); - return ( - (original as string[]).filter((str) => - contain ? str.includes(value) : !str.includes(value), - ) || [] - ).join(' '); + return ((original as string[]).filter((str) => (contain ? str.includes(value) : !str.includes(value))) || []).join( + ' ' + ); } /**