sanity-plugin-seofields 1.5.0 → 1.5.2

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.
@@ -0,0 +1,2037 @@
1
+ import { __spreadProps, __spreadValues } from './chunk-2NMEKWO5.js';
2
+ import { useMemo, useState, useRef, useEffect, useCallback } from 'react';
3
+ import { useClient, useWorkspace } from 'sanity';
4
+ import { useIntentLink } from 'sanity/router';
5
+ import { usePaneRouter } from 'sanity/structure';
6
+ import styled, { keyframes, css } from 'styled-components';
7
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
8
+
9
+ var themeVars = {
10
+ light: {
11
+ "--seo-bg": "#f0f2f5",
12
+ "--seo-card-bg": "#ffffff",
13
+ "--seo-text-primary": "#1f2937",
14
+ "--seo-text-secondary": "#6b7280",
15
+ "--seo-text-muted": "#9ca3af",
16
+ "--seo-border": "#e5e7eb",
17
+ "--seo-border-light": "#f3f4f6",
18
+ "--seo-input-bg": "#f9fafb",
19
+ "--seo-header-bg": "#f9fafb",
20
+ "--seo-accent": "#6366f1",
21
+ "--seo-accent-hover": "#4f46e5",
22
+ "--seo-link": "#4f46e5",
23
+ "--seo-link-hover": "#4338ca",
24
+ "--seo-hover-bg": "#fafafa",
25
+ "--seo-shadow": "rgba(0, 0, 0, 0.07)",
26
+ "--seo-shadow-hover": "rgba(0, 0, 0, 0.1)",
27
+ "--seo-focus-ring": "rgba(99, 102, 241, 0.1)",
28
+ "--seo-issue-color": "#ef4444",
29
+ "--seo-more-issues": "#6b7280",
30
+ "--seo-more-issues-hover": "#374151",
31
+ "--seo-popover-bg": "#1f2937",
32
+ "--seo-popover-text": "#ffffff",
33
+ "--seo-spinner-track": "#e5e7eb",
34
+ "--seo-spinner-fill": "#6366f1",
35
+ "--seo-btn-bg": "#ffffff",
36
+ "--seo-btn-text": "#374151",
37
+ "--seo-btn-border": "#d1d5db",
38
+ "--seo-btn-hover-bg": "#f3f4f6",
39
+ "--seo-btn-hover-border": "#9ca3af",
40
+ "--seo-select-arrow": "%236b7280",
41
+ "--seo-score-excellent-bg": "#d1fae5",
42
+ "--seo-score-excellent-text": "#065f46",
43
+ "--seo-score-good-bg": "#fef3c7",
44
+ "--seo-score-good-text": "#92400e",
45
+ "--seo-score-fair-bg": "#ffedd5",
46
+ "--seo-score-fair-text": "#9a3412",
47
+ "--seo-score-poor-bg": "#fee2e2",
48
+ "--seo-score-poor-text": "#991b1b",
49
+ "--seo-preview-bg": "#fef3c7",
50
+ "--seo-preview-text": "#92400e",
51
+ "--seo-deprecation-bg": "#fffbeb",
52
+ "--seo-deprecation-border": "#fcd34d",
53
+ "--seo-deprecation-text": "#78350f",
54
+ "--seo-deprecation-link": "#92400e",
55
+ "--seo-upgrade-bg": "#ffffff",
56
+ "--seo-upgrade-border": "#e5e7eb",
57
+ "--seo-upgrade-code-bg": "#f3f4f6",
58
+ "--seo-upgrade-code-text": "#374151",
59
+ "--seo-upgrade-code-border": "#e5e7eb",
60
+ "--seo-warning-bg": "#fef3c7",
61
+ "--seo-warning-border": "#fcd34d",
62
+ "--seo-warning-text": "#92400e",
63
+ "--seo-theme-picker-bg": "#ffffff",
64
+ "--seo-theme-picker-border": "#e5e7eb",
65
+ "--seo-theme-picker-active": "#f0f0ff",
66
+ "--seo-theme-picker-active-border": "#6366f1"
67
+ },
68
+ dark: {
69
+ "--seo-bg": "#1a1b1e",
70
+ "--seo-card-bg": "#25262b",
71
+ "--seo-text-primary": "#c9cdd3",
72
+ "--seo-text-secondary": "#8b919a",
73
+ "--seo-text-muted": "#6b7280",
74
+ "--seo-border": "#373a40",
75
+ "--seo-border-light": "#2c2e33",
76
+ "--seo-input-bg": "#2c2e33",
77
+ "--seo-header-bg": "#2c2e33",
78
+ "--seo-accent": "#818cf8",
79
+ "--seo-accent-hover": "#6366f1",
80
+ "--seo-link": "#818cf8",
81
+ "--seo-link-hover": "#a5b4fc",
82
+ "--seo-hover-bg": "#2c2e33",
83
+ "--seo-shadow": "rgba(0, 0, 0, 0.25)",
84
+ "--seo-shadow-hover": "rgba(0, 0, 0, 0.35)",
85
+ "--seo-focus-ring": "rgba(129, 140, 248, 0.15)",
86
+ "--seo-issue-color": "#f87171",
87
+ "--seo-more-issues": "#8b919a",
88
+ "--seo-more-issues-hover": "#c9cdd3",
89
+ "--seo-popover-bg": "#373a40",
90
+ "--seo-popover-text": "#e5e7eb",
91
+ "--seo-spinner-track": "#373a40",
92
+ "--seo-spinner-fill": "#818cf8",
93
+ "--seo-btn-bg": "#2c2e33",
94
+ "--seo-btn-text": "#c9cdd3",
95
+ "--seo-btn-border": "#373a40",
96
+ "--seo-btn-hover-bg": "#373a40",
97
+ "--seo-btn-hover-border": "#4b5058",
98
+ "--seo-select-arrow": "%238b919a",
99
+ "--seo-score-excellent-bg": "#064e3b",
100
+ "--seo-score-excellent-text": "#6ee7b7",
101
+ "--seo-score-good-bg": "#78350f",
102
+ "--seo-score-good-text": "#fcd34d",
103
+ "--seo-score-fair-bg": "#7c2d12",
104
+ "--seo-score-fair-text": "#fdba74",
105
+ "--seo-score-poor-bg": "#7f1d1d",
106
+ "--seo-score-poor-text": "#fca5a5",
107
+ "--seo-preview-bg": "#78350f",
108
+ "--seo-preview-text": "#fcd34d",
109
+ "--seo-deprecation-bg": "#422006",
110
+ "--seo-deprecation-border": "#a16207",
111
+ "--seo-deprecation-text": "#fcd34d",
112
+ "--seo-deprecation-link": "#fbbf24",
113
+ "--seo-upgrade-bg": "#25262b",
114
+ "--seo-upgrade-border": "#373a40",
115
+ "--seo-upgrade-code-bg": "#2c2e33",
116
+ "--seo-upgrade-code-text": "#c9cdd3",
117
+ "--seo-upgrade-code-border": "#373a40",
118
+ "--seo-warning-bg": "#422006",
119
+ "--seo-warning-border": "#a16207",
120
+ "--seo-warning-text": "#fcd34d",
121
+ "--seo-theme-picker-bg": "#25262b",
122
+ "--seo-theme-picker-border": "#373a40",
123
+ "--seo-theme-picker-active": "#2d2d44",
124
+ "--seo-theme-picker-active-border": "#818cf8"
125
+ }
126
+ };
127
+ var DashboardContainer = styled.div`
128
+ width: 100%;
129
+ min-height: 100%;
130
+ background: var(--seo-bg);
131
+ padding: 28px 32px;
132
+ box-sizing: border-box;
133
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
134
+ color: var(--seo-text-primary);
135
+ `;
136
+ var PageHeader = styled.div`
137
+ display: flex;
138
+ align-items: flex-start;
139
+ justify-content: space-between;
140
+ gap: 12px;
141
+ margin-bottom: 28px;
142
+ `;
143
+ var PageTitle = styled.h1`
144
+ margin: 0 0 6px 0;
145
+ font-size: 22px;
146
+ font-weight: 700;
147
+ color: var(--seo-text-primary);
148
+ letter-spacing: -0.3px;
149
+ display: flex;
150
+ align-items: center;
151
+ gap: 10px;
152
+ `;
153
+ var PreviewBadge = styled.span`
154
+ display: inline-block;
155
+ background: var(--seo-preview-bg);
156
+ color: var(--seo-preview-text);
157
+ font-size: 11px;
158
+ font-weight: 600;
159
+ padding: 4px 8px;
160
+ border-radius: 4px;
161
+ text-transform: uppercase;
162
+ letter-spacing: 0.5px;
163
+ margin-left: 8px;
164
+ `;
165
+ var PageSubtitle = styled.p`
166
+ margin: 0;
167
+ font-size: 13px;
168
+ color: var(--seo-text-secondary);
169
+ `;
170
+ var StatsGrid = styled.div`
171
+ display: grid;
172
+ grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
173
+ gap: 14px;
174
+ margin-bottom: 20px;
175
+ `;
176
+ var StatCard = styled.div`
177
+ background: var(--seo-card-bg);
178
+ border-radius: 10px;
179
+ padding: 16px 18px;
180
+ box-shadow:
181
+ 0 1px 3px var(--seo-shadow),
182
+ 0 1px 2px var(--seo-shadow);
183
+ border-left: ${(p) => p.$accent ? `4px solid ${p.$accent}` : "4px solid transparent"};
184
+ transition: box-shadow 0.15s ease;
185
+
186
+ &:hover {
187
+ box-shadow: 0 4px 12px var(--seo-shadow-hover);
188
+ }
189
+ `;
190
+ var StatLabel = styled.div`
191
+ font-size: 11px;
192
+ font-weight: 500;
193
+ color: var(--seo-text-muted);
194
+ text-transform: uppercase;
195
+ letter-spacing: 0.5px;
196
+ margin-bottom: 8px;
197
+ `;
198
+ var StatValue = styled.div`
199
+ font-size: 26px;
200
+ font-weight: 700;
201
+ color: var(--seo-text-primary);
202
+ line-height: 1;
203
+ `;
204
+ var ControlsBar = styled.div`
205
+ background: var(--seo-card-bg);
206
+ border-radius: 10px;
207
+ padding: 14px 18px;
208
+ display: flex;
209
+ align-items: center;
210
+ gap: 12px;
211
+ flex-wrap: wrap;
212
+ margin-bottom: 20px;
213
+ box-shadow: 0 1px 3px var(--seo-shadow);
214
+ `;
215
+ var SearchWrapper = styled.div`
216
+ position: relative;
217
+ flex: 1;
218
+ min-width: 220px;
219
+ `;
220
+ var SearchIconSvg = styled.span`
221
+ position: absolute;
222
+ left: 11px;
223
+ top: 50%;
224
+ transform: translateY(-50%);
225
+ color: var(--seo-text-muted);
226
+ display: flex;
227
+ align-items: center;
228
+ pointer-events: none;
229
+ `;
230
+ var SearchInput = styled.input`
231
+ width: 100%;
232
+ height: 36px;
233
+ padding: 0 12px 0 34px;
234
+ border: 1px solid var(--seo-border);
235
+ border-radius: 7px;
236
+ font-size: 13px;
237
+ color: var(--seo-text-primary);
238
+ background: var(--seo-input-bg);
239
+ box-sizing: border-box;
240
+ outline: none;
241
+ transition:
242
+ border-color 0.15s,
243
+ background 0.15s;
244
+
245
+ &::placeholder {
246
+ color: var(--seo-text-muted);
247
+ }
248
+
249
+ &:focus {
250
+ border-color: var(--seo-accent);
251
+ background: var(--seo-card-bg);
252
+ box-shadow: 0 0 0 3px var(--seo-focus-ring);
253
+ }
254
+ `;
255
+ var StyledSelect = styled.select`
256
+ height: 36px;
257
+ padding: 0 32px 0 12px;
258
+ border: 1px solid var(--seo-border);
259
+ border-radius: 7px;
260
+ font-size: 13px;
261
+ color: var(--seo-text-secondary);
262
+ background: var(--seo-input-bg)
263
+ 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")
264
+ no-repeat right 10px center;
265
+ appearance: none;
266
+ outline: none;
267
+ cursor: pointer;
268
+ transition: border-color 0.15s;
269
+
270
+ &:focus {
271
+ border-color: var(--seo-accent);
272
+ background-color: var(--seo-card-bg);
273
+ box-shadow: 0 0 0 3px var(--seo-focus-ring);
274
+ }
275
+ `;
276
+ var TableCard = styled.div`
277
+ background: var(--seo-card-bg);
278
+ border-radius: 10px;
279
+ box-shadow: 0 1px 3px var(--seo-shadow);
280
+ overflow: hidden;
281
+ `;
282
+ var TableHeader = styled.div`
283
+ display: flex;
284
+ align-items: center;
285
+ padding: 11px 20px;
286
+ background: var(--seo-header-bg);
287
+ border-bottom: 1px solid var(--seo-border);
288
+ font-size: 11px;
289
+ font-weight: 600;
290
+ color: var(--seo-text-secondary);
291
+ text-transform: uppercase;
292
+ letter-spacing: 0.5px;
293
+ gap: 12px;
294
+ `;
295
+ var TableRow = styled.div`
296
+ display: flex;
297
+ align-items: center;
298
+ padding: 13px 20px;
299
+ border-bottom: 1px solid var(--seo-border-light);
300
+ gap: 12px;
301
+ transition: background 0.1s;
302
+
303
+ &:last-child {
304
+ border-bottom: none;
305
+ }
306
+
307
+ &:hover {
308
+ background: var(--seo-hover-bg);
309
+ }
310
+ `;
311
+ var ColTitle = styled.div`
312
+ flex: 2;
313
+ min-width: 0;
314
+ `;
315
+ var TitleWrapper = styled.div`
316
+ display: flex;
317
+ align-items: center;
318
+ gap: 4px;
319
+ flex-wrap: wrap;
320
+ min-width: 0;
321
+ `;
322
+ var TitleCell = styled.div`
323
+ min-width: 0;
324
+ overflow: hidden;
325
+ flex: 1;
326
+ `;
327
+ var ColType = styled.div`
328
+ flex: 0.8;
329
+ min-width: 80px;
330
+ `;
331
+ var ColScore = styled.div`
332
+ flex: 0.6;
333
+ min-width: 70px;
334
+ `;
335
+ var ColIssues = styled.div`
336
+ flex: 2;
337
+ min-width: 0;
338
+ `;
339
+ var DocTitleLink = styled.a`
340
+ font-size: 13px;
341
+ font-weight: 600;
342
+ color: var(--seo-link);
343
+ white-space: nowrap;
344
+ overflow: hidden;
345
+ text-overflow: ellipsis;
346
+ text-decoration: none;
347
+ display: block;
348
+ transition: color 0.15s;
349
+
350
+ &:hover {
351
+ color: var(--seo-link-hover);
352
+ text-decoration: underline;
353
+ }
354
+ `;
355
+ var DocId = styled.div`
356
+ font-size: 11px;
357
+ color: var(--seo-text-muted);
358
+ margin-top: 2px;
359
+ white-space: nowrap;
360
+ overflow: hidden;
361
+ text-overflow: ellipsis;
362
+ `;
363
+ var TypeBadge = styled.span`
364
+ display: inline-block;
365
+ padding: 3px 8px;
366
+ border-radius: 5px;
367
+ font-size: 11px;
368
+ font-weight: 500;
369
+ background: ${(p) => p.$bgColor || "#ede9fe"};
370
+ color: ${(p) => p.$textColor || "#5b21b6"};
371
+ `;
372
+ var TypeText = styled.span`
373
+ font-size: 12px;
374
+ font-weight: 500;
375
+ color: var(--seo-text-secondary);
376
+ `;
377
+ var CustomBadge = styled.span`
378
+ display: inline-block;
379
+ padding: 2px 6px;
380
+ border-radius: 4px;
381
+ font-size: ${(p) => p.$fontSize || "10px"};
382
+ font-weight: 600;
383
+ background: ${(p) => p.$bgColor || "#e0e7ff"};
384
+ color: ${(p) => p.$textColor || "#3730a3"};
385
+ white-space: nowrap;
386
+ `;
387
+ var ScoreBadge = styled.span`
388
+ display: inline-block;
389
+ padding: 4px 10px;
390
+ border-radius: 6px;
391
+ font-size: 12px;
392
+ font-weight: 700;
393
+ background: ${(p) => {
394
+ if (p.$score >= 80) return "var(--seo-score-excellent-bg)";
395
+ if (p.$score >= 60) return "var(--seo-score-good-bg)";
396
+ if (p.$score >= 40) return "var(--seo-score-fair-bg)";
397
+ return "var(--seo-score-poor-bg)";
398
+ }};
399
+ color: ${(p) => {
400
+ if (p.$score >= 80) return "var(--seo-score-excellent-text)";
401
+ if (p.$score >= 60) return "var(--seo-score-good-text)";
402
+ if (p.$score >= 40) return "var(--seo-score-fair-text)";
403
+ return "var(--seo-score-poor-text)";
404
+ }};
405
+ `;
406
+ var IssueTag = styled.div`
407
+ font-size: 11px;
408
+ color: var(--seo-issue-color);
409
+ line-height: 1.5;
410
+ white-space: nowrap;
411
+ overflow: hidden;
412
+ text-overflow: ellipsis;
413
+ `;
414
+ var NonStringTitleWarning = styled.div`
415
+ display: inline-flex;
416
+ align-items: center;
417
+ gap: 4px;
418
+ margin-top: 4px;
419
+ padding: 2px 7px;
420
+ border-radius: 4px;
421
+ background: var(--seo-warning-bg);
422
+ border: 1px solid var(--seo-warning-border);
423
+ font-size: 10px;
424
+ font-weight: 600;
425
+ color: var(--seo-warning-text);
426
+ line-height: 1.4;
427
+ cursor: default;
428
+ white-space: normal;
429
+ `;
430
+ var MoreIssues = styled.div`
431
+ font-size: 11px;
432
+ color: var(--seo-more-issues);
433
+ cursor: pointer;
434
+ transition: color 0.15s;
435
+
436
+ &:hover {
437
+ color: var(--seo-more-issues-hover);
438
+ }
439
+ `;
440
+ var MoreIssuesWrapper = styled.div`
441
+ position: relative;
442
+ display: inline-block;
443
+ `;
444
+ var IssuesPopover = styled.div`
445
+ position: fixed;
446
+ bottom: auto;
447
+ left: 0;
448
+ transform: translateY(calc(-100% - 14px));
449
+ background: var(--seo-popover-bg);
450
+ color: var(--seo-popover-text);
451
+ padding: 12px;
452
+ border-radius: 8px;
453
+ font-size: 12px;
454
+ z-index: 50;
455
+ box-shadow: 0 10px 25px var(--seo-shadow-hover);
456
+ width: 280px;
457
+ word-break: break-word;
458
+ line-height: 1.5;
459
+
460
+ &::after {
461
+ content: '';
462
+ position: absolute;
463
+ bottom: -6px;
464
+ left: 12px;
465
+ width: 0;
466
+ height: 0;
467
+ border-left: 6px solid transparent;
468
+ border-right: 6px solid transparent;
469
+ border-top: 6px solid var(--seo-popover-bg);
470
+ }
471
+ `;
472
+ var PopoverIssueItem = styled.div`
473
+ display: flex;
474
+ gap: 6px;
475
+ margin-bottom: 6px;
476
+
477
+ &:last-child {
478
+ margin-bottom: 0;
479
+ }
480
+ `;
481
+ var UpgradeContainer = styled.div`
482
+ display: flex;
483
+ align-items: center;
484
+ justify-content: center;
485
+ min-height: 100%;
486
+ padding: 60px 24px;
487
+ `;
488
+ var UpgradeBox = styled.div`
489
+ background: var(--seo-upgrade-bg);
490
+ border-radius: 16px;
491
+ padding: 48px 40px;
492
+ max-width: 480px;
493
+ width: 100%;
494
+ text-align: center;
495
+ box-shadow:
496
+ 0 4px 24px var(--seo-shadow),
497
+ 0 1px 4px var(--seo-shadow);
498
+ border: 1px solid var(--seo-upgrade-border);
499
+ `;
500
+ var UpgradeLock = styled.div`
501
+ font-size: 40px;
502
+ margin-bottom: 16px;
503
+ `;
504
+ var UpgradeTitle = styled.h2`
505
+ margin: 0 0 10px;
506
+ font-size: 20px;
507
+ font-weight: 700;
508
+ color: var(--seo-text-primary);
509
+ `;
510
+ var UpgradeText = styled.p`
511
+ margin: 0 0 20px;
512
+ font-size: 14px;
513
+ color: var(--seo-text-secondary);
514
+ line-height: 1.6;
515
+ `;
516
+ var UpgradeCode = styled.pre`
517
+ background: var(--seo-upgrade-code-bg);
518
+ border-radius: 8px;
519
+ padding: 14px 16px;
520
+ font-size: 12px;
521
+ color: var(--seo-upgrade-code-text);
522
+ text-align: left;
523
+ margin: 0 0 24px;
524
+ overflow-x: auto;
525
+ line-height: 1.6;
526
+ border: 1px solid var(--seo-upgrade-code-border);
527
+ `;
528
+ var UpgradeButton = styled.a`
529
+ display: inline-block;
530
+ background: var(--seo-accent);
531
+ color: #ffffff;
532
+ font-size: 14px;
533
+ font-weight: 600;
534
+ padding: 10px 24px;
535
+ border-radius: 8px;
536
+ text-decoration: none;
537
+ transition: background 0.15s;
538
+
539
+ &:hover {
540
+ background: var(--seo-accent-hover);
541
+ }
542
+ `;
543
+ var ReloadButton = styled.button`
544
+ display: inline-block;
545
+ background: transparent;
546
+ color: var(--seo-text-secondary);
547
+ font-size: 13px;
548
+ font-weight: 500;
549
+ padding: 8px 20px;
550
+ border-radius: 8px;
551
+ border: 1px solid var(--seo-btn-border);
552
+ cursor: pointer;
553
+ margin-top: 10px;
554
+ transition:
555
+ background 0.15s,
556
+ color 0.15s,
557
+ border-color 0.15s;
558
+
559
+ &:hover {
560
+ background: var(--seo-btn-hover-bg);
561
+ color: var(--seo-text-primary);
562
+ border-color: var(--seo-btn-hover-border);
563
+ }
564
+ `;
565
+ var spin = keyframes`
566
+ to { transform: rotate(360deg); }
567
+ `;
568
+ var DashboardRefreshButton = styled.button`
569
+ display: inline-flex;
570
+ align-items: center;
571
+ gap: 6px;
572
+ background: var(--seo-btn-bg);
573
+ color: var(--seo-btn-text);
574
+ font-size: 13px;
575
+ font-weight: 500;
576
+ padding: 8px 14px;
577
+ border-radius: 8px;
578
+ border: 1px solid var(--seo-btn-border);
579
+ cursor: pointer;
580
+ flex-shrink: 0;
581
+ transition:
582
+ background 0.15s,
583
+ color 0.15s,
584
+ border-color 0.15s,
585
+ box-shadow 0.15s;
586
+ box-shadow: 0 1px 2px var(--seo-shadow);
587
+
588
+ svg {
589
+ animation: ${(p) => p.$spinning ? css`
590
+ ${spin} 0.7s linear infinite
591
+ ` : "none"};
592
+ }
593
+
594
+ &:hover {
595
+ background: var(--seo-btn-hover-bg);
596
+ border-color: var(--seo-btn-hover-border);
597
+ box-shadow: 0 2px 6px var(--seo-shadow);
598
+ }
599
+
600
+ &:disabled {
601
+ opacity: 0.6;
602
+ cursor: not-allowed;
603
+ }
604
+ `;
605
+ var ThemeSwitcher = styled.div`
606
+ display: inline-flex;
607
+ align-items: center;
608
+ gap: 3px;
609
+ background: var(--seo-theme-picker-bg);
610
+ border: 1px solid var(--seo-theme-picker-border);
611
+ border-radius: 9px;
612
+ padding: 3px;
613
+ flex-shrink: 0;
614
+ box-shadow: 0 1px 3px var(--seo-shadow);
615
+ `;
616
+ var getThemeButtonActiveStyles = (theme) => {
617
+ switch (theme) {
618
+ case "light":
619
+ return css`
620
+ background: #fff4ed;
621
+ border: 1.5px solid #f97316;
622
+ color: #ea6c0a;
623
+ box-shadow:
624
+ 0 0 0 2px rgba(249, 115, 22, 0.15),
625
+ 0 1px 3px rgba(0, 0, 0, 0.08);
626
+ `;
627
+ case "dark":
628
+ return css`
629
+ background: #eff6ff;
630
+ border: 1.5px solid #3b82f6;
631
+ color: #2563eb;
632
+ box-shadow:
633
+ 0 0 0 2px rgba(59, 130, 246, 0.15),
634
+ 0 1px 3px rgba(0, 0, 0, 0.08);
635
+ `;
636
+ case "system":
637
+ return css`
638
+ background: #f0fdf4;
639
+ border: 1.5px solid #22c55e;
640
+ color: #16a34a;
641
+ box-shadow:
642
+ 0 0 0 2px rgba(34, 197, 94, 0.15),
643
+ 0 1px 3px rgba(0, 0, 0, 0.08);
644
+ `;
645
+ default:
646
+ return css`
647
+ background: #f0fdf4;
648
+ border: 1.5px solid #22c55e;
649
+ color: #16a34a;
650
+ box-shadow:
651
+ 0 0 0 2px rgba(34, 197, 94, 0.15),
652
+ 0 1px 3px rgba(0, 0, 0, 0.08);
653
+ `;
654
+ }
655
+ };
656
+ var getThemeButtonHoverStyles = (theme) => {
657
+ switch (theme) {
658
+ case "light":
659
+ return css`
660
+ background: #fff4ed;
661
+ border-color: rgba(249, 115, 22, 0.2);
662
+ color: #ea6c0a;
663
+ `;
664
+ case "dark":
665
+ return css`
666
+ background: #eff6ff;
667
+ border-color: rgba(59, 130, 246, 0.2);
668
+ color: #2563eb;
669
+ `;
670
+ case "system":
671
+ return css`
672
+ background: #f0fdf4;
673
+ border-color: rgba(34, 197, 94, 0.2);
674
+ color: #16a34a;
675
+ `;
676
+ default:
677
+ return css`
678
+ background: #f0fdf4;
679
+ border-color: rgba(34, 197, 94, 0.2);
680
+ color: #16a34a;
681
+ `;
682
+ }
683
+ };
684
+ var ThemeButton = styled.button`
685
+ display: inline-flex;
686
+ align-items: center;
687
+ justify-content: center;
688
+ width: 32px;
689
+ height: 28px;
690
+ border-radius: 6px;
691
+ position: relative;
692
+ cursor: pointer;
693
+ padding: 0;
694
+ transition:
695
+ background 0.18s,
696
+ color 0.18s,
697
+ border-color 0.18s,
698
+ box-shadow 0.18s;
699
+
700
+ /* Per-theme active colours */
701
+ ${(p) => p.$active ? getThemeButtonActiveStyles(p.$theme) : css`
702
+ background: transparent;
703
+ border: 1.5px solid transparent;
704
+ color: var(--seo-text-muted);
705
+ `}
706
+
707
+ &:hover {
708
+ ${(p) => p.$active ? "" : getThemeButtonHoverStyles(p.$theme)}
709
+ }
710
+
711
+ svg {
712
+ width: 14px;
713
+ height: 14px;
714
+ }
715
+ `;
716
+ var SunIcon = () => /* @__PURE__ */ jsxs(
717
+ "svg",
718
+ {
719
+ viewBox: "0 0 24 24",
720
+ fill: "none",
721
+ stroke: "currentColor",
722
+ strokeWidth: "2",
723
+ strokeLinecap: "round",
724
+ strokeLinejoin: "round",
725
+ children: [
726
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "5" }),
727
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "1", x2: "12", y2: "3" }),
728
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "21", x2: "12", y2: "23" }),
729
+ /* @__PURE__ */ jsx("line", { x1: "4.22", y1: "4.22", x2: "5.64", y2: "5.64" }),
730
+ /* @__PURE__ */ jsx("line", { x1: "18.36", y1: "18.36", x2: "19.78", y2: "19.78" }),
731
+ /* @__PURE__ */ jsx("line", { x1: "1", y1: "12", x2: "3", y2: "12" }),
732
+ /* @__PURE__ */ jsx("line", { x1: "21", y1: "12", x2: "23", y2: "12" }),
733
+ /* @__PURE__ */ jsx("line", { x1: "4.22", y1: "19.78", x2: "5.64", y2: "18.36" }),
734
+ /* @__PURE__ */ jsx("line", { x1: "18.36", y1: "5.64", x2: "19.78", y2: "4.22" })
735
+ ]
736
+ }
737
+ );
738
+ var MoonIcon = () => /* @__PURE__ */ jsx(
739
+ "svg",
740
+ {
741
+ viewBox: "0 0 24 24",
742
+ fill: "none",
743
+ stroke: "currentColor",
744
+ strokeWidth: "2",
745
+ strokeLinecap: "round",
746
+ strokeLinejoin: "round",
747
+ children: /* @__PURE__ */ jsx("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" })
748
+ }
749
+ );
750
+ var MonitorIcon = () => /* @__PURE__ */ jsxs(
751
+ "svg",
752
+ {
753
+ viewBox: "0 0 24 24",
754
+ fill: "none",
755
+ stroke: "currentColor",
756
+ strokeWidth: "2",
757
+ strokeLinecap: "round",
758
+ strokeLinejoin: "round",
759
+ children: [
760
+ /* @__PURE__ */ jsx("rect", { x: "2", y: "3", width: "20", height: "14", rx: "2", ry: "2" }),
761
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "21", x2: "16", y2: "21" }),
762
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "17", x2: "12", y2: "21" })
763
+ ]
764
+ }
765
+ );
766
+ var DocTitleAnchor = ({ id, type, structureTool, children }) => {
767
+ const { basePath } = useWorkspace();
768
+ const { onClick: intentOnClick, href: intentHref } = useIntentLink({
769
+ intent: "edit",
770
+ params: { id, type }
771
+ });
772
+ const href = structureTool ? `${basePath}/${structureTool}/intent/edit/id=${id};type=${type}/` : intentHref;
773
+ const onClick = structureTool ? void 0 : intentOnClick;
774
+ return /* @__PURE__ */ jsx(DocTitleLink, { href, onClick, title: "Open document", children });
775
+ };
776
+ var PaneLinkWrapper = styled.span`
777
+ display: block;
778
+ min-width: 0;
779
+ overflow: hidden;
780
+
781
+ a {
782
+ font-size: 13px;
783
+ font-weight: 600;
784
+ color: var(--seo-link);
785
+ white-space: nowrap;
786
+ overflow: hidden;
787
+ text-overflow: ellipsis;
788
+ text-decoration: none;
789
+ display: block;
790
+ transition: color 0.15s;
791
+
792
+ &:hover {
793
+ color: var(--seo-link-hover);
794
+ text-decoration: underline;
795
+ }
796
+ }
797
+ `;
798
+ var DocTitleAnchorPane = ({
799
+ id,
800
+ type,
801
+ children
802
+ }) => {
803
+ const { ChildLink } = usePaneRouter();
804
+ return /* @__PURE__ */ jsx(PaneLinkWrapper, { children: /* @__PURE__ */ jsx(ChildLink, { childId: id, childParameters: { type }, children }) });
805
+ };
806
+ var DocBadgeRenderer = ({ doc, docBadge }) => {
807
+ const badge = docBadge(doc);
808
+ if (!badge) return null;
809
+ return /* @__PURE__ */ jsx(CustomBadge, { $bgColor: badge.bgColor, $textColor: badge.textColor, $fontSize: badge.fontSize, children: badge.label });
810
+ };
811
+ var Spinner = styled.div`
812
+ width: 28px;
813
+ height: 28px;
814
+ border: 3px solid var(--seo-spinner-track);
815
+ border-top-color: var(--seo-spinner-fill);
816
+ border-radius: 50%;
817
+ animation: ${spin} 0.7s linear infinite;
818
+ margin: 0 auto 12px;
819
+ `;
820
+ var LoadingState = styled.div`
821
+ padding: 48px 24px;
822
+ text-align: center;
823
+ color: var(--seo-text-secondary);
824
+ font-size: 13px;
825
+ `;
826
+ var EmptyState = styled.div`
827
+ padding: 48px 24px;
828
+ text-align: center;
829
+ color: var(--seo-text-muted);
830
+ font-size: 13px;
831
+ `;
832
+ var DeprecationBanner = styled.div`
833
+ background: var(--seo-deprecation-bg);
834
+ border: 1px solid var(--seo-deprecation-border);
835
+ border-radius: 8px;
836
+ padding: 10px 14px;
837
+ font-size: 12px;
838
+ color: var(--seo-deprecation-text);
839
+ margin-bottom: 16px;
840
+ line-height: 1.6;
841
+ `;
842
+ var DeprecationBannerLink = styled.a`
843
+ color: var(--seo-deprecation-link);
844
+ font-weight: 600;
845
+ text-decoration: underline;
846
+ &:hover {
847
+ color: var(--seo-deprecation-text);
848
+ }
849
+ `;
850
+ var ExportButton = styled.button`
851
+ display: inline-flex;
852
+ align-items: center;
853
+ gap: 5px;
854
+ background: var(--seo-btn-bg);
855
+ color: var(--seo-btn-text);
856
+ font-size: 12px;
857
+ font-weight: 500;
858
+ padding: 6px 11px;
859
+ border-radius: 7px;
860
+ border: 1px solid var(--seo-btn-border);
861
+ cursor: pointer;
862
+ flex-shrink: 0;
863
+ transition:
864
+ background 0.15s,
865
+ color 0.15s,
866
+ border-color 0.15s;
867
+
868
+ &:hover {
869
+ background: var(--seo-btn-hover-bg);
870
+ border-color: var(--seo-btn-hover-border);
871
+ }
872
+ `;
873
+ var PaginationBar = styled.div`
874
+ display: flex;
875
+ align-items: center;
876
+ justify-content: space-between;
877
+ flex-wrap: wrap;
878
+ gap: 10px;
879
+ background: var(--seo-card-bg);
880
+ border-top: 1px solid var(--seo-border);
881
+ padding: 10px 20px;
882
+ font-size: 12px;
883
+ color: var(--seo-text-secondary);
884
+ `;
885
+ var PaginationCenter = styled.div`
886
+ display: flex;
887
+ align-items: center;
888
+ gap: 8px;
889
+ `;
890
+ var PaginationButton = styled.button`
891
+ display: inline-flex;
892
+ align-items: center;
893
+ justify-content: center;
894
+ width: 30px;
895
+ height: 30px;
896
+ border-radius: 6px;
897
+ border: 1px solid var(--seo-border);
898
+ background: var(--seo-btn-bg);
899
+ color: var(--seo-btn-text);
900
+ font-size: 13px;
901
+ cursor: pointer;
902
+ transition:
903
+ background 0.15s,
904
+ border-color 0.15s;
905
+
906
+ &:disabled {
907
+ opacity: 0.4;
908
+ cursor: not-allowed;
909
+ }
910
+
911
+ &:not(:disabled):hover {
912
+ background: var(--seo-btn-hover-bg);
913
+ border-color: var(--seo-btn-hover-border);
914
+ }
915
+ `;
916
+ var StatsRow = styled.div`
917
+ display: flex;
918
+ align-items: center;
919
+ flex-wrap: wrap;
920
+ gap: 6px;
921
+ margin-bottom: 20px;
922
+ `;
923
+ var StatPill = styled.span`
924
+ display: inline-flex;
925
+ align-items: center;
926
+ gap: 4px;
927
+ padding: 8px 12px;
928
+ border-radius: 10px;
929
+ font-size: 12px;
930
+ font-weight: 600;
931
+ background: var(--seo-card-bg);
932
+ border: 1px solid var(--seo-border);
933
+ color: var(--seo-text-secondary);
934
+ `;
935
+ var TYPE_COLOR_PALETTE = [
936
+ { bg: "#dbeafe", text: "#0c4a6e" },
937
+ // Blue
938
+ { bg: "#dcfce7", text: "#14532d" },
939
+ // Green
940
+ { bg: "#fce7f3", text: "#500724" },
941
+ // Pink
942
+ { bg: "#fed7aa", text: "#7c2d12" },
943
+ // Orange
944
+ { bg: "#e9d5ff", text: "#581c87" },
945
+ // Purple
946
+ { bg: "#f3e8ff", text: "#3f0f5c" },
947
+ // Deep Purple
948
+ { bg: "#ccfbf1", text: "#134e4a" },
949
+ // Teal
950
+ { bg: "#ddd6fe", text: "#3730a3" },
951
+ // Indigo
952
+ { bg: "#fca5a5", text: "#7f1d1d" },
953
+ // Red
954
+ { bg: "#a7f3d0", text: "#065f46" },
955
+ // Emerald
956
+ { bg: "#fbbf24", text: "#78350f" },
957
+ // Amber
958
+ { bg: "#c4b5fd", text: "#3b0764" },
959
+ // Violet
960
+ { bg: "#f0fdf4", text: "#15803d" },
961
+ // Light Green
962
+ { bg: "#fef2f2", text: "#991b1b" },
963
+ // Light Red
964
+ { bg: "#f5f3ff", text: "#5b21b6" },
965
+ // Light Purple
966
+ { bg: "#fffbeb", text: "#92400e" }
967
+ // Light Amber
968
+ ];
969
+ var getTypeColor = (type) => {
970
+ let hash = 0;
971
+ for (let i = 0; i < type.length; i += 1) {
972
+ const char = type.charCodeAt(i);
973
+ hash = Math.abs(hash * 31 + char);
974
+ }
975
+ const colorIndex = hash % TYPE_COLOR_PALETTE.length;
976
+ return TYPE_COLOR_PALETTE[colorIndex];
977
+ };
978
+ var getStatusCategory = (score) => {
979
+ if (score >= 80) return "excellent";
980
+ if (score >= 60) return "good";
981
+ if (score >= 40) return "fair";
982
+ if (score > 0) return "poor";
983
+ return "missing";
984
+ };
985
+ var RenderLicenseLoading = ({ text }) => /* @__PURE__ */ jsxs(LoadingState, { style: { padding: "80px 24px" }, children: [
986
+ /* @__PURE__ */ jsx(Spinner, {}),
987
+ text != null ? text : "Verifying license\u2026"
988
+ ] });
989
+ var RenderLicenseInvalid = ({ licenseKey, validateLicense }) => /* @__PURE__ */ jsx(UpgradeContainer, { children: /* @__PURE__ */ jsx(UpgradeBox, { children: licenseKey ? /* @__PURE__ */ jsxs(Fragment, { children: [
990
+ /* @__PURE__ */ jsx(UpgradeLock, { children: "\u274C" }),
991
+ /* @__PURE__ */ jsx(UpgradeTitle, { children: "Invalid License Key" }),
992
+ /* @__PURE__ */ jsx(UpgradeText, { children: "The license key you provided is invalid or has been revoked. Please check your key and update it in the plugin config." }),
993
+ /* @__PURE__ */ jsx(UpgradeCode, { children: `seofields({
994
+ healthDashboard: {
995
+ licenseKey: 'YOUR_LICENSE_KEY', // \u2190 replace with a valid key
996
+ },
997
+ })` }),
998
+ /* @__PURE__ */ jsx(
999
+ UpgradeButton,
1000
+ {
1001
+ href: "https://sanity-plugin-seofields.thehardik.in",
1002
+ target: "_blank",
1003
+ rel: "noopener noreferrer",
1004
+ children: "Get a New License Key \u2192"
1005
+ }
1006
+ ),
1007
+ /* @__PURE__ */ jsx("br", {}),
1008
+ /* @__PURE__ */ jsx(ReloadButton, { onClick: () => validateLicense(true), children: "Click here If You Just Updated Your Key" })
1009
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1010
+ /* @__PURE__ */ jsx(UpgradeLock, { children: "\u{1F512}" }),
1011
+ /* @__PURE__ */ jsx(UpgradeTitle, { children: "SEO Health Dashboard" }),
1012
+ /* @__PURE__ */ jsx(UpgradeText, { children: "This feature requires a license key. Add your key to the plugin config to unlock the full dashboard." }),
1013
+ /* @__PURE__ */ jsx(UpgradeCode, { children: `// sanity.config.ts
1014
+ import { seofields } from 'sanity-plugin-seofields'
1015
+
1016
+ export default defineConfig({
1017
+ plugins: [
1018
+ seofields({
1019
+ healthDashboard: {
1020
+ licenseKey: 'SEOF-XXXX-XXXX-XXXX',
1021
+ },
1022
+ }),
1023
+ ],
1024
+ })` }),
1025
+ /* @__PURE__ */ jsx(
1026
+ UpgradeButton,
1027
+ {
1028
+ href: "https://sanity-plugin-seofields.thehardik.in",
1029
+ target: "_blank",
1030
+ rel: "noopener noreferrer",
1031
+ children: "Get a License Key \u2192"
1032
+ }
1033
+ )
1034
+ ] }) }) });
1035
+ var scoreMetaTitle = (title) => {
1036
+ const issues = [];
1037
+ let score = 0;
1038
+ if (title && title.length >= 50 && title.length <= 60) {
1039
+ score = 15;
1040
+ } else if (title && title.length > 0) {
1041
+ score = 10;
1042
+ if (title.length < 50) issues.push("Meta title too short (< 50 chars)");
1043
+ if (title.length > 60) issues.push("Meta title too long (> 60 chars)");
1044
+ } else {
1045
+ issues.push("Missing meta title");
1046
+ }
1047
+ return { score, issues };
1048
+ };
1049
+ var scoreMetaDescription = (description) => {
1050
+ const issues = [];
1051
+ let score = 0;
1052
+ if (description && description.length >= 120 && description.length <= 160) {
1053
+ score = 15;
1054
+ } else if (description && description.length > 0) {
1055
+ score = 10;
1056
+ if (description.length < 120) issues.push("Meta description too short (< 120 chars)");
1057
+ if (description.length > 160) issues.push("Meta description too long (> 160 chars)");
1058
+ } else {
1059
+ issues.push("Missing meta description");
1060
+ }
1061
+ return { score, issues };
1062
+ };
1063
+ var scoreOpenGraph = (openGraph) => {
1064
+ const issues = [];
1065
+ let score = 0;
1066
+ if (openGraph) {
1067
+ if (openGraph.title) score += 6;
1068
+ else issues.push("Missing OG title");
1069
+ if (openGraph.description) score += 6;
1070
+ else issues.push("Missing OG description");
1071
+ if (openGraph.image) score += 6;
1072
+ else issues.push("Missing OG image");
1073
+ if (openGraph.type) score += 7;
1074
+ else issues.push("Missing OG type");
1075
+ } else {
1076
+ issues.push("Open Graph not configured");
1077
+ }
1078
+ return { score, issues };
1079
+ };
1080
+ var scoreTwitterCard = (twitter) => {
1081
+ const issues = [];
1082
+ let score = 0;
1083
+ if (twitter) {
1084
+ if (twitter.title) score += 5;
1085
+ else issues.push("Missing Twitter title");
1086
+ if (twitter.description) score += 5;
1087
+ else issues.push("Missing Twitter description");
1088
+ if (twitter.image) score += 5;
1089
+ else issues.push("Missing Twitter image");
1090
+ } else {
1091
+ issues.push("Twitter Card not configured");
1092
+ }
1093
+ return { score, issues };
1094
+ };
1095
+ var calculateHealthScore = (doc) => {
1096
+ if (!doc.seo) {
1097
+ return { score: 0, status: "missing", issues: ["SEO fields not configured"] };
1098
+ }
1099
+ const { title, description, keywords, robots, canonicalUrl, openGraph, twitter } = doc.seo;
1100
+ let score = 0;
1101
+ const issues = [];
1102
+ const titleResult = scoreMetaTitle(title);
1103
+ score += titleResult.score;
1104
+ issues.push(...titleResult.issues);
1105
+ const descResult = scoreMetaDescription(description);
1106
+ score += descResult.score;
1107
+ issues.push(...descResult.issues);
1108
+ if (doc.seo.metaImage) score += 10;
1109
+ else issues.push("Missing meta image");
1110
+ if (keywords && keywords.length > 0) score += 10;
1111
+ else issues.push("No keywords defined");
1112
+ if (robots && !robots.noIndex) score += 5;
1113
+ else if (!robots) score += 5;
1114
+ if (canonicalUrl) score += 0;
1115
+ const ogResult = scoreOpenGraph(openGraph);
1116
+ score += ogResult.score;
1117
+ issues.push(...ogResult.issues);
1118
+ const twResult = scoreTwitterCard(twitter);
1119
+ score += twResult.score;
1120
+ issues.push(...twResult.issues);
1121
+ const hasMetaImage = !!doc.seo.metaImage;
1122
+ const hasOgImage = !!(openGraph && openGraph.image);
1123
+ const hasTwitterImage = !!(twitter && twitter.image);
1124
+ if (hasMetaImage && hasOgImage && hasTwitterImage) {
1125
+ score += 5;
1126
+ } else {
1127
+ const missingImages = [];
1128
+ if (!hasMetaImage) missingImages.push("meta image");
1129
+ if (!hasOgImage) missingImages.push("OG image");
1130
+ if (!hasTwitterImage) missingImages.push("Twitter image");
1131
+ issues.push(`Missing images for full score: ${missingImages.join(", ")}`);
1132
+ }
1133
+ const status = getStatusCategory(score);
1134
+ return { score, status, issues };
1135
+ };
1136
+ var resolveTypeLabel = (type, typeLabels) => {
1137
+ var _a;
1138
+ return (_a = typeLabels == null ? void 0 : typeLabels[type]) != null ? _a : type;
1139
+ };
1140
+ var buildTitleProjection = (titleField) => {
1141
+ if (!titleField || titleField === "title") return "title";
1142
+ if (typeof titleField === "string") return `"title": ${titleField}`;
1143
+ const cases = Object.entries(titleField).map(([type, field]) => `_type == "${type}" => ${field}`).join(", ");
1144
+ return `"title": select(${cases}, title)`;
1145
+ };
1146
+ var generateDummyData = () => {
1147
+ const dummyDocs = [
1148
+ {
1149
+ _id: "preview-post-1",
1150
+ _type: "post",
1151
+ title: "Getting Started with SEO Best Practices",
1152
+ slug: { current: "getting-started-seo" },
1153
+ _updatedAt: new Date(Date.now() - 2 * 24 * 60 * 60 * 1e3).toISOString(),
1154
+ seo: {
1155
+ title: "Getting Started with SEO Best Practices | My Blog",
1156
+ description: "Learn the fundamentals of SEO optimization to improve your website visibility and search rankings.",
1157
+ keywords: ["seo", "best practices", "optimization"],
1158
+ metaImage: { _type: "image", asset: { _ref: "image-123", _type: "reference" } },
1159
+ openGraph: {
1160
+ title: "SEO Best Practices Guide",
1161
+ description: "Master SEO optimization",
1162
+ image: { _type: "image", asset: { _ref: "image-123", _type: "reference" }, alt: "SEO Guide" },
1163
+ type: "article"
1164
+ },
1165
+ twitter: {
1166
+ title: "SEO Best Practices",
1167
+ description: "Learn SEO optimization",
1168
+ image: { _type: "image", asset: { _ref: "image-123", _type: "reference" }, alt: "Guide" },
1169
+ card: "summary_large_image"
1170
+ }
1171
+ }
1172
+ },
1173
+ {
1174
+ _id: "preview-post-2",
1175
+ _type: "post",
1176
+ title: "Advanced Analytics Strategy",
1177
+ slug: { current: "advanced-analytics" },
1178
+ _updatedAt: new Date(Date.now() - 5 * 24 * 60 * 60 * 1e3).toISOString(),
1179
+ seo: {
1180
+ title: "Advanced Analytics",
1181
+ description: "Strategy tips",
1182
+ keywords: ["analytics", "data"],
1183
+ openGraph: {
1184
+ title: "Analytics Guide"
1185
+ }
1186
+ }
1187
+ },
1188
+ {
1189
+ _id: "preview-page-1",
1190
+ _type: "page",
1191
+ title: "About Us",
1192
+ slug: { current: "about" },
1193
+ _updatedAt: new Date(Date.now() - 10 * 24 * 60 * 60 * 1e3).toISOString(),
1194
+ seo: {
1195
+ title: "About",
1196
+ keywords: ["company", "team"],
1197
+ metaImage: { _type: "image", asset: { _ref: "image-456", _type: "reference" } }
1198
+ }
1199
+ },
1200
+ {
1201
+ _id: "preview-post-3",
1202
+ _type: "post",
1203
+ title: "Content Marketing Trends for 2024",
1204
+ slug: { current: "content-marketing-trends" },
1205
+ _updatedAt: new Date(Date.now() - 1 * 24 * 60 * 60 * 1e3).toISOString(),
1206
+ seo: {
1207
+ title: "Content Marketing Trends 2024",
1208
+ description: "Discover the latest content marketing trends and strategies to engage your audience effectively.",
1209
+ keywords: ["content marketing", "trends", "strategy", "engagement"],
1210
+ metaImage: { _type: "image", asset: { _ref: "image-789", _type: "reference" } },
1211
+ openGraph: {
1212
+ title: "Content Marketing Trends 2024",
1213
+ description: "Latest trends in content marketing",
1214
+ image: { _type: "image", asset: { _ref: "image-789", _type: "reference" }, alt: "Trends" },
1215
+ type: "article"
1216
+ },
1217
+ twitter: {
1218
+ title: "Content Marketing Trends",
1219
+ description: "Discover the latest trends",
1220
+ card: "summary"
1221
+ }
1222
+ }
1223
+ },
1224
+ {
1225
+ _id: "preview-post-4",
1226
+ _type: "product",
1227
+ title: "Pro Plan",
1228
+ slug: { current: "pro-plan" },
1229
+ _updatedAt: new Date(Date.now() - 15 * 24 * 60 * 60 * 1e3).toISOString(),
1230
+ seo: {
1231
+ title: "Pro",
1232
+ keywords: ["pricing"]
1233
+ }
1234
+ },
1235
+ {
1236
+ _id: "preview-page-2",
1237
+ _type: "page",
1238
+ title: "Contact",
1239
+ slug: { current: "contact" },
1240
+ _updatedAt: new Date(Date.now() - 8 * 24 * 60 * 60 * 1e3).toISOString(),
1241
+ seo: {
1242
+ openGraph: {
1243
+ title: "Get in Touch"
1244
+ }
1245
+ }
1246
+ },
1247
+ {
1248
+ _id: "preview-post-5",
1249
+ _type: "post",
1250
+ title: "Mobile Optimization Guide",
1251
+ slug: { current: "mobile-optimization" },
1252
+ _updatedAt: new Date(Date.now() - 3 * 24 * 60 * 60 * 1e3).toISOString(),
1253
+ seo: {
1254
+ title: "Mobile Optimization Guide: Best Practices for Responsive Design",
1255
+ description: "Complete guide to mobile optimization including responsive design, performance tips, and user experience best practices for modern web development.",
1256
+ keywords: ["mobile", "optimization", "responsive", "performance"],
1257
+ metaImage: { _type: "image", asset: { _ref: "image-mobile", _type: "reference" } },
1258
+ openGraph: {
1259
+ title: "Mobile Optimization Best Practices",
1260
+ description: "Master mobile web optimization",
1261
+ image: { _type: "image", asset: { _ref: "image-mobile", _type: "reference" }, alt: "Mobile" },
1262
+ type: "article"
1263
+ },
1264
+ twitter: {
1265
+ title: "Mobile Optimization Tips",
1266
+ description: "Responsive design best practices",
1267
+ image: { _type: "image", asset: { _ref: "image-mobile", _type: "reference" }, alt: "Mobile" },
1268
+ card: "summary_large_image"
1269
+ }
1270
+ }
1271
+ }
1272
+ ];
1273
+ return dummyDocs.map((doc) => __spreadProps(__spreadValues({}, doc), {
1274
+ health: calculateHealthScore(doc)
1275
+ }));
1276
+ };
1277
+ var VALIDATION_ENDPOINT = "https://sanity-plugin-seofields.thehardik.in/api/validate-license";
1278
+ var CACHE_TTL_MS = 60 * 60 * 1e3;
1279
+ var SeoHealthDashboard = ({
1280
+ icon = "\u{1F4CA}",
1281
+ title = "SEO Health Dashboard",
1282
+ description = "Monitor and optimize SEO fields across all your documents",
1283
+ showTypeColumn = true,
1284
+ showDocumentId = true,
1285
+ queryTypes,
1286
+ queryRequireSeo = true,
1287
+ customQuery,
1288
+ apiVersion = "2023-01-01",
1289
+ licenseKey,
1290
+ typeDisplayLabels,
1291
+ typeColumnMode = "badge",
1292
+ titleField,
1293
+ getDocumentBadge,
1294
+ loadingLicense,
1295
+ loadingDocuments,
1296
+ noDocuments,
1297
+ previewMode = false,
1298
+ openInPane = false,
1299
+ structureTool,
1300
+ _deprecationWarnings,
1301
+ exportEnabled = true,
1302
+ exportFormats = ["csv", "json"],
1303
+ compactStats = false
1304
+ }) => {
1305
+ const resolvedTypeLabels = typeDisplayLabels;
1306
+ const resolvedDocBadge = getDocumentBadge;
1307
+ const allDeprecationWarnings = useMemo(() => _deprecationWarnings != null ? _deprecationWarnings : [], [_deprecationWarnings]);
1308
+ const deprecationGroups = useMemo(() => {
1309
+ const groups = /* @__PURE__ */ new Map();
1310
+ for (const w of allDeprecationWarnings) {
1311
+ if (!groups.has(w.version)) {
1312
+ groups.set(w.version, { version: w.version, changelogUrl: w.changelogUrl, keys: [] });
1313
+ }
1314
+ groups.get(w.version).keys.push(w.key);
1315
+ }
1316
+ return Array.from(groups.values());
1317
+ }, [allDeprecationWarnings]);
1318
+ const client = useClient({ apiVersion });
1319
+ const [licenseStatus, setLicenseStatus] = useState("loading");
1320
+ const [documents, setDocuments] = useState([]);
1321
+ const [loading, setLoading] = useState(true);
1322
+ const [isRefreshing, setIsRefreshing] = useState(false);
1323
+ const [searchQuery, setSearchQuery] = useState("");
1324
+ const [filterStatus, setFilterStatus] = useState(() => {
1325
+ var _a;
1326
+ try {
1327
+ return (_a = localStorage.getItem("seo-dashboard-filter-status")) != null ? _a : "all";
1328
+ } catch (e) {
1329
+ return "all";
1330
+ }
1331
+ });
1332
+ const [filterType, setFilterType] = useState(() => {
1333
+ var _a;
1334
+ try {
1335
+ return (_a = localStorage.getItem("seo-dashboard-filter-type")) != null ? _a : "all";
1336
+ } catch (e) {
1337
+ return "all";
1338
+ }
1339
+ });
1340
+ const [sortBy, setSortBy] = useState("score");
1341
+ const [currentPage, setCurrentPage] = useState(1);
1342
+ const [pageSize, setPageSize] = useState(() => {
1343
+ try {
1344
+ const stored = localStorage.getItem("seo-dashboard-page-size");
1345
+ return stored ? Number(stored) : 25;
1346
+ } catch (e) {
1347
+ return 25;
1348
+ }
1349
+ });
1350
+ const searchInputRef = useRef(null);
1351
+ const [activePopover, setActivePopover] = useState(null);
1352
+ const [themeMode, setThemeMode] = useState(() => {
1353
+ try {
1354
+ const saved = localStorage.getItem("seo-dashboard-theme");
1355
+ if (saved === "light" || saved === "dark" || saved === "system") return saved;
1356
+ } catch (e) {
1357
+ }
1358
+ return "system";
1359
+ });
1360
+ const [systemPrefersDark, setSystemPrefersDark] = useState(
1361
+ () => {
1362
+ var _a;
1363
+ return typeof window !== "undefined" && ((_a = window.matchMedia) == null ? void 0 : _a.call(window, "(prefers-color-scheme: dark)").matches);
1364
+ }
1365
+ );
1366
+ useEffect(() => {
1367
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
1368
+ const handler = (e) => setSystemPrefersDark(e.matches);
1369
+ mq.addEventListener("change", handler);
1370
+ return () => mq.removeEventListener("change", handler);
1371
+ }, []);
1372
+ const resolvedTheme = (() => {
1373
+ if (themeMode === "system") {
1374
+ return systemPrefersDark ? "dark" : "light";
1375
+ }
1376
+ return themeMode;
1377
+ })();
1378
+ const currentVars = themeVars[resolvedTheme];
1379
+ const handleThemeChange = useCallback((mode) => {
1380
+ setThemeMode(mode);
1381
+ try {
1382
+ localStorage.setItem("seo-dashboard-theme", mode);
1383
+ } catch (e) {
1384
+ }
1385
+ }, []);
1386
+ const handleThemeChangeLight = useCallback(() => handleThemeChange("light"), [handleThemeChange]);
1387
+ const handleThemeChangeDark = useCallback(() => handleThemeChange("dark"), [handleThemeChange]);
1388
+ const handleThemeChangeSystem = useCallback(
1389
+ () => handleThemeChange("system"),
1390
+ [handleThemeChange]
1391
+ );
1392
+ const handleFilterStatusChange = useCallback((value) => {
1393
+ setFilterStatus(value);
1394
+ try {
1395
+ localStorage.setItem("seo-dashboard-filter-status", value);
1396
+ } catch (e) {
1397
+ }
1398
+ setCurrentPage(1);
1399
+ }, []);
1400
+ const handleFilterTypeChange = useCallback((value) => {
1401
+ setFilterType(value);
1402
+ try {
1403
+ localStorage.setItem("seo-dashboard-filter-type", value);
1404
+ } catch (e) {
1405
+ }
1406
+ setCurrentPage(1);
1407
+ }, []);
1408
+ const validateLicense = useCallback(
1409
+ async (forceRefresh = false) => {
1410
+ var _a;
1411
+ if (previewMode) {
1412
+ setLicenseStatus("valid");
1413
+ return;
1414
+ }
1415
+ if (!licenseKey) {
1416
+ setLicenseStatus("invalid");
1417
+ return;
1418
+ }
1419
+ const projectId = (_a = client.config().projectId) != null ? _a : "";
1420
+ const cacheKey = `seofields_license_${projectId}`;
1421
+ if (forceRefresh) {
1422
+ try {
1423
+ sessionStorage.removeItem(cacheKey);
1424
+ } catch (e) {
1425
+ }
1426
+ }
1427
+ if (!forceRefresh) {
1428
+ try {
1429
+ const cached = sessionStorage.getItem(cacheKey);
1430
+ if (cached) {
1431
+ const { valid, ts } = JSON.parse(cached);
1432
+ if (Date.now() - ts < CACHE_TTL_MS) {
1433
+ setLicenseStatus(valid ? "valid" : "invalid");
1434
+ return;
1435
+ }
1436
+ }
1437
+ } catch (e) {
1438
+ }
1439
+ }
1440
+ setLicenseStatus("loading");
1441
+ try {
1442
+ const res = await fetch(VALIDATION_ENDPOINT, {
1443
+ method: "POST",
1444
+ headers: { "Content-Type": "application/json" },
1445
+ body: JSON.stringify({ licenseKey, projectId })
1446
+ });
1447
+ const valid = res.ok;
1448
+ setLicenseStatus(valid ? "valid" : "invalid");
1449
+ try {
1450
+ sessionStorage.setItem(cacheKey, JSON.stringify({ valid, ts: Date.now() }));
1451
+ } catch (e) {
1452
+ }
1453
+ } catch (e) {
1454
+ setLicenseStatus("valid");
1455
+ }
1456
+ },
1457
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1458
+ [licenseKey, previewMode]
1459
+ );
1460
+ useEffect(() => {
1461
+ validateLicense();
1462
+ }, [licenseKey, previewMode]);
1463
+ const handleMouseEnterIssues = (el, issues) => {
1464
+ if (!el) return;
1465
+ const rect = el.getBoundingClientRect();
1466
+ const popoverWidth = 280;
1467
+ const viewportWidth = window.innerWidth;
1468
+ let left = rect.left;
1469
+ if (left + popoverWidth > viewportWidth - 10) left = viewportWidth - popoverWidth - 10;
1470
+ if (left < 10) left = 10;
1471
+ setActivePopover({ top: rect.top, left, issues });
1472
+ };
1473
+ const JSONStringifiedQueryTypes = JSON.stringify(queryTypes);
1474
+ const JSONStringifiedTitleField = JSON.stringify(titleField);
1475
+ const fetchDocuments = useCallback(
1476
+ async (isManualRefresh = false) => {
1477
+ try {
1478
+ if (isManualRefresh) {
1479
+ setIsRefreshing(true);
1480
+ } else {
1481
+ setLoading(true);
1482
+ }
1483
+ if (previewMode) {
1484
+ setDocuments(generateDummyData());
1485
+ return;
1486
+ }
1487
+ let groqQuery;
1488
+ let params = {};
1489
+ if (customQuery) {
1490
+ groqQuery = customQuery;
1491
+ } else if (queryTypes && queryTypes.length > 0) {
1492
+ const seoFilter = queryRequireSeo ? " && seo != null" : "";
1493
+ const titleProj = buildTitleProjection(titleField);
1494
+ groqQuery = `*[_type in $types${seoFilter} && !(_id in path("drafts.**"))]{
1495
+ _id,
1496
+ _type,
1497
+ ${titleProj},
1498
+ slug,
1499
+ seo,
1500
+ _updatedAt
1501
+ }`;
1502
+ params = { types: queryTypes };
1503
+ } else {
1504
+ const titleProj = buildTitleProjection(titleField);
1505
+ groqQuery = `*[seo != null && !(_id in path("drafts.**"))]{
1506
+ _id,
1507
+ _type,
1508
+ ${titleProj},
1509
+ slug,
1510
+ seo,
1511
+ _updatedAt
1512
+ }`;
1513
+ }
1514
+ const result = await client.fetch(groqQuery, params, { perspective: "published" });
1515
+ const docsWithHealth = result.map((doc) => __spreadProps(__spreadValues({}, doc), {
1516
+ health: calculateHealthScore(doc)
1517
+ }));
1518
+ setDocuments(docsWithHealth);
1519
+ } catch (error) {
1520
+ console.error("Error fetching documents:", error);
1521
+ } finally {
1522
+ setLoading(false);
1523
+ setIsRefreshing(false);
1524
+ }
1525
+ },
1526
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1527
+ [
1528
+ client,
1529
+ customQuery,
1530
+ queryRequireSeo,
1531
+ JSONStringifiedQueryTypes,
1532
+ JSONStringifiedTitleField,
1533
+ previewMode
1534
+ ]
1535
+ );
1536
+ useEffect(() => {
1537
+ fetchDocuments();
1538
+ }, [
1539
+ client,
1540
+ customQuery,
1541
+ queryRequireSeo,
1542
+ JSONStringifiedQueryTypes,
1543
+ JSONStringifiedTitleField,
1544
+ previewMode
1545
+ ]);
1546
+ const handleRefresh = useCallback(() => {
1547
+ fetchDocuments(true);
1548
+ }, [fetchDocuments]);
1549
+ const uniqueDocumentTypes = useMemo(() => {
1550
+ const types = new Set(documents.map((doc) => doc._type));
1551
+ return Array.from(types).sort();
1552
+ }, [documents]);
1553
+ const filteredAndSortedDocs = useMemo(() => {
1554
+ let filtered = documents;
1555
+ if (searchQuery) {
1556
+ filtered = filtered.filter(
1557
+ (doc) => {
1558
+ var _a, _b;
1559
+ return ((_a = doc.title) == null ? void 0 : _a.toLowerCase().includes(searchQuery.toLowerCase())) || ((_b = doc._id) == null ? void 0 : _b.toLowerCase().includes(searchQuery.toLowerCase()));
1560
+ }
1561
+ );
1562
+ }
1563
+ if (filterStatus !== "all") {
1564
+ filtered = filtered.filter((doc) => doc.health.status === filterStatus);
1565
+ }
1566
+ if (filterType !== "all") {
1567
+ filtered = filtered.filter((doc) => doc._type === filterType);
1568
+ }
1569
+ const sorted = [...filtered].sort((a, b) => {
1570
+ if (sortBy === "score") {
1571
+ return b.health.score - a.health.score;
1572
+ }
1573
+ return (a.title || "").localeCompare(b.title || "");
1574
+ });
1575
+ return sorted;
1576
+ }, [documents, searchQuery, filterStatus, filterType, sortBy]);
1577
+ const totalPages = Math.max(1, Math.ceil(filteredAndSortedDocs.length / pageSize));
1578
+ const paginatedDocs = useMemo(() => {
1579
+ const start = (currentPage - 1) * pageSize;
1580
+ return filteredAndSortedDocs.slice(start, start + pageSize);
1581
+ }, [filteredAndSortedDocs, currentPage, pageSize]);
1582
+ const handleExportCSV = useCallback(() => {
1583
+ const exportDocs = filteredAndSortedDocs;
1584
+ const rows = exportDocs.map((doc) => ({
1585
+ id: doc._id,
1586
+ type: doc._type,
1587
+ title: typeof doc.title === "string" ? doc.title : "",
1588
+ score: doc.health.score,
1589
+ status: doc.health.status,
1590
+ issues: doc.health.issues.join(" | ")
1591
+ }));
1592
+ const header = "id,type,title,score,status,issues";
1593
+ const csvRows = rows.map(
1594
+ (r) => [
1595
+ r.id,
1596
+ r.type,
1597
+ `"${r.title.replace(/"/g, '""')}"`,
1598
+ r.score,
1599
+ r.status,
1600
+ `"${r.issues.replace(/"/g, '""')}"`
1601
+ ].join(",")
1602
+ );
1603
+ const csv = [header, ...csvRows].join("\n");
1604
+ const blob = new Blob([csv], { type: "text/csv" });
1605
+ const url = URL.createObjectURL(blob);
1606
+ const a = document.createElement("a");
1607
+ a.href = url;
1608
+ a.download = "seo-health-export.csv";
1609
+ a.click();
1610
+ URL.revokeObjectURL(url);
1611
+ }, [filteredAndSortedDocs]);
1612
+ const handleExportJSON = useCallback(() => {
1613
+ const exportDocs = filteredAndSortedDocs;
1614
+ const blob = new Blob([JSON.stringify(exportDocs, null, 2)], {
1615
+ type: "application/json"
1616
+ });
1617
+ const url = URL.createObjectURL(blob);
1618
+ const a = document.createElement("a");
1619
+ a.href = url;
1620
+ a.download = "seo-health-export.json";
1621
+ a.click();
1622
+ URL.revokeObjectURL(url);
1623
+ }, [filteredAndSortedDocs]);
1624
+ const stats = useMemo(() => {
1625
+ const total = documents.length;
1626
+ const excellent = documents.filter((d) => d.health.score >= 80).length;
1627
+ const good = documents.filter((d) => d.health.score >= 60 && d.health.score < 80).length;
1628
+ const fair = documents.filter((d) => d.health.score >= 40 && d.health.score < 60).length;
1629
+ const poor = documents.filter((d) => d.health.score > 0 && d.health.score < 40).length;
1630
+ const missing = documents.filter((d) => d.health.score === 0).length;
1631
+ const avgScore = total > 0 ? Math.round(documents.reduce((sum, d) => sum + d.health.score, 0) / total) : 0;
1632
+ return { total, excellent, good, fair, poor, missing, avgScore };
1633
+ }, [documents]);
1634
+ const handleMouseLeave = useCallback(() => {
1635
+ setActivePopover(null);
1636
+ }, []);
1637
+ useEffect(() => {
1638
+ setCurrentPage(1);
1639
+ }, [searchQuery, filterStatus, filterType, sortBy]);
1640
+ useEffect(() => {
1641
+ const onKeyDown = (e) => {
1642
+ var _a;
1643
+ const target = e.target;
1644
+ const isEditable = target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable;
1645
+ if (e.key === "/" && !isEditable) {
1646
+ e.preventDefault();
1647
+ (_a = searchInputRef.current) == null ? void 0 : _a.focus();
1648
+ }
1649
+ };
1650
+ window.addEventListener("keydown", onKeyDown);
1651
+ return () => window.removeEventListener("keydown", onKeyDown);
1652
+ }, []);
1653
+ const renderDashboardContent = () => /* @__PURE__ */ jsxs(Fragment, { children: [
1654
+ /* @__PURE__ */ jsxs(PageHeader, { children: [
1655
+ /* @__PURE__ */ jsxs("div", { children: [
1656
+ /* @__PURE__ */ jsxs(PageTitle, { children: [
1657
+ /* @__PURE__ */ jsxs("span", { children: [
1658
+ icon,
1659
+ " ",
1660
+ title
1661
+ ] }),
1662
+ previewMode && /* @__PURE__ */ jsx(PreviewBadge, { children: "Preview Mode" })
1663
+ ] }),
1664
+ /* @__PURE__ */ jsx(PageSubtitle, { children: description })
1665
+ ] }),
1666
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "10px", flexShrink: 0 }, children: [
1667
+ /* @__PURE__ */ jsxs(ThemeSwitcher, { children: [
1668
+ /* @__PURE__ */ jsx(
1669
+ ThemeButton,
1670
+ {
1671
+ $theme: "light",
1672
+ $active: themeMode === "light",
1673
+ onClick: handleThemeChangeLight,
1674
+ title: "Light theme",
1675
+ children: /* @__PURE__ */ jsx(SunIcon, {})
1676
+ }
1677
+ ),
1678
+ /* @__PURE__ */ jsx(
1679
+ ThemeButton,
1680
+ {
1681
+ $theme: "dark",
1682
+ $active: themeMode === "dark",
1683
+ onClick: handleThemeChangeDark,
1684
+ title: "Dark theme",
1685
+ children: /* @__PURE__ */ jsx(MoonIcon, {})
1686
+ }
1687
+ ),
1688
+ /* @__PURE__ */ jsx(
1689
+ ThemeButton,
1690
+ {
1691
+ $theme: "system",
1692
+ $active: themeMode === "system",
1693
+ onClick: handleThemeChangeSystem,
1694
+ title: "System default",
1695
+ children: /* @__PURE__ */ jsx(MonitorIcon, {})
1696
+ }
1697
+ )
1698
+ ] }),
1699
+ /* @__PURE__ */ jsxs(
1700
+ DashboardRefreshButton,
1701
+ {
1702
+ onClick: handleRefresh,
1703
+ disabled: loading || isRefreshing,
1704
+ $spinning: isRefreshing,
1705
+ title: "Refresh documents",
1706
+ children: [
1707
+ /* @__PURE__ */ jsxs(
1708
+ "svg",
1709
+ {
1710
+ width: "14",
1711
+ height: "14",
1712
+ viewBox: "0 0 24 24",
1713
+ fill: "none",
1714
+ stroke: "currentColor",
1715
+ strokeWidth: "2.2",
1716
+ strokeLinecap: "round",
1717
+ strokeLinejoin: "round",
1718
+ children: [
1719
+ /* @__PURE__ */ jsx("polyline", { points: "23 4 23 10 17 10" }),
1720
+ /* @__PURE__ */ jsx("polyline", { points: "1 20 1 14 7 14" }),
1721
+ /* @__PURE__ */ jsx("path", { d: "M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" })
1722
+ ]
1723
+ }
1724
+ ),
1725
+ "Refresh"
1726
+ ]
1727
+ }
1728
+ )
1729
+ ] })
1730
+ ] }),
1731
+ deprecationGroups.length > 0 && /* @__PURE__ */ jsxs(DeprecationBanner, { children: [
1732
+ /* @__PURE__ */ jsx("strong", { children: "\u26A0\uFE0F Deprecated config keys detected:" }),
1733
+ " ",
1734
+ deprecationGroups.map((group, gi) => /* @__PURE__ */ jsxs("span", { children: [
1735
+ group.keys.map((w, i) => /* @__PURE__ */ jsxs("span", { children: [
1736
+ /* @__PURE__ */ jsx("code", { style: { background: "#fef9c3", padding: "1px 4px", borderRadius: 3 }, children: w.split("\u2192")[0].trim() }),
1737
+ " \u2192 ",
1738
+ /* @__PURE__ */ jsx("code", { style: { background: "#dcfce7", padding: "1px 4px", borderRadius: 3 }, children: w.split("\u2192")[1].trim() }),
1739
+ i < group.keys.length - 1 ? " \xB7 " : ""
1740
+ ] }, w)),
1741
+ " ",
1742
+ "(",
1743
+ /* @__PURE__ */ jsxs(
1744
+ DeprecationBannerLink,
1745
+ {
1746
+ href: group.changelogUrl,
1747
+ target: "_blank",
1748
+ rel: "noopener noreferrer",
1749
+ children: [
1750
+ group.version,
1751
+ " changelog"
1752
+ ]
1753
+ }
1754
+ ),
1755
+ ")",
1756
+ gi < deprecationGroups.length - 1 ? " \xB7 " : ""
1757
+ ] }, group.version)),
1758
+ " ",
1759
+ "\u2014 Please update your config."
1760
+ ] }),
1761
+ !loading && compactStats && /* @__PURE__ */ jsxs(StatsRow, { children: [
1762
+ /* @__PURE__ */ jsxs(StatPill, { children: [
1763
+ "\u{1F4CB} ",
1764
+ stats.total
1765
+ ] }),
1766
+ /* @__PURE__ */ jsxs(StatPill, { children: [
1767
+ "\u{1F7E2} Excellent: ",
1768
+ stats.excellent
1769
+ ] }),
1770
+ /* @__PURE__ */ jsxs(StatPill, { children: [
1771
+ "\u{1F7E1} Good: ",
1772
+ stats.good
1773
+ ] }),
1774
+ /* @__PURE__ */ jsxs(StatPill, { children: [
1775
+ "\u{1F7E0} Fair: ",
1776
+ stats.fair
1777
+ ] }),
1778
+ /* @__PURE__ */ jsxs(StatPill, { children: [
1779
+ "\u{1F534} Poor/Missing: ",
1780
+ stats.poor + stats.missing
1781
+ ] }),
1782
+ /* @__PURE__ */ jsxs(StatPill, { children: [
1783
+ "\u{1F4CA} Avg: ",
1784
+ stats.avgScore,
1785
+ "%"
1786
+ ] }),
1787
+ /* @__PURE__ */ jsxs(StatPill, { children: [
1788
+ "\u{1F5C2}\uFE0F Types: ",
1789
+ uniqueDocumentTypes.length
1790
+ ] })
1791
+ ] }),
1792
+ !loading && !compactStats && /* @__PURE__ */ jsxs(StatsGrid, { children: [
1793
+ /* @__PURE__ */ jsxs(StatCard, { children: [
1794
+ /* @__PURE__ */ jsx(StatLabel, { children: "Total Docs" }),
1795
+ /* @__PURE__ */ jsx(StatValue, { children: stats.total })
1796
+ ] }),
1797
+ /* @__PURE__ */ jsxs(StatCard, { children: [
1798
+ /* @__PURE__ */ jsx(StatLabel, { children: "Avg Score" }),
1799
+ /* @__PURE__ */ jsxs(StatValue, { children: [
1800
+ stats.avgScore,
1801
+ "%"
1802
+ ] })
1803
+ ] }),
1804
+ /* @__PURE__ */ jsxs(StatCard, { $accent: "#10b981", children: [
1805
+ /* @__PURE__ */ jsx(StatLabel, { children: "Excellent (80+)" }),
1806
+ /* @__PURE__ */ jsx(StatValue, { children: stats.excellent })
1807
+ ] }),
1808
+ /* @__PURE__ */ jsxs(StatCard, { $accent: "#f59e0b", children: [
1809
+ /* @__PURE__ */ jsx(StatLabel, { children: "Good (60\u201379)" }),
1810
+ /* @__PURE__ */ jsx(StatValue, { children: stats.good })
1811
+ ] }),
1812
+ /* @__PURE__ */ jsxs(StatCard, { $accent: "#f97316", children: [
1813
+ /* @__PURE__ */ jsx(StatLabel, { children: "Fair (40\u201359)" }),
1814
+ /* @__PURE__ */ jsx(StatValue, { children: stats.fair })
1815
+ ] }),
1816
+ /* @__PURE__ */ jsxs(StatCard, { $accent: "#ef4444", children: [
1817
+ /* @__PURE__ */ jsx(StatLabel, { children: "Poor / Missing" }),
1818
+ /* @__PURE__ */ jsx(StatValue, { children: stats.poor + stats.missing })
1819
+ ] })
1820
+ ] }),
1821
+ /* @__PURE__ */ jsxs(ControlsBar, { children: [
1822
+ /* @__PURE__ */ jsxs(SearchWrapper, { children: [
1823
+ /* @__PURE__ */ jsx(SearchIconSvg, { children: /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx(
1824
+ "path",
1825
+ {
1826
+ fillRule: "evenodd",
1827
+ 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",
1828
+ clipRule: "evenodd"
1829
+ }
1830
+ ) }) }),
1831
+ /* @__PURE__ */ jsx(
1832
+ SearchInput,
1833
+ {
1834
+ ref: searchInputRef,
1835
+ placeholder: "Search documents... (press / to focus)",
1836
+ value: searchQuery,
1837
+ onChange: (e) => setSearchQuery(e.currentTarget.value)
1838
+ }
1839
+ )
1840
+ ] }),
1841
+ /* @__PURE__ */ jsxs(
1842
+ StyledSelect,
1843
+ {
1844
+ value: filterStatus,
1845
+ onChange: (e) => handleFilterStatusChange(e.currentTarget.value),
1846
+ children: [
1847
+ /* @__PURE__ */ jsx("option", { value: "all", children: "All Status" }),
1848
+ /* @__PURE__ */ jsx("option", { value: "excellent", children: "Excellent" }),
1849
+ /* @__PURE__ */ jsx("option", { value: "good", children: "Good" }),
1850
+ /* @__PURE__ */ jsx("option", { value: "fair", children: "Fair" }),
1851
+ /* @__PURE__ */ jsx("option", { value: "poor", children: "Poor" }),
1852
+ /* @__PURE__ */ jsx("option", { value: "missing", children: "Missing" })
1853
+ ]
1854
+ }
1855
+ ),
1856
+ uniqueDocumentTypes.length > 1 && /* @__PURE__ */ jsxs(
1857
+ StyledSelect,
1858
+ {
1859
+ value: filterType,
1860
+ onChange: (e) => handleFilterTypeChange(e.currentTarget.value),
1861
+ children: [
1862
+ /* @__PURE__ */ jsx("option", { value: "all", children: "All Types" }),
1863
+ uniqueDocumentTypes.map((type) => /* @__PURE__ */ jsx("option", { value: type, children: resolveTypeLabel(type, resolvedTypeLabels) }, type))
1864
+ ]
1865
+ }
1866
+ ),
1867
+ /* @__PURE__ */ jsxs(
1868
+ StyledSelect,
1869
+ {
1870
+ value: sortBy,
1871
+ onChange: (e) => setSortBy(e.currentTarget.value),
1872
+ children: [
1873
+ /* @__PURE__ */ jsx("option", { value: "score", children: "Sort by Score" }),
1874
+ /* @__PURE__ */ jsx("option", { value: "title", children: "Sort by Title" })
1875
+ ]
1876
+ }
1877
+ ),
1878
+ exportEnabled && exportFormats.includes("csv") && /* @__PURE__ */ jsx(ExportButton, { onClick: handleExportCSV, title: "Export all filtered as CSV", children: "\u2B07 CSV" }),
1879
+ exportEnabled && exportFormats.includes("json") && /* @__PURE__ */ jsx(ExportButton, { onClick: handleExportJSON, title: "Export all filtered as JSON", children: "\u2B07 JSON" })
1880
+ ] }),
1881
+ /* @__PURE__ */ jsxs(TableCard, { children: [
1882
+ loading && /* @__PURE__ */ jsxs(LoadingState, { children: [
1883
+ /* @__PURE__ */ jsx(Spinner, {}),
1884
+ loadingDocuments != null ? loadingDocuments : "Loading documents\u2026"
1885
+ ] }),
1886
+ !loading && (filteredAndSortedDocs.length === 0 ? /* @__PURE__ */ jsx(EmptyState, { children: noDocuments != null ? noDocuments : "No documents found" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1887
+ /* @__PURE__ */ jsxs(TableHeader, { children: [
1888
+ /* @__PURE__ */ jsx(ColTitle, { children: "Title" }),
1889
+ showTypeColumn && /* @__PURE__ */ jsx(ColType, { children: "Type" }),
1890
+ /* @__PURE__ */ jsx(ColScore, { children: "Score" }),
1891
+ /* @__PURE__ */ jsx(ColIssues, { children: "Top Issues" })
1892
+ ] }),
1893
+ paginatedDocs.map((doc) => {
1894
+ return /* @__PURE__ */ jsxs(TableRow, { children: [
1895
+ /* @__PURE__ */ jsx(ColTitle, { children: /* @__PURE__ */ jsx(TitleWrapper, { children: /* @__PURE__ */ jsxs(TitleCell, { children: [
1896
+ doc.title !== null && typeof doc.title !== "string" ? /* @__PURE__ */ jsx(NonStringTitleWarning, { title: "title is not a string \u2014 use pt::text(title) in your query.groq projection to convert Portable Text to a plain string", children: "\u26A0 title is not a string \u2014 use pt::text(title) in query.groq" }) : /* @__PURE__ */ jsx(Fragment, { children: openInPane ? /* @__PURE__ */ jsx(DocTitleAnchorPane, { id: doc._id, type: doc._type, children: typeof doc.title === "string" ? doc.title || "Untitled" : "Untitled" }) : /* @__PURE__ */ jsx(
1897
+ DocTitleAnchor,
1898
+ {
1899
+ id: doc._id,
1900
+ type: doc._type,
1901
+ structureTool,
1902
+ children: typeof doc.title === "string" ? doc.title || "Untitled" : "Untitled"
1903
+ }
1904
+ ) }),
1905
+ showDocumentId && /* @__PURE__ */ jsx(DocId, { children: doc._id }),
1906
+ resolvedDocBadge && /* @__PURE__ */ jsx(
1907
+ DocBadgeRenderer,
1908
+ {
1909
+ doc,
1910
+ docBadge: resolvedDocBadge
1911
+ }
1912
+ )
1913
+ ] }) }) }),
1914
+ showTypeColumn && /* @__PURE__ */ jsx(ColType, { children: typeColumnMode === "text" ? /* @__PURE__ */ jsx(TypeText, { children: resolveTypeLabel(doc._type, resolvedTypeLabels) }) : (() => {
1915
+ const typeColor = getTypeColor(doc._type);
1916
+ return /* @__PURE__ */ jsx(TypeBadge, { $bgColor: typeColor.bg, $textColor: typeColor.text, children: resolveTypeLabel(doc._type, resolvedTypeLabels) });
1917
+ })() }),
1918
+ /* @__PURE__ */ jsx(ColScore, { children: /* @__PURE__ */ jsxs(ScoreBadge, { $score: doc.health.score, children: [
1919
+ doc.health.score,
1920
+ "%"
1921
+ ] }) }),
1922
+ /* @__PURE__ */ jsxs(ColIssues, { children: [
1923
+ doc.health.issues.slice(0, 2).map((issue) => /* @__PURE__ */ jsxs(IssueTag, { children: [
1924
+ "\u2022 ",
1925
+ issue
1926
+ ] }, `issue-${doc._id}-${issue}`)),
1927
+ doc.health.issues.length > 2 && /* @__PURE__ */ jsx(
1928
+ MoreIssuesWrapper,
1929
+ {
1930
+ onMouseEnter: function(e) {
1931
+ handleMouseEnterIssues(
1932
+ e.currentTarget,
1933
+ doc.health.issues.slice(2)
1934
+ );
1935
+ },
1936
+ onMouseLeave: handleMouseLeave,
1937
+ children: /* @__PURE__ */ jsxs(MoreIssues, { children: [
1938
+ "+",
1939
+ doc.health.issues.length - 2,
1940
+ " more issues"
1941
+ ] })
1942
+ }
1943
+ )
1944
+ ] })
1945
+ ] }, doc._id);
1946
+ }),
1947
+ /* @__PURE__ */ jsxs(PaginationBar, { children: [
1948
+ /* @__PURE__ */ jsxs("span", { children: [
1949
+ "Showing ",
1950
+ Math.min((currentPage - 1) * pageSize + 1, filteredAndSortedDocs.length),
1951
+ "\u2013",
1952
+ Math.min(currentPage * pageSize, filteredAndSortedDocs.length),
1953
+ " of",
1954
+ " ",
1955
+ filteredAndSortedDocs.length,
1956
+ " documents"
1957
+ ] }),
1958
+ /* @__PURE__ */ jsxs(PaginationCenter, { children: [
1959
+ /* @__PURE__ */ jsx(
1960
+ PaginationButton,
1961
+ {
1962
+ disabled: currentPage === 1,
1963
+ onClick: () => setCurrentPage((p) => Math.max(1, p - 1)),
1964
+ title: "Previous page",
1965
+ children: "\u2039"
1966
+ }
1967
+ ),
1968
+ /* @__PURE__ */ jsxs("span", { children: [
1969
+ "Page ",
1970
+ currentPage,
1971
+ " of ",
1972
+ totalPages
1973
+ ] }),
1974
+ /* @__PURE__ */ jsx(
1975
+ PaginationButton,
1976
+ {
1977
+ disabled: currentPage >= totalPages,
1978
+ onClick: () => setCurrentPage((p) => Math.min(totalPages, p + 1)),
1979
+ title: "Next page",
1980
+ children: "\u203A"
1981
+ }
1982
+ )
1983
+ ] }),
1984
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
1985
+ /* @__PURE__ */ jsx("span", { children: "Per page:" }),
1986
+ /* @__PURE__ */ jsxs(
1987
+ StyledSelect,
1988
+ {
1989
+ value: pageSize,
1990
+ style: { height: 30, fontSize: 12, padding: "0 28px 0 8px" },
1991
+ onChange: (e) => {
1992
+ const size = Number(e.currentTarget.value);
1993
+ setPageSize(size);
1994
+ try {
1995
+ localStorage.setItem("seo-dashboard-page-size", String(size));
1996
+ } catch (e2) {
1997
+ }
1998
+ setCurrentPage(1);
1999
+ },
2000
+ children: [
2001
+ /* @__PURE__ */ jsx("option", { value: 25, children: "25" }),
2002
+ /* @__PURE__ */ jsx("option", { value: 50, children: "50" }),
2003
+ /* @__PURE__ */ jsx("option", { value: 100, children: "100" }),
2004
+ /* @__PURE__ */ jsx("option", { value: 200, children: "200" })
2005
+ ]
2006
+ }
2007
+ )
2008
+ ] })
2009
+ ] })
2010
+ ] }))
2011
+ ] }),
2012
+ activePopover && /* @__PURE__ */ jsx(
2013
+ IssuesPopover,
2014
+ {
2015
+ style: {
2016
+ top: activePopover.top,
2017
+ left: activePopover.left,
2018
+ transform: "translateY(calc(-100% - 10px))"
2019
+ },
2020
+ children: activePopover.issues.map((issue) => /* @__PURE__ */ jsxs(PopoverIssueItem, { children: [
2021
+ "\u26A0\uFE0F ",
2022
+ issue
2023
+ ] }, issue))
2024
+ }
2025
+ )
2026
+ ] });
2027
+ return /* @__PURE__ */ jsxs(DashboardContainer, { style: currentVars, children: [
2028
+ licenseStatus === "loading" && /* @__PURE__ */ jsx(RenderLicenseLoading, { text: loadingLicense }),
2029
+ licenseStatus === "invalid" && /* @__PURE__ */ jsx(RenderLicenseInvalid, { licenseKey, validateLicense }),
2030
+ licenseStatus === "valid" && renderDashboardContent()
2031
+ ] });
2032
+ };
2033
+ var SeoHealthDashboard_default = SeoHealthDashboard;
2034
+
2035
+ export { SeoHealthDashboard_default };
2036
+ //# sourceMappingURL=chunk-UCVSMPEJ.js.map
2037
+ //# sourceMappingURL=chunk-UCVSMPEJ.js.map