PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 1.0.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v1.0.0
2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.10.0 All 48 releases
thinkrank / src / metabox / components / SerpPreview.js

SerpPreview.js in ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO 1.0.0, at src/metabox/components/SerpPreview.js

155 lines 7.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * SERP Preview Component
3 *
4 * Shows how the page will appear in search engine results
5 *
6 * @package ThinkRank
7 * @since 1.0.0
8 */
9
10 import { __ } from '@wordpress/i18n';
11 import { useState } from '@wordpress/element';
12
13 /**
14 * SERP Preview Component
15 */
16 const SerpPreview = ({ title, description, postTitle, homeUrl }) => {
17 const [viewMode, setViewMode] = useState('desktop'); // 'desktop' or 'mobile' - desktop first
18
19 // Use SEO title or fallback to post title
20 const displayTitle = title || postTitle || __('Your SEO Title', 'thinkrank');
21 const displayDescription = description || __('Please provide a meta description by editing the snippet below. If you don\'t, Google will try to find a relevant part of your post to show in the search results.', 'thinkrank');
22
23 // Get site info automatically
24 const siteName = window.thinkrankMetabox?.siteName || document.title.split('')[1] || 'Your Site';
25 const domain = homeUrl ? new URL(homeUrl).hostname : window.location.hostname;
26 const faviconUrl = window.thinkrankMetabox?.faviconUrl || `${homeUrl}/favicon.ico`;
27
28 // Get current date for preview
29 const currentDate = new Date().toLocaleDateString('en-US', {
30 month: 'short',
31 day: 'numeric',
32 year: 'numeric'
33 });
34
35 // Generate post URL (simplified for preview)
36 const postSlug = postTitle ? postTitle.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '') : 'how-to-create-website-content-wit';
37 const postUrl = `${domain}${postSlug}`;
38
39 // Get featured image (if available)
40 const featuredImageUrl = window.thinkrankMetabox?.featuredImageUrl;
41
42 return (
43 <div className="thinkrank-card">
44 <div className="thinkrank-card-header">
45 <h4>{__('Search Engine Preview', 'thinkrank')}</h4>
46 <p className="description">
47 {__('Determine how your post should look in the search results.', 'thinkrank')}
48 </p>
49 </div>
50 <div className="thinkrank-card-body">
51 {/* Preview Mode Toggle */}
52 <div className="serp-preview-controls">
53 <span className="preview-label">{__('Preview as:', 'thinkrank')}</span>
54 <div className="preview-toggle">
55 <label className={`toggle-option ${viewMode === 'desktop' ? 'active' : ''}`}>
56 <input
57 type="radio"
58 name="serp-view-mode"
59 value="desktop"
60 checked={viewMode === 'desktop'}
61 onChange={() => setViewMode('desktop')}
62 />
63 <span className="radio-icon"></span>
64 {__('Desktop result', 'thinkrank')}
65 </label>
66 <label className={`toggle-option ${viewMode === 'mobile' ? 'active' : ''}`}>
67 <input
68 type="radio"
69 name="serp-view-mode"
70 value="mobile"
71 checked={viewMode === 'mobile'}
72 onChange={() => setViewMode('mobile')}
73 />
74 <span className="radio-icon"></span>
75 {__('Mobile result', 'thinkrank')}
76 </label>
77 </div>
78 </div>
79
80 {/* SERP Result Preview */}
81 <div className={`serp-result-container ${viewMode}`}>
82 <div className="serp-result">
83 {/* Site Info */}
84 <div className="serp-site-info">
85 <div className="site-favicon">
86 <img
87 src={faviconUrl}
88 alt={siteName}
89 className="favicon-image"
90 onError={(e) => {
91 e.target.style.display = 'none';
92 e.target.nextSibling.style.display = 'block';
93 }}
94 />
95 <div className="favicon-placeholder" style={{display: 'none'}}></div>
96 </div>
97 <div className="site-details">
98 <div className="site-name">{siteName}</div>
99 </div>
100 <div className="serp-menu"></div>
101 </div>
102
103 {/* URL */}
104 <div className="serp-url">
105 {postUrl}
106 </div>
107
108 {/* Title */}
109 <div className="serp-title" id="serp-title-preview">
110 <a href="#" className="serp-title-link">
111 {displayTitle}
112 </a>
113 </div>
114
115 {/* Content Area with Image on Right (for mobile) */}
116 <div className="serp-content">
117 {/* Date and Description */}
118 <div className="serp-meta">
119 <span className="serp-date">{currentDate}</span>
120 <span className="serp-separator"></span>
121 <span className="serp-description" id="serp-description-preview">
122 {displayDescription}
123 </span>
124 </div>
125
126 {/* Featured Image (for mobile - positioned on right) */}
127 {viewMode === 'mobile' && (
128 <div className="serp-featured-image">
129 {featuredImageUrl ? (
130 <img
131 src={featuredImageUrl}
132 alt={displayTitle}
133 className="featured-image"
134 onError={(e) => {
135 e.target.style.display = 'none';
136 e.target.nextSibling.style.display = 'block';
137 }}
138 />
139 ) : (
140 <div className="image-placeholder">
141 <div className="image-icon">🖼️</div>
142 </div>
143 )}
144 </div>
145 )}
146 </div>
147 </div>
148 </div>
149 </div>
150 </div>
151 );
152 };
153
154 export default SerpPreview;
155