| 1 |
/** |
| 2 |
* Statistics dashboard shared filter state. |
| 3 |
* |
| 4 |
* The singleton is the ONLY mutation path: modules call lpStatsState.set() |
| 5 |
* and every tab module re-renders on the 'lp-stats:filter-changed' event. |
| 6 |
* Tab-specific deep-link params (e.g. order_status) are read by their own |
| 7 |
* tab module — only the four global filters live here. |
| 8 |
* |
| 9 |
* On every set() the five filters are written back to the URL (replaceState, |
| 10 |
* no history spam) so the current view is always copy/paste shareable; other |
| 11 |
* query params (page, tab, order_status, …) are preserved untouched. |
| 12 |
* |
| 13 |
* @since 4.4.2 |
| 14 |
* @version 1.2.0 |
| 15 |
*/ |
| 16 |
|
| 17 |
export const LP_STATS_FILTER_CHANGED = 'lp-stats:filter-changed'; |
| 18 |
export const LP_STATS_EXPORT_CSV = 'lp-stats:export-csv'; |
| 19 |
// Server-resolved range echoed by a stats payload ( data.range ). Carries the |
| 20 |
// authoritative toggle label so the filter bar can reconcile its optimistic one. |
| 21 |
export const LP_STATS_RANGE_RESOLVED = 'lp-stats:range-resolved'; |
| 22 |
|
| 23 |
const COMPARE_DEFAULT = 'previous_period'; |
| 24 |
|
| 25 |
export class LpStatsState { |
| 26 |
constructor() { |
| 27 |
const params = new URL( window.location.href ).searchParams; |
| 28 |
|
| 29 |
this.filters = { |
| 30 |
filtertype: params.get( 'filtertype' ) || 'today', |
| 31 |
date: params.get( 'date' ) || '', |
| 32 |
compare: |
| 33 |
'previous_year' === params.get( 'compare' ) |
| 34 |
? 'previous_year' |
| 35 |
: COMPARE_DEFAULT, |
| 36 |
instructor_id: parseInt( params.get( 'instructor_id' ), 10 ) || 0, |
| 37 |
category_id: parseInt( params.get( 'category_id' ), 10 ) || 0, |
| 38 |
}; |
| 39 |
} |
| 40 |
|
| 41 |
get() { |
| 42 |
return { ...this.filters }; |
| 43 |
} |
| 44 |
|
| 45 |
set( partial = {} ) { |
| 46 |
this.filters = { ...this.filters, ...partial }; |
| 47 |
|
| 48 |
this.syncUrl(); |
| 49 |
|
| 50 |
document.dispatchEvent( |
| 51 |
new CustomEvent( LP_STATS_FILTER_CHANGED, { detail: this.get() } ) |
| 52 |
); |
| 53 |
} |
| 54 |
|
| 55 |
/** |
| 56 |
* Reflect the current filters in the URL without pushing a history entry. |
| 57 |
* Defaults (today / empty date / id 0) are dropped to keep URLs clean; |
| 58 |
* unrelated params (page, tab, order_status, …) are left as-is. |
| 59 |
*/ |
| 60 |
syncUrl() { |
| 61 |
if ( ! window.history || typeof window.history.replaceState !== 'function' ) { |
| 62 |
return; |
| 63 |
} |
| 64 |
|
| 65 |
const url = new URL( window.location.href ); |
| 66 |
const params = url.searchParams; |
| 67 |
const { filtertype, date, compare, instructor_id: instructorId, category_id: categoryId } = this.filters; |
| 68 |
|
| 69 |
this.writeParam( params, 'filtertype', filtertype && filtertype !== 'today' ? filtertype : '' ); |
| 70 |
this.writeParam( params, 'date', date ); |
| 71 |
this.writeParam( params, 'compare', compare && compare !== COMPARE_DEFAULT ? compare : '' ); |
| 72 |
this.writeParam( params, 'instructor_id', instructorId > 0 ? String( instructorId ) : '' ); |
| 73 |
this.writeParam( params, 'category_id', categoryId > 0 ? String( categoryId ) : '' ); |
| 74 |
|
| 75 |
window.history.replaceState( null, '', url.toString() ); |
| 76 |
} |
| 77 |
|
| 78 |
/** |
| 79 |
* Set the param when a truthy value is given, otherwise remove it. |
| 80 |
*/ |
| 81 |
writeParam( params, key, value ) { |
| 82 |
if ( value ) { |
| 83 |
params.set( key, value ); |
| 84 |
} else { |
| 85 |
params.delete( key ); |
| 86 |
} |
| 87 |
} |
| 88 |
} |
| 89 |
|
| 90 |
export const lpStatsState = new LpStatsState(); |
| 91 |
|