sanity-plugin-seofields 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -40
- package/dist/index.cjs +1924 -1618
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -17
- package/dist/index.d.ts +68 -17
- package/dist/index.js +1879 -1563
- package/dist/index.js.map +1 -1
- package/dist/next.cjs +1506 -0
- package/dist/next.cjs.map +1 -1
- package/dist/next.d.cts +4 -1
- package/dist/next.d.ts +4 -1
- package/dist/next.js +1457 -0
- package/dist/next.js.map +1 -1
- package/dist/schema/next.cjs +1548 -0
- package/dist/schema/next.cjs.map +1 -0
- package/dist/schema/next.d.cts +438 -0
- package/dist/schema/next.d.ts +438 -0
- package/dist/schema/next.js +1476 -0
- package/dist/schema/next.js.map +1 -0
- package/dist/schema.cjs +1743 -0
- package/dist/schema.cjs.map +1 -0
- package/dist/schema.d.cts +387 -0
- package/dist/schema.d.ts +387 -0
- package/dist/schema.js +1691 -0
- package/dist/schema.js.map +1 -0
- package/dist/types-CVaAX7uy.d.cts +589 -0
- package/dist/types-Ci-ZZT7A.d.ts +589 -0
- package/dist/{types-B91ena4g.d.cts → types-R3n9Fu4w.d.cts} +16 -1
- package/dist/{types-B91ena4g.d.ts → types-R3n9Fu4w.d.ts} +16 -1
- package/package.json +18 -3
package/dist/index.cjs
CHANGED
|
@@ -34,6 +34,9 @@ var __objRest = (source, exclude) => {
|
|
|
34
34
|
}
|
|
35
35
|
return target;
|
|
36
36
|
};
|
|
37
|
+
var __esm = (fn, res) => function __init() {
|
|
38
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
39
|
+
};
|
|
37
40
|
var __export = (target, all) => {
|
|
38
41
|
for (var name in all)
|
|
39
42
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -56,35 +59,651 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
56
59
|
));
|
|
57
60
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
58
61
|
|
|
59
|
-
// src/
|
|
60
|
-
var
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
62
|
+
// src/utils/seoUtils.ts
|
|
63
|
+
var stopWords, hasMatchingKeyword, hasKeywordOveruse, startsWithStopWord, truncate, hasExcessivePunctuation, getMetaTitleValidationMessages, getMetaDescriptionValidationMessages, getOgTitleValidation, getOgDescriptionValidation, getTwitterTitleValidation, getTwitterDescriptionValidation, isSubImageSet, isMetaImageSet, getMetaImageValidation, getOgImageValidation, getOgImageUrlValidation, getTwitterImageValidation, getTwitterImageUrlValidation;
|
|
64
|
+
var init_seoUtils = __esm({
|
|
65
|
+
"src/utils/seoUtils.ts"() {
|
|
66
|
+
"use strict";
|
|
67
|
+
stopWords = ["the", "a", "an", "and", "or", "but"];
|
|
68
|
+
hasMatchingKeyword = (title, keywordList) => {
|
|
69
|
+
if (!title || keywordList.length === 0) return false;
|
|
70
|
+
const lowerTitle = title.toLowerCase();
|
|
71
|
+
return keywordList.some((keyword) => keyword && lowerTitle.includes(keyword.toLowerCase()));
|
|
72
|
+
};
|
|
73
|
+
hasKeywordOveruse = (title, keywordList, maxOccurrences = 3) => {
|
|
74
|
+
if (!title || keywordList.length === 0) return false;
|
|
75
|
+
const lowerTitle = title.toLowerCase();
|
|
76
|
+
return keywordList.some((keyword) => {
|
|
77
|
+
if (!keyword) return false;
|
|
78
|
+
const matches = lowerTitle.match(new RegExp(keyword.toLowerCase(), "g"));
|
|
79
|
+
return matches ? matches.length > maxOccurrences : false;
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
startsWithStopWord = (title) => {
|
|
83
|
+
if (!title) return false;
|
|
84
|
+
const firstWord = title.trim().split(" ")[0].toLowerCase();
|
|
85
|
+
return stopWords.includes(firstWord);
|
|
86
|
+
};
|
|
87
|
+
truncate = (text, maxLength) => text.length > maxLength ? `${text.slice(0, maxLength)}\u2026` : text;
|
|
88
|
+
hasExcessivePunctuation = (title) => /[!@#$%^&*]{2,}/.test(title);
|
|
89
|
+
getMetaTitleValidationMessages = (title, keywords, isParentseoField) => {
|
|
90
|
+
const feedback = [];
|
|
91
|
+
const minChar = 50;
|
|
92
|
+
const maxChar = 60;
|
|
93
|
+
const charCount = (title == null ? void 0 : title.length) || 0;
|
|
94
|
+
if (!(title == null ? void 0 : title.trim())) {
|
|
95
|
+
feedback.push({ text: "Meta Title is empty. Add content to improve SEO.", color: "red" });
|
|
96
|
+
return feedback;
|
|
97
|
+
}
|
|
98
|
+
if (charCount < minChar)
|
|
99
|
+
feedback.push({
|
|
100
|
+
text: `Title is ${charCount} characters \u2014 below recommended ${minChar}.`,
|
|
101
|
+
color: "orange"
|
|
102
|
+
});
|
|
103
|
+
else if (charCount > maxChar)
|
|
104
|
+
feedback.push({
|
|
105
|
+
text: `Title is ${charCount} characters \u2014 exceeds recommended ${maxChar}.`,
|
|
106
|
+
color: "red"
|
|
107
|
+
});
|
|
108
|
+
else feedback.push({ text: `Title length (${charCount}) looks good for SEO.`, color: "green" });
|
|
109
|
+
if (isParentseoField) {
|
|
110
|
+
if (keywords.length > 0) {
|
|
111
|
+
const hasKeyword = hasMatchingKeyword(title, keywords);
|
|
112
|
+
feedback.push({
|
|
113
|
+
text: hasKeyword ? "Keyword found in title \u2014 good job!" : "Keywords defined but missing in title.",
|
|
114
|
+
color: hasKeyword ? "green" : "red"
|
|
115
|
+
});
|
|
116
|
+
if (hasKeywordOveruse(title, keywords)) {
|
|
117
|
+
feedback.push({
|
|
118
|
+
text: "Keyword appears too many times \u2014 avoid keyword stuffing.",
|
|
119
|
+
color: "orange"
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
feedback.push({
|
|
124
|
+
text: "No keywords defined. Consider adding relevant keywords.",
|
|
125
|
+
color: "orange"
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (startsWithStopWord(title))
|
|
130
|
+
feedback.push({ text: "Title starts with a stop word \u2014 consider rephrasing.", color: "orange" });
|
|
131
|
+
if (hasExcessivePunctuation(title))
|
|
132
|
+
feedback.push({ text: "Title contains excessive punctuation \u2014 simplify it.", color: "orange" });
|
|
133
|
+
return feedback;
|
|
134
|
+
};
|
|
135
|
+
getMetaDescriptionValidationMessages = (description, keywords, isParentseoField) => {
|
|
136
|
+
const feedback = [];
|
|
137
|
+
const minChar = 120;
|
|
138
|
+
const maxChar = 160;
|
|
139
|
+
const charCount = (description == null ? void 0 : description.length) || 0;
|
|
140
|
+
if (!(description == null ? void 0 : description.trim())) {
|
|
141
|
+
feedback.push({ text: "Meta description is empty. Add content to improve SEO.", color: "red" });
|
|
142
|
+
return feedback;
|
|
143
|
+
}
|
|
144
|
+
if (charCount < minChar)
|
|
145
|
+
feedback.push({
|
|
146
|
+
text: `Description is ${charCount} chars \u2014 below recommended ${minChar}.`,
|
|
147
|
+
color: "orange"
|
|
148
|
+
});
|
|
149
|
+
else if (charCount > maxChar)
|
|
150
|
+
feedback.push({
|
|
151
|
+
text: `Description is ${charCount} chars \u2014 exceeds recommended ${maxChar}.`,
|
|
152
|
+
color: "red"
|
|
153
|
+
});
|
|
154
|
+
else
|
|
155
|
+
feedback.push({ text: `Description length (${charCount}) looks good for SEO.`, color: "green" });
|
|
156
|
+
if (isParentseoField) {
|
|
157
|
+
if (keywords.length > 0) {
|
|
158
|
+
const hasKeyword = hasMatchingKeyword(description, keywords);
|
|
159
|
+
feedback.push({
|
|
160
|
+
text: hasKeyword ? "Keyword found in description \u2014 good job!" : "Keywords defined but missing in description.",
|
|
161
|
+
color: hasKeyword ? "green" : "red"
|
|
162
|
+
});
|
|
163
|
+
if (hasKeywordOveruse(description, keywords)) {
|
|
164
|
+
feedback.push({
|
|
165
|
+
text: "Keyword appears too many times \u2014 avoid keyword stuffing.",
|
|
166
|
+
color: "orange"
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
feedback.push({
|
|
171
|
+
text: "No keywords defined. Consider adding relevant keywords.",
|
|
172
|
+
color: "orange"
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (startsWithStopWord(description))
|
|
177
|
+
feedback.push({
|
|
178
|
+
text: "Description starts with a stop word \u2014 consider rephrasing.",
|
|
179
|
+
color: "orange"
|
|
180
|
+
});
|
|
181
|
+
if (hasExcessivePunctuation(description))
|
|
182
|
+
feedback.push({
|
|
183
|
+
text: "Description contains excessive punctuation \u2014 simplify it.",
|
|
184
|
+
color: "orange"
|
|
185
|
+
});
|
|
186
|
+
return feedback;
|
|
187
|
+
};
|
|
188
|
+
getOgTitleValidation = (title, keywords = [], isParentseoField) => {
|
|
189
|
+
const feedback = [];
|
|
190
|
+
const min = 40;
|
|
191
|
+
const max = 60;
|
|
192
|
+
const count = (title == null ? void 0 : title.length) || 0;
|
|
193
|
+
if (!(title == null ? void 0 : title.trim())) {
|
|
194
|
+
feedback.push({ text: "OG Title is empty. Add content for better social preview.", color: "red" });
|
|
195
|
+
return feedback;
|
|
196
|
+
}
|
|
197
|
+
if (count < min)
|
|
198
|
+
feedback.push({
|
|
199
|
+
text: `OG Title is ${count} chars \u2014 shorter than recommended ${min}.`,
|
|
200
|
+
color: "orange"
|
|
201
|
+
});
|
|
202
|
+
else if (count > max)
|
|
203
|
+
feedback.push({ text: `OG Title is ${count} chars \u2014 exceeds recommended ${max}.`, color: "red" });
|
|
204
|
+
else feedback.push({ text: `OG Title length (${count}) looks good.`, color: "green" });
|
|
205
|
+
if (isParentseoField) {
|
|
206
|
+
if (keywords.length > 0) {
|
|
207
|
+
const hasKeyword = hasMatchingKeyword(title, keywords);
|
|
208
|
+
feedback.push({
|
|
209
|
+
text: hasKeyword ? "Keyword found in OG title \u2014 good job!" : "Keywords defined but missing in OG title.",
|
|
210
|
+
color: hasKeyword ? "green" : "red"
|
|
211
|
+
});
|
|
212
|
+
if (hasKeywordOveruse(title, keywords)) {
|
|
213
|
+
feedback.push({
|
|
214
|
+
text: "Keyword appears too many times in OG title \u2014 avoid keyword stuffing.",
|
|
215
|
+
color: "orange"
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
feedback.push({
|
|
220
|
+
text: "No keywords defined. Consider adding relevant keywords.",
|
|
221
|
+
color: "orange"
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (startsWithStopWord(title))
|
|
226
|
+
feedback.push({
|
|
227
|
+
text: "OG Title starts with a stop word \u2014 consider rephrasing.",
|
|
228
|
+
color: "orange"
|
|
229
|
+
});
|
|
230
|
+
if (hasExcessivePunctuation(title))
|
|
231
|
+
feedback.push({ text: "OG Title contains excessive punctuation \u2014 simplify it.", color: "orange" });
|
|
232
|
+
return feedback;
|
|
233
|
+
};
|
|
234
|
+
getOgDescriptionValidation = (desc, keywords = [], isParentseoField) => {
|
|
235
|
+
const feedback = [];
|
|
236
|
+
const min = 90;
|
|
237
|
+
const max = 120;
|
|
238
|
+
const count = (desc == null ? void 0 : desc.length) || 0;
|
|
239
|
+
if (!(desc == null ? void 0 : desc.trim())) {
|
|
240
|
+
feedback.push({
|
|
241
|
+
text: "OG Description is empty. Add content for better social preview.",
|
|
242
|
+
color: "red"
|
|
243
|
+
});
|
|
244
|
+
return feedback;
|
|
245
|
+
}
|
|
246
|
+
if (count < min)
|
|
247
|
+
feedback.push({
|
|
248
|
+
text: `OG Description is ${count} chars \u2014 shorter than recommended ${min}.`,
|
|
249
|
+
color: "orange"
|
|
250
|
+
});
|
|
251
|
+
else if (count > max)
|
|
252
|
+
feedback.push({
|
|
253
|
+
text: `OG Description is ${count} chars \u2014 exceeds recommended ${max}.`,
|
|
254
|
+
color: "red"
|
|
255
|
+
});
|
|
256
|
+
else feedback.push({ text: `OG Description length (${count}) looks good.`, color: "green" });
|
|
257
|
+
if (isParentseoField) {
|
|
258
|
+
if (keywords.length > 0) {
|
|
259
|
+
const hasKeyword = hasMatchingKeyword(desc, keywords);
|
|
260
|
+
feedback.push({
|
|
261
|
+
text: hasKeyword ? "Keyword found in OG description \u2014 good job!" : "Keywords defined but missing in OG description.",
|
|
262
|
+
color: hasKeyword ? "green" : "red"
|
|
263
|
+
});
|
|
264
|
+
if (hasKeywordOveruse(desc, keywords)) {
|
|
265
|
+
feedback.push({
|
|
266
|
+
text: "Keyword appears too many times in OG description \u2014 avoid keyword stuffing.",
|
|
267
|
+
color: "orange"
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
} else {
|
|
271
|
+
feedback.push({
|
|
272
|
+
text: "No keywords defined. Consider adding relevant keywords.",
|
|
273
|
+
color: "orange"
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (startsWithStopWord(desc))
|
|
278
|
+
feedback.push({
|
|
279
|
+
text: "OG Description starts with a stop word \u2014 consider rephrasing.",
|
|
280
|
+
color: "orange"
|
|
281
|
+
});
|
|
282
|
+
if (hasExcessivePunctuation(desc))
|
|
283
|
+
feedback.push({
|
|
284
|
+
text: "OG Description contains excessive punctuation \u2014 simplify it.",
|
|
285
|
+
color: "orange"
|
|
286
|
+
});
|
|
287
|
+
return feedback;
|
|
288
|
+
};
|
|
289
|
+
getTwitterTitleValidation = (title, keywords = [], isParentseoField) => {
|
|
290
|
+
const feedback = [];
|
|
291
|
+
const min = 30;
|
|
292
|
+
const max = 70;
|
|
293
|
+
const count = (title == null ? void 0 : title.length) || 0;
|
|
294
|
+
if (!(title == null ? void 0 : title.trim())) {
|
|
295
|
+
feedback.push({ text: "X Title is empty. Add content for better SEO.", color: "red" });
|
|
296
|
+
return feedback;
|
|
297
|
+
}
|
|
298
|
+
if (count < min)
|
|
299
|
+
feedback.push({
|
|
300
|
+
text: `X Title is ${count} chars \u2014 shorter than recommended ${min}.`,
|
|
301
|
+
color: "orange"
|
|
302
|
+
});
|
|
303
|
+
else if (count > max)
|
|
304
|
+
feedback.push({
|
|
305
|
+
text: `X Title is ${count} chars \u2014 exceeds recommended ${max}.`,
|
|
306
|
+
color: "red"
|
|
307
|
+
});
|
|
308
|
+
else feedback.push({ text: `X Title length (${count}) looks good.`, color: "green" });
|
|
309
|
+
if (isParentseoField) {
|
|
310
|
+
if (keywords.length > 0) {
|
|
311
|
+
const hasKeyword = hasMatchingKeyword(title, keywords);
|
|
312
|
+
feedback.push({
|
|
313
|
+
text: hasKeyword ? "Keyword found in X title \u2014 good job!" : "Keywords defined but missing in X title.",
|
|
314
|
+
color: hasKeyword ? "green" : "red"
|
|
315
|
+
});
|
|
316
|
+
} else {
|
|
317
|
+
feedback.push({
|
|
318
|
+
text: "No keywords defined. Consider adding relevant keywords.",
|
|
319
|
+
color: "orange"
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (/[!@#$%^&*]{2,}/.test(title))
|
|
324
|
+
feedback.push({ text: "X Title has excessive punctuation \u2014 simplify it.", color: "orange" });
|
|
325
|
+
return feedback;
|
|
326
|
+
};
|
|
327
|
+
getTwitterDescriptionValidation = (desc, keywords = [], isParentseoField) => {
|
|
328
|
+
const feedback = [];
|
|
329
|
+
const min = 50;
|
|
330
|
+
const max = 200;
|
|
331
|
+
const count = (desc == null ? void 0 : desc.length) || 0;
|
|
332
|
+
if (!(desc == null ? void 0 : desc.trim())) {
|
|
333
|
+
feedback.push({ text: "X Description is empty. Add content for better SEO.", color: "red" });
|
|
334
|
+
return feedback;
|
|
335
|
+
}
|
|
336
|
+
if (count < min)
|
|
337
|
+
feedback.push({
|
|
338
|
+
text: `X Description is ${count} chars \u2014 shorter than recommended ${min}.`,
|
|
339
|
+
color: "orange"
|
|
340
|
+
});
|
|
341
|
+
else if (count > max)
|
|
342
|
+
feedback.push({
|
|
343
|
+
text: `X Description is ${count} chars \u2014 exceeds recommended ${max}.`,
|
|
344
|
+
color: "red"
|
|
345
|
+
});
|
|
346
|
+
else feedback.push({ text: `X Description length (${count}) looks good.`, color: "green" });
|
|
347
|
+
if (isParentseoField) {
|
|
348
|
+
if (keywords.length > 0) {
|
|
349
|
+
const hasKeyword = hasMatchingKeyword(desc, keywords);
|
|
350
|
+
feedback.push({
|
|
351
|
+
text: hasKeyword ? "Keyword found in X description \u2014 good job!" : "Keywords defined but missing in X description.",
|
|
352
|
+
color: hasKeyword ? "green" : "red"
|
|
353
|
+
});
|
|
354
|
+
} else {
|
|
355
|
+
feedback.push({
|
|
356
|
+
text: "No keywords defined. Consider adding relevant keywords.",
|
|
357
|
+
color: "orange"
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
if (/[!@#$%^&*]{2,}/.test(desc))
|
|
362
|
+
feedback.push({
|
|
363
|
+
text: "X Description has excessive punctuation \u2014 simplify it.",
|
|
364
|
+
color: "orange"
|
|
365
|
+
});
|
|
366
|
+
return feedback;
|
|
367
|
+
};
|
|
368
|
+
isSubImageSet = (subObj) => {
|
|
369
|
+
var _a;
|
|
370
|
+
if (!subObj) return false;
|
|
371
|
+
if (subObj.imageType === "url") return !!((_a = subObj.imageUrl) == null ? void 0 : _a.trim());
|
|
372
|
+
const img = subObj.image;
|
|
373
|
+
return !!(img == null ? void 0 : img.asset);
|
|
374
|
+
};
|
|
375
|
+
isMetaImageSet = (seoParent) => {
|
|
376
|
+
if (!seoParent) return false;
|
|
377
|
+
const metaImage = seoParent.metaImage;
|
|
378
|
+
return !!(metaImage == null ? void 0 : metaImage.asset);
|
|
379
|
+
};
|
|
380
|
+
getMetaImageValidation = (hasImage, seoParent) => {
|
|
381
|
+
const feedback = [];
|
|
382
|
+
if (!hasImage) {
|
|
383
|
+
feedback.push({
|
|
384
|
+
text: "No meta image provided. Adding an image improves click-through rates.",
|
|
385
|
+
color: "red"
|
|
386
|
+
});
|
|
387
|
+
return feedback;
|
|
388
|
+
}
|
|
389
|
+
feedback.push({ text: "Meta image is set \u2014 great for SEO and social sharing.", color: "green" });
|
|
390
|
+
const ogSet = isSubImageSet(seoParent == null ? void 0 : seoParent.openGraph);
|
|
391
|
+
const twSet = isSubImageSet(seoParent == null ? void 0 : seoParent.twitter);
|
|
392
|
+
if (!ogSet && !twSet) {
|
|
393
|
+
feedback.push({
|
|
394
|
+
text: "OG and Twitter images are missing \u2014 add them for full social coverage.",
|
|
395
|
+
color: "orange"
|
|
396
|
+
});
|
|
397
|
+
} else if (!ogSet) {
|
|
398
|
+
feedback.push({
|
|
399
|
+
text: "OG image is missing \u2014 add it for better Facebook/LinkedIn previews.",
|
|
400
|
+
color: "orange"
|
|
401
|
+
});
|
|
402
|
+
} else if (twSet) {
|
|
403
|
+
feedback.push({ text: "All images set (Meta, OG, Twitter) \u2014 full coverage!", color: "green" });
|
|
404
|
+
} else {
|
|
405
|
+
feedback.push({
|
|
406
|
+
text: "Twitter image is missing \u2014 add it for better X (Twitter) cards.",
|
|
407
|
+
color: "orange"
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
return feedback;
|
|
411
|
+
};
|
|
412
|
+
getOgImageValidation = (hasImage, altText, seoParent) => {
|
|
413
|
+
const feedback = [];
|
|
414
|
+
if (!hasImage) {
|
|
415
|
+
feedback.push({
|
|
416
|
+
text: "No OG image provided. Social shares will lack a visual preview.",
|
|
417
|
+
color: "red"
|
|
418
|
+
});
|
|
419
|
+
return feedback;
|
|
420
|
+
}
|
|
421
|
+
feedback.push({ text: "OG image is set \u2014 good for social sharing.", color: "green" });
|
|
422
|
+
if (altText == null ? void 0 : altText.trim()) {
|
|
423
|
+
feedback.push({ text: "Alt text is set \u2014 good for accessibility.", color: "green" });
|
|
424
|
+
} else {
|
|
425
|
+
feedback.push({ text: "Consider adding alt text for better accessibility.", color: "orange" });
|
|
426
|
+
}
|
|
427
|
+
const metaSet = isMetaImageSet(seoParent);
|
|
428
|
+
const twSet = isSubImageSet(seoParent == null ? void 0 : seoParent.twitter);
|
|
429
|
+
if (metaSet && twSet) {
|
|
430
|
+
feedback.push({ text: "All images set (Meta, OG, Twitter) \u2014 full coverage!", color: "green" });
|
|
431
|
+
} else {
|
|
432
|
+
if (!metaSet)
|
|
433
|
+
feedback.push({
|
|
434
|
+
text: "Meta image is missing \u2014 add it for search engine results.",
|
|
435
|
+
color: "orange"
|
|
436
|
+
});
|
|
437
|
+
if (!twSet)
|
|
438
|
+
feedback.push({
|
|
439
|
+
text: "Twitter image is missing \u2014 add it for X (Twitter) cards.",
|
|
440
|
+
color: "orange"
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
return feedback;
|
|
444
|
+
};
|
|
445
|
+
getOgImageUrlValidation = (imageUrl, seoParent) => {
|
|
446
|
+
const feedback = [];
|
|
447
|
+
if (!(imageUrl == null ? void 0 : imageUrl.trim())) {
|
|
448
|
+
feedback.push({
|
|
449
|
+
text: "No OG image URL provided. Social shares will lack a visual preview.",
|
|
450
|
+
color: "red"
|
|
451
|
+
});
|
|
452
|
+
return feedback;
|
|
453
|
+
}
|
|
454
|
+
feedback.push({ text: "OG image URL is set \u2014 good for social sharing.", color: "green" });
|
|
455
|
+
const metaSet = isMetaImageSet(seoParent);
|
|
456
|
+
const twSet = isSubImageSet(seoParent == null ? void 0 : seoParent.twitter);
|
|
457
|
+
if (metaSet && twSet) {
|
|
458
|
+
feedback.push({ text: "All images set (Meta, OG, Twitter) \u2014 full coverage!", color: "green" });
|
|
459
|
+
} else {
|
|
460
|
+
if (!metaSet)
|
|
461
|
+
feedback.push({
|
|
462
|
+
text: "Meta image is missing \u2014 add it for search engine results.",
|
|
463
|
+
color: "orange"
|
|
464
|
+
});
|
|
465
|
+
if (!twSet)
|
|
466
|
+
feedback.push({
|
|
467
|
+
text: "Twitter image is missing \u2014 add it for X (Twitter) cards.",
|
|
468
|
+
color: "orange"
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
return feedback;
|
|
472
|
+
};
|
|
473
|
+
getTwitterImageValidation = (hasImage, altText, seoParent) => {
|
|
474
|
+
const feedback = [];
|
|
475
|
+
if (!hasImage) {
|
|
476
|
+
feedback.push({
|
|
477
|
+
text: "No Twitter image provided. Posts on X will lack a visual.",
|
|
478
|
+
color: "red"
|
|
479
|
+
});
|
|
480
|
+
return feedback;
|
|
481
|
+
}
|
|
482
|
+
feedback.push({ text: "Twitter image is set \u2014 good for X sharing.", color: "green" });
|
|
483
|
+
if (altText == null ? void 0 : altText.trim()) {
|
|
484
|
+
feedback.push({ text: "Alt text is set \u2014 good for accessibility.", color: "green" });
|
|
485
|
+
} else {
|
|
486
|
+
feedback.push({ text: "Consider adding alt text for better accessibility.", color: "orange" });
|
|
487
|
+
}
|
|
488
|
+
const metaSet = isMetaImageSet(seoParent);
|
|
489
|
+
const ogSet = isSubImageSet(seoParent == null ? void 0 : seoParent.openGraph);
|
|
490
|
+
if (metaSet && ogSet) {
|
|
491
|
+
feedback.push({ text: "All images set (Meta, OG, Twitter) \u2014 full coverage!", color: "green" });
|
|
492
|
+
} else {
|
|
493
|
+
if (!metaSet)
|
|
494
|
+
feedback.push({
|
|
495
|
+
text: "Meta image is missing \u2014 add it for search engine results.",
|
|
496
|
+
color: "orange"
|
|
497
|
+
});
|
|
498
|
+
if (!ogSet)
|
|
499
|
+
feedback.push({
|
|
500
|
+
text: "OG image is missing \u2014 add it for Facebook/LinkedIn sharing.",
|
|
501
|
+
color: "orange"
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
return feedback;
|
|
505
|
+
};
|
|
506
|
+
getTwitterImageUrlValidation = (imageUrl, seoParent) => {
|
|
507
|
+
const feedback = [];
|
|
508
|
+
if (!(imageUrl == null ? void 0 : imageUrl.trim())) {
|
|
509
|
+
feedback.push({
|
|
510
|
+
text: "No Twitter image URL provided. Posts on X will lack a visual.",
|
|
511
|
+
color: "red"
|
|
512
|
+
});
|
|
513
|
+
return feedback;
|
|
514
|
+
}
|
|
515
|
+
feedback.push({ text: "Twitter image URL is set \u2014 good for X sharing.", color: "green" });
|
|
516
|
+
const metaSet = isMetaImageSet(seoParent);
|
|
517
|
+
const ogSet = isSubImageSet(seoParent == null ? void 0 : seoParent.openGraph);
|
|
518
|
+
if (metaSet && ogSet) {
|
|
519
|
+
feedback.push({ text: "All images set (Meta, OG, Twitter) \u2014 full coverage!", color: "green" });
|
|
520
|
+
} else {
|
|
521
|
+
if (!metaSet)
|
|
522
|
+
feedback.push({
|
|
523
|
+
text: "Meta image is missing \u2014 add it for search engine results.",
|
|
524
|
+
color: "orange"
|
|
525
|
+
});
|
|
526
|
+
if (!ogSet)
|
|
527
|
+
feedback.push({
|
|
528
|
+
text: "OG image is missing \u2014 add it for Facebook/LinkedIn sharing.",
|
|
529
|
+
color: "orange"
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
return feedback;
|
|
533
|
+
};
|
|
534
|
+
}
|
|
73
535
|
});
|
|
74
|
-
module.exports = __toCommonJS(src_exports);
|
|
75
536
|
|
|
76
|
-
// src/
|
|
77
|
-
var
|
|
78
|
-
|
|
537
|
+
// src/components/SeoPreview.tsx
|
|
538
|
+
var SeoPreview_exports = {};
|
|
539
|
+
__export(SeoPreview_exports, {
|
|
540
|
+
default: () => SeoPreview_default
|
|
541
|
+
});
|
|
542
|
+
var import_ui12, import_sanity14, import_styled_components, import_jsx_runtime12, PreviewContainer, PreviewHeader, PreviewBody, SerpUrl, SerpTitle, SerpDescription, LiveIndicator, SeoPreview, SeoPreview_default;
|
|
543
|
+
var init_SeoPreview = __esm({
|
|
544
|
+
"src/components/SeoPreview.tsx"() {
|
|
545
|
+
"use strict";
|
|
546
|
+
import_ui12 = require("@sanity/ui");
|
|
547
|
+
import_sanity14 = require("sanity");
|
|
548
|
+
import_styled_components = __toESM(require("styled-components"), 1);
|
|
549
|
+
init_seoUtils();
|
|
550
|
+
import_jsx_runtime12 = require("react/jsx-runtime");
|
|
551
|
+
PreviewContainer = import_styled_components.default.div`
|
|
552
|
+
max-width: 600px;
|
|
553
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
554
|
+
background: #ffffff;
|
|
555
|
+
border: 1px solid #dadce0;
|
|
556
|
+
border-radius: 8px;
|
|
557
|
+
overflow: hidden;
|
|
558
|
+
`;
|
|
559
|
+
PreviewHeader = import_styled_components.default.div`
|
|
560
|
+
background: #f8f9fa;
|
|
561
|
+
padding: 12px 16px;
|
|
562
|
+
border-bottom: 1px solid #dadce0;
|
|
563
|
+
display: flex;
|
|
564
|
+
align-items: center;
|
|
565
|
+
justify-content: space-between;
|
|
566
|
+
gap: 8px;
|
|
567
|
+
`;
|
|
568
|
+
PreviewBody = import_styled_components.default.div`
|
|
569
|
+
padding: 16px;
|
|
570
|
+
`;
|
|
571
|
+
SerpUrl = import_styled_components.default.p`
|
|
572
|
+
margin: 0 0 4px;
|
|
573
|
+
color: #006621;
|
|
574
|
+
font-size: 13px;
|
|
575
|
+
line-height: 1.4;
|
|
576
|
+
word-break: break-word;
|
|
577
|
+
`;
|
|
578
|
+
SerpTitle = import_styled_components.default.h3`
|
|
579
|
+
margin: 0 0 8px;
|
|
580
|
+
color: #1a0dab;
|
|
581
|
+
font-size: 18px;
|
|
582
|
+
font-weight: 500;
|
|
583
|
+
line-height: 1.4;
|
|
584
|
+
word-break: break-word;
|
|
585
|
+
|
|
586
|
+
&:hover {
|
|
587
|
+
text-decoration: underline;
|
|
588
|
+
}
|
|
589
|
+
`;
|
|
590
|
+
SerpDescription = import_styled_components.default.p`
|
|
591
|
+
margin: 0;
|
|
592
|
+
color: #545454;
|
|
593
|
+
font-size: 14px;
|
|
594
|
+
line-height: 1.6;
|
|
595
|
+
word-break: break-word;
|
|
596
|
+
display: -webkit-box;
|
|
597
|
+
-webkit-line-clamp: 2;
|
|
598
|
+
-webkit-box-orient: vertical;
|
|
599
|
+
overflow: hidden;
|
|
600
|
+
`;
|
|
601
|
+
LiveIndicator = import_styled_components.default.span`
|
|
602
|
+
display: inline-flex;
|
|
603
|
+
align-items: center;
|
|
604
|
+
gap: 4px;
|
|
605
|
+
font-size: 11px;
|
|
606
|
+
font-weight: 600;
|
|
607
|
+
text-transform: uppercase;
|
|
608
|
+
letter-spacing: 0.05em;
|
|
609
|
+
color: #4f46e5;
|
|
610
|
+
background: #f0f4ff;
|
|
611
|
+
padding: 4px 8px;
|
|
612
|
+
border-radius: 4px;
|
|
613
|
+
`;
|
|
614
|
+
SeoPreview = (props) => {
|
|
615
|
+
var _a, _b;
|
|
616
|
+
const { path, schemaType } = props;
|
|
617
|
+
const { options } = schemaType;
|
|
618
|
+
const baseUrl = (options == null ? void 0 : options.baseUrl) || "https://www.example.com";
|
|
619
|
+
const prefixFunction = options == null ? void 0 : options.prefix;
|
|
620
|
+
const parent = (0, import_sanity14.useFormValue)([path[0]]) || {
|
|
621
|
+
title: "",
|
|
622
|
+
description: "",
|
|
623
|
+
canonicalUrl: ""
|
|
624
|
+
};
|
|
625
|
+
const rootDoc = (0, import_sanity14.useFormValue)([]) || {
|
|
626
|
+
slug: { current: "" }
|
|
627
|
+
};
|
|
628
|
+
const slug = ((_a = rootDoc == null ? void 0 : rootDoc.slug) == null ? void 0 : _a.current) || "";
|
|
629
|
+
const {
|
|
630
|
+
title,
|
|
631
|
+
description,
|
|
632
|
+
canonicalUrl: url
|
|
633
|
+
} = parent;
|
|
634
|
+
const base = (_b = url || baseUrl) == null ? void 0 : _b.replace(/\/+$/, "");
|
|
635
|
+
const slugStr = String(slug || "").replace(/^\/+/, "");
|
|
636
|
+
const pref = String(
|
|
637
|
+
prefixFunction ? prefixFunction(rootDoc) : ""
|
|
638
|
+
).replace(/^\/+|\/+$/g, "");
|
|
639
|
+
const urlPath = [pref, slugStr].filter(Boolean).join("/");
|
|
640
|
+
const finalUrl = urlPath ? `${base}/${urlPath}` : base;
|
|
641
|
+
const domain = (() => {
|
|
642
|
+
try {
|
|
643
|
+
const u = new URL(finalUrl || base);
|
|
644
|
+
return u.hostname;
|
|
645
|
+
} catch (e) {
|
|
646
|
+
return "example.com";
|
|
647
|
+
}
|
|
648
|
+
})();
|
|
649
|
+
const urlDisplay = `${domain}${urlPath ? ` \u203A ${urlPath.split("/").slice(-1)[0]}` : ""}`;
|
|
650
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ui12.Box, { padding: 3, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(PreviewContainer, { children: [
|
|
651
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(PreviewHeader, { children: [
|
|
652
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
653
|
+
"span",
|
|
654
|
+
{
|
|
655
|
+
style: {
|
|
656
|
+
fontSize: "11px",
|
|
657
|
+
color: "#5f6368",
|
|
658
|
+
textTransform: "uppercase",
|
|
659
|
+
letterSpacing: "0.05em"
|
|
660
|
+
},
|
|
661
|
+
children: "Search Preview"
|
|
662
|
+
}
|
|
663
|
+
),
|
|
664
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(LiveIndicator, { children: [
|
|
665
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
666
|
+
"span",
|
|
667
|
+
{
|
|
668
|
+
style: {
|
|
669
|
+
width: "4px",
|
|
670
|
+
height: "4px",
|
|
671
|
+
borderRadius: "50%",
|
|
672
|
+
backgroundColor: "#4f46e5",
|
|
673
|
+
display: "inline-block"
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
),
|
|
677
|
+
"Live"
|
|
678
|
+
] })
|
|
679
|
+
] }),
|
|
680
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(PreviewBody, { children: [
|
|
681
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SerpUrl, { children: finalUrl ? urlDisplay : "example.com \u203A page-url" }),
|
|
682
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SerpTitle, { children: title && title.length > 0 ? truncate(title, 60) : "Your SEO Title will appear here" }),
|
|
683
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SerpDescription, { children: description && description.length > 0 ? truncate(description, 160) : "Your meta description will show up here. Make it compelling!" })
|
|
684
|
+
] })
|
|
685
|
+
] }) });
|
|
686
|
+
};
|
|
687
|
+
SeoPreview_default = SeoPreview;
|
|
688
|
+
}
|
|
689
|
+
});
|
|
79
690
|
|
|
80
691
|
// src/components/SeoHealthDashboard.tsx
|
|
81
|
-
var
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
var
|
|
86
|
-
var
|
|
87
|
-
|
|
692
|
+
var SeoHealthDashboard_exports = {};
|
|
693
|
+
__export(SeoHealthDashboard_exports, {
|
|
694
|
+
default: () => SeoHealthDashboard_default
|
|
695
|
+
});
|
|
696
|
+
var import_react13, import_sanity19, import_router, import_structure, import_styled_components2, import_jsx_runtime13, DashboardContainer, PageHeader, PageTitle, PreviewBadge, PageSubtitle, StatsGrid, StatCard, StatLabel, StatValue, ControlsBar, SearchWrapper, SearchIconSvg, SearchInput, StyledSelect, TableCard, TableHeader, TableRow, ColTitle, TitleWrapper, TitleCell, ColType, ColScore, ColIssues, DocTitleLink, DocId, TypeBadge, TypeText, CustomBadge, ScoreBadge, IssueTag, NonStringTitleWarning, MoreIssues, MoreIssuesWrapper, IssuesPopover, PopoverIssueItem, UpgradeContainer, UpgradeBox, UpgradeLock, UpgradeTitle, UpgradeText, UpgradeCode, UpgradeButton, ReloadButton, spin, DashboardRefreshButton, DocTitleAnchor, PaneLinkWrapper, DocTitleAnchorPane, DocBadgeRenderer, Spinner, LoadingState, EmptyState, DeprecationBanner, DeprecationBannerLink, TYPE_COLOR_PALETTE, getTypeColor, getStatusCategory, scoreMetaTitle, scoreMetaDescription, scoreOpenGraph, scoreTwitterCard, calculateHealthScore, resolveTypeLabel, buildTitleProjection, generateDummyData, SeoHealthDashboard, SeoHealthDashboard_default;
|
|
697
|
+
var init_SeoHealthDashboard = __esm({
|
|
698
|
+
"src/components/SeoHealthDashboard.tsx"() {
|
|
699
|
+
"use strict";
|
|
700
|
+
import_react13 = require("react");
|
|
701
|
+
import_sanity19 = require("sanity");
|
|
702
|
+
import_router = require("sanity/router");
|
|
703
|
+
import_structure = require("sanity/structure");
|
|
704
|
+
import_styled_components2 = __toESM(require("styled-components"), 1);
|
|
705
|
+
import_jsx_runtime13 = require("react/jsx-runtime");
|
|
706
|
+
DashboardContainer = import_styled_components2.default.div`
|
|
88
707
|
width: 100%;
|
|
89
708
|
min-height: 100%;
|
|
90
709
|
background: #f0f2f5;
|
|
@@ -92,10 +711,14 @@ var DashboardContainer = import_styled_components.default.div`
|
|
|
92
711
|
box-sizing: border-box;
|
|
93
712
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
94
713
|
`;
|
|
95
|
-
|
|
714
|
+
PageHeader = import_styled_components2.default.div`
|
|
715
|
+
display: flex;
|
|
716
|
+
align-items: flex-start;
|
|
717
|
+
justify-content: space-between;
|
|
718
|
+
gap: 12px;
|
|
96
719
|
margin-bottom: 28px;
|
|
97
720
|
`;
|
|
98
|
-
|
|
721
|
+
PageTitle = import_styled_components2.default.h1`
|
|
99
722
|
margin: 0 0 6px 0;
|
|
100
723
|
font-size: 22px;
|
|
101
724
|
font-weight: 700;
|
|
@@ -105,7 +728,7 @@ var PageTitle = import_styled_components.default.h1`
|
|
|
105
728
|
align-items: center;
|
|
106
729
|
gap: 10px;
|
|
107
730
|
`;
|
|
108
|
-
|
|
731
|
+
PreviewBadge = import_styled_components2.default.span`
|
|
109
732
|
display: inline-block;
|
|
110
733
|
background: #fef3c7;
|
|
111
734
|
color: #92400e;
|
|
@@ -117,18 +740,18 @@ var PreviewBadge = import_styled_components.default.span`
|
|
|
117
740
|
letter-spacing: 0.5px;
|
|
118
741
|
margin-left: 8px;
|
|
119
742
|
`;
|
|
120
|
-
|
|
743
|
+
PageSubtitle = import_styled_components2.default.p`
|
|
121
744
|
margin: 0;
|
|
122
745
|
font-size: 13px;
|
|
123
746
|
color: #6b7280;
|
|
124
747
|
`;
|
|
125
|
-
|
|
748
|
+
StatsGrid = import_styled_components2.default.div`
|
|
126
749
|
display: grid;
|
|
127
750
|
grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
|
|
128
751
|
gap: 14px;
|
|
129
752
|
margin-bottom: 20px;
|
|
130
753
|
`;
|
|
131
|
-
|
|
754
|
+
StatCard = import_styled_components2.default.div`
|
|
132
755
|
background: #ffffff;
|
|
133
756
|
border-radius: 10px;
|
|
134
757
|
padding: 16px 18px;
|
|
@@ -142,7 +765,7 @@ var StatCard = import_styled_components.default.div`
|
|
|
142
765
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
143
766
|
}
|
|
144
767
|
`;
|
|
145
|
-
|
|
768
|
+
StatLabel = import_styled_components2.default.div`
|
|
146
769
|
font-size: 11px;
|
|
147
770
|
font-weight: 500;
|
|
148
771
|
color: #9ca3af;
|
|
@@ -150,13 +773,13 @@ var StatLabel = import_styled_components.default.div`
|
|
|
150
773
|
letter-spacing: 0.5px;
|
|
151
774
|
margin-bottom: 8px;
|
|
152
775
|
`;
|
|
153
|
-
|
|
776
|
+
StatValue = import_styled_components2.default.div`
|
|
154
777
|
font-size: 26px;
|
|
155
778
|
font-weight: 700;
|
|
156
779
|
color: #111827;
|
|
157
780
|
line-height: 1;
|
|
158
781
|
`;
|
|
159
|
-
|
|
782
|
+
ControlsBar = import_styled_components2.default.div`
|
|
160
783
|
background: #ffffff;
|
|
161
784
|
border-radius: 10px;
|
|
162
785
|
padding: 14px 18px;
|
|
@@ -167,12 +790,12 @@ var ControlsBar = import_styled_components.default.div`
|
|
|
167
790
|
margin-bottom: 20px;
|
|
168
791
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.07);
|
|
169
792
|
`;
|
|
170
|
-
|
|
793
|
+
SearchWrapper = import_styled_components2.default.div`
|
|
171
794
|
position: relative;
|
|
172
795
|
flex: 1;
|
|
173
796
|
min-width: 220px;
|
|
174
797
|
`;
|
|
175
|
-
|
|
798
|
+
SearchIconSvg = import_styled_components2.default.span`
|
|
176
799
|
position: absolute;
|
|
177
800
|
left: 11px;
|
|
178
801
|
top: 50%;
|
|
@@ -182,7 +805,7 @@ var SearchIconSvg = import_styled_components.default.span`
|
|
|
182
805
|
align-items: center;
|
|
183
806
|
pointer-events: none;
|
|
184
807
|
`;
|
|
185
|
-
|
|
808
|
+
SearchInput = import_styled_components2.default.input`
|
|
186
809
|
width: 100%;
|
|
187
810
|
height: 36px;
|
|
188
811
|
padding: 0 12px 0 34px;
|
|
@@ -207,7 +830,7 @@ var SearchInput = import_styled_components.default.input`
|
|
|
207
830
|
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
|
|
208
831
|
}
|
|
209
832
|
`;
|
|
210
|
-
|
|
833
|
+
StyledSelect = import_styled_components2.default.select`
|
|
211
834
|
height: 36px;
|
|
212
835
|
padding: 0 32px 0 12px;
|
|
213
836
|
border: 1px solid #e5e7eb;
|
|
@@ -228,13 +851,13 @@ var StyledSelect = import_styled_components.default.select`
|
|
|
228
851
|
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
|
|
229
852
|
}
|
|
230
853
|
`;
|
|
231
|
-
|
|
854
|
+
TableCard = import_styled_components2.default.div`
|
|
232
855
|
background: #ffffff;
|
|
233
856
|
border-radius: 10px;
|
|
234
857
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.07);
|
|
235
858
|
overflow: hidden;
|
|
236
859
|
`;
|
|
237
|
-
|
|
860
|
+
TableHeader = import_styled_components2.default.div`
|
|
238
861
|
display: flex;
|
|
239
862
|
align-items: center;
|
|
240
863
|
padding: 11px 20px;
|
|
@@ -247,7 +870,7 @@ var TableHeader = import_styled_components.default.div`
|
|
|
247
870
|
letter-spacing: 0.5px;
|
|
248
871
|
gap: 12px;
|
|
249
872
|
`;
|
|
250
|
-
|
|
873
|
+
TableRow = import_styled_components2.default.div`
|
|
251
874
|
display: flex;
|
|
252
875
|
align-items: center;
|
|
253
876
|
padding: 13px 20px;
|
|
@@ -263,35 +886,35 @@ var TableRow = import_styled_components.default.div`
|
|
|
263
886
|
background: #fafafa;
|
|
264
887
|
}
|
|
265
888
|
`;
|
|
266
|
-
|
|
889
|
+
ColTitle = import_styled_components2.default.div`
|
|
267
890
|
flex: 2;
|
|
268
891
|
min-width: 0;
|
|
269
892
|
`;
|
|
270
|
-
|
|
893
|
+
TitleWrapper = import_styled_components2.default.div`
|
|
271
894
|
display: flex;
|
|
272
895
|
align-items: center;
|
|
273
896
|
gap: 4px;
|
|
274
897
|
flex-wrap: wrap;
|
|
275
898
|
min-width: 0;
|
|
276
899
|
`;
|
|
277
|
-
|
|
900
|
+
TitleCell = import_styled_components2.default.div`
|
|
278
901
|
min-width: 0;
|
|
279
902
|
overflow: hidden;
|
|
280
903
|
flex: 1;
|
|
281
904
|
`;
|
|
282
|
-
|
|
905
|
+
ColType = import_styled_components2.default.div`
|
|
283
906
|
flex: 0.8;
|
|
284
907
|
min-width: 80px;
|
|
285
908
|
`;
|
|
286
|
-
|
|
909
|
+
ColScore = import_styled_components2.default.div`
|
|
287
910
|
flex: 0.6;
|
|
288
911
|
min-width: 70px;
|
|
289
912
|
`;
|
|
290
|
-
|
|
913
|
+
ColIssues = import_styled_components2.default.div`
|
|
291
914
|
flex: 2;
|
|
292
915
|
min-width: 0;
|
|
293
916
|
`;
|
|
294
|
-
|
|
917
|
+
DocTitleLink = import_styled_components2.default.a`
|
|
295
918
|
font-size: 13px;
|
|
296
919
|
font-weight: 600;
|
|
297
920
|
color: #4f46e5;
|
|
@@ -307,7 +930,7 @@ var DocTitleLink = import_styled_components.default.a`
|
|
|
307
930
|
text-decoration: underline;
|
|
308
931
|
}
|
|
309
932
|
`;
|
|
310
|
-
|
|
933
|
+
DocId = import_styled_components2.default.div`
|
|
311
934
|
font-size: 11px;
|
|
312
935
|
color: #9ca3af;
|
|
313
936
|
margin-top: 2px;
|
|
@@ -315,7 +938,7 @@ var DocId = import_styled_components.default.div`
|
|
|
315
938
|
overflow: hidden;
|
|
316
939
|
text-overflow: ellipsis;
|
|
317
940
|
`;
|
|
318
|
-
|
|
941
|
+
TypeBadge = import_styled_components2.default.span`
|
|
319
942
|
display: inline-block;
|
|
320
943
|
padding: 3px 8px;
|
|
321
944
|
border-radius: 5px;
|
|
@@ -324,12 +947,12 @@ var TypeBadge = import_styled_components.default.span`
|
|
|
324
947
|
background: ${(p) => p.$bgColor || "#ede9fe"};
|
|
325
948
|
color: ${(p) => p.$textColor || "#5b21b6"};
|
|
326
949
|
`;
|
|
327
|
-
|
|
950
|
+
TypeText = import_styled_components2.default.span`
|
|
328
951
|
font-size: 12px;
|
|
329
952
|
font-weight: 500;
|
|
330
953
|
color: #374151;
|
|
331
954
|
`;
|
|
332
|
-
|
|
955
|
+
CustomBadge = import_styled_components2.default.span`
|
|
333
956
|
display: inline-block;
|
|
334
957
|
padding: 2px 6px;
|
|
335
958
|
border-radius: 4px;
|
|
@@ -339,26 +962,26 @@ var CustomBadge = import_styled_components.default.span`
|
|
|
339
962
|
color: ${(p) => p.$textColor || "#3730a3"};
|
|
340
963
|
white-space: nowrap;
|
|
341
964
|
`;
|
|
342
|
-
|
|
965
|
+
ScoreBadge = import_styled_components2.default.span`
|
|
343
966
|
display: inline-block;
|
|
344
967
|
padding: 4px 10px;
|
|
345
968
|
border-radius: 6px;
|
|
346
969
|
font-size: 12px;
|
|
347
970
|
font-weight: 700;
|
|
348
971
|
background: ${(p) => {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
}};
|
|
972
|
+
if (p.$score >= 80) return "#d1fae5";
|
|
973
|
+
if (p.$score >= 60) return "#fef3c7";
|
|
974
|
+
if (p.$score >= 40) return "#ffedd5";
|
|
975
|
+
return "#fee2e2";
|
|
976
|
+
}};
|
|
354
977
|
color: ${(p) => {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}};
|
|
978
|
+
if (p.$score >= 80) return "#065f46";
|
|
979
|
+
if (p.$score >= 60) return "#92400e";
|
|
980
|
+
if (p.$score >= 40) return "#9a3412";
|
|
981
|
+
return "#991b1b";
|
|
982
|
+
}};
|
|
360
983
|
`;
|
|
361
|
-
|
|
984
|
+
IssueTag = import_styled_components2.default.div`
|
|
362
985
|
font-size: 11px;
|
|
363
986
|
color: #ef4444;
|
|
364
987
|
line-height: 1.5;
|
|
@@ -366,7 +989,23 @@ var IssueTag = import_styled_components.default.div`
|
|
|
366
989
|
overflow: hidden;
|
|
367
990
|
text-overflow: ellipsis;
|
|
368
991
|
`;
|
|
369
|
-
|
|
992
|
+
NonStringTitleWarning = import_styled_components2.default.div`
|
|
993
|
+
display: inline-flex;
|
|
994
|
+
align-items: center;
|
|
995
|
+
gap: 4px;
|
|
996
|
+
margin-top: 4px;
|
|
997
|
+
padding: 2px 7px;
|
|
998
|
+
border-radius: 4px;
|
|
999
|
+
background: #fef3c7;
|
|
1000
|
+
border: 1px solid #fcd34d;
|
|
1001
|
+
font-size: 10px;
|
|
1002
|
+
font-weight: 600;
|
|
1003
|
+
color: #92400e;
|
|
1004
|
+
line-height: 1.4;
|
|
1005
|
+
cursor: default;
|
|
1006
|
+
white-space: normal;
|
|
1007
|
+
`;
|
|
1008
|
+
MoreIssues = import_styled_components2.default.div`
|
|
370
1009
|
font-size: 11px;
|
|
371
1010
|
color: #6b7280;
|
|
372
1011
|
cursor: pointer;
|
|
@@ -376,11 +1015,11 @@ var MoreIssues = import_styled_components.default.div`
|
|
|
376
1015
|
color: #374151;
|
|
377
1016
|
}
|
|
378
1017
|
`;
|
|
379
|
-
|
|
1018
|
+
MoreIssuesWrapper = import_styled_components2.default.div`
|
|
380
1019
|
position: relative;
|
|
381
1020
|
display: inline-block;
|
|
382
1021
|
`;
|
|
383
|
-
|
|
1022
|
+
IssuesPopover = import_styled_components2.default.div`
|
|
384
1023
|
position: absolute;
|
|
385
1024
|
bottom: auto;
|
|
386
1025
|
left: 0;
|
|
@@ -408,7 +1047,7 @@ var IssuesPopover = import_styled_components.default.div`
|
|
|
408
1047
|
border-top: 6px solid #1f2937;
|
|
409
1048
|
}
|
|
410
1049
|
`;
|
|
411
|
-
|
|
1050
|
+
PopoverIssueItem = import_styled_components2.default.div`
|
|
412
1051
|
display: flex;
|
|
413
1052
|
gap: 6px;
|
|
414
1053
|
margin-bottom: 6px;
|
|
@@ -417,14 +1056,14 @@ var PopoverIssueItem = import_styled_components.default.div`
|
|
|
417
1056
|
margin-bottom: 0;
|
|
418
1057
|
}
|
|
419
1058
|
`;
|
|
420
|
-
|
|
1059
|
+
UpgradeContainer = import_styled_components2.default.div`
|
|
421
1060
|
display: flex;
|
|
422
1061
|
align-items: center;
|
|
423
1062
|
justify-content: center;
|
|
424
1063
|
min-height: 100%;
|
|
425
1064
|
padding: 60px 24px;
|
|
426
1065
|
`;
|
|
427
|
-
|
|
1066
|
+
UpgradeBox = import_styled_components2.default.div`
|
|
428
1067
|
background: #ffffff;
|
|
429
1068
|
border-radius: 16px;
|
|
430
1069
|
padding: 48px 40px;
|
|
@@ -436,23 +1075,23 @@ var UpgradeBox = import_styled_components.default.div`
|
|
|
436
1075
|
0 1px 4px rgba(0, 0, 0, 0.05);
|
|
437
1076
|
border: 1px solid #e5e7eb;
|
|
438
1077
|
`;
|
|
439
|
-
|
|
1078
|
+
UpgradeLock = import_styled_components2.default.div`
|
|
440
1079
|
font-size: 40px;
|
|
441
1080
|
margin-bottom: 16px;
|
|
442
1081
|
`;
|
|
443
|
-
|
|
1082
|
+
UpgradeTitle = import_styled_components2.default.h2`
|
|
444
1083
|
margin: 0 0 10px;
|
|
445
1084
|
font-size: 20px;
|
|
446
1085
|
font-weight: 700;
|
|
447
1086
|
color: #111827;
|
|
448
1087
|
`;
|
|
449
|
-
|
|
1088
|
+
UpgradeText = import_styled_components2.default.p`
|
|
450
1089
|
margin: 0 0 20px;
|
|
451
1090
|
font-size: 14px;
|
|
452
1091
|
color: #6b7280;
|
|
453
1092
|
line-height: 1.6;
|
|
454
1093
|
`;
|
|
455
|
-
|
|
1094
|
+
UpgradeCode = import_styled_components2.default.pre`
|
|
456
1095
|
background: #f3f4f6;
|
|
457
1096
|
border-radius: 8px;
|
|
458
1097
|
padding: 14px 16px;
|
|
@@ -464,7 +1103,7 @@ var UpgradeCode = import_styled_components.default.pre`
|
|
|
464
1103
|
line-height: 1.6;
|
|
465
1104
|
border: 1px solid #e5e7eb;
|
|
466
1105
|
`;
|
|
467
|
-
|
|
1106
|
+
UpgradeButton = import_styled_components2.default.a`
|
|
468
1107
|
display: inline-block;
|
|
469
1108
|
background: #4f46e5;
|
|
470
1109
|
color: #ffffff;
|
|
@@ -479,7 +1118,7 @@ var UpgradeButton = import_styled_components.default.a`
|
|
|
479
1118
|
background: #4338ca;
|
|
480
1119
|
}
|
|
481
1120
|
`;
|
|
482
|
-
|
|
1121
|
+
ReloadButton = import_styled_components2.default.button`
|
|
483
1122
|
display: inline-block;
|
|
484
1123
|
background: transparent;
|
|
485
1124
|
color: #6b7280;
|
|
@@ -501,55 +1140,92 @@ var ReloadButton = import_styled_components.default.button`
|
|
|
501
1140
|
border-color: #9ca3af;
|
|
502
1141
|
}
|
|
503
1142
|
`;
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
color
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
1143
|
+
spin = import_styled_components2.keyframes`
|
|
1144
|
+
to { transform: rotate(360deg); }
|
|
1145
|
+
`;
|
|
1146
|
+
DashboardRefreshButton = import_styled_components2.default.button`
|
|
1147
|
+
display: inline-flex;
|
|
1148
|
+
align-items: center;
|
|
1149
|
+
gap: 6px;
|
|
1150
|
+
background: #ffffff;
|
|
1151
|
+
color: #374151;
|
|
1152
|
+
font-size: 13px;
|
|
1153
|
+
font-weight: 500;
|
|
1154
|
+
padding: 8px 14px;
|
|
1155
|
+
border-radius: 8px;
|
|
1156
|
+
border: 1px solid #d1d5db;
|
|
1157
|
+
cursor: pointer;
|
|
1158
|
+
flex-shrink: 0;
|
|
1159
|
+
transition:
|
|
1160
|
+
background 0.15s,
|
|
1161
|
+
color 0.15s,
|
|
1162
|
+
border-color 0.15s,
|
|
1163
|
+
box-shadow 0.15s;
|
|
1164
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
1165
|
+
|
|
1166
|
+
svg {
|
|
1167
|
+
animation: ${(p) => p.$spinning ? import_styled_components2.css`
|
|
1168
|
+
${spin} 0.7s linear infinite
|
|
1169
|
+
` : "none"};
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
&:hover {
|
|
1173
|
+
background: #f3f4f6;
|
|
1174
|
+
border-color: #9ca3af;
|
|
1175
|
+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
&:disabled {
|
|
1179
|
+
opacity: 0.6;
|
|
1180
|
+
cursor: not-allowed;
|
|
1181
|
+
}
|
|
1182
|
+
`;
|
|
1183
|
+
DocTitleAnchor = ({ id, type, structureTool, children }) => {
|
|
1184
|
+
const { basePath } = (0, import_sanity19.useWorkspace)();
|
|
1185
|
+
const { onClick: intentOnClick, href: intentHref } = (0, import_router.useIntentLink)({
|
|
1186
|
+
intent: "edit",
|
|
1187
|
+
params: { id, type }
|
|
1188
|
+
});
|
|
1189
|
+
const href = structureTool ? `${basePath}/${structureTool}/intent/edit/id=${id};type=${type}/` : intentHref;
|
|
1190
|
+
const onClick = structureTool ? void 0 : intentOnClick;
|
|
1191
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DocTitleLink, { href, onClick, title: "Open document", children });
|
|
1192
|
+
};
|
|
1193
|
+
PaneLinkWrapper = import_styled_components2.default.span`
|
|
1194
|
+
display: block;
|
|
1195
|
+
min-width: 0;
|
|
1196
|
+
overflow: hidden;
|
|
1197
|
+
|
|
1198
|
+
a {
|
|
1199
|
+
font-size: 13px;
|
|
1200
|
+
font-weight: 600;
|
|
1201
|
+
color: #4f46e5;
|
|
1202
|
+
white-space: nowrap;
|
|
1203
|
+
overflow: hidden;
|
|
1204
|
+
text-overflow: ellipsis;
|
|
1205
|
+
text-decoration: none;
|
|
1206
|
+
display: block;
|
|
1207
|
+
transition: color 0.15s;
|
|
1208
|
+
|
|
1209
|
+
&:hover {
|
|
1210
|
+
color: #4338ca;
|
|
532
1211
|
text-decoration: underline;
|
|
533
1212
|
}
|
|
534
1213
|
}
|
|
535
1214
|
`;
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
}) => {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
};
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
};
|
|
549
|
-
|
|
550
|
-
to { transform: rotate(360deg); }
|
|
551
|
-
`;
|
|
552
|
-
var Spinner = import_styled_components.default.div`
|
|
1215
|
+
DocTitleAnchorPane = ({
|
|
1216
|
+
id,
|
|
1217
|
+
type,
|
|
1218
|
+
children
|
|
1219
|
+
}) => {
|
|
1220
|
+
const { ChildLink } = (0, import_structure.usePaneRouter)();
|
|
1221
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PaneLinkWrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ChildLink, { childId: id, childParameters: { type }, children }) });
|
|
1222
|
+
};
|
|
1223
|
+
DocBadgeRenderer = ({ doc, docBadge }) => {
|
|
1224
|
+
const badge = docBadge(doc);
|
|
1225
|
+
if (!badge) return null;
|
|
1226
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CustomBadge, { $bgColor: badge.bgColor, $textColor: badge.textColor, $fontSize: badge.fontSize, children: badge.label });
|
|
1227
|
+
};
|
|
1228
|
+
Spinner = import_styled_components2.default.div`
|
|
553
1229
|
width: 28px;
|
|
554
1230
|
height: 28px;
|
|
555
1231
|
border: 3px solid #e5e7eb;
|
|
@@ -558,424 +1234,463 @@ var Spinner = import_styled_components.default.div`
|
|
|
558
1234
|
animation: ${spin} 0.7s linear infinite;
|
|
559
1235
|
margin: 0 auto 12px;
|
|
560
1236
|
`;
|
|
561
|
-
|
|
1237
|
+
LoadingState = import_styled_components2.default.div`
|
|
562
1238
|
padding: 48px 24px;
|
|
563
1239
|
text-align: center;
|
|
564
1240
|
color: #6b7280;
|
|
565
1241
|
font-size: 13px;
|
|
566
1242
|
`;
|
|
567
|
-
|
|
1243
|
+
EmptyState = import_styled_components2.default.div`
|
|
568
1244
|
padding: 48px 24px;
|
|
569
1245
|
text-align: center;
|
|
570
1246
|
color: #9ca3af;
|
|
571
1247
|
font-size: 13px;
|
|
572
1248
|
`;
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
// Indigo
|
|
590
|
-
{ bg: "#fca5a5", text: "#7f1d1d" },
|
|
591
|
-
// Red
|
|
592
|
-
{ bg: "#a7f3d0", text: "#065f46" },
|
|
593
|
-
// Emerald
|
|
594
|
-
{ bg: "#fbbf24", text: "#78350f" },
|
|
595
|
-
// Amber
|
|
596
|
-
{ bg: "#c4b5fd", text: "#3b0764" },
|
|
597
|
-
// Violet
|
|
598
|
-
{ bg: "#f0fdf4", text: "#15803d" },
|
|
599
|
-
// Light Green
|
|
600
|
-
{ bg: "#fef2f2", text: "#991b1b" },
|
|
601
|
-
// Light Red
|
|
602
|
-
{ bg: "#f5f3ff", text: "#5b21b6" },
|
|
603
|
-
// Light Purple
|
|
604
|
-
{ bg: "#fffbeb", text: "#92400e" }
|
|
605
|
-
// Light Amber
|
|
606
|
-
];
|
|
607
|
-
var getTypeColor = (type) => {
|
|
608
|
-
let hash = 0;
|
|
609
|
-
for (let i = 0; i < type.length; i += 1) {
|
|
610
|
-
const char = type.charCodeAt(i);
|
|
611
|
-
hash = Math.abs(hash * 31 + char);
|
|
612
|
-
}
|
|
613
|
-
const colorIndex = hash % TYPE_COLOR_PALETTE.length;
|
|
614
|
-
return TYPE_COLOR_PALETTE[colorIndex];
|
|
615
|
-
};
|
|
616
|
-
var getStatusCategory = (score) => {
|
|
617
|
-
if (score >= 80) return "excellent";
|
|
618
|
-
if (score >= 60) return "good";
|
|
619
|
-
if (score >= 40) return "fair";
|
|
620
|
-
if (score > 0) return "poor";
|
|
621
|
-
return "missing";
|
|
622
|
-
};
|
|
623
|
-
var scoreMetaTitle = (title) => {
|
|
624
|
-
const issues = [];
|
|
625
|
-
let score = 0;
|
|
626
|
-
if (title && title.length >= 50 && title.length <= 60) {
|
|
627
|
-
score = 15;
|
|
628
|
-
} else if (title && title.length > 0) {
|
|
629
|
-
score = 10;
|
|
630
|
-
if (title.length < 50) issues.push("Meta title too short (< 50 chars)");
|
|
631
|
-
if (title.length > 60) issues.push("Meta title too long (> 60 chars)");
|
|
632
|
-
} else {
|
|
633
|
-
issues.push("Missing meta title");
|
|
634
|
-
}
|
|
635
|
-
return { score, issues };
|
|
636
|
-
};
|
|
637
|
-
var scoreMetaDescription = (description) => {
|
|
638
|
-
const issues = [];
|
|
639
|
-
let score = 0;
|
|
640
|
-
if (description && description.length >= 120 && description.length <= 160) {
|
|
641
|
-
score = 15;
|
|
642
|
-
} else if (description && description.length > 0) {
|
|
643
|
-
score = 10;
|
|
644
|
-
if (description.length < 120) issues.push("Meta description too short (< 120 chars)");
|
|
645
|
-
if (description.length > 160) issues.push("Meta description too long (> 160 chars)");
|
|
646
|
-
} else {
|
|
647
|
-
issues.push("Missing meta description");
|
|
648
|
-
}
|
|
649
|
-
return { score, issues };
|
|
650
|
-
};
|
|
651
|
-
var scoreOpenGraph = (openGraph2) => {
|
|
652
|
-
const issues = [];
|
|
653
|
-
let score = 0;
|
|
654
|
-
if (openGraph2) {
|
|
655
|
-
if (openGraph2.title) score += 6;
|
|
656
|
-
else issues.push("Missing OG title");
|
|
657
|
-
if (openGraph2.description) score += 6;
|
|
658
|
-
else issues.push("Missing OG description");
|
|
659
|
-
if (openGraph2.image) score += 6;
|
|
660
|
-
else issues.push("Missing OG image");
|
|
661
|
-
if (openGraph2.type) score += 7;
|
|
662
|
-
else issues.push("Missing OG type");
|
|
663
|
-
} else {
|
|
664
|
-
issues.push("Open Graph not configured");
|
|
665
|
-
}
|
|
666
|
-
return { score, issues };
|
|
667
|
-
};
|
|
668
|
-
var scoreTwitterCard = (twitter2) => {
|
|
669
|
-
const issues = [];
|
|
670
|
-
let score = 0;
|
|
671
|
-
if (twitter2) {
|
|
672
|
-
if (twitter2.title) score += 5;
|
|
673
|
-
else issues.push("Missing Twitter title");
|
|
674
|
-
if (twitter2.description) score += 5;
|
|
675
|
-
else issues.push("Missing Twitter description");
|
|
676
|
-
if (twitter2.image) score += 5;
|
|
677
|
-
else issues.push("Missing Twitter image");
|
|
678
|
-
} else {
|
|
679
|
-
issues.push("Twitter Card not configured");
|
|
680
|
-
}
|
|
681
|
-
return { score, issues };
|
|
682
|
-
};
|
|
683
|
-
var calculateHealthScore = (doc) => {
|
|
684
|
-
if (!doc.seo) {
|
|
685
|
-
return { score: 0, status: "missing", issues: ["SEO fields not configured"] };
|
|
686
|
-
}
|
|
687
|
-
const { title, description, keywords, robots, canonicalUrl, openGraph: openGraph2, twitter: twitter2 } = doc.seo;
|
|
688
|
-
let score = 0;
|
|
689
|
-
const issues = [];
|
|
690
|
-
const titleResult = scoreMetaTitle(title);
|
|
691
|
-
score += titleResult.score;
|
|
692
|
-
issues.push(...titleResult.issues);
|
|
693
|
-
const descResult = scoreMetaDescription(description);
|
|
694
|
-
score += descResult.score;
|
|
695
|
-
issues.push(...descResult.issues);
|
|
696
|
-
if (doc.seo.metaImage) score += 10;
|
|
697
|
-
else issues.push("Missing meta image");
|
|
698
|
-
if (keywords && keywords.length > 0) score += 10;
|
|
699
|
-
else issues.push("No keywords defined");
|
|
700
|
-
if (robots && !robots.noIndex) score += 5;
|
|
701
|
-
else if (!robots) score += 5;
|
|
702
|
-
if (canonicalUrl) score += 0;
|
|
703
|
-
const ogResult = scoreOpenGraph(openGraph2);
|
|
704
|
-
score += ogResult.score;
|
|
705
|
-
issues.push(...ogResult.issues);
|
|
706
|
-
const twResult = scoreTwitterCard(twitter2);
|
|
707
|
-
score += twResult.score;
|
|
708
|
-
issues.push(...twResult.issues);
|
|
709
|
-
const hasMetaImage = !!doc.seo.metaImage;
|
|
710
|
-
const hasOgImage = !!(openGraph2 && openGraph2.image);
|
|
711
|
-
const hasTwitterImage = !!(twitter2 && twitter2.image);
|
|
712
|
-
if (hasMetaImage && hasOgImage && hasTwitterImage) {
|
|
713
|
-
score += 5;
|
|
714
|
-
} else {
|
|
715
|
-
const missingImages = [];
|
|
716
|
-
if (!hasMetaImage) missingImages.push("meta image");
|
|
717
|
-
if (!hasOgImage) missingImages.push("OG image");
|
|
718
|
-
if (!hasTwitterImage) missingImages.push("Twitter image");
|
|
719
|
-
issues.push(`Missing images for full score: ${missingImages.join(", ")}`);
|
|
1249
|
+
DeprecationBanner = import_styled_components2.default.div`
|
|
1250
|
+
background: #fffbeb;
|
|
1251
|
+
border: 1px solid #fcd34d;
|
|
1252
|
+
border-radius: 8px;
|
|
1253
|
+
padding: 10px 14px;
|
|
1254
|
+
font-size: 12px;
|
|
1255
|
+
color: #78350f;
|
|
1256
|
+
margin-bottom: 16px;
|
|
1257
|
+
line-height: 1.6;
|
|
1258
|
+
`;
|
|
1259
|
+
DeprecationBannerLink = import_styled_components2.default.a`
|
|
1260
|
+
color: #92400e;
|
|
1261
|
+
font-weight: 600;
|
|
1262
|
+
text-decoration: underline;
|
|
1263
|
+
&:hover {
|
|
1264
|
+
color: #78350f;
|
|
720
1265
|
}
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
1266
|
+
`;
|
|
1267
|
+
TYPE_COLOR_PALETTE = [
|
|
1268
|
+
{ bg: "#dbeafe", text: "#0c4a6e" },
|
|
1269
|
+
// Blue
|
|
1270
|
+
{ bg: "#dcfce7", text: "#14532d" },
|
|
1271
|
+
// Green
|
|
1272
|
+
{ bg: "#fce7f3", text: "#500724" },
|
|
1273
|
+
// Pink
|
|
1274
|
+
{ bg: "#fed7aa", text: "#7c2d12" },
|
|
1275
|
+
// Orange
|
|
1276
|
+
{ bg: "#e9d5ff", text: "#581c87" },
|
|
1277
|
+
// Purple
|
|
1278
|
+
{ bg: "#f3e8ff", text: "#3f0f5c" },
|
|
1279
|
+
// Deep Purple
|
|
1280
|
+
{ bg: "#ccfbf1", text: "#134e4a" },
|
|
1281
|
+
// Teal
|
|
1282
|
+
{ bg: "#ddd6fe", text: "#3730a3" },
|
|
1283
|
+
// Indigo
|
|
1284
|
+
{ bg: "#fca5a5", text: "#7f1d1d" },
|
|
1285
|
+
// Red
|
|
1286
|
+
{ bg: "#a7f3d0", text: "#065f46" },
|
|
1287
|
+
// Emerald
|
|
1288
|
+
{ bg: "#fbbf24", text: "#78350f" },
|
|
1289
|
+
// Amber
|
|
1290
|
+
{ bg: "#c4b5fd", text: "#3b0764" },
|
|
1291
|
+
// Violet
|
|
1292
|
+
{ bg: "#f0fdf4", text: "#15803d" },
|
|
1293
|
+
// Light Green
|
|
1294
|
+
{ bg: "#fef2f2", text: "#991b1b" },
|
|
1295
|
+
// Light Red
|
|
1296
|
+
{ bg: "#f5f3ff", text: "#5b21b6" },
|
|
1297
|
+
// Light Purple
|
|
1298
|
+
{ bg: "#fffbeb", text: "#92400e" }
|
|
1299
|
+
// Light Amber
|
|
1300
|
+
];
|
|
1301
|
+
getTypeColor = (type) => {
|
|
1302
|
+
let hash = 0;
|
|
1303
|
+
for (let i = 0; i < type.length; i += 1) {
|
|
1304
|
+
const char = type.charCodeAt(i);
|
|
1305
|
+
hash = Math.abs(hash * 31 + char);
|
|
759
1306
|
}
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
1307
|
+
const colorIndex = hash % TYPE_COLOR_PALETTE.length;
|
|
1308
|
+
return TYPE_COLOR_PALETTE[colorIndex];
|
|
1309
|
+
};
|
|
1310
|
+
getStatusCategory = (score) => {
|
|
1311
|
+
if (score >= 80) return "excellent";
|
|
1312
|
+
if (score >= 60) return "good";
|
|
1313
|
+
if (score >= 40) return "fair";
|
|
1314
|
+
if (score > 0) return "poor";
|
|
1315
|
+
return "missing";
|
|
1316
|
+
};
|
|
1317
|
+
scoreMetaTitle = (title) => {
|
|
1318
|
+
const issues = [];
|
|
1319
|
+
let score = 0;
|
|
1320
|
+
if (title && title.length >= 50 && title.length <= 60) {
|
|
1321
|
+
score = 15;
|
|
1322
|
+
} else if (title && title.length > 0) {
|
|
1323
|
+
score = 10;
|
|
1324
|
+
if (title.length < 50) issues.push("Meta title too short (< 50 chars)");
|
|
1325
|
+
if (title.length > 60) issues.push("Meta title too long (> 60 chars)");
|
|
1326
|
+
} else {
|
|
1327
|
+
issues.push("Missing meta title");
|
|
774
1328
|
}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
1329
|
+
return { score, issues };
|
|
1330
|
+
};
|
|
1331
|
+
scoreMetaDescription = (description) => {
|
|
1332
|
+
const issues = [];
|
|
1333
|
+
let score = 0;
|
|
1334
|
+
if (description && description.length >= 120 && description.length <= 160) {
|
|
1335
|
+
score = 15;
|
|
1336
|
+
} else if (description && description.length > 0) {
|
|
1337
|
+
score = 10;
|
|
1338
|
+
if (description.length < 120) issues.push("Meta description too short (< 120 chars)");
|
|
1339
|
+
if (description.length > 160) issues.push("Meta description too long (> 160 chars)");
|
|
1340
|
+
} else {
|
|
1341
|
+
issues.push("Missing meta description");
|
|
786
1342
|
}
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
type: "article"
|
|
804
|
-
},
|
|
805
|
-
twitter: {
|
|
806
|
-
title: "Content Marketing Trends",
|
|
807
|
-
description: "Discover the latest trends",
|
|
808
|
-
card: "summary"
|
|
809
|
-
}
|
|
1343
|
+
return { score, issues };
|
|
1344
|
+
};
|
|
1345
|
+
scoreOpenGraph = (openGraph2) => {
|
|
1346
|
+
const issues = [];
|
|
1347
|
+
let score = 0;
|
|
1348
|
+
if (openGraph2) {
|
|
1349
|
+
if (openGraph2.title) score += 6;
|
|
1350
|
+
else issues.push("Missing OG title");
|
|
1351
|
+
if (openGraph2.description) score += 6;
|
|
1352
|
+
else issues.push("Missing OG description");
|
|
1353
|
+
if (openGraph2.image) score += 6;
|
|
1354
|
+
else issues.push("Missing OG image");
|
|
1355
|
+
if (openGraph2.type) score += 7;
|
|
1356
|
+
else issues.push("Missing OG type");
|
|
1357
|
+
} else {
|
|
1358
|
+
issues.push("Open Graph not configured");
|
|
810
1359
|
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
1360
|
+
return { score, issues };
|
|
1361
|
+
};
|
|
1362
|
+
scoreTwitterCard = (twitter2) => {
|
|
1363
|
+
const issues = [];
|
|
1364
|
+
let score = 0;
|
|
1365
|
+
if (twitter2) {
|
|
1366
|
+
if (twitter2.title) score += 5;
|
|
1367
|
+
else issues.push("Missing Twitter title");
|
|
1368
|
+
if (twitter2.description) score += 5;
|
|
1369
|
+
else issues.push("Missing Twitter description");
|
|
1370
|
+
if (twitter2.image) score += 5;
|
|
1371
|
+
else issues.push("Missing Twitter image");
|
|
1372
|
+
} else {
|
|
1373
|
+
issues.push("Twitter Card not configured");
|
|
821
1374
|
}
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
slug: { current: "contact" },
|
|
828
|
-
_updatedAt: new Date(Date.now() - 8 * 24 * 60 * 60 * 1e3).toISOString(),
|
|
829
|
-
seo: {
|
|
830
|
-
openGraph: {
|
|
831
|
-
title: "Get in Touch"
|
|
832
|
-
}
|
|
1375
|
+
return { score, issues };
|
|
1376
|
+
};
|
|
1377
|
+
calculateHealthScore = (doc) => {
|
|
1378
|
+
if (!doc.seo) {
|
|
1379
|
+
return { score: 0, status: "missing", issues: ["SEO fields not configured"] };
|
|
833
1380
|
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
1381
|
+
const { title, description, keywords, robots, canonicalUrl, openGraph: openGraph2, twitter: twitter2 } = doc.seo;
|
|
1382
|
+
let score = 0;
|
|
1383
|
+
const issues = [];
|
|
1384
|
+
const titleResult = scoreMetaTitle(title);
|
|
1385
|
+
score += titleResult.score;
|
|
1386
|
+
issues.push(...titleResult.issues);
|
|
1387
|
+
const descResult = scoreMetaDescription(description);
|
|
1388
|
+
score += descResult.score;
|
|
1389
|
+
issues.push(...descResult.issues);
|
|
1390
|
+
if (doc.seo.metaImage) score += 10;
|
|
1391
|
+
else issues.push("Missing meta image");
|
|
1392
|
+
if (keywords && keywords.length > 0) score += 10;
|
|
1393
|
+
else issues.push("No keywords defined");
|
|
1394
|
+
if (robots && !robots.noIndex) score += 5;
|
|
1395
|
+
else if (!robots) score += 5;
|
|
1396
|
+
if (canonicalUrl) score += 0;
|
|
1397
|
+
const ogResult = scoreOpenGraph(openGraph2);
|
|
1398
|
+
score += ogResult.score;
|
|
1399
|
+
issues.push(...ogResult.issues);
|
|
1400
|
+
const twResult = scoreTwitterCard(twitter2);
|
|
1401
|
+
score += twResult.score;
|
|
1402
|
+
issues.push(...twResult.issues);
|
|
1403
|
+
const hasMetaImage = !!doc.seo.metaImage;
|
|
1404
|
+
const hasOgImage = !!(openGraph2 && openGraph2.image);
|
|
1405
|
+
const hasTwitterImage = !!(twitter2 && twitter2.image);
|
|
1406
|
+
if (hasMetaImage && hasOgImage && hasTwitterImage) {
|
|
1407
|
+
score += 5;
|
|
1408
|
+
} else {
|
|
1409
|
+
const missingImages = [];
|
|
1410
|
+
if (!hasMetaImage) missingImages.push("meta image");
|
|
1411
|
+
if (!hasOgImage) missingImages.push("OG image");
|
|
1412
|
+
if (!hasTwitterImage) missingImages.push("Twitter image");
|
|
1413
|
+
issues.push(`Missing images for full score: ${missingImages.join(", ")}`);
|
|
858
1414
|
}
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
}));
|
|
864
|
-
};
|
|
865
|
-
var SeoHealthDashboard = ({
|
|
866
|
-
icon = "\u{1F4CA}",
|
|
867
|
-
title = "SEO Health Dashboard",
|
|
868
|
-
description = "Monitor and optimize SEO fields across all your documents",
|
|
869
|
-
showTypeColumn = true,
|
|
870
|
-
showDocumentId = true,
|
|
871
|
-
queryTypes,
|
|
872
|
-
queryRequireSeo = true,
|
|
873
|
-
customQuery,
|
|
874
|
-
apiVersion = "2023-01-01",
|
|
875
|
-
licenseKey,
|
|
876
|
-
typeLabels,
|
|
877
|
-
typeColumnMode = "badge",
|
|
878
|
-
titleField,
|
|
879
|
-
docBadge,
|
|
880
|
-
loadingLicense,
|
|
881
|
-
loadingDocuments,
|
|
882
|
-
noDocuments,
|
|
883
|
-
previewMode = false,
|
|
884
|
-
openInPane = false,
|
|
885
|
-
structureTool
|
|
886
|
-
}) => {
|
|
887
|
-
const client = (0, import_sanity.useClient)({ apiVersion });
|
|
888
|
-
const [licenseStatus, setLicenseStatus] = (0, import_react.useState)("loading");
|
|
889
|
-
const [documents, setDocuments] = (0, import_react.useState)([]);
|
|
890
|
-
const [loading, setLoading] = (0, import_react.useState)(true);
|
|
891
|
-
const [searchQuery, setSearchQuery] = (0, import_react.useState)("");
|
|
892
|
-
const [filterStatus, setFilterStatus] = (0, import_react.useState)("all");
|
|
893
|
-
const [filterType, setFilterType] = (0, import_react.useState)("all");
|
|
894
|
-
const [sortBy, setSortBy] = (0, import_react.useState)("score");
|
|
895
|
-
const [activePopover, setActivePopover] = (0, import_react.useState)(null);
|
|
896
|
-
const VALIDATION_ENDPOINT = "https://sanity-plugin-seofields.thehardik.in/api/validate-license";
|
|
897
|
-
const CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
898
|
-
const validateLicense = (0, import_react.useCallback)(
|
|
899
|
-
async (forceRefresh = false) => {
|
|
1415
|
+
const status = getStatusCategory(score);
|
|
1416
|
+
return { score, status, issues };
|
|
1417
|
+
};
|
|
1418
|
+
resolveTypeLabel = (type, typeLabels) => {
|
|
900
1419
|
var _a;
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
if (
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
const
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1420
|
+
return (_a = typeLabels == null ? void 0 : typeLabels[type]) != null ? _a : type;
|
|
1421
|
+
};
|
|
1422
|
+
buildTitleProjection = (titleField) => {
|
|
1423
|
+
if (!titleField || titleField === "title") return "title";
|
|
1424
|
+
if (typeof titleField === "string") return `"title": ${titleField}`;
|
|
1425
|
+
const cases = Object.entries(titleField).map(([type, field]) => `_type == "${type}" => ${field}`).join(", ");
|
|
1426
|
+
return `"title": select(${cases}, title)`;
|
|
1427
|
+
};
|
|
1428
|
+
generateDummyData = () => {
|
|
1429
|
+
const dummyDocs = [
|
|
1430
|
+
{
|
|
1431
|
+
_id: "preview-post-1",
|
|
1432
|
+
_type: "post",
|
|
1433
|
+
title: "Getting Started with SEO Best Practices",
|
|
1434
|
+
slug: { current: "getting-started-seo" },
|
|
1435
|
+
_updatedAt: new Date(Date.now() - 2 * 24 * 60 * 60 * 1e3).toISOString(),
|
|
1436
|
+
seo: {
|
|
1437
|
+
title: "Getting Started with SEO Best Practices | My Blog",
|
|
1438
|
+
description: "Learn the fundamentals of SEO optimization to improve your website visibility and search rankings.",
|
|
1439
|
+
keywords: ["seo", "best practices", "optimization"],
|
|
1440
|
+
metaImage: { _type: "image", asset: { _ref: "image-123", _type: "reference" } },
|
|
1441
|
+
openGraph: {
|
|
1442
|
+
title: "SEO Best Practices Guide",
|
|
1443
|
+
description: "Master SEO optimization",
|
|
1444
|
+
image: { _type: "image", asset: { _ref: "image-123", _type: "reference" }, alt: "SEO Guide" },
|
|
1445
|
+
type: "article"
|
|
1446
|
+
},
|
|
1447
|
+
twitter: {
|
|
1448
|
+
title: "SEO Best Practices",
|
|
1449
|
+
description: "Learn SEO optimization",
|
|
1450
|
+
image: { _type: "image", asset: { _ref: "image-123", _type: "reference" }, alt: "Guide" },
|
|
1451
|
+
card: "summary_large_image"
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
},
|
|
1455
|
+
{
|
|
1456
|
+
_id: "preview-post-2",
|
|
1457
|
+
_type: "post",
|
|
1458
|
+
title: "Advanced Analytics Strategy",
|
|
1459
|
+
slug: { current: "advanced-analytics" },
|
|
1460
|
+
_updatedAt: new Date(Date.now() - 5 * 24 * 60 * 60 * 1e3).toISOString(),
|
|
1461
|
+
seo: {
|
|
1462
|
+
title: "Advanced Analytics",
|
|
1463
|
+
description: "Strategy tips",
|
|
1464
|
+
keywords: ["analytics", "data"],
|
|
1465
|
+
openGraph: {
|
|
1466
|
+
title: "Analytics Guide"
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
},
|
|
1470
|
+
{
|
|
1471
|
+
_id: "preview-page-1",
|
|
1472
|
+
_type: "page",
|
|
1473
|
+
title: "About Us",
|
|
1474
|
+
slug: { current: "about" },
|
|
1475
|
+
_updatedAt: new Date(Date.now() - 10 * 24 * 60 * 60 * 1e3).toISOString(),
|
|
1476
|
+
seo: {
|
|
1477
|
+
title: "About",
|
|
1478
|
+
keywords: ["company", "team"],
|
|
1479
|
+
metaImage: { _type: "image", asset: { _ref: "image-456", _type: "reference" } }
|
|
1480
|
+
}
|
|
1481
|
+
},
|
|
1482
|
+
{
|
|
1483
|
+
_id: "preview-post-3",
|
|
1484
|
+
_type: "post",
|
|
1485
|
+
title: "Content Marketing Trends for 2024",
|
|
1486
|
+
slug: { current: "content-marketing-trends" },
|
|
1487
|
+
_updatedAt: new Date(Date.now() - 1 * 24 * 60 * 60 * 1e3).toISOString(),
|
|
1488
|
+
seo: {
|
|
1489
|
+
title: "Content Marketing Trends 2024",
|
|
1490
|
+
description: "Discover the latest content marketing trends and strategies to engage your audience effectively.",
|
|
1491
|
+
keywords: ["content marketing", "trends", "strategy", "engagement"],
|
|
1492
|
+
metaImage: { _type: "image", asset: { _ref: "image-789", _type: "reference" } },
|
|
1493
|
+
openGraph: {
|
|
1494
|
+
title: "Content Marketing Trends 2024",
|
|
1495
|
+
description: "Latest trends in content marketing",
|
|
1496
|
+
image: { _type: "image", asset: { _ref: "image-789", _type: "reference" }, alt: "Trends" },
|
|
1497
|
+
type: "article"
|
|
1498
|
+
},
|
|
1499
|
+
twitter: {
|
|
1500
|
+
title: "Content Marketing Trends",
|
|
1501
|
+
description: "Discover the latest trends",
|
|
1502
|
+
card: "summary"
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
},
|
|
1506
|
+
{
|
|
1507
|
+
_id: "preview-post-4",
|
|
1508
|
+
_type: "product",
|
|
1509
|
+
title: "Pro Plan",
|
|
1510
|
+
slug: { current: "pro-plan" },
|
|
1511
|
+
_updatedAt: new Date(Date.now() - 15 * 24 * 60 * 60 * 1e3).toISOString(),
|
|
1512
|
+
seo: {
|
|
1513
|
+
title: "Pro",
|
|
1514
|
+
keywords: ["pricing"]
|
|
1515
|
+
}
|
|
1516
|
+
},
|
|
1517
|
+
{
|
|
1518
|
+
_id: "preview-page-2",
|
|
1519
|
+
_type: "page",
|
|
1520
|
+
title: "Contact",
|
|
1521
|
+
slug: { current: "contact" },
|
|
1522
|
+
_updatedAt: new Date(Date.now() - 8 * 24 * 60 * 60 * 1e3).toISOString(),
|
|
1523
|
+
seo: {
|
|
1524
|
+
openGraph: {
|
|
1525
|
+
title: "Get in Touch"
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
},
|
|
1529
|
+
{
|
|
1530
|
+
_id: "preview-post-5",
|
|
1531
|
+
_type: "post",
|
|
1532
|
+
title: "Mobile Optimization Guide",
|
|
1533
|
+
slug: { current: "mobile-optimization" },
|
|
1534
|
+
_updatedAt: new Date(Date.now() - 3 * 24 * 60 * 60 * 1e3).toISOString(),
|
|
1535
|
+
seo: {
|
|
1536
|
+
title: "Mobile Optimization Guide: Best Practices for Responsive Design",
|
|
1537
|
+
description: "Complete guide to mobile optimization including responsive design, performance tips, and user experience best practices for modern web development.",
|
|
1538
|
+
keywords: ["mobile", "optimization", "responsive", "performance"],
|
|
1539
|
+
metaImage: { _type: "image", asset: { _ref: "image-mobile", _type: "reference" } },
|
|
1540
|
+
openGraph: {
|
|
1541
|
+
title: "Mobile Optimization Best Practices",
|
|
1542
|
+
description: "Master mobile web optimization",
|
|
1543
|
+
image: { _type: "image", asset: { _ref: "image-mobile", _type: "reference" }, alt: "Mobile" },
|
|
1544
|
+
type: "article"
|
|
1545
|
+
},
|
|
1546
|
+
twitter: {
|
|
1547
|
+
title: "Mobile Optimization Tips",
|
|
1548
|
+
description: "Responsive design best practices",
|
|
1549
|
+
image: { _type: "image", asset: { _ref: "image-mobile", _type: "reference" }, alt: "Mobile" },
|
|
1550
|
+
card: "summary_large_image"
|
|
925
1551
|
}
|
|
926
1552
|
}
|
|
927
|
-
} catch (e) {
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
setLicenseStatus("loading");
|
|
931
|
-
try {
|
|
932
|
-
const res = await fetch(VALIDATION_ENDPOINT, {
|
|
933
|
-
method: "POST",
|
|
934
|
-
headers: { "Content-Type": "application/json" },
|
|
935
|
-
body: JSON.stringify({ licenseKey, projectId })
|
|
936
|
-
});
|
|
937
|
-
const valid = res.ok;
|
|
938
|
-
setLicenseStatus(valid ? "valid" : "invalid");
|
|
939
|
-
try {
|
|
940
|
-
sessionStorage.setItem(cacheKey, JSON.stringify({ valid, ts: Date.now() }));
|
|
941
|
-
} catch (e) {
|
|
942
1553
|
}
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1554
|
+
];
|
|
1555
|
+
return dummyDocs.map((doc) => __spreadProps(__spreadValues({}, doc), {
|
|
1556
|
+
health: calculateHealthScore(doc)
|
|
1557
|
+
}));
|
|
1558
|
+
};
|
|
1559
|
+
SeoHealthDashboard = ({
|
|
1560
|
+
icon = "\u{1F4CA}",
|
|
1561
|
+
title = "SEO Health Dashboard",
|
|
1562
|
+
description = "Monitor and optimize SEO fields across all your documents",
|
|
1563
|
+
showTypeColumn = true,
|
|
1564
|
+
showDocumentId = true,
|
|
1565
|
+
queryTypes,
|
|
1566
|
+
queryRequireSeo = true,
|
|
1567
|
+
customQuery,
|
|
1568
|
+
apiVersion = "2023-01-01",
|
|
1569
|
+
licenseKey,
|
|
1570
|
+
typeDisplayLabels,
|
|
1571
|
+
typeColumnMode = "badge",
|
|
1572
|
+
titleField,
|
|
1573
|
+
getDocumentBadge,
|
|
1574
|
+
loadingLicense,
|
|
1575
|
+
loadingDocuments,
|
|
1576
|
+
noDocuments,
|
|
1577
|
+
previewMode = false,
|
|
1578
|
+
openInPane = false,
|
|
1579
|
+
structureTool,
|
|
1580
|
+
_deprecationWarnings
|
|
1581
|
+
}) => {
|
|
1582
|
+
const resolvedTypeLabels = typeDisplayLabels;
|
|
1583
|
+
const resolvedDocBadge = getDocumentBadge;
|
|
1584
|
+
const allDeprecationWarnings = (0, import_react13.useMemo)(() => _deprecationWarnings != null ? _deprecationWarnings : [], [_deprecationWarnings]);
|
|
1585
|
+
const deprecationGroups = (0, import_react13.useMemo)(() => {
|
|
1586
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1587
|
+
for (const w of allDeprecationWarnings) {
|
|
1588
|
+
if (!groups.has(w.version)) {
|
|
1589
|
+
groups.set(w.version, { version: w.version, changelogUrl: w.changelogUrl, keys: [] });
|
|
1590
|
+
}
|
|
1591
|
+
groups.get(w.version).keys.push(w.key);
|
|
970
1592
|
}
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1593
|
+
return Array.from(groups.values());
|
|
1594
|
+
}, [allDeprecationWarnings]);
|
|
1595
|
+
const client = (0, import_sanity19.useClient)({ apiVersion });
|
|
1596
|
+
const [licenseStatus, setLicenseStatus] = (0, import_react13.useState)("loading");
|
|
1597
|
+
const [documents, setDocuments] = (0, import_react13.useState)([]);
|
|
1598
|
+
const [loading, setLoading] = (0, import_react13.useState)(true);
|
|
1599
|
+
const [isRefreshing, setIsRefreshing] = (0, import_react13.useState)(false);
|
|
1600
|
+
const [searchQuery, setSearchQuery] = (0, import_react13.useState)("");
|
|
1601
|
+
const [filterStatus, setFilterStatus] = (0, import_react13.useState)("all");
|
|
1602
|
+
const [filterType, setFilterType] = (0, import_react13.useState)("all");
|
|
1603
|
+
const [sortBy, setSortBy] = (0, import_react13.useState)("score");
|
|
1604
|
+
const [activePopover, setActivePopover] = (0, import_react13.useState)(null);
|
|
1605
|
+
const VALIDATION_ENDPOINT = "https://sanity-plugin-seofields.thehardik.in/api/validate-license";
|
|
1606
|
+
const CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
1607
|
+
const validateLicense = (0, import_react13.useCallback)(
|
|
1608
|
+
async (forceRefresh = false) => {
|
|
1609
|
+
var _a;
|
|
1610
|
+
if (previewMode) {
|
|
1611
|
+
setLicenseStatus("valid");
|
|
1612
|
+
return;
|
|
1613
|
+
}
|
|
1614
|
+
if (!licenseKey) {
|
|
1615
|
+
setLicenseStatus("invalid");
|
|
1616
|
+
return;
|
|
1617
|
+
}
|
|
1618
|
+
const projectId = (_a = client.config().projectId) != null ? _a : "";
|
|
1619
|
+
const cacheKey = `seofields_license_${projectId}`;
|
|
1620
|
+
if (forceRefresh) {
|
|
1621
|
+
try {
|
|
1622
|
+
sessionStorage.removeItem(cacheKey);
|
|
1623
|
+
} catch (e) {
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
if (!forceRefresh) {
|
|
1627
|
+
try {
|
|
1628
|
+
const cached = sessionStorage.getItem(cacheKey);
|
|
1629
|
+
if (cached) {
|
|
1630
|
+
const { valid, ts } = JSON.parse(cached);
|
|
1631
|
+
if (Date.now() - ts < CACHE_TTL_MS) {
|
|
1632
|
+
setLicenseStatus(valid ? "valid" : "invalid");
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
} catch (e) {
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
setLicenseStatus("loading");
|
|
1640
|
+
try {
|
|
1641
|
+
const res = await fetch(VALIDATION_ENDPOINT, {
|
|
1642
|
+
method: "POST",
|
|
1643
|
+
headers: { "Content-Type": "application/json" },
|
|
1644
|
+
body: JSON.stringify({ licenseKey, projectId })
|
|
1645
|
+
});
|
|
1646
|
+
const valid = res.ok;
|
|
1647
|
+
setLicenseStatus(valid ? "valid" : "invalid");
|
|
1648
|
+
try {
|
|
1649
|
+
sessionStorage.setItem(cacheKey, JSON.stringify({ valid, ts: Date.now() }));
|
|
1650
|
+
} catch (e) {
|
|
1651
|
+
}
|
|
1652
|
+
} catch (e) {
|
|
1653
|
+
setLicenseStatus("valid");
|
|
1654
|
+
}
|
|
1655
|
+
},
|
|
1656
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1657
|
+
[licenseKey, previewMode]
|
|
1658
|
+
);
|
|
1659
|
+
(0, import_react13.useEffect)(() => {
|
|
1660
|
+
validateLicense();
|
|
1661
|
+
}, [licenseKey, previewMode]);
|
|
1662
|
+
const handleMouseEnterIssues = (el, issues) => {
|
|
1663
|
+
if (!el) return;
|
|
1664
|
+
const rect = el.getBoundingClientRect();
|
|
1665
|
+
const popoverWidth = 280;
|
|
1666
|
+
const viewportWidth = window.innerWidth;
|
|
1667
|
+
let left = rect.left;
|
|
1668
|
+
if (left + popoverWidth > viewportWidth - 10) left = viewportWidth - popoverWidth - 10;
|
|
1669
|
+
if (left < 10) left = 10;
|
|
1670
|
+
setActivePopover({ top: rect.top, left, issues });
|
|
1671
|
+
};
|
|
1672
|
+
const JSONStringifiedQueryTypes = JSON.stringify(queryTypes);
|
|
1673
|
+
const JSONStringifiedTitleField = JSON.stringify(titleField);
|
|
1674
|
+
const fetchDocuments = (0, import_react13.useCallback)(
|
|
1675
|
+
async (isManualRefresh = false) => {
|
|
1676
|
+
try {
|
|
1677
|
+
if (isManualRefresh) {
|
|
1678
|
+
setIsRefreshing(true);
|
|
1679
|
+
} else {
|
|
1680
|
+
setLoading(true);
|
|
1681
|
+
}
|
|
1682
|
+
if (previewMode) {
|
|
1683
|
+
setDocuments(generateDummyData());
|
|
1684
|
+
return;
|
|
1685
|
+
}
|
|
1686
|
+
let groqQuery;
|
|
1687
|
+
let params = {};
|
|
1688
|
+
if (customQuery) {
|
|
1689
|
+
groqQuery = customQuery;
|
|
1690
|
+
} else if (queryTypes && queryTypes.length > 0) {
|
|
1691
|
+
const seoFilter = queryRequireSeo ? " && seo != null" : "";
|
|
1692
|
+
const titleProj = buildTitleProjection(titleField);
|
|
1693
|
+
groqQuery = `*[_type in $types${seoFilter} && !(_id in path("drafts.**"))]{
|
|
979
1694
|
_id,
|
|
980
1695
|
_type,
|
|
981
1696
|
${titleProj},
|
|
@@ -983,10 +1698,10 @@ var SeoHealthDashboard = ({
|
|
|
983
1698
|
seo,
|
|
984
1699
|
_updatedAt
|
|
985
1700
|
}`;
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
1701
|
+
params = { types: queryTypes };
|
|
1702
|
+
} else {
|
|
1703
|
+
const titleProj = buildTitleProjection(titleField);
|
|
1704
|
+
groqQuery = `*[seo != null && !(_id in path("drafts.**"))]{
|
|
990
1705
|
_id,
|
|
991
1706
|
_type,
|
|
992
1707
|
${titleProj},
|
|
@@ -994,100 +1709,113 @@ var SeoHealthDashboard = ({
|
|
|
994
1709
|
seo,
|
|
995
1710
|
_updatedAt
|
|
996
1711
|
}`;
|
|
1712
|
+
}
|
|
1713
|
+
const result = await client.fetch(groqQuery, params, { perspective: "published" });
|
|
1714
|
+
const docsWithHealth = result.map((doc) => __spreadProps(__spreadValues({}, doc), {
|
|
1715
|
+
health: calculateHealthScore(doc)
|
|
1716
|
+
}));
|
|
1717
|
+
setDocuments(docsWithHealth);
|
|
1718
|
+
} catch (error) {
|
|
1719
|
+
console.error("Error fetching documents:", error);
|
|
1720
|
+
} finally {
|
|
1721
|
+
setLoading(false);
|
|
1722
|
+
setIsRefreshing(false);
|
|
1723
|
+
}
|
|
1724
|
+
},
|
|
1725
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1726
|
+
[
|
|
1727
|
+
client,
|
|
1728
|
+
customQuery,
|
|
1729
|
+
queryRequireSeo,
|
|
1730
|
+
JSONStringifiedQueryTypes,
|
|
1731
|
+
JSONStringifiedTitleField,
|
|
1732
|
+
previewMode
|
|
1733
|
+
]
|
|
1734
|
+
);
|
|
1735
|
+
(0, import_react13.useEffect)(() => {
|
|
1736
|
+
fetchDocuments();
|
|
1737
|
+
}, [
|
|
1738
|
+
client,
|
|
1739
|
+
customQuery,
|
|
1740
|
+
queryRequireSeo,
|
|
1741
|
+
JSONStringifiedQueryTypes,
|
|
1742
|
+
JSONStringifiedTitleField,
|
|
1743
|
+
previewMode
|
|
1744
|
+
]);
|
|
1745
|
+
const handleRefresh = (0, import_react13.useCallback)(() => {
|
|
1746
|
+
fetchDocuments(true);
|
|
1747
|
+
}, [fetchDocuments]);
|
|
1748
|
+
const uniqueDocumentTypes = (0, import_react13.useMemo)(() => {
|
|
1749
|
+
const types2 = new Set(documents.map((doc) => doc._type));
|
|
1750
|
+
return Array.from(types2).sort();
|
|
1751
|
+
}, [documents]);
|
|
1752
|
+
const filteredAndSortedDocs = (0, import_react13.useMemo)(() => {
|
|
1753
|
+
let filtered = documents;
|
|
1754
|
+
if (searchQuery) {
|
|
1755
|
+
filtered = filtered.filter(
|
|
1756
|
+
(doc) => {
|
|
1757
|
+
var _a, _b;
|
|
1758
|
+
return ((_a = doc.title) == null ? void 0 : _a.toLowerCase().includes(searchQuery.toLowerCase())) || ((_b = doc._id) == null ? void 0 : _b.toLowerCase().includes(searchQuery.toLowerCase()));
|
|
1759
|
+
}
|
|
1760
|
+
);
|
|
997
1761
|
}
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
health: calculateHealthScore(doc)
|
|
1001
|
-
}));
|
|
1002
|
-
setDocuments(docsWithHealth);
|
|
1003
|
-
} catch (error) {
|
|
1004
|
-
console.error("Error fetching documents:", error);
|
|
1005
|
-
} finally {
|
|
1006
|
-
setLoading(false);
|
|
1007
|
-
}
|
|
1008
|
-
};
|
|
1009
|
-
fetchDocuments();
|
|
1010
|
-
}, [
|
|
1011
|
-
client,
|
|
1012
|
-
customQuery,
|
|
1013
|
-
queryRequireSeo,
|
|
1014
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1015
|
-
JSON.stringify(queryTypes),
|
|
1016
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1017
|
-
JSON.stringify(titleField),
|
|
1018
|
-
previewMode
|
|
1019
|
-
]);
|
|
1020
|
-
const uniqueDocumentTypes = (0, import_react.useMemo)(() => {
|
|
1021
|
-
const types2 = new Set(documents.map((doc) => doc._type));
|
|
1022
|
-
return Array.from(types2).sort();
|
|
1023
|
-
}, [documents]);
|
|
1024
|
-
const filteredAndSortedDocs = (0, import_react.useMemo)(() => {
|
|
1025
|
-
let filtered = documents;
|
|
1026
|
-
if (searchQuery) {
|
|
1027
|
-
filtered = filtered.filter(
|
|
1028
|
-
(doc) => {
|
|
1029
|
-
var _a, _b;
|
|
1030
|
-
return ((_a = doc.title) == null ? void 0 : _a.toLowerCase().includes(searchQuery.toLowerCase())) || ((_b = doc._id) == null ? void 0 : _b.toLowerCase().includes(searchQuery.toLowerCase()));
|
|
1762
|
+
if (filterStatus !== "all") {
|
|
1763
|
+
filtered = filtered.filter((doc) => doc.health.status === filterStatus);
|
|
1031
1764
|
}
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
licenseStatus === "invalid" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UpgradeContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UpgradeBox, { children: licenseKey ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1067
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(UpgradeLock, { children: "\u274C" }),
|
|
1068
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(UpgradeTitle, { children: "Invalid License Key" }),
|
|
1069
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(UpgradeText, { children: "The license key you provided is invalid or has been revoked. Please check your key and update it in the plugin config." }),
|
|
1070
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(UpgradeCode, { children: `seofields({
|
|
1765
|
+
if (filterType !== "all") {
|
|
1766
|
+
filtered = filtered.filter((doc) => doc._type === filterType);
|
|
1767
|
+
}
|
|
1768
|
+
const sorted = [...filtered].sort((a, b) => {
|
|
1769
|
+
if (sortBy === "score") {
|
|
1770
|
+
return b.health.score - a.health.score;
|
|
1771
|
+
}
|
|
1772
|
+
return (a.title || "").localeCompare(b.title || "");
|
|
1773
|
+
});
|
|
1774
|
+
return sorted;
|
|
1775
|
+
}, [documents, searchQuery, filterStatus, filterType, sortBy]);
|
|
1776
|
+
const stats = (0, import_react13.useMemo)(() => {
|
|
1777
|
+
const total = documents.length;
|
|
1778
|
+
const excellent = documents.filter((d) => d.health.score >= 80).length;
|
|
1779
|
+
const good = documents.filter((d) => d.health.score >= 60 && d.health.score < 80).length;
|
|
1780
|
+
const fair = documents.filter((d) => d.health.score >= 40 && d.health.score < 60).length;
|
|
1781
|
+
const poor = documents.filter((d) => d.health.score > 0 && d.health.score < 40).length;
|
|
1782
|
+
const missing = documents.filter((d) => d.health.score === 0).length;
|
|
1783
|
+
const avgScore = total > 0 ? Math.round(documents.reduce((sum, d) => sum + d.health.score, 0) / total) : 0;
|
|
1784
|
+
return { total, excellent, good, fair, poor, missing, avgScore };
|
|
1785
|
+
}, [documents]);
|
|
1786
|
+
const handleMouseLeave = (0, import_react13.useCallback)(() => {
|
|
1787
|
+
setActivePopover(null);
|
|
1788
|
+
}, []);
|
|
1789
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DashboardContainer, { children: [
|
|
1790
|
+
licenseStatus === "loading" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(LoadingState, { style: { padding: "80px 24px" }, children: [
|
|
1791
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Spinner, {}),
|
|
1792
|
+
loadingLicense != null ? loadingLicense : "Verifying license\u2026"
|
|
1793
|
+
] }),
|
|
1794
|
+
licenseStatus === "invalid" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(UpgradeContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(UpgradeBox, { children: licenseKey ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
1795
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(UpgradeLock, { children: "\u274C" }),
|
|
1796
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(UpgradeTitle, { children: "Invalid License Key" }),
|
|
1797
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(UpgradeText, { children: "The license key you provided is invalid or has been revoked. Please check your key and update it in the plugin config." }),
|
|
1798
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(UpgradeCode, { children: `seofields({
|
|
1071
1799
|
healthDashboard: {
|
|
1072
1800
|
licenseKey: 'YOUR_LICENSE_KEY', // \u2190 replace with a valid key
|
|
1073
1801
|
},
|
|
1074
1802
|
})` }),
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1803
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1804
|
+
UpgradeButton,
|
|
1805
|
+
{
|
|
1806
|
+
href: "https://sanity-plugin-seofields.thehardik.in",
|
|
1807
|
+
target: "_blank",
|
|
1808
|
+
rel: "noopener noreferrer",
|
|
1809
|
+
children: "Get a New License Key \u2192"
|
|
1810
|
+
}
|
|
1811
|
+
),
|
|
1812
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("br", {}),
|
|
1813
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ReloadButton, { onClick: () => validateLicense(true), children: "Click here If You Just Updated Your Key" })
|
|
1814
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
1815
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(UpgradeLock, { children: "\u{1F512}" }),
|
|
1816
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(UpgradeTitle, { children: "SEO Health Dashboard" }),
|
|
1817
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(UpgradeText, { children: "This feature requires a license key. Add your key to the plugin config to unlock the full dashboard." }),
|
|
1818
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(UpgradeCode, { children: `// sanity.config.ts
|
|
1091
1819
|
import { seofields } from 'sanity-plugin-seofields'
|
|
1092
1820
|
|
|
1093
1821
|
export default defineConfig({
|
|
@@ -1099,708 +1827,333 @@ export default defineConfig({
|
|
|
1099
1827
|
}),
|
|
1100
1828
|
],
|
|
1101
1829
|
})` }),
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
{
|
|
1105
|
-
href: "https://sanity-plugin-seofields.thehardik.in",
|
|
1106
|
-
target: "_blank",
|
|
1107
|
-
rel: "noopener noreferrer",
|
|
1108
|
-
children: "Get a License Key \u2192"
|
|
1109
|
-
}
|
|
1110
|
-
)
|
|
1111
|
-
] }) }) }),
|
|
1112
|
-
licenseStatus === "valid" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1113
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(PageHeader, { children: [
|
|
1114
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(PageTitle, { children: [
|
|
1115
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
1116
|
-
icon,
|
|
1117
|
-
" ",
|
|
1118
|
-
title
|
|
1119
|
-
] }),
|
|
1120
|
-
previewMode && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PreviewBadge, { children: "Preview Mode" })
|
|
1121
|
-
] }),
|
|
1122
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PageSubtitle, { children: description })
|
|
1123
|
-
] }),
|
|
1124
|
-
!loading && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(StatsGrid, { children: [
|
|
1125
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(StatCard, { children: [
|
|
1126
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatLabel, { children: "Total Docs" }),
|
|
1127
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatValue, { children: stats.total })
|
|
1128
|
-
] }),
|
|
1129
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(StatCard, { children: [
|
|
1130
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatLabel, { children: "Avg Score" }),
|
|
1131
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(StatValue, { children: [
|
|
1132
|
-
stats.avgScore,
|
|
1133
|
-
"%"
|
|
1134
|
-
] })
|
|
1135
|
-
] }),
|
|
1136
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(StatCard, { $accent: "#10b981", children: [
|
|
1137
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatLabel, { children: "Excellent (80+)" }),
|
|
1138
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatValue, { children: stats.excellent })
|
|
1139
|
-
] }),
|
|
1140
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(StatCard, { $accent: "#f59e0b", children: [
|
|
1141
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatLabel, { children: "Good (60\u201379)" }),
|
|
1142
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatValue, { children: stats.good })
|
|
1143
|
-
] }),
|
|
1144
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(StatCard, { $accent: "#f97316", children: [
|
|
1145
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatLabel, { children: "Fair (40\u201359)" }),
|
|
1146
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatValue, { children: stats.fair })
|
|
1147
|
-
] }),
|
|
1148
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(StatCard, { $accent: "#ef4444", children: [
|
|
1149
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatLabel, { children: "Poor / Missing" }),
|
|
1150
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatValue, { children: stats.poor + stats.missing })
|
|
1151
|
-
] })
|
|
1152
|
-
] }),
|
|
1153
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ControlsBar, { children: [
|
|
1154
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SearchWrapper, { children: [
|
|
1155
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIconSvg, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1156
|
-
"path",
|
|
1830
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1831
|
+
UpgradeButton,
|
|
1157
1832
|
{
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
) }) }),
|
|
1163
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1164
|
-
SearchInput,
|
|
1165
|
-
{
|
|
1166
|
-
placeholder: "Search documents...",
|
|
1167
|
-
value: searchQuery,
|
|
1168
|
-
onChange: (e) => setSearchQuery(e.currentTarget.value)
|
|
1833
|
+
href: "https://sanity-plugin-seofields.thehardik.in",
|
|
1834
|
+
target: "_blank",
|
|
1835
|
+
rel: "noopener noreferrer",
|
|
1836
|
+
children: "Get a License Key \u2192"
|
|
1169
1837
|
}
|
|
1170
1838
|
)
|
|
1171
|
-
] }),
|
|
1172
|
-
/* @__PURE__ */ (0,
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
/* @__PURE__ */ (0,
|
|
1184
|
-
]
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
/* @__PURE__ */ (0,
|
|
1217
|
-
/* @__PURE__ */ (0,
|
|
1218
|
-
|
|
1219
|
-
/* @__PURE__ */ (0,
|
|
1220
|
-
|
|
1839
|
+
] }) }) }),
|
|
1840
|
+
licenseStatus === "valid" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
1841
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(PageHeader, { children: [
|
|
1842
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
|
|
1843
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(PageTitle, { children: [
|
|
1844
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { children: [
|
|
1845
|
+
icon,
|
|
1846
|
+
" ",
|
|
1847
|
+
title
|
|
1848
|
+
] }),
|
|
1849
|
+
previewMode && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PreviewBadge, { children: "Preview Mode" })
|
|
1850
|
+
] }),
|
|
1851
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PageSubtitle, { children: description })
|
|
1852
|
+
] }),
|
|
1853
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
1854
|
+
DashboardRefreshButton,
|
|
1855
|
+
{
|
|
1856
|
+
onClick: handleRefresh,
|
|
1857
|
+
disabled: loading || isRefreshing,
|
|
1858
|
+
$spinning: isRefreshing,
|
|
1859
|
+
title: "Refresh documents",
|
|
1860
|
+
children: [
|
|
1861
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
1862
|
+
"svg",
|
|
1863
|
+
{
|
|
1864
|
+
width: "14",
|
|
1865
|
+
height: "14",
|
|
1866
|
+
viewBox: "0 0 24 24",
|
|
1867
|
+
fill: "none",
|
|
1868
|
+
stroke: "currentColor",
|
|
1869
|
+
strokeWidth: "2.2",
|
|
1870
|
+
strokeLinecap: "round",
|
|
1871
|
+
strokeLinejoin: "round",
|
|
1872
|
+
children: [
|
|
1873
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("polyline", { points: "23 4 23 10 17 10" }),
|
|
1874
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("polyline", { points: "1 20 1 14 7 14" }),
|
|
1875
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" })
|
|
1876
|
+
]
|
|
1877
|
+
}
|
|
1878
|
+
),
|
|
1879
|
+
"Refresh"
|
|
1880
|
+
]
|
|
1881
|
+
}
|
|
1882
|
+
)
|
|
1883
|
+
] }),
|
|
1884
|
+
deprecationGroups.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DeprecationBanner, { children: [
|
|
1885
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("strong", { children: "\u26A0\uFE0F Deprecated config keys detected:" }),
|
|
1886
|
+
" ",
|
|
1887
|
+
deprecationGroups.map((group, gi) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { children: [
|
|
1888
|
+
group.keys.map((w, i) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { children: [
|
|
1889
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("code", { style: { background: "#fef9c3", padding: "1px 4px", borderRadius: 3 }, children: w.split("\u2192")[0].trim() }),
|
|
1890
|
+
" \u2192 ",
|
|
1891
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("code", { style: { background: "#dcfce7", padding: "1px 4px", borderRadius: 3 }, children: w.split("\u2192")[1].trim() }),
|
|
1892
|
+
i < group.keys.length - 1 ? " \xB7 " : ""
|
|
1893
|
+
] }, w)),
|
|
1894
|
+
" ",
|
|
1895
|
+
"(",
|
|
1896
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
1897
|
+
DeprecationBannerLink,
|
|
1898
|
+
{
|
|
1899
|
+
href: group.changelogUrl,
|
|
1900
|
+
target: "_blank",
|
|
1901
|
+
rel: "noopener noreferrer",
|
|
1902
|
+
children: [
|
|
1903
|
+
group.version,
|
|
1904
|
+
" changelog"
|
|
1905
|
+
]
|
|
1906
|
+
}
|
|
1907
|
+
),
|
|
1908
|
+
")",
|
|
1909
|
+
gi < deprecationGroups.length - 1 ? " \xB7 " : ""
|
|
1910
|
+
] }, group.version)),
|
|
1911
|
+
" ",
|
|
1912
|
+
"\u2014 Please update your config."
|
|
1221
1913
|
] }),
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
/* @__PURE__ */ (0,
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
children: doc.title || "Untitled"
|
|
1232
|
-
}
|
|
1233
|
-
),
|
|
1234
|
-
showDocumentId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DocId, { children: doc._id }),
|
|
1235
|
-
docBadge && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1236
|
-
DocBadgeRenderer,
|
|
1237
|
-
{
|
|
1238
|
-
doc,
|
|
1239
|
-
docBadge
|
|
1240
|
-
}
|
|
1241
|
-
)
|
|
1242
|
-
] }) }) }),
|
|
1243
|
-
showTypeColumn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ColType, { children: typeColumnMode === "text" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TypeText, { children: resolveTypeLabel(doc._type, typeLabels) }) : (() => {
|
|
1244
|
-
const typeColor = getTypeColor(doc._type);
|
|
1245
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TypeBadge, { $bgColor: typeColor.bg, $textColor: typeColor.text, children: resolveTypeLabel(doc._type, typeLabels) });
|
|
1246
|
-
})() }),
|
|
1247
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ColScore, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ScoreBadge, { $score: doc.health.score, children: [
|
|
1248
|
-
doc.health.score,
|
|
1914
|
+
!loading && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(StatsGrid, { children: [
|
|
1915
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(StatCard, { children: [
|
|
1916
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StatLabel, { children: "Total Docs" }),
|
|
1917
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StatValue, { children: stats.total })
|
|
1918
|
+
] }),
|
|
1919
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(StatCard, { children: [
|
|
1920
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StatLabel, { children: "Avg Score" }),
|
|
1921
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(StatValue, { children: [
|
|
1922
|
+
stats.avgScore,
|
|
1249
1923
|
"%"
|
|
1250
|
-
] }) }),
|
|
1251
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ColIssues, { children: [
|
|
1252
|
-
doc.health.issues.slice(0, 2).map((issue) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(IssueTag, { children: [
|
|
1253
|
-
"\u2022 ",
|
|
1254
|
-
issue
|
|
1255
|
-
] }, `issue-${doc._id}-${issue}`)),
|
|
1256
|
-
doc.health.issues.length > 2 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1257
|
-
MoreIssuesWrapper,
|
|
1258
|
-
{
|
|
1259
|
-
onMouseEnter: function(e) {
|
|
1260
|
-
handleMouseEnterIssues(
|
|
1261
|
-
e.currentTarget,
|
|
1262
|
-
doc.health.issues
|
|
1263
|
-
);
|
|
1264
|
-
},
|
|
1265
|
-
onMouseLeave: handleMouseLeave,
|
|
1266
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(MoreIssues, { children: [
|
|
1267
|
-
"+",
|
|
1268
|
-
doc.health.issues.length - 2,
|
|
1269
|
-
" more issues"
|
|
1270
|
-
] })
|
|
1271
|
-
}
|
|
1272
|
-
)
|
|
1273
1924
|
] })
|
|
1274
|
-
] },
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1925
|
+
] }),
|
|
1926
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(StatCard, { $accent: "#10b981", children: [
|
|
1927
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StatLabel, { children: "Excellent (80+)" }),
|
|
1928
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StatValue, { children: stats.excellent })
|
|
1929
|
+
] }),
|
|
1930
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(StatCard, { $accent: "#f59e0b", children: [
|
|
1931
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StatLabel, { children: "Good (60\u201379)" }),
|
|
1932
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StatValue, { children: stats.good })
|
|
1933
|
+
] }),
|
|
1934
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(StatCard, { $accent: "#f97316", children: [
|
|
1935
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StatLabel, { children: "Fair (40\u201359)" }),
|
|
1936
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StatValue, { children: stats.fair })
|
|
1937
|
+
] }),
|
|
1938
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(StatCard, { $accent: "#ef4444", children: [
|
|
1939
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StatLabel, { children: "Poor / Missing" }),
|
|
1940
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StatValue, { children: stats.poor + stats.missing })
|
|
1941
|
+
] })
|
|
1942
|
+
] }),
|
|
1943
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(ControlsBar, { children: [
|
|
1944
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(SearchWrapper, { children: [
|
|
1945
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SearchIconSvg, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1946
|
+
"path",
|
|
1947
|
+
{
|
|
1948
|
+
fillRule: "evenodd",
|
|
1949
|
+
d: "M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z",
|
|
1950
|
+
clipRule: "evenodd"
|
|
1951
|
+
}
|
|
1952
|
+
) }) }),
|
|
1953
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1954
|
+
SearchInput,
|
|
1955
|
+
{
|
|
1956
|
+
placeholder: "Search documents...",
|
|
1957
|
+
value: searchQuery,
|
|
1958
|
+
onChange: (e) => setSearchQuery(e.currentTarget.value)
|
|
1959
|
+
}
|
|
1960
|
+
)
|
|
1961
|
+
] }),
|
|
1962
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
1963
|
+
StyledSelect,
|
|
1964
|
+
{
|
|
1965
|
+
value: filterStatus,
|
|
1966
|
+
onChange: (e) => setFilterStatus(e.currentTarget.value),
|
|
1967
|
+
children: [
|
|
1968
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: "all", children: "All Status" }),
|
|
1969
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: "excellent", children: "Excellent" }),
|
|
1970
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: "good", children: "Good" }),
|
|
1971
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: "fair", children: "Fair" }),
|
|
1972
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: "poor", children: "Poor" }),
|
|
1973
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: "missing", children: "Missing" })
|
|
1974
|
+
]
|
|
1975
|
+
}
|
|
1976
|
+
),
|
|
1977
|
+
uniqueDocumentTypes.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
1978
|
+
StyledSelect,
|
|
1979
|
+
{
|
|
1980
|
+
value: filterType,
|
|
1981
|
+
onChange: (e) => setFilterType(e.currentTarget.value),
|
|
1982
|
+
children: [
|
|
1983
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: "all", children: "All Types" }),
|
|
1984
|
+
uniqueDocumentTypes.map((type) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: type, children: resolveTypeLabel(type, resolvedTypeLabels) }, type))
|
|
1985
|
+
]
|
|
1986
|
+
}
|
|
1987
|
+
),
|
|
1988
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
1989
|
+
StyledSelect,
|
|
1990
|
+
{
|
|
1991
|
+
value: sortBy,
|
|
1992
|
+
onChange: (e) => setSortBy(e.currentTarget.value),
|
|
1993
|
+
children: [
|
|
1994
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: "score", children: "Sort by Score" }),
|
|
1995
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: "title", children: "Sort by Title" })
|
|
1996
|
+
]
|
|
1997
|
+
}
|
|
1998
|
+
)
|
|
1999
|
+
] }),
|
|
2000
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(TableCard, { children: [
|
|
2001
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(LoadingState, { children: [
|
|
2002
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Spinner, {}),
|
|
2003
|
+
loadingDocuments != null ? loadingDocuments : "Loading documents\u2026"
|
|
2004
|
+
] }),
|
|
2005
|
+
!loading && (filteredAndSortedDocs.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(EmptyState, { children: noDocuments != null ? noDocuments : "No documents found" }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
2006
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(TableHeader, { children: [
|
|
2007
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ColTitle, { children: "Title" }),
|
|
2008
|
+
showTypeColumn && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ColType, { children: "Type" }),
|
|
2009
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ColScore, { children: "Score" }),
|
|
2010
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ColIssues, { children: "Top Issues" })
|
|
2011
|
+
] }),
|
|
2012
|
+
filteredAndSortedDocs.map((doc) => {
|
|
2013
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(TableRow, { children: [
|
|
2014
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ColTitle, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TitleWrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(TitleCell, { children: [
|
|
2015
|
+
doc.title !== null && typeof doc.title !== "string" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(NonStringTitleWarning, { title: "title is not a string \u2014 use pt::text(title) in your query.groq projection to convert Portable Text to a plain string", children: "\u26A0 title is not a string \u2014 use pt::text(title) in query.groq" }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_jsx_runtime13.Fragment, { children: openInPane ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DocTitleAnchorPane, { id: doc._id, type: doc._type, children: typeof doc.title === "string" ? doc.title || "Untitled" : "Untitled" }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2016
|
+
DocTitleAnchor,
|
|
2017
|
+
{
|
|
2018
|
+
id: doc._id,
|
|
2019
|
+
type: doc._type,
|
|
2020
|
+
structureTool,
|
|
2021
|
+
children: typeof doc.title === "string" ? doc.title || "Untitled" : "Untitled"
|
|
2022
|
+
}
|
|
2023
|
+
) }),
|
|
2024
|
+
showDocumentId && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DocId, { children: doc._id }),
|
|
2025
|
+
resolvedDocBadge && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2026
|
+
DocBadgeRenderer,
|
|
2027
|
+
{
|
|
2028
|
+
doc,
|
|
2029
|
+
docBadge: resolvedDocBadge
|
|
2030
|
+
}
|
|
2031
|
+
)
|
|
2032
|
+
] }) }) }),
|
|
2033
|
+
showTypeColumn && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ColType, { children: typeColumnMode === "text" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TypeText, { children: resolveTypeLabel(doc._type, resolvedTypeLabels) }) : (() => {
|
|
2034
|
+
const typeColor = getTypeColor(doc._type);
|
|
2035
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TypeBadge, { $bgColor: typeColor.bg, $textColor: typeColor.text, children: resolveTypeLabel(doc._type, resolvedTypeLabels) });
|
|
2036
|
+
})() }),
|
|
2037
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ColScore, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(ScoreBadge, { $score: doc.health.score, children: [
|
|
2038
|
+
doc.health.score,
|
|
2039
|
+
"%"
|
|
2040
|
+
] }) }),
|
|
2041
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(ColIssues, { children: [
|
|
2042
|
+
doc.health.issues.slice(0, 2).map((issue) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(IssueTag, { children: [
|
|
2043
|
+
"\u2022 ",
|
|
2044
|
+
issue
|
|
2045
|
+
] }, `issue-${doc._id}-${issue}`)),
|
|
2046
|
+
doc.health.issues.length > 2 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2047
|
+
MoreIssuesWrapper,
|
|
2048
|
+
{
|
|
2049
|
+
onMouseEnter: function(e) {
|
|
2050
|
+
handleMouseEnterIssues(
|
|
2051
|
+
e.currentTarget,
|
|
2052
|
+
doc.health.issues.slice(2)
|
|
2053
|
+
);
|
|
2054
|
+
},
|
|
2055
|
+
onMouseLeave: handleMouseLeave,
|
|
2056
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(MoreIssues, { children: [
|
|
2057
|
+
"+",
|
|
2058
|
+
doc.health.issues.length - 2,
|
|
2059
|
+
" more issues"
|
|
2060
|
+
] })
|
|
2061
|
+
}
|
|
2062
|
+
)
|
|
2063
|
+
] })
|
|
2064
|
+
] }, doc._id);
|
|
2065
|
+
})
|
|
2066
|
+
] }))
|
|
2067
|
+
] }),
|
|
2068
|
+
activePopover && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2069
|
+
IssuesPopover,
|
|
2070
|
+
{
|
|
2071
|
+
style: {
|
|
2072
|
+
top: activePopover.top,
|
|
2073
|
+
left: activePopover.left,
|
|
2074
|
+
transform: "translateY(calc(-100% - 10px))"
|
|
2075
|
+
},
|
|
2076
|
+
children: activePopover.issues.map((issue) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(PopoverIssueItem, { children: [
|
|
2077
|
+
"\u26A0\uFE0F ",
|
|
2078
|
+
issue
|
|
2079
|
+
] }, issue))
|
|
2080
|
+
}
|
|
2081
|
+
),
|
|
2082
|
+
" "
|
|
2083
|
+
] }),
|
|
2084
|
+
" "
|
|
2085
|
+
] });
|
|
2086
|
+
};
|
|
2087
|
+
SeoHealthDashboard_default = SeoHealthDashboard;
|
|
2088
|
+
}
|
|
2089
|
+
});
|
|
1298
2090
|
|
|
1299
2091
|
// src/components/SeoHealthTool.tsx
|
|
1300
|
-
var
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
};
|
|
1304
|
-
var
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
// src/utils/seoUtils.ts
|
|
1315
|
-
var stopWords = ["the", "a", "an", "and", "or", "but"];
|
|
1316
|
-
var hasMatchingKeyword = (title, keywordList) => {
|
|
1317
|
-
if (!title || keywordList.length === 0) return false;
|
|
1318
|
-
const lowerTitle = title.toLowerCase();
|
|
1319
|
-
return keywordList.some((keyword) => keyword && lowerTitle.includes(keyword.toLowerCase()));
|
|
1320
|
-
};
|
|
1321
|
-
var hasKeywordOveruse = (title, keywordList, maxOccurrences = 3) => {
|
|
1322
|
-
if (!title || keywordList.length === 0) return false;
|
|
1323
|
-
const lowerTitle = title.toLowerCase();
|
|
1324
|
-
return keywordList.some((keyword) => {
|
|
1325
|
-
if (!keyword) return false;
|
|
1326
|
-
const matches = lowerTitle.match(new RegExp(keyword.toLowerCase(), "g"));
|
|
1327
|
-
return matches ? matches.length > maxOccurrences : false;
|
|
1328
|
-
});
|
|
1329
|
-
};
|
|
1330
|
-
var startsWithStopWord = (title) => {
|
|
1331
|
-
if (!title) return false;
|
|
1332
|
-
const firstWord = title.trim().split(" ")[0].toLowerCase();
|
|
1333
|
-
return stopWords.includes(firstWord);
|
|
1334
|
-
};
|
|
1335
|
-
var truncate = (text, maxLength) => text.length > maxLength ? `${text.slice(0, maxLength)}\u2026` : text;
|
|
1336
|
-
var hasExcessivePunctuation = (title) => /[!@#$%^&*]{2,}/.test(title);
|
|
1337
|
-
var getMetaTitleValidationMessages = (title, keywords, isParentseoField) => {
|
|
1338
|
-
const feedback = [];
|
|
1339
|
-
const minChar = 50;
|
|
1340
|
-
const maxChar = 60;
|
|
1341
|
-
const charCount = (title == null ? void 0 : title.length) || 0;
|
|
1342
|
-
if (!(title == null ? void 0 : title.trim())) {
|
|
1343
|
-
feedback.push({ text: "Meta Title is empty. Add content to improve SEO.", color: "red" });
|
|
1344
|
-
return feedback;
|
|
1345
|
-
}
|
|
1346
|
-
if (charCount < minChar)
|
|
1347
|
-
feedback.push({
|
|
1348
|
-
text: `Title is ${charCount} characters \u2014 below recommended ${minChar}.`,
|
|
1349
|
-
color: "orange"
|
|
1350
|
-
});
|
|
1351
|
-
else if (charCount > maxChar)
|
|
1352
|
-
feedback.push({
|
|
1353
|
-
text: `Title is ${charCount} characters \u2014 exceeds recommended ${maxChar}.`,
|
|
1354
|
-
color: "red"
|
|
1355
|
-
});
|
|
1356
|
-
else feedback.push({ text: `Title length (${charCount}) looks good for SEO.`, color: "green" });
|
|
1357
|
-
if (isParentseoField) {
|
|
1358
|
-
if (keywords.length > 0) {
|
|
1359
|
-
const hasKeyword = hasMatchingKeyword(title, keywords);
|
|
1360
|
-
feedback.push({
|
|
1361
|
-
text: hasKeyword ? "Keyword found in title \u2014 good job!" : "Keywords defined but missing in title.",
|
|
1362
|
-
color: hasKeyword ? "green" : "red"
|
|
1363
|
-
});
|
|
1364
|
-
if (hasKeywordOveruse(title, keywords)) {
|
|
1365
|
-
feedback.push({
|
|
1366
|
-
text: "Keyword appears too many times \u2014 avoid keyword stuffing.",
|
|
1367
|
-
color: "orange"
|
|
1368
|
-
});
|
|
1369
|
-
}
|
|
1370
|
-
} else {
|
|
1371
|
-
feedback.push({
|
|
1372
|
-
text: "No keywords defined. Consider adding relevant keywords.",
|
|
1373
|
-
color: "orange"
|
|
1374
|
-
});
|
|
1375
|
-
}
|
|
1376
|
-
}
|
|
1377
|
-
if (startsWithStopWord(title))
|
|
1378
|
-
feedback.push({ text: "Title starts with a stop word \u2014 consider rephrasing.", color: "orange" });
|
|
1379
|
-
if (hasExcessivePunctuation(title))
|
|
1380
|
-
feedback.push({ text: "Title contains excessive punctuation \u2014 simplify it.", color: "orange" });
|
|
1381
|
-
return feedback;
|
|
1382
|
-
};
|
|
1383
|
-
var getMetaDescriptionValidationMessages = (description, keywords, isParentseoField) => {
|
|
1384
|
-
const feedback = [];
|
|
1385
|
-
const minChar = 120;
|
|
1386
|
-
const maxChar = 160;
|
|
1387
|
-
const charCount = (description == null ? void 0 : description.length) || 0;
|
|
1388
|
-
if (!(description == null ? void 0 : description.trim())) {
|
|
1389
|
-
feedback.push({ text: "Meta description is empty. Add content to improve SEO.", color: "red" });
|
|
1390
|
-
return feedback;
|
|
1391
|
-
}
|
|
1392
|
-
if (charCount < minChar)
|
|
1393
|
-
feedback.push({
|
|
1394
|
-
text: `Description is ${charCount} chars \u2014 below recommended ${minChar}.`,
|
|
1395
|
-
color: "orange"
|
|
1396
|
-
});
|
|
1397
|
-
else if (charCount > maxChar)
|
|
1398
|
-
feedback.push({
|
|
1399
|
-
text: `Description is ${charCount} chars \u2014 exceeds recommended ${maxChar}.`,
|
|
1400
|
-
color: "red"
|
|
1401
|
-
});
|
|
1402
|
-
else
|
|
1403
|
-
feedback.push({ text: `Description length (${charCount}) looks good for SEO.`, color: "green" });
|
|
1404
|
-
if (isParentseoField) {
|
|
1405
|
-
if (keywords.length > 0) {
|
|
1406
|
-
const hasKeyword = hasMatchingKeyword(description, keywords);
|
|
1407
|
-
feedback.push({
|
|
1408
|
-
text: hasKeyword ? "Keyword found in description \u2014 good job!" : "Keywords defined but missing in description.",
|
|
1409
|
-
color: hasKeyword ? "green" : "red"
|
|
1410
|
-
});
|
|
1411
|
-
if (hasKeywordOveruse(description, keywords)) {
|
|
1412
|
-
feedback.push({
|
|
1413
|
-
text: "Keyword appears too many times \u2014 avoid keyword stuffing.",
|
|
1414
|
-
color: "orange"
|
|
1415
|
-
});
|
|
1416
|
-
}
|
|
1417
|
-
} else {
|
|
1418
|
-
feedback.push({
|
|
1419
|
-
text: "No keywords defined. Consider adding relevant keywords.",
|
|
1420
|
-
color: "orange"
|
|
1421
|
-
});
|
|
1422
|
-
}
|
|
1423
|
-
}
|
|
1424
|
-
if (startsWithStopWord(description))
|
|
1425
|
-
feedback.push({
|
|
1426
|
-
text: "Description starts with a stop word \u2014 consider rephrasing.",
|
|
1427
|
-
color: "orange"
|
|
1428
|
-
});
|
|
1429
|
-
if (hasExcessivePunctuation(description))
|
|
1430
|
-
feedback.push({
|
|
1431
|
-
text: "Description contains excessive punctuation \u2014 simplify it.",
|
|
1432
|
-
color: "orange"
|
|
1433
|
-
});
|
|
1434
|
-
return feedback;
|
|
1435
|
-
};
|
|
1436
|
-
var getOgTitleValidation = (title, keywords = [], isParentseoField) => {
|
|
1437
|
-
const feedback = [];
|
|
1438
|
-
const min = 40;
|
|
1439
|
-
const max = 60;
|
|
1440
|
-
const count = (title == null ? void 0 : title.length) || 0;
|
|
1441
|
-
if (!(title == null ? void 0 : title.trim())) {
|
|
1442
|
-
feedback.push({ text: "OG Title is empty. Add content for better social preview.", color: "red" });
|
|
1443
|
-
return feedback;
|
|
1444
|
-
}
|
|
1445
|
-
if (count < min)
|
|
1446
|
-
feedback.push({
|
|
1447
|
-
text: `OG Title is ${count} chars \u2014 shorter than recommended ${min}.`,
|
|
1448
|
-
color: "orange"
|
|
1449
|
-
});
|
|
1450
|
-
else if (count > max)
|
|
1451
|
-
feedback.push({ text: `OG Title is ${count} chars \u2014 exceeds recommended ${max}.`, color: "red" });
|
|
1452
|
-
else feedback.push({ text: `OG Title length (${count}) looks good.`, color: "green" });
|
|
1453
|
-
if (isParentseoField) {
|
|
1454
|
-
if (keywords.length > 0) {
|
|
1455
|
-
const hasKeyword = hasMatchingKeyword(title, keywords);
|
|
1456
|
-
feedback.push({
|
|
1457
|
-
text: hasKeyword ? "Keyword found in OG title \u2014 good job!" : "Keywords defined but missing in OG title.",
|
|
1458
|
-
color: hasKeyword ? "green" : "red"
|
|
1459
|
-
});
|
|
1460
|
-
if (hasKeywordOveruse(title, keywords)) {
|
|
1461
|
-
feedback.push({
|
|
1462
|
-
text: "Keyword appears too many times in OG title \u2014 avoid keyword stuffing.",
|
|
1463
|
-
color: "orange"
|
|
1464
|
-
});
|
|
1465
|
-
}
|
|
1466
|
-
} else {
|
|
1467
|
-
feedback.push({
|
|
1468
|
-
text: "No keywords defined. Consider adding relevant keywords.",
|
|
1469
|
-
color: "orange"
|
|
1470
|
-
});
|
|
1471
|
-
}
|
|
1472
|
-
}
|
|
1473
|
-
if (startsWithStopWord(title))
|
|
1474
|
-
feedback.push({
|
|
1475
|
-
text: "OG Title starts with a stop word \u2014 consider rephrasing.",
|
|
1476
|
-
color: "orange"
|
|
1477
|
-
});
|
|
1478
|
-
if (hasExcessivePunctuation(title))
|
|
1479
|
-
feedback.push({ text: "OG Title contains excessive punctuation \u2014 simplify it.", color: "orange" });
|
|
1480
|
-
return feedback;
|
|
1481
|
-
};
|
|
1482
|
-
var getOgDescriptionValidation = (desc, keywords = [], isParentseoField) => {
|
|
1483
|
-
const feedback = [];
|
|
1484
|
-
const min = 90;
|
|
1485
|
-
const max = 120;
|
|
1486
|
-
const count = (desc == null ? void 0 : desc.length) || 0;
|
|
1487
|
-
if (!(desc == null ? void 0 : desc.trim())) {
|
|
1488
|
-
feedback.push({
|
|
1489
|
-
text: "OG Description is empty. Add content for better social preview.",
|
|
1490
|
-
color: "red"
|
|
1491
|
-
});
|
|
1492
|
-
return feedback;
|
|
1493
|
-
}
|
|
1494
|
-
if (count < min)
|
|
1495
|
-
feedback.push({
|
|
1496
|
-
text: `OG Description is ${count} chars \u2014 shorter than recommended ${min}.`,
|
|
1497
|
-
color: "orange"
|
|
1498
|
-
});
|
|
1499
|
-
else if (count > max)
|
|
1500
|
-
feedback.push({
|
|
1501
|
-
text: `OG Description is ${count} chars \u2014 exceeds recommended ${max}.`,
|
|
1502
|
-
color: "red"
|
|
1503
|
-
});
|
|
1504
|
-
else feedback.push({ text: `OG Description length (${count}) looks good.`, color: "green" });
|
|
1505
|
-
if (isParentseoField) {
|
|
1506
|
-
if (keywords.length > 0) {
|
|
1507
|
-
const hasKeyword = hasMatchingKeyword(desc, keywords);
|
|
1508
|
-
feedback.push({
|
|
1509
|
-
text: hasKeyword ? "Keyword found in OG description \u2014 good job!" : "Keywords defined but missing in OG description.",
|
|
1510
|
-
color: hasKeyword ? "green" : "red"
|
|
1511
|
-
});
|
|
1512
|
-
if (hasKeywordOveruse(desc, keywords)) {
|
|
1513
|
-
feedback.push({
|
|
1514
|
-
text: "Keyword appears too many times in OG description \u2014 avoid keyword stuffing.",
|
|
1515
|
-
color: "orange"
|
|
1516
|
-
});
|
|
1517
|
-
}
|
|
1518
|
-
} else {
|
|
1519
|
-
feedback.push({
|
|
1520
|
-
text: "No keywords defined. Consider adding relevant keywords.",
|
|
1521
|
-
color: "orange"
|
|
1522
|
-
});
|
|
1523
|
-
}
|
|
1524
|
-
}
|
|
1525
|
-
if (startsWithStopWord(desc))
|
|
1526
|
-
feedback.push({
|
|
1527
|
-
text: "OG Description starts with a stop word \u2014 consider rephrasing.",
|
|
1528
|
-
color: "orange"
|
|
1529
|
-
});
|
|
1530
|
-
if (hasExcessivePunctuation(desc))
|
|
1531
|
-
feedback.push({
|
|
1532
|
-
text: "OG Description contains excessive punctuation \u2014 simplify it.",
|
|
1533
|
-
color: "orange"
|
|
1534
|
-
});
|
|
1535
|
-
return feedback;
|
|
1536
|
-
};
|
|
1537
|
-
var getTwitterTitleValidation = (title, keywords = [], isParentseoField) => {
|
|
1538
|
-
const feedback = [];
|
|
1539
|
-
const min = 30;
|
|
1540
|
-
const max = 70;
|
|
1541
|
-
const count = (title == null ? void 0 : title.length) || 0;
|
|
1542
|
-
if (!(title == null ? void 0 : title.trim())) {
|
|
1543
|
-
feedback.push({ text: "X Title is empty. Add content for better SEO.", color: "red" });
|
|
1544
|
-
return feedback;
|
|
1545
|
-
}
|
|
1546
|
-
if (count < min)
|
|
1547
|
-
feedback.push({
|
|
1548
|
-
text: `X Title is ${count} chars \u2014 shorter than recommended ${min}.`,
|
|
1549
|
-
color: "orange"
|
|
1550
|
-
});
|
|
1551
|
-
else if (count > max)
|
|
1552
|
-
feedback.push({
|
|
1553
|
-
text: `X Title is ${count} chars \u2014 exceeds recommended ${max}.`,
|
|
1554
|
-
color: "red"
|
|
1555
|
-
});
|
|
1556
|
-
else feedback.push({ text: `X Title length (${count}) looks good.`, color: "green" });
|
|
1557
|
-
if (isParentseoField) {
|
|
1558
|
-
if (keywords.length > 0) {
|
|
1559
|
-
const hasKeyword = hasMatchingKeyword(title, keywords);
|
|
1560
|
-
feedback.push({
|
|
1561
|
-
text: hasKeyword ? "Keyword found in X title \u2014 good job!" : "Keywords defined but missing in X title.",
|
|
1562
|
-
color: hasKeyword ? "green" : "red"
|
|
1563
|
-
});
|
|
1564
|
-
} else {
|
|
1565
|
-
feedback.push({
|
|
1566
|
-
text: "No keywords defined. Consider adding relevant keywords.",
|
|
1567
|
-
color: "orange"
|
|
1568
|
-
});
|
|
1569
|
-
}
|
|
1570
|
-
}
|
|
1571
|
-
if (/[!@#$%^&*]{2,}/.test(title))
|
|
1572
|
-
feedback.push({ text: "X Title has excessive punctuation \u2014 simplify it.", color: "orange" });
|
|
1573
|
-
return feedback;
|
|
1574
|
-
};
|
|
1575
|
-
var getTwitterDescriptionValidation = (desc, keywords = [], isParentseoField) => {
|
|
1576
|
-
const feedback = [];
|
|
1577
|
-
const min = 50;
|
|
1578
|
-
const max = 200;
|
|
1579
|
-
const count = (desc == null ? void 0 : desc.length) || 0;
|
|
1580
|
-
if (!(desc == null ? void 0 : desc.trim())) {
|
|
1581
|
-
feedback.push({ text: "X Description is empty. Add content for better SEO.", color: "red" });
|
|
1582
|
-
return feedback;
|
|
1583
|
-
}
|
|
1584
|
-
if (count < min)
|
|
1585
|
-
feedback.push({
|
|
1586
|
-
text: `X Description is ${count} chars \u2014 shorter than recommended ${min}.`,
|
|
1587
|
-
color: "orange"
|
|
1588
|
-
});
|
|
1589
|
-
else if (count > max)
|
|
1590
|
-
feedback.push({
|
|
1591
|
-
text: `X Description is ${count} chars \u2014 exceeds recommended ${max}.`,
|
|
1592
|
-
color: "red"
|
|
1593
|
-
});
|
|
1594
|
-
else feedback.push({ text: `X Description length (${count}) looks good.`, color: "green" });
|
|
1595
|
-
if (isParentseoField) {
|
|
1596
|
-
if (keywords.length > 0) {
|
|
1597
|
-
const hasKeyword = hasMatchingKeyword(desc, keywords);
|
|
1598
|
-
feedback.push({
|
|
1599
|
-
text: hasKeyword ? "Keyword found in X description \u2014 good job!" : "Keywords defined but missing in X description.",
|
|
1600
|
-
color: hasKeyword ? "green" : "red"
|
|
1601
|
-
});
|
|
1602
|
-
} else {
|
|
1603
|
-
feedback.push({
|
|
1604
|
-
text: "No keywords defined. Consider adding relevant keywords.",
|
|
1605
|
-
color: "orange"
|
|
1606
|
-
});
|
|
1607
|
-
}
|
|
1608
|
-
}
|
|
1609
|
-
if (/[!@#$%^&*]{2,}/.test(desc))
|
|
1610
|
-
feedback.push({
|
|
1611
|
-
text: "X Description has excessive punctuation \u2014 simplify it.",
|
|
1612
|
-
color: "orange"
|
|
1613
|
-
});
|
|
1614
|
-
return feedback;
|
|
1615
|
-
};
|
|
1616
|
-
var isSubImageSet = (subObj) => {
|
|
1617
|
-
var _a;
|
|
1618
|
-
if (!subObj) return false;
|
|
1619
|
-
if (subObj.imageType === "url") return !!((_a = subObj.imageUrl) == null ? void 0 : _a.trim());
|
|
1620
|
-
const img = subObj.image;
|
|
1621
|
-
return !!(img == null ? void 0 : img.asset);
|
|
1622
|
-
};
|
|
1623
|
-
var isMetaImageSet = (seoParent) => {
|
|
1624
|
-
if (!seoParent) return false;
|
|
1625
|
-
const metaImage = seoParent.metaImage;
|
|
1626
|
-
return !!(metaImage == null ? void 0 : metaImage.asset);
|
|
1627
|
-
};
|
|
1628
|
-
var getMetaImageValidation = (hasImage, seoParent) => {
|
|
1629
|
-
const feedback = [];
|
|
1630
|
-
if (!hasImage) {
|
|
1631
|
-
feedback.push({
|
|
1632
|
-
text: "No meta image provided. Adding an image improves click-through rates.",
|
|
1633
|
-
color: "red"
|
|
1634
|
-
});
|
|
1635
|
-
return feedback;
|
|
1636
|
-
}
|
|
1637
|
-
feedback.push({ text: "Meta image is set \u2014 great for SEO and social sharing.", color: "green" });
|
|
1638
|
-
const ogSet = isSubImageSet(seoParent == null ? void 0 : seoParent.openGraph);
|
|
1639
|
-
const twSet = isSubImageSet(seoParent == null ? void 0 : seoParent.twitter);
|
|
1640
|
-
if (!ogSet && !twSet) {
|
|
1641
|
-
feedback.push({
|
|
1642
|
-
text: "OG and Twitter images are missing \u2014 add them for full social coverage.",
|
|
1643
|
-
color: "orange"
|
|
1644
|
-
});
|
|
1645
|
-
} else if (!ogSet) {
|
|
1646
|
-
feedback.push({
|
|
1647
|
-
text: "OG image is missing \u2014 add it for better Facebook/LinkedIn previews.",
|
|
1648
|
-
color: "orange"
|
|
1649
|
-
});
|
|
1650
|
-
} else if (twSet) {
|
|
1651
|
-
feedback.push({ text: "All images set (Meta, OG, Twitter) \u2014 full coverage!", color: "green" });
|
|
1652
|
-
} else {
|
|
1653
|
-
feedback.push({
|
|
1654
|
-
text: "Twitter image is missing \u2014 add it for better X (Twitter) cards.",
|
|
1655
|
-
color: "orange"
|
|
1656
|
-
});
|
|
1657
|
-
}
|
|
1658
|
-
return feedback;
|
|
1659
|
-
};
|
|
1660
|
-
var getOgImageValidation = (hasImage, altText, seoParent) => {
|
|
1661
|
-
const feedback = [];
|
|
1662
|
-
if (!hasImage) {
|
|
1663
|
-
feedback.push({
|
|
1664
|
-
text: "No OG image provided. Social shares will lack a visual preview.",
|
|
1665
|
-
color: "red"
|
|
1666
|
-
});
|
|
1667
|
-
return feedback;
|
|
1668
|
-
}
|
|
1669
|
-
feedback.push({ text: "OG image is set \u2014 good for social sharing.", color: "green" });
|
|
1670
|
-
if (altText == null ? void 0 : altText.trim()) {
|
|
1671
|
-
feedback.push({ text: "Alt text is set \u2014 good for accessibility.", color: "green" });
|
|
1672
|
-
} else {
|
|
1673
|
-
feedback.push({ text: "Consider adding alt text for better accessibility.", color: "orange" });
|
|
1674
|
-
}
|
|
1675
|
-
const metaSet = isMetaImageSet(seoParent);
|
|
1676
|
-
const twSet = isSubImageSet(seoParent == null ? void 0 : seoParent.twitter);
|
|
1677
|
-
if (metaSet && twSet) {
|
|
1678
|
-
feedback.push({ text: "All images set (Meta, OG, Twitter) \u2014 full coverage!", color: "green" });
|
|
1679
|
-
} else {
|
|
1680
|
-
if (!metaSet)
|
|
1681
|
-
feedback.push({
|
|
1682
|
-
text: "Meta image is missing \u2014 add it for search engine results.",
|
|
1683
|
-
color: "orange"
|
|
1684
|
-
});
|
|
1685
|
-
if (!twSet)
|
|
1686
|
-
feedback.push({
|
|
1687
|
-
text: "Twitter image is missing \u2014 add it for X (Twitter) cards.",
|
|
1688
|
-
color: "orange"
|
|
1689
|
-
});
|
|
1690
|
-
}
|
|
1691
|
-
return feedback;
|
|
1692
|
-
};
|
|
1693
|
-
var getOgImageUrlValidation = (imageUrl, seoParent) => {
|
|
1694
|
-
const feedback = [];
|
|
1695
|
-
if (!(imageUrl == null ? void 0 : imageUrl.trim())) {
|
|
1696
|
-
feedback.push({
|
|
1697
|
-
text: "No OG image URL provided. Social shares will lack a visual preview.",
|
|
1698
|
-
color: "red"
|
|
1699
|
-
});
|
|
1700
|
-
return feedback;
|
|
1701
|
-
}
|
|
1702
|
-
feedback.push({ text: "OG image URL is set \u2014 good for social sharing.", color: "green" });
|
|
1703
|
-
const metaSet = isMetaImageSet(seoParent);
|
|
1704
|
-
const twSet = isSubImageSet(seoParent == null ? void 0 : seoParent.twitter);
|
|
1705
|
-
if (metaSet && twSet) {
|
|
1706
|
-
feedback.push({ text: "All images set (Meta, OG, Twitter) \u2014 full coverage!", color: "green" });
|
|
1707
|
-
} else {
|
|
1708
|
-
if (!metaSet)
|
|
1709
|
-
feedback.push({
|
|
1710
|
-
text: "Meta image is missing \u2014 add it for search engine results.",
|
|
1711
|
-
color: "orange"
|
|
1712
|
-
});
|
|
1713
|
-
if (!twSet)
|
|
1714
|
-
feedback.push({
|
|
1715
|
-
text: "Twitter image is missing \u2014 add it for X (Twitter) cards.",
|
|
1716
|
-
color: "orange"
|
|
1717
|
-
});
|
|
1718
|
-
}
|
|
1719
|
-
return feedback;
|
|
1720
|
-
};
|
|
1721
|
-
var getTwitterImageValidation = (hasImage, altText, seoParent) => {
|
|
1722
|
-
const feedback = [];
|
|
1723
|
-
if (!hasImage) {
|
|
1724
|
-
feedback.push({
|
|
1725
|
-
text: "No Twitter image provided. Posts on X will lack a visual.",
|
|
1726
|
-
color: "red"
|
|
1727
|
-
});
|
|
1728
|
-
return feedback;
|
|
1729
|
-
}
|
|
1730
|
-
feedback.push({ text: "Twitter image is set \u2014 good for X sharing.", color: "green" });
|
|
1731
|
-
if (altText == null ? void 0 : altText.trim()) {
|
|
1732
|
-
feedback.push({ text: "Alt text is set \u2014 good for accessibility.", color: "green" });
|
|
1733
|
-
} else {
|
|
1734
|
-
feedback.push({ text: "Consider adding alt text for better accessibility.", color: "orange" });
|
|
1735
|
-
}
|
|
1736
|
-
const metaSet = isMetaImageSet(seoParent);
|
|
1737
|
-
const ogSet = isSubImageSet(seoParent == null ? void 0 : seoParent.openGraph);
|
|
1738
|
-
if (metaSet && ogSet) {
|
|
1739
|
-
feedback.push({ text: "All images set (Meta, OG, Twitter) \u2014 full coverage!", color: "green" });
|
|
1740
|
-
} else {
|
|
1741
|
-
if (!metaSet)
|
|
1742
|
-
feedback.push({
|
|
1743
|
-
text: "Meta image is missing \u2014 add it for search engine results.",
|
|
1744
|
-
color: "orange"
|
|
1745
|
-
});
|
|
1746
|
-
if (!ogSet)
|
|
1747
|
-
feedback.push({
|
|
1748
|
-
text: "OG image is missing \u2014 add it for Facebook/LinkedIn sharing.",
|
|
1749
|
-
color: "orange"
|
|
1750
|
-
});
|
|
1751
|
-
}
|
|
1752
|
-
return feedback;
|
|
1753
|
-
};
|
|
1754
|
-
var getTwitterImageUrlValidation = (imageUrl, seoParent) => {
|
|
1755
|
-
const feedback = [];
|
|
1756
|
-
if (!(imageUrl == null ? void 0 : imageUrl.trim())) {
|
|
1757
|
-
feedback.push({
|
|
1758
|
-
text: "No Twitter image URL provided. Posts on X will lack a visual.",
|
|
1759
|
-
color: "red"
|
|
1760
|
-
});
|
|
1761
|
-
return feedback;
|
|
1762
|
-
}
|
|
1763
|
-
feedback.push({ text: "Twitter image URL is set \u2014 good for X sharing.", color: "green" });
|
|
1764
|
-
const metaSet = isMetaImageSet(seoParent);
|
|
1765
|
-
const ogSet = isSubImageSet(seoParent == null ? void 0 : seoParent.openGraph);
|
|
1766
|
-
if (metaSet && ogSet) {
|
|
1767
|
-
feedback.push({ text: "All images set (Meta, OG, Twitter) \u2014 full coverage!", color: "green" });
|
|
1768
|
-
} else {
|
|
1769
|
-
if (!metaSet)
|
|
1770
|
-
feedback.push({
|
|
1771
|
-
text: "Meta image is missing \u2014 add it for search engine results.",
|
|
1772
|
-
color: "orange"
|
|
1773
|
-
});
|
|
1774
|
-
if (!ogSet)
|
|
1775
|
-
feedback.push({
|
|
1776
|
-
text: "OG image is missing \u2014 add it for Facebook/LinkedIn sharing.",
|
|
1777
|
-
color: "orange"
|
|
1778
|
-
});
|
|
2092
|
+
var SeoHealthTool_exports = {};
|
|
2093
|
+
__export(SeoHealthTool_exports, {
|
|
2094
|
+
default: () => SeoHealthTool_default
|
|
2095
|
+
});
|
|
2096
|
+
var import_jsx_runtime14, SeoHealthTool, SeoHealthTool_default;
|
|
2097
|
+
var init_SeoHealthTool = __esm({
|
|
2098
|
+
"src/components/SeoHealthTool.tsx"() {
|
|
2099
|
+
"use strict";
|
|
2100
|
+
init_SeoHealthDashboard();
|
|
2101
|
+
import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2102
|
+
SeoHealthTool = (props) => {
|
|
2103
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SeoHealthDashboard_default, __spreadValues({}, props));
|
|
2104
|
+
};
|
|
2105
|
+
SeoHealthTool_default = SeoHealthTool;
|
|
1779
2106
|
}
|
|
1780
|
-
|
|
1781
|
-
|
|
2107
|
+
});
|
|
2108
|
+
|
|
2109
|
+
// src/index.ts
|
|
2110
|
+
var src_exports = {};
|
|
2111
|
+
__export(src_exports, {
|
|
2112
|
+
allSchemas: () => types,
|
|
2113
|
+
createSeoHealthPane: () => createSeoHealthPane,
|
|
2114
|
+
default: () => src_default,
|
|
2115
|
+
metaAttributeSchema: () => metaAttribute_default,
|
|
2116
|
+
metaTagSchema: () => metaTag_default,
|
|
2117
|
+
openGraphSchema: () => openGraph,
|
|
2118
|
+
robotsSchema: () => robots_default,
|
|
2119
|
+
seoFieldsSchema: () => seoFieldsSchema,
|
|
2120
|
+
twitterSchema: () => twitter
|
|
2121
|
+
});
|
|
2122
|
+
module.exports = __toCommonJS(src_exports);
|
|
2123
|
+
|
|
2124
|
+
// src/plugin.ts
|
|
2125
|
+
var import_react14 = __toESM(require("react"), 1);
|
|
2126
|
+
var import_sanity20 = require("sanity");
|
|
2127
|
+
|
|
2128
|
+
// src/schemas/index.ts
|
|
2129
|
+
var import_react12 = __toESM(require("react"), 1);
|
|
2130
|
+
var import_sanity15 = require("sanity");
|
|
1782
2131
|
|
|
1783
2132
|
// src/components/meta/MetaDescription.tsx
|
|
1784
|
-
var
|
|
2133
|
+
var import_ui = require("@sanity/ui");
|
|
2134
|
+
var import_react = require("react");
|
|
2135
|
+
var import_sanity = require("sanity");
|
|
2136
|
+
init_seoUtils();
|
|
2137
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
1785
2138
|
var MetaDescription = (props) => {
|
|
1786
2139
|
const { value, renderDefault, path } = props;
|
|
1787
|
-
const parent = (0,
|
|
2140
|
+
const parent = (0, import_sanity.useFormValue)([path[0]]);
|
|
1788
2141
|
const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
|
|
1789
|
-
const keywords = (0,
|
|
1790
|
-
const feedbackItems = (0,
|
|
2142
|
+
const keywords = (0, import_react.useMemo)(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
|
|
2143
|
+
const feedbackItems = (0, import_react.useMemo)(
|
|
1791
2144
|
() => getMetaDescriptionValidationMessages(value || "", keywords, isParentseoField),
|
|
1792
2145
|
[value, keywords, isParentseoField]
|
|
1793
2146
|
);
|
|
1794
|
-
return /* @__PURE__ */ (0,
|
|
2147
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ui.Stack, { space: 3, children: [
|
|
1795
2148
|
renderDefault(props),
|
|
1796
|
-
/* @__PURE__ */ (0,
|
|
1797
|
-
/* @__PURE__ */ (0,
|
|
2149
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
2150
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1798
2151
|
"div",
|
|
1799
2152
|
{
|
|
1800
2153
|
style: { width: 10, height: 10, borderRadius: "50%", backgroundColor: item.color }
|
|
1801
2154
|
}
|
|
1802
2155
|
),
|
|
1803
|
-
/* @__PURE__ */ (0,
|
|
2156
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ui.Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
1804
2157
|
] }, item.text)) })
|
|
1805
2158
|
] });
|
|
1806
2159
|
};
|
|
@@ -1808,21 +2161,22 @@ var MetaDescription_default = MetaDescription;
|
|
|
1808
2161
|
|
|
1809
2162
|
// src/components/meta/MetaImage.tsx
|
|
1810
2163
|
var import_ui2 = require("@sanity/ui");
|
|
1811
|
-
var
|
|
1812
|
-
var
|
|
1813
|
-
|
|
2164
|
+
var import_react2 = require("react");
|
|
2165
|
+
var import_sanity2 = require("sanity");
|
|
2166
|
+
init_seoUtils();
|
|
2167
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1814
2168
|
var MetaImage = (props) => {
|
|
1815
2169
|
const { value, renderDefault, path } = props;
|
|
1816
|
-
const seoParent = (0,
|
|
2170
|
+
const seoParent = (0, import_sanity2.useFormValue)([path[0]]);
|
|
1817
2171
|
const hasImage = !!(value == null ? void 0 : value.asset);
|
|
1818
|
-
const feedbackItems = (0,
|
|
2172
|
+
const feedbackItems = (0, import_react2.useMemo)(
|
|
1819
2173
|
() => getMetaImageValidation(hasImage, seoParent),
|
|
1820
2174
|
[hasImage, seoParent]
|
|
1821
2175
|
);
|
|
1822
|
-
return /* @__PURE__ */ (0,
|
|
2176
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ui2.Stack, { space: 3, children: [
|
|
1823
2177
|
renderDefault(props),
|
|
1824
|
-
/* @__PURE__ */ (0,
|
|
1825
|
-
/* @__PURE__ */ (0,
|
|
2178
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ui2.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
2179
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1826
2180
|
"div",
|
|
1827
2181
|
{
|
|
1828
2182
|
style: {
|
|
@@ -1833,7 +2187,7 @@ var MetaImage = (props) => {
|
|
|
1833
2187
|
}
|
|
1834
2188
|
}
|
|
1835
2189
|
),
|
|
1836
|
-
/* @__PURE__ */ (0,
|
|
2190
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ui2.Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
1837
2191
|
] }, item.text)) })
|
|
1838
2192
|
] });
|
|
1839
2193
|
};
|
|
@@ -1841,22 +2195,23 @@ var MetaImage_default = MetaImage;
|
|
|
1841
2195
|
|
|
1842
2196
|
// src/components/meta/MetaTitle.tsx
|
|
1843
2197
|
var import_ui3 = require("@sanity/ui");
|
|
1844
|
-
var
|
|
1845
|
-
var
|
|
1846
|
-
|
|
2198
|
+
var import_react3 = require("react");
|
|
2199
|
+
var import_sanity3 = require("sanity");
|
|
2200
|
+
init_seoUtils();
|
|
2201
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1847
2202
|
var MetaTitle = (props) => {
|
|
1848
2203
|
const { value, renderDefault, path } = props;
|
|
1849
|
-
const parent = (0,
|
|
2204
|
+
const parent = (0, import_sanity3.useFormValue)([path[0]]);
|
|
1850
2205
|
const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
|
|
1851
|
-
const keywords = (0,
|
|
1852
|
-
const feedbackItems = (0,
|
|
2206
|
+
const keywords = (0, import_react3.useMemo)(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
|
|
2207
|
+
const feedbackItems = (0, import_react3.useMemo)(
|
|
1853
2208
|
() => getMetaTitleValidationMessages(value || "", keywords, isParentseoField),
|
|
1854
2209
|
[value, keywords, isParentseoField]
|
|
1855
2210
|
);
|
|
1856
|
-
return /* @__PURE__ */ (0,
|
|
2211
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui3.Stack, { space: 3, children: [
|
|
1857
2212
|
renderDefault(props),
|
|
1858
|
-
/* @__PURE__ */ (0,
|
|
1859
|
-
/* @__PURE__ */ (0,
|
|
2213
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui3.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
2214
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1860
2215
|
"div",
|
|
1861
2216
|
{
|
|
1862
2217
|
style: {
|
|
@@ -1867,155 +2222,12 @@ var MetaTitle = (props) => {
|
|
|
1867
2222
|
}
|
|
1868
2223
|
}
|
|
1869
2224
|
),
|
|
1870
|
-
/* @__PURE__ */ (0,
|
|
2225
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui3.Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
1871
2226
|
] }, item.text)) })
|
|
1872
2227
|
] });
|
|
1873
2228
|
};
|
|
1874
2229
|
var MetaTitle_default = MetaTitle;
|
|
1875
2230
|
|
|
1876
|
-
// src/components/SeoPreview.tsx
|
|
1877
|
-
var import_ui4 = require("@sanity/ui");
|
|
1878
|
-
var import_sanity5 = require("sanity");
|
|
1879
|
-
var import_styled_components2 = __toESM(require("styled-components"), 1);
|
|
1880
|
-
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1881
|
-
var PreviewContainer = import_styled_components2.default.div`
|
|
1882
|
-
max-width: 600px;
|
|
1883
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
1884
|
-
background: #ffffff;
|
|
1885
|
-
border: 1px solid #dadce0;
|
|
1886
|
-
border-radius: 8px;
|
|
1887
|
-
overflow: hidden;
|
|
1888
|
-
`;
|
|
1889
|
-
var PreviewHeader = import_styled_components2.default.div`
|
|
1890
|
-
background: #f8f9fa;
|
|
1891
|
-
padding: 12px 16px;
|
|
1892
|
-
border-bottom: 1px solid #dadce0;
|
|
1893
|
-
display: flex;
|
|
1894
|
-
align-items: center;
|
|
1895
|
-
justify-content: space-between;
|
|
1896
|
-
gap: 8px;
|
|
1897
|
-
`;
|
|
1898
|
-
var PreviewBody = import_styled_components2.default.div`
|
|
1899
|
-
padding: 16px;
|
|
1900
|
-
`;
|
|
1901
|
-
var SerpUrl = import_styled_components2.default.p`
|
|
1902
|
-
margin: 0 0 4px;
|
|
1903
|
-
color: #006621;
|
|
1904
|
-
font-size: 13px;
|
|
1905
|
-
line-height: 1.4;
|
|
1906
|
-
word-break: break-word;
|
|
1907
|
-
`;
|
|
1908
|
-
var SerpTitle = import_styled_components2.default.h3`
|
|
1909
|
-
margin: 0 0 8px;
|
|
1910
|
-
color: #1a0dab;
|
|
1911
|
-
font-size: 18px;
|
|
1912
|
-
font-weight: 500;
|
|
1913
|
-
line-height: 1.4;
|
|
1914
|
-
word-break: break-word;
|
|
1915
|
-
|
|
1916
|
-
&:hover {
|
|
1917
|
-
text-decoration: underline;
|
|
1918
|
-
}
|
|
1919
|
-
`;
|
|
1920
|
-
var SerpDescription = import_styled_components2.default.p`
|
|
1921
|
-
margin: 0;
|
|
1922
|
-
color: #545454;
|
|
1923
|
-
font-size: 14px;
|
|
1924
|
-
line-height: 1.6;
|
|
1925
|
-
word-break: break-word;
|
|
1926
|
-
display: -webkit-box;
|
|
1927
|
-
-webkit-line-clamp: 2;
|
|
1928
|
-
-webkit-box-orient: vertical;
|
|
1929
|
-
overflow: hidden;
|
|
1930
|
-
`;
|
|
1931
|
-
var LiveIndicator = import_styled_components2.default.span`
|
|
1932
|
-
display: inline-flex;
|
|
1933
|
-
align-items: center;
|
|
1934
|
-
gap: 4px;
|
|
1935
|
-
font-size: 11px;
|
|
1936
|
-
font-weight: 600;
|
|
1937
|
-
text-transform: uppercase;
|
|
1938
|
-
letter-spacing: 0.05em;
|
|
1939
|
-
color: #4f46e5;
|
|
1940
|
-
background: #f0f4ff;
|
|
1941
|
-
padding: 4px 8px;
|
|
1942
|
-
border-radius: 4px;
|
|
1943
|
-
`;
|
|
1944
|
-
var SeoPreview = (props) => {
|
|
1945
|
-
var _a, _b;
|
|
1946
|
-
const { path, schemaType } = props;
|
|
1947
|
-
const { options } = schemaType;
|
|
1948
|
-
const baseUrl = (options == null ? void 0 : options.baseUrl) || "https://www.example.com";
|
|
1949
|
-
const prefixFunction = options == null ? void 0 : options.prefix;
|
|
1950
|
-
const parent = (0, import_sanity5.useFormValue)([path[0]]) || {
|
|
1951
|
-
title: "",
|
|
1952
|
-
description: "",
|
|
1953
|
-
canonicalUrl: ""
|
|
1954
|
-
};
|
|
1955
|
-
const rootDoc = (0, import_sanity5.useFormValue)([]) || {
|
|
1956
|
-
slug: { current: "" }
|
|
1957
|
-
};
|
|
1958
|
-
const slug = ((_a = rootDoc == null ? void 0 : rootDoc.slug) == null ? void 0 : _a.current) || "";
|
|
1959
|
-
const {
|
|
1960
|
-
title,
|
|
1961
|
-
description,
|
|
1962
|
-
canonicalUrl: url
|
|
1963
|
-
} = parent;
|
|
1964
|
-
const base = (_b = url || baseUrl) == null ? void 0 : _b.replace(/\/+$/, "");
|
|
1965
|
-
const slugStr = String(slug || "").replace(/^\/+/, "");
|
|
1966
|
-
const pref = String(
|
|
1967
|
-
prefixFunction ? prefixFunction(rootDoc) : ""
|
|
1968
|
-
).replace(/^\/+|\/+$/g, "");
|
|
1969
|
-
const urlPath = [pref, slugStr].filter(Boolean).join("/");
|
|
1970
|
-
const finalUrl = urlPath ? `${base}/${urlPath}` : base;
|
|
1971
|
-
const domain = (() => {
|
|
1972
|
-
try {
|
|
1973
|
-
const u = new URL(finalUrl || base);
|
|
1974
|
-
return u.hostname;
|
|
1975
|
-
} catch (e) {
|
|
1976
|
-
return "example.com";
|
|
1977
|
-
}
|
|
1978
|
-
})();
|
|
1979
|
-
const urlDisplay = `${domain}${urlPath ? ` \u203A ${urlPath.split("/").slice(-1)[0]}` : ""}`;
|
|
1980
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ui4.Box, { padding: 3, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(PreviewContainer, { children: [
|
|
1981
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(PreviewHeader, { children: [
|
|
1982
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1983
|
-
"span",
|
|
1984
|
-
{
|
|
1985
|
-
style: {
|
|
1986
|
-
fontSize: "11px",
|
|
1987
|
-
color: "#5f6368",
|
|
1988
|
-
textTransform: "uppercase",
|
|
1989
|
-
letterSpacing: "0.05em"
|
|
1990
|
-
},
|
|
1991
|
-
children: "Search Preview"
|
|
1992
|
-
}
|
|
1993
|
-
),
|
|
1994
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(LiveIndicator, { children: [
|
|
1995
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1996
|
-
"span",
|
|
1997
|
-
{
|
|
1998
|
-
style: {
|
|
1999
|
-
width: "4px",
|
|
2000
|
-
height: "4px",
|
|
2001
|
-
borderRadius: "50%",
|
|
2002
|
-
backgroundColor: "#4f46e5",
|
|
2003
|
-
display: "inline-block"
|
|
2004
|
-
}
|
|
2005
|
-
}
|
|
2006
|
-
),
|
|
2007
|
-
"Live"
|
|
2008
|
-
] })
|
|
2009
|
-
] }),
|
|
2010
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(PreviewBody, { children: [
|
|
2011
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SerpUrl, { children: finalUrl ? urlDisplay : "example.com \u203A page-url" }),
|
|
2012
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SerpTitle, { children: title && title.length > 0 ? truncate(title, 60) : "Your SEO Title will appear here" }),
|
|
2013
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SerpDescription, { children: description && description.length > 0 ? truncate(description, 160) : "Your meta description will show up here. Make it compelling!" })
|
|
2014
|
-
] })
|
|
2015
|
-
] }) });
|
|
2016
|
-
};
|
|
2017
|
-
var SeoPreview_default = SeoPreview;
|
|
2018
|
-
|
|
2019
2231
|
// src/utils/fieldsUtils.ts
|
|
2020
2232
|
var DEFAULT_FIELD_INFO = {
|
|
2021
2233
|
title: {
|
|
@@ -2140,26 +2352,27 @@ var isEmpty = (value) => {
|
|
|
2140
2352
|
};
|
|
2141
2353
|
|
|
2142
2354
|
// src/schemas/types/openGraph/index.ts
|
|
2143
|
-
var
|
|
2355
|
+
var import_sanity8 = require("sanity");
|
|
2144
2356
|
|
|
2145
2357
|
// src/components/openGraph/OgDescription.tsx
|
|
2146
|
-
var
|
|
2147
|
-
var
|
|
2148
|
-
var
|
|
2149
|
-
|
|
2358
|
+
var import_ui4 = require("@sanity/ui");
|
|
2359
|
+
var import_react4 = require("react");
|
|
2360
|
+
var import_sanity4 = require("sanity");
|
|
2361
|
+
init_seoUtils();
|
|
2362
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
2150
2363
|
var OgDescription = (props) => {
|
|
2151
2364
|
const { value, renderDefault, path } = props;
|
|
2152
|
-
const parent = (0,
|
|
2365
|
+
const parent = (0, import_sanity4.useFormValue)([path[0]]);
|
|
2153
2366
|
const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
|
|
2154
|
-
const keywords = (0,
|
|
2155
|
-
const feedbackItems = (0,
|
|
2367
|
+
const keywords = (0, import_react4.useMemo)(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
|
|
2368
|
+
const feedbackItems = (0, import_react4.useMemo)(
|
|
2156
2369
|
() => getOgDescriptionValidation(value || "", keywords, isParentseoField),
|
|
2157
2370
|
[value, keywords, isParentseoField]
|
|
2158
2371
|
);
|
|
2159
|
-
return /* @__PURE__ */ (0,
|
|
2372
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ui4.Stack, { space: 3, children: [
|
|
2160
2373
|
renderDefault(props),
|
|
2161
|
-
/* @__PURE__ */ (0,
|
|
2162
|
-
/* @__PURE__ */ (0,
|
|
2374
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui4.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
2375
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2163
2376
|
"div",
|
|
2164
2377
|
{
|
|
2165
2378
|
style: {
|
|
@@ -2170,31 +2383,32 @@ var OgDescription = (props) => {
|
|
|
2170
2383
|
}
|
|
2171
2384
|
}
|
|
2172
2385
|
),
|
|
2173
|
-
/* @__PURE__ */ (0,
|
|
2386
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui4.Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
2174
2387
|
] }, item.text)) })
|
|
2175
2388
|
] });
|
|
2176
2389
|
};
|
|
2177
2390
|
var OgDescription_default = OgDescription;
|
|
2178
2391
|
|
|
2179
2392
|
// src/components/openGraph/OgImage.tsx
|
|
2180
|
-
var
|
|
2181
|
-
var
|
|
2182
|
-
var
|
|
2183
|
-
|
|
2393
|
+
var import_ui5 = require("@sanity/ui");
|
|
2394
|
+
var import_react5 = require("react");
|
|
2395
|
+
var import_sanity5 = require("sanity");
|
|
2396
|
+
init_seoUtils();
|
|
2397
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
2184
2398
|
var OgImage = (props) => {
|
|
2185
2399
|
const { value, renderDefault, path } = props;
|
|
2186
|
-
const seoParent = (0,
|
|
2400
|
+
const seoParent = (0, import_sanity5.useFormValue)([path[0]]);
|
|
2187
2401
|
const imgValue = value;
|
|
2188
2402
|
const hasImage = !!(imgValue == null ? void 0 : imgValue.asset);
|
|
2189
2403
|
const altText = imgValue == null ? void 0 : imgValue.alt;
|
|
2190
|
-
const feedbackItems = (0,
|
|
2404
|
+
const feedbackItems = (0, import_react5.useMemo)(
|
|
2191
2405
|
() => getOgImageValidation(hasImage, altText, seoParent),
|
|
2192
2406
|
[hasImage, altText, seoParent]
|
|
2193
2407
|
);
|
|
2194
|
-
return /* @__PURE__ */ (0,
|
|
2408
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Stack, { space: 3, children: [
|
|
2195
2409
|
renderDefault(props),
|
|
2196
|
-
/* @__PURE__ */ (0,
|
|
2197
|
-
/* @__PURE__ */ (0,
|
|
2410
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
2411
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2198
2412
|
"div",
|
|
2199
2413
|
{
|
|
2200
2414
|
style: {
|
|
@@ -2205,25 +2419,26 @@ var OgImage = (props) => {
|
|
|
2205
2419
|
}
|
|
2206
2420
|
}
|
|
2207
2421
|
),
|
|
2208
|
-
/* @__PURE__ */ (0,
|
|
2422
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
2209
2423
|
] }, item.text)) })
|
|
2210
2424
|
] });
|
|
2211
2425
|
};
|
|
2212
2426
|
var OgImage_default = OgImage;
|
|
2213
2427
|
|
|
2214
2428
|
// src/components/openGraph/OgImageUrl.tsx
|
|
2215
|
-
var
|
|
2216
|
-
var
|
|
2217
|
-
var
|
|
2218
|
-
|
|
2429
|
+
var import_ui6 = require("@sanity/ui");
|
|
2430
|
+
var import_react6 = require("react");
|
|
2431
|
+
var import_sanity6 = require("sanity");
|
|
2432
|
+
init_seoUtils();
|
|
2433
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
2219
2434
|
var OgImageUrl = (props) => {
|
|
2220
2435
|
const { value, renderDefault, path } = props;
|
|
2221
|
-
const seoParent = (0,
|
|
2222
|
-
const feedbackItems = (0,
|
|
2223
|
-
return /* @__PURE__ */ (0,
|
|
2436
|
+
const seoParent = (0, import_sanity6.useFormValue)([path[0]]);
|
|
2437
|
+
const feedbackItems = (0, import_react6.useMemo)(() => getOgImageUrlValidation(value, seoParent), [value, seoParent]);
|
|
2438
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_ui6.Stack, { space: 3, children: [
|
|
2224
2439
|
renderDefault(props),
|
|
2225
|
-
/* @__PURE__ */ (0,
|
|
2226
|
-
/* @__PURE__ */ (0,
|
|
2440
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ui6.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
2441
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2227
2442
|
"div",
|
|
2228
2443
|
{
|
|
2229
2444
|
style: {
|
|
@@ -2234,30 +2449,31 @@ var OgImageUrl = (props) => {
|
|
|
2234
2449
|
}
|
|
2235
2450
|
}
|
|
2236
2451
|
),
|
|
2237
|
-
/* @__PURE__ */ (0,
|
|
2452
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ui6.Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
2238
2453
|
] }, item.text)) })
|
|
2239
2454
|
] });
|
|
2240
2455
|
};
|
|
2241
2456
|
var OgImageUrl_default = OgImageUrl;
|
|
2242
2457
|
|
|
2243
2458
|
// src/components/openGraph/OgTitle.tsx
|
|
2244
|
-
var
|
|
2245
|
-
var
|
|
2246
|
-
var
|
|
2247
|
-
|
|
2459
|
+
var import_ui7 = require("@sanity/ui");
|
|
2460
|
+
var import_react7 = require("react");
|
|
2461
|
+
var import_sanity7 = require("sanity");
|
|
2462
|
+
init_seoUtils();
|
|
2463
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
2248
2464
|
var OgTitle = (props) => {
|
|
2249
2465
|
const { value, renderDefault, path } = props;
|
|
2250
|
-
const parent = (0,
|
|
2466
|
+
const parent = (0, import_sanity7.useFormValue)([path[0]]);
|
|
2251
2467
|
const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
|
|
2252
|
-
const keywords = (0,
|
|
2253
|
-
const feedbackItems = (0,
|
|
2468
|
+
const keywords = (0, import_react7.useMemo)(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
|
|
2469
|
+
const feedbackItems = (0, import_react7.useMemo)(
|
|
2254
2470
|
() => getOgTitleValidation(value || "", keywords, isParentseoField),
|
|
2255
2471
|
[value, keywords, isParentseoField]
|
|
2256
2472
|
);
|
|
2257
|
-
return /* @__PURE__ */ (0,
|
|
2473
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ui7.Stack, { space: 3, children: [
|
|
2258
2474
|
renderDefault(props),
|
|
2259
|
-
/* @__PURE__ */ (0,
|
|
2260
|
-
/* @__PURE__ */ (0,
|
|
2475
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ui7.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
2476
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2261
2477
|
"div",
|
|
2262
2478
|
{
|
|
2263
2479
|
style: {
|
|
@@ -2268,7 +2484,7 @@ var OgTitle = (props) => {
|
|
|
2268
2484
|
}
|
|
2269
2485
|
}
|
|
2270
2486
|
),
|
|
2271
|
-
/* @__PURE__ */ (0,
|
|
2487
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ui7.Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
2272
2488
|
] }, item.text)) })
|
|
2273
2489
|
] });
|
|
2274
2490
|
};
|
|
@@ -2276,19 +2492,19 @@ var OgTitle_default = OgTitle;
|
|
|
2276
2492
|
|
|
2277
2493
|
// src/schemas/types/openGraph/index.ts
|
|
2278
2494
|
function openGraph(config = {}) {
|
|
2279
|
-
return (0,
|
|
2495
|
+
return (0, import_sanity8.defineType)({
|
|
2280
2496
|
name: "openGraph",
|
|
2281
2497
|
title: "Open Graph Settings",
|
|
2282
2498
|
type: "object",
|
|
2283
2499
|
fields: [
|
|
2284
|
-
(0,
|
|
2500
|
+
(0, import_sanity8.defineField)(__spreadProps(__spreadValues({
|
|
2285
2501
|
name: "url",
|
|
2286
2502
|
type: "url"
|
|
2287
2503
|
}, getFieldInfo("openGraphUrl", config.fieldOverrides)), {
|
|
2288
2504
|
hidden: getFieldHiddenFunction("openGraphUrl", config),
|
|
2289
2505
|
description: "The canonical URL of the page. This should be the full URL including protocol (https://)."
|
|
2290
2506
|
})),
|
|
2291
|
-
(0,
|
|
2507
|
+
(0, import_sanity8.defineField)(__spreadProps(__spreadValues({
|
|
2292
2508
|
name: "title"
|
|
2293
2509
|
}, getFieldInfo("openGraphTitle", config.fieldOverrides)), {
|
|
2294
2510
|
type: "string",
|
|
@@ -2298,7 +2514,7 @@ function openGraph(config = {}) {
|
|
|
2298
2514
|
// Can also wrap with a string input + preview
|
|
2299
2515
|
}
|
|
2300
2516
|
})),
|
|
2301
|
-
(0,
|
|
2517
|
+
(0, import_sanity8.defineField)(__spreadProps(__spreadValues({
|
|
2302
2518
|
name: "description"
|
|
2303
2519
|
}, getFieldInfo("openGraphDescription", config.fieldOverrides)), {
|
|
2304
2520
|
type: "text",
|
|
@@ -2309,13 +2525,13 @@ function openGraph(config = {}) {
|
|
|
2309
2525
|
// Can also wrap with a text area + preview
|
|
2310
2526
|
}
|
|
2311
2527
|
})),
|
|
2312
|
-
(0,
|
|
2528
|
+
(0, import_sanity8.defineField)(__spreadProps(__spreadValues({
|
|
2313
2529
|
name: "siteName"
|
|
2314
2530
|
}, getFieldInfo("openGraphSiteName", config.fieldOverrides)), {
|
|
2315
2531
|
type: "string",
|
|
2316
2532
|
hidden: getFieldHiddenFunction("openGraphSiteName", config)
|
|
2317
2533
|
})),
|
|
2318
|
-
(0,
|
|
2534
|
+
(0, import_sanity8.defineField)(__spreadProps(__spreadValues({
|
|
2319
2535
|
name: "type"
|
|
2320
2536
|
}, getFieldInfo("openGraphType", config.fieldOverrides)), {
|
|
2321
2537
|
type: "string",
|
|
@@ -2334,7 +2550,7 @@ function openGraph(config = {}) {
|
|
|
2334
2550
|
hidden: getFieldHiddenFunction("openGraphType", config),
|
|
2335
2551
|
initialValue: "website"
|
|
2336
2552
|
})),
|
|
2337
|
-
(0,
|
|
2553
|
+
(0, import_sanity8.defineField)(__spreadProps(__spreadValues({
|
|
2338
2554
|
name: "imageType"
|
|
2339
2555
|
}, getFieldInfo("openGraphImageType", config.fieldOverrides)), {
|
|
2340
2556
|
type: "string",
|
|
@@ -2347,7 +2563,7 @@ function openGraph(config = {}) {
|
|
|
2347
2563
|
hidden: getFieldHiddenFunction("openGraphImage", config),
|
|
2348
2564
|
initialValue: "upload"
|
|
2349
2565
|
})),
|
|
2350
|
-
(0,
|
|
2566
|
+
(0, import_sanity8.defineField)(__spreadProps(__spreadValues({
|
|
2351
2567
|
name: "image"
|
|
2352
2568
|
}, getFieldInfo("openGraphImage", config.fieldOverrides)), {
|
|
2353
2569
|
type: "image",
|
|
@@ -2358,7 +2574,7 @@ function openGraph(config = {}) {
|
|
|
2358
2574
|
input: OgImage_default
|
|
2359
2575
|
},
|
|
2360
2576
|
fields: [
|
|
2361
|
-
(0,
|
|
2577
|
+
(0, import_sanity8.defineField)({
|
|
2362
2578
|
name: "alt",
|
|
2363
2579
|
title: "Image Alt Text",
|
|
2364
2580
|
type: "string",
|
|
@@ -2372,7 +2588,7 @@ function openGraph(config = {}) {
|
|
|
2372
2588
|
return typeof hiddenFn === "function" ? hiddenFn(context) : hiddenFn;
|
|
2373
2589
|
}
|
|
2374
2590
|
})),
|
|
2375
|
-
(0,
|
|
2591
|
+
(0, import_sanity8.defineField)(__spreadProps(__spreadValues({
|
|
2376
2592
|
name: "imageUrl"
|
|
2377
2593
|
}, getFieldInfo("openGraphImageUrl", config.fieldOverrides)), {
|
|
2378
2594
|
type: "url",
|
|
@@ -2391,26 +2607,27 @@ function openGraph(config = {}) {
|
|
|
2391
2607
|
}
|
|
2392
2608
|
|
|
2393
2609
|
// src/schemas/types/twitter/index.ts
|
|
2394
|
-
var
|
|
2610
|
+
var import_sanity13 = require("sanity");
|
|
2395
2611
|
|
|
2396
2612
|
// src/components/twitter/twitterDescription.tsx
|
|
2397
|
-
var
|
|
2398
|
-
var
|
|
2399
|
-
var
|
|
2400
|
-
|
|
2613
|
+
var import_ui8 = require("@sanity/ui");
|
|
2614
|
+
var import_react8 = require("react");
|
|
2615
|
+
var import_sanity9 = require("sanity");
|
|
2616
|
+
init_seoUtils();
|
|
2617
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
2401
2618
|
var TwitterDescription = (props) => {
|
|
2402
2619
|
const { value, renderDefault, path } = props;
|
|
2403
|
-
const parent = (0,
|
|
2620
|
+
const parent = (0, import_sanity9.useFormValue)([path[0]]);
|
|
2404
2621
|
const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
|
|
2405
|
-
const keywords = (0,
|
|
2406
|
-
const feedbackItems = (0,
|
|
2622
|
+
const keywords = (0, import_react8.useMemo)(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
|
|
2623
|
+
const feedbackItems = (0, import_react8.useMemo)(
|
|
2407
2624
|
() => getTwitterDescriptionValidation(value || "", keywords, isParentseoField),
|
|
2408
2625
|
[value, keywords, isParentseoField]
|
|
2409
2626
|
);
|
|
2410
|
-
return /* @__PURE__ */ (0,
|
|
2627
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ui8.Stack, { space: 3, children: [
|
|
2411
2628
|
renderDefault(props),
|
|
2412
|
-
/* @__PURE__ */ (0,
|
|
2413
|
-
/* @__PURE__ */ (0,
|
|
2629
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ui8.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
2630
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2414
2631
|
"div",
|
|
2415
2632
|
{
|
|
2416
2633
|
style: {
|
|
@@ -2421,31 +2638,32 @@ var TwitterDescription = (props) => {
|
|
|
2421
2638
|
}
|
|
2422
2639
|
}
|
|
2423
2640
|
),
|
|
2424
|
-
/* @__PURE__ */ (0,
|
|
2641
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ui8.Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
2425
2642
|
] }, item.text)) })
|
|
2426
2643
|
] });
|
|
2427
2644
|
};
|
|
2428
2645
|
var twitterDescription_default = TwitterDescription;
|
|
2429
2646
|
|
|
2430
2647
|
// src/components/twitter/TwitterImage.tsx
|
|
2431
|
-
var
|
|
2432
|
-
var
|
|
2433
|
-
var
|
|
2434
|
-
|
|
2648
|
+
var import_ui9 = require("@sanity/ui");
|
|
2649
|
+
var import_react9 = require("react");
|
|
2650
|
+
var import_sanity10 = require("sanity");
|
|
2651
|
+
init_seoUtils();
|
|
2652
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
2435
2653
|
var TwitterImage = (props) => {
|
|
2436
2654
|
const { value, renderDefault, path } = props;
|
|
2437
|
-
const seoParent = (0,
|
|
2655
|
+
const seoParent = (0, import_sanity10.useFormValue)([path[0]]);
|
|
2438
2656
|
const imgValue = value;
|
|
2439
2657
|
const hasImage = !!(imgValue == null ? void 0 : imgValue.asset);
|
|
2440
2658
|
const altText = imgValue == null ? void 0 : imgValue.alt;
|
|
2441
|
-
const feedbackItems = (0,
|
|
2659
|
+
const feedbackItems = (0, import_react9.useMemo)(
|
|
2442
2660
|
() => getTwitterImageValidation(hasImage, altText, seoParent),
|
|
2443
2661
|
[hasImage, altText, seoParent]
|
|
2444
2662
|
);
|
|
2445
|
-
return /* @__PURE__ */ (0,
|
|
2663
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ui9.Stack, { space: 3, children: [
|
|
2446
2664
|
renderDefault(props),
|
|
2447
|
-
/* @__PURE__ */ (0,
|
|
2448
|
-
/* @__PURE__ */ (0,
|
|
2665
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ui9.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
2666
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2449
2667
|
"div",
|
|
2450
2668
|
{
|
|
2451
2669
|
style: {
|
|
@@ -2456,28 +2674,29 @@ var TwitterImage = (props) => {
|
|
|
2456
2674
|
}
|
|
2457
2675
|
}
|
|
2458
2676
|
),
|
|
2459
|
-
/* @__PURE__ */ (0,
|
|
2677
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ui9.Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
2460
2678
|
] }, item.text)) })
|
|
2461
2679
|
] });
|
|
2462
2680
|
};
|
|
2463
2681
|
var TwitterImage_default = TwitterImage;
|
|
2464
2682
|
|
|
2465
2683
|
// src/components/twitter/TwitterImageUrl.tsx
|
|
2466
|
-
var
|
|
2467
|
-
var
|
|
2468
|
-
var
|
|
2469
|
-
|
|
2684
|
+
var import_ui10 = require("@sanity/ui");
|
|
2685
|
+
var import_react10 = require("react");
|
|
2686
|
+
var import_sanity11 = require("sanity");
|
|
2687
|
+
init_seoUtils();
|
|
2688
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
2470
2689
|
var TwitterImageUrl = (props) => {
|
|
2471
2690
|
const { value, renderDefault, path } = props;
|
|
2472
|
-
const seoParent = (0,
|
|
2473
|
-
const feedbackItems = (0,
|
|
2691
|
+
const seoParent = (0, import_sanity11.useFormValue)([path[0]]);
|
|
2692
|
+
const feedbackItems = (0, import_react10.useMemo)(
|
|
2474
2693
|
() => getTwitterImageUrlValidation(value, seoParent),
|
|
2475
2694
|
[value, seoParent]
|
|
2476
2695
|
);
|
|
2477
|
-
return /* @__PURE__ */ (0,
|
|
2696
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ui10.Stack, { space: 3, children: [
|
|
2478
2697
|
renderDefault(props),
|
|
2479
|
-
/* @__PURE__ */ (0,
|
|
2480
|
-
/* @__PURE__ */ (0,
|
|
2698
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ui10.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
2699
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2481
2700
|
"div",
|
|
2482
2701
|
{
|
|
2483
2702
|
style: {
|
|
@@ -2488,30 +2707,31 @@ var TwitterImageUrl = (props) => {
|
|
|
2488
2707
|
}
|
|
2489
2708
|
}
|
|
2490
2709
|
),
|
|
2491
|
-
/* @__PURE__ */ (0,
|
|
2710
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ui10.Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
2492
2711
|
] }, item.text)) })
|
|
2493
2712
|
] });
|
|
2494
2713
|
};
|
|
2495
2714
|
var TwitterImageUrl_default = TwitterImageUrl;
|
|
2496
2715
|
|
|
2497
2716
|
// src/components/twitter/twitterTitle.tsx
|
|
2498
|
-
var
|
|
2499
|
-
var
|
|
2500
|
-
var
|
|
2501
|
-
|
|
2717
|
+
var import_ui11 = require("@sanity/ui");
|
|
2718
|
+
var import_react11 = require("react");
|
|
2719
|
+
var import_sanity12 = require("sanity");
|
|
2720
|
+
init_seoUtils();
|
|
2721
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
2502
2722
|
var TwitterTitle = (props) => {
|
|
2503
2723
|
const { value, renderDefault, path } = props;
|
|
2504
|
-
const parent = (0,
|
|
2724
|
+
const parent = (0, import_sanity12.useFormValue)([path[0]]);
|
|
2505
2725
|
const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
|
|
2506
|
-
const keywords = (0,
|
|
2507
|
-
const feedbackItems = (0,
|
|
2726
|
+
const keywords = (0, import_react11.useMemo)(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
|
|
2727
|
+
const feedbackItems = (0, import_react11.useMemo)(
|
|
2508
2728
|
() => getTwitterTitleValidation(value || "", keywords, isParentseoField),
|
|
2509
2729
|
[value, keywords, isParentseoField]
|
|
2510
2730
|
);
|
|
2511
|
-
return /* @__PURE__ */ (0,
|
|
2731
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ui11.Stack, { space: 3, children: [
|
|
2512
2732
|
renderDefault(props),
|
|
2513
|
-
/* @__PURE__ */ (0,
|
|
2514
|
-
/* @__PURE__ */ (0,
|
|
2733
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ui11.Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
2734
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2515
2735
|
"div",
|
|
2516
2736
|
{
|
|
2517
2737
|
style: {
|
|
@@ -2522,7 +2742,7 @@ var TwitterTitle = (props) => {
|
|
|
2522
2742
|
}
|
|
2523
2743
|
}
|
|
2524
2744
|
),
|
|
2525
|
-
/* @__PURE__ */ (0,
|
|
2745
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ui11.Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
2526
2746
|
] }, item.text)) })
|
|
2527
2747
|
] });
|
|
2528
2748
|
};
|
|
@@ -2530,12 +2750,12 @@ var twitterTitle_default = TwitterTitle;
|
|
|
2530
2750
|
|
|
2531
2751
|
// src/schemas/types/twitter/index.ts
|
|
2532
2752
|
function twitter(config = {}) {
|
|
2533
|
-
return (0,
|
|
2753
|
+
return (0, import_sanity13.defineType)({
|
|
2534
2754
|
name: "twitter",
|
|
2535
2755
|
title: "X (Formerly Twitter)",
|
|
2536
2756
|
type: "object",
|
|
2537
2757
|
fields: [
|
|
2538
|
-
(0,
|
|
2758
|
+
(0, import_sanity13.defineField)(__spreadProps(__spreadValues({
|
|
2539
2759
|
name: "card"
|
|
2540
2760
|
}, getFieldInfo("twitterCard", config.fieldOverrides)), {
|
|
2541
2761
|
type: "string",
|
|
@@ -2551,19 +2771,19 @@ function twitter(config = {}) {
|
|
|
2551
2771
|
initialValue: "summary_large_image"
|
|
2552
2772
|
// good default
|
|
2553
2773
|
})),
|
|
2554
|
-
(0,
|
|
2774
|
+
(0, import_sanity13.defineField)(__spreadProps(__spreadValues({
|
|
2555
2775
|
name: "site"
|
|
2556
2776
|
}, getFieldInfo("twitterSite", config.fieldOverrides)), {
|
|
2557
2777
|
type: "string",
|
|
2558
2778
|
hidden: getFieldHiddenFunction("twitterSite", config)
|
|
2559
2779
|
})),
|
|
2560
|
-
(0,
|
|
2780
|
+
(0, import_sanity13.defineField)(__spreadProps(__spreadValues({
|
|
2561
2781
|
name: "creator",
|
|
2562
2782
|
type: "string"
|
|
2563
2783
|
}, getFieldInfo("twitterCreator", config.fieldOverrides)), {
|
|
2564
2784
|
hidden: getFieldHiddenFunction("twitterCreator", config)
|
|
2565
2785
|
})),
|
|
2566
|
-
(0,
|
|
2786
|
+
(0, import_sanity13.defineField)(__spreadProps(__spreadValues({
|
|
2567
2787
|
name: "title",
|
|
2568
2788
|
type: "string"
|
|
2569
2789
|
}, getFieldInfo("twitterTitle", config.fieldOverrides)), {
|
|
@@ -2572,7 +2792,7 @@ function twitter(config = {}) {
|
|
|
2572
2792
|
input: twitterTitle_default
|
|
2573
2793
|
}
|
|
2574
2794
|
})),
|
|
2575
|
-
(0,
|
|
2795
|
+
(0, import_sanity13.defineField)(__spreadProps(__spreadValues({
|
|
2576
2796
|
name: "description",
|
|
2577
2797
|
type: "text",
|
|
2578
2798
|
rows: 3
|
|
@@ -2582,7 +2802,7 @@ function twitter(config = {}) {
|
|
|
2582
2802
|
input: twitterDescription_default
|
|
2583
2803
|
}
|
|
2584
2804
|
})),
|
|
2585
|
-
(0,
|
|
2805
|
+
(0, import_sanity13.defineField)(__spreadProps(__spreadValues({
|
|
2586
2806
|
name: "imageType"
|
|
2587
2807
|
}, getFieldInfo("twitterImageType", config.fieldOverrides)), {
|
|
2588
2808
|
type: "string",
|
|
@@ -2595,7 +2815,7 @@ function twitter(config = {}) {
|
|
|
2595
2815
|
hidden: getFieldHiddenFunction("twitterImage", config),
|
|
2596
2816
|
initialValue: "upload"
|
|
2597
2817
|
})),
|
|
2598
|
-
(0,
|
|
2818
|
+
(0, import_sanity13.defineField)(__spreadProps(__spreadValues({
|
|
2599
2819
|
name: "image"
|
|
2600
2820
|
}, getFieldInfo("twitterImage", config.fieldOverrides)), {
|
|
2601
2821
|
type: "image",
|
|
@@ -2606,7 +2826,7 @@ function twitter(config = {}) {
|
|
|
2606
2826
|
input: TwitterImage_default
|
|
2607
2827
|
},
|
|
2608
2828
|
fields: [
|
|
2609
|
-
(0,
|
|
2829
|
+
(0, import_sanity13.defineField)({
|
|
2610
2830
|
name: "alt",
|
|
2611
2831
|
title: "Image Alt Text",
|
|
2612
2832
|
type: "string",
|
|
@@ -2620,7 +2840,7 @@ function twitter(config = {}) {
|
|
|
2620
2840
|
return typeof hiddenFn === "function" ? hiddenFn(context) : hiddenFn;
|
|
2621
2841
|
}
|
|
2622
2842
|
})),
|
|
2623
|
-
(0,
|
|
2843
|
+
(0, import_sanity13.defineField)(__spreadProps(__spreadValues({
|
|
2624
2844
|
name: "imageUrl"
|
|
2625
2845
|
}, getFieldInfo("twitterImageUrl", config.fieldOverrides)), {
|
|
2626
2846
|
type: "url",
|
|
@@ -2639,13 +2859,15 @@ function twitter(config = {}) {
|
|
|
2639
2859
|
}
|
|
2640
2860
|
|
|
2641
2861
|
// src/schemas/index.ts
|
|
2862
|
+
var LazySeoPreview = import_react12.default.lazy(() => Promise.resolve().then(() => (init_SeoPreview(), SeoPreview_exports)));
|
|
2863
|
+
var SeoPreviewWrapper = (props) => import_react12.default.createElement(import_react12.default.Suspense, { fallback: null }, import_react12.default.createElement(LazySeoPreview, props));
|
|
2642
2864
|
function seoFieldsSchema(config = {}) {
|
|
2643
|
-
return (0,
|
|
2865
|
+
return (0, import_sanity15.defineType)({
|
|
2644
2866
|
name: "seoFields",
|
|
2645
2867
|
title: "SEO Fields",
|
|
2646
2868
|
type: "object",
|
|
2647
2869
|
fields: [
|
|
2648
|
-
(0,
|
|
2870
|
+
(0, import_sanity15.defineField)({
|
|
2649
2871
|
name: "robots",
|
|
2650
2872
|
title: "Robots Settings",
|
|
2651
2873
|
type: "robots",
|
|
@@ -2654,11 +2876,11 @@ function seoFieldsSchema(config = {}) {
|
|
|
2654
2876
|
}),
|
|
2655
2877
|
// 👇 conditionally spread preview field
|
|
2656
2878
|
...typeof config.seoPreview === "boolean" && config.seoPreview || typeof config.seoPreview === "object" && !isEmpty(config.seoPreview) ? [
|
|
2657
|
-
(0,
|
|
2879
|
+
(0, import_sanity15.defineField)({
|
|
2658
2880
|
name: "preview",
|
|
2659
2881
|
title: "SEO Preview",
|
|
2660
2882
|
type: "string",
|
|
2661
|
-
components: { input:
|
|
2883
|
+
components: { input: SeoPreviewWrapper },
|
|
2662
2884
|
options: __spreadValues({
|
|
2663
2885
|
baseUrl: config.baseUrl || "https://www.example.com"
|
|
2664
2886
|
}, typeof config.seoPreview === "object" && config.seoPreview && config.seoPreview.prefix ? { prefix: config.seoPreview.prefix } : {}),
|
|
@@ -2669,7 +2891,7 @@ function seoFieldsSchema(config = {}) {
|
|
|
2669
2891
|
readOnly: true
|
|
2670
2892
|
})
|
|
2671
2893
|
] : [],
|
|
2672
|
-
(0,
|
|
2894
|
+
(0, import_sanity15.defineField)(__spreadProps(__spreadValues({
|
|
2673
2895
|
name: "title"
|
|
2674
2896
|
}, getFieldInfo("title", config.fieldOverrides)), {
|
|
2675
2897
|
// title: 'Meta Title',
|
|
@@ -2682,7 +2904,7 @@ function seoFieldsSchema(config = {}) {
|
|
|
2682
2904
|
// validation: (Rule) => Rule.max(60).warning('Meta title should be under 60 characters.'),
|
|
2683
2905
|
hidden: getFieldHiddenFunction("title", config)
|
|
2684
2906
|
})),
|
|
2685
|
-
(0,
|
|
2907
|
+
(0, import_sanity15.defineField)(__spreadProps(__spreadValues({
|
|
2686
2908
|
name: "description"
|
|
2687
2909
|
}, getFieldInfo("description", config.fieldOverrides)), {
|
|
2688
2910
|
// title: 'Meta Description',
|
|
@@ -2696,7 +2918,7 @@ function seoFieldsSchema(config = {}) {
|
|
|
2696
2918
|
// validation: (Rule) => Rule.max(160).warning('Meta description should be under 160 characters.'),
|
|
2697
2919
|
hidden: getFieldHiddenFunction("description", config)
|
|
2698
2920
|
})),
|
|
2699
|
-
(0,
|
|
2921
|
+
(0, import_sanity15.defineField)(__spreadProps(__spreadValues({
|
|
2700
2922
|
name: "metaImage"
|
|
2701
2923
|
}, getFieldInfo("metaImage", config.fieldOverrides)), {
|
|
2702
2924
|
// title: 'Meta Image',
|
|
@@ -2711,7 +2933,7 @@ function seoFieldsSchema(config = {}) {
|
|
|
2711
2933
|
},
|
|
2712
2934
|
hidden: getFieldHiddenFunction("metaImage", config)
|
|
2713
2935
|
})),
|
|
2714
|
-
(0,
|
|
2936
|
+
(0, import_sanity15.defineField)(__spreadProps(__spreadValues({
|
|
2715
2937
|
name: "metaAttributes"
|
|
2716
2938
|
}, getFieldInfo("metaAttributes", config.fieldOverrides)), {
|
|
2717
2939
|
type: "array",
|
|
@@ -2720,7 +2942,7 @@ function seoFieldsSchema(config = {}) {
|
|
|
2720
2942
|
// 'Add custom meta attributes to the head of the document for additional SEO and social media integration.',
|
|
2721
2943
|
hidden: getFieldHiddenFunction("metaAttributes", config)
|
|
2722
2944
|
})),
|
|
2723
|
-
(0,
|
|
2945
|
+
(0, import_sanity15.defineField)(__spreadProps(__spreadValues({
|
|
2724
2946
|
name: "keywords"
|
|
2725
2947
|
}, getFieldInfo("keywords", config.fieldOverrides)), {
|
|
2726
2948
|
title: "Keywords",
|
|
@@ -2729,7 +2951,7 @@ function seoFieldsSchema(config = {}) {
|
|
|
2729
2951
|
description: "Add relevant keywords for this page. These keywords will be used for SEO purposes.",
|
|
2730
2952
|
hidden: getFieldHiddenFunction("keywords", config)
|
|
2731
2953
|
})),
|
|
2732
|
-
(0,
|
|
2954
|
+
(0, import_sanity15.defineField)(__spreadProps(__spreadValues({
|
|
2733
2955
|
name: "canonicalUrl"
|
|
2734
2956
|
}, getFieldInfo("canonicalUrl", config.fieldOverrides)), {
|
|
2735
2957
|
title: "Canonical URL",
|
|
@@ -2744,18 +2966,18 @@ function seoFieldsSchema(config = {}) {
|
|
|
2744
2966
|
}
|
|
2745
2967
|
|
|
2746
2968
|
// src/schemas/types/metaAttribute/index.ts
|
|
2747
|
-
var
|
|
2748
|
-
var metaAttribute_default = (0,
|
|
2969
|
+
var import_sanity16 = require("sanity");
|
|
2970
|
+
var metaAttribute_default = (0, import_sanity16.defineType)({
|
|
2749
2971
|
name: "metaAttribute",
|
|
2750
2972
|
title: "Meta Attribute",
|
|
2751
2973
|
type: "object",
|
|
2752
2974
|
fields: [
|
|
2753
|
-
(0,
|
|
2975
|
+
(0, import_sanity16.defineField)({
|
|
2754
2976
|
name: "key",
|
|
2755
2977
|
title: "Attribute Name",
|
|
2756
2978
|
type: "string"
|
|
2757
2979
|
}),
|
|
2758
|
-
(0,
|
|
2980
|
+
(0, import_sanity16.defineField)({
|
|
2759
2981
|
name: "type",
|
|
2760
2982
|
title: "Attribute Value Type",
|
|
2761
2983
|
type: "string",
|
|
@@ -2767,13 +2989,13 @@ var metaAttribute_default = (0, import_sanity17.defineType)({
|
|
|
2767
2989
|
},
|
|
2768
2990
|
initialValue: "string"
|
|
2769
2991
|
}),
|
|
2770
|
-
(0,
|
|
2992
|
+
(0, import_sanity16.defineField)({
|
|
2771
2993
|
name: "value",
|
|
2772
2994
|
title: "Attribute Value",
|
|
2773
2995
|
type: "string",
|
|
2774
2996
|
hidden: ({ parent }) => (parent == null ? void 0 : parent.type) === "image"
|
|
2775
2997
|
}),
|
|
2776
|
-
(0,
|
|
2998
|
+
(0, import_sanity16.defineField)({
|
|
2777
2999
|
name: "image",
|
|
2778
3000
|
title: "Attribute Image Value",
|
|
2779
3001
|
type: "image",
|
|
@@ -2805,8 +3027,8 @@ var metaAttribute_default = (0, import_sanity17.defineType)({
|
|
|
2805
3027
|
});
|
|
2806
3028
|
|
|
2807
3029
|
// src/schemas/types/metaTag/index.ts
|
|
2808
|
-
var
|
|
2809
|
-
var metaTag_default = (0,
|
|
3030
|
+
var import_sanity17 = require("sanity");
|
|
3031
|
+
var metaTag_default = (0, import_sanity17.defineType)({
|
|
2810
3032
|
name: "metaTag",
|
|
2811
3033
|
title: "Meta Tag",
|
|
2812
3034
|
type: "object",
|
|
@@ -2822,20 +3044,20 @@ var metaTag_default = (0, import_sanity18.defineType)({
|
|
|
2822
3044
|
});
|
|
2823
3045
|
|
|
2824
3046
|
// src/schemas/types/robots/index.ts
|
|
2825
|
-
var
|
|
2826
|
-
var robots_default = (0,
|
|
3047
|
+
var import_sanity18 = require("sanity");
|
|
3048
|
+
var robots_default = (0, import_sanity18.defineType)({
|
|
2827
3049
|
name: "robots",
|
|
2828
3050
|
title: "Robots Settings",
|
|
2829
3051
|
type: "object",
|
|
2830
3052
|
fields: [
|
|
2831
|
-
(0,
|
|
3053
|
+
(0, import_sanity18.defineField)({
|
|
2832
3054
|
name: "noIndex",
|
|
2833
3055
|
title: "No Index",
|
|
2834
3056
|
type: "boolean",
|
|
2835
3057
|
initialValue: false,
|
|
2836
3058
|
description: "Enable this to prevent search engines from indexing this page. The page will not appear in search results."
|
|
2837
3059
|
}),
|
|
2838
|
-
(0,
|
|
3060
|
+
(0, import_sanity18.defineField)({
|
|
2839
3061
|
name: "noFollow",
|
|
2840
3062
|
title: "No Follow",
|
|
2841
3063
|
type: "boolean",
|
|
@@ -2862,58 +3084,135 @@ function types(config = {}) {
|
|
|
2862
3084
|
}
|
|
2863
3085
|
|
|
2864
3086
|
// src/plugin.ts
|
|
3087
|
+
var V132 = "v1.3.2";
|
|
3088
|
+
var CHANGELOG_V132 = `https://github.com/hardik-143/sanity-plugin-seofields/blob/main/CHANGELOG.md#132--2026-03-23`;
|
|
2865
3089
|
var resolveDashboardConfig = (healthDashboard) => {
|
|
2866
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
|
|
3090
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
|
|
2867
3091
|
const cfg = typeof healthDashboard === "object" ? healthDashboard : void 0;
|
|
3092
|
+
const deprecationWarnings = [];
|
|
3093
|
+
if (((_a = cfg == null ? void 0 : cfg.display) == null ? void 0 : _a.typeColumn) !== void 0) {
|
|
3094
|
+
deprecationWarnings.push({
|
|
3095
|
+
key: "display.typeColumn \u2192 showTypeColumn",
|
|
3096
|
+
version: V132,
|
|
3097
|
+
changelogUrl: CHANGELOG_V132
|
|
3098
|
+
});
|
|
3099
|
+
if (cfg.showTypeColumn) {
|
|
3100
|
+
console.warn(
|
|
3101
|
+
`[sanity-plugin-seofields] Both "healthDashboard.display.typeColumn" and "healthDashboard.showTypeColumn" are set. "showTypeColumn" will take precedence. Please remove "healthDashboard.display.typeColumn". See ${CHANGELOG_V132}`
|
|
3102
|
+
);
|
|
3103
|
+
} else {
|
|
3104
|
+
console.warn(
|
|
3105
|
+
`[sanity-plugin-seofields] "healthDashboard.display.typeColumn" is deprecated. Use "healthDashboard.showTypeColumn" instead. See ${CHANGELOG_V132}`
|
|
3106
|
+
);
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
if (((_b = cfg == null ? void 0 : cfg.display) == null ? void 0 : _b.documentId) !== void 0) {
|
|
3110
|
+
deprecationWarnings.push({
|
|
3111
|
+
key: "display.documentId \u2192 showDocumentId",
|
|
3112
|
+
version: V132,
|
|
3113
|
+
changelogUrl: CHANGELOG_V132
|
|
3114
|
+
});
|
|
3115
|
+
if (cfg.showDocumentId) {
|
|
3116
|
+
console.warn(
|
|
3117
|
+
`[sanity-plugin-seofields] Both "healthDashboard.display.documentId" and "healthDashboard.showDocumentId" are set. "showDocumentId" will take precedence. Please remove "healthDashboard.display.documentId". See ${CHANGELOG_V132}`
|
|
3118
|
+
);
|
|
3119
|
+
} else {
|
|
3120
|
+
console.warn(
|
|
3121
|
+
`[sanity-plugin-seofields] "healthDashboard.display.documentId" is deprecated. Use "healthDashboard.showDocumentId" instead. See ${CHANGELOG_V132}`
|
|
3122
|
+
);
|
|
3123
|
+
}
|
|
3124
|
+
}
|
|
3125
|
+
if (cfg == null ? void 0 : cfg.typeLabels) {
|
|
3126
|
+
deprecationWarnings.push({
|
|
3127
|
+
key: "typeLabels \u2192 typeDisplayLabels",
|
|
3128
|
+
version: V132,
|
|
3129
|
+
changelogUrl: CHANGELOG_V132
|
|
3130
|
+
});
|
|
3131
|
+
if (cfg.typeDisplayLabels) {
|
|
3132
|
+
console.warn(
|
|
3133
|
+
`[sanity-plugin-seofields] Both "healthDashboard.typeLabels" and "healthDashboard.typeDisplayLabels" are set. "typeDisplayLabels" will take precedence. Please remove "typeLabels". See ${CHANGELOG_V132}`
|
|
3134
|
+
);
|
|
3135
|
+
} else {
|
|
3136
|
+
console.warn(
|
|
3137
|
+
`[sanity-plugin-seofields] "healthDashboard.typeLabels" is deprecated. Use "healthDashboard.typeDisplayLabels" instead. See ${CHANGELOG_V132}`
|
|
3138
|
+
);
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3141
|
+
if (cfg == null ? void 0 : cfg.docBadge) {
|
|
3142
|
+
deprecationWarnings.push({
|
|
3143
|
+
key: "docBadge \u2192 getDocumentBadge",
|
|
3144
|
+
version: V132,
|
|
3145
|
+
changelogUrl: CHANGELOG_V132
|
|
3146
|
+
});
|
|
3147
|
+
if (cfg == null ? void 0 : cfg.getDocumentBadge) {
|
|
3148
|
+
console.warn(
|
|
3149
|
+
`[sanity-plugin-seofields] Both "healthDashboard.docBadge" and "healthDashboard.getDocumentBadge" are set. "getDocumentBadge" will take precedence. Please remove "docBadge". See ${CHANGELOG_V132}`
|
|
3150
|
+
);
|
|
3151
|
+
} else {
|
|
3152
|
+
console.warn(
|
|
3153
|
+
`[sanity-plugin-seofields] "healthDashboard.docBadge" is deprecated. Use "healthDashboard.getDocumentBadge" instead. See ${CHANGELOG_V132}`
|
|
3154
|
+
);
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
2868
3157
|
return {
|
|
2869
3158
|
enabled: healthDashboard !== false,
|
|
2870
|
-
toolTitle: (
|
|
2871
|
-
toolName: (
|
|
2872
|
-
icon: (
|
|
2873
|
-
title: (
|
|
2874
|
-
description: (
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
3159
|
+
toolTitle: (_d = (_c = cfg == null ? void 0 : cfg.tool) == null ? void 0 : _c.title) != null ? _d : "SEO Health",
|
|
3160
|
+
toolName: (_f = (_e = cfg == null ? void 0 : cfg.tool) == null ? void 0 : _e.name) != null ? _f : "seo-health-dashboard",
|
|
3161
|
+
icon: (_g = cfg == null ? void 0 : cfg.content) == null ? void 0 : _g.icon,
|
|
3162
|
+
title: (_h = cfg == null ? void 0 : cfg.content) == null ? void 0 : _h.title,
|
|
3163
|
+
description: (_i = cfg == null ? void 0 : cfg.content) == null ? void 0 : _i.description,
|
|
3164
|
+
// New flat keys take precedence; fall back to deprecated display.* for backwards compat
|
|
3165
|
+
showTypeColumn: (_k = cfg == null ? void 0 : cfg.showTypeColumn) != null ? _k : (_j = cfg == null ? void 0 : cfg.display) == null ? void 0 : _j.typeColumn,
|
|
3166
|
+
showDocumentId: (_m = cfg == null ? void 0 : cfg.showDocumentId) != null ? _m : (_l = cfg == null ? void 0 : cfg.display) == null ? void 0 : _l.documentId,
|
|
3167
|
+
queryTypes: (_n = cfg == null ? void 0 : cfg.query) == null ? void 0 : _n.types,
|
|
3168
|
+
queryRequireSeo: (_o = cfg == null ? void 0 : cfg.query) == null ? void 0 : _o.requireSeo,
|
|
3169
|
+
queryGroq: (_p = cfg == null ? void 0 : cfg.query) == null ? void 0 : _p.groq,
|
|
2880
3170
|
apiVersion: cfg == null ? void 0 : cfg.apiVersion,
|
|
2881
3171
|
licenseKey: cfg == null ? void 0 : cfg.licenseKey,
|
|
2882
|
-
|
|
3172
|
+
// New key takes precedence; fall back to deprecated key for backwards compat
|
|
3173
|
+
typeDisplayLabels: (_q = cfg == null ? void 0 : cfg.typeDisplayLabels) != null ? _q : cfg == null ? void 0 : cfg.typeLabels,
|
|
2883
3174
|
typeColumnMode: cfg == null ? void 0 : cfg.typeColumnMode,
|
|
2884
3175
|
titleField: cfg == null ? void 0 : cfg.titleField,
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
3176
|
+
// New key takes precedence; fall back to deprecated key for backwards compat
|
|
3177
|
+
getDocumentBadge: (_r = cfg == null ? void 0 : cfg.getDocumentBadge) != null ? _r : cfg == null ? void 0 : cfg.docBadge,
|
|
3178
|
+
loadingLicense: (_s = cfg == null ? void 0 : cfg.content) == null ? void 0 : _s.loadingLicense,
|
|
3179
|
+
loadingDocuments: (_t = cfg == null ? void 0 : cfg.content) == null ? void 0 : _t.loadingDocuments,
|
|
3180
|
+
noDocuments: (_u = cfg == null ? void 0 : cfg.content) == null ? void 0 : _u.noDocuments,
|
|
2889
3181
|
previewMode: cfg == null ? void 0 : cfg.previewMode,
|
|
2890
|
-
structureTool: cfg == null ? void 0 : cfg.structureTool
|
|
3182
|
+
structureTool: cfg == null ? void 0 : cfg.structureTool,
|
|
3183
|
+
deprecationWarnings
|
|
2891
3184
|
};
|
|
2892
3185
|
};
|
|
2893
3186
|
var seofields = (0, import_sanity20.definePlugin)((config = {}) => {
|
|
2894
3187
|
const { healthDashboard = true } = config;
|
|
2895
3188
|
const dash = resolveDashboardConfig(healthDashboard);
|
|
2896
|
-
const
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
3189
|
+
const LazySeoHealthTool = import_react14.default.lazy(() => Promise.resolve().then(() => (init_SeoHealthTool(), SeoHealthTool_exports)));
|
|
3190
|
+
const BoundSeoHealthTool = () => import_react14.default.createElement(
|
|
3191
|
+
import_react14.default.Suspense,
|
|
3192
|
+
{ fallback: null },
|
|
3193
|
+
import_react14.default.createElement(LazySeoHealthTool, {
|
|
3194
|
+
icon: dash.icon,
|
|
3195
|
+
title: dash.title,
|
|
3196
|
+
description: dash.description,
|
|
3197
|
+
showTypeColumn: dash.showTypeColumn,
|
|
3198
|
+
showDocumentId: dash.showDocumentId,
|
|
3199
|
+
queryTypes: dash.queryTypes,
|
|
3200
|
+
queryRequireSeo: dash.queryRequireSeo,
|
|
3201
|
+
customQuery: dash.queryGroq,
|
|
3202
|
+
apiVersion: dash.apiVersion,
|
|
3203
|
+
licenseKey: dash.licenseKey,
|
|
3204
|
+
typeDisplayLabels: dash.typeDisplayLabels,
|
|
3205
|
+
typeColumnMode: dash.typeColumnMode,
|
|
3206
|
+
titleField: dash.titleField,
|
|
3207
|
+
getDocumentBadge: dash.getDocumentBadge,
|
|
3208
|
+
loadingLicense: dash.loadingLicense,
|
|
3209
|
+
loadingDocuments: dash.loadingDocuments,
|
|
3210
|
+
noDocuments: dash.noDocuments,
|
|
3211
|
+
previewMode: dash.previewMode,
|
|
3212
|
+
structureTool: dash.structureTool,
|
|
3213
|
+
_deprecationWarnings: dash.deprecationWarnings
|
|
3214
|
+
})
|
|
3215
|
+
);
|
|
2917
3216
|
return __spreadValues({
|
|
2918
3217
|
name: "sanity-plugin-seofields",
|
|
2919
3218
|
schema: {
|
|
@@ -2933,11 +3232,20 @@ var seofields = (0, import_sanity20.definePlugin)((config = {}) => {
|
|
|
2933
3232
|
var plugin_default = seofields;
|
|
2934
3233
|
|
|
2935
3234
|
// src/components/SeoHealthPane.tsx
|
|
3235
|
+
var import_react15 = __toESM(require("react"), 1);
|
|
2936
3236
|
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
3237
|
+
var LazySeoHealthDashboard = import_react15.default.lazy(() => Promise.resolve().then(() => (init_SeoHealthDashboard(), SeoHealthDashboard_exports)));
|
|
2937
3238
|
function createSeoHealthPane(optionsOrS, optionsWhenS) {
|
|
2938
3239
|
const S = optionsOrS;
|
|
2939
3240
|
const _a = optionsWhenS != null ? optionsWhenS : {}, { query, openInPane = true, title: paneTitle } = _a, rest = __objRest(_a, ["query", "openInPane", "title"]);
|
|
2940
|
-
const SeoHealthPane = () => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3241
|
+
const SeoHealthPane = () => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react15.default.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3242
|
+
LazySeoHealthDashboard,
|
|
3243
|
+
__spreadValues({
|
|
3244
|
+
customQuery: query,
|
|
3245
|
+
openInPane,
|
|
3246
|
+
title: paneTitle
|
|
3247
|
+
}, rest)
|
|
3248
|
+
) });
|
|
2941
3249
|
SeoHealthPane.displayName = "SeoHealthPane";
|
|
2942
3250
|
return S.component(SeoHealthPane).title(paneTitle != null ? paneTitle : "SEO Health").child((docId, { params }) => {
|
|
2943
3251
|
const builder = S.document().documentId(docId);
|
|
@@ -2949,8 +3257,6 @@ function createSeoHealthPane(optionsOrS, optionsWhenS) {
|
|
|
2949
3257
|
var src_default = plugin_default;
|
|
2950
3258
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2951
3259
|
0 && (module.exports = {
|
|
2952
|
-
SeoHealthDashboard,
|
|
2953
|
-
SeoHealthTool,
|
|
2954
3260
|
allSchemas,
|
|
2955
3261
|
createSeoHealthPane,
|
|
2956
3262
|
metaAttributeSchema,
|