@orion-studios/payload-seo-audit 1.0.0 → 1.1.1

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.
Files changed (60) hide show
  1. package/README.md +48 -45
  2. package/dist/api/cron.d.ts.map +1 -1
  3. package/dist/api/cron.js +3 -23
  4. package/dist/api/run-stream.d.ts.map +1 -1
  5. package/dist/api/run-stream.js +19 -215
  6. package/dist/api/run.d.ts.map +1 -1
  7. package/dist/api/run.js +4 -25
  8. package/dist/collections/SeoDashboardView.d.ts +3 -0
  9. package/dist/collections/SeoDashboardView.d.ts.map +1 -0
  10. package/dist/collections/SeoDashboardView.js +30 -0
  11. package/dist/collections/SeoSnapshots.d.ts.map +1 -1
  12. package/dist/collections/SeoSnapshots.js +26 -0
  13. package/dist/components/layout/SeoReportShell.js +2 -2
  14. package/dist/components/types.d.ts +10 -0
  15. package/dist/components/types.d.ts.map +1 -1
  16. package/dist/components/views/SeoDashboard.d.ts.map +1 -1
  17. package/dist/components/views/SeoDashboard.js +68 -53
  18. package/dist/components/views/SeoPageReport.d.ts.map +1 -1
  19. package/dist/components/views/SeoPageReport.js +21 -10
  20. package/dist/components/views/SeoSnapshotReport.d.ts.map +1 -1
  21. package/dist/components/views/SeoSnapshotReport.js +21 -10
  22. package/dist/config.d.ts +1 -0
  23. package/dist/config.d.ts.map +1 -1
  24. package/dist/config.js +1 -0
  25. package/dist/globals/SeoDashboard.js +1 -1
  26. package/dist/globals/SeoIntegrations.d.ts +3 -0
  27. package/dist/globals/SeoIntegrations.d.ts.map +1 -0
  28. package/dist/globals/SeoIntegrations.js +305 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +4 -4
  31. package/dist/utilities/crux.d.ts +6 -0
  32. package/dist/utilities/crux.d.ts.map +1 -0
  33. package/dist/utilities/crux.js +244 -0
  34. package/dist/utilities/dataforseo.d.ts +12 -0
  35. package/dist/utilities/dataforseo.d.ts.map +1 -0
  36. package/dist/utilities/dataforseo.js +169 -0
  37. package/dist/utilities/gsc.d.ts +4 -11
  38. package/dist/utilities/gsc.d.ts.map +1 -1
  39. package/dist/utilities/gsc.js +58 -22
  40. package/dist/utilities/integrationSettings.d.ts +10 -0
  41. package/dist/utilities/integrationSettings.d.ts.map +1 -0
  42. package/dist/utilities/integrationSettings.js +198 -0
  43. package/dist/utilities/opsWebhook.d.ts +15 -0
  44. package/dist/utilities/opsWebhook.d.ts.map +1 -0
  45. package/dist/utilities/opsWebhook.js +130 -0
  46. package/dist/utilities/pagespeed.d.ts +1 -1
  47. package/dist/utilities/pagespeed.d.ts.map +1 -1
  48. package/dist/utilities/pagespeed.js +11 -5
  49. package/dist/utilities/providers.d.ts +2 -2
  50. package/dist/utilities/providers.d.ts.map +1 -1
  51. package/dist/utilities/providers.js +12 -7
  52. package/dist/utilities/runAudit.d.ts +4 -1
  53. package/dist/utilities/runAudit.d.ts.map +1 -1
  54. package/dist/utilities/runAudit.js +112 -11
  55. package/dist/utilities/secrets.d.ts +23 -0
  56. package/dist/utilities/secrets.d.ts.map +1 -0
  57. package/dist/utilities/secrets.js +108 -0
  58. package/dist/utilities/types.d.ts +85 -0
  59. package/dist/utilities/types.d.ts.map +1 -1
  60. package/package.json +1 -1
@@ -42,6 +42,7 @@ const MetricCardRow_1 = require("../visualization/MetricCardRow");
42
42
  const CategoryScoreGrid_1 = require("../visualization/CategoryScoreGrid");
43
43
  const ScoreTrendChart_1 = require("../visualization/ScoreTrendChart");
44
44
  const IssueTable_1 = require("../tables/IssueTable");
45
+ const SeoReportShell_1 = require("../layout/SeoReportShell");
45
46
  const SeoReportHeader_1 = require("../layout/SeoReportHeader");
46
47
  const scoreHelpers_1 = require("../utils/scoreHelpers");
47
48
  const formatters_1 = require("../utils/formatters");
@@ -144,13 +145,21 @@ const SeoDashboard = () => {
144
145
  else if (step === 'pagespeed')
145
146
  setRunningProgress(65);
146
147
  else if (step === 'pagespeed-complete' || step === 'pagespeed-skipped')
147
- setRunningProgress(75);
148
+ setRunningProgress(72);
149
+ else if (step === 'crux')
150
+ setRunningProgress(78);
151
+ else if (step === 'crux-complete' || step === 'crux-skipped')
152
+ setRunningProgress(82);
148
153
  else if (step === 'gsc')
149
- setRunningProgress(80);
154
+ setRunningProgress(84);
150
155
  else if (step === 'gsc-complete' || step === 'gsc-skipped')
151
- setRunningProgress(85);
152
- else if (step === 'saving')
156
+ setRunningProgress(88);
157
+ else if (step === 'authority')
153
158
  setRunningProgress(90);
159
+ else if (step === 'authority-complete' || step === 'authority-skipped')
160
+ setRunningProgress(92);
161
+ else if (step === 'saving')
162
+ setRunningProgress(95);
154
163
  }
155
164
  else if (eventType === 'complete') {
156
165
  setRunningProgress(100);
@@ -163,7 +172,7 @@ const SeoDashboard = () => {
163
172
  throw new Error(data.message || 'Audit failed');
164
173
  }
165
174
  }
166
- catch (parseErr) {
175
+ catch {
167
176
  }
168
177
  }
169
178
  }
@@ -186,54 +195,60 @@ const SeoDashboard = () => {
186
195
  return (c.critical ?? 0) + (c.high ?? 0) + (c.medium ?? 0) + (c.low ?? 0) + (c.info ?? 0);
187
196
  }, [latest]);
188
197
  if (loading) {
189
- return (react_1.default.createElement("section", { className: "seo-report mb-6 rounded-xl border border-slate-200 bg-white p-6 dark:border-slate-700 dark:bg-slate-800" },
190
- react_1.default.createElement("div", { className: "flex items-center gap-3" },
191
- react_1.default.createElement("div", { className: "h-5 w-5 animate-spin rounded-full border-2 border-slate-300 border-t-slate-600 dark:border-slate-600 dark:border-t-slate-300" }),
192
- react_1.default.createElement("p", { className: "text-sm text-slate-500 dark:text-slate-400" }, "Loading SEO data..."))));
198
+ return (react_1.default.createElement(SeoReportShell_1.SeoReportShell, null,
199
+ react_1.default.createElement("section", { className: "rounded-xl border border-slate-200 bg-white p-6 dark:border-slate-700 dark:bg-slate-800" },
200
+ react_1.default.createElement("div", { className: "flex items-center gap-3" },
201
+ react_1.default.createElement("div", { className: "h-5 w-5 animate-spin rounded-full border-2 border-slate-300 border-t-slate-600 dark:border-slate-600 dark:border-t-slate-300" }),
202
+ react_1.default.createElement("p", { className: "text-sm text-slate-500 dark:text-slate-400" }, "Loading SEO data...")))));
193
203
  }
194
- return (react_1.default.createElement("section", { className: "seo-report mb-6 space-y-6 rounded-xl border border-slate-200 bg-white p-6 dark:border-slate-700 dark:bg-slate-800" },
195
- react_1.default.createElement(SeoReportHeader_1.SeoReportHeader, { title: "SEO Health Report", subtitle: latest
196
- ? `Last audit: ${(0, formatters_1.formatTimestamp)(latest.startedAt)}`
197
- : 'No audits have been run yet.', actions: react_1.default.createElement(react_1.default.Fragment, null,
198
- react_1.default.createElement("button", { type: "button", onClick: () => void fetchData(), className: "rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-slate-700 hover:bg-slate-50 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-200 dark:hover:bg-slate-600" }, "Refresh"),
199
- react_1.default.createElement("button", { type: "button", onClick: () => void runAuditNow(), disabled: running, className: "rounded-md bg-slate-900 px-3 py-2 text-sm text-white disabled:opacity-60 dark:bg-slate-100 dark:text-slate-900" }, running ? 'Running Audit...' : 'Run Audit Now')) }),
200
- running && runningStep && (react_1.default.createElement("div", { className: "rounded-md border border-blue-200 bg-blue-50 px-4 py-3 dark:border-blue-800 dark:bg-blue-900/30" },
201
- react_1.default.createElement("div", { className: "flex items-center gap-3 mb-2" },
202
- react_1.default.createElement("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-blue-300 border-t-blue-600 dark:border-blue-700 dark:border-t-blue-400" }),
203
- react_1.default.createElement("p", { className: "text-sm text-blue-700 dark:text-blue-300" }, runningStep),
204
- react_1.default.createElement("span", { className: "ml-auto text-sm font-medium text-blue-700 dark:text-blue-300" },
205
- runningProgress,
206
- "%")),
207
- react_1.default.createElement("div", { className: "h-2 w-full overflow-hidden rounded-full bg-blue-100 dark:bg-blue-900" },
208
- react_1.default.createElement("div", { className: "h-full rounded-full bg-blue-600 transition-all duration-500 ease-out dark:bg-blue-400", style: { width: `${runningProgress}%` } })))),
209
- error && (react_1.default.createElement("div", { className: "rounded-md border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-800 dark:bg-red-900/30 dark:text-red-300" }, error)),
210
- !latest ? (react_1.default.createElement("div", { className: "flex flex-col items-center justify-center gap-3 rounded-lg border-2 border-dashed border-slate-200 py-16 dark:border-slate-700" },
211
- react_1.default.createElement("p", { className: "text-lg font-medium text-slate-600 dark:text-slate-300" }, "No SEO audits yet"),
212
- react_1.default.createElement("p", { className: "text-sm text-slate-500 dark:text-slate-400" }, "Run your first audit to see your site's SEO health."))) : (react_1.default.createElement(react_1.default.Fragment, null,
213
- react_1.default.createElement("div", { className: "flex flex-col items-center gap-2 rounded-lg border border-slate-200 bg-gradient-to-br from-slate-50 to-white py-8 dark:border-slate-700 dark:from-slate-800/50 dark:to-slate-900/50" },
214
- react_1.default.createElement(ScoreGauge_1.ScoreGauge, { score: overallScore, size: 140, showGrade: true }),
215
- delta && (react_1.default.createElement("span", { className: `text-sm font-medium ${delta.className}` },
216
- delta.text,
217
- " from previous"))),
218
- react_1.default.createElement(MetricCardRow_1.MetricCardRow, null,
219
- react_1.default.createElement(MetricCard_1.MetricCard, { label: "Pages Crawled", value: (0, formatters_1.formatNumber)(latest.metrics?.pagesCrawled) }),
220
- react_1.default.createElement(MetricCard_1.MetricCard, { label: "Critical Issues", value: latest.issueCounts?.critical ?? 0, valueClassName: (latest.issueCounts?.critical ?? 0) > 0 ? 'text-red-600 dark:text-red-400' : 'text-slate-900 dark:text-slate-100' }),
221
- react_1.default.createElement(MetricCard_1.MetricCard, { label: "High Issues", value: latest.issueCounts?.high ?? 0, valueClassName: (latest.issueCounts?.high ?? 0) > 0 ? 'text-orange-600 dark:text-orange-400' : 'text-slate-900 dark:text-slate-100' }),
222
- react_1.default.createElement(MetricCard_1.MetricCard, { label: "Lighthouse", value: latest.metrics?.avgLighthousePerformance != null
223
- ? `${Math.round(latest.metrics.avgLighthousePerformance)}`
224
- : '-', subtext: "Performance" }),
225
- react_1.default.createElement(MetricCard_1.MetricCard, { label: "Avg LCP", value: (0, formatters_1.formatMs)(latest.metrics?.avgLCPMs), subtext: "Largest Contentful Paint" }),
226
- react_1.default.createElement(MetricCard_1.MetricCard, { label: "Avg CLS", value: (0, formatters_1.formatCLS)(latest.metrics?.avgCLS), subtext: "Cumulative Layout Shift" })),
227
- react_1.default.createElement("div", null,
228
- react_1.default.createElement("h2", { className: "mb-3 text-sm font-semibold text-slate-900 dark:text-slate-100" }, "Category Breakdown"),
229
- react_1.default.createElement(CategoryScoreGrid_1.CategoryScoreGrid, { scores: latest.scores || {}, previousScores: previous?.scores })),
230
- react_1.default.createElement("div", null,
231
- react_1.default.createElement("h2", { className: "mb-3 text-sm font-semibold text-slate-900 dark:text-slate-100" }, "Score Trend"),
232
- react_1.default.createElement(ScoreTrendChart_1.ScoreTrendChart, { points: trendPoints })),
233
- react_1.default.createElement("div", null,
234
- react_1.default.createElement("div", { className: "mb-3 flex items-center justify-between" },
235
- react_1.default.createElement("h2", { className: "text-sm font-semibold text-slate-900 dark:text-slate-100" }, "Top Issues"),
236
- latest && (react_1.default.createElement("a", { href: `/admin/collections/seo-snapshots/${latest.id}`, className: "text-sm text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200" }, "View Full Report \u2192"))),
237
- react_1.default.createElement(IssueTable_1.IssueTable, { issues: latestIssues, pageSize: 10 }))))));
204
+ return (react_1.default.createElement(SeoReportShell_1.SeoReportShell, null,
205
+ react_1.default.createElement("section", { className: "space-y-6 rounded-xl border border-slate-200 bg-white p-6 dark:border-slate-700 dark:bg-slate-800" },
206
+ react_1.default.createElement(SeoReportHeader_1.SeoReportHeader, { title: "SEO Health Report", subtitle: latest ? `Last audit: ${(0, formatters_1.formatTimestamp)(latest.startedAt)}` : 'No audits have been run yet.', actions: react_1.default.createElement(react_1.default.Fragment, null,
207
+ react_1.default.createElement("a", { href: "/admin/globals/seo-integrations", className: "rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm font-medium text-slate-700 transition-colors hover:bg-slate-50 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-200 dark:hover:bg-slate-600" }, "Integrations"),
208
+ react_1.default.createElement("button", { type: "button", onClick: () => void fetchData(), className: "rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm font-medium text-slate-700 transition-colors hover:bg-slate-50 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-200 dark:hover:bg-slate-600" }, "Refresh"),
209
+ react_1.default.createElement("button", { type: "button", onClick: () => void runAuditNow(), disabled: running, className: "rounded-lg bg-slate-900 px-3 py-2 text-sm font-medium text-white transition-colors hover:bg-slate-700 disabled:cursor-not-allowed disabled:opacity-60 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-white" }, running ? 'Running Audit...' : 'Run Audit Now')) }),
210
+ running && runningStep && (react_1.default.createElement("div", { className: "rounded-md border border-blue-200 bg-blue-50 px-4 py-3 dark:border-blue-800 dark:bg-blue-900/30" },
211
+ react_1.default.createElement("div", { className: "mb-2 flex items-center gap-3" },
212
+ react_1.default.createElement("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-blue-300 border-t-blue-600 dark:border-blue-700 dark:border-t-blue-400" }),
213
+ react_1.default.createElement("p", { className: "text-sm text-blue-700 dark:text-blue-300" }, runningStep),
214
+ react_1.default.createElement("span", { className: "ml-auto text-sm font-medium text-blue-700 dark:text-blue-300" },
215
+ runningProgress,
216
+ "%")),
217
+ react_1.default.createElement("div", { className: "h-2 w-full overflow-hidden rounded-full bg-blue-100 dark:bg-blue-900" },
218
+ react_1.default.createElement("div", { className: "h-full rounded-full bg-blue-600 transition-all duration-500 ease-out dark:bg-blue-400", style: { width: `${runningProgress}%` } })))),
219
+ error && (react_1.default.createElement("div", { className: "rounded-md border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-800 dark:bg-red-900/30 dark:text-red-300" }, error)),
220
+ !latest ? (react_1.default.createElement("div", { className: "flex flex-col items-center justify-center gap-3 rounded-lg border-2 border-dashed border-slate-200 py-16 dark:border-slate-700" },
221
+ react_1.default.createElement("p", { className: "text-lg font-medium text-slate-600 dark:text-slate-300" }, "No SEO audits yet"),
222
+ react_1.default.createElement("p", { className: "text-sm text-slate-500 dark:text-slate-400" }, "Run your first audit to see your site's SEO health."))) : (react_1.default.createElement(react_1.default.Fragment, null,
223
+ react_1.default.createElement("div", { className: "flex flex-col items-center gap-2 rounded-lg border border-slate-200 bg-gradient-to-br from-slate-50 to-white py-8 dark:border-slate-700 dark:from-slate-800/50 dark:to-slate-900/50" },
224
+ react_1.default.createElement(ScoreGauge_1.ScoreGauge, { score: overallScore, size: 140, showGrade: true }),
225
+ delta && react_1.default.createElement("span", { className: `text-sm font-medium ${delta.className}` },
226
+ delta.text,
227
+ " from previous")),
228
+ react_1.default.createElement(MetricCardRow_1.MetricCardRow, null,
229
+ react_1.default.createElement(MetricCard_1.MetricCard, { label: "Pages Crawled", value: (0, formatters_1.formatNumber)(latest.metrics?.pagesCrawled) }),
230
+ react_1.default.createElement(MetricCard_1.MetricCard, { label: "Critical Issues", value: latest.issueCounts?.critical ?? 0, valueClassName: (latest.issueCounts?.critical ?? 0) > 0
231
+ ? 'text-red-600 dark:text-red-400'
232
+ : 'text-slate-900 dark:text-slate-100' }),
233
+ react_1.default.createElement(MetricCard_1.MetricCard, { label: "High Issues", value: latest.issueCounts?.high ?? 0, valueClassName: (latest.issueCounts?.high ?? 0) > 0
234
+ ? 'text-orange-600 dark:text-orange-400'
235
+ : 'text-slate-900 dark:text-slate-100' }),
236
+ react_1.default.createElement(MetricCard_1.MetricCard, { label: "Lighthouse", value: latest.metrics?.avgLighthousePerformance != null ? `${Math.round(latest.metrics.avgLighthousePerformance)}` : '-', subtext: "Performance" }),
237
+ react_1.default.createElement(MetricCard_1.MetricCard, { label: "Avg LCP", value: (0, formatters_1.formatMs)(latest.metrics?.avgLCPMs), subtext: "Largest Contentful Paint" }),
238
+ react_1.default.createElement(MetricCard_1.MetricCard, { label: "Avg CLS", value: (0, formatters_1.formatCLS)(latest.metrics?.avgCLS), subtext: "Cumulative Layout Shift" }),
239
+ react_1.default.createElement(MetricCard_1.MetricCard, { label: "CrUX LCP p75", value: (0, formatters_1.formatMs)(latest.metrics?.cruxP75LCPMs), subtext: latest.metrics?.cruxAvailable ? 'Field Data' : 'Unavailable' }),
240
+ react_1.default.createElement(MetricCard_1.MetricCard, { label: "CrUX INP p75", value: (0, formatters_1.formatMs)(latest.metrics?.cruxP75INPMs), subtext: latest.metrics?.cruxAvailable ? 'Field Data' : 'Unavailable' }),
241
+ react_1.default.createElement(MetricCard_1.MetricCard, { label: "CrUX CLS p75", value: (0, formatters_1.formatCLS)(latest.metrics?.cruxP75CLS), subtext: latest.metrics?.cruxSource ? `Source: ${latest.metrics.cruxSource}` : 'No source' })),
242
+ react_1.default.createElement("div", null,
243
+ react_1.default.createElement("h2", { className: "mb-3 text-sm font-semibold text-slate-900 dark:text-slate-100" }, "Category Breakdown"),
244
+ react_1.default.createElement(CategoryScoreGrid_1.CategoryScoreGrid, { scores: latest.scores || {}, previousScores: previous?.scores })),
245
+ react_1.default.createElement("div", null,
246
+ react_1.default.createElement("h2", { className: "mb-3 text-sm font-semibold text-slate-900 dark:text-slate-100" }, "Score Trend"),
247
+ react_1.default.createElement(ScoreTrendChart_1.ScoreTrendChart, { points: trendPoints })),
248
+ react_1.default.createElement("div", null,
249
+ react_1.default.createElement("div", { className: "mb-3 flex items-center justify-between" },
250
+ react_1.default.createElement("h2", { className: "text-sm font-semibold text-slate-900 dark:text-slate-100" }, "Top Issues"),
251
+ latest && (react_1.default.createElement("a", { href: `/admin/collections/seo-snapshots/${latest.id}`, className: "text-sm text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200" }, "View Full Report \u2192"))),
252
+ react_1.default.createElement(IssueTable_1.IssueTable, { issues: latestIssues, pageSize: 10 })))))));
238
253
  };
239
254
  exports.SeoDashboard = SeoDashboard;
@@ -1 +1 @@
1
- {"version":3,"file":"SeoPageReport.d.ts","sourceRoot":"","sources":["../../../src/components/views/SeoPageReport.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAoD,MAAM,OAAO,CAAA;AAyDxE,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EA8SjC,CAAA"}
1
+ {"version":3,"file":"SeoPageReport.d.ts","sourceRoot":"","sources":["../../../src/components/views/SeoPageReport.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAoD,MAAM,OAAO,CAAA;AA4DxE,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAwTjC,CAAA"}
@@ -36,6 +36,7 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
37
  exports.SeoPageReport = void 0;
38
38
  const react_1 = __importStar(require("react"));
39
+ const navigation_1 = require("next/navigation");
39
40
  const lucide_react_1 = require("lucide-react");
40
41
  const ScoreGauge_1 = require("../visualization/ScoreGauge");
41
42
  const ScoreBar_1 = require("../visualization/ScoreBar");
@@ -46,15 +47,15 @@ const SeoReportHeader_1 = require("../layout/SeoReportHeader");
46
47
  const PdfDownloadButton_1 = require("../pdf/PdfDownloadButton");
47
48
  const formatters_1 = require("../utils/formatters");
48
49
  const types_1 = require("../types");
49
- const getIdFromPath = () => {
50
- if (typeof window === 'undefined')
50
+ const getIdFromPath = (pathname) => {
51
+ if (!pathname)
51
52
  return null;
52
- const segments = window.location.pathname.split('/');
53
+ const segments = pathname.split('/').filter(Boolean);
53
54
  const idx = segments.indexOf('seo-page-results');
54
- if (idx >= 0 && segments[idx + 1]) {
55
- return segments[idx + 1];
56
- }
57
- return null;
55
+ const id = idx >= 0 ? segments[idx + 1] : null;
56
+ if (!id || id === 'create')
57
+ return null;
58
+ return decodeURIComponent(id);
58
59
  };
59
60
  const CATEGORY_ICONS = {
60
61
  metadata: react_1.default.createElement(lucide_react_1.FileText, { size: 16 }),
@@ -80,10 +81,20 @@ const CATEGORIES = [
80
81
  'performance',
81
82
  ];
82
83
  const SeoPageReport = () => {
84
+ const pathname = (0, navigation_1.usePathname)();
83
85
  const [pageResult, setPageResult] = (0, react_1.useState)(null);
84
86
  const [loading, setLoading] = (0, react_1.useState)(true);
85
87
  const [error, setError] = (0, react_1.useState)(null);
86
- const pageResultId = (0, react_1.useMemo)(() => getIdFromPath(), []);
88
+ const pageResultId = (0, react_1.useMemo)(() => getIdFromPath(pathname), [pathname]);
89
+ const handleBack = (0, react_1.useCallback)(() => {
90
+ if (typeof window === 'undefined')
91
+ return;
92
+ if (window.history.length > 1) {
93
+ window.history.back();
94
+ return;
95
+ }
96
+ window.location.href = '/admin/collections/seo-snapshots';
97
+ }, []);
87
98
  const fetchPageResult = (0, react_1.useCallback)(async () => {
88
99
  if (!pageResultId) {
89
100
  setError('Could not determine page result ID.');
@@ -136,13 +147,13 @@ const SeoPageReport = () => {
136
147
  react_1.default.createElement("p", { className: "text-sm text-slate-500 dark:text-slate-400" }, "Loading page report..."))));
137
148
  }
138
149
  if (error || !pageResult) {
139
- return (react_1.default.createElement(SeoReportShell_1.SeoReportShell, { onBack: () => window.history.back(), backLabel: "Back" },
150
+ return (react_1.default.createElement(SeoReportShell_1.SeoReportShell, { onBack: handleBack, backLabel: "Back" },
140
151
  react_1.default.createElement("div", { className: "rounded-md border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-800 dark:bg-red-900/30 dark:text-red-300" }, error || 'No data available.')));
141
152
  }
142
153
  const overallScore = pageResult.overallScore ?? 0;
143
154
  const titleInfo = (0, formatters_1.formatCharCount)(pageResult.title, 20, 65);
144
155
  const descInfo = (0, formatters_1.formatCharCount)(pageResult.metaDescription, 70, 165);
145
- return (react_1.default.createElement(SeoReportShell_1.SeoReportShell, { onBack: () => window.history.back(), backLabel: "Back to Snapshot" },
156
+ return (react_1.default.createElement(SeoReportShell_1.SeoReportShell, { onBack: handleBack, backLabel: "Back to Snapshot" },
146
157
  react_1.default.createElement("div", { className: "space-y-6", id: "seo-page-report" },
147
158
  react_1.default.createElement(SeoReportHeader_1.SeoReportHeader, { title: "Page SEO Report", subtitle: pageResult.url, actions: react_1.default.createElement(PdfDownloadButton_1.PdfDownloadButton, { targetId: "seo-page-report", filename: `page-seo-${pageResult.path || pageResultId}` }), badges: pageResult.statusCode ? (react_1.default.createElement("span", { className: `inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-medium ${pageResult.statusCode === 200
148
159
  ? 'border-green-200 bg-green-50 text-green-700'
@@ -1 +1 @@
1
- {"version":3,"file":"SeoSnapshotReport.d.ts","sourceRoot":"","sources":["../../../src/components/views/SeoSnapshotReport.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAoD,MAAM,OAAO,CAAA;AAgCxE,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EA0UrC,CAAA"}
1
+ {"version":3,"file":"SeoSnapshotReport.d.ts","sourceRoot":"","sources":["../../../src/components/views/SeoSnapshotReport.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAoD,MAAM,OAAO,CAAA;AA8BxE,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAoVrC,CAAA"}
@@ -36,6 +36,7 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
37
  exports.SeoSnapshotReport = void 0;
38
38
  const react_1 = __importStar(require("react"));
39
+ const navigation_1 = require("next/navigation");
39
40
  const ScoreGauge_1 = require("../visualization/ScoreGauge");
40
41
  const MetricCard_1 = require("../visualization/MetricCard");
41
42
  const CategoryScoreGrid_1 = require("../visualization/CategoryScoreGrid");
@@ -49,17 +50,18 @@ const PdfDownloadButton_1 = require("../pdf/PdfDownloadButton");
49
50
  const formatters_1 = require("../utils/formatters");
50
51
  const scoreHelpers_1 = require("../utils/scoreHelpers");
51
52
  const types_1 = require("../types");
52
- const getIdFromPath = () => {
53
- if (typeof window === 'undefined')
53
+ const getIdFromPath = (pathname) => {
54
+ if (!pathname)
54
55
  return null;
55
- const segments = window.location.pathname.split('/');
56
+ const segments = pathname.split('/').filter(Boolean);
56
57
  const idx = segments.indexOf('seo-snapshots');
57
- if (idx >= 0 && segments[idx + 1]) {
58
- return segments[idx + 1];
59
- }
60
- return null;
58
+ const id = idx >= 0 ? segments[idx + 1] : null;
59
+ if (!id || id === 'create')
60
+ return null;
61
+ return decodeURIComponent(id);
61
62
  };
62
63
  const SeoSnapshotReport = () => {
64
+ const pathname = (0, navigation_1.usePathname)();
63
65
  const [reportData, setReportData] = (0, react_1.useState)(null);
64
66
  const [loading, setLoading] = (0, react_1.useState)(true);
65
67
  const [error, setError] = (0, react_1.useState)(null);
@@ -73,7 +75,16 @@ const SeoSnapshotReport = () => {
73
75
  });
74
76
  const [pageResultsLoading, setPageResultsLoading] = (0, react_1.useState)(false);
75
77
  const [activeTab, setActiveTab] = (0, react_1.useState)('overview');
76
- const snapshotId = (0, react_1.useMemo)(() => getIdFromPath(), []);
78
+ const snapshotId = (0, react_1.useMemo)(() => getIdFromPath(pathname), [pathname]);
79
+ const handleBackToSnapshots = (0, react_1.useCallback)(() => {
80
+ if (typeof window === 'undefined')
81
+ return;
82
+ if (window.history.length > 1) {
83
+ window.history.back();
84
+ return;
85
+ }
86
+ window.location.href = '/admin/collections/seo-snapshots';
87
+ }, []);
77
88
  const fetchReport = (0, react_1.useCallback)(async () => {
78
89
  if (!snapshotId) {
79
90
  setError('Could not determine snapshot ID.');
@@ -144,7 +155,7 @@ const SeoSnapshotReport = () => {
144
155
  react_1.default.createElement("p", { className: "text-sm text-slate-500 dark:text-slate-400" }, "Loading snapshot report..."))));
145
156
  }
146
157
  if (error || !reportData) {
147
- return (react_1.default.createElement(SeoReportShell_1.SeoReportShell, { onBack: () => window.history.back(), backLabel: "Back to Snapshots" },
158
+ return (react_1.default.createElement(SeoReportShell_1.SeoReportShell, { onBack: handleBackToSnapshots, backLabel: "Back to Snapshots" },
148
159
  react_1.default.createElement("div", { className: "rounded-md border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-800 dark:bg-red-900/30 dark:text-red-300" }, error || 'No data available.')));
149
160
  }
150
161
  const { snapshot, aggregated } = reportData;
@@ -152,7 +163,7 @@ const SeoSnapshotReport = () => {
152
163
  const metrics = snapshot.metrics || {};
153
164
  const issueCounts = snapshot.issueCounts || {};
154
165
  const overallScore = scores.overall ?? 0;
155
- return (react_1.default.createElement(SeoReportShell_1.SeoReportShell, { onBack: () => window.history.back(), backLabel: "Back to Snapshots" },
166
+ return (react_1.default.createElement(SeoReportShell_1.SeoReportShell, { onBack: handleBackToSnapshots, backLabel: "Back to Snapshots" },
156
167
  react_1.default.createElement("div", { className: "space-y-6", id: "seo-snapshot-report" },
157
168
  react_1.default.createElement(SeoReportHeader_1.SeoReportHeader, { title: snapshot.runLabel || 'SEO Audit Report', subtitle: (0, formatters_1.formatTimestamp)(snapshot.startedAt), actions: react_1.default.createElement(PdfDownloadButton_1.PdfDownloadButton, { targetId: "seo-snapshot-report", filename: `seo-report-${snapshot.runLabel || snapshotId}` }), badges: react_1.default.createElement(react_1.default.Fragment, null,
158
169
  react_1.default.createElement("span", { className: "inline-flex items-center rounded-full border border-slate-200 bg-slate-50 px-2.5 py-0.5 text-xs font-medium text-slate-600 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-400" }, snapshot.runType || 'manual'),
package/dist/config.d.ts CHANGED
@@ -21,6 +21,7 @@ export type SeoAuditPluginConfig = {
21
21
  integrations?: {
22
22
  enablePageSpeed?: boolean;
23
23
  enableSearchConsole?: boolean;
24
+ enableCrUX?: boolean;
24
25
  };
25
26
  thresholds?: {
26
27
  targetLCPMs?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,oBAAoB,GAAG;IAIjC,IAAI,EAAE;QAEJ,IAAI,EAAE,MAAM,CAAA;QAEZ,MAAM,EAAE,MAAM,CAAA;QAEd,aAAa,EAAE,MAAM,CAAA;QAErB,UAAU,CAAC,EAAE,MAAM,CAAA;QAEnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;KACnB,CAAA;IAKD,KAAK,CAAC,EAAE;QAEN,QAAQ,CAAC,EAAE,MAAM,CAAA;QAEjB,QAAQ,CAAC,EAAE,MAAM,CAAA;QAEjB,gBAAgB,CAAC,EAAE,MAAM,CAAA;QAEzB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;QAE1B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;KAC3B,CAAA;IAKD,QAAQ,CAAC,EAAE;QAET,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;QAEtB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;KACnB,CAAA;IAQD,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAA;IAK/B,YAAY,CAAC,EAAE;QAEb,eAAe,CAAC,EAAE,OAAO,CAAA;QAEzB,mBAAmB,CAAC,EAAE,OAAO,CAAA;KAC9B,CAAA;IAKD,UAAU,CAAC,EAAE;QAEX,WAAW,CAAC,EAAE,MAAM,CAAA;QAEpB,SAAS,CAAC,EAAE,MAAM,CAAA;KACnB,CAAA;CACF,CAAA;AAKD,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC,IAAI,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC,GAAG;IAC/E,IAAI,EAAE,QAAQ,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAA;IAC5C,KAAK,EAAE,QAAQ,CAAC,WAAW,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IAC3D,QAAQ,EAAE,QAAQ,CAAC,WAAW,CAAC,oBAAoB,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;IACjE,YAAY,EAAE,QAAQ,CAAC,WAAW,CAAC,oBAAoB,CAAC,cAAc,CAAC,CAAC,CAAC,CAAA;IACzE,UAAU,EAAE,QAAQ,CAAC,WAAW,CAAC,oBAAoB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;CACtE,CAAA;AAKD,wBAAgB,eAAe,CAAC,MAAM,EAAE,oBAAoB,GAAG,mBAAmB,CAiCjF"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,oBAAoB,GAAG;IAIjC,IAAI,EAAE;QAEJ,IAAI,EAAE,MAAM,CAAA;QAEZ,MAAM,EAAE,MAAM,CAAA;QAEd,aAAa,EAAE,MAAM,CAAA;QAErB,UAAU,CAAC,EAAE,MAAM,CAAA;QAEnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;KACnB,CAAA;IAKD,KAAK,CAAC,EAAE;QAEN,QAAQ,CAAC,EAAE,MAAM,CAAA;QAEjB,QAAQ,CAAC,EAAE,MAAM,CAAA;QAEjB,gBAAgB,CAAC,EAAE,MAAM,CAAA;QAEzB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;QAE1B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;KAC3B,CAAA;IAKD,QAAQ,CAAC,EAAE;QAET,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;QAEtB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;KACnB,CAAA;IAQD,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAA;IAK/B,YAAY,CAAC,EAAE;QAEb,eAAe,CAAC,EAAE,OAAO,CAAA;QAEzB,mBAAmB,CAAC,EAAE,OAAO,CAAA;QAE7B,UAAU,CAAC,EAAE,OAAO,CAAA;KACrB,CAAA;IAKD,UAAU,CAAC,EAAE;QAEX,WAAW,CAAC,EAAE,MAAM,CAAA;QAEpB,SAAS,CAAC,EAAE,MAAM,CAAA;KACnB,CAAA;CACF,CAAA;AAKD,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC,IAAI,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC,GAAG;IAC/E,IAAI,EAAE,QAAQ,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAA;IAC5C,KAAK,EAAE,QAAQ,CAAC,WAAW,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IAC3D,QAAQ,EAAE,QAAQ,CAAC,WAAW,CAAC,oBAAoB,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;IACjE,YAAY,EAAE,QAAQ,CAAC,WAAW,CAAC,oBAAoB,CAAC,cAAc,CAAC,CAAC,CAAC,CAAA;IACzE,UAAU,EAAE,QAAQ,CAAC,WAAW,CAAC,oBAAoB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;CACtE,CAAA;AAKD,wBAAgB,eAAe,CAAC,MAAM,EAAE,oBAAoB,GAAG,mBAAmB,CAkCjF"}
package/dist/config.js CHANGED
@@ -27,6 +27,7 @@ function normalizeConfig(config) {
27
27
  integrations: {
28
28
  enablePageSpeed: config.integrations?.enablePageSpeed ?? true,
29
29
  enableSearchConsole: config.integrations?.enableSearchConsole ?? false,
30
+ enableCrUX: config.integrations?.enableCrUX ?? false,
30
31
  },
31
32
  thresholds: {
32
33
  targetLCPMs: config.thresholds?.targetLCPMs ?? 2500,
@@ -6,7 +6,7 @@ exports.SeoDashboard = {
6
6
  slug: 'seo-dashboard',
7
7
  label: 'SEO Dashboard',
8
8
  admin: {
9
- group: 'SEO',
9
+ group: false,
10
10
  components: {
11
11
  views: {
12
12
  edit: {
@@ -0,0 +1,3 @@
1
+ import type { GlobalConfig } from 'payload';
2
+ export declare const SeoIntegrations: GlobalConfig;
3
+ //# sourceMappingURL=SeoIntegrations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SeoIntegrations.d.ts","sourceRoot":"","sources":["../../src/globals/SeoIntegrations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAe3C,eAAO,MAAM,eAAe,EAAE,YAiS7B,CAAA"}