HEX
Server: LiteSpeed
System: Linux server902.web-hosting.com 4.18.0-553.54.1.lve.el8.x86_64 #1 SMP Wed Jun 4 13:01:13 UTC 2025 x86_64
User: deshuvsd (2181)
PHP: 8.1.33
Disabled: NONE
Upload Files
File: /home/deshuvsd/www/wp-content/plugins/surerank/src/apps/seo-popup/components/analyze/analyze.js
import { Accordion, Text, Alert } from '@bsf/force-ui';
import { __ } from '@wordpress/i18n';
import { useCallback, useEffect, useMemo } from '@wordpress/element';
import {
	useSelect,
	useDispatch,
	select as staticSelect,
} from '@wordpress/data';
import RefreshButtonPortal from '@SeoPopup/components/refresh-button-portal';
import { STORE_NAME } from '@/store/constants';
import PageChecksHoc from '@SeoPopup/components/page-seo-checks/page-checks-hoc';
import PageBuilderPageSeoChecksHoc from '@SeoPopup/components/page-seo-checks/page-builder-page-checks-hoc';
import KeywordInput from '@SeoPopup/components/keyword-input';
import { ENABLE_PAGE_LEVEL_SEO } from '@Global/constants';
import {
	isPageBuilderActive,
	isElementorBuilder,
	refreshPageChecks,
	isSeoAnalysisDisabled,
	isBricksBuilder,
	isAvadaBuilder,
} from '@SeoPopup/components/page-seo-checks/analyzer/utils/page-builder';
import { calculateCheckStatus } from '@SeoPopup/utils/calculate-check-status';

const ChecksComponent = ( { type } ) => {
	if ( isSeoAnalysisDisabled() ) {
		return null;
	}

	const isPageBuilder = isPageBuilderActive();

	if ( isPageBuilder ) {
		return <PageBuilderPageSeoChecksHoc type={ type } />;
	}

	return <PageChecksHoc type={ type } />;
};

const Analyze = () => {
	const isPageBuilder = isPageBuilderActive();

	// Get entire pageSeoChecks state and extract what we need
	const pageSeoChecks = useSelect(
		( select ) => select( STORE_NAME ).getPageSeoChecks(),
		[]
	);

	const hasAnyPageCheckIssues = useMemo( () => {
		const categorizedChecks = pageSeoChecks?.categorizedPageChecks || {};
		return calculateCheckStatus( categorizedChecks )?.status !== 'success';
	}, [ pageSeoChecks?.categorizedPageChecks ] );

	const isRefreshing = pageSeoChecks?.isRefreshing || false;
	const brokenLinkStateFromStore = pageSeoChecks?.brokenLinkState || {
		isChecking: false,
		checkedLinks: [],
		brokenLinks: [],
		allLinks: [],
	};

	// Convert arrays back to Sets for backward compatibility
	const brokenLinkState = {
		...brokenLinkStateFromStore,
		checkedLinks: new Set( brokenLinkStateFromStore.checkedLinks ),
		brokenLinks: new Set( brokenLinkStateFromStore.brokenLinks ),
	};

	const modalState = useSelect(
		( select ) => select( STORE_NAME ).getModalState(),
		[]
	);

	const refreshCalled = useSelect(
		( select ) => select( STORE_NAME ).getRefreshCalled(),
		[]
	);

	const { setPageSeoCheck, setRefreshCalled } = useDispatch( STORE_NAME );

	// Create Redux action wrapper functions to maintain compatibility with refreshPageChecks
	const setIsRefreshing = useCallback(
		( value ) => {
			setPageSeoCheck( 'isRefreshing', value );
		},
		[ setPageSeoCheck ]
	);

	const setBrokenLinkState = useCallback(
		( value ) => {
			// Convert Sets to arrays before storing in Redux
			let storeValue = value;
			if ( typeof value === 'function' ) {
				// For functional updates, we need to get current state, apply function, then convert
				const currentState = {
					...brokenLinkStateFromStore,
					checkedLinks: new Set(
						brokenLinkStateFromStore.checkedLinks
					),
					brokenLinks: new Set(
						brokenLinkStateFromStore.brokenLinks
					),
				};
				const updatedState = value( currentState );
				storeValue = {
					...updatedState,
					checkedLinks: Array.from( updatedState.checkedLinks || [] ),
					brokenLinks: Array.from( updatedState.brokenLinks || [] ),
				};
			} else if ( value && typeof value === 'object' ) {
				storeValue = {
					...value,
					checkedLinks:
						value.checkedLinks instanceof Set
							? Array.from( value.checkedLinks )
							: value.checkedLinks || [],
					brokenLinks:
						value.brokenLinks instanceof Set
							? Array.from( value.brokenLinks )
							: value.brokenLinks || [],
				};
			}
			setPageSeoCheck( 'brokenLinkState', storeValue );
		},
		[ setPageSeoCheck, brokenLinkStateFromStore ]
	);

	const handleRefreshWithBrokenLinks = useCallback( async () => {
		setRefreshCalled( true );
		await refreshPageChecks(
			setIsRefreshing,
			setBrokenLinkState,
			setPageSeoCheck,
			staticSelect,
			pageSeoChecks,
			brokenLinkState
		);
	}, [
		setIsRefreshing,
		setBrokenLinkState,
		setPageSeoCheck,
		pageSeoChecks,
		brokenLinkState,
		setRefreshCalled,
	] );

	// Auto-refresh functionality for page builders
	useEffect( () => {
		if ( isPageBuilder && modalState && ! refreshCalled ) {
			refreshPageChecks(
				setIsRefreshing,
				setBrokenLinkState,
				setPageSeoCheck,
				staticSelect,
				pageSeoChecks,
				brokenLinkState
			);
			setRefreshCalled( true );
		}
	}, [
		isPageBuilder,
		modalState,
		refreshCalled,
		setPageSeoCheck,
		pageSeoChecks,
		brokenLinkState,
		setRefreshCalled,
	] );

	// Determine default accordion value based on page check status
	const defaultAccordionValue = hasAnyPageCheckIssues
		? 'page-checks'
		: 'keyword-checks';

	// Early return if no valid component is found.
	if ( isSeoAnalysisDisabled() ) {
		return (
			<div>
				<Text
					color="help"
					size={ 14 }
					className="text-center py-5 border-0.5 border-solid border-border-secondary rounded-md"
				>
					{ __(
						'SEO analysis is not available for this page.',
						'surerank'
					) }
				</Text>
			</div>
		);
	}

	return (
		<div className="space-y-2">
			{ /* Show save message only for Elementor */ }
			{ isElementorBuilder() && (
				<div className="[&_p.mr-10]:mr-0">
					<Alert
						variant="info"
						content={
							<span className="flex items-start gap-2">
								<p>
									{ __(
										'Please save changes in the editor before refreshing the checks.',
										'surerank'
									) }
								</p>
								<span className="-mr-3 refresh-button-container shrink-0" />
							</span>
						}
						className="shadow-none"
					/>
				</div>
			) }
			{ /* Render RefreshButtonPortal at top level so it's always available for page builders */ }
			{ isPageBuilder && (
				<RefreshButtonPortal
					isRefreshing={ isRefreshing }
					isChecking={ pageSeoChecks.isCheckingLinks }
					onClick={ handleRefreshWithBrokenLinks }
				/>
			) }
			<Accordion
				autoClose={ true }
				defaultValue={ defaultAccordionValue }
				type="boxed"
			>
				<Accordion.Item
					value="page-checks"
					className="bg-background-primary overflow-hidden"
				>
					<Accordion.Trigger className="text-base [&>svg]:size-5 pr-2 pl-3 py-3">
						{ __( 'Page Checks', 'surerank' ) }
					</Accordion.Trigger>
					<Accordion.Content>
						<div className="pt-3">
							<ChecksComponent type="page" />
						</div>
					</Accordion.Content>
				</Accordion.Item>
				<Accordion.Item
					value="keyword-checks"
					className="bg-background-primary overflow-hidden"
				>
					<Accordion.Trigger className="text-base [&>svg]:size-5 pr-2 pl-3 py-3">
						{ __( 'Keyword Checks', 'surerank' ) }
					</Accordion.Trigger>
					<Accordion.Content>
						<div className="pt-3">
							{ ENABLE_PAGE_LEVEL_SEO &&
								! isBricksBuilder() &&
								! isAvadaBuilder() && (
									<div className="flex items-center gap-2 mb-3">
										<KeywordInput />
									</div>
								) }
							<ChecksComponent type="keyword" />
						</div>
					</Accordion.Content>
				</Accordion.Item>
			</Accordion>
		</div>
	);
};

export default Analyze;