sanity-plugin-seo
Version:
Complete SEO toolkit for Sanity Studio with live scoring, AI suggestions, team workflows, and 30+ structured data types. Free and AI tiers live. Pro coming soon.
60 lines (54 loc) • 1.82 kB
text/typescript
import { EditIcon, ClockIcon, CheckmarkCircleIcon } from "@sanity/icons";
/* eslint-disable @typescript-eslint/no-explicit-any */
export type WorkflowStatus = "draft" | "review" | "approved";
export interface WorkflowDoc {
_id: string;
_type: string;
_updatedAt: string;
docTitle: string;
seoStatus: WorkflowStatus;
reviewNotes: string;
score: number;
issues: string[];
seo: Record<string, any> | null;
}
export const STATUS_CFG: Record<
WorkflowStatus,
{ label: string; color: string; bg: string; border: string; Icon: any }
> = {
draft: {
label: "Draft",
color: "var(--card-muted-fg-color)",
bg: "var(--card-border-color)",
border: "var(--card-border-color)",
Icon: EditIcon,
},
review: {
label: "Needs Review",
color: "#f59e0b",
bg: "rgba(245, 158, 11, 0.12)",
border: "rgba(245, 158, 11, 0.25)",
Icon: ClockIcon,
},
approved: {
label: "Approved",
color: "#22c55e",
bg: "rgba(34, 197, 94, 0.12)",
border: "rgba(34, 197, 94, 0.25)",
Icon: CheckmarkCircleIcon,
},
};
export function buildIssues(seo: Record<string, any> | null): string[] {
if (!seo) return ["No SEO data"];
const issues: string[] = [];
if (!seo.metaTitle) issues.push("Missing meta title");
else if (seo.metaTitle.length < 50 || seo.metaTitle.length > 60)
issues.push("Meta title length out of range (50–60 chars)");
if (!seo.metaDescription) issues.push("Missing meta description");
else if (seo.metaDescription.length < 100 || seo.metaDescription.length > 160)
issues.push("Meta description length out of range (100–160 chars)");
if (!seo.metaImage?.asset) issues.push("No meta image");
if (!seo.focusKeyword) issues.push("No focus keyword");
if (!seo.openGraph?.title) issues.push("No Open Graph title");
return issues;
}