import {__, sprintf} from '@wordpress/i18n'; import {useMemo} from '@wordpress/element'; import '../../../../admin/assets/design/scss/_sortable_table_header.scss'; import {buildCompareLookup, computeDelta, getRowKey} from './compareMath'; import {formatBucketLabel} from './dateLabels'; const formatNumber = ( num ) => new Intl.NumberFormat( 'default' ).format( Math.round( num ) ); const formatPercent = ( num ) => `${num.toFixed( 2 )}%`; /* translators: %s: difference between two rates, in percentage points. */ const formatPoints = ( num ) => sprintf( __( '%s pp', 'adpresso' ), num.toFixed( 2 ) ); const {dateFormat = 'F j, Y', rowLimit = 500} = window.adpressoAnalyticsData || {}; // Single source for header and cell order. const METRIC_COLUMNS = [ {key: 'impressions', label: __( 'Impressions', 'adpresso' ), format: formatNumber, formatDelta: formatNumber}, {key: 'clicks', label: __( 'Clicks', 'adpresso' ), format: formatNumber, formatDelta: formatNumber}, {key: 'ctr', label: __( 'CTR (%)', 'adpresso' ), format: formatPercent, formatDelta: formatPoints}, {key: 'viewability', label: __( 'Active View Rate (%)', 'adpresso' ), format: formatPercent, formatDelta: formatPoints, pro: true}, {key: 'impressions_av', label: __( 'Active View Impressions', 'adpresso' ), format: formatNumber, formatDelta: formatNumber, pro: true} ]; /** * A metric with its comparison value and the change (absolute, and in % when the comparison value isn't 0). */ export const CompareCell = ( {current, previous, format, formatDelta = format} ) => { const {delta, percent} = computeDelta( current, previous ); const trend = delta > 0 ? 'is-positive' : (delta < 0 ? 'is-negative' : 'is-neutral'); const sign = delta > 0 ? '+' : ''; return (
{format( current )} {__( 'Comparison period:', 'adpresso' )} {format( previous )} ({sign}{formatDelta( delta )}{percent !== null && `, ${sign}${percent.toFixed( 1 )}%`})
); }; const DataTable = ( {tableData, isPro, viewMode, sorting, onSort, compareData = null, onDrilldown, visibleColumns = {}, grouping = 'day'} ) => { const formatDate = ( date ) => formatBucketLabel( date, grouping, dateFormat ); const showCol = ( key ) => visibleColumns[key] !== false; const columns = METRIC_COLUMNS.filter( col => (!col.pro || isPro) && showCol( col.key ) ); const hasCompare = Array.isArray( compareData ); const getVisibleColumnCount = () => 1 + columns.length; const SortableHeader = ( {label, columnKey} ) => { const isSorted = sorting.orderBy === columnKey; const newOrder = (isSorted && sorting.order === 'asc') ? 'desc' : 'asc'; const ariaSort = isSorted ? (sorting.order === 'asc' ? 'ascending' : 'descending') : 'none'; return ( onSort( {orderBy: columnKey, order: newOrder} )} onClick={( e ) => { e.preventDefault(); onSort( {orderBy: columnKey, order: newOrder} ); }} > {label} ); }; const getFirstColumnHeader = () => { switch ( viewMode ) { case 'ads': return __( 'Ad', 'adpresso' ); case 'groups': return __( 'Group', 'adpresso' ); case 'placements': return __( 'Placement', 'adpresso' ); case 'date': default: return __( 'Date', 'adpresso' ); } }; const renderFirstColumn = ( row ) => { let content; let drilldownParams = null; let rowId = 0; switch ( viewMode ) { case 'ads': content = row.name || __( '(No Title)', 'adpresso' ); break; case 'groups': rowId = row.group_id; // ID 0: ads delivered outside a group. content = Number( rowId ) === 0 ? __( 'Without group', 'adpresso' ) : ( row.name || __( '(No Title)', 'adpresso' ) ); drilldownParams = { viewMode: 'ads', filters: {groups: [rowId]} }; break; case 'placements': rowId = row.placement_id; // ID 0: ads delivered without a placement (shortcode, block, widget). content = Number( rowId ) === 0 ? __( 'Without placement', 'adpresso' ) : ( row.name || __( '(No Title)', 'adpresso' ) ); // Define the action for the drilldown. Show ads filtered by this placement. drilldownParams = { viewMode: 'ads', filters: {placements: [rowId]} }; break; case 'date': default: return formatDate( row.date ); } if ( false && drilldownParams ) { return ( { e.preventDefault(); onDrilldown( drilldownParams ); }} title={__( 'Show items', 'adpresso' )} > {content} (ID: {rowId}) ); } return ( <> {content} {row.ad_id && (ID: {row.ad_id})} ); }; const compareMap = useMemo( () => buildCompareLookup( tableData, compareData, viewMode ), [tableData, compareData, viewMode] ); return ( {viewMode === 'date' ? ( ) : ( )} {columns.map( col => )} {tableData.length === 0 && ( )} {tableData.map( ( row, index ) => { const rowKey = row[getRowKey( viewMode )]; const lookupKey = viewMode === 'date' ? rowKey : String( rowKey ); // Date rows past the end of a shorter compare range have no counterpart; entities without one had 0. const compareRow = hasCompare ? (compareMap.get( lookupKey ) || (viewMode === 'date' ? null : {})) : null; return ( {columns.map( col => ( ) )} ); } )} {viewMode !== 'date' && tableData.length >= rowLimit && ( )}
{__( 'No data to display.', 'adpresso' )}
{renderFirstColumn( row )} {compareRow && compareRow.date && ( {/* translators: %s: the date of the comparison period. */} {sprintf( __( 'vs. %s', 'adpresso' ), formatDate( compareRow.date ) )} )} {compareRow ? : col.format( row[col.key] || 0 )}
{/* translators: %d: maximum number of rows shown in the statistics table. */} {sprintf( __( 'Showing the first %d entries for the current sorting. Use the filters to narrow down the list.', 'adpresso' ), rowLimit )}
); }; export default DataTable;