171 lines
11 KiB
TypeScript
171 lines
11 KiB
TypeScript
import type { Metadata } from 'next'
|
|
import Link from 'next/link'
|
|
import { ArrowLeft } from 'lucide-react'
|
|
import { Footer } from '@/components/layout/Footer'
|
|
import { notFound } from 'next/navigation'
|
|
|
|
const features: Record<string, {
|
|
title: string
|
|
metaDescription: string
|
|
intro: string
|
|
howItWorks: string
|
|
vsAlternatives: string
|
|
faqs: { q: string; a: string }[]
|
|
}> = {
|
|
'noise-filtering': {
|
|
title: 'AI Noise Filtering',
|
|
metaDescription: 'SiteChangeMonitor uses AI to automatically filter out cookie banners, timestamps, rotating ads, and session IDs — delivering zero-noise website change alerts.',
|
|
intro: 'AI Noise Filtering is the core feature that sets SiteChangeMonitor apart. It automatically identifies and filters out irrelevant page changes — cookie banners, footer timestamps, rotating ads, and session-specific content — so you only receive alerts for meaningful updates.',
|
|
howItWorks: 'Our AI analyzes each page change in context. It recognizes common noise patterns (date stamps, consent popups, A/B test variations, ad rotations) and suppresses them automatically. You can also add custom ignore rules using CSS selectors, regex patterns, or plain text matching.',
|
|
vsAlternatives: 'Visualping and Distill.io require manual configuration of ignore rules, which most users skip — leading to constant false alerts. SiteChangeMonitor applies smart noise filtering by default, significantly reducing false positives without any setup.',
|
|
faqs: [
|
|
{ q: 'Does AI filtering work on all websites?', a: 'Yes. Our filtering engine recognizes common noise patterns across all site types — SPAs, e-commerce, news sites, and more. You can also add custom rules for edge cases.' },
|
|
{ q: 'Can I customize what gets filtered?', a: 'Absolutely. You can add custom ignore rules (CSS selectors, regex, text patterns) on top of the automatic AI filtering.' },
|
|
{ q: 'Will I miss important changes?', a: 'No. The AI only filters known noise patterns. Any change it cannot confidently classify as noise is passed through to you.' },
|
|
],
|
|
},
|
|
'visual-diff': {
|
|
title: 'Visual Diff & Screenshots',
|
|
metaDescription: 'See exactly what changed on any web page with side-by-side screenshot diffs. SiteChangeMonitor provides visual proof of every change.',
|
|
intro: 'Visual Diff gives you screenshot-based proof of every website change. Instead of parsing raw HTML diffs, see side-by-side visual comparisons that highlight exactly what changed on the page.',
|
|
howItWorks: 'SiteChangeMonitor captures full-page screenshots on every check. When a change is detected, we generate a visual diff that highlights modified areas. You can compare any two snapshots in your version history.',
|
|
vsAlternatives: 'Distill.io offers text-based diffs only. UptimeRobot has no diff capability. Visualping offers visual diffs but lacks AI noise filtering, so most of the changes shown are irrelevant noise.',
|
|
faqs: [
|
|
{ q: 'What format are the screenshots?', a: 'Screenshots are captured as full-page PNG images and stored with timestamps for audit purposes.' },
|
|
{ q: 'Can I compare any two versions?', a: 'Yes. You can select any two snapshots from the version history and generate a visual diff between them.' },
|
|
{ q: 'Are screenshots included in the free plan?', a: 'Yes, visual diffs are available on all plans including the Forever Free tier.' },
|
|
],
|
|
},
|
|
'keyword-monitoring': {
|
|
title: 'Keyword Monitoring',
|
|
metaDescription: 'Set keyword triggers on any web page. Get alerted when specific words appear, disappear, or cross a count threshold. SiteChangeMonitor keyword monitoring.',
|
|
intro: 'Keyword Monitoring lets you set precise triggers for when specific words or phrases appear, disappear, or cross a count threshold on any monitored page. Ideal for tracking pricing terms, product availability, job postings, and competitive messaging.',
|
|
howItWorks: 'Add one or more keyword rules to any monitor. Choose trigger type: "appears" (word added to page), "disappears" (word removed), or "count" (word frequency crosses a threshold). Supports exact match and regex patterns.',
|
|
vsAlternatives: 'UptimeRobot offers basic keyword checking but no appear/disappear logic. Distill.io supports conditions but requires complex selector configuration. SiteChangeMonitor makes keyword monitoring a first-class feature with a simple UI.',
|
|
faqs: [
|
|
{ q: 'Can I use regex for keyword matching?', a: 'Yes. You can use full regular expressions for complex pattern matching, such as price formats ($XX.XX) or phone numbers.' },
|
|
{ q: 'What is a count threshold trigger?', a: 'Count triggers alert you when a keyword appears more (or fewer) than N times on a page. Useful for tracking inventory counts or job listing volumes.' },
|
|
{ q: 'Can I combine keyword alerts with noise filtering?', a: 'Yes. Noise filtering runs first, then keyword checks run on the cleaned content — ensuring accurate keyword detection.' },
|
|
],
|
|
},
|
|
'seo-ranking': {
|
|
title: 'SEO & Ranking Alerts',
|
|
metaDescription: 'Monitor search engine ranking changes, featured snippet movements, and SERP updates. SiteChangeMonitor alerts SEO teams to ranking shifts.',
|
|
intro: 'SEO & Ranking Alerts help SEO professionals track changes in search engine results pages. Monitor your target keywords for ranking shifts, featured snippet ownership changes, and new competitor appearances.',
|
|
howItWorks: 'Point a monitor at a Google search results URL for your keyword. SiteChangeMonitor captures the SERP from a clean, non-personalized browser session and alerts you when rankings change. AI filtering removes localized variations.',
|
|
vsAlternatives: 'Dedicated SEO tools like Ahrefs and SEMrush track rankings but cost $99+/month and are built for large-scale keyword tracking. SiteChangeMonitor is ideal for focused SERP monitoring at a fraction of the cost.',
|
|
faqs: [
|
|
{ q: 'Does this replace my SEO rank tracker?', a: 'It complements rank trackers by providing instant alerts on SERP changes. Use it for your most important keywords that need real-time monitoring.' },
|
|
{ q: 'How do you handle personalized results?', a: 'We use clean, non-personalized browser sessions from consistent locations to ensure results are not skewed by personal search history.' },
|
|
{ q: 'Can I track featured snippets?', a: 'Yes. Set a keyword trigger for your brand name in the featured snippet area to know instantly when you gain or lose the snippet.' },
|
|
],
|
|
},
|
|
'multi-channel-alerts': {
|
|
title: 'Multi-Channel Alerts',
|
|
metaDescription: 'Get website change notifications via email, Slack, or webhooks. SiteChangeMonitor delivers alerts where your team works.',
|
|
intro: 'Multi-Channel Alerts deliver website change notifications where your team already works — email, Slack, or webhooks. Route different monitors to different channels based on urgency and team.',
|
|
howItWorks: 'Configure alert channels per monitor or globally. Each channel can have its own rules: immediate alerts for critical monitors, daily digests for informational ones. Webhooks support custom payloads for integration with any system.',
|
|
vsAlternatives: 'Visualping restricts Slack integration to enterprise plans. Distill.io supports basic notifications but lacks channel routing. SiteChangeMonitor includes Slack and webhook channels on Pro plans and above, with email alerts on every plan.',
|
|
faqs: [
|
|
{ q: 'Is Slack included in the free plan?', a: 'Slack and webhook integrations are available on Pro and Business plans. The free plan includes email notifications.' },
|
|
{ q: 'Can I set up digest emails?', a: 'Yes. Choose between instant alerts and daily or weekly digest emails that summarize all changes across your monitors.' },
|
|
{ q: 'Do webhooks support custom payloads?', a: 'Yes. You can customize the webhook payload format to integrate with any system — Zapier, Make, n8n, or your own API.' },
|
|
],
|
|
},
|
|
}
|
|
|
|
export function generateStaticParams() {
|
|
return Object.keys(features).map((slug) => ({ slug }))
|
|
}
|
|
|
|
export function generateMetadata({ params }: { params: { slug: string } }): Metadata {
|
|
const data = features[params.slug]
|
|
if (!data) return {}
|
|
return {
|
|
title: data.title,
|
|
description: data.metaDescription,
|
|
alternates: { canonical: `/features/${params.slug}` },
|
|
openGraph: { title: data.title, description: data.metaDescription, url: `/features/${params.slug}` },
|
|
}
|
|
}
|
|
|
|
export default function FeaturePage({ params }: { params: { slug: string } }) {
|
|
const data = features[params.slug]
|
|
if (!data) notFound()
|
|
|
|
const faqJsonLd = {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'FAQPage',
|
|
mainEntity: data.faqs.map((faq) => ({
|
|
'@type': 'Question',
|
|
name: faq.q,
|
|
acceptedAnswer: { '@type': 'Answer', text: faq.a },
|
|
})),
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-background flex flex-col">
|
|
<script
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(faqJsonLd) }}
|
|
/>
|
|
<div className="flex-1 py-24 px-6">
|
|
<div className="mx-auto max-w-4xl space-y-12">
|
|
<div className="space-y-4">
|
|
<Link href="/features" className="inline-flex items-center text-sm text-muted-foreground hover:text-foreground transition-colors">
|
|
<ArrowLeft className="mr-2 h-4 w-4" />
|
|
All Features
|
|
</Link>
|
|
<h1 className="text-4xl md:text-5xl font-bold font-display text-foreground">
|
|
{data.title}
|
|
</h1>
|
|
<p className="text-xl text-muted-foreground max-w-3xl">{data.intro}</p>
|
|
</div>
|
|
|
|
<section>
|
|
<h2 className="text-2xl font-bold text-foreground mb-4">How It Works</h2>
|
|
<p className="text-muted-foreground">{data.howItWorks}</p>
|
|
</section>
|
|
|
|
<section>
|
|
<h2 className="text-2xl font-bold text-foreground mb-4">vs. Alternatives</h2>
|
|
<p className="text-muted-foreground">{data.vsAlternatives}</p>
|
|
</section>
|
|
|
|
<section>
|
|
<h2 className="text-2xl font-bold text-foreground mb-6">FAQ</h2>
|
|
<dl className="space-y-6">
|
|
{data.faqs.map((faq, i) => (
|
|
<div key={i}>
|
|
<dt className="font-medium text-foreground">{faq.q}</dt>
|
|
<dd className="mt-1 text-muted-foreground">{faq.a}</dd>
|
|
</div>
|
|
))}
|
|
</dl>
|
|
</section>
|
|
|
|
{/* CTA */}
|
|
<section className="text-center py-12">
|
|
<h2 className="text-2xl font-bold text-foreground mb-4">Try {data.title}</h2>
|
|
<p className="text-muted-foreground mb-6">Join the waitlist for early access.</p>
|
|
<Link
|
|
href="/"
|
|
className="inline-flex items-center rounded-full bg-primary px-8 py-3 font-medium text-primary-foreground hover:bg-primary/90 transition-colors"
|
|
>
|
|
Join the Waitlist
|
|
</Link>
|
|
</section>
|
|
|
|
{/* Internal Links */}
|
|
<nav className="flex flex-wrap gap-3 text-sm">
|
|
<Link href="/features" className="text-primary hover:underline">All Features</Link>
|
|
<span className="text-muted-foreground">•</span>
|
|
<Link href="/use-cases" className="text-primary hover:underline">Use Cases</Link>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
<Footer />
|
|
</div>
|
|
)
|
|
}
|