262 lines
10 KiB
JavaScript
262 lines
10 KiB
JavaScript
import React, { useState } from 'react'
|
||
import './ComprehensiveMetrics.css'
|
||
|
||
function ComprehensiveMetrics({ metricsData, hookpilotStatus }) {
|
||
const [expandedDomain, setExpandedDomain] = useState(null)
|
||
|
||
if (!metricsData || metricsData.length === 0) {
|
||
return null
|
||
}
|
||
|
||
const toggleDomainDetails = (domain) => {
|
||
setExpandedDomain(expandedDomain === domain ? null : domain)
|
||
}
|
||
|
||
const getStageColor = (stage) => {
|
||
switch (stage) {
|
||
case 'NEW': return '#ff6b6b'
|
||
case 'GROWING': return '#ffa726'
|
||
case 'ESTABLISHED': return '#66bb6a'
|
||
case 'ERROR': return '#9e9e9e'
|
||
default: return '#9e9e9e'
|
||
}
|
||
}
|
||
|
||
const getStageIcon = (stage) => {
|
||
switch (stage) {
|
||
case 'NEW': return '🌱'
|
||
case 'GROWING': return '📈'
|
||
case 'ESTABLISHED': return '🏆'
|
||
case 'ERROR': return '❌'
|
||
default: return '❓'
|
||
}
|
||
}
|
||
|
||
const renderTopKeywords = (keywords) => {
|
||
if (!keywords || keywords.length === 0) {
|
||
return <span className="no-data">No keywords data</span>
|
||
}
|
||
|
||
return (
|
||
<div className="keywords-list">
|
||
{keywords.slice(0, 5).map((keyword, index) => (
|
||
<div key={index} className="keyword-item">
|
||
<span className="keyword-phrase">{keyword.phrase}</span>
|
||
<span className="keyword-metrics">
|
||
SV: {keyword.searchVolume?.toLocaleString() || 'N/A'} |
|
||
KD: {keyword.keywordDifficulty || 'N/A'} |
|
||
Pos: {keyword.position || 'N/A'}
|
||
</span>
|
||
</div>
|
||
))}
|
||
{keywords.length > 5 && (
|
||
<div className="more-keywords">+{keywords.length - 5} more keywords</div>
|
||
)}
|
||
</div>
|
||
)
|
||
}
|
||
|
||
const renderTopCategories = (categories) => {
|
||
if (!categories || categories.length === 0) {
|
||
return <span className="no-data">No categories data</span>
|
||
}
|
||
|
||
return (
|
||
<div className="categories-list">
|
||
{categories.map((category, index) => (
|
||
<div key={index} className="category-item">
|
||
<span className="category-url">{category.url}</span>
|
||
<span className="category-traffic">{category.traffic?.toLocaleString() || 0} visits</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
)
|
||
}
|
||
|
||
const renderDetailedView = (domainData) => {
|
||
if (!domainData || domainData.error) return null
|
||
|
||
return (
|
||
<div className="detailed-view">
|
||
<div className="detailed-sections">
|
||
<div className="section">
|
||
<h4>📊 Complete Metrics</h4>
|
||
<div className="metrics-grid">
|
||
<div className="metric-item">
|
||
<span className="metric-label">Authority Score:</span>
|
||
<span className="metric-value">{domainData.authorityScore}</span>
|
||
</div>
|
||
<div className="metric-item">
|
||
<span className="metric-label">Organic Traffic:</span>
|
||
<span className="metric-value">{domainData.organicTraffic?.toLocaleString()}</span>
|
||
</div>
|
||
<div className="metric-item">
|
||
<span className="metric-label">Organic Keywords:</span>
|
||
<span className="metric-value">{domainData.organicKeywords?.toLocaleString()}</span>
|
||
</div>
|
||
<div className="metric-item">
|
||
<span className="metric-label">Paid Traffic:</span>
|
||
<span className="metric-value">{domainData.paidTraffic?.toLocaleString()}</span>
|
||
</div>
|
||
<div className="metric-item">
|
||
<span className="metric-label">Paid Keywords:</span>
|
||
<span className="metric-value">{domainData.paidKeywords?.toLocaleString()}</span>
|
||
</div>
|
||
<div className="metric-item">
|
||
<span className="metric-label">Total Backlinks:</span>
|
||
<span className="metric-value">{domainData.totalBacklinks?.toLocaleString()}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="section">
|
||
<h4>🔍 Top 10 Keywords</h4>
|
||
<div className="keywords-detailed">
|
||
{domainData.top10Keywords?.map((keyword, index) => (
|
||
<div key={index} className="keyword-detailed">
|
||
<div className="keyword-main">
|
||
<span className="keyword-rank">#{index + 1}</span>
|
||
<span className="keyword-text">{keyword.phrase}</span>
|
||
</div>
|
||
<div className="keyword-stats">
|
||
<span>SV: {keyword.searchVolume?.toLocaleString() || 'N/A'}</span>
|
||
<span>KD: {keyword.keywordDifficulty || 'N/A'}</span>
|
||
<span>Pos: {keyword.position || 'N/A'}</span>
|
||
<span>CPC: ${keyword.cpc?.toFixed(2) || 'N/A'}</span>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="section">
|
||
<h4>📄 Top Categories/Pages</h4>
|
||
<div className="categories-detailed">
|
||
{domainData.topCategories?.map((category, index) => (
|
||
<div key={index} className="category-detailed">
|
||
<div className="category-url">{category.url}</div>
|
||
<div className="category-stats">
|
||
<span>Traffic: {category.traffic?.toLocaleString() || 0}</span>
|
||
<span>Keywords: {category.positionCount || 1}</span>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<div className="comprehensive-metrics">
|
||
<div className="metrics-header">
|
||
<div className="header-content">
|
||
<h2>📊 Comprehensive Domain Metrics</h2>
|
||
<p>Complete SEO analysis with all required metrics for SEO maturity assessment</p>
|
||
</div>
|
||
{hookpilotStatus && (
|
||
<div className={`hookpilot-status ${hookpilotStatus.success ? 'success' : 'error'}`}>
|
||
{hookpilotStatus.success ? '✅ Sent to Hookpilot' : '❌ Hookpilot Error'}
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
<div className="metrics-table-container">
|
||
<table className="metrics-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Actions</th>
|
||
<th>Domain</th>
|
||
<th>Authority Score</th>
|
||
<th>Organic Keywords</th>
|
||
<th>Top 10 Keywords</th>
|
||
<th>Referring Domains</th>
|
||
<th>Monthly Traffic</th>
|
||
<th>Top Categories</th>
|
||
<th>Stage</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{metricsData.map((domainData, index) => (
|
||
<React.Fragment key={index}>
|
||
<tr className={domainData.error ? 'error-row' : ''}>
|
||
<td className="actions-cell">
|
||
{!domainData.error && (
|
||
<button
|
||
className="expand-button"
|
||
onClick={() => toggleDomainDetails(domainData.domain)}
|
||
title="View detailed metrics"
|
||
>
|
||
{expandedDomain === domainData.domain ? '▼' : '▶'}
|
||
</button>
|
||
)}
|
||
</td>
|
||
<td className="domain-cell">
|
||
<strong>{domainData.domain}</strong>
|
||
{index === 0 && <span className="client-badge">Client</span>}
|
||
</td>
|
||
<td className="metric-cell">
|
||
{domainData.error ? 'Error' : domainData.authorityScore?.toLocaleString()}
|
||
</td>
|
||
<td className="metric-cell">
|
||
{domainData.error ? 'Error' : domainData.organicKeywords?.toLocaleString()}
|
||
</td>
|
||
<td className="keywords-cell">
|
||
{domainData.error ? 'Error' : renderTopKeywords(domainData.top10Keywords)}
|
||
</td>
|
||
<td className="metric-cell">
|
||
{domainData.error ? 'Error' : domainData.referringDomains?.toLocaleString()}
|
||
</td>
|
||
<td className="metric-cell">
|
||
{domainData.error ? 'Error' : domainData.monthlyTraffic?.toLocaleString()}
|
||
</td>
|
||
<td className="categories-cell">
|
||
{domainData.error ? 'Error' : renderTopCategories(domainData.topCategories)}
|
||
</td>
|
||
<td className="stage-cell">
|
||
<span
|
||
className="stage-badge"
|
||
style={{ backgroundColor: getStageColor(domainData.stage) }}
|
||
>
|
||
{getStageIcon(domainData.stage)} {domainData.stage}
|
||
</span>
|
||
</td>
|
||
</tr>
|
||
{expandedDomain === domainData.domain && !domainData.error && (
|
||
<tr className="expanded-row">
|
||
<td colSpan="9">
|
||
{renderDetailedView(domainData)}
|
||
</td>
|
||
</tr>
|
||
)}
|
||
</React.Fragment>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
{/* Classification Legend */}
|
||
<div className="classification-legend">
|
||
<h4>📋 SEO Maturity Classification Criteria</h4>
|
||
<div className="legend-items">
|
||
<div className="legend-item">
|
||
<span className="legend-color" style={{ backgroundColor: '#ff6b6b' }}></span>
|
||
<strong>NEW:</strong> AS < 20 OR Organic KW < 500 OR < 10 Ref Domains
|
||
</div>
|
||
<div className="legend-item">
|
||
<span className="legend-color" style={{ backgroundColor: '#ffa726' }}></span>
|
||
<strong>GROWING:</strong> AS 20–40 OR Organic KW 500–3000
|
||
</div>
|
||
<div className="legend-item">
|
||
<span className="legend-color" style={{ backgroundColor: '#66bb6a' }}></span>
|
||
<strong>ESTABLISHED:</strong> AS > 40 OR Organic KW > 3000 OR > 100 Ref Domains
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default ComprehensiveMetrics
|