import { blogPosts } from "./blog-data"; import { authors } from "./author-data"; import type { BlogPost, PillarKey, AuthorProfile } from "./types"; /** * Blog slugs that are 301-redirected in next.config.mjs. * They must not appear in the blog index, the learn pillars, the sitemap * or IndexNow submissions - linking to a redirect source wastes crawl budget * and passes link equity through an unnecessary hop. * Keep this in sync with the redirects() block in next.config.mjs. */ export const REDIRECTED_BLOG_SLUGS = new Set([ "qr-code-analytics", "qr-code-restaurant-menu", // Duplicate of the standalone /qr-code-print-size-guide, which is the version // Google actually ranks. Both were live and self-canonical until 2026-08-13. "qr-code-print-size-guide", ]); export function isRedirectedSlug(slug: string): boolean { return REDIRECTED_BLOG_SLUGS.has(slug); } export function getPublishedPosts(): BlogPost[] { const currentDate = new Date(); return blogPosts.filter(p => { if (!p.published) return false; if (REDIRECTED_BLOG_SLUGS.has(p.slug)) return false; const publishDate = p.datePublished ? new Date(p.datePublished) : new Date(p.date); return publishDate <= currentDate; }); } export function getPostBySlug(slug: string): BlogPost | undefined { return blogPosts.find(p => p.slug === slug); } export function getPublishedPostBySlug(slug: string): BlogPost | undefined { const p = getPostBySlug(slug); if (!p?.published) return undefined; const currentDate = new Date(); const publishDate = p.datePublished ? new Date(p.datePublished) : new Date(p.date); return publishDate <= currentDate ? p : undefined; } export function getPostsByPillar(pillar: PillarKey): BlogPost[] { return getPublishedPosts() .filter(p => p.pillar === pillar) .sort((a, b) => (new Date(a.datePublished).getTime() < new Date(b.datePublished).getTime() ? 1 : -1)); } export function getAuthorBySlug(slug: string): AuthorProfile | undefined { return authors.find(a => a.slug === slug); } export function getPostsByAuthor(slug: string): BlogPost[] { return getPublishedPosts() .filter(p => p.authorSlug === slug) .sort((a, b) => (new Date(a.datePublished).getTime() < new Date(b.datePublished).getTime() ? 1 : -1)); } export function getRelatedPosts(post: BlogPost, limit = 4): BlogPost[] { const published = getPublishedPosts(); // explicit relatedSlugs first const explicit = (post.relatedSlugs ?? []) .map(s => published.find(p => p.slug === s)) .filter((p): p is BlogPost => !!p); if (explicit.length >= limit) return explicit.slice(0, limit); // fallback: same pillar, not itself, newest const fallback = published .filter(p => p.slug !== post.slug && p.pillar === post.pillar) .slice(0, limit - explicit.length); return [...explicit, ...fallback]; }