580 lines
19 KiB
TypeScript
580 lines
19 KiB
TypeScript
import { Component, OnInit, OnDestroy, ViewChild, AfterViewInit } from '@angular/core';
|
|
import { ActivatedRoute } from '@angular/router';
|
|
|
|
import { Subscription, interval } from 'rxjs';
|
|
|
|
import { SelectItem } from 'primeng/api';
|
|
import { Dropdown } from 'primeng/dropdown';
|
|
import { Table } from 'primeng/table';
|
|
import { FilterUtils } from 'primeng/utils';
|
|
|
|
import { IUIJob } from '../models/job.model';
|
|
import * as jobActions from '../actions/job.actions';
|
|
import * as clientActions from '@app/client/actions/client.actions';
|
|
|
|
import { select } from '@ngrx/store';
|
|
import * as fromJobs from '../reducers/';
|
|
|
|
import * as fromClients from '@app/client/reducers';
|
|
|
|
import { GC, RoleIds, globals, jobInvoiceStatus, jobListStatus, locales } from '@app/shared/global';
|
|
import { DatePipe } from '@angular/common';
|
|
import { Client } from '@app/client/models/client.model';
|
|
import { BaseComp } from '@app/shared/base/base.component';
|
|
import { Utils } from '@app/shared/utils';
|
|
import { selectLimit } from '@app/reducers';
|
|
import { Acre } from '@app/domain/models/subscription.model';
|
|
import { SUB, SubTexts, SubType } from '@app/profile/common';
|
|
import { InvoiceService } from '@app/domain/services/invoice.service';
|
|
import { RestoreTableState } from '@app/shared/restore-table-state';
|
|
import { GAService } from '@app/shared/ga.service';
|
|
import { JobCacheService } from '@app/domain/services/job-cache.service';
|
|
import { FilterDefinition, FilterChangeEvent } from '@app/shared/dynamic-filter/dynamic-filter.component';
|
|
|
|
|
|
@Component({
|
|
selector: 'agm-job-list',
|
|
templateUrl: './job-list.component.html',
|
|
styleUrls: ['./job-list.component.css']
|
|
})
|
|
export class JobListComponent extends BaseComp implements OnInit, AfterViewInit, OnDestroy {
|
|
globals = globals;
|
|
readonly dropdownStyle = { 'min-width': '170px', 'color': 'black' };
|
|
|
|
jobs: Array<IUIJob> = [];
|
|
filteredJobs: Array<IUIJob> = [];
|
|
currentJob: IUIJob;
|
|
currClient: SelectItem;
|
|
filterClientLocked = false;
|
|
clients: SelectItem[];
|
|
defaultInvoiceSetting;
|
|
|
|
private currentByTime: string[] | undefined;
|
|
private lastFiltersQuery: Record<string, any> | undefined;
|
|
|
|
jobFilterDefinitions: FilterDefinition[] = [];
|
|
readonly defaultDynamicFilters = [
|
|
...(!this.isClientUser ? [{ key: 'client', value: null }] : []),
|
|
{ key: 'createdAt', value: '1m' }
|
|
];
|
|
|
|
@ViewChild('dt') public dt: Table;
|
|
private _cl: Dropdown;
|
|
@ViewChild('cl') set cl(dropdown: Dropdown) {
|
|
this._cl = dropdown;
|
|
if (dropdown) {
|
|
dropdown.registerOnChange((newVal) => {
|
|
this.currClient = newVal;
|
|
this.filteredJobs = newVal.value
|
|
? this.jobs.filter(j => j.client?._id === newVal.value)
|
|
: this.jobs;
|
|
this.dt.first = 0;
|
|
});
|
|
}
|
|
}
|
|
|
|
rows1Page = [10, 15, 30, 60, 100];
|
|
cols: any[];
|
|
|
|
status: SelectItem[] = [GC.selAll, ...GC.selJobStatuses];
|
|
statusFilter;
|
|
startDateFilter: Date;
|
|
endDateFilter: Date;
|
|
reloadOps: SelectItem[];
|
|
reloadBy = 0;
|
|
reload$: Subscription;
|
|
showStatusPlus: boolean;
|
|
|
|
totalJobs;
|
|
|
|
acre: Acre;
|
|
|
|
searchAccordionOpen = sessionStorage.getItem('job-list-accordion') === 'true';
|
|
|
|
get canWrite(): boolean {
|
|
return this.authSvc.hasRole([RoleIds.APP, RoleIds.APP_ADM, RoleIds.OFFICER, RoleIds.PILOT, RoleIds.CLIENT]);
|
|
}
|
|
|
|
get canWriteInvoice(): boolean {
|
|
return this.authSvc.canAccessInvoice
|
|
&& this.jobs?.length > 0;
|
|
}
|
|
|
|
constructor(
|
|
private readonly route: ActivatedRoute,
|
|
private readonly datePipe: DatePipe,
|
|
private readonly invoiceSvc: InvoiceService,
|
|
private readonly restoreTableSvc: RestoreTableState,
|
|
private readonly gaService: GAService,
|
|
private readonly jobCache: JobCacheService
|
|
) {
|
|
super();
|
|
this.currClient = ({ label: globals.all, value: null });
|
|
this.totalJobs = { '=0': '', '=1': '1 ' + $localize`:@@job:job`.toLocaleLowerCase(), 'other': $localize`:@@total#Jobs:Total: # jobs` };
|
|
|
|
this.status = [
|
|
{ label: globals.all, value: jobListStatus.ALL },
|
|
{ label: globals.statusNew, value: jobListStatus.NEW },
|
|
{ label: globals.statusReady, value: jobListStatus.READY },
|
|
{ label: globals.statusDownloaded, value: jobListStatus.DOWNLOAD },
|
|
{ label: globals.statusSprayed, value: jobListStatus.SPRAY },
|
|
{ label: globals.statusInvoiced, value: jobListStatus.INVOICED },
|
|
];
|
|
|
|
this.statusFilter = jobListStatus.ALL;
|
|
|
|
this.cols = [
|
|
{ field: '_id', header: $localize`:@@id:Id` + ' ' + globals.num, width: '10%', filtered: true, filterMatchMode: 'contains' },
|
|
{
|
|
field: 'orderNumber',
|
|
header: $localize`:@@order:Order` + ' ' + globals.num,
|
|
width: '10%',
|
|
filtered: true,
|
|
filterMatchMode: 'contains'
|
|
},
|
|
{ field: 'name', header: globals.name, width: this.isClientUser ? '34%' : '20%', filtered: true, filterMatchMode: 'contains' },
|
|
{ field: 'startDate', header: $localize`:@@startDate:Start Date`, width: '12%' },
|
|
{ field: 'endDate', header: $localize`:@@endDate:End Date`, width: '12%' },
|
|
{ field: 'status', header: $localize`:@@status:Status`, width: '22%' },
|
|
];
|
|
if (!this.isClientUser) {
|
|
this.cols.unshift({ field: 'client.name', header: $localize`:@@client:Client`, width: '14%' });
|
|
}
|
|
|
|
this.reloadOps = [
|
|
{ label: globals.noReload, value: 0 },
|
|
{ label: globals.reloadByMinutes.replace('#count#', '5'), value: 5 },
|
|
{ label: globals.reloadByMinutes.replace('#count#', '10'), value: 10 },
|
|
{ label: globals.reloadByMinutes.replace('#count#', '15'), value: 15 }
|
|
];
|
|
this.showStatusPlus = !this.authSvc.hasRole([RoleIds.CLIENT, RoleIds.INSPECTOR]);
|
|
this.defaultInvoiceSetting = this.invoiceSvc.defaultSetting;
|
|
|
|
(FilterUtils as any)['dateIs'] = (value: any, filter: any): boolean => {
|
|
if (!filter) { return true; }
|
|
if (!value) { return false; }
|
|
const valDate = new Date(value);
|
|
const filterDate = new Date(filter);
|
|
return valDate.getFullYear() === filterDate.getFullYear()
|
|
&& valDate.getMonth() === filterDate.getMonth()
|
|
&& valDate.getDate() === filterDate.getDate();
|
|
};
|
|
|
|
this.jobFilterDefinitions = [
|
|
...(!this.isClientUser ? [{ key: 'client', label: $localize`:@@client:Client`, dataType: 'select' as const, options: [] }] : []),
|
|
{ key: '_id', label: $localize`:@@id:Id` + ' ' + globals.num, dataType: 'text' as const },
|
|
{ key: 'orderNumber', label: $localize`:@@order:Order` + ' ' + globals.num, dataType: 'text' as const },
|
|
{ key: 'name', label: globals.name, dataType: 'text' as const },
|
|
{ key: 'startDate', label: $localize`:@@startDate:Start Date`, dataType: 'date' as const },
|
|
{ key: 'endDate', label: $localize`:@@endDate:End Date`, dataType: 'date' as const },
|
|
{ key: 'createdAt', label: $localize`:@@createdDate:Created Date`, dataType: 'date-preset' as const },
|
|
{ key: 'status', label: $localize`:@@status:Status`, dataType: 'select-multi' as const, options: GC.selJobStatuses },
|
|
];
|
|
}
|
|
|
|
ngOnInit() {
|
|
// Initialize subscriptions first to get accurate data
|
|
this.sub$ = this.store.pipe(select(fromClients.getAllClients)).subscribe(clients => {
|
|
if (Utils.isEmptyArray(clients)) {
|
|
return;
|
|
}
|
|
|
|
this.clients = clients.map(it => ({ value: it._id, label: it.name }));
|
|
if (!this.isClientUser) {
|
|
this.clients.unshift(({ label: globals.all, value: null }));
|
|
const clientDef = this.jobFilterDefinitions.find(f => f.key === 'client');
|
|
if (clientDef) { clientDef.options = this.clients; }
|
|
}
|
|
});
|
|
this.sub$.add(this.store.pipe(select(fromClients.getSelectedClient)).subscribe(client => {
|
|
if (client) {
|
|
if (this.currClient.value !== client._id) {
|
|
this.currClient = ({ label: client.name, value: client._id });
|
|
}
|
|
} else {
|
|
this.currClient = ({ label: globals.all, value: null });
|
|
}
|
|
})); this.sub$.add(this.store.pipe(select(fromJobs.getJobsByClient)).subscribe(jobs => {
|
|
this.jobs = jobs;
|
|
this.filteredJobs = jobs;
|
|
}));
|
|
this.sub$.add(this.store.pipe(select(fromJobs.getSelectedJob)).subscribe((job) => {
|
|
this.currentJob = job;
|
|
}));
|
|
|
|
this.sub$.add(this.store.select(selectLimit(SubType.PACKAGE)).subscribe((pkg) => {
|
|
if (pkg) {
|
|
const lookupKey = this.authSvc.getCurLookupKey(SubType.PACKAGE);
|
|
|
|
// If lookup key is empty (user data not loaded yet), find first package key
|
|
let effectiveLookupKey = lookupKey;
|
|
if (!lookupKey && pkg) {
|
|
const packageKeys = Object.keys(pkg);
|
|
if (packageKeys.length > 0) {
|
|
effectiveLookupKey = packageKeys[0]; // Use first available package
|
|
}
|
|
}
|
|
|
|
this.acre = pkg[effectiveLookupKey]?.acre;
|
|
}
|
|
}));
|
|
}
|
|
|
|
ngAfterViewInit(): void {
|
|
// Track job list viewed ONCE when component is fully initialized
|
|
this.trackJobListViewedEvent();
|
|
|
|
const listFilter = sessionStorage.getItem('jtb-ops') ? JSON.parse(sessionStorage.getItem('jtb-ops')) : null;
|
|
|
|
if (listFilter?.filters) {
|
|
const status = listFilter.filters.status?.value;
|
|
const invoiced = listFilter.filters.invoiceStatus?.value;
|
|
this.restoreStatusState(status, invoiced);
|
|
}
|
|
setTimeout(() => {
|
|
if (this.dt.rows >= this.dt.totalRecords) {
|
|
this.dt.first = 0;
|
|
}
|
|
}, 100);
|
|
}
|
|
|
|
private trackJobListViewedEvent(): void {
|
|
// Track agricultural business intelligence (complements automatic page_view)
|
|
this.gaService.trackJobListViewed({
|
|
user_id: this.authSvc.user?._id || 'anonymous',
|
|
platform: 'web',
|
|
view_type: 'table',
|
|
total_jobs: this.jobs?.length || 0,
|
|
displayed_jobs: this.jobs?.length || 0,
|
|
sort_by: this.dt?.sortField || null,
|
|
filter_count: this.getActiveFilterCount(),
|
|
client_filter_applied: !!this.currClient?.value,
|
|
reload_interval: this.reloadBy
|
|
});
|
|
}
|
|
|
|
restoreStatusState(status, invoiced) {
|
|
const statusMap = {
|
|
0: jobListStatus.NEW,
|
|
1: jobListStatus.READY,
|
|
2: jobListStatus.DOWNLOAD,
|
|
3: jobListStatus.SPRAY,
|
|
[jobInvoiceStatus.INVOICED]: jobListStatus.INVOICED
|
|
};
|
|
this.statusFilter = statusMap[status] ?? statusMap[invoiced] ?? jobListStatus.ALL;
|
|
}
|
|
|
|
fetchJobsByClient(clientId) {
|
|
const statusMap = {
|
|
[jobListStatus.ALL]: jobListStatus.ALL,
|
|
[jobListStatus.NEW]: 0,
|
|
[jobListStatus.READY]: 1,
|
|
[jobListStatus.DOWNLOAD]: 2,
|
|
[jobListStatus.SPRAY]: 3,
|
|
[jobListStatus.INVOICED]: jobInvoiceStatus.INVOICED
|
|
};
|
|
|
|
const statusValue = statusMap[this.statusFilter] ?? jobListStatus.ALL;
|
|
this.store.dispatch(new jobActions.Fetch({
|
|
clientId: clientId,
|
|
jobsByPilot: (this.authSvc.isPilotUser && this.settings.jobsByPilot),
|
|
byTime: this.currentByTime,
|
|
status: statusValue
|
|
}));
|
|
}
|
|
|
|
onCreatedDateChanged(byTime: string[]): void {
|
|
this.currentByTime = byTime;
|
|
this.reloadJobs();
|
|
}
|
|
|
|
onDateFilter(value: Date, field: string) {
|
|
this.dt.filter(value, field, 'dateIs');
|
|
}
|
|
|
|
onAccordionToggle(expanded: boolean) {
|
|
sessionStorage.setItem('job-list-accordion', String(expanded));
|
|
}
|
|
|
|
restoreTableFirst() {
|
|
this.restoreTableSvc.restoreTableFirst(this.dt);
|
|
}
|
|
|
|
onPageChange(e) {
|
|
this.restoreTableSvc.onPageChange(this.dt, e);
|
|
}
|
|
|
|
onRowSelect(event) {
|
|
this.store.dispatch(new jobActions.Select(this.currentJob));
|
|
|
|
// Track job selection
|
|
if (this.currentJob) {
|
|
const positionInList = this.jobs.findIndex(job => job._id === this.currentJob._id) + 1;
|
|
|
|
this.gaService.trackJobSelected({
|
|
user_id: this.authSvc.user?._id || 'anonymous',
|
|
platform: 'web',
|
|
job_id: this.currentJob._id.toString(),
|
|
selection_method: 'row_click',
|
|
position_in_list: positionInList,
|
|
job_type: this.currentJob.appType || 'unknown',
|
|
job_status: this.currentJob.status?.toString() || 'unknown'
|
|
});
|
|
}
|
|
}
|
|
|
|
get canAddNew(): boolean {
|
|
// Check subscription package loaded (!!this.acre) and not over limit
|
|
// Note: With unlimited acres (limit: null), overLimit will always be false,
|
|
// but keep this check for defensive programming in case limited plans return
|
|
return !!this.acre && !this.acre.overLimit;
|
|
}
|
|
|
|
displaySubDia() {
|
|
return this.confirmSvc.confirm({
|
|
header: SubTexts.textUpgradeSub,
|
|
message: SubTexts.textUpgradeSubMsg,
|
|
accept: () => {
|
|
this.router.navigate([SUB.PROFILE, SUB.MY_SERVICES]);
|
|
}
|
|
});
|
|
}
|
|
|
|
newJob() {
|
|
if (this.canAddNew) {
|
|
return this.router.navigate(['./0/edit'], { relativeTo: this.route });
|
|
}
|
|
return this.displaySubDia();
|
|
}
|
|
|
|
duplicateJob() {
|
|
if (this.canAddNew) {
|
|
// Track bulk action (duplicate)
|
|
this.gaService.trackJobBulkAction({
|
|
user_id: this.authSvc.user?._id || 'anonymous',
|
|
platform: 'web',
|
|
action_type: 'duplicate',
|
|
job_count: 1,
|
|
job_ids: [this.currentJob._id.toString()],
|
|
success_rate: 1.0
|
|
});
|
|
|
|
return this.router.navigate([`./${this.currentJob._id}/edit`, { dup: true }], { relativeTo: this.route });
|
|
}
|
|
return this.displaySubDia();
|
|
}
|
|
|
|
editJob() {
|
|
this.router.navigate([`./${this.currentJob._id}/edit`], { relativeTo: this.route });
|
|
}
|
|
|
|
editJobMap() {
|
|
this.router.navigate([`./${this.currentJob._id}/editMap`, { flag: 0 }], { relativeTo: this.route });
|
|
}
|
|
|
|
canEdit() {
|
|
return (this.currentJob && this.currentJob._id !== 0);
|
|
}
|
|
|
|
canCreateInvoice() {
|
|
return (this.currentJob &&
|
|
this.currentJob.status != 0 &&
|
|
this.currentJob.costings &&
|
|
this.currentJob.costings.billableAmount &&
|
|
this.currentJob.invoiceStatus == jobInvoiceStatus.NONE);
|
|
}
|
|
|
|
reloadJobs() {
|
|
const startTime = performance.now();
|
|
this.jobCache.invalidate();
|
|
|
|
if (this.lastFiltersQuery) {
|
|
this.store.dispatch(new jobActions.Fetch({
|
|
jobsByPilot: (this.authSvc.isPilotUser && this.settings.jobsByPilot),
|
|
filters: JSON.stringify(this.lastFiltersQuery)
|
|
}));
|
|
} else {
|
|
this.fetchJobsByClient(this.currClient && this.currClient.value);
|
|
}
|
|
|
|
// Track job list reload
|
|
setTimeout(() => {
|
|
const endTime = performance.now();
|
|
this.gaService.trackJobListViewed({
|
|
user_id: this.authSvc.user?._id || 'anonymous',
|
|
platform: 'web',
|
|
view_type: 'table',
|
|
total_jobs: this.jobs?.length || 0,
|
|
displayed_jobs: this.jobs?.length || 0,
|
|
sort_by: this.dt?.sortField || null,
|
|
filter_count: this.getActiveFilterCount(),
|
|
load_time_ms: Math.round(endTime - startTime),
|
|
client_filter_applied: !!this.currClient?.value,
|
|
reload_interval: this.reloadBy
|
|
});
|
|
}, 100);
|
|
}
|
|
|
|
reloadChanged(value) {
|
|
if (this.reload$) {
|
|
this.reload$.unsubscribe();
|
|
}
|
|
if (!value) {
|
|
return;
|
|
}
|
|
this.reload$ = interval(value * 60 * 1000).subscribe(() => this.reloadJobs());
|
|
}
|
|
|
|
deleteJob() {
|
|
this.confirmSvc.confirm({
|
|
message: globals.confirmDeleteThing.replace('#thing#', globals.job),
|
|
accept: () => {
|
|
this.store.dispatch(new jobActions.Delete(this.currentJob));
|
|
this.currentJob = null;
|
|
}
|
|
});
|
|
}
|
|
|
|
createInvoice() {
|
|
if (!this.defaultInvoiceSetting) {
|
|
this.msgSvc.addFailedMsg($localize`:@@noInvoiceSettingOnCreateInvoiceErr:Please create invoice setting before create invoice`);
|
|
return;
|
|
}
|
|
if (this.defaultInvoiceSetting && this.currentJob.costings.currency != this.defaultInvoiceSetting.currency) {
|
|
this.msgSvc.addFailedMsg($localize`:@@jobCurrencyNotMatchSettingErr:This job's currency does not match with invoice currency setting.`);
|
|
return;
|
|
}
|
|
this.router.navigate(['/invoices/edit/0']);
|
|
}
|
|
|
|
gotoClients() {
|
|
this.router.navigate(['/clients']);
|
|
}
|
|
|
|
onFiltersSubmit(event: FilterChangeEvent) {
|
|
const q = { ...event.query };
|
|
// Ensure createdAt always has a value so the server always applies a date range.
|
|
// Default to 'Past 1 Month' if the user has not added a Created Date filter.
|
|
if (!q.createdAt) {
|
|
q.createdAt = { value: '1m', operator: 'and', valueOperator: 'exact', dataType: 'date-preset' };
|
|
}
|
|
|
|
// Sync the table's client dropdown to match the client selected in the search filters.
|
|
const clientId = q.client?.value ?? null;
|
|
const matchedClient = this.clients?.find(c => c.value === clientId);
|
|
this.currClient = matchedClient || { label: globals.all, value: null };
|
|
this.filterClientLocked = !!clientId;
|
|
|
|
this.lastFiltersQuery = q;
|
|
this.store.dispatch(new jobActions.Fetch({
|
|
jobsByPilot: (this.authSvc.isPilotUser && this.settings.jobsByPilot),
|
|
filters: JSON.stringify(q)
|
|
}));
|
|
}
|
|
|
|
getUsers(byUsers) {
|
|
if (!byUsers || !Array.isArray(byUsers) || byUsers.length === 0) {
|
|
return '';
|
|
}
|
|
let byStr = '';
|
|
for (let i = 0; i < byUsers.length; i++) {
|
|
const it = byUsers[i];
|
|
byStr += `${it.user} - ${this.datePipe.transform(it.date, 'MMM.dd')}`;
|
|
if (i !== byUsers.length - 1) {
|
|
byStr += ', ';
|
|
}
|
|
}
|
|
return $localize`:@@by:by` + ': <br/>' + byStr;
|
|
}
|
|
|
|
handleStatusFilter(value) {
|
|
const previousCount = this.jobs?.length || 0;
|
|
|
|
switch (value) {
|
|
case jobListStatus.ALL:
|
|
this.dt.filter(null, 'status', 'equals');
|
|
this.dt.filter('', 'invoiceStatus', 'contains');
|
|
this.statusFilter = jobListStatus.ALL;
|
|
break;
|
|
case jobListStatus.NEW:
|
|
this.dt.filter(0, 'status', 'equals');
|
|
this.dt.filter('', 'invoiceStatus', 'contains');
|
|
this.statusFilter = jobListStatus.NEW;
|
|
break;
|
|
case jobListStatus.READY:
|
|
this.dt.filter(1, 'status', 'equals');
|
|
this.dt.filter('', 'invoiceStatus', 'contains');
|
|
this.statusFilter = jobListStatus.READY;
|
|
break;
|
|
case jobListStatus.DOWNLOAD:
|
|
this.dt.filter(2, 'status', 'equals');
|
|
this.dt.filter('', 'invoiceStatus', 'contains');
|
|
this.statusFilter = jobListStatus.DOWNLOAD;
|
|
break;
|
|
case jobListStatus.SPRAY:
|
|
this.dt.filter(3, 'status', 'equals');
|
|
this.dt.filter('', 'invoiceStatus', 'contains');
|
|
this.statusFilter = jobListStatus.SPRAY;
|
|
break;
|
|
case jobListStatus.INVOICED:
|
|
this.dt.filter(null, 'status', 'equals');
|
|
this.dt.filter(jobInvoiceStatus.INVOICED, 'invoiceStatus', 'contains');
|
|
this.statusFilter = jobListStatus.INVOICED;
|
|
break;
|
|
}
|
|
|
|
// Track filter usage
|
|
setTimeout(() => {
|
|
const currentCount = this.jobs?.length || 0;
|
|
this.gaService.trackJobListFiltered({
|
|
user_id: this.authSvc.user?._id || 'anonymous',
|
|
platform: 'web',
|
|
filter_type: 'status',
|
|
filter_value: value,
|
|
results_before: previousCount,
|
|
results_after: currentCount,
|
|
filter_effectiveness: previousCount > 0 ? (currentCount / previousCount) : 0
|
|
});
|
|
}, 100);
|
|
}
|
|
|
|
// Helper method to count active filters
|
|
private getActiveFilterCount(): number {
|
|
let count = 0;
|
|
|
|
// Check status filter
|
|
if (this.statusFilter && this.statusFilter !== jobListStatus.ALL) {
|
|
count++;
|
|
}
|
|
|
|
// Check client filter
|
|
if (this.currClient?.value) {
|
|
count++;
|
|
}
|
|
|
|
// Check date filter
|
|
if (this.currentByTime && this.currentByTime.length > 0) {
|
|
count++;
|
|
}
|
|
|
|
// Check table column filters
|
|
if (this.dt?.filters) {
|
|
Object.keys(this.dt.filters).forEach(key => {
|
|
const filter = this.dt.filters[key];
|
|
if (filter && filter.value && filter.value !== '') {
|
|
count++;
|
|
}
|
|
});
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
ngOnDestroy() {
|
|
super.ngOnDestroy();
|
|
if (this.reload$) {
|
|
this.reload$.unsubscribe();
|
|
}
|
|
}
|
|
}
|