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/search-console-widget/traffic-display.js
import { __, sprintf } from '@wordpress/i18n';
import { Container, Skeleton, Text, LineChart } from '@bsf/force-ui';
import { ExternalLink } from '@wordpress/components';
import { ArrowDown, ArrowUp } from 'lucide-react';
import {
	cn,
	formatNumber,
	prepareURL,
	getMetricStyles,
	getMetricValues,
} from '@/functions/utils';
import { useWidgetState } from './context/widget-context';
import { DEFAULT_DATE_RANGE } from './constants';
import { InfoTooltip } from '@AdminComponents/tooltip';

/**
 * Clicks and Impressions Metric Card
 *
 * @param {Object}  props           Component props
 * @param {Object}  props.item      Metric item data
 * @param {boolean} props.isLoading Loading state
 * @return {JSX.Element} Metric card component
 */
const ClicksAndImpressions = ( { item, isLoading } ) => {
	const { renderValue, renderDifference } = getMetricValues( item );
	let Icon = item.percentageType === 'success' ? ArrowUp : ArrowDown;

	const { differenceClassName, fallbackClassName } = getMetricStyles( item );

	// Render N/A and null for difference and icon when both value and previous are null.
	if ( item.value === null && item.previous === null ) {
		Icon = null;
	}

	if ( item.value === 0 && item.previous === 0 ) {
		Icon = null;
	}

	return (
		<Container.Item
			key={ item.label }
			className="px-3 py-3 space-y-2 w-full h-full bg-background-primary shadow-sm rounded-sm border-0.5 border-solid border-[#C3C4C7]"
		>
			<Container
				align="center"
				justify="between"
				gap="sm"
				className="p-1"
			>
				<Text size={ 14 } weight={ 500 }>
					{ item.label }
				</Text>
				<span className={ cn( 'size-2 rounded-sm', item.color ) } />
			</Container>
			<Container
				align="center"
				justify="between"
				gap="sm"
				className="p-1"
			>
				{ isLoading ? (
					<Skeleton variant="rectangular" className="w-24 h-7" />
				) : (
					<Text
						size={ 20 }
						weight={ 600 }
						className={ cn( fallbackClassName ) }
					>
						{ renderValue }
					</Text>
				) }
				{ isLoading ? (
					<Skeleton variant="rectangular" className="w-16 h-4" />
				) : (
					<Text
						size={ 14 }
						weight={ 500 }
						className={ cn(
							differenceClassName,
							fallbackClassName
						) }
					>
						{ !! Icon && <Icon className="size-5 align-bottom" /> }
						<span className="text-inherit">
							{ renderDifference }
						</span>
					</Text>
				) }
			</Container>
		</Container.Item>
	);
};

/**
 * Traffic Display Component
 * Shows the line chart with clicks and impressions data
 *
 * @return {JSX.Element} Traffic display component
 */
const TrafficDisplay = () => {
	const { isLoading, clicksData, siteTraffic } = useWidgetState();
	const {
		settings_page_url: settingsPageURL,
		gsc_selected_site: selectedSite,
	} = window.surerank_search_console_widget || {};

	return (
		<>
			<Container direction="column" className="gap-2">
				<div className="p-2 w-full bg-background-primary shadow-sm rounded-sm border-0.5 border-solid border-[#C3C4C7]">
					<Container
						gap="none"
						justify="between"
						align="center"
						className="p-1 mb-2"
					>
						<div className="flex items-center gap-1">
							<Text
								as="h4"
								size={ 16 }
								weight={ 600 }
								className="!m-0 !text-base !font-semibold"
							>
								{ sprintf(
									// translators: %d is the number of days.
									__(
										'Traffic from last %d days',
										'surerank'
									),
									DEFAULT_DATE_RANGE
								) }
							</Text>
							<InfoTooltip
								content={ sprintf(
									// translators: %s is the selected site URL.
									__( 'Site: %s', 'surerank' ),
									prepareURL( selectedSite )
								) }
							/>
						</div>
						<ExternalLink
							className="text-xs font-semibold [&>span]:no-underline [&:hover>:first-child]:underline"
							href={ settingsPageURL }
						>
							{ __( 'View More', 'surerank' ) }
						</ExternalLink>
					</Container>
					{ isLoading && (
						<Skeleton
							variant="rectangular"
							className="w-full h-[288px]"
						/>
					) }
					{ ! isLoading && siteTraffic.length === 0 && (
						<Container
							gap="md"
							direction="column"
							align="center"
							justify="center"
							className="h-[288px] p-8 gap-2"
						>
							<Text
								size={ 14 }
								weight={ 600 }
								className="text-center"
								color="primary"
							>
								{ __( 'No data available', 'surerank' ) }
							</Text>
							<Text
								size={ 14 }
								weight={ 400 }
								color="tertiary"
								className="text-center max-w-md"
							>
								{ __(
									'Search Console data might take up to 30 days to appear for newly added sites. Please check back later.',
									'surerank'
								) }
							</Text>
						</Container>
					) }
					{ ! isLoading && siteTraffic.length > 0 && (
						<LineChart
							colors={ [
								{
									stroke: '#2171B1',
								},
								{
									stroke: '#72AEE6',
								},
							] }
							yAxisFontColor={ [ '#2171B1', '#72AEE6' ] }
							data={ siteTraffic }
							dataKeys={ [ 'impressions', 'clicks' ] }
							showTooltip
							showXAxis={ true }
							showYAxis={ true }
							biaxial
							tooltipIndicator="dot"
							variant="gradient"
							xAxisDataKey="readableDate"
							yAxisTickFormatter={ ( value ) =>
								formatNumber( value )
							}
							showLegend={ false }
							chartHeight={ 288 }
							chartWidth="100%"
							lineChartWrapperProps={ {
								margin: {
									top: 25,
									right: -18,
									bottom: 10,
									left: -18,
								},
							} }
						/>
					) }
				</div>
				<Container
					className="w-full gap-2 grid grid-cols-1 md:grid-cols-2"
					align="stretch"
				>
					{ clicksData.map( ( item ) => (
						<ClicksAndImpressions
							key={ item.label }
							item={ item }
							isLoading={ isLoading }
						/>
					) ) }
				</Container>
			</Container>
		</>
	);
};

export default TrafficDisplay;