Openstatus www.openstatus.dev

refactor: report templates to guides (#1811)

* refactor: report-templates to guides

* fix: cmdk search

authored by

Maximilian Kaske and committed by
GitHub
8ea946d1 e463f837

+94 -87
+37
apps/web/src/app/(landing)/guides/page.tsx
···
··· 1 + import { defaultMetadata, ogMetadata } from "@/app/shared-metadata"; 2 + import { twitterMetadata } from "@/app/shared-metadata"; 3 + import { getGuides } from "@/content/utils"; 4 + import type { Metadata } from "next"; 5 + import { ContentCategory } from "../content-category"; 6 + import { ContentList } from "../content-list"; 7 + 8 + const TITLE = "Guides"; 9 + const DESCRIPTION = "All the latest guides from openstatus."; 10 + 11 + export const metadata: Metadata = { 12 + ...defaultMetadata, 13 + title: TITLE, 14 + description: DESCRIPTION, 15 + openGraph: { 16 + ...ogMetadata, 17 + title: TITLE, 18 + description: DESCRIPTION, 19 + }, 20 + twitter: { 21 + ...twitterMetadata, 22 + title: TITLE, 23 + description: DESCRIPTION, 24 + images: [`/api/og?title=${TITLE}&description=${DESCRIPTION}`], 25 + }, 26 + }; 27 + 28 + export default function GuidesListPage() { 29 + const allGuides = getGuides(); 30 + return ( 31 + <div className="prose dark:prose-invert max-w-none"> 32 + <h1>Guides</h1> 33 + <ContentCategory data={allGuides} prefix="/guides" /> 34 + <ContentList data={allGuides} prefix="/guides" withCategory /> 35 + </div> 36 + ); 37 + }
+10 -10
apps/web/src/app/(landing)/report-template/[slug]/page.tsx apps/web/src/app/(landing)/guides/[slug]/page.tsx
··· 5 getPageMetadata, 6 } from "@/app/shared-metadata"; 7 import { CustomMDX } from "@/content/mdx"; 8 - import { formatDate, getReportTemplates } from "@/content/utils"; 9 import { getAuthor } from "@/data/author"; 10 import type { Metadata } from "next"; 11 import Image from "next/image"; ··· 16 export const dynamicParams = false; 17 18 export async function generateStaticParams() { 19 - const posts = getReportTemplates(); 20 21 return posts.map((post) => ({ 22 slug: post.slug, ··· 29 params: Promise<{ slug: string }>; 30 }): Promise<Metadata | undefined> { 31 const { slug } = await params; 32 - const post = getReportTemplates().find((post) => post.slug === slug); 33 if (!post) { 34 return; 35 } 36 37 - const metadata = getPageMetadata(post, "report-template"); 38 39 return metadata; 40 } 41 42 - export default async function ReportTemplate({ 43 params, 44 }: { 45 params: Promise<{ slug: string }>; 46 }) { 47 const { slug } = await params; 48 - const posts = getReportTemplates().sort( 49 (a, b) => 50 b.metadata.publishedAt.getTime() - a.metadata.publishedAt.getTime(), 51 ); ··· 60 61 const jsonLDBlog: WithContext<BlogPosting> = getJsonLDBlogPosting( 62 post, 63 - "report-template", 64 ); 65 66 const jsonLDBreadcrumb: WithContext<BreadcrumbList> = getJsonLDBreadcrumbList( 67 [ 68 { name: "Home", url: BASE_URL }, 69 - { name: "Report Templates", url: `${BASE_URL}/report-template` }, 70 - { name: post.metadata.title, url: `${BASE_URL}/report-template/${slug}` }, 71 ], 72 ); 73 ··· 108 <ContentPagination 109 previousPost={previousPost} 110 nextPost={nextPost} 111 - prefix="/report-template" 112 /> 113 </section> 114 );
··· 5 getPageMetadata, 6 } from "@/app/shared-metadata"; 7 import { CustomMDX } from "@/content/mdx"; 8 + import { formatDate, getGuides } from "@/content/utils"; 9 import { getAuthor } from "@/data/author"; 10 import type { Metadata } from "next"; 11 import Image from "next/image"; ··· 16 export const dynamicParams = false; 17 18 export async function generateStaticParams() { 19 + const posts = getGuides(); 20 21 return posts.map((post) => ({ 22 slug: post.slug, ··· 29 params: Promise<{ slug: string }>; 30 }): Promise<Metadata | undefined> { 31 const { slug } = await params; 32 + const post = getGuides().find((post) => post.slug === slug); 33 if (!post) { 34 return; 35 } 36 37 + const metadata = getPageMetadata(post, "guides"); 38 39 return metadata; 40 } 41 42 + export default async function Guide({ 43 params, 44 }: { 45 params: Promise<{ slug: string }>; 46 }) { 47 const { slug } = await params; 48 + const posts = getGuides().sort( 49 (a, b) => 50 b.metadata.publishedAt.getTime() - a.metadata.publishedAt.getTime(), 51 ); ··· 60 61 const jsonLDBlog: WithContext<BlogPosting> = getJsonLDBlogPosting( 62 post, 63 + "guides", 64 ); 65 66 const jsonLDBreadcrumb: WithContext<BreadcrumbList> = getJsonLDBreadcrumbList( 67 [ 68 { name: "Home", url: BASE_URL }, 69 + { name: "Guides", url: `${BASE_URL}/guides` }, 70 + { name: post.metadata.title, url: `${BASE_URL}/guides/${slug}` }, 71 ], 72 ); 73 ··· 108 <ContentPagination 109 previousPost={previousPost} 110 nextPost={nextPost} 111 + prefix="/guides" 112 /> 113 </section> 114 );
+10 -10
apps/web/src/app/(landing)/report-template/category/[slug]/page.tsx apps/web/src/app/(landing)/guides/category/[slug]/page.tsx
··· 5 ogMetadata, 6 twitterMetadata, 7 } from "@/app/shared-metadata"; 8 - import { getReportTemplates } from "@/content/utils"; 9 import type { Metadata } from "next"; 10 11 - const TITLE = "Report Template Category"; 12 - const DESCRIPTION = "All the latest templates from openstatus."; 13 14 export const metadata: Metadata = { 15 ...defaultMetadata, ··· 31 export const dynamicParams = false; 32 33 export async function generateStaticParams() { 34 - const posts = getReportTemplates(); 35 const categories = [...new Set(posts.map((post) => post.metadata.category))]; 36 37 return categories.map((category) => ({ ··· 39 })); 40 } 41 42 - export default async function ReportTemplateCategoryPage({ 43 params, 44 }: { 45 params: Promise<{ slug: string }>; 46 }) { 47 const { slug } = await params; 48 - const allReportTemplates = getReportTemplates(); 49 - const filteredReportTemplates = allReportTemplates.filter( 50 (post) => post.metadata.category.toLowerCase() === slug.toLowerCase(), 51 ); 52 53 return ( 54 <div className="prose dark:prose-invert max-w-none"> 55 - <h1 className="capitalize">Report Template | {slug}</h1> 56 - <ContentCategory data={allReportTemplates} prefix="/report-template" /> 57 - <ContentList data={filteredReportTemplates} prefix="/report-template" /> 58 </div> 59 ); 60 }
··· 5 ogMetadata, 6 twitterMetadata, 7 } from "@/app/shared-metadata"; 8 + import { getGuides } from "@/content/utils"; 9 import type { Metadata } from "next"; 10 11 + const TITLE = "Guide Category"; 12 + const DESCRIPTION = "All the latest guides from openstatus."; 13 14 export const metadata: Metadata = { 15 ...defaultMetadata, ··· 31 export const dynamicParams = false; 32 33 export async function generateStaticParams() { 34 + const posts = getGuides(); 35 const categories = [...new Set(posts.map((post) => post.metadata.category))]; 36 37 return categories.map((category) => ({ ··· 39 })); 40 } 41 42 + export default async function GuideCategoryPage({ 43 params, 44 }: { 45 params: Promise<{ slug: string }>; 46 }) { 47 const { slug } = await params; 48 + const allGuides = getGuides(); 49 + const filteredGuides = allGuides.filter( 50 (post) => post.metadata.category.toLowerCase() === slug.toLowerCase(), 51 ); 52 53 return ( 54 <div className="prose dark:prose-invert max-w-none"> 55 + <h1 className="capitalize">Guides | {slug}</h1> 56 + <ContentCategory data={allGuides} prefix="/guides" /> 57 + <ContentList data={filteredGuides} prefix="/guides" /> 58 </div> 59 ); 60 }
-41
apps/web/src/app/(landing)/report-template/page.tsx
··· 1 - import { defaultMetadata, ogMetadata } from "@/app/shared-metadata"; 2 - import { twitterMetadata } from "@/app/shared-metadata"; 3 - import { getReportTemplates } from "@/content/utils"; 4 - import type { Metadata } from "next"; 5 - import { ContentCategory } from "../content-category"; 6 - import { ContentList } from "../content-list"; 7 - 8 - const TITLE = "Report Template"; 9 - const DESCRIPTION = "All the latest templates from openstatus."; 10 - 11 - export const metadata: Metadata = { 12 - ...defaultMetadata, 13 - title: TITLE, 14 - description: DESCRIPTION, 15 - openGraph: { 16 - ...ogMetadata, 17 - title: TITLE, 18 - description: DESCRIPTION, 19 - }, 20 - twitter: { 21 - ...twitterMetadata, 22 - title: TITLE, 23 - description: DESCRIPTION, 24 - images: [`/api/og?title=${TITLE}&description=${DESCRIPTION}`], 25 - }, 26 - }; 27 - 28 - export default function ReportTemplateListPage() { 29 - const allReportTemplates = getReportTemplates(); 30 - return ( 31 - <div className="prose dark:prose-invert max-w-none"> 32 - <h1>Report Templates</h1> 33 - <ContentCategory data={allReportTemplates} prefix="/report-template" /> 34 - <ContentList 35 - data={allReportTemplates} 36 - prefix="/report-template" 37 - withCategory 38 - /> 39 - </div> 40 - ); 41 - }
···
+1
apps/web/src/app/api/search/route.ts
··· 70 ...getPages("tools").filter((tool) => tool.slug !== "checker-slug"), 71 ...getPages("compare"), 72 ...getPages("product"), 73 home, 74 ]; 75 } else {
··· 70 ...getPages("tools").filter((tool) => tool.slug !== "checker-slug"), 71 ...getPages("compare"), 72 ...getPages("product"), 73 + ...getPages("guides"), 74 home, 75 ]; 76 } else {
+6 -6
apps/web/src/app/sitemap.ts
··· 2 getBlogPosts, 3 getChangelogPosts, 4 getComparePages, 5 getHomePage, 6 getProductPages, 7 - getReportTemplates, 8 getToolsPages, 9 getUnrelatedPages, 10 } from "@/content/utils"; ··· 20 const allPlaygrounds = getToolsPages().filter( 21 (tool) => tool.slug !== "checker-slug", 22 ); 23 - const allReportTemplates = getReportTemplates(); 24 25 export default function sitemap(): MetadataRoute.Sitemap { 26 const blogs = allPosts.map((post) => ({ ··· 65 priority: 0.6, 66 })); 67 68 - const reportTemplates = allReportTemplates.map((reportTemplate) => ({ 69 - url: `https://www.openstatus.dev/report-template/${reportTemplate.slug}`, 70 - lastModified: reportTemplate.metadata.publishedAt, 71 changeFrequency: "monthly" as const, 72 priority: 0.6, 73 })); ··· 89 ...landings, 90 ...products, 91 ...playgrounds, 92 - ...reportTemplates, 93 ]; 94 }
··· 2 getBlogPosts, 3 getChangelogPosts, 4 getComparePages, 5 + getGuides, 6 getHomePage, 7 getProductPages, 8 getToolsPages, 9 getUnrelatedPages, 10 } from "@/content/utils"; ··· 20 const allPlaygrounds = getToolsPages().filter( 21 (tool) => tool.slug !== "checker-slug", 22 ); 23 + const allGuides = getGuides(); 24 25 export default function sitemap(): MetadataRoute.Sitemap { 26 const blogs = allPosts.map((post) => ({ ··· 65 priority: 0.6, 66 })); 67 68 + const guides = allGuides.map((guide) => ({ 69 + url: `https://www.openstatus.dev/guides/${guide.slug}`, 70 + lastModified: guide.metadata.publishedAt, 71 changeFrequency: "monthly" as const, 72 priority: 0.6, 73 })); ··· 89 ...landings, 90 ...products, 91 ...playgrounds, 92 + ...guides, 93 ]; 94 }
+6
apps/web/src/content/cmdk.tsx
··· 107 page: "compare", 108 }, 109 { 110 type: "item", 111 label: "Go to About", 112 href: "/about",
··· 107 page: "compare", 108 }, 109 { 110 + type: "group", 111 + label: "Search in Guides...", 112 + heading: "Guides", 113 + page: "guides", 114 + }, 115 + { 116 type: "item", 117 label: "Go to About", 118 href: "/about",
+1 -1
apps/web/src/content/pages/blog/top-five-atlassian-statuspage-alternatives.mdx apps/web/src/content/pages/guides/top-five-atlassian-statuspage-alternatives.mdx
··· 4 description: "Explore the best alternatives to Atlassian Statuspage including OpenStatus, Status-io, Datadog Status Page, Instatus, and Betterstack." 5 author: "Thibault Le Ouay Ducasse" 6 publishedAt: "2025-11-05" 7 - category: "guide" 8 --- 9 10 Atlassian Statuspage has long been a go-to choice for organizations needing to communicate their service status. It's a robust, mature platform. However, the service reliability and incident communication landscape is evolving rapidly.
··· 4 description: "Explore the best alternatives to Atlassian Statuspage including OpenStatus, Status-io, Datadog Status Page, Instatus, and Betterstack." 5 author: "Thibault Le Ouay Ducasse" 6 publishedAt: "2025-11-05" 7 + category: "alternative" 8 --- 9 10 Atlassian Statuspage has long been a go-to choice for organizations needing to communicate their service status. It's a robust, mature platform. However, the service reliability and incident communication landscape is evolving rapidly.
+2 -2
apps/web/src/content/pages/report-template/api-service-disruption.mdx apps/web/src/content/pages/guides/api-service-disruption.mdx
··· 1 --- 2 title: "API Service Disruption Template" 3 description: "Professional template for communicating API outages and third-party service failures to your users." 4 - author: "OpenStatus" 5 publishedAt: "2026-01-19" 6 - category: "infrastructure" 7 --- 8 9 Use this template when your API or third-party service provider is experiencing issues. Based on real-world examples from companies like Vercel, Stripe, and GitHub.
··· 1 --- 2 title: "API Service Disruption Template" 3 description: "Professional template for communicating API outages and third-party service failures to your users." 4 + author: "openstatus" 5 publishedAt: "2026-01-19" 6 + category: "template" 7 --- 8 9 Use this template when your API or third-party service provider is experiencing issues. Based on real-world examples from companies like Vercel, Stripe, and GitHub.
+2 -2
apps/web/src/content/pages/report-template/database-performance-degradation.mdx apps/web/src/content/pages/guides/database-performance-degradation.mdx
··· 1 --- 2 title: "Database Performance Degradation Template" 3 description: "Technical template for communicating database latency and performance issues to your engineering-focused audience." 4 - author: "OpenStatus" 5 publishedAt: "2026-01-19" 6 - category: "performance" 7 --- 8 9 Use this template when experiencing database performance issues, elevated latency, or query slowdowns. Ideal for engineering teams and technical stakeholders.
··· 1 --- 2 title: "Database Performance Degradation Template" 3 description: "Technical template for communicating database latency and performance issues to your engineering-focused audience." 4 + author: "openstatus" 5 publishedAt: "2026-01-19" 6 + category: "template" 7 --- 8 9 Use this template when experiencing database performance issues, elevated latency, or query slowdowns. Ideal for engineering teams and technical stakeholders.
+2 -2
apps/web/src/content/pages/report-template/deployment-rollback.mdx apps/web/src/content/pages/guides/deployment-rollback.mdx
··· 1 --- 2 title: "Deployment Rollback Template" 3 description: "Template for communicating deployment failures and rollback procedures during service disruptions." 4 - author: "OpenStatus" 5 publishedAt: "2026-01-19" 6 - category: "deployment" 7 --- 8 9 Use this template when a deployment causes issues and requires rolling back to a previous version. Communicates the issue, mitigation, and resolution clearly.
··· 1 --- 2 title: "Deployment Rollback Template" 3 description: "Template for communicating deployment failures and rollback procedures during service disruptions." 4 + author: "openstatus" 5 publishedAt: "2026-01-19" 6 + category: "template" 7 --- 8 9 Use this template when a deployment causes issues and requires rolling back to a previous version. Communicates the issue, mitigation, and resolution clearly.
+2 -2
apps/web/src/content/pages/report-template/feature-degradation.mdx apps/web/src/content/pages/guides/feature-degradation.mdx
··· 1 --- 2 title: "Feature Degradation Template" 3 description: "Template for communicating partial service degradation where some features are impacted but the service remains available." 4 - author: "OpenStatus" 5 publishedAt: "2026-01-19" 6 - category: "performance" 7 --- 8 9 Use this template when specific features are degraded or unavailable, but the core service remains operational. Helps users understand what's working and what isn't.
··· 1 --- 2 title: "Feature Degradation Template" 3 description: "Template for communicating partial service degradation where some features are impacted but the service remains available." 4 + author: "openstatus" 5 publishedAt: "2026-01-19" 6 + category: "template" 7 --- 8 9 Use this template when specific features are degraded or unavailable, but the core service remains operational. Helps users understand what's working and what isn't.
+2 -2
apps/web/src/content/pages/report-template/network-connectivity-issues.mdx apps/web/src/content/pages/guides/network-connectivity-issues.mdx
··· 1 --- 2 title: "Network Connectivity Issues Template" 3 description: "Template for communicating network outages, CDN problems, and regional connectivity issues to affected users." 4 - author: "OpenStatus" 5 publishedAt: "2026-01-19" 6 - category: "infrastructure" 7 --- 8 9 Use this template when experiencing network-related issues affecting user connectivity, including CDN problems, regional outages, or ISP issues.
··· 1 --- 2 title: "Network Connectivity Issues Template" 3 description: "Template for communicating network outages, CDN problems, and regional connectivity issues to affected users." 4 + author: "openstatus" 5 publishedAt: "2026-01-19" 6 + category: "template" 7 --- 8 9 Use this template when experiencing network-related issues affecting user connectivity, including CDN problems, regional outages, or ISP issues.
+2 -2
apps/web/src/content/pages/report-template/scheduled-maintenance.mdx apps/web/src/content/pages/guides/scheduled-maintenance.mdx
··· 1 --- 2 title: "Scheduled Maintenance Template" 3 description: "Proactive template for communicating planned maintenance windows, upgrades, and scheduled downtime to users." 4 - author: "OpenStatus" 5 publishedAt: "2026-01-19" 6 - category: "maintenance" 7 --- 8 9 Use this template for planned maintenance, infrastructure upgrades, or scheduled downtime. Proactive communication builds trust and reduces support burden.
··· 1 --- 2 title: "Scheduled Maintenance Template" 3 description: "Proactive template for communicating planned maintenance windows, upgrades, and scheduled downtime to users." 4 + author: "openstatus" 5 publishedAt: "2026-01-19" 6 + category: "template" 7 --- 8 9 Use this template for planned maintenance, infrastructure upgrades, or scheduled downtime. Proactive communication builds trust and reduces support burden.
+2 -2
apps/web/src/content/pages/report-template/security-incident-response.mdx apps/web/src/content/pages/guides/security-incident-response.mdx
··· 1 --- 2 title: "Security Incident Response Template" 3 description: "Formal template for communicating security issues with professional, reassuring language that maintains user trust." 4 - author: "OpenStatus" 5 publishedAt: "2026-01-19" 6 - category: "security" 7 --- 8 9 Use this template when investigating or responding to security-related incidents. Maintains professional tone while reassuring users about security measures.
··· 1 --- 2 title: "Security Incident Response Template" 3 description: "Formal template for communicating security issues with professional, reassuring language that maintains user trust." 4 + author: "openstatus" 5 publishedAt: "2026-01-19" 6 + category: "template" 7 --- 8 9 Use this template when investigating or responding to security-related incidents. Maintains professional tone while reassuring users about security measures.
+7 -3
apps/web/src/content/utils.ts
··· 91 ); 92 } 93 94 - export function getReportTemplates(): MDXData[] { 95 return getMDXDataFromDir( 96 - path.join(process.cwd(), "src", "content", "pages", "report-template"), 97 - "/report-template", 98 ); 99 } 100 ··· 158 "unrelated", 159 "compare", 160 "tools", 161 "all", 162 ] as const; 163 ··· 177 return getComparePages(); 178 case "tools": 179 return getToolsPages(); 180 case "all": 181 return [ 182 ...getBlogPosts(), ··· 185 ...getUnrelatedPages(), 186 ...getComparePages(), 187 ...getToolsPages(), 188 ]; 189 default: 190 throw new Error(`Unknown page type: ${type}`);
··· 91 ); 92 } 93 94 + export function getGuides(): MDXData[] { 95 return getMDXDataFromDir( 96 + path.join(process.cwd(), "src", "content", "pages", "guides"), 97 + "/guides", 98 ); 99 } 100 ··· 158 "unrelated", 159 "compare", 160 "tools", 161 + "guides", 162 "all", 163 ] as const; 164 ··· 178 return getComparePages(); 179 case "tools": 180 return getToolsPages(); 181 + case "guides": 182 + return getGuides(); 183 case "all": 184 return [ 185 ...getBlogPosts(), ··· 188 ...getUnrelatedPages(), 189 ...getComparePages(), 190 ...getToolsPages(), 191 + ...getGuides(), 192 ]; 193 default: 194 throw new Error(`Unknown page type: ${type}`);
+2 -2
apps/web/src/data/content.ts
··· 31 href: "https://docs.openstatus.dev", 32 }, 33 { 34 - label: "Report Templates", 35 - href: "/report-template", 36 }, 37 { 38 label: "External Status",
··· 31 href: "https://docs.openstatus.dev", 32 }, 33 { 34 + label: "Guides", 35 + href: "/guides", 36 }, 37 { 38 label: "External Status",