import { useMemo, useState, useEffect } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import apiFetch from '@wordpress/api-fetch'; import AnalysisPie from '../../shared/AnalysisPie'; import AnalysisPieDetails from '../../shared/AnalysisPieDetails'; import AnalysisPieDetail from '../../shared/AnalysisPieDetail'; import { decodeHtmlEntities, getPossiblePieColors } from './../../shared/utils'; import PageAnalytics from '../../shared/PageAnalytics'; type SiteRuleItem = { postId: number; postTitle: string; name: string; title: string; clicks: number; views: number; modal: ModalData; }; type SiteRule = { title: string; count: number; items: SiteRuleItem[]; }; export default function SiteRules( props ) { const { getCachedData, setCachedData } = props; const [ data, setData ] = useState( null ); const [ error, setError ] = useState( null ); const [ isModalOpen, setIsModalOpen ] = useState( false ); const [ modalData, setModalData ] = useState( null ); const ENDPOINT = '/growthstack/v1/analytics/personalization/'; useEffect( () => { const cached = getCachedData( ENDPOINT ); if ( cached?.isFetched ) { setData( cached.data ); setError( cached.error ); return; } setError( null ); apiFetch( { path: ENDPOINT } ) .then( ( response ) => { const responseData = response || null; setData( responseData ); setCachedData( ENDPOINT, responseData, null ); } ) .catch( ( fetchError ) => { setError( fetchError ); setCachedData( ENDPOINT, null, fetchError ); // eslint-disable-next-line no-console console.error( 'Failed to fetch personalization data', fetchError ); } ); }, [ getCachedData, setCachedData ] ); const isLoading = ! data && ! error; const refinedData = useMemo( () => { if ( !! data && !! data.overview_365 ) { const rules = [] as SiteRule[]; // make SiteRule from data?.overview_365 as SitePersonalizationReport[] Object.values( data?.overview_365 as SitePersonalizationReport[] ).forEach( ( page ) => { const items = page.analytics.map( ( variation ) => { if ( variation.is_default ) { return null; // skip default variations } return { postId: page.id, postTitle: page.title, name: variation.name, title: decodeHtmlEntities( variation.rule ), clicks: variation.clicks, views: variation.views, modal: page.modal, }; } ); // Add items under rule if exist, otherwise create a new rule if ( items.length > 0 ) { items.forEach( ( item ) => { if ( item ) { const existingRule = rules.find( ( rule ) => rule.title === item.title ); if ( existingRule ) { existingRule.items.push( item ); existingRule.count += 1; } else { rules.push( { title: item.title, count: 1, items: [ item ], } ); } } } ); } } ); // sort rules by count descending rules.sort( ( a, b ) => b.count - a.count ); // sort items by views descending within each rule rules.forEach( ( rule ) => { rule.items.sort( ( a, b ) => b.views - a.views ); } ); return rules; } return []; }, [ data ] ); // sort by viewCount const sortedData = refinedData; const possibleColors = getPossiblePieColors(); const dataPoints = sortedData.map( ( rule ) => ( { label: rule.title, value: rule.count, color: possibleColors[ sortedData.indexOf( rule ) % possibleColors.length ], } ) ); return (

{ __( 'Rules Used', 'liana-with-growthstack' ) }

{ /*

{ __( 'Default variations are not shown here.', 'liana-with-growthstack' ) }

*/ } { /* { isLoading && ( ) } */ } { error && (
{ __( "There's an error fetching the data.", 'liana-with-growthstack' ) }
) } { !! sortedData && sortedData.length > 0 && ( <> { dataPoints.map( ( point, index ) => ( ( { label: `${ item.postTitle }: ${ item.name }`, displayCount: item.views + ' ' + __( 'views', 'liana-with-growthstack' ), onClick: () => { setModalData( item.modal ); setIsModalOpen( true ); }, } ) ) } /> ) ) } { isModalOpen && ( setIsModalOpen( false ) } data={ modalData } /> ) } ) } { !! sortedData && sortedData.length === 0 && ! isLoading && (
{ __( "There's no data to show yet.", 'liana-with-growthstack' ) }
) }
); }