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 / admin / components / common / TitlePreview.js

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

230 lines 8.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Title Preview Component
3 *
4 * Live preview of title formats with real-time updates
5 *
6 * @package ThinkRank
7 * @since 1.0.0
8 */
9
10 import { useState, useEffect } from '@wordpress/element';
11 import { __ } from '@wordpress/i18n';
12 import {
13 Card,
14 CardBody,
15 CardHeader,
16 __experimentalSpacer as Spacer
17 } from '@wordpress/components';
18
19 /**
20 * Title Preview Component
21 */
22 const TitlePreview = ({
23 titleFormats,
24 separator,
25 siteInfo,
26 disabled = false
27 }) => {
28 const [previews, setPreviews] = useState({});
29
30 /**
31 * Get separator character
32 */
33 const getSeparatorChar = (separatorType) => {
34 const separators = {
35 'pipe': '|',
36 'dash': '-',
37 'double_right': '»',
38 'single_right': '',
39 'bullet': ''
40 };
41 return separators[separatorType] || '|';
42 };
43
44 /**
45 * Replace template variables with sample data
46 */
47 const replaceVariables = (template, pageType) => {
48 const sampleData = {
49 '%site_title%': siteInfo.site_name || 'Your Site Name',
50 '%site_name%': siteInfo.site_name || 'Your Site Name',
51 '%site_description%': siteInfo.site_description || 'Your site description',
52 '%tagline%': siteInfo.tagline || 'Your site tagline',
53 '%post_title%': 'Sample Blog Post Title',
54 '%page_title%': 'Sample Page Title',
55 '%category_title%': 'Sample Category',
56 '%tag_title%': 'Sample Tag',
57 '%author_name%': 'John Doe',
58 '%search_term%': 'sample search',
59 '%archive_title%': 'Sample Archive'
60 };
61
62 let result = template;
63 Object.keys(sampleData).forEach(variable => {
64 result = result.replace(new RegExp(variable.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), sampleData[variable]);
65 });
66
67 return result;
68 };
69
70 /**
71 * Generate title preview
72 */
73 const generatePreview = (template, pageType) => {
74 if (!template) return '';
75
76 const separatorChar = ` ${getSeparatorChar(separator)} `;
77 let preview = replaceVariables(template, pageType);
78
79 // Replace separator placeholders
80 preview = preview.replace(/\s*\|\s*/g, separatorChar);
81
82 return preview;
83 };
84
85 /**
86 * Update previews when formats or separator change
87 */
88 useEffect(() => {
89 const newPreviews = {};
90
91 Object.keys(titleFormats).forEach(pageType => {
92 newPreviews[pageType] = generatePreview(titleFormats[pageType], pageType);
93 });
94
95 setPreviews(newPreviews);
96 }, [titleFormats, separator, siteInfo]);
97
98 /**
99 * Get page type display name
100 */
101 const getPageTypeLabel = (pageType) => {
102 const labels = {
103 'homepage_title': __('Homepage', 'thinkrank'),
104 'post_title': __('Blog Post', 'thinkrank'),
105 'page_title': __('Page', 'thinkrank'),
106 'category_title': __('Category', 'thinkrank'),
107 'tag_title': __('Tag', 'thinkrank'),
108 'author_title': __('Author', 'thinkrank'),
109 'search_title': __('Search Results', 'thinkrank'),
110 'archive_title': __('Archive', 'thinkrank')
111 };
112 return labels[pageType] || pageType;
113 };
114
115 /**
116 * Get title length and status
117 */
118 const getTitleStatus = (title) => {
119 const length = title.length;
120 let status = 'good';
121 let message = '';
122
123 if (length < 30) {
124 status = 'warning';
125 message = __('Too short - consider adding more descriptive text', 'thinkrank');
126 } else if (length > 60) {
127 status = 'warning';
128 message = __('Too long - may be truncated in search results', 'thinkrank');
129 } else {
130 status = 'good';
131 message = __('Good length for SEO', 'thinkrank');
132 }
133
134 return { length, status, message };
135 };
136
137 if (disabled) {
138 return (
139 <Card size="small">
140 <CardBody>
141 <p style={{ color: '#757575', fontStyle: 'italic' }}>
142 {__('Title preview is disabled. Enable site identity to see live previews.', 'thinkrank')}
143 </p>
144 </CardBody>
145 </Card>
146 );
147 }
148
149 return (
150 <Card size="small">
151 <CardHeader>
152 <h4>{__('Live Title Preview', 'thinkrank')}</h4>
153 <p style={{ fontSize: '14px', color: '#757575', margin: '4px 0 0 0' }}>
154 {__('See how your titles will appear with current settings', 'thinkrank')}
155 </p>
156 </CardHeader>
157 <CardBody>
158 <div className="title-preview-list">
159 {Object.keys(previews).map(pageType => {
160 const preview = previews[pageType];
161 const titleStatus = getTitleStatus(preview);
162
163 return (
164 <div key={pageType} className="title-preview-item" style={{ marginBottom: '16px' }}>
165 <div style={{
166 display: 'flex',
167 justifyContent: 'space-between',
168 alignItems: 'center',
169 marginBottom: '4px'
170 }}>
171 <strong>{getPageTypeLabel(pageType)}</strong>
172 <span style={{
173 fontSize: '12px',
174 color: titleStatus.status === 'good' ? '#00a32a' : '#dba617'
175 }}>
176 {titleStatus.length} chars
177 </span>
178 </div>
179
180 <div style={{
181 padding: '8px 12px',
182 backgroundColor: '#f6f7f7',
183 border: '1px solid #ddd',
184 borderRadius: '4px',
185 fontFamily: 'Arial, sans-serif',
186 fontSize: '18px',
187 color: '#1a0dab',
188 lineHeight: '1.3'
189 }}>
190 {preview || __('No title format set', 'thinkrank')}
191 </div>
192
193 {titleStatus.message && (
194 <p style={{
195 fontSize: '12px',
196 color: titleStatus.status === 'good' ? '#00a32a' : '#dba617',
197 margin: '4px 0 0 0'
198 }}>
199 {titleStatus.message}
200 </p>
201 )}
202 </div>
203 );
204 })}
205 </div>
206
207 <Spacer marginY={3} />
208
209 <div style={{
210 padding: '12px',
211 backgroundColor: '#e7f3ff',
212 border: '1px solid #72aee6',
213 borderRadius: '4px',
214 fontSize: '13px'
215 }}>
216 <strong>{__('SEO Tips:', 'thinkrank')}</strong>
217 <ul style={{ margin: '8px 0 0 0', paddingLeft: '20px' }}>
218 <li>{__('Keep titles between 30-60 characters for optimal SEO', 'thinkrank')}</li>
219 <li>{__('Include your target keyword near the beginning', 'thinkrank')}</li>
220 <li>{__('Make titles descriptive and compelling for users', 'thinkrank')}</li>
221 <li>{__('Use consistent separators across your site', 'thinkrank')}</li>
222 </ul>
223 </div>
224 </CardBody>
225 </Card>
226 );
227 };
228
229 export default TitlePreview;
230