sanity-plugin-seofields 1.4.1 → 1.5.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 +1 -1
- package/dist/SeoHealthDashboard-3VBITMWT.cjs +10 -0
- package/dist/SeoHealthDashboard-3VBITMWT.cjs.map +1 -0
- package/dist/SeoHealthDashboard-BNJBRQVN.js +4 -0
- package/dist/SeoHealthDashboard-BNJBRQVN.js.map +1 -0
- package/dist/SeoHealthTool-4P6JST5S.cjs +14 -0
- package/dist/SeoHealthTool-4P6JST5S.cjs.map +1 -0
- package/dist/SeoHealthTool-MQBE5YGN.js +12 -0
- package/dist/SeoHealthTool-MQBE5YGN.js.map +1 -0
- package/dist/SeoPreview-G3LPA7GV.js +148 -0
- package/dist/SeoPreview-G3LPA7GV.js.map +1 -0
- package/dist/SeoPreview-Y3CFDVBR.cjs +154 -0
- package/dist/SeoPreview-Y3CFDVBR.cjs.map +1 -0
- package/dist/chunk-25JLWVEU.js +472 -0
- package/dist/chunk-25JLWVEU.js.map +1 -0
- package/dist/chunk-2NMEKWO5.js +35 -0
- package/dist/chunk-2NMEKWO5.js.map +1 -0
- package/dist/chunk-527WXITP.js +428 -0
- package/dist/chunk-527WXITP.js.map +1 -0
- package/dist/chunk-6CYMVS3O.js +1245 -0
- package/dist/chunk-6CYMVS3O.js.map +1 -0
- package/dist/chunk-D2GWRRK5.cjs +1293 -0
- package/dist/chunk-D2GWRRK5.cjs.map +1 -0
- package/dist/chunk-DYVCCQHT.cjs +1400 -0
- package/dist/chunk-DYVCCQHT.cjs.map +1 -0
- package/dist/chunk-IFDLKZET.cjs +485 -0
- package/dist/chunk-IFDLKZET.cjs.map +1 -0
- package/dist/chunk-L3L3FSPJ.cjs +478 -0
- package/dist/chunk-L3L3FSPJ.cjs.map +1 -0
- package/dist/chunk-S367Y35J.cjs +39 -0
- package/dist/chunk-S367Y35J.cjs.map +1 -0
- package/dist/chunk-WUIZN7VI.js +1394 -0
- package/dist/chunk-WUIZN7VI.js.map +1 -0
- package/dist/cli.js +67 -0
- package/dist/define-cli.cjs +12 -0
- package/dist/define-cli.cjs.map +1 -0
- package/dist/define-cli.d.cts +42 -0
- package/dist/define-cli.d.ts +42 -0
- package/dist/define-cli.js +10 -0
- package/dist/define-cli.js.map +1 -0
- package/dist/index.cjs +154 -2365
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +109 -2303
- package/dist/index.js.map +1 -1
- package/dist/next.cjs +235 -1591
- package/dist/next.cjs.map +1 -1
- package/dist/next.js +7 -1482
- package/dist/next.js.map +1 -1
- package/dist/schema/next.cjs +200 -1542
- package/dist/schema/next.cjs.map +1 -1
- package/dist/schema/next.js +4 -1475
- package/dist/schema/next.js.map +1 -1
- package/dist/schema.cjs +252 -1514
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.js +58 -1391
- package/dist/schema.js.map +1 -1
- package/package.json +21 -5
package/dist/index.js
CHANGED
|
@@ -1,2107 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
8
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
|
-
var __spreadValues = (a, b) => {
|
|
10
|
-
for (var prop in b || (b = {}))
|
|
11
|
-
if (__hasOwnProp.call(b, prop))
|
|
12
|
-
__defNormalProp(a, prop, b[prop]);
|
|
13
|
-
if (__getOwnPropSymbols)
|
|
14
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
15
|
-
if (__propIsEnum.call(b, prop))
|
|
16
|
-
__defNormalProp(a, prop, b[prop]);
|
|
17
|
-
}
|
|
18
|
-
return a;
|
|
19
|
-
};
|
|
20
|
-
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
21
|
-
var __objRest = (source, exclude) => {
|
|
22
|
-
var target = {};
|
|
23
|
-
for (var prop in source)
|
|
24
|
-
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
25
|
-
target[prop] = source[prop];
|
|
26
|
-
if (source != null && __getOwnPropSymbols)
|
|
27
|
-
for (var prop of __getOwnPropSymbols(source)) {
|
|
28
|
-
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
29
|
-
target[prop] = source[prop];
|
|
30
|
-
}
|
|
31
|
-
return target;
|
|
32
|
-
};
|
|
33
|
-
var __esm = (fn, res) => function __init() {
|
|
34
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
35
|
-
};
|
|
36
|
-
var __export = (target, all) => {
|
|
37
|
-
for (var name in all)
|
|
38
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
// src/utils/seoUtils.ts
|
|
42
|
-
var stopWords, hasMatchingKeyword, hasKeywordOveruse, startsWithStopWord, truncate, hasExcessivePunctuation, getMetaTitleValidationMessages, getMetaDescriptionValidationMessages, getOgTitleValidation, getOgDescriptionValidation, getTwitterTitleValidation, getTwitterDescriptionValidation, isSubImageSet, isMetaImageSet, getMetaImageValidation, getOgImageValidation, getOgImageUrlValidation, getTwitterImageValidation, getTwitterImageUrlValidation;
|
|
43
|
-
var init_seoUtils = __esm({
|
|
44
|
-
"src/utils/seoUtils.ts"() {
|
|
45
|
-
"use strict";
|
|
46
|
-
stopWords = ["the", "a", "an", "and", "or", "but"];
|
|
47
|
-
hasMatchingKeyword = (title, keywordList) => {
|
|
48
|
-
if (!title || keywordList.length === 0) return false;
|
|
49
|
-
const lowerTitle = title.toLowerCase();
|
|
50
|
-
return keywordList.some((keyword) => keyword && lowerTitle.includes(keyword.toLowerCase()));
|
|
51
|
-
};
|
|
52
|
-
hasKeywordOveruse = (title, keywordList, maxOccurrences = 3) => {
|
|
53
|
-
if (!title || keywordList.length === 0) return false;
|
|
54
|
-
const lowerTitle = title.toLowerCase();
|
|
55
|
-
return keywordList.some((keyword) => {
|
|
56
|
-
if (!keyword) return false;
|
|
57
|
-
const matches = lowerTitle.match(new RegExp(keyword.toLowerCase(), "g"));
|
|
58
|
-
return matches ? matches.length > maxOccurrences : false;
|
|
59
|
-
});
|
|
60
|
-
};
|
|
61
|
-
startsWithStopWord = (title) => {
|
|
62
|
-
if (!title) return false;
|
|
63
|
-
const firstWord = title.trim().split(" ")[0].toLowerCase();
|
|
64
|
-
return stopWords.includes(firstWord);
|
|
65
|
-
};
|
|
66
|
-
truncate = (text, maxLength) => text.length > maxLength ? `${text.slice(0, maxLength)}\u2026` : text;
|
|
67
|
-
hasExcessivePunctuation = (title) => /[!@#$%^&*]{2,}/.test(title);
|
|
68
|
-
getMetaTitleValidationMessages = (title, keywords, isParentseoField) => {
|
|
69
|
-
const feedback = [];
|
|
70
|
-
const minChar = 50;
|
|
71
|
-
const maxChar = 60;
|
|
72
|
-
const charCount = (title == null ? void 0 : title.length) || 0;
|
|
73
|
-
if (!(title == null ? void 0 : title.trim())) {
|
|
74
|
-
feedback.push({ text: "Meta Title is empty. Add content to improve SEO.", color: "red" });
|
|
75
|
-
return feedback;
|
|
76
|
-
}
|
|
77
|
-
if (charCount < minChar)
|
|
78
|
-
feedback.push({
|
|
79
|
-
text: `Title is ${charCount} characters \u2014 below recommended ${minChar}.`,
|
|
80
|
-
color: "orange"
|
|
81
|
-
});
|
|
82
|
-
else if (charCount > maxChar)
|
|
83
|
-
feedback.push({
|
|
84
|
-
text: `Title is ${charCount} characters \u2014 exceeds recommended ${maxChar}.`,
|
|
85
|
-
color: "red"
|
|
86
|
-
});
|
|
87
|
-
else feedback.push({ text: `Title length (${charCount}) looks good for SEO.`, color: "green" });
|
|
88
|
-
if (isParentseoField) {
|
|
89
|
-
if (keywords.length > 0) {
|
|
90
|
-
const hasKeyword = hasMatchingKeyword(title, keywords);
|
|
91
|
-
feedback.push({
|
|
92
|
-
text: hasKeyword ? "Keyword found in title \u2014 good job!" : "Keywords defined but missing in title.",
|
|
93
|
-
color: hasKeyword ? "green" : "red"
|
|
94
|
-
});
|
|
95
|
-
if (hasKeywordOveruse(title, keywords)) {
|
|
96
|
-
feedback.push({
|
|
97
|
-
text: "Keyword appears too many times \u2014 avoid keyword stuffing.",
|
|
98
|
-
color: "orange"
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
} else {
|
|
102
|
-
feedback.push({
|
|
103
|
-
text: "No keywords defined. Consider adding relevant keywords.",
|
|
104
|
-
color: "orange"
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
if (startsWithStopWord(title))
|
|
109
|
-
feedback.push({ text: "Title starts with a stop word \u2014 consider rephrasing.", color: "orange" });
|
|
110
|
-
if (hasExcessivePunctuation(title))
|
|
111
|
-
feedback.push({ text: "Title contains excessive punctuation \u2014 simplify it.", color: "orange" });
|
|
112
|
-
return feedback;
|
|
113
|
-
};
|
|
114
|
-
getMetaDescriptionValidationMessages = (description, keywords, isParentseoField) => {
|
|
115
|
-
const feedback = [];
|
|
116
|
-
const minChar = 120;
|
|
117
|
-
const maxChar = 160;
|
|
118
|
-
const charCount = (description == null ? void 0 : description.length) || 0;
|
|
119
|
-
if (!(description == null ? void 0 : description.trim())) {
|
|
120
|
-
feedback.push({ text: "Meta description is empty. Add content to improve SEO.", color: "red" });
|
|
121
|
-
return feedback;
|
|
122
|
-
}
|
|
123
|
-
if (charCount < minChar)
|
|
124
|
-
feedback.push({
|
|
125
|
-
text: `Description is ${charCount} chars \u2014 below recommended ${minChar}.`,
|
|
126
|
-
color: "orange"
|
|
127
|
-
});
|
|
128
|
-
else if (charCount > maxChar)
|
|
129
|
-
feedback.push({
|
|
130
|
-
text: `Description is ${charCount} chars \u2014 exceeds recommended ${maxChar}.`,
|
|
131
|
-
color: "red"
|
|
132
|
-
});
|
|
133
|
-
else
|
|
134
|
-
feedback.push({ text: `Description length (${charCount}) looks good for SEO.`, color: "green" });
|
|
135
|
-
if (isParentseoField) {
|
|
136
|
-
if (keywords.length > 0) {
|
|
137
|
-
const hasKeyword = hasMatchingKeyword(description, keywords);
|
|
138
|
-
feedback.push({
|
|
139
|
-
text: hasKeyword ? "Keyword found in description \u2014 good job!" : "Keywords defined but missing in description.",
|
|
140
|
-
color: hasKeyword ? "green" : "red"
|
|
141
|
-
});
|
|
142
|
-
if (hasKeywordOveruse(description, keywords)) {
|
|
143
|
-
feedback.push({
|
|
144
|
-
text: "Keyword appears too many times \u2014 avoid keyword stuffing.",
|
|
145
|
-
color: "orange"
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
} else {
|
|
149
|
-
feedback.push({
|
|
150
|
-
text: "No keywords defined. Consider adding relevant keywords.",
|
|
151
|
-
color: "orange"
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
if (startsWithStopWord(description))
|
|
156
|
-
feedback.push({
|
|
157
|
-
text: "Description starts with a stop word \u2014 consider rephrasing.",
|
|
158
|
-
color: "orange"
|
|
159
|
-
});
|
|
160
|
-
if (hasExcessivePunctuation(description))
|
|
161
|
-
feedback.push({
|
|
162
|
-
text: "Description contains excessive punctuation \u2014 simplify it.",
|
|
163
|
-
color: "orange"
|
|
164
|
-
});
|
|
165
|
-
return feedback;
|
|
166
|
-
};
|
|
167
|
-
getOgTitleValidation = (title, keywords = [], isParentseoField) => {
|
|
168
|
-
const feedback = [];
|
|
169
|
-
const min = 40;
|
|
170
|
-
const max = 60;
|
|
171
|
-
const count = (title == null ? void 0 : title.length) || 0;
|
|
172
|
-
if (!(title == null ? void 0 : title.trim())) {
|
|
173
|
-
feedback.push({ text: "OG Title is empty. Add content for better social preview.", color: "red" });
|
|
174
|
-
return feedback;
|
|
175
|
-
}
|
|
176
|
-
if (count < min)
|
|
177
|
-
feedback.push({
|
|
178
|
-
text: `OG Title is ${count} chars \u2014 shorter than recommended ${min}.`,
|
|
179
|
-
color: "orange"
|
|
180
|
-
});
|
|
181
|
-
else if (count > max)
|
|
182
|
-
feedback.push({ text: `OG Title is ${count} chars \u2014 exceeds recommended ${max}.`, color: "red" });
|
|
183
|
-
else feedback.push({ text: `OG Title length (${count}) looks good.`, color: "green" });
|
|
184
|
-
if (isParentseoField) {
|
|
185
|
-
if (keywords.length > 0) {
|
|
186
|
-
const hasKeyword = hasMatchingKeyword(title, keywords);
|
|
187
|
-
feedback.push({
|
|
188
|
-
text: hasKeyword ? "Keyword found in OG title \u2014 good job!" : "Keywords defined but missing in OG title.",
|
|
189
|
-
color: hasKeyword ? "green" : "red"
|
|
190
|
-
});
|
|
191
|
-
if (hasKeywordOveruse(title, keywords)) {
|
|
192
|
-
feedback.push({
|
|
193
|
-
text: "Keyword appears too many times in OG title \u2014 avoid keyword stuffing.",
|
|
194
|
-
color: "orange"
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
} else {
|
|
198
|
-
feedback.push({
|
|
199
|
-
text: "No keywords defined. Consider adding relevant keywords.",
|
|
200
|
-
color: "orange"
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
if (startsWithStopWord(title))
|
|
205
|
-
feedback.push({
|
|
206
|
-
text: "OG Title starts with a stop word \u2014 consider rephrasing.",
|
|
207
|
-
color: "orange"
|
|
208
|
-
});
|
|
209
|
-
if (hasExcessivePunctuation(title))
|
|
210
|
-
feedback.push({ text: "OG Title contains excessive punctuation \u2014 simplify it.", color: "orange" });
|
|
211
|
-
return feedback;
|
|
212
|
-
};
|
|
213
|
-
getOgDescriptionValidation = (desc, keywords = [], isParentseoField) => {
|
|
214
|
-
const feedback = [];
|
|
215
|
-
const min = 90;
|
|
216
|
-
const max = 120;
|
|
217
|
-
const count = (desc == null ? void 0 : desc.length) || 0;
|
|
218
|
-
if (!(desc == null ? void 0 : desc.trim())) {
|
|
219
|
-
feedback.push({
|
|
220
|
-
text: "OG Description is empty. Add content for better social preview.",
|
|
221
|
-
color: "red"
|
|
222
|
-
});
|
|
223
|
-
return feedback;
|
|
224
|
-
}
|
|
225
|
-
if (count < min)
|
|
226
|
-
feedback.push({
|
|
227
|
-
text: `OG Description is ${count} chars \u2014 shorter than recommended ${min}.`,
|
|
228
|
-
color: "orange"
|
|
229
|
-
});
|
|
230
|
-
else if (count > max)
|
|
231
|
-
feedback.push({
|
|
232
|
-
text: `OG Description is ${count} chars \u2014 exceeds recommended ${max}.`,
|
|
233
|
-
color: "red"
|
|
234
|
-
});
|
|
235
|
-
else feedback.push({ text: `OG Description length (${count}) looks good.`, color: "green" });
|
|
236
|
-
if (isParentseoField) {
|
|
237
|
-
if (keywords.length > 0) {
|
|
238
|
-
const hasKeyword = hasMatchingKeyword(desc, keywords);
|
|
239
|
-
feedback.push({
|
|
240
|
-
text: hasKeyword ? "Keyword found in OG description \u2014 good job!" : "Keywords defined but missing in OG description.",
|
|
241
|
-
color: hasKeyword ? "green" : "red"
|
|
242
|
-
});
|
|
243
|
-
if (hasKeywordOveruse(desc, keywords)) {
|
|
244
|
-
feedback.push({
|
|
245
|
-
text: "Keyword appears too many times in OG description \u2014 avoid keyword stuffing.",
|
|
246
|
-
color: "orange"
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
} else {
|
|
250
|
-
feedback.push({
|
|
251
|
-
text: "No keywords defined. Consider adding relevant keywords.",
|
|
252
|
-
color: "orange"
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
if (startsWithStopWord(desc))
|
|
257
|
-
feedback.push({
|
|
258
|
-
text: "OG Description starts with a stop word \u2014 consider rephrasing.",
|
|
259
|
-
color: "orange"
|
|
260
|
-
});
|
|
261
|
-
if (hasExcessivePunctuation(desc))
|
|
262
|
-
feedback.push({
|
|
263
|
-
text: "OG Description contains excessive punctuation \u2014 simplify it.",
|
|
264
|
-
color: "orange"
|
|
265
|
-
});
|
|
266
|
-
return feedback;
|
|
267
|
-
};
|
|
268
|
-
getTwitterTitleValidation = (title, keywords = [], isParentseoField) => {
|
|
269
|
-
const feedback = [];
|
|
270
|
-
const min = 30;
|
|
271
|
-
const max = 70;
|
|
272
|
-
const count = (title == null ? void 0 : title.length) || 0;
|
|
273
|
-
if (!(title == null ? void 0 : title.trim())) {
|
|
274
|
-
feedback.push({ text: "X Title is empty. Add content for better SEO.", color: "red" });
|
|
275
|
-
return feedback;
|
|
276
|
-
}
|
|
277
|
-
if (count < min)
|
|
278
|
-
feedback.push({
|
|
279
|
-
text: `X Title is ${count} chars \u2014 shorter than recommended ${min}.`,
|
|
280
|
-
color: "orange"
|
|
281
|
-
});
|
|
282
|
-
else if (count > max)
|
|
283
|
-
feedback.push({
|
|
284
|
-
text: `X Title is ${count} chars \u2014 exceeds recommended ${max}.`,
|
|
285
|
-
color: "red"
|
|
286
|
-
});
|
|
287
|
-
else feedback.push({ text: `X Title length (${count}) looks good.`, color: "green" });
|
|
288
|
-
if (isParentseoField) {
|
|
289
|
-
if (keywords.length > 0) {
|
|
290
|
-
const hasKeyword = hasMatchingKeyword(title, keywords);
|
|
291
|
-
feedback.push({
|
|
292
|
-
text: hasKeyword ? "Keyword found in X title \u2014 good job!" : "Keywords defined but missing in X title.",
|
|
293
|
-
color: hasKeyword ? "green" : "red"
|
|
294
|
-
});
|
|
295
|
-
} else {
|
|
296
|
-
feedback.push({
|
|
297
|
-
text: "No keywords defined. Consider adding relevant keywords.",
|
|
298
|
-
color: "orange"
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
if (/[!@#$%^&*]{2,}/.test(title))
|
|
303
|
-
feedback.push({ text: "X Title has excessive punctuation \u2014 simplify it.", color: "orange" });
|
|
304
|
-
return feedback;
|
|
305
|
-
};
|
|
306
|
-
getTwitterDescriptionValidation = (desc, keywords = [], isParentseoField) => {
|
|
307
|
-
const feedback = [];
|
|
308
|
-
const min = 50;
|
|
309
|
-
const max = 200;
|
|
310
|
-
const count = (desc == null ? void 0 : desc.length) || 0;
|
|
311
|
-
if (!(desc == null ? void 0 : desc.trim())) {
|
|
312
|
-
feedback.push({ text: "X Description is empty. Add content for better SEO.", color: "red" });
|
|
313
|
-
return feedback;
|
|
314
|
-
}
|
|
315
|
-
if (count < min)
|
|
316
|
-
feedback.push({
|
|
317
|
-
text: `X Description is ${count} chars \u2014 shorter than recommended ${min}.`,
|
|
318
|
-
color: "orange"
|
|
319
|
-
});
|
|
320
|
-
else if (count > max)
|
|
321
|
-
feedback.push({
|
|
322
|
-
text: `X Description is ${count} chars \u2014 exceeds recommended ${max}.`,
|
|
323
|
-
color: "red"
|
|
324
|
-
});
|
|
325
|
-
else feedback.push({ text: `X Description length (${count}) looks good.`, color: "green" });
|
|
326
|
-
if (isParentseoField) {
|
|
327
|
-
if (keywords.length > 0) {
|
|
328
|
-
const hasKeyword = hasMatchingKeyword(desc, keywords);
|
|
329
|
-
feedback.push({
|
|
330
|
-
text: hasKeyword ? "Keyword found in X description \u2014 good job!" : "Keywords defined but missing in X description.",
|
|
331
|
-
color: hasKeyword ? "green" : "red"
|
|
332
|
-
});
|
|
333
|
-
} else {
|
|
334
|
-
feedback.push({
|
|
335
|
-
text: "No keywords defined. Consider adding relevant keywords.",
|
|
336
|
-
color: "orange"
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
if (/[!@#$%^&*]{2,}/.test(desc))
|
|
341
|
-
feedback.push({
|
|
342
|
-
text: "X Description has excessive punctuation \u2014 simplify it.",
|
|
343
|
-
color: "orange"
|
|
344
|
-
});
|
|
345
|
-
return feedback;
|
|
346
|
-
};
|
|
347
|
-
isSubImageSet = (subObj) => {
|
|
348
|
-
var _a;
|
|
349
|
-
if (!subObj) return false;
|
|
350
|
-
if (subObj.imageType === "url") return !!((_a = subObj.imageUrl) == null ? void 0 : _a.trim());
|
|
351
|
-
const img = subObj.image;
|
|
352
|
-
return !!(img == null ? void 0 : img.asset);
|
|
353
|
-
};
|
|
354
|
-
isMetaImageSet = (seoParent) => {
|
|
355
|
-
if (!seoParent) return false;
|
|
356
|
-
const metaImage = seoParent.metaImage;
|
|
357
|
-
return !!(metaImage == null ? void 0 : metaImage.asset);
|
|
358
|
-
};
|
|
359
|
-
getMetaImageValidation = (hasImage, seoParent) => {
|
|
360
|
-
const feedback = [];
|
|
361
|
-
if (!hasImage) {
|
|
362
|
-
feedback.push({
|
|
363
|
-
text: "No meta image provided. Adding an image improves click-through rates.",
|
|
364
|
-
color: "red"
|
|
365
|
-
});
|
|
366
|
-
return feedback;
|
|
367
|
-
}
|
|
368
|
-
feedback.push({ text: "Meta image is set \u2014 great for SEO and social sharing.", color: "green" });
|
|
369
|
-
const ogSet = isSubImageSet(seoParent == null ? void 0 : seoParent.openGraph);
|
|
370
|
-
const twSet = isSubImageSet(seoParent == null ? void 0 : seoParent.twitter);
|
|
371
|
-
if (!ogSet && !twSet) {
|
|
372
|
-
feedback.push({
|
|
373
|
-
text: "OG and Twitter images are missing \u2014 add them for full social coverage.",
|
|
374
|
-
color: "orange"
|
|
375
|
-
});
|
|
376
|
-
} else if (!ogSet) {
|
|
377
|
-
feedback.push({
|
|
378
|
-
text: "OG image is missing \u2014 add it for better Facebook/LinkedIn previews.",
|
|
379
|
-
color: "orange"
|
|
380
|
-
});
|
|
381
|
-
} else if (twSet) {
|
|
382
|
-
feedback.push({ text: "All images set (Meta, OG, Twitter) \u2014 full coverage!", color: "green" });
|
|
383
|
-
} else {
|
|
384
|
-
feedback.push({
|
|
385
|
-
text: "Twitter image is missing \u2014 add it for better X (Twitter) cards.",
|
|
386
|
-
color: "orange"
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
return feedback;
|
|
390
|
-
};
|
|
391
|
-
getOgImageValidation = (hasImage, altText, seoParent) => {
|
|
392
|
-
const feedback = [];
|
|
393
|
-
if (!hasImage) {
|
|
394
|
-
feedback.push({
|
|
395
|
-
text: "No OG image provided. Social shares will lack a visual preview.",
|
|
396
|
-
color: "red"
|
|
397
|
-
});
|
|
398
|
-
return feedback;
|
|
399
|
-
}
|
|
400
|
-
feedback.push({ text: "OG image is set \u2014 good for social sharing.", color: "green" });
|
|
401
|
-
if (altText == null ? void 0 : altText.trim()) {
|
|
402
|
-
feedback.push({ text: "Alt text is set \u2014 good for accessibility.", color: "green" });
|
|
403
|
-
} else {
|
|
404
|
-
feedback.push({ text: "Consider adding alt text for better accessibility.", color: "orange" });
|
|
405
|
-
}
|
|
406
|
-
const metaSet = isMetaImageSet(seoParent);
|
|
407
|
-
const twSet = isSubImageSet(seoParent == null ? void 0 : seoParent.twitter);
|
|
408
|
-
if (metaSet && twSet) {
|
|
409
|
-
feedback.push({ text: "All images set (Meta, OG, Twitter) \u2014 full coverage!", color: "green" });
|
|
410
|
-
} else {
|
|
411
|
-
if (!metaSet)
|
|
412
|
-
feedback.push({
|
|
413
|
-
text: "Meta image is missing \u2014 add it for search engine results.",
|
|
414
|
-
color: "orange"
|
|
415
|
-
});
|
|
416
|
-
if (!twSet)
|
|
417
|
-
feedback.push({
|
|
418
|
-
text: "Twitter image is missing \u2014 add it for X (Twitter) cards.",
|
|
419
|
-
color: "orange"
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
return feedback;
|
|
423
|
-
};
|
|
424
|
-
getOgImageUrlValidation = (imageUrl, seoParent) => {
|
|
425
|
-
const feedback = [];
|
|
426
|
-
if (!(imageUrl == null ? void 0 : imageUrl.trim())) {
|
|
427
|
-
feedback.push({
|
|
428
|
-
text: "No OG image URL provided. Social shares will lack a visual preview.",
|
|
429
|
-
color: "red"
|
|
430
|
-
});
|
|
431
|
-
return feedback;
|
|
432
|
-
}
|
|
433
|
-
feedback.push({ text: "OG image URL is set \u2014 good for social sharing.", color: "green" });
|
|
434
|
-
const metaSet = isMetaImageSet(seoParent);
|
|
435
|
-
const twSet = isSubImageSet(seoParent == null ? void 0 : seoParent.twitter);
|
|
436
|
-
if (metaSet && twSet) {
|
|
437
|
-
feedback.push({ text: "All images set (Meta, OG, Twitter) \u2014 full coverage!", color: "green" });
|
|
438
|
-
} else {
|
|
439
|
-
if (!metaSet)
|
|
440
|
-
feedback.push({
|
|
441
|
-
text: "Meta image is missing \u2014 add it for search engine results.",
|
|
442
|
-
color: "orange"
|
|
443
|
-
});
|
|
444
|
-
if (!twSet)
|
|
445
|
-
feedback.push({
|
|
446
|
-
text: "Twitter image is missing \u2014 add it for X (Twitter) cards.",
|
|
447
|
-
color: "orange"
|
|
448
|
-
});
|
|
449
|
-
}
|
|
450
|
-
return feedback;
|
|
451
|
-
};
|
|
452
|
-
getTwitterImageValidation = (hasImage, altText, seoParent) => {
|
|
453
|
-
const feedback = [];
|
|
454
|
-
if (!hasImage) {
|
|
455
|
-
feedback.push({
|
|
456
|
-
text: "No Twitter image provided. Posts on X will lack a visual.",
|
|
457
|
-
color: "red"
|
|
458
|
-
});
|
|
459
|
-
return feedback;
|
|
460
|
-
}
|
|
461
|
-
feedback.push({ text: "Twitter image is set \u2014 good for X sharing.", color: "green" });
|
|
462
|
-
if (altText == null ? void 0 : altText.trim()) {
|
|
463
|
-
feedback.push({ text: "Alt text is set \u2014 good for accessibility.", color: "green" });
|
|
464
|
-
} else {
|
|
465
|
-
feedback.push({ text: "Consider adding alt text for better accessibility.", color: "orange" });
|
|
466
|
-
}
|
|
467
|
-
const metaSet = isMetaImageSet(seoParent);
|
|
468
|
-
const ogSet = isSubImageSet(seoParent == null ? void 0 : seoParent.openGraph);
|
|
469
|
-
if (metaSet && ogSet) {
|
|
470
|
-
feedback.push({ text: "All images set (Meta, OG, Twitter) \u2014 full coverage!", color: "green" });
|
|
471
|
-
} else {
|
|
472
|
-
if (!metaSet)
|
|
473
|
-
feedback.push({
|
|
474
|
-
text: "Meta image is missing \u2014 add it for search engine results.",
|
|
475
|
-
color: "orange"
|
|
476
|
-
});
|
|
477
|
-
if (!ogSet)
|
|
478
|
-
feedback.push({
|
|
479
|
-
text: "OG image is missing \u2014 add it for Facebook/LinkedIn sharing.",
|
|
480
|
-
color: "orange"
|
|
481
|
-
});
|
|
482
|
-
}
|
|
483
|
-
return feedback;
|
|
484
|
-
};
|
|
485
|
-
getTwitterImageUrlValidation = (imageUrl, seoParent) => {
|
|
486
|
-
const feedback = [];
|
|
487
|
-
if (!(imageUrl == null ? void 0 : imageUrl.trim())) {
|
|
488
|
-
feedback.push({
|
|
489
|
-
text: "No Twitter image URL provided. Posts on X will lack a visual.",
|
|
490
|
-
color: "red"
|
|
491
|
-
});
|
|
492
|
-
return feedback;
|
|
493
|
-
}
|
|
494
|
-
feedback.push({ text: "Twitter image URL is set \u2014 good for X sharing.", color: "green" });
|
|
495
|
-
const metaSet = isMetaImageSet(seoParent);
|
|
496
|
-
const ogSet = isSubImageSet(seoParent == null ? void 0 : seoParent.openGraph);
|
|
497
|
-
if (metaSet && ogSet) {
|
|
498
|
-
feedback.push({ text: "All images set (Meta, OG, Twitter) \u2014 full coverage!", color: "green" });
|
|
499
|
-
} else {
|
|
500
|
-
if (!metaSet)
|
|
501
|
-
feedback.push({
|
|
502
|
-
text: "Meta image is missing \u2014 add it for search engine results.",
|
|
503
|
-
color: "orange"
|
|
504
|
-
});
|
|
505
|
-
if (!ogSet)
|
|
506
|
-
feedback.push({
|
|
507
|
-
text: "OG image is missing \u2014 add it for Facebook/LinkedIn sharing.",
|
|
508
|
-
color: "orange"
|
|
509
|
-
});
|
|
510
|
-
}
|
|
511
|
-
return feedback;
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
// src/components/SeoPreview.tsx
|
|
517
|
-
var SeoPreview_exports = {};
|
|
518
|
-
__export(SeoPreview_exports, {
|
|
519
|
-
default: () => SeoPreview_default
|
|
520
|
-
});
|
|
521
|
-
import { Box } from "@sanity/ui";
|
|
522
|
-
import { useFormValue as useFormValue12 } from "sanity";
|
|
523
|
-
import styled from "styled-components";
|
|
524
|
-
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
525
|
-
var PreviewContainer, PreviewHeader, PreviewBody, SerpUrl, SerpTitle, SerpDescription, LiveIndicator, SeoPreview, SeoPreview_default;
|
|
526
|
-
var init_SeoPreview = __esm({
|
|
527
|
-
"src/components/SeoPreview.tsx"() {
|
|
528
|
-
"use strict";
|
|
529
|
-
init_seoUtils();
|
|
530
|
-
PreviewContainer = styled.div`
|
|
531
|
-
max-width: 600px;
|
|
532
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
533
|
-
background: #ffffff;
|
|
534
|
-
border: 1px solid #dadce0;
|
|
535
|
-
border-radius: 8px;
|
|
536
|
-
overflow: hidden;
|
|
537
|
-
`;
|
|
538
|
-
PreviewHeader = styled.div`
|
|
539
|
-
background: #f8f9fa;
|
|
540
|
-
padding: 12px 16px;
|
|
541
|
-
border-bottom: 1px solid #dadce0;
|
|
542
|
-
display: flex;
|
|
543
|
-
align-items: center;
|
|
544
|
-
justify-content: space-between;
|
|
545
|
-
gap: 8px;
|
|
546
|
-
`;
|
|
547
|
-
PreviewBody = styled.div`
|
|
548
|
-
padding: 16px;
|
|
549
|
-
`;
|
|
550
|
-
SerpUrl = styled.p`
|
|
551
|
-
margin: 0 0 4px;
|
|
552
|
-
color: #006621;
|
|
553
|
-
font-size: 13px;
|
|
554
|
-
line-height: 1.4;
|
|
555
|
-
word-break: break-word;
|
|
556
|
-
`;
|
|
557
|
-
SerpTitle = styled.h3`
|
|
558
|
-
margin: 0 0 8px;
|
|
559
|
-
color: #1a0dab;
|
|
560
|
-
font-size: 18px;
|
|
561
|
-
font-weight: 500;
|
|
562
|
-
line-height: 1.4;
|
|
563
|
-
word-break: break-word;
|
|
564
|
-
|
|
565
|
-
&:hover {
|
|
566
|
-
text-decoration: underline;
|
|
567
|
-
}
|
|
568
|
-
`;
|
|
569
|
-
SerpDescription = styled.p`
|
|
570
|
-
margin: 0;
|
|
571
|
-
color: #545454;
|
|
572
|
-
font-size: 14px;
|
|
573
|
-
line-height: 1.6;
|
|
574
|
-
word-break: break-word;
|
|
575
|
-
display: -webkit-box;
|
|
576
|
-
-webkit-line-clamp: 2;
|
|
577
|
-
-webkit-box-orient: vertical;
|
|
578
|
-
overflow: hidden;
|
|
579
|
-
`;
|
|
580
|
-
LiveIndicator = styled.span`
|
|
581
|
-
display: inline-flex;
|
|
582
|
-
align-items: center;
|
|
583
|
-
gap: 4px;
|
|
584
|
-
font-size: 11px;
|
|
585
|
-
font-weight: 600;
|
|
586
|
-
text-transform: uppercase;
|
|
587
|
-
letter-spacing: 0.05em;
|
|
588
|
-
color: #4f46e5;
|
|
589
|
-
background: #f0f4ff;
|
|
590
|
-
padding: 4px 8px;
|
|
591
|
-
border-radius: 4px;
|
|
592
|
-
`;
|
|
593
|
-
SeoPreview = (props) => {
|
|
594
|
-
var _a, _b;
|
|
595
|
-
const { path, schemaType } = props;
|
|
596
|
-
const { options } = schemaType;
|
|
597
|
-
const baseUrl = (options == null ? void 0 : options.baseUrl) || "https://www.example.com";
|
|
598
|
-
const prefixFunction = options == null ? void 0 : options.prefix;
|
|
599
|
-
const parent = useFormValue12([path[0]]) || {
|
|
600
|
-
title: "",
|
|
601
|
-
description: "",
|
|
602
|
-
canonicalUrl: ""
|
|
603
|
-
};
|
|
604
|
-
const rootDoc = useFormValue12([]) || {
|
|
605
|
-
slug: { current: "" }
|
|
606
|
-
};
|
|
607
|
-
const slug = ((_a = rootDoc == null ? void 0 : rootDoc.slug) == null ? void 0 : _a.current) || "";
|
|
608
|
-
const {
|
|
609
|
-
title,
|
|
610
|
-
description,
|
|
611
|
-
canonicalUrl: url
|
|
612
|
-
} = parent;
|
|
613
|
-
const base = (_b = url || baseUrl) == null ? void 0 : _b.replace(/\/+$/, "");
|
|
614
|
-
const slugStr = String(slug || "").replace(/^\/+/, "");
|
|
615
|
-
const pref = String(
|
|
616
|
-
prefixFunction ? prefixFunction(rootDoc) : ""
|
|
617
|
-
).replace(/^\/+|\/+$/g, "");
|
|
618
|
-
const urlPath = [pref, slugStr].filter(Boolean).join("/");
|
|
619
|
-
const finalUrl = urlPath ? `${base}/${urlPath}` : base;
|
|
620
|
-
const domain = (() => {
|
|
621
|
-
try {
|
|
622
|
-
const u = new URL(finalUrl || base);
|
|
623
|
-
return u.hostname;
|
|
624
|
-
} catch (e) {
|
|
625
|
-
return "example.com";
|
|
626
|
-
}
|
|
627
|
-
})();
|
|
628
|
-
const urlDisplay = `${domain}${urlPath ? ` \u203A ${urlPath.split("/").slice(-1)[0]}` : ""}`;
|
|
629
|
-
return /* @__PURE__ */ jsx12(Box, { padding: 3, children: /* @__PURE__ */ jsxs12(PreviewContainer, { children: [
|
|
630
|
-
/* @__PURE__ */ jsxs12(PreviewHeader, { children: [
|
|
631
|
-
/* @__PURE__ */ jsx12(
|
|
632
|
-
"span",
|
|
633
|
-
{
|
|
634
|
-
style: {
|
|
635
|
-
fontSize: "11px",
|
|
636
|
-
color: "#5f6368",
|
|
637
|
-
textTransform: "uppercase",
|
|
638
|
-
letterSpacing: "0.05em"
|
|
639
|
-
},
|
|
640
|
-
children: "Search Preview"
|
|
641
|
-
}
|
|
642
|
-
),
|
|
643
|
-
/* @__PURE__ */ jsxs12(LiveIndicator, { children: [
|
|
644
|
-
/* @__PURE__ */ jsx12(
|
|
645
|
-
"span",
|
|
646
|
-
{
|
|
647
|
-
style: {
|
|
648
|
-
width: "4px",
|
|
649
|
-
height: "4px",
|
|
650
|
-
borderRadius: "50%",
|
|
651
|
-
backgroundColor: "#4f46e5",
|
|
652
|
-
display: "inline-block"
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
),
|
|
656
|
-
"Live"
|
|
657
|
-
] })
|
|
658
|
-
] }),
|
|
659
|
-
/* @__PURE__ */ jsxs12(PreviewBody, { children: [
|
|
660
|
-
/* @__PURE__ */ jsx12(SerpUrl, { children: finalUrl ? urlDisplay : "example.com \u203A page-url" }),
|
|
661
|
-
/* @__PURE__ */ jsx12(SerpTitle, { children: title && title.length > 0 ? truncate(title, 60) : "Your SEO Title will appear here" }),
|
|
662
|
-
/* @__PURE__ */ jsx12(SerpDescription, { children: description && description.length > 0 ? truncate(description, 160) : "Your meta description will show up here. Make it compelling!" })
|
|
663
|
-
] })
|
|
664
|
-
] }) });
|
|
665
|
-
};
|
|
666
|
-
SeoPreview_default = SeoPreview;
|
|
667
|
-
}
|
|
668
|
-
});
|
|
669
|
-
|
|
670
|
-
// src/components/SeoHealthDashboard.tsx
|
|
671
|
-
var SeoHealthDashboard_exports = {};
|
|
672
|
-
__export(SeoHealthDashboard_exports, {
|
|
673
|
-
default: () => SeoHealthDashboard_default
|
|
674
|
-
});
|
|
675
|
-
import { useCallback, useEffect, useMemo as useMemo12, useState } from "react";
|
|
676
|
-
import { useClient, useWorkspace } from "sanity";
|
|
677
|
-
import { useIntentLink } from "sanity/router";
|
|
678
|
-
import { usePaneRouter } from "sanity/structure";
|
|
679
|
-
import styled2, { css, keyframes } from "styled-components";
|
|
680
|
-
import { Fragment, jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
681
|
-
var 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;
|
|
682
|
-
var init_SeoHealthDashboard = __esm({
|
|
683
|
-
"src/components/SeoHealthDashboard.tsx"() {
|
|
684
|
-
"use strict";
|
|
685
|
-
DashboardContainer = styled2.div`
|
|
686
|
-
width: 100%;
|
|
687
|
-
min-height: 100%;
|
|
688
|
-
background: #f0f2f5;
|
|
689
|
-
padding: 28px 32px;
|
|
690
|
-
box-sizing: border-box;
|
|
691
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
692
|
-
`;
|
|
693
|
-
PageHeader = styled2.div`
|
|
694
|
-
display: flex;
|
|
695
|
-
align-items: flex-start;
|
|
696
|
-
justify-content: space-between;
|
|
697
|
-
gap: 12px;
|
|
698
|
-
margin-bottom: 28px;
|
|
699
|
-
`;
|
|
700
|
-
PageTitle = styled2.h1`
|
|
701
|
-
margin: 0 0 6px 0;
|
|
702
|
-
font-size: 22px;
|
|
703
|
-
font-weight: 700;
|
|
704
|
-
color: #111827;
|
|
705
|
-
letter-spacing: -0.3px;
|
|
706
|
-
display: flex;
|
|
707
|
-
align-items: center;
|
|
708
|
-
gap: 10px;
|
|
709
|
-
`;
|
|
710
|
-
PreviewBadge = styled2.span`
|
|
711
|
-
display: inline-block;
|
|
712
|
-
background: #fef3c7;
|
|
713
|
-
color: #92400e;
|
|
714
|
-
font-size: 11px;
|
|
715
|
-
font-weight: 600;
|
|
716
|
-
padding: 4px 8px;
|
|
717
|
-
border-radius: 4px;
|
|
718
|
-
text-transform: uppercase;
|
|
719
|
-
letter-spacing: 0.5px;
|
|
720
|
-
margin-left: 8px;
|
|
721
|
-
`;
|
|
722
|
-
PageSubtitle = styled2.p`
|
|
723
|
-
margin: 0;
|
|
724
|
-
font-size: 13px;
|
|
725
|
-
color: #6b7280;
|
|
726
|
-
`;
|
|
727
|
-
StatsGrid = styled2.div`
|
|
728
|
-
display: grid;
|
|
729
|
-
grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
|
|
730
|
-
gap: 14px;
|
|
731
|
-
margin-bottom: 20px;
|
|
732
|
-
`;
|
|
733
|
-
StatCard = styled2.div`
|
|
734
|
-
background: #ffffff;
|
|
735
|
-
border-radius: 10px;
|
|
736
|
-
padding: 16px 18px;
|
|
737
|
-
box-shadow:
|
|
738
|
-
0 1px 3px rgba(0, 0, 0, 0.07),
|
|
739
|
-
0 1px 2px rgba(0, 0, 0, 0.05);
|
|
740
|
-
border-left: ${(p) => p.$accent ? `4px solid ${p.$accent}` : "4px solid transparent"};
|
|
741
|
-
transition: box-shadow 0.15s ease;
|
|
742
|
-
|
|
743
|
-
&:hover {
|
|
744
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
745
|
-
}
|
|
746
|
-
`;
|
|
747
|
-
StatLabel = styled2.div`
|
|
748
|
-
font-size: 11px;
|
|
749
|
-
font-weight: 500;
|
|
750
|
-
color: #9ca3af;
|
|
751
|
-
text-transform: uppercase;
|
|
752
|
-
letter-spacing: 0.5px;
|
|
753
|
-
margin-bottom: 8px;
|
|
754
|
-
`;
|
|
755
|
-
StatValue = styled2.div`
|
|
756
|
-
font-size: 26px;
|
|
757
|
-
font-weight: 700;
|
|
758
|
-
color: #111827;
|
|
759
|
-
line-height: 1;
|
|
760
|
-
`;
|
|
761
|
-
ControlsBar = styled2.div`
|
|
762
|
-
background: #ffffff;
|
|
763
|
-
border-radius: 10px;
|
|
764
|
-
padding: 14px 18px;
|
|
765
|
-
display: flex;
|
|
766
|
-
align-items: center;
|
|
767
|
-
gap: 12px;
|
|
768
|
-
flex-wrap: wrap;
|
|
769
|
-
margin-bottom: 20px;
|
|
770
|
-
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.07);
|
|
771
|
-
`;
|
|
772
|
-
SearchWrapper = styled2.div`
|
|
773
|
-
position: relative;
|
|
774
|
-
flex: 1;
|
|
775
|
-
min-width: 220px;
|
|
776
|
-
`;
|
|
777
|
-
SearchIconSvg = styled2.span`
|
|
778
|
-
position: absolute;
|
|
779
|
-
left: 11px;
|
|
780
|
-
top: 50%;
|
|
781
|
-
transform: translateY(-50%);
|
|
782
|
-
color: #9ca3af;
|
|
783
|
-
display: flex;
|
|
784
|
-
align-items: center;
|
|
785
|
-
pointer-events: none;
|
|
786
|
-
`;
|
|
787
|
-
SearchInput = styled2.input`
|
|
788
|
-
width: 100%;
|
|
789
|
-
height: 36px;
|
|
790
|
-
padding: 0 12px 0 34px;
|
|
791
|
-
border: 1px solid #e5e7eb;
|
|
792
|
-
border-radius: 7px;
|
|
793
|
-
font-size: 13px;
|
|
794
|
-
color: #111827;
|
|
795
|
-
background: #f9fafb;
|
|
796
|
-
box-sizing: border-box;
|
|
797
|
-
outline: none;
|
|
798
|
-
transition:
|
|
799
|
-
border-color 0.15s,
|
|
800
|
-
background 0.15s;
|
|
801
|
-
|
|
802
|
-
&::placeholder {
|
|
803
|
-
color: #9ca3af;
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
&:focus {
|
|
807
|
-
border-color: #6366f1;
|
|
808
|
-
background: #fff;
|
|
809
|
-
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
|
|
810
|
-
}
|
|
811
|
-
`;
|
|
812
|
-
StyledSelect = styled2.select`
|
|
813
|
-
height: 36px;
|
|
814
|
-
padding: 0 32px 0 12px;
|
|
815
|
-
border: 1px solid #e5e7eb;
|
|
816
|
-
border-radius: 7px;
|
|
817
|
-
font-size: 13px;
|
|
818
|
-
color: #374151;
|
|
819
|
-
background: #f9fafb
|
|
820
|
-
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%236b7280' d='M6 8L1 3h10z'/%3E%3C/svg%3E")
|
|
821
|
-
no-repeat right 10px center;
|
|
822
|
-
appearance: none;
|
|
823
|
-
outline: none;
|
|
824
|
-
cursor: pointer;
|
|
825
|
-
transition: border-color 0.15s;
|
|
826
|
-
|
|
827
|
-
&:focus {
|
|
828
|
-
border-color: #6366f1;
|
|
829
|
-
background-color: #fff;
|
|
830
|
-
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
|
|
831
|
-
}
|
|
832
|
-
`;
|
|
833
|
-
TableCard = styled2.div`
|
|
834
|
-
background: #ffffff;
|
|
835
|
-
border-radius: 10px;
|
|
836
|
-
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.07);
|
|
837
|
-
overflow: hidden;
|
|
838
|
-
`;
|
|
839
|
-
TableHeader = styled2.div`
|
|
840
|
-
display: flex;
|
|
841
|
-
align-items: center;
|
|
842
|
-
padding: 11px 20px;
|
|
843
|
-
background: #f9fafb;
|
|
844
|
-
border-bottom: 1px solid #e5e7eb;
|
|
845
|
-
font-size: 11px;
|
|
846
|
-
font-weight: 600;
|
|
847
|
-
color: #6b7280;
|
|
848
|
-
text-transform: uppercase;
|
|
849
|
-
letter-spacing: 0.5px;
|
|
850
|
-
gap: 12px;
|
|
851
|
-
`;
|
|
852
|
-
TableRow = styled2.div`
|
|
853
|
-
display: flex;
|
|
854
|
-
align-items: center;
|
|
855
|
-
padding: 13px 20px;
|
|
856
|
-
border-bottom: 1px solid #f3f4f6;
|
|
857
|
-
gap: 12px;
|
|
858
|
-
transition: background 0.1s;
|
|
859
|
-
|
|
860
|
-
&:last-child {
|
|
861
|
-
border-bottom: none;
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
&:hover {
|
|
865
|
-
background: #fafafa;
|
|
866
|
-
}
|
|
867
|
-
`;
|
|
868
|
-
ColTitle = styled2.div`
|
|
869
|
-
flex: 2;
|
|
870
|
-
min-width: 0;
|
|
871
|
-
`;
|
|
872
|
-
TitleWrapper = styled2.div`
|
|
873
|
-
display: flex;
|
|
874
|
-
align-items: center;
|
|
875
|
-
gap: 4px;
|
|
876
|
-
flex-wrap: wrap;
|
|
877
|
-
min-width: 0;
|
|
878
|
-
`;
|
|
879
|
-
TitleCell = styled2.div`
|
|
880
|
-
min-width: 0;
|
|
881
|
-
overflow: hidden;
|
|
882
|
-
flex: 1;
|
|
883
|
-
`;
|
|
884
|
-
ColType = styled2.div`
|
|
885
|
-
flex: 0.8;
|
|
886
|
-
min-width: 80px;
|
|
887
|
-
`;
|
|
888
|
-
ColScore = styled2.div`
|
|
889
|
-
flex: 0.6;
|
|
890
|
-
min-width: 70px;
|
|
891
|
-
`;
|
|
892
|
-
ColIssues = styled2.div`
|
|
893
|
-
flex: 2;
|
|
894
|
-
min-width: 0;
|
|
895
|
-
`;
|
|
896
|
-
DocTitleLink = styled2.a`
|
|
897
|
-
font-size: 13px;
|
|
898
|
-
font-weight: 600;
|
|
899
|
-
color: #4f46e5;
|
|
900
|
-
white-space: nowrap;
|
|
901
|
-
overflow: hidden;
|
|
902
|
-
text-overflow: ellipsis;
|
|
903
|
-
text-decoration: none;
|
|
904
|
-
display: block;
|
|
905
|
-
transition: color 0.15s;
|
|
906
|
-
|
|
907
|
-
&:hover {
|
|
908
|
-
color: #4338ca;
|
|
909
|
-
text-decoration: underline;
|
|
910
|
-
}
|
|
911
|
-
`;
|
|
912
|
-
DocId = styled2.div`
|
|
913
|
-
font-size: 11px;
|
|
914
|
-
color: #9ca3af;
|
|
915
|
-
margin-top: 2px;
|
|
916
|
-
white-space: nowrap;
|
|
917
|
-
overflow: hidden;
|
|
918
|
-
text-overflow: ellipsis;
|
|
919
|
-
`;
|
|
920
|
-
TypeBadge = styled2.span`
|
|
921
|
-
display: inline-block;
|
|
922
|
-
padding: 3px 8px;
|
|
923
|
-
border-radius: 5px;
|
|
924
|
-
font-size: 11px;
|
|
925
|
-
font-weight: 500;
|
|
926
|
-
background: ${(p) => p.$bgColor || "#ede9fe"};
|
|
927
|
-
color: ${(p) => p.$textColor || "#5b21b6"};
|
|
928
|
-
`;
|
|
929
|
-
TypeText = styled2.span`
|
|
930
|
-
font-size: 12px;
|
|
931
|
-
font-weight: 500;
|
|
932
|
-
color: #374151;
|
|
933
|
-
`;
|
|
934
|
-
CustomBadge = styled2.span`
|
|
935
|
-
display: inline-block;
|
|
936
|
-
padding: 2px 6px;
|
|
937
|
-
border-radius: 4px;
|
|
938
|
-
font-size: ${(p) => p.$fontSize || "10px"};
|
|
939
|
-
font-weight: 600;
|
|
940
|
-
background: ${(p) => p.$bgColor || "#e0e7ff"};
|
|
941
|
-
color: ${(p) => p.$textColor || "#3730a3"};
|
|
942
|
-
white-space: nowrap;
|
|
943
|
-
`;
|
|
944
|
-
ScoreBadge = styled2.span`
|
|
945
|
-
display: inline-block;
|
|
946
|
-
padding: 4px 10px;
|
|
947
|
-
border-radius: 6px;
|
|
948
|
-
font-size: 12px;
|
|
949
|
-
font-weight: 700;
|
|
950
|
-
background: ${(p) => {
|
|
951
|
-
if (p.$score >= 80) return "#d1fae5";
|
|
952
|
-
if (p.$score >= 60) return "#fef3c7";
|
|
953
|
-
if (p.$score >= 40) return "#ffedd5";
|
|
954
|
-
return "#fee2e2";
|
|
955
|
-
}};
|
|
956
|
-
color: ${(p) => {
|
|
957
|
-
if (p.$score >= 80) return "#065f46";
|
|
958
|
-
if (p.$score >= 60) return "#92400e";
|
|
959
|
-
if (p.$score >= 40) return "#9a3412";
|
|
960
|
-
return "#991b1b";
|
|
961
|
-
}};
|
|
962
|
-
`;
|
|
963
|
-
IssueTag = styled2.div`
|
|
964
|
-
font-size: 11px;
|
|
965
|
-
color: #ef4444;
|
|
966
|
-
line-height: 1.5;
|
|
967
|
-
white-space: nowrap;
|
|
968
|
-
overflow: hidden;
|
|
969
|
-
text-overflow: ellipsis;
|
|
970
|
-
`;
|
|
971
|
-
NonStringTitleWarning = styled2.div`
|
|
972
|
-
display: inline-flex;
|
|
973
|
-
align-items: center;
|
|
974
|
-
gap: 4px;
|
|
975
|
-
margin-top: 4px;
|
|
976
|
-
padding: 2px 7px;
|
|
977
|
-
border-radius: 4px;
|
|
978
|
-
background: #fef3c7;
|
|
979
|
-
border: 1px solid #fcd34d;
|
|
980
|
-
font-size: 10px;
|
|
981
|
-
font-weight: 600;
|
|
982
|
-
color: #92400e;
|
|
983
|
-
line-height: 1.4;
|
|
984
|
-
cursor: default;
|
|
985
|
-
white-space: normal;
|
|
986
|
-
`;
|
|
987
|
-
MoreIssues = styled2.div`
|
|
988
|
-
font-size: 11px;
|
|
989
|
-
color: #6b7280;
|
|
990
|
-
cursor: pointer;
|
|
991
|
-
transition: color 0.15s;
|
|
992
|
-
|
|
993
|
-
&:hover {
|
|
994
|
-
color: #374151;
|
|
995
|
-
}
|
|
996
|
-
`;
|
|
997
|
-
MoreIssuesWrapper = styled2.div`
|
|
998
|
-
position: relative;
|
|
999
|
-
display: inline-block;
|
|
1000
|
-
`;
|
|
1001
|
-
IssuesPopover = styled2.div`
|
|
1002
|
-
position: fixed;
|
|
1003
|
-
bottom: auto;
|
|
1004
|
-
left: 0;
|
|
1005
|
-
transform: translateY(calc(-100% - 14px));
|
|
1006
|
-
background: #1f2937;
|
|
1007
|
-
color: #ffffff;
|
|
1008
|
-
padding: 12px;
|
|
1009
|
-
border-radius: 8px;
|
|
1010
|
-
font-size: 12px;
|
|
1011
|
-
z-index: 50;
|
|
1012
|
-
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
|
|
1013
|
-
width: 280px;
|
|
1014
|
-
word-break: break-word;
|
|
1015
|
-
line-height: 1.5;
|
|
1016
|
-
|
|
1017
|
-
&::after {
|
|
1018
|
-
content: '';
|
|
1019
|
-
position: absolute;
|
|
1020
|
-
bottom: -6px;
|
|
1021
|
-
left: 12px;
|
|
1022
|
-
width: 0;
|
|
1023
|
-
height: 0;
|
|
1024
|
-
border-left: 6px solid transparent;
|
|
1025
|
-
border-right: 6px solid transparent;
|
|
1026
|
-
border-top: 6px solid #1f2937;
|
|
1027
|
-
}
|
|
1028
|
-
`;
|
|
1029
|
-
PopoverIssueItem = styled2.div`
|
|
1030
|
-
display: flex;
|
|
1031
|
-
gap: 6px;
|
|
1032
|
-
margin-bottom: 6px;
|
|
1033
|
-
|
|
1034
|
-
&:last-child {
|
|
1035
|
-
margin-bottom: 0;
|
|
1036
|
-
}
|
|
1037
|
-
`;
|
|
1038
|
-
UpgradeContainer = styled2.div`
|
|
1039
|
-
display: flex;
|
|
1040
|
-
align-items: center;
|
|
1041
|
-
justify-content: center;
|
|
1042
|
-
min-height: 100%;
|
|
1043
|
-
padding: 60px 24px;
|
|
1044
|
-
`;
|
|
1045
|
-
UpgradeBox = styled2.div`
|
|
1046
|
-
background: #ffffff;
|
|
1047
|
-
border-radius: 16px;
|
|
1048
|
-
padding: 48px 40px;
|
|
1049
|
-
max-width: 480px;
|
|
1050
|
-
width: 100%;
|
|
1051
|
-
text-align: center;
|
|
1052
|
-
box-shadow:
|
|
1053
|
-
0 4px 24px rgba(0, 0, 0, 0.08),
|
|
1054
|
-
0 1px 4px rgba(0, 0, 0, 0.05);
|
|
1055
|
-
border: 1px solid #e5e7eb;
|
|
1056
|
-
`;
|
|
1057
|
-
UpgradeLock = styled2.div`
|
|
1058
|
-
font-size: 40px;
|
|
1059
|
-
margin-bottom: 16px;
|
|
1060
|
-
`;
|
|
1061
|
-
UpgradeTitle = styled2.h2`
|
|
1062
|
-
margin: 0 0 10px;
|
|
1063
|
-
font-size: 20px;
|
|
1064
|
-
font-weight: 700;
|
|
1065
|
-
color: #111827;
|
|
1066
|
-
`;
|
|
1067
|
-
UpgradeText = styled2.p`
|
|
1068
|
-
margin: 0 0 20px;
|
|
1069
|
-
font-size: 14px;
|
|
1070
|
-
color: #6b7280;
|
|
1071
|
-
line-height: 1.6;
|
|
1072
|
-
`;
|
|
1073
|
-
UpgradeCode = styled2.pre`
|
|
1074
|
-
background: #f3f4f6;
|
|
1075
|
-
border-radius: 8px;
|
|
1076
|
-
padding: 14px 16px;
|
|
1077
|
-
font-size: 12px;
|
|
1078
|
-
color: #374151;
|
|
1079
|
-
text-align: left;
|
|
1080
|
-
margin: 0 0 24px;
|
|
1081
|
-
overflow-x: auto;
|
|
1082
|
-
line-height: 1.6;
|
|
1083
|
-
border: 1px solid #e5e7eb;
|
|
1084
|
-
`;
|
|
1085
|
-
UpgradeButton = styled2.a`
|
|
1086
|
-
display: inline-block;
|
|
1087
|
-
background: #4f46e5;
|
|
1088
|
-
color: #ffffff;
|
|
1089
|
-
font-size: 14px;
|
|
1090
|
-
font-weight: 600;
|
|
1091
|
-
padding: 10px 24px;
|
|
1092
|
-
border-radius: 8px;
|
|
1093
|
-
text-decoration: none;
|
|
1094
|
-
transition: background 0.15s;
|
|
1095
|
-
|
|
1096
|
-
&:hover {
|
|
1097
|
-
background: #4338ca;
|
|
1098
|
-
}
|
|
1099
|
-
`;
|
|
1100
|
-
ReloadButton = styled2.button`
|
|
1101
|
-
display: inline-block;
|
|
1102
|
-
background: transparent;
|
|
1103
|
-
color: #6b7280;
|
|
1104
|
-
font-size: 13px;
|
|
1105
|
-
font-weight: 500;
|
|
1106
|
-
padding: 8px 20px;
|
|
1107
|
-
border-radius: 8px;
|
|
1108
|
-
border: 1px solid #d1d5db;
|
|
1109
|
-
cursor: pointer;
|
|
1110
|
-
margin-top: 10px;
|
|
1111
|
-
transition:
|
|
1112
|
-
background 0.15s,
|
|
1113
|
-
color 0.15s,
|
|
1114
|
-
border-color 0.15s;
|
|
1115
|
-
|
|
1116
|
-
&:hover {
|
|
1117
|
-
background: #f3f4f6;
|
|
1118
|
-
color: #374151;
|
|
1119
|
-
border-color: #9ca3af;
|
|
1120
|
-
}
|
|
1121
|
-
`;
|
|
1122
|
-
spin = keyframes`
|
|
1123
|
-
to { transform: rotate(360deg); }
|
|
1124
|
-
`;
|
|
1125
|
-
DashboardRefreshButton = styled2.button`
|
|
1126
|
-
display: inline-flex;
|
|
1127
|
-
align-items: center;
|
|
1128
|
-
gap: 6px;
|
|
1129
|
-
background: #ffffff;
|
|
1130
|
-
color: #374151;
|
|
1131
|
-
font-size: 13px;
|
|
1132
|
-
font-weight: 500;
|
|
1133
|
-
padding: 8px 14px;
|
|
1134
|
-
border-radius: 8px;
|
|
1135
|
-
border: 1px solid #d1d5db;
|
|
1136
|
-
cursor: pointer;
|
|
1137
|
-
flex-shrink: 0;
|
|
1138
|
-
transition:
|
|
1139
|
-
background 0.15s,
|
|
1140
|
-
color 0.15s,
|
|
1141
|
-
border-color 0.15s,
|
|
1142
|
-
box-shadow 0.15s;
|
|
1143
|
-
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
1144
|
-
|
|
1145
|
-
svg {
|
|
1146
|
-
animation: ${(p) => p.$spinning ? css`
|
|
1147
|
-
${spin} 0.7s linear infinite
|
|
1148
|
-
` : "none"};
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
&:hover {
|
|
1152
|
-
background: #f3f4f6;
|
|
1153
|
-
border-color: #9ca3af;
|
|
1154
|
-
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
|
|
1155
|
-
}
|
|
1156
|
-
|
|
1157
|
-
&:disabled {
|
|
1158
|
-
opacity: 0.6;
|
|
1159
|
-
cursor: not-allowed;
|
|
1160
|
-
}
|
|
1161
|
-
`;
|
|
1162
|
-
DocTitleAnchor = ({ id, type, structureTool, children }) => {
|
|
1163
|
-
const { basePath } = useWorkspace();
|
|
1164
|
-
const { onClick: intentOnClick, href: intentHref } = useIntentLink({
|
|
1165
|
-
intent: "edit",
|
|
1166
|
-
params: { id, type }
|
|
1167
|
-
});
|
|
1168
|
-
const href = structureTool ? `${basePath}/${structureTool}/intent/edit/id=${id};type=${type}/` : intentHref;
|
|
1169
|
-
const onClick = structureTool ? void 0 : intentOnClick;
|
|
1170
|
-
return /* @__PURE__ */ jsx13(DocTitleLink, { href, onClick, title: "Open document", children });
|
|
1171
|
-
};
|
|
1172
|
-
PaneLinkWrapper = styled2.span`
|
|
1173
|
-
display: block;
|
|
1174
|
-
min-width: 0;
|
|
1175
|
-
overflow: hidden;
|
|
1176
|
-
|
|
1177
|
-
a {
|
|
1178
|
-
font-size: 13px;
|
|
1179
|
-
font-weight: 600;
|
|
1180
|
-
color: #4f46e5;
|
|
1181
|
-
white-space: nowrap;
|
|
1182
|
-
overflow: hidden;
|
|
1183
|
-
text-overflow: ellipsis;
|
|
1184
|
-
text-decoration: none;
|
|
1185
|
-
display: block;
|
|
1186
|
-
transition: color 0.15s;
|
|
1187
|
-
|
|
1188
|
-
&:hover {
|
|
1189
|
-
color: #4338ca;
|
|
1190
|
-
text-decoration: underline;
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
`;
|
|
1194
|
-
DocTitleAnchorPane = ({
|
|
1195
|
-
id,
|
|
1196
|
-
type,
|
|
1197
|
-
children
|
|
1198
|
-
}) => {
|
|
1199
|
-
const { ChildLink } = usePaneRouter();
|
|
1200
|
-
return /* @__PURE__ */ jsx13(PaneLinkWrapper, { children: /* @__PURE__ */ jsx13(ChildLink, { childId: id, childParameters: { type }, children }) });
|
|
1201
|
-
};
|
|
1202
|
-
DocBadgeRenderer = ({ doc, docBadge }) => {
|
|
1203
|
-
const badge = docBadge(doc);
|
|
1204
|
-
if (!badge) return null;
|
|
1205
|
-
return /* @__PURE__ */ jsx13(CustomBadge, { $bgColor: badge.bgColor, $textColor: badge.textColor, $fontSize: badge.fontSize, children: badge.label });
|
|
1206
|
-
};
|
|
1207
|
-
Spinner = styled2.div`
|
|
1208
|
-
width: 28px;
|
|
1209
|
-
height: 28px;
|
|
1210
|
-
border: 3px solid #e5e7eb;
|
|
1211
|
-
border-top-color: #6366f1;
|
|
1212
|
-
border-radius: 50%;
|
|
1213
|
-
animation: ${spin} 0.7s linear infinite;
|
|
1214
|
-
margin: 0 auto 12px;
|
|
1215
|
-
`;
|
|
1216
|
-
LoadingState = styled2.div`
|
|
1217
|
-
padding: 48px 24px;
|
|
1218
|
-
text-align: center;
|
|
1219
|
-
color: #6b7280;
|
|
1220
|
-
font-size: 13px;
|
|
1221
|
-
`;
|
|
1222
|
-
EmptyState = styled2.div`
|
|
1223
|
-
padding: 48px 24px;
|
|
1224
|
-
text-align: center;
|
|
1225
|
-
color: #9ca3af;
|
|
1226
|
-
font-size: 13px;
|
|
1227
|
-
`;
|
|
1228
|
-
DeprecationBanner = styled2.div`
|
|
1229
|
-
background: #fffbeb;
|
|
1230
|
-
border: 1px solid #fcd34d;
|
|
1231
|
-
border-radius: 8px;
|
|
1232
|
-
padding: 10px 14px;
|
|
1233
|
-
font-size: 12px;
|
|
1234
|
-
color: #78350f;
|
|
1235
|
-
margin-bottom: 16px;
|
|
1236
|
-
line-height: 1.6;
|
|
1237
|
-
`;
|
|
1238
|
-
DeprecationBannerLink = styled2.a`
|
|
1239
|
-
color: #92400e;
|
|
1240
|
-
font-weight: 600;
|
|
1241
|
-
text-decoration: underline;
|
|
1242
|
-
&:hover {
|
|
1243
|
-
color: #78350f;
|
|
1244
|
-
}
|
|
1245
|
-
`;
|
|
1246
|
-
TYPE_COLOR_PALETTE = [
|
|
1247
|
-
{ bg: "#dbeafe", text: "#0c4a6e" },
|
|
1248
|
-
// Blue
|
|
1249
|
-
{ bg: "#dcfce7", text: "#14532d" },
|
|
1250
|
-
// Green
|
|
1251
|
-
{ bg: "#fce7f3", text: "#500724" },
|
|
1252
|
-
// Pink
|
|
1253
|
-
{ bg: "#fed7aa", text: "#7c2d12" },
|
|
1254
|
-
// Orange
|
|
1255
|
-
{ bg: "#e9d5ff", text: "#581c87" },
|
|
1256
|
-
// Purple
|
|
1257
|
-
{ bg: "#f3e8ff", text: "#3f0f5c" },
|
|
1258
|
-
// Deep Purple
|
|
1259
|
-
{ bg: "#ccfbf1", text: "#134e4a" },
|
|
1260
|
-
// Teal
|
|
1261
|
-
{ bg: "#ddd6fe", text: "#3730a3" },
|
|
1262
|
-
// Indigo
|
|
1263
|
-
{ bg: "#fca5a5", text: "#7f1d1d" },
|
|
1264
|
-
// Red
|
|
1265
|
-
{ bg: "#a7f3d0", text: "#065f46" },
|
|
1266
|
-
// Emerald
|
|
1267
|
-
{ bg: "#fbbf24", text: "#78350f" },
|
|
1268
|
-
// Amber
|
|
1269
|
-
{ bg: "#c4b5fd", text: "#3b0764" },
|
|
1270
|
-
// Violet
|
|
1271
|
-
{ bg: "#f0fdf4", text: "#15803d" },
|
|
1272
|
-
// Light Green
|
|
1273
|
-
{ bg: "#fef2f2", text: "#991b1b" },
|
|
1274
|
-
// Light Red
|
|
1275
|
-
{ bg: "#f5f3ff", text: "#5b21b6" },
|
|
1276
|
-
// Light Purple
|
|
1277
|
-
{ bg: "#fffbeb", text: "#92400e" }
|
|
1278
|
-
// Light Amber
|
|
1279
|
-
];
|
|
1280
|
-
getTypeColor = (type) => {
|
|
1281
|
-
let hash = 0;
|
|
1282
|
-
for (let i = 0; i < type.length; i += 1) {
|
|
1283
|
-
const char = type.charCodeAt(i);
|
|
1284
|
-
hash = Math.abs(hash * 31 + char);
|
|
1285
|
-
}
|
|
1286
|
-
const colorIndex = hash % TYPE_COLOR_PALETTE.length;
|
|
1287
|
-
return TYPE_COLOR_PALETTE[colorIndex];
|
|
1288
|
-
};
|
|
1289
|
-
getStatusCategory = (score) => {
|
|
1290
|
-
if (score >= 80) return "excellent";
|
|
1291
|
-
if (score >= 60) return "good";
|
|
1292
|
-
if (score >= 40) return "fair";
|
|
1293
|
-
if (score > 0) return "poor";
|
|
1294
|
-
return "missing";
|
|
1295
|
-
};
|
|
1296
|
-
scoreMetaTitle = (title) => {
|
|
1297
|
-
const issues = [];
|
|
1298
|
-
let score = 0;
|
|
1299
|
-
if (title && title.length >= 50 && title.length <= 60) {
|
|
1300
|
-
score = 15;
|
|
1301
|
-
} else if (title && title.length > 0) {
|
|
1302
|
-
score = 10;
|
|
1303
|
-
if (title.length < 50) issues.push("Meta title too short (< 50 chars)");
|
|
1304
|
-
if (title.length > 60) issues.push("Meta title too long (> 60 chars)");
|
|
1305
|
-
} else {
|
|
1306
|
-
issues.push("Missing meta title");
|
|
1307
|
-
}
|
|
1308
|
-
return { score, issues };
|
|
1309
|
-
};
|
|
1310
|
-
scoreMetaDescription = (description) => {
|
|
1311
|
-
const issues = [];
|
|
1312
|
-
let score = 0;
|
|
1313
|
-
if (description && description.length >= 120 && description.length <= 160) {
|
|
1314
|
-
score = 15;
|
|
1315
|
-
} else if (description && description.length > 0) {
|
|
1316
|
-
score = 10;
|
|
1317
|
-
if (description.length < 120) issues.push("Meta description too short (< 120 chars)");
|
|
1318
|
-
if (description.length > 160) issues.push("Meta description too long (> 160 chars)");
|
|
1319
|
-
} else {
|
|
1320
|
-
issues.push("Missing meta description");
|
|
1321
|
-
}
|
|
1322
|
-
return { score, issues };
|
|
1323
|
-
};
|
|
1324
|
-
scoreOpenGraph = (openGraph2) => {
|
|
1325
|
-
const issues = [];
|
|
1326
|
-
let score = 0;
|
|
1327
|
-
if (openGraph2) {
|
|
1328
|
-
if (openGraph2.title) score += 6;
|
|
1329
|
-
else issues.push("Missing OG title");
|
|
1330
|
-
if (openGraph2.description) score += 6;
|
|
1331
|
-
else issues.push("Missing OG description");
|
|
1332
|
-
if (openGraph2.image) score += 6;
|
|
1333
|
-
else issues.push("Missing OG image");
|
|
1334
|
-
if (openGraph2.type) score += 7;
|
|
1335
|
-
else issues.push("Missing OG type");
|
|
1336
|
-
} else {
|
|
1337
|
-
issues.push("Open Graph not configured");
|
|
1338
|
-
}
|
|
1339
|
-
return { score, issues };
|
|
1340
|
-
};
|
|
1341
|
-
scoreTwitterCard = (twitter2) => {
|
|
1342
|
-
const issues = [];
|
|
1343
|
-
let score = 0;
|
|
1344
|
-
if (twitter2) {
|
|
1345
|
-
if (twitter2.title) score += 5;
|
|
1346
|
-
else issues.push("Missing Twitter title");
|
|
1347
|
-
if (twitter2.description) score += 5;
|
|
1348
|
-
else issues.push("Missing Twitter description");
|
|
1349
|
-
if (twitter2.image) score += 5;
|
|
1350
|
-
else issues.push("Missing Twitter image");
|
|
1351
|
-
} else {
|
|
1352
|
-
issues.push("Twitter Card not configured");
|
|
1353
|
-
}
|
|
1354
|
-
return { score, issues };
|
|
1355
|
-
};
|
|
1356
|
-
calculateHealthScore = (doc) => {
|
|
1357
|
-
if (!doc.seo) {
|
|
1358
|
-
return { score: 0, status: "missing", issues: ["SEO fields not configured"] };
|
|
1359
|
-
}
|
|
1360
|
-
const { title, description, keywords, robots, canonicalUrl, openGraph: openGraph2, twitter: twitter2 } = doc.seo;
|
|
1361
|
-
let score = 0;
|
|
1362
|
-
const issues = [];
|
|
1363
|
-
const titleResult = scoreMetaTitle(title);
|
|
1364
|
-
score += titleResult.score;
|
|
1365
|
-
issues.push(...titleResult.issues);
|
|
1366
|
-
const descResult = scoreMetaDescription(description);
|
|
1367
|
-
score += descResult.score;
|
|
1368
|
-
issues.push(...descResult.issues);
|
|
1369
|
-
if (doc.seo.metaImage) score += 10;
|
|
1370
|
-
else issues.push("Missing meta image");
|
|
1371
|
-
if (keywords && keywords.length > 0) score += 10;
|
|
1372
|
-
else issues.push("No keywords defined");
|
|
1373
|
-
if (robots && !robots.noIndex) score += 5;
|
|
1374
|
-
else if (!robots) score += 5;
|
|
1375
|
-
if (canonicalUrl) score += 0;
|
|
1376
|
-
const ogResult = scoreOpenGraph(openGraph2);
|
|
1377
|
-
score += ogResult.score;
|
|
1378
|
-
issues.push(...ogResult.issues);
|
|
1379
|
-
const twResult = scoreTwitterCard(twitter2);
|
|
1380
|
-
score += twResult.score;
|
|
1381
|
-
issues.push(...twResult.issues);
|
|
1382
|
-
const hasMetaImage = !!doc.seo.metaImage;
|
|
1383
|
-
const hasOgImage = !!(openGraph2 && openGraph2.image);
|
|
1384
|
-
const hasTwitterImage = !!(twitter2 && twitter2.image);
|
|
1385
|
-
if (hasMetaImage && hasOgImage && hasTwitterImage) {
|
|
1386
|
-
score += 5;
|
|
1387
|
-
} else {
|
|
1388
|
-
const missingImages = [];
|
|
1389
|
-
if (!hasMetaImage) missingImages.push("meta image");
|
|
1390
|
-
if (!hasOgImage) missingImages.push("OG image");
|
|
1391
|
-
if (!hasTwitterImage) missingImages.push("Twitter image");
|
|
1392
|
-
issues.push(`Missing images for full score: ${missingImages.join(", ")}`);
|
|
1393
|
-
}
|
|
1394
|
-
const status = getStatusCategory(score);
|
|
1395
|
-
return { score, status, issues };
|
|
1396
|
-
};
|
|
1397
|
-
resolveTypeLabel = (type, typeLabels) => {
|
|
1398
|
-
var _a;
|
|
1399
|
-
return (_a = typeLabels == null ? void 0 : typeLabels[type]) != null ? _a : type;
|
|
1400
|
-
};
|
|
1401
|
-
buildTitleProjection = (titleField) => {
|
|
1402
|
-
if (!titleField || titleField === "title") return "title";
|
|
1403
|
-
if (typeof titleField === "string") return `"title": ${titleField}`;
|
|
1404
|
-
const cases = Object.entries(titleField).map(([type, field]) => `_type == "${type}" => ${field}`).join(", ");
|
|
1405
|
-
return `"title": select(${cases}, title)`;
|
|
1406
|
-
};
|
|
1407
|
-
generateDummyData = () => {
|
|
1408
|
-
const dummyDocs = [
|
|
1409
|
-
{
|
|
1410
|
-
_id: "preview-post-1",
|
|
1411
|
-
_type: "post",
|
|
1412
|
-
title: "Getting Started with SEO Best Practices",
|
|
1413
|
-
slug: { current: "getting-started-seo" },
|
|
1414
|
-
_updatedAt: new Date(Date.now() - 2 * 24 * 60 * 60 * 1e3).toISOString(),
|
|
1415
|
-
seo: {
|
|
1416
|
-
title: "Getting Started with SEO Best Practices | My Blog",
|
|
1417
|
-
description: "Learn the fundamentals of SEO optimization to improve your website visibility and search rankings.",
|
|
1418
|
-
keywords: ["seo", "best practices", "optimization"],
|
|
1419
|
-
metaImage: { _type: "image", asset: { _ref: "image-123", _type: "reference" } },
|
|
1420
|
-
openGraph: {
|
|
1421
|
-
title: "SEO Best Practices Guide",
|
|
1422
|
-
description: "Master SEO optimization",
|
|
1423
|
-
image: { _type: "image", asset: { _ref: "image-123", _type: "reference" }, alt: "SEO Guide" },
|
|
1424
|
-
type: "article"
|
|
1425
|
-
},
|
|
1426
|
-
twitter: {
|
|
1427
|
-
title: "SEO Best Practices",
|
|
1428
|
-
description: "Learn SEO optimization",
|
|
1429
|
-
image: { _type: "image", asset: { _ref: "image-123", _type: "reference" }, alt: "Guide" },
|
|
1430
|
-
card: "summary_large_image"
|
|
1431
|
-
}
|
|
1432
|
-
}
|
|
1433
|
-
},
|
|
1434
|
-
{
|
|
1435
|
-
_id: "preview-post-2",
|
|
1436
|
-
_type: "post",
|
|
1437
|
-
title: "Advanced Analytics Strategy",
|
|
1438
|
-
slug: { current: "advanced-analytics" },
|
|
1439
|
-
_updatedAt: new Date(Date.now() - 5 * 24 * 60 * 60 * 1e3).toISOString(),
|
|
1440
|
-
seo: {
|
|
1441
|
-
title: "Advanced Analytics",
|
|
1442
|
-
description: "Strategy tips",
|
|
1443
|
-
keywords: ["analytics", "data"],
|
|
1444
|
-
openGraph: {
|
|
1445
|
-
title: "Analytics Guide"
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
1448
|
-
},
|
|
1449
|
-
{
|
|
1450
|
-
_id: "preview-page-1",
|
|
1451
|
-
_type: "page",
|
|
1452
|
-
title: "About Us",
|
|
1453
|
-
slug: { current: "about" },
|
|
1454
|
-
_updatedAt: new Date(Date.now() - 10 * 24 * 60 * 60 * 1e3).toISOString(),
|
|
1455
|
-
seo: {
|
|
1456
|
-
title: "About",
|
|
1457
|
-
keywords: ["company", "team"],
|
|
1458
|
-
metaImage: { _type: "image", asset: { _ref: "image-456", _type: "reference" } }
|
|
1459
|
-
}
|
|
1460
|
-
},
|
|
1461
|
-
{
|
|
1462
|
-
_id: "preview-post-3",
|
|
1463
|
-
_type: "post",
|
|
1464
|
-
title: "Content Marketing Trends for 2024",
|
|
1465
|
-
slug: { current: "content-marketing-trends" },
|
|
1466
|
-
_updatedAt: new Date(Date.now() - 1 * 24 * 60 * 60 * 1e3).toISOString(),
|
|
1467
|
-
seo: {
|
|
1468
|
-
title: "Content Marketing Trends 2024",
|
|
1469
|
-
description: "Discover the latest content marketing trends and strategies to engage your audience effectively.",
|
|
1470
|
-
keywords: ["content marketing", "trends", "strategy", "engagement"],
|
|
1471
|
-
metaImage: { _type: "image", asset: { _ref: "image-789", _type: "reference" } },
|
|
1472
|
-
openGraph: {
|
|
1473
|
-
title: "Content Marketing Trends 2024",
|
|
1474
|
-
description: "Latest trends in content marketing",
|
|
1475
|
-
image: { _type: "image", asset: { _ref: "image-789", _type: "reference" }, alt: "Trends" },
|
|
1476
|
-
type: "article"
|
|
1477
|
-
},
|
|
1478
|
-
twitter: {
|
|
1479
|
-
title: "Content Marketing Trends",
|
|
1480
|
-
description: "Discover the latest trends",
|
|
1481
|
-
card: "summary"
|
|
1482
|
-
}
|
|
1483
|
-
}
|
|
1484
|
-
},
|
|
1485
|
-
{
|
|
1486
|
-
_id: "preview-post-4",
|
|
1487
|
-
_type: "product",
|
|
1488
|
-
title: "Pro Plan",
|
|
1489
|
-
slug: { current: "pro-plan" },
|
|
1490
|
-
_updatedAt: new Date(Date.now() - 15 * 24 * 60 * 60 * 1e3).toISOString(),
|
|
1491
|
-
seo: {
|
|
1492
|
-
title: "Pro",
|
|
1493
|
-
keywords: ["pricing"]
|
|
1494
|
-
}
|
|
1495
|
-
},
|
|
1496
|
-
{
|
|
1497
|
-
_id: "preview-page-2",
|
|
1498
|
-
_type: "page",
|
|
1499
|
-
title: "Contact",
|
|
1500
|
-
slug: { current: "contact" },
|
|
1501
|
-
_updatedAt: new Date(Date.now() - 8 * 24 * 60 * 60 * 1e3).toISOString(),
|
|
1502
|
-
seo: {
|
|
1503
|
-
openGraph: {
|
|
1504
|
-
title: "Get in Touch"
|
|
1505
|
-
}
|
|
1506
|
-
}
|
|
1507
|
-
},
|
|
1508
|
-
{
|
|
1509
|
-
_id: "preview-post-5",
|
|
1510
|
-
_type: "post",
|
|
1511
|
-
title: "Mobile Optimization Guide",
|
|
1512
|
-
slug: { current: "mobile-optimization" },
|
|
1513
|
-
_updatedAt: new Date(Date.now() - 3 * 24 * 60 * 60 * 1e3).toISOString(),
|
|
1514
|
-
seo: {
|
|
1515
|
-
title: "Mobile Optimization Guide: Best Practices for Responsive Design",
|
|
1516
|
-
description: "Complete guide to mobile optimization including responsive design, performance tips, and user experience best practices for modern web development.",
|
|
1517
|
-
keywords: ["mobile", "optimization", "responsive", "performance"],
|
|
1518
|
-
metaImage: { _type: "image", asset: { _ref: "image-mobile", _type: "reference" } },
|
|
1519
|
-
openGraph: {
|
|
1520
|
-
title: "Mobile Optimization Best Practices",
|
|
1521
|
-
description: "Master mobile web optimization",
|
|
1522
|
-
image: { _type: "image", asset: { _ref: "image-mobile", _type: "reference" }, alt: "Mobile" },
|
|
1523
|
-
type: "article"
|
|
1524
|
-
},
|
|
1525
|
-
twitter: {
|
|
1526
|
-
title: "Mobile Optimization Tips",
|
|
1527
|
-
description: "Responsive design best practices",
|
|
1528
|
-
image: { _type: "image", asset: { _ref: "image-mobile", _type: "reference" }, alt: "Mobile" },
|
|
1529
|
-
card: "summary_large_image"
|
|
1530
|
-
}
|
|
1531
|
-
}
|
|
1532
|
-
}
|
|
1533
|
-
];
|
|
1534
|
-
return dummyDocs.map((doc) => __spreadProps(__spreadValues({}, doc), {
|
|
1535
|
-
health: calculateHealthScore(doc)
|
|
1536
|
-
}));
|
|
1537
|
-
};
|
|
1538
|
-
SeoHealthDashboard = ({
|
|
1539
|
-
icon = "\u{1F4CA}",
|
|
1540
|
-
title = "SEO Health Dashboard",
|
|
1541
|
-
description = "Monitor and optimize SEO fields across all your documents",
|
|
1542
|
-
showTypeColumn = true,
|
|
1543
|
-
showDocumentId = true,
|
|
1544
|
-
queryTypes,
|
|
1545
|
-
queryRequireSeo = true,
|
|
1546
|
-
customQuery,
|
|
1547
|
-
apiVersion = "2023-01-01",
|
|
1548
|
-
licenseKey,
|
|
1549
|
-
typeDisplayLabels,
|
|
1550
|
-
typeColumnMode = "badge",
|
|
1551
|
-
titleField,
|
|
1552
|
-
getDocumentBadge,
|
|
1553
|
-
loadingLicense,
|
|
1554
|
-
loadingDocuments,
|
|
1555
|
-
noDocuments,
|
|
1556
|
-
previewMode = false,
|
|
1557
|
-
openInPane = false,
|
|
1558
|
-
structureTool,
|
|
1559
|
-
_deprecationWarnings
|
|
1560
|
-
}) => {
|
|
1561
|
-
const resolvedTypeLabels = typeDisplayLabels;
|
|
1562
|
-
const resolvedDocBadge = getDocumentBadge;
|
|
1563
|
-
const allDeprecationWarnings = useMemo12(() => _deprecationWarnings != null ? _deprecationWarnings : [], [_deprecationWarnings]);
|
|
1564
|
-
const deprecationGroups = useMemo12(() => {
|
|
1565
|
-
const groups = /* @__PURE__ */ new Map();
|
|
1566
|
-
for (const w of allDeprecationWarnings) {
|
|
1567
|
-
if (!groups.has(w.version)) {
|
|
1568
|
-
groups.set(w.version, { version: w.version, changelogUrl: w.changelogUrl, keys: [] });
|
|
1569
|
-
}
|
|
1570
|
-
groups.get(w.version).keys.push(w.key);
|
|
1571
|
-
}
|
|
1572
|
-
return Array.from(groups.values());
|
|
1573
|
-
}, [allDeprecationWarnings]);
|
|
1574
|
-
const client = useClient({ apiVersion });
|
|
1575
|
-
const [licenseStatus, setLicenseStatus] = useState("loading");
|
|
1576
|
-
const [documents, setDocuments] = useState([]);
|
|
1577
|
-
const [loading, setLoading] = useState(true);
|
|
1578
|
-
const [isRefreshing, setIsRefreshing] = useState(false);
|
|
1579
|
-
const [searchQuery, setSearchQuery] = useState("");
|
|
1580
|
-
const [filterStatus, setFilterStatus] = useState("all");
|
|
1581
|
-
const [filterType, setFilterType] = useState("all");
|
|
1582
|
-
const [sortBy, setSortBy] = useState("score");
|
|
1583
|
-
const [activePopover, setActivePopover] = useState(null);
|
|
1584
|
-
const VALIDATION_ENDPOINT = "https://sanity-plugin-seofields.thehardik.in/api/validate-license";
|
|
1585
|
-
const CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
1586
|
-
const validateLicense = useCallback(
|
|
1587
|
-
async (forceRefresh = false) => {
|
|
1588
|
-
var _a;
|
|
1589
|
-
if (previewMode) {
|
|
1590
|
-
setLicenseStatus("valid");
|
|
1591
|
-
return;
|
|
1592
|
-
}
|
|
1593
|
-
if (!licenseKey) {
|
|
1594
|
-
setLicenseStatus("invalid");
|
|
1595
|
-
return;
|
|
1596
|
-
}
|
|
1597
|
-
const projectId = (_a = client.config().projectId) != null ? _a : "";
|
|
1598
|
-
const cacheKey = `seofields_license_${projectId}`;
|
|
1599
|
-
if (forceRefresh) {
|
|
1600
|
-
try {
|
|
1601
|
-
sessionStorage.removeItem(cacheKey);
|
|
1602
|
-
} catch (e) {
|
|
1603
|
-
}
|
|
1604
|
-
}
|
|
1605
|
-
if (!forceRefresh) {
|
|
1606
|
-
try {
|
|
1607
|
-
const cached = sessionStorage.getItem(cacheKey);
|
|
1608
|
-
if (cached) {
|
|
1609
|
-
const { valid, ts } = JSON.parse(cached);
|
|
1610
|
-
if (Date.now() - ts < CACHE_TTL_MS) {
|
|
1611
|
-
setLicenseStatus(valid ? "valid" : "invalid");
|
|
1612
|
-
return;
|
|
1613
|
-
}
|
|
1614
|
-
}
|
|
1615
|
-
} catch (e) {
|
|
1616
|
-
}
|
|
1617
|
-
}
|
|
1618
|
-
setLicenseStatus("loading");
|
|
1619
|
-
try {
|
|
1620
|
-
const res = await fetch(VALIDATION_ENDPOINT, {
|
|
1621
|
-
method: "POST",
|
|
1622
|
-
headers: { "Content-Type": "application/json" },
|
|
1623
|
-
body: JSON.stringify({ licenseKey, projectId })
|
|
1624
|
-
});
|
|
1625
|
-
const valid = res.ok;
|
|
1626
|
-
setLicenseStatus(valid ? "valid" : "invalid");
|
|
1627
|
-
try {
|
|
1628
|
-
sessionStorage.setItem(cacheKey, JSON.stringify({ valid, ts: Date.now() }));
|
|
1629
|
-
} catch (e) {
|
|
1630
|
-
}
|
|
1631
|
-
} catch (e) {
|
|
1632
|
-
setLicenseStatus("valid");
|
|
1633
|
-
}
|
|
1634
|
-
},
|
|
1635
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1636
|
-
[licenseKey, previewMode]
|
|
1637
|
-
);
|
|
1638
|
-
useEffect(() => {
|
|
1639
|
-
validateLicense();
|
|
1640
|
-
}, [licenseKey, previewMode]);
|
|
1641
|
-
const handleMouseEnterIssues = (el, issues) => {
|
|
1642
|
-
if (!el) return;
|
|
1643
|
-
const rect = el.getBoundingClientRect();
|
|
1644
|
-
const popoverWidth = 280;
|
|
1645
|
-
const viewportWidth = window.innerWidth;
|
|
1646
|
-
let left = rect.left;
|
|
1647
|
-
if (left + popoverWidth > viewportWidth - 10) left = viewportWidth - popoverWidth - 10;
|
|
1648
|
-
if (left < 10) left = 10;
|
|
1649
|
-
setActivePopover({ top: rect.top, left, issues });
|
|
1650
|
-
};
|
|
1651
|
-
const JSONStringifiedQueryTypes = JSON.stringify(queryTypes);
|
|
1652
|
-
const JSONStringifiedTitleField = JSON.stringify(titleField);
|
|
1653
|
-
const fetchDocuments = useCallback(
|
|
1654
|
-
async (isManualRefresh = false) => {
|
|
1655
|
-
try {
|
|
1656
|
-
if (isManualRefresh) {
|
|
1657
|
-
setIsRefreshing(true);
|
|
1658
|
-
} else {
|
|
1659
|
-
setLoading(true);
|
|
1660
|
-
}
|
|
1661
|
-
if (previewMode) {
|
|
1662
|
-
setDocuments(generateDummyData());
|
|
1663
|
-
return;
|
|
1664
|
-
}
|
|
1665
|
-
let groqQuery;
|
|
1666
|
-
let params = {};
|
|
1667
|
-
if (customQuery) {
|
|
1668
|
-
groqQuery = customQuery;
|
|
1669
|
-
} else if (queryTypes && queryTypes.length > 0) {
|
|
1670
|
-
const seoFilter = queryRequireSeo ? " && seo != null" : "";
|
|
1671
|
-
const titleProj = buildTitleProjection(titleField);
|
|
1672
|
-
groqQuery = `*[_type in $types${seoFilter} && !(_id in path("drafts.**"))]{
|
|
1673
|
-
_id,
|
|
1674
|
-
_type,
|
|
1675
|
-
${titleProj},
|
|
1676
|
-
slug,
|
|
1677
|
-
seo,
|
|
1678
|
-
_updatedAt
|
|
1679
|
-
}`;
|
|
1680
|
-
params = { types: queryTypes };
|
|
1681
|
-
} else {
|
|
1682
|
-
const titleProj = buildTitleProjection(titleField);
|
|
1683
|
-
groqQuery = `*[seo != null && !(_id in path("drafts.**"))]{
|
|
1684
|
-
_id,
|
|
1685
|
-
_type,
|
|
1686
|
-
${titleProj},
|
|
1687
|
-
slug,
|
|
1688
|
-
seo,
|
|
1689
|
-
_updatedAt
|
|
1690
|
-
}`;
|
|
1691
|
-
}
|
|
1692
|
-
const result = await client.fetch(groqQuery, params, { perspective: "published" });
|
|
1693
|
-
const docsWithHealth = result.map((doc) => __spreadProps(__spreadValues({}, doc), {
|
|
1694
|
-
health: calculateHealthScore(doc)
|
|
1695
|
-
}));
|
|
1696
|
-
setDocuments(docsWithHealth);
|
|
1697
|
-
} catch (error) {
|
|
1698
|
-
console.error("Error fetching documents:", error);
|
|
1699
|
-
} finally {
|
|
1700
|
-
setLoading(false);
|
|
1701
|
-
setIsRefreshing(false);
|
|
1702
|
-
}
|
|
1703
|
-
},
|
|
1704
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1705
|
-
[
|
|
1706
|
-
client,
|
|
1707
|
-
customQuery,
|
|
1708
|
-
queryRequireSeo,
|
|
1709
|
-
JSONStringifiedQueryTypes,
|
|
1710
|
-
JSONStringifiedTitleField,
|
|
1711
|
-
previewMode
|
|
1712
|
-
]
|
|
1713
|
-
);
|
|
1714
|
-
useEffect(() => {
|
|
1715
|
-
fetchDocuments();
|
|
1716
|
-
}, [
|
|
1717
|
-
client,
|
|
1718
|
-
customQuery,
|
|
1719
|
-
queryRequireSeo,
|
|
1720
|
-
JSONStringifiedQueryTypes,
|
|
1721
|
-
JSONStringifiedTitleField,
|
|
1722
|
-
previewMode
|
|
1723
|
-
]);
|
|
1724
|
-
const handleRefresh = useCallback(() => {
|
|
1725
|
-
fetchDocuments(true);
|
|
1726
|
-
}, [fetchDocuments]);
|
|
1727
|
-
const uniqueDocumentTypes = useMemo12(() => {
|
|
1728
|
-
const types2 = new Set(documents.map((doc) => doc._type));
|
|
1729
|
-
return Array.from(types2).sort();
|
|
1730
|
-
}, [documents]);
|
|
1731
|
-
const filteredAndSortedDocs = useMemo12(() => {
|
|
1732
|
-
let filtered = documents;
|
|
1733
|
-
if (searchQuery) {
|
|
1734
|
-
filtered = filtered.filter(
|
|
1735
|
-
(doc) => {
|
|
1736
|
-
var _a, _b;
|
|
1737
|
-
return ((_a = doc.title) == null ? void 0 : _a.toLowerCase().includes(searchQuery.toLowerCase())) || ((_b = doc._id) == null ? void 0 : _b.toLowerCase().includes(searchQuery.toLowerCase()));
|
|
1738
|
-
}
|
|
1739
|
-
);
|
|
1740
|
-
}
|
|
1741
|
-
if (filterStatus !== "all") {
|
|
1742
|
-
filtered = filtered.filter((doc) => doc.health.status === filterStatus);
|
|
1743
|
-
}
|
|
1744
|
-
if (filterType !== "all") {
|
|
1745
|
-
filtered = filtered.filter((doc) => doc._type === filterType);
|
|
1746
|
-
}
|
|
1747
|
-
const sorted = [...filtered].sort((a, b) => {
|
|
1748
|
-
if (sortBy === "score") {
|
|
1749
|
-
return b.health.score - a.health.score;
|
|
1750
|
-
}
|
|
1751
|
-
return (a.title || "").localeCompare(b.title || "");
|
|
1752
|
-
});
|
|
1753
|
-
return sorted;
|
|
1754
|
-
}, [documents, searchQuery, filterStatus, filterType, sortBy]);
|
|
1755
|
-
const stats = useMemo12(() => {
|
|
1756
|
-
const total = documents.length;
|
|
1757
|
-
const excellent = documents.filter((d) => d.health.score >= 80).length;
|
|
1758
|
-
const good = documents.filter((d) => d.health.score >= 60 && d.health.score < 80).length;
|
|
1759
|
-
const fair = documents.filter((d) => d.health.score >= 40 && d.health.score < 60).length;
|
|
1760
|
-
const poor = documents.filter((d) => d.health.score > 0 && d.health.score < 40).length;
|
|
1761
|
-
const missing = documents.filter((d) => d.health.score === 0).length;
|
|
1762
|
-
const avgScore = total > 0 ? Math.round(documents.reduce((sum, d) => sum + d.health.score, 0) / total) : 0;
|
|
1763
|
-
return { total, excellent, good, fair, poor, missing, avgScore };
|
|
1764
|
-
}, [documents]);
|
|
1765
|
-
const handleMouseLeave = useCallback(() => {
|
|
1766
|
-
setActivePopover(null);
|
|
1767
|
-
}, []);
|
|
1768
|
-
return /* @__PURE__ */ jsxs13(DashboardContainer, { children: [
|
|
1769
|
-
licenseStatus === "loading" && /* @__PURE__ */ jsxs13(LoadingState, { style: { padding: "80px 24px" }, children: [
|
|
1770
|
-
/* @__PURE__ */ jsx13(Spinner, {}),
|
|
1771
|
-
loadingLicense != null ? loadingLicense : "Verifying license\u2026"
|
|
1772
|
-
] }),
|
|
1773
|
-
licenseStatus === "invalid" && /* @__PURE__ */ jsx13(UpgradeContainer, { children: /* @__PURE__ */ jsx13(UpgradeBox, { children: licenseKey ? /* @__PURE__ */ jsxs13(Fragment, { children: [
|
|
1774
|
-
/* @__PURE__ */ jsx13(UpgradeLock, { children: "\u274C" }),
|
|
1775
|
-
/* @__PURE__ */ jsx13(UpgradeTitle, { children: "Invalid License Key" }),
|
|
1776
|
-
/* @__PURE__ */ jsx13(UpgradeText, { children: "The license key you provided is invalid or has been revoked. Please check your key and update it in the plugin config." }),
|
|
1777
|
-
/* @__PURE__ */ jsx13(UpgradeCode, { children: `seofields({
|
|
1778
|
-
healthDashboard: {
|
|
1779
|
-
licenseKey: 'YOUR_LICENSE_KEY', // \u2190 replace with a valid key
|
|
1780
|
-
},
|
|
1781
|
-
})` }),
|
|
1782
|
-
/* @__PURE__ */ jsx13(
|
|
1783
|
-
UpgradeButton,
|
|
1784
|
-
{
|
|
1785
|
-
href: "https://sanity-plugin-seofields.thehardik.in",
|
|
1786
|
-
target: "_blank",
|
|
1787
|
-
rel: "noopener noreferrer",
|
|
1788
|
-
children: "Get a New License Key \u2192"
|
|
1789
|
-
}
|
|
1790
|
-
),
|
|
1791
|
-
/* @__PURE__ */ jsx13("br", {}),
|
|
1792
|
-
/* @__PURE__ */ jsx13(ReloadButton, { onClick: () => validateLicense(true), children: "Click here If You Just Updated Your Key" })
|
|
1793
|
-
] }) : /* @__PURE__ */ jsxs13(Fragment, { children: [
|
|
1794
|
-
/* @__PURE__ */ jsx13(UpgradeLock, { children: "\u{1F512}" }),
|
|
1795
|
-
/* @__PURE__ */ jsx13(UpgradeTitle, { children: "SEO Health Dashboard" }),
|
|
1796
|
-
/* @__PURE__ */ jsx13(UpgradeText, { children: "This feature requires a license key. Add your key to the plugin config to unlock the full dashboard." }),
|
|
1797
|
-
/* @__PURE__ */ jsx13(UpgradeCode, { children: `// sanity.config.ts
|
|
1798
|
-
import { seofields } from 'sanity-plugin-seofields'
|
|
1799
|
-
|
|
1800
|
-
export default defineConfig({
|
|
1801
|
-
plugins: [
|
|
1802
|
-
seofields({
|
|
1803
|
-
healthDashboard: {
|
|
1804
|
-
licenseKey: 'SEOF-XXXX-XXXX-XXXX',
|
|
1805
|
-
},
|
|
1806
|
-
}),
|
|
1807
|
-
],
|
|
1808
|
-
})` }),
|
|
1809
|
-
/* @__PURE__ */ jsx13(
|
|
1810
|
-
UpgradeButton,
|
|
1811
|
-
{
|
|
1812
|
-
href: "https://sanity-plugin-seofields.thehardik.in",
|
|
1813
|
-
target: "_blank",
|
|
1814
|
-
rel: "noopener noreferrer",
|
|
1815
|
-
children: "Get a License Key \u2192"
|
|
1816
|
-
}
|
|
1817
|
-
)
|
|
1818
|
-
] }) }) }),
|
|
1819
|
-
licenseStatus === "valid" && /* @__PURE__ */ jsxs13(Fragment, { children: [
|
|
1820
|
-
/* @__PURE__ */ jsxs13(PageHeader, { children: [
|
|
1821
|
-
/* @__PURE__ */ jsxs13("div", { children: [
|
|
1822
|
-
/* @__PURE__ */ jsxs13(PageTitle, { children: [
|
|
1823
|
-
/* @__PURE__ */ jsxs13("span", { children: [
|
|
1824
|
-
icon,
|
|
1825
|
-
" ",
|
|
1826
|
-
title
|
|
1827
|
-
] }),
|
|
1828
|
-
previewMode && /* @__PURE__ */ jsx13(PreviewBadge, { children: "Preview Mode" })
|
|
1829
|
-
] }),
|
|
1830
|
-
/* @__PURE__ */ jsx13(PageSubtitle, { children: description })
|
|
1831
|
-
] }),
|
|
1832
|
-
/* @__PURE__ */ jsxs13(
|
|
1833
|
-
DashboardRefreshButton,
|
|
1834
|
-
{
|
|
1835
|
-
onClick: handleRefresh,
|
|
1836
|
-
disabled: loading || isRefreshing,
|
|
1837
|
-
$spinning: isRefreshing,
|
|
1838
|
-
title: "Refresh documents",
|
|
1839
|
-
children: [
|
|
1840
|
-
/* @__PURE__ */ jsxs13(
|
|
1841
|
-
"svg",
|
|
1842
|
-
{
|
|
1843
|
-
width: "14",
|
|
1844
|
-
height: "14",
|
|
1845
|
-
viewBox: "0 0 24 24",
|
|
1846
|
-
fill: "none",
|
|
1847
|
-
stroke: "currentColor",
|
|
1848
|
-
strokeWidth: "2.2",
|
|
1849
|
-
strokeLinecap: "round",
|
|
1850
|
-
strokeLinejoin: "round",
|
|
1851
|
-
children: [
|
|
1852
|
-
/* @__PURE__ */ jsx13("polyline", { points: "23 4 23 10 17 10" }),
|
|
1853
|
-
/* @__PURE__ */ jsx13("polyline", { points: "1 20 1 14 7 14" }),
|
|
1854
|
-
/* @__PURE__ */ jsx13("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" })
|
|
1855
|
-
]
|
|
1856
|
-
}
|
|
1857
|
-
),
|
|
1858
|
-
"Refresh"
|
|
1859
|
-
]
|
|
1860
|
-
}
|
|
1861
|
-
)
|
|
1862
|
-
] }),
|
|
1863
|
-
deprecationGroups.length > 0 && /* @__PURE__ */ jsxs13(DeprecationBanner, { children: [
|
|
1864
|
-
/* @__PURE__ */ jsx13("strong", { children: "\u26A0\uFE0F Deprecated config keys detected:" }),
|
|
1865
|
-
" ",
|
|
1866
|
-
deprecationGroups.map((group, gi) => /* @__PURE__ */ jsxs13("span", { children: [
|
|
1867
|
-
group.keys.map((w, i) => /* @__PURE__ */ jsxs13("span", { children: [
|
|
1868
|
-
/* @__PURE__ */ jsx13("code", { style: { background: "#fef9c3", padding: "1px 4px", borderRadius: 3 }, children: w.split("\u2192")[0].trim() }),
|
|
1869
|
-
" \u2192 ",
|
|
1870
|
-
/* @__PURE__ */ jsx13("code", { style: { background: "#dcfce7", padding: "1px 4px", borderRadius: 3 }, children: w.split("\u2192")[1].trim() }),
|
|
1871
|
-
i < group.keys.length - 1 ? " \xB7 " : ""
|
|
1872
|
-
] }, w)),
|
|
1873
|
-
" ",
|
|
1874
|
-
"(",
|
|
1875
|
-
/* @__PURE__ */ jsxs13(
|
|
1876
|
-
DeprecationBannerLink,
|
|
1877
|
-
{
|
|
1878
|
-
href: group.changelogUrl,
|
|
1879
|
-
target: "_blank",
|
|
1880
|
-
rel: "noopener noreferrer",
|
|
1881
|
-
children: [
|
|
1882
|
-
group.version,
|
|
1883
|
-
" changelog"
|
|
1884
|
-
]
|
|
1885
|
-
}
|
|
1886
|
-
),
|
|
1887
|
-
")",
|
|
1888
|
-
gi < deprecationGroups.length - 1 ? " \xB7 " : ""
|
|
1889
|
-
] }, group.version)),
|
|
1890
|
-
" ",
|
|
1891
|
-
"\u2014 Please update your config."
|
|
1892
|
-
] }),
|
|
1893
|
-
!loading && /* @__PURE__ */ jsxs13(StatsGrid, { children: [
|
|
1894
|
-
/* @__PURE__ */ jsxs13(StatCard, { children: [
|
|
1895
|
-
/* @__PURE__ */ jsx13(StatLabel, { children: "Total Docs" }),
|
|
1896
|
-
/* @__PURE__ */ jsx13(StatValue, { children: stats.total })
|
|
1897
|
-
] }),
|
|
1898
|
-
/* @__PURE__ */ jsxs13(StatCard, { children: [
|
|
1899
|
-
/* @__PURE__ */ jsx13(StatLabel, { children: "Avg Score" }),
|
|
1900
|
-
/* @__PURE__ */ jsxs13(StatValue, { children: [
|
|
1901
|
-
stats.avgScore,
|
|
1902
|
-
"%"
|
|
1903
|
-
] })
|
|
1904
|
-
] }),
|
|
1905
|
-
/* @__PURE__ */ jsxs13(StatCard, { $accent: "#10b981", children: [
|
|
1906
|
-
/* @__PURE__ */ jsx13(StatLabel, { children: "Excellent (80+)" }),
|
|
1907
|
-
/* @__PURE__ */ jsx13(StatValue, { children: stats.excellent })
|
|
1908
|
-
] }),
|
|
1909
|
-
/* @__PURE__ */ jsxs13(StatCard, { $accent: "#f59e0b", children: [
|
|
1910
|
-
/* @__PURE__ */ jsx13(StatLabel, { children: "Good (60\u201379)" }),
|
|
1911
|
-
/* @__PURE__ */ jsx13(StatValue, { children: stats.good })
|
|
1912
|
-
] }),
|
|
1913
|
-
/* @__PURE__ */ jsxs13(StatCard, { $accent: "#f97316", children: [
|
|
1914
|
-
/* @__PURE__ */ jsx13(StatLabel, { children: "Fair (40\u201359)" }),
|
|
1915
|
-
/* @__PURE__ */ jsx13(StatValue, { children: stats.fair })
|
|
1916
|
-
] }),
|
|
1917
|
-
/* @__PURE__ */ jsxs13(StatCard, { $accent: "#ef4444", children: [
|
|
1918
|
-
/* @__PURE__ */ jsx13(StatLabel, { children: "Poor / Missing" }),
|
|
1919
|
-
/* @__PURE__ */ jsx13(StatValue, { children: stats.poor + stats.missing })
|
|
1920
|
-
] })
|
|
1921
|
-
] }),
|
|
1922
|
-
/* @__PURE__ */ jsxs13(ControlsBar, { children: [
|
|
1923
|
-
/* @__PURE__ */ jsxs13(SearchWrapper, { children: [
|
|
1924
|
-
/* @__PURE__ */ jsx13(SearchIconSvg, { children: /* @__PURE__ */ jsx13("svg", { width: "14", height: "14", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx13(
|
|
1925
|
-
"path",
|
|
1926
|
-
{
|
|
1927
|
-
fillRule: "evenodd",
|
|
1928
|
-
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",
|
|
1929
|
-
clipRule: "evenodd"
|
|
1930
|
-
}
|
|
1931
|
-
) }) }),
|
|
1932
|
-
/* @__PURE__ */ jsx13(
|
|
1933
|
-
SearchInput,
|
|
1934
|
-
{
|
|
1935
|
-
placeholder: "Search documents...",
|
|
1936
|
-
value: searchQuery,
|
|
1937
|
-
onChange: (e) => setSearchQuery(e.currentTarget.value)
|
|
1938
|
-
}
|
|
1939
|
-
)
|
|
1940
|
-
] }),
|
|
1941
|
-
/* @__PURE__ */ jsxs13(
|
|
1942
|
-
StyledSelect,
|
|
1943
|
-
{
|
|
1944
|
-
value: filterStatus,
|
|
1945
|
-
onChange: (e) => setFilterStatus(e.currentTarget.value),
|
|
1946
|
-
children: [
|
|
1947
|
-
/* @__PURE__ */ jsx13("option", { value: "all", children: "All Status" }),
|
|
1948
|
-
/* @__PURE__ */ jsx13("option", { value: "excellent", children: "Excellent" }),
|
|
1949
|
-
/* @__PURE__ */ jsx13("option", { value: "good", children: "Good" }),
|
|
1950
|
-
/* @__PURE__ */ jsx13("option", { value: "fair", children: "Fair" }),
|
|
1951
|
-
/* @__PURE__ */ jsx13("option", { value: "poor", children: "Poor" }),
|
|
1952
|
-
/* @__PURE__ */ jsx13("option", { value: "missing", children: "Missing" })
|
|
1953
|
-
]
|
|
1954
|
-
}
|
|
1955
|
-
),
|
|
1956
|
-
uniqueDocumentTypes.length > 1 && /* @__PURE__ */ jsxs13(
|
|
1957
|
-
StyledSelect,
|
|
1958
|
-
{
|
|
1959
|
-
value: filterType,
|
|
1960
|
-
onChange: (e) => setFilterType(e.currentTarget.value),
|
|
1961
|
-
children: [
|
|
1962
|
-
/* @__PURE__ */ jsx13("option", { value: "all", children: "All Types" }),
|
|
1963
|
-
uniqueDocumentTypes.map((type) => /* @__PURE__ */ jsx13("option", { value: type, children: resolveTypeLabel(type, resolvedTypeLabels) }, type))
|
|
1964
|
-
]
|
|
1965
|
-
}
|
|
1966
|
-
),
|
|
1967
|
-
/* @__PURE__ */ jsxs13(
|
|
1968
|
-
StyledSelect,
|
|
1969
|
-
{
|
|
1970
|
-
value: sortBy,
|
|
1971
|
-
onChange: (e) => setSortBy(e.currentTarget.value),
|
|
1972
|
-
children: [
|
|
1973
|
-
/* @__PURE__ */ jsx13("option", { value: "score", children: "Sort by Score" }),
|
|
1974
|
-
/* @__PURE__ */ jsx13("option", { value: "title", children: "Sort by Title" })
|
|
1975
|
-
]
|
|
1976
|
-
}
|
|
1977
|
-
)
|
|
1978
|
-
] }),
|
|
1979
|
-
/* @__PURE__ */ jsxs13(TableCard, { children: [
|
|
1980
|
-
loading && /* @__PURE__ */ jsxs13(LoadingState, { children: [
|
|
1981
|
-
/* @__PURE__ */ jsx13(Spinner, {}),
|
|
1982
|
-
loadingDocuments != null ? loadingDocuments : "Loading documents\u2026"
|
|
1983
|
-
] }),
|
|
1984
|
-
!loading && (filteredAndSortedDocs.length === 0 ? /* @__PURE__ */ jsx13(EmptyState, { children: noDocuments != null ? noDocuments : "No documents found" }) : /* @__PURE__ */ jsxs13(Fragment, { children: [
|
|
1985
|
-
/* @__PURE__ */ jsxs13(TableHeader, { children: [
|
|
1986
|
-
/* @__PURE__ */ jsx13(ColTitle, { children: "Title" }),
|
|
1987
|
-
showTypeColumn && /* @__PURE__ */ jsx13(ColType, { children: "Type" }),
|
|
1988
|
-
/* @__PURE__ */ jsx13(ColScore, { children: "Score" }),
|
|
1989
|
-
/* @__PURE__ */ jsx13(ColIssues, { children: "Top Issues" })
|
|
1990
|
-
] }),
|
|
1991
|
-
filteredAndSortedDocs.map((doc) => {
|
|
1992
|
-
return /* @__PURE__ */ jsxs13(TableRow, { children: [
|
|
1993
|
-
/* @__PURE__ */ jsx13(ColTitle, { children: /* @__PURE__ */ jsx13(TitleWrapper, { children: /* @__PURE__ */ jsxs13(TitleCell, { children: [
|
|
1994
|
-
doc.title !== null && typeof doc.title !== "string" ? /* @__PURE__ */ jsx13(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__ */ jsx13(Fragment, { children: openInPane ? /* @__PURE__ */ jsx13(DocTitleAnchorPane, { id: doc._id, type: doc._type, children: typeof doc.title === "string" ? doc.title || "Untitled" : "Untitled" }) : /* @__PURE__ */ jsx13(
|
|
1995
|
-
DocTitleAnchor,
|
|
1996
|
-
{
|
|
1997
|
-
id: doc._id,
|
|
1998
|
-
type: doc._type,
|
|
1999
|
-
structureTool,
|
|
2000
|
-
children: typeof doc.title === "string" ? doc.title || "Untitled" : "Untitled"
|
|
2001
|
-
}
|
|
2002
|
-
) }),
|
|
2003
|
-
showDocumentId && /* @__PURE__ */ jsx13(DocId, { children: doc._id }),
|
|
2004
|
-
resolvedDocBadge && /* @__PURE__ */ jsx13(
|
|
2005
|
-
DocBadgeRenderer,
|
|
2006
|
-
{
|
|
2007
|
-
doc,
|
|
2008
|
-
docBadge: resolvedDocBadge
|
|
2009
|
-
}
|
|
2010
|
-
)
|
|
2011
|
-
] }) }) }),
|
|
2012
|
-
showTypeColumn && /* @__PURE__ */ jsx13(ColType, { children: typeColumnMode === "text" ? /* @__PURE__ */ jsx13(TypeText, { children: resolveTypeLabel(doc._type, resolvedTypeLabels) }) : (() => {
|
|
2013
|
-
const typeColor = getTypeColor(doc._type);
|
|
2014
|
-
return /* @__PURE__ */ jsx13(TypeBadge, { $bgColor: typeColor.bg, $textColor: typeColor.text, children: resolveTypeLabel(doc._type, resolvedTypeLabels) });
|
|
2015
|
-
})() }),
|
|
2016
|
-
/* @__PURE__ */ jsx13(ColScore, { children: /* @__PURE__ */ jsxs13(ScoreBadge, { $score: doc.health.score, children: [
|
|
2017
|
-
doc.health.score,
|
|
2018
|
-
"%"
|
|
2019
|
-
] }) }),
|
|
2020
|
-
/* @__PURE__ */ jsxs13(ColIssues, { children: [
|
|
2021
|
-
doc.health.issues.slice(0, 2).map((issue) => /* @__PURE__ */ jsxs13(IssueTag, { children: [
|
|
2022
|
-
"\u2022 ",
|
|
2023
|
-
issue
|
|
2024
|
-
] }, `issue-${doc._id}-${issue}`)),
|
|
2025
|
-
doc.health.issues.length > 2 && /* @__PURE__ */ jsx13(
|
|
2026
|
-
MoreIssuesWrapper,
|
|
2027
|
-
{
|
|
2028
|
-
onMouseEnter: function(e) {
|
|
2029
|
-
handleMouseEnterIssues(
|
|
2030
|
-
e.currentTarget,
|
|
2031
|
-
doc.health.issues.slice(2)
|
|
2032
|
-
);
|
|
2033
|
-
},
|
|
2034
|
-
onMouseLeave: handleMouseLeave,
|
|
2035
|
-
children: /* @__PURE__ */ jsxs13(MoreIssues, { children: [
|
|
2036
|
-
"+",
|
|
2037
|
-
doc.health.issues.length - 2,
|
|
2038
|
-
" more issues"
|
|
2039
|
-
] })
|
|
2040
|
-
}
|
|
2041
|
-
)
|
|
2042
|
-
] })
|
|
2043
|
-
] }, doc._id);
|
|
2044
|
-
})
|
|
2045
|
-
] }))
|
|
2046
|
-
] }),
|
|
2047
|
-
activePopover && /* @__PURE__ */ jsx13(
|
|
2048
|
-
IssuesPopover,
|
|
2049
|
-
{
|
|
2050
|
-
style: {
|
|
2051
|
-
top: activePopover.top,
|
|
2052
|
-
left: activePopover.left,
|
|
2053
|
-
transform: "translateY(calc(-100% - 10px))"
|
|
2054
|
-
},
|
|
2055
|
-
children: activePopover.issues.map((issue) => /* @__PURE__ */ jsxs13(PopoverIssueItem, { children: [
|
|
2056
|
-
"\u26A0\uFE0F ",
|
|
2057
|
-
issue
|
|
2058
|
-
] }, issue))
|
|
2059
|
-
}
|
|
2060
|
-
),
|
|
2061
|
-
" "
|
|
2062
|
-
] }),
|
|
2063
|
-
" "
|
|
2064
|
-
] });
|
|
2065
|
-
};
|
|
2066
|
-
SeoHealthDashboard_default = SeoHealthDashboard;
|
|
2067
|
-
}
|
|
2068
|
-
});
|
|
2069
|
-
|
|
2070
|
-
// src/components/SeoHealthTool.tsx
|
|
2071
|
-
var SeoHealthTool_exports = {};
|
|
2072
|
-
__export(SeoHealthTool_exports, {
|
|
2073
|
-
default: () => SeoHealthTool_default
|
|
2074
|
-
});
|
|
2075
|
-
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
2076
|
-
var SeoHealthTool, SeoHealthTool_default;
|
|
2077
|
-
var init_SeoHealthTool = __esm({
|
|
2078
|
-
"src/components/SeoHealthTool.tsx"() {
|
|
2079
|
-
"use strict";
|
|
2080
|
-
init_SeoHealthDashboard();
|
|
2081
|
-
SeoHealthTool = (props) => {
|
|
2082
|
-
return /* @__PURE__ */ jsx14(SeoHealthDashboard_default, __spreadValues({}, props));
|
|
2083
|
-
};
|
|
2084
|
-
SeoHealthTool_default = SeoHealthTool;
|
|
2085
|
-
}
|
|
2086
|
-
});
|
|
2087
|
-
|
|
2088
|
-
// src/plugin.ts
|
|
2089
|
-
import React14 from "react";
|
|
2090
|
-
import { definePlugin } from "sanity";
|
|
2091
|
-
|
|
2092
|
-
// src/schemas/index.ts
|
|
2093
|
-
import React12 from "react";
|
|
2094
|
-
import {
|
|
2095
|
-
defineField as defineField3,
|
|
2096
|
-
defineType as defineType3
|
|
2097
|
-
} from "sanity";
|
|
1
|
+
import { getMetaTitleValidationMessages, getMetaDescriptionValidationMessages, getMetaImageValidation, getOgTitleValidation, getOgDescriptionValidation, getOgImageValidation, getOgImageUrlValidation, getTwitterTitleValidation, getTwitterDescriptionValidation, getTwitterImageValidation, getTwitterImageUrlValidation } from './chunk-25JLWVEU.js';
|
|
2
|
+
import { __spreadValues, __spreadProps, __objRest } from './chunk-2NMEKWO5.js';
|
|
3
|
+
import React12, { useMemo } from 'react';
|
|
4
|
+
import { defineType, defineField, definePlugin, useFormValue } from 'sanity';
|
|
5
|
+
import { Stack, Text } from '@sanity/ui';
|
|
6
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2098
7
|
|
|
2099
|
-
// src/components/meta/MetaDescription.tsx
|
|
2100
|
-
init_seoUtils();
|
|
2101
|
-
import { Stack, Text } from "@sanity/ui";
|
|
2102
|
-
import { useMemo } from "react";
|
|
2103
|
-
import { useFormValue } from "sanity";
|
|
2104
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2105
8
|
var MetaDescription = (props) => {
|
|
2106
9
|
const { value, renderDefault, path } = props;
|
|
2107
10
|
const parent = useFormValue([path[0]]);
|
|
@@ -2125,25 +28,18 @@ var MetaDescription = (props) => {
|
|
|
2125
28
|
] });
|
|
2126
29
|
};
|
|
2127
30
|
var MetaDescription_default = MetaDescription;
|
|
2128
|
-
|
|
2129
|
-
// src/components/meta/MetaImage.tsx
|
|
2130
|
-
init_seoUtils();
|
|
2131
|
-
import { Stack as Stack2, Text as Text2 } from "@sanity/ui";
|
|
2132
|
-
import { useMemo as useMemo2 } from "react";
|
|
2133
|
-
import { useFormValue as useFormValue2 } from "sanity";
|
|
2134
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
2135
31
|
var MetaImage = (props) => {
|
|
2136
32
|
const { value, renderDefault, path } = props;
|
|
2137
|
-
const seoParent =
|
|
33
|
+
const seoParent = useFormValue([path[0]]);
|
|
2138
34
|
const hasImage = !!(value == null ? void 0 : value.asset);
|
|
2139
|
-
const feedbackItems =
|
|
35
|
+
const feedbackItems = useMemo(
|
|
2140
36
|
() => getMetaImageValidation(hasImage, seoParent),
|
|
2141
37
|
[hasImage, seoParent]
|
|
2142
38
|
);
|
|
2143
|
-
return /* @__PURE__ */
|
|
39
|
+
return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
2144
40
|
renderDefault(props),
|
|
2145
|
-
/* @__PURE__ */
|
|
2146
|
-
/* @__PURE__ */
|
|
41
|
+
/* @__PURE__ */ jsx(Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
42
|
+
/* @__PURE__ */ jsx(
|
|
2147
43
|
"div",
|
|
2148
44
|
{
|
|
2149
45
|
style: {
|
|
@@ -2154,31 +50,24 @@ var MetaImage = (props) => {
|
|
|
2154
50
|
}
|
|
2155
51
|
}
|
|
2156
52
|
),
|
|
2157
|
-
/* @__PURE__ */
|
|
53
|
+
/* @__PURE__ */ jsx(Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
2158
54
|
] }, item.text)) })
|
|
2159
55
|
] });
|
|
2160
56
|
};
|
|
2161
57
|
var MetaImage_default = MetaImage;
|
|
2162
|
-
|
|
2163
|
-
// src/components/meta/MetaTitle.tsx
|
|
2164
|
-
init_seoUtils();
|
|
2165
|
-
import { Stack as Stack3, Text as Text3 } from "@sanity/ui";
|
|
2166
|
-
import { useMemo as useMemo3 } from "react";
|
|
2167
|
-
import { useFormValue as useFormValue3 } from "sanity";
|
|
2168
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
2169
58
|
var MetaTitle = (props) => {
|
|
2170
59
|
const { value, renderDefault, path } = props;
|
|
2171
|
-
const parent =
|
|
60
|
+
const parent = useFormValue([path[0]]);
|
|
2172
61
|
const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
|
|
2173
|
-
const keywords =
|
|
2174
|
-
const feedbackItems =
|
|
62
|
+
const keywords = useMemo(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
|
|
63
|
+
const feedbackItems = useMemo(
|
|
2175
64
|
() => getMetaTitleValidationMessages(value || "", keywords, isParentseoField),
|
|
2176
65
|
[value, keywords, isParentseoField]
|
|
2177
66
|
);
|
|
2178
|
-
return /* @__PURE__ */
|
|
67
|
+
return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
2179
68
|
renderDefault(props),
|
|
2180
|
-
/* @__PURE__ */
|
|
2181
|
-
/* @__PURE__ */
|
|
69
|
+
/* @__PURE__ */ jsx(Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
70
|
+
/* @__PURE__ */ jsx(
|
|
2182
71
|
"div",
|
|
2183
72
|
{
|
|
2184
73
|
style: {
|
|
@@ -2189,7 +78,7 @@ var MetaTitle = (props) => {
|
|
|
2189
78
|
}
|
|
2190
79
|
}
|
|
2191
80
|
),
|
|
2192
|
-
/* @__PURE__ */
|
|
81
|
+
/* @__PURE__ */ jsx(Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
2193
82
|
] }, item.text)) })
|
|
2194
83
|
] });
|
|
2195
84
|
};
|
|
@@ -2317,29 +206,19 @@ var getFieldHiddenFunction = (fieldName, config) => {
|
|
|
2317
206
|
var isEmpty = (value) => {
|
|
2318
207
|
return value === null || value === void 0 || typeof value === "string" && value.trim() === "" || Array.isArray(value) && value.length === 0 || typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0;
|
|
2319
208
|
};
|
|
2320
|
-
|
|
2321
|
-
// src/schemas/types/openGraph/index.ts
|
|
2322
|
-
import { defineField, defineType } from "sanity";
|
|
2323
|
-
|
|
2324
|
-
// src/components/openGraph/OgDescription.tsx
|
|
2325
|
-
init_seoUtils();
|
|
2326
|
-
import { Stack as Stack4, Text as Text4 } from "@sanity/ui";
|
|
2327
|
-
import { useMemo as useMemo4 } from "react";
|
|
2328
|
-
import { useFormValue as useFormValue4 } from "sanity";
|
|
2329
|
-
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2330
209
|
var OgDescription = (props) => {
|
|
2331
210
|
const { value, renderDefault, path } = props;
|
|
2332
|
-
const parent =
|
|
211
|
+
const parent = useFormValue([path[0]]);
|
|
2333
212
|
const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
|
|
2334
|
-
const keywords =
|
|
2335
|
-
const feedbackItems =
|
|
213
|
+
const keywords = useMemo(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
|
|
214
|
+
const feedbackItems = useMemo(
|
|
2336
215
|
() => getOgDescriptionValidation(value || "", keywords, isParentseoField),
|
|
2337
216
|
[value, keywords, isParentseoField]
|
|
2338
217
|
);
|
|
2339
|
-
return /* @__PURE__ */
|
|
218
|
+
return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
2340
219
|
renderDefault(props),
|
|
2341
|
-
/* @__PURE__ */
|
|
2342
|
-
/* @__PURE__ */
|
|
220
|
+
/* @__PURE__ */ jsx(Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
221
|
+
/* @__PURE__ */ jsx(
|
|
2343
222
|
"div",
|
|
2344
223
|
{
|
|
2345
224
|
style: {
|
|
@@ -2350,32 +229,25 @@ var OgDescription = (props) => {
|
|
|
2350
229
|
}
|
|
2351
230
|
}
|
|
2352
231
|
),
|
|
2353
|
-
/* @__PURE__ */
|
|
232
|
+
/* @__PURE__ */ jsx(Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
2354
233
|
] }, item.text)) })
|
|
2355
234
|
] });
|
|
2356
235
|
};
|
|
2357
236
|
var OgDescription_default = OgDescription;
|
|
2358
|
-
|
|
2359
|
-
// src/components/openGraph/OgImage.tsx
|
|
2360
|
-
init_seoUtils();
|
|
2361
|
-
import { Stack as Stack5, Text as Text5 } from "@sanity/ui";
|
|
2362
|
-
import { useMemo as useMemo5 } from "react";
|
|
2363
|
-
import { useFormValue as useFormValue5 } from "sanity";
|
|
2364
|
-
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2365
237
|
var OgImage = (props) => {
|
|
2366
238
|
const { value, renderDefault, path } = props;
|
|
2367
|
-
const seoParent =
|
|
239
|
+
const seoParent = useFormValue([path[0]]);
|
|
2368
240
|
const imgValue = value;
|
|
2369
241
|
const hasImage = !!(imgValue == null ? void 0 : imgValue.asset);
|
|
2370
242
|
const altText = imgValue == null ? void 0 : imgValue.alt;
|
|
2371
|
-
const feedbackItems =
|
|
243
|
+
const feedbackItems = useMemo(
|
|
2372
244
|
() => getOgImageValidation(hasImage, altText, seoParent),
|
|
2373
245
|
[hasImage, altText, seoParent]
|
|
2374
246
|
);
|
|
2375
|
-
return /* @__PURE__ */
|
|
247
|
+
return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
2376
248
|
renderDefault(props),
|
|
2377
|
-
/* @__PURE__ */
|
|
2378
|
-
/* @__PURE__ */
|
|
249
|
+
/* @__PURE__ */ jsx(Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
250
|
+
/* @__PURE__ */ jsx(
|
|
2379
251
|
"div",
|
|
2380
252
|
{
|
|
2381
253
|
style: {
|
|
@@ -2386,26 +258,19 @@ var OgImage = (props) => {
|
|
|
2386
258
|
}
|
|
2387
259
|
}
|
|
2388
260
|
),
|
|
2389
|
-
/* @__PURE__ */
|
|
261
|
+
/* @__PURE__ */ jsx(Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
2390
262
|
] }, item.text)) })
|
|
2391
263
|
] });
|
|
2392
264
|
};
|
|
2393
265
|
var OgImage_default = OgImage;
|
|
2394
|
-
|
|
2395
|
-
// src/components/openGraph/OgImageUrl.tsx
|
|
2396
|
-
init_seoUtils();
|
|
2397
|
-
import { Stack as Stack6, Text as Text6 } from "@sanity/ui";
|
|
2398
|
-
import { useMemo as useMemo6 } from "react";
|
|
2399
|
-
import { useFormValue as useFormValue6 } from "sanity";
|
|
2400
|
-
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2401
266
|
var OgImageUrl = (props) => {
|
|
2402
267
|
const { value, renderDefault, path } = props;
|
|
2403
|
-
const seoParent =
|
|
2404
|
-
const feedbackItems =
|
|
2405
|
-
return /* @__PURE__ */
|
|
268
|
+
const seoParent = useFormValue([path[0]]);
|
|
269
|
+
const feedbackItems = useMemo(() => getOgImageUrlValidation(value, seoParent), [value, seoParent]);
|
|
270
|
+
return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
2406
271
|
renderDefault(props),
|
|
2407
|
-
/* @__PURE__ */
|
|
2408
|
-
/* @__PURE__ */
|
|
272
|
+
/* @__PURE__ */ jsx(Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
273
|
+
/* @__PURE__ */ jsx(
|
|
2409
274
|
"div",
|
|
2410
275
|
{
|
|
2411
276
|
style: {
|
|
@@ -2416,31 +281,24 @@ var OgImageUrl = (props) => {
|
|
|
2416
281
|
}
|
|
2417
282
|
}
|
|
2418
283
|
),
|
|
2419
|
-
/* @__PURE__ */
|
|
284
|
+
/* @__PURE__ */ jsx(Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
2420
285
|
] }, item.text)) })
|
|
2421
286
|
] });
|
|
2422
287
|
};
|
|
2423
288
|
var OgImageUrl_default = OgImageUrl;
|
|
2424
|
-
|
|
2425
|
-
// src/components/openGraph/OgTitle.tsx
|
|
2426
|
-
init_seoUtils();
|
|
2427
|
-
import { Stack as Stack7, Text as Text7 } from "@sanity/ui";
|
|
2428
|
-
import { useMemo as useMemo7 } from "react";
|
|
2429
|
-
import { useFormValue as useFormValue7 } from "sanity";
|
|
2430
|
-
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2431
289
|
var OgTitle = (props) => {
|
|
2432
290
|
const { value, renderDefault, path } = props;
|
|
2433
|
-
const parent =
|
|
291
|
+
const parent = useFormValue([path[0]]);
|
|
2434
292
|
const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
|
|
2435
|
-
const keywords =
|
|
2436
|
-
const feedbackItems =
|
|
293
|
+
const keywords = useMemo(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
|
|
294
|
+
const feedbackItems = useMemo(
|
|
2437
295
|
() => getOgTitleValidation(value || "", keywords, isParentseoField),
|
|
2438
296
|
[value, keywords, isParentseoField]
|
|
2439
297
|
);
|
|
2440
|
-
return /* @__PURE__ */
|
|
298
|
+
return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
2441
299
|
renderDefault(props),
|
|
2442
|
-
/* @__PURE__ */
|
|
2443
|
-
/* @__PURE__ */
|
|
300
|
+
/* @__PURE__ */ jsx(Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
301
|
+
/* @__PURE__ */ jsx(
|
|
2444
302
|
"div",
|
|
2445
303
|
{
|
|
2446
304
|
style: {
|
|
@@ -2451,7 +309,7 @@ var OgTitle = (props) => {
|
|
|
2451
309
|
}
|
|
2452
310
|
}
|
|
2453
311
|
),
|
|
2454
|
-
/* @__PURE__ */
|
|
312
|
+
/* @__PURE__ */ jsx(Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
2455
313
|
] }, item.text)) })
|
|
2456
314
|
] });
|
|
2457
315
|
};
|
|
@@ -2572,29 +430,19 @@ function openGraph(config = {}) {
|
|
|
2572
430
|
]
|
|
2573
431
|
});
|
|
2574
432
|
}
|
|
2575
|
-
|
|
2576
|
-
// src/schemas/types/twitter/index.ts
|
|
2577
|
-
import { defineField as defineField2, defineType as defineType2 } from "sanity";
|
|
2578
|
-
|
|
2579
|
-
// src/components/twitter/twitterDescription.tsx
|
|
2580
|
-
init_seoUtils();
|
|
2581
|
-
import { Stack as Stack8, Text as Text8 } from "@sanity/ui";
|
|
2582
|
-
import { useMemo as useMemo8 } from "react";
|
|
2583
|
-
import { useFormValue as useFormValue8 } from "sanity";
|
|
2584
|
-
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2585
433
|
var TwitterDescription = (props) => {
|
|
2586
434
|
const { value, renderDefault, path } = props;
|
|
2587
|
-
const parent =
|
|
435
|
+
const parent = useFormValue([path[0]]);
|
|
2588
436
|
const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
|
|
2589
|
-
const keywords =
|
|
2590
|
-
const feedbackItems =
|
|
437
|
+
const keywords = useMemo(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
|
|
438
|
+
const feedbackItems = useMemo(
|
|
2591
439
|
() => getTwitterDescriptionValidation(value || "", keywords, isParentseoField),
|
|
2592
440
|
[value, keywords, isParentseoField]
|
|
2593
441
|
);
|
|
2594
|
-
return /* @__PURE__ */
|
|
442
|
+
return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
2595
443
|
renderDefault(props),
|
|
2596
|
-
/* @__PURE__ */
|
|
2597
|
-
/* @__PURE__ */
|
|
444
|
+
/* @__PURE__ */ jsx(Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
445
|
+
/* @__PURE__ */ jsx(
|
|
2598
446
|
"div",
|
|
2599
447
|
{
|
|
2600
448
|
style: {
|
|
@@ -2605,32 +453,25 @@ var TwitterDescription = (props) => {
|
|
|
2605
453
|
}
|
|
2606
454
|
}
|
|
2607
455
|
),
|
|
2608
|
-
/* @__PURE__ */
|
|
456
|
+
/* @__PURE__ */ jsx(Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
2609
457
|
] }, item.text)) })
|
|
2610
458
|
] });
|
|
2611
459
|
};
|
|
2612
460
|
var twitterDescription_default = TwitterDescription;
|
|
2613
|
-
|
|
2614
|
-
// src/components/twitter/TwitterImage.tsx
|
|
2615
|
-
init_seoUtils();
|
|
2616
|
-
import { Stack as Stack9, Text as Text9 } from "@sanity/ui";
|
|
2617
|
-
import { useMemo as useMemo9 } from "react";
|
|
2618
|
-
import { useFormValue as useFormValue9 } from "sanity";
|
|
2619
|
-
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2620
461
|
var TwitterImage = (props) => {
|
|
2621
462
|
const { value, renderDefault, path } = props;
|
|
2622
|
-
const seoParent =
|
|
463
|
+
const seoParent = useFormValue([path[0]]);
|
|
2623
464
|
const imgValue = value;
|
|
2624
465
|
const hasImage = !!(imgValue == null ? void 0 : imgValue.asset);
|
|
2625
466
|
const altText = imgValue == null ? void 0 : imgValue.alt;
|
|
2626
|
-
const feedbackItems =
|
|
467
|
+
const feedbackItems = useMemo(
|
|
2627
468
|
() => getTwitterImageValidation(hasImage, altText, seoParent),
|
|
2628
469
|
[hasImage, altText, seoParent]
|
|
2629
470
|
);
|
|
2630
|
-
return /* @__PURE__ */
|
|
471
|
+
return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
2631
472
|
renderDefault(props),
|
|
2632
|
-
/* @__PURE__ */
|
|
2633
|
-
/* @__PURE__ */
|
|
473
|
+
/* @__PURE__ */ jsx(Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
474
|
+
/* @__PURE__ */ jsx(
|
|
2634
475
|
"div",
|
|
2635
476
|
{
|
|
2636
477
|
style: {
|
|
@@ -2641,29 +482,22 @@ var TwitterImage = (props) => {
|
|
|
2641
482
|
}
|
|
2642
483
|
}
|
|
2643
484
|
),
|
|
2644
|
-
/* @__PURE__ */
|
|
485
|
+
/* @__PURE__ */ jsx(Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
2645
486
|
] }, item.text)) })
|
|
2646
487
|
] });
|
|
2647
488
|
};
|
|
2648
489
|
var TwitterImage_default = TwitterImage;
|
|
2649
|
-
|
|
2650
|
-
// src/components/twitter/TwitterImageUrl.tsx
|
|
2651
|
-
init_seoUtils();
|
|
2652
|
-
import { Stack as Stack10, Text as Text10 } from "@sanity/ui";
|
|
2653
|
-
import { useMemo as useMemo10 } from "react";
|
|
2654
|
-
import { useFormValue as useFormValue10 } from "sanity";
|
|
2655
|
-
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2656
490
|
var TwitterImageUrl = (props) => {
|
|
2657
491
|
const { value, renderDefault, path } = props;
|
|
2658
|
-
const seoParent =
|
|
2659
|
-
const feedbackItems =
|
|
492
|
+
const seoParent = useFormValue([path[0]]);
|
|
493
|
+
const feedbackItems = useMemo(
|
|
2660
494
|
() => getTwitterImageUrlValidation(value, seoParent),
|
|
2661
495
|
[value, seoParent]
|
|
2662
496
|
);
|
|
2663
|
-
return /* @__PURE__ */
|
|
497
|
+
return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
2664
498
|
renderDefault(props),
|
|
2665
|
-
/* @__PURE__ */
|
|
2666
|
-
/* @__PURE__ */
|
|
499
|
+
/* @__PURE__ */ jsx(Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
500
|
+
/* @__PURE__ */ jsx(
|
|
2667
501
|
"div",
|
|
2668
502
|
{
|
|
2669
503
|
style: {
|
|
@@ -2674,31 +508,24 @@ var TwitterImageUrl = (props) => {
|
|
|
2674
508
|
}
|
|
2675
509
|
}
|
|
2676
510
|
),
|
|
2677
|
-
/* @__PURE__ */
|
|
511
|
+
/* @__PURE__ */ jsx(Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
2678
512
|
] }, item.text)) })
|
|
2679
513
|
] });
|
|
2680
514
|
};
|
|
2681
515
|
var TwitterImageUrl_default = TwitterImageUrl;
|
|
2682
|
-
|
|
2683
|
-
// src/components/twitter/twitterTitle.tsx
|
|
2684
|
-
init_seoUtils();
|
|
2685
|
-
import { Stack as Stack11, Text as Text11 } from "@sanity/ui";
|
|
2686
|
-
import { useMemo as useMemo11 } from "react";
|
|
2687
|
-
import { useFormValue as useFormValue11 } from "sanity";
|
|
2688
|
-
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2689
516
|
var TwitterTitle = (props) => {
|
|
2690
517
|
const { value, renderDefault, path } = props;
|
|
2691
|
-
const parent =
|
|
518
|
+
const parent = useFormValue([path[0]]);
|
|
2692
519
|
const isParentseoField = parent && (parent == null ? void 0 : parent._type) === "seoFields";
|
|
2693
|
-
const keywords =
|
|
2694
|
-
const feedbackItems =
|
|
520
|
+
const keywords = useMemo(() => (parent == null ? void 0 : parent.keywords) || [], [parent == null ? void 0 : parent.keywords]);
|
|
521
|
+
const feedbackItems = useMemo(
|
|
2695
522
|
() => getTwitterTitleValidation(value || "", keywords, isParentseoField),
|
|
2696
523
|
[value, keywords, isParentseoField]
|
|
2697
524
|
);
|
|
2698
|
-
return /* @__PURE__ */
|
|
525
|
+
return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
2699
526
|
renderDefault(props),
|
|
2700
|
-
/* @__PURE__ */
|
|
2701
|
-
/* @__PURE__ */
|
|
527
|
+
/* @__PURE__ */ jsx(Stack, { space: 2, children: feedbackItems.map((item) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [
|
|
528
|
+
/* @__PURE__ */ jsx(
|
|
2702
529
|
"div",
|
|
2703
530
|
{
|
|
2704
531
|
style: {
|
|
@@ -2709,7 +536,7 @@ var TwitterTitle = (props) => {
|
|
|
2709
536
|
}
|
|
2710
537
|
}
|
|
2711
538
|
),
|
|
2712
|
-
/* @__PURE__ */
|
|
539
|
+
/* @__PURE__ */ jsx(Text, { weight: "bold", muted: true, size: 14, children: item.text })
|
|
2713
540
|
] }, item.text)) })
|
|
2714
541
|
] });
|
|
2715
542
|
};
|
|
@@ -2717,12 +544,12 @@ var twitterTitle_default = TwitterTitle;
|
|
|
2717
544
|
|
|
2718
545
|
// src/schemas/types/twitter/index.ts
|
|
2719
546
|
function twitter(config = {}) {
|
|
2720
|
-
return
|
|
547
|
+
return defineType({
|
|
2721
548
|
name: "twitter",
|
|
2722
549
|
title: "X (Formerly Twitter)",
|
|
2723
550
|
type: "object",
|
|
2724
551
|
fields: [
|
|
2725
|
-
|
|
552
|
+
defineField(__spreadProps(__spreadValues({
|
|
2726
553
|
name: "card"
|
|
2727
554
|
}, getFieldInfo("twitterCard", config.fieldOverrides)), {
|
|
2728
555
|
type: "string",
|
|
@@ -2738,19 +565,19 @@ function twitter(config = {}) {
|
|
|
2738
565
|
initialValue: "summary_large_image"
|
|
2739
566
|
// good default
|
|
2740
567
|
})),
|
|
2741
|
-
|
|
568
|
+
defineField(__spreadProps(__spreadValues({
|
|
2742
569
|
name: "site"
|
|
2743
570
|
}, getFieldInfo("twitterSite", config.fieldOverrides)), {
|
|
2744
571
|
type: "string",
|
|
2745
572
|
hidden: getFieldHiddenFunction("twitterSite", config)
|
|
2746
573
|
})),
|
|
2747
|
-
|
|
574
|
+
defineField(__spreadProps(__spreadValues({
|
|
2748
575
|
name: "creator",
|
|
2749
576
|
type: "string"
|
|
2750
577
|
}, getFieldInfo("twitterCreator", config.fieldOverrides)), {
|
|
2751
578
|
hidden: getFieldHiddenFunction("twitterCreator", config)
|
|
2752
579
|
})),
|
|
2753
|
-
|
|
580
|
+
defineField(__spreadProps(__spreadValues({
|
|
2754
581
|
name: "title",
|
|
2755
582
|
type: "string"
|
|
2756
583
|
}, getFieldInfo("twitterTitle", config.fieldOverrides)), {
|
|
@@ -2759,7 +586,7 @@ function twitter(config = {}) {
|
|
|
2759
586
|
input: twitterTitle_default
|
|
2760
587
|
}
|
|
2761
588
|
})),
|
|
2762
|
-
|
|
589
|
+
defineField(__spreadProps(__spreadValues({
|
|
2763
590
|
name: "description",
|
|
2764
591
|
type: "text",
|
|
2765
592
|
rows: 3
|
|
@@ -2769,7 +596,7 @@ function twitter(config = {}) {
|
|
|
2769
596
|
input: twitterDescription_default
|
|
2770
597
|
}
|
|
2771
598
|
})),
|
|
2772
|
-
|
|
599
|
+
defineField(__spreadProps(__spreadValues({
|
|
2773
600
|
name: "imageType"
|
|
2774
601
|
}, getFieldInfo("twitterImageType", config.fieldOverrides)), {
|
|
2775
602
|
type: "string",
|
|
@@ -2782,7 +609,7 @@ function twitter(config = {}) {
|
|
|
2782
609
|
hidden: getFieldHiddenFunction("twitterImage", config),
|
|
2783
610
|
initialValue: "upload"
|
|
2784
611
|
})),
|
|
2785
|
-
|
|
612
|
+
defineField(__spreadProps(__spreadValues({
|
|
2786
613
|
name: "image"
|
|
2787
614
|
}, getFieldInfo("twitterImage", config.fieldOverrides)), {
|
|
2788
615
|
type: "image",
|
|
@@ -2793,7 +620,7 @@ function twitter(config = {}) {
|
|
|
2793
620
|
input: TwitterImage_default
|
|
2794
621
|
},
|
|
2795
622
|
fields: [
|
|
2796
|
-
|
|
623
|
+
defineField({
|
|
2797
624
|
name: "alt",
|
|
2798
625
|
title: "Image Alt Text",
|
|
2799
626
|
type: "string",
|
|
@@ -2807,7 +634,7 @@ function twitter(config = {}) {
|
|
|
2807
634
|
return typeof hiddenFn === "function" ? hiddenFn(context) : hiddenFn;
|
|
2808
635
|
}
|
|
2809
636
|
})),
|
|
2810
|
-
|
|
637
|
+
defineField(__spreadProps(__spreadValues({
|
|
2811
638
|
name: "imageUrl"
|
|
2812
639
|
}, getFieldInfo("twitterImageUrl", config.fieldOverrides)), {
|
|
2813
640
|
type: "url",
|
|
@@ -2826,15 +653,15 @@ function twitter(config = {}) {
|
|
|
2826
653
|
}
|
|
2827
654
|
|
|
2828
655
|
// src/schemas/index.ts
|
|
2829
|
-
var LazySeoPreview = React12.lazy(() =>
|
|
656
|
+
var LazySeoPreview = React12.lazy(() => import('./SeoPreview-G3LPA7GV.js'));
|
|
2830
657
|
var SeoPreviewWrapper = (props) => React12.createElement(React12.Suspense, { fallback: null }, React12.createElement(LazySeoPreview, props));
|
|
2831
658
|
function seoFieldsSchema(config = {}) {
|
|
2832
|
-
return
|
|
659
|
+
return defineType({
|
|
2833
660
|
name: "seoFields",
|
|
2834
661
|
title: "SEO Fields",
|
|
2835
662
|
type: "object",
|
|
2836
663
|
fields: [
|
|
2837
|
-
|
|
664
|
+
defineField({
|
|
2838
665
|
name: "robots",
|
|
2839
666
|
title: "Robots Settings",
|
|
2840
667
|
type: "robots",
|
|
@@ -2843,7 +670,7 @@ function seoFieldsSchema(config = {}) {
|
|
|
2843
670
|
}),
|
|
2844
671
|
// 👇 conditionally spread preview field
|
|
2845
672
|
...typeof config.seoPreview === "boolean" && config.seoPreview || typeof config.seoPreview === "object" && !isEmpty(config.seoPreview) ? [
|
|
2846
|
-
|
|
673
|
+
defineField({
|
|
2847
674
|
name: "preview",
|
|
2848
675
|
title: "SEO Preview",
|
|
2849
676
|
type: "string",
|
|
@@ -2858,7 +685,7 @@ function seoFieldsSchema(config = {}) {
|
|
|
2858
685
|
readOnly: true
|
|
2859
686
|
})
|
|
2860
687
|
] : [],
|
|
2861
|
-
|
|
688
|
+
defineField(__spreadProps(__spreadValues({
|
|
2862
689
|
name: "title"
|
|
2863
690
|
}, getFieldInfo("title", config.fieldOverrides)), {
|
|
2864
691
|
// title: 'Meta Title',
|
|
@@ -2871,7 +698,7 @@ function seoFieldsSchema(config = {}) {
|
|
|
2871
698
|
// validation: (Rule) => Rule.max(60).warning('Meta title should be under 60 characters.'),
|
|
2872
699
|
hidden: getFieldHiddenFunction("title", config)
|
|
2873
700
|
})),
|
|
2874
|
-
|
|
701
|
+
defineField(__spreadProps(__spreadValues({
|
|
2875
702
|
name: "description"
|
|
2876
703
|
}, getFieldInfo("description", config.fieldOverrides)), {
|
|
2877
704
|
// title: 'Meta Description',
|
|
@@ -2885,7 +712,7 @@ function seoFieldsSchema(config = {}) {
|
|
|
2885
712
|
// validation: (Rule) => Rule.max(160).warning('Meta description should be under 160 characters.'),
|
|
2886
713
|
hidden: getFieldHiddenFunction("description", config)
|
|
2887
714
|
})),
|
|
2888
|
-
|
|
715
|
+
defineField(__spreadProps(__spreadValues({
|
|
2889
716
|
name: "metaImage"
|
|
2890
717
|
}, getFieldInfo("metaImage", config.fieldOverrides)), {
|
|
2891
718
|
// title: 'Meta Image',
|
|
@@ -2900,7 +727,7 @@ function seoFieldsSchema(config = {}) {
|
|
|
2900
727
|
},
|
|
2901
728
|
hidden: getFieldHiddenFunction("metaImage", config)
|
|
2902
729
|
})),
|
|
2903
|
-
|
|
730
|
+
defineField(__spreadProps(__spreadValues({
|
|
2904
731
|
name: "metaAttributes"
|
|
2905
732
|
}, getFieldInfo("metaAttributes", config.fieldOverrides)), {
|
|
2906
733
|
type: "array",
|
|
@@ -2909,7 +736,7 @@ function seoFieldsSchema(config = {}) {
|
|
|
2909
736
|
// 'Add custom meta attributes to the head of the document for additional SEO and social media integration.',
|
|
2910
737
|
hidden: getFieldHiddenFunction("metaAttributes", config)
|
|
2911
738
|
})),
|
|
2912
|
-
|
|
739
|
+
defineField(__spreadProps(__spreadValues({
|
|
2913
740
|
name: "keywords"
|
|
2914
741
|
}, getFieldInfo("keywords", config.fieldOverrides)), {
|
|
2915
742
|
title: "Keywords",
|
|
@@ -2918,7 +745,7 @@ function seoFieldsSchema(config = {}) {
|
|
|
2918
745
|
description: "Add relevant keywords for this page. These keywords will be used for SEO purposes.",
|
|
2919
746
|
hidden: getFieldHiddenFunction("keywords", config)
|
|
2920
747
|
})),
|
|
2921
|
-
|
|
748
|
+
defineField(__spreadProps(__spreadValues({
|
|
2922
749
|
name: "canonicalUrl"
|
|
2923
750
|
}, getFieldInfo("canonicalUrl", config.fieldOverrides)), {
|
|
2924
751
|
title: "Canonical URL",
|
|
@@ -2931,20 +758,17 @@ function seoFieldsSchema(config = {}) {
|
|
|
2931
758
|
]
|
|
2932
759
|
});
|
|
2933
760
|
}
|
|
2934
|
-
|
|
2935
|
-
// src/schemas/types/metaAttribute/index.ts
|
|
2936
|
-
import { defineField as defineField4, defineType as defineType4 } from "sanity";
|
|
2937
|
-
var metaAttribute_default = defineType4({
|
|
761
|
+
var metaAttribute_default = defineType({
|
|
2938
762
|
name: "metaAttribute",
|
|
2939
763
|
title: "Meta Attribute",
|
|
2940
764
|
type: "object",
|
|
2941
765
|
fields: [
|
|
2942
|
-
|
|
766
|
+
defineField({
|
|
2943
767
|
name: "key",
|
|
2944
768
|
title: "Attribute Name",
|
|
2945
769
|
type: "string"
|
|
2946
770
|
}),
|
|
2947
|
-
|
|
771
|
+
defineField({
|
|
2948
772
|
name: "type",
|
|
2949
773
|
title: "Attribute Value Type",
|
|
2950
774
|
type: "string",
|
|
@@ -2956,13 +780,13 @@ var metaAttribute_default = defineType4({
|
|
|
2956
780
|
},
|
|
2957
781
|
initialValue: "string"
|
|
2958
782
|
}),
|
|
2959
|
-
|
|
783
|
+
defineField({
|
|
2960
784
|
name: "value",
|
|
2961
785
|
title: "Attribute Value",
|
|
2962
786
|
type: "string",
|
|
2963
787
|
hidden: ({ parent }) => (parent == null ? void 0 : parent.type) === "image"
|
|
2964
788
|
}),
|
|
2965
|
-
|
|
789
|
+
defineField({
|
|
2966
790
|
name: "image",
|
|
2967
791
|
title: "Attribute Image Value",
|
|
2968
792
|
type: "image",
|
|
@@ -2992,10 +816,7 @@ var metaAttribute_default = defineType4({
|
|
|
2992
816
|
}
|
|
2993
817
|
}
|
|
2994
818
|
});
|
|
2995
|
-
|
|
2996
|
-
// src/schemas/types/metaTag/index.ts
|
|
2997
|
-
import { defineType as defineType5 } from "sanity";
|
|
2998
|
-
var metaTag_default = defineType5({
|
|
819
|
+
var metaTag_default = defineType({
|
|
2999
820
|
name: "metaTag",
|
|
3000
821
|
title: "Meta Tag",
|
|
3001
822
|
type: "object",
|
|
@@ -3009,22 +830,19 @@ var metaTag_default = defineType5({
|
|
|
3009
830
|
}
|
|
3010
831
|
]
|
|
3011
832
|
});
|
|
3012
|
-
|
|
3013
|
-
// src/schemas/types/robots/index.ts
|
|
3014
|
-
import { defineField as defineField5, defineType as defineType6 } from "sanity";
|
|
3015
|
-
var robots_default = defineType6({
|
|
833
|
+
var robots_default = defineType({
|
|
3016
834
|
name: "robots",
|
|
3017
835
|
title: "Robots Settings",
|
|
3018
836
|
type: "object",
|
|
3019
837
|
fields: [
|
|
3020
|
-
|
|
838
|
+
defineField({
|
|
3021
839
|
name: "noIndex",
|
|
3022
840
|
title: "No Index",
|
|
3023
841
|
type: "boolean",
|
|
3024
842
|
initialValue: false,
|
|
3025
843
|
description: "Enable this to prevent search engines from indexing this page. The page will not appear in search results."
|
|
3026
844
|
}),
|
|
3027
|
-
|
|
845
|
+
defineField({
|
|
3028
846
|
name: "noFollow",
|
|
3029
847
|
title: "No Follow",
|
|
3030
848
|
type: "boolean",
|
|
@@ -3153,11 +971,11 @@ var resolveDashboardConfig = (healthDashboard) => {
|
|
|
3153
971
|
var seofields = definePlugin((config = {}) => {
|
|
3154
972
|
const { healthDashboard = true } = config;
|
|
3155
973
|
const dash = resolveDashboardConfig(healthDashboard);
|
|
3156
|
-
const LazySeoHealthTool =
|
|
3157
|
-
const BoundSeoHealthTool = () =>
|
|
3158
|
-
|
|
974
|
+
const LazySeoHealthTool = React12.lazy(() => import('./SeoHealthTool-MQBE5YGN.js'));
|
|
975
|
+
const BoundSeoHealthTool = () => React12.createElement(
|
|
976
|
+
React12.Suspense,
|
|
3159
977
|
{ fallback: null },
|
|
3160
|
-
|
|
978
|
+
React12.createElement(LazySeoHealthTool, {
|
|
3161
979
|
icon: dash.icon,
|
|
3162
980
|
title: dash.title,
|
|
3163
981
|
description: dash.description,
|
|
@@ -3197,15 +1015,11 @@ var seofields = definePlugin((config = {}) => {
|
|
|
3197
1015
|
});
|
|
3198
1016
|
});
|
|
3199
1017
|
var plugin_default = seofields;
|
|
3200
|
-
|
|
3201
|
-
// src/components/SeoHealthPane.tsx
|
|
3202
|
-
import React15 from "react";
|
|
3203
|
-
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
3204
|
-
var LazySeoHealthDashboard = React15.lazy(() => Promise.resolve().then(() => (init_SeoHealthDashboard(), SeoHealthDashboard_exports)));
|
|
1018
|
+
var LazySeoHealthDashboard = React12.lazy(() => import('./SeoHealthDashboard-BNJBRQVN.js'));
|
|
3205
1019
|
function createSeoHealthPane(optionsOrS, optionsWhenS) {
|
|
3206
1020
|
const S = optionsOrS;
|
|
3207
1021
|
const _a = optionsWhenS != null ? optionsWhenS : {}, { query, openInPane = true, title: paneTitle } = _a, rest = __objRest(_a, ["query", "openInPane", "title"]);
|
|
3208
|
-
const SeoHealthPane = () => /* @__PURE__ */
|
|
1022
|
+
const SeoHealthPane = () => /* @__PURE__ */ jsx(React12.Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
|
|
3209
1023
|
LazySeoHealthDashboard,
|
|
3210
1024
|
__spreadValues({
|
|
3211
1025
|
customQuery: query,
|
|
@@ -3222,15 +1036,7 @@ function createSeoHealthPane(optionsOrS, optionsWhenS) {
|
|
|
3222
1036
|
|
|
3223
1037
|
// src/index.ts
|
|
3224
1038
|
var src_default = plugin_default;
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
src_default as default,
|
|
3229
|
-
metaAttribute_default as metaAttributeSchema,
|
|
3230
|
-
metaTag_default as metaTagSchema,
|
|
3231
|
-
openGraph as openGraphSchema,
|
|
3232
|
-
robots_default as robotsSchema,
|
|
3233
|
-
seoFieldsSchema,
|
|
3234
|
-
twitter as twitterSchema
|
|
3235
|
-
};
|
|
1039
|
+
|
|
1040
|
+
export { types as allSchemas, createSeoHealthPane, src_default as default, metaAttribute_default as metaAttributeSchema, metaTag_default as metaTagSchema, openGraph as openGraphSchema, robots_default as robotsSchema, seoFieldsSchema, twitter as twitterSchema };
|
|
1041
|
+
//# sourceMappingURL=index.js.map
|
|
3236
1042
|
//# sourceMappingURL=index.js.map
|