From 9dc55517b1aeef2010a92a7d30c91c9fb4243af8 Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Mon, 25 May 2026 15:26:01 -0400 Subject: [PATCH] 08: Migrate & redesign Blog, Ads, and Dashboard pages - Blog listing page with hero, responsive grid, tag filters, load more - Blog post page with markdown rendering, related posts, social share - Ads landing page with conversion copy, pricing, FAQ, testimonials - Dashboard shell with sidebar, topbar, stat cards, activity feed - Dashboard components: Sidebar, TopBar, StatCard, ActivityFeed, QuickActions - Comprehensive test suite covering all pages and components --- web/src/components/dashboard/ActivityFeed.tsx | 80 ++++ web/src/components/dashboard/QuickActions.tsx | 40 ++ web/src/components/dashboard/Sidebar.tsx | 146 +++++++ web/src/components/dashboard/StatCard.tsx | 54 +++ web/src/components/dashboard/TopBar.tsx | 91 +++++ web/src/components/dashboard/index.ts | 5 + web/src/routes/(webapp)/dashboard.tsx | 98 +++++ web/src/routes/ads.tsx | 326 ++++++++++++++++ web/src/routes/blog.tsx | 196 ++++++++++ web/src/routes/blog/[slug].tsx | 363 ++++++++++++++++++ web/src/routes/migrated-pages.test.tsx | 266 +++++++++++++ 11 files changed, 1665 insertions(+) create mode 100644 web/src/components/dashboard/ActivityFeed.tsx create mode 100644 web/src/components/dashboard/QuickActions.tsx create mode 100644 web/src/components/dashboard/Sidebar.tsx create mode 100644 web/src/components/dashboard/StatCard.tsx create mode 100644 web/src/components/dashboard/TopBar.tsx create mode 100644 web/src/components/dashboard/index.ts create mode 100644 web/src/routes/(webapp)/dashboard.tsx create mode 100644 web/src/routes/ads.tsx create mode 100644 web/src/routes/blog.tsx create mode 100644 web/src/routes/blog/[slug].tsx create mode 100644 web/src/routes/migrated-pages.test.tsx diff --git a/web/src/components/dashboard/ActivityFeed.tsx b/web/src/components/dashboard/ActivityFeed.tsx new file mode 100644 index 0000000..a9a1925 --- /dev/null +++ b/web/src/components/dashboard/ActivityFeed.tsx @@ -0,0 +1,80 @@ +import { For, Show } from "solid-js"; +import { cn } from "~/lib/utils"; +import { Badge } from "~/components/ui"; + +interface Activity { + id: string; + title: string; + description: string; + timestamp: string; + type: "alert" | "success" | "info" | "warning"; +} + +interface ActivityFeedProps { + activities: Activity[]; + class?: string; +} + +const typeVariants: Record = { + alert: "error", + success: "success", + info: "info", + warning: "warning", +}; + +function ActivityIcon(props: { type: Activity["type"] }) { + return ( +
+ + + + + + + + + + + + + + + + + +
+ ); +} + +export default function ActivityFeed(props: ActivityFeedProps) { + return ( +
+
+

Recent Activity

+
+
+ + {(activity) => ( +
+ +
+
+ {activity.title} + {activity.type} +
+

{activity.description}

+
+ {activity.timestamp} +
+ )} +
+
+
+ ); +} diff --git a/web/src/components/dashboard/QuickActions.tsx b/web/src/components/dashboard/QuickActions.tsx new file mode 100644 index 0000000..e1a02e6 --- /dev/null +++ b/web/src/components/dashboard/QuickActions.tsx @@ -0,0 +1,40 @@ +import { For, type JSX } from "solid-js"; +import { A } from "@solidjs/router"; +import { cn } from "~/lib/utils"; + +interface QuickAction { + label: string; + href: string; + icon: () => JSX.Element; +} + +interface QuickActionsProps { + actions: QuickAction[]; + class?: string; +} + +export default function QuickActions(props: QuickActionsProps) { + return ( +
+
+

Quick Actions

+
+
+ + {(action) => { + const Icon = action.icon; + return ( + + + {action.label} + + ); + }} + +
+
+ ); +} diff --git a/web/src/components/dashboard/Sidebar.tsx b/web/src/components/dashboard/Sidebar.tsx new file mode 100644 index 0000000..fd49860 --- /dev/null +++ b/web/src/components/dashboard/Sidebar.tsx @@ -0,0 +1,146 @@ +import { createSignal, Show, type JSX } from "solid-js"; +import { A, useLocation } from "@solidjs/router"; +import { cn } from "~/lib/utils"; + +interface SidebarLink { + label: string; + href: string; + icon: () => JSX.Element; +} + +function OverviewIcon() { + return ( + + + + + + + ); +} + +function DarkWatchIcon() { + return ( + + + + ); +} + +function VoicePrintIcon() { + return ( + + + + ); +} + +function SpamShieldIcon() { + return ( + + + + ); +} + +function HomeTitleIcon() { + return ( + + + + ); +} + +function RemoveBrokersIcon() { + return ( + + + + ); +} + +function SettingsIcon() { + return ( + + + + + + + + ); +} + +const sidebarLinks: SidebarLink[] = [ + { label: "Overview", href: "/dashboard", icon: OverviewIcon }, + { label: "DarkWatch", href: "/dashboard/darkwatch", icon: DarkWatchIcon }, + { label: "VoicePrint", href: "/dashboard/voiceprint", icon: VoicePrintIcon }, + { label: "SpamShield", href: "/dashboard/spamshield", icon: SpamShieldIcon }, + { label: "HomeTitle", href: "/dashboard/hometitle", icon: HomeTitleIcon }, + { label: "RemoveBrokers", href: "/dashboard/removebrokers", icon: RemoveBrokersIcon }, + { label: "Settings", href: "/dashboard/settings", icon: SettingsIcon }, +]; + +interface SidebarProps { + open: boolean; + onClose: () => void; +} + +export default function Sidebar(props: SidebarProps) { + const location = useLocation(); + + return ( + <> + + +
props.onClose()} + /> + + + ); +} diff --git a/web/src/components/dashboard/StatCard.tsx b/web/src/components/dashboard/StatCard.tsx new file mode 100644 index 0000000..e1a6429 --- /dev/null +++ b/web/src/components/dashboard/StatCard.tsx @@ -0,0 +1,54 @@ +import { Show, type JSX } from "solid-js"; +import { cn } from "~/lib/utils"; + +interface StatCardProps { + label: string; + value: string; + trend?: "up" | "down"; + trendLabel?: string; + icon: () => JSX.Element; + class?: string; +} + +export default function StatCard(props: StatCardProps) { + const Icon = props.icon; + return ( +
+
+ {props.label} +
+ +
+
+
{props.value}
+ +
+ + + + + {props.trendLabel} + +
+
+
+ ); +} diff --git a/web/src/components/dashboard/TopBar.tsx b/web/src/components/dashboard/TopBar.tsx new file mode 100644 index 0000000..41d1b56 --- /dev/null +++ b/web/src/components/dashboard/TopBar.tsx @@ -0,0 +1,91 @@ +import { createSignal, Show } from "solid-js"; +import { A } from "@solidjs/router"; +import { cn } from "~/lib/utils"; + +interface TopBarProps { + onMenuToggle: () => void; +} + +export default function TopBar(props: TopBarProps) { + const [showDropdown, setShowDropdown] = createSignal(false); + + return ( +
+
+ + +
+ +
+ + +
+ + + + <> +
setShowDropdown(false)} + /> +
+
+

John Doe

+

john@shieldai.app

+
+ setShowDropdown(false)}> + Settings + + +
+ + +
+
+
+ ); +} diff --git a/web/src/components/dashboard/index.ts b/web/src/components/dashboard/index.ts new file mode 100644 index 0000000..0ae59de --- /dev/null +++ b/web/src/components/dashboard/index.ts @@ -0,0 +1,5 @@ +export { default as Sidebar } from "./Sidebar"; +export { default as TopBar } from "./TopBar"; +export { default as StatCard } from "./StatCard"; +export { default as ActivityFeed } from "./ActivityFeed"; +export { default as QuickActions } from "./QuickActions"; diff --git a/web/src/routes/(webapp)/dashboard.tsx b/web/src/routes/(webapp)/dashboard.tsx new file mode 100644 index 0000000..069f01d --- /dev/null +++ b/web/src/routes/(webapp)/dashboard.tsx @@ -0,0 +1,98 @@ +import { createSignal, For } from "solid-js"; +import { Title } from "@solidjs/meta"; +import { Sidebar, TopBar, StatCard, ActivityFeed, QuickActions } from "~/components/dashboard"; + +function AlertsIcon() { + return ( + + + + + ); +} + +function ShieldIcon() { + return ( + + + + + ); +} + +function EyeIcon() { + return ( + + + + + ); +} + +function ActivityIcon() { + return ( + + + + ); +} + +const statCards = [ + { label: "Active Threats", value: "3", trend: "down" as const, trendLabel: "2 fewer than yesterday", icon: AlertsIcon }, + { label: "Protected Accounts", value: "12", trend: "up" as const, trendLabel: "2 new this week", icon: ShieldIcon }, + { label: "Dark Web Scans", value: "1,847", trend: "up" as const, trendLabel: "12% increase", icon: EyeIcon }, + { label: "Alerts Today", value: "7", trend: "down" as const, trendLabel: "3 fewer than yesterday", icon: ActivityIcon }, +]; + +const activities = [ + { id: "1", title: "New credential leak detected", description: "Your email was found in a data breach on a dark web forum", timestamp: "5m ago", type: "alert" as const }, + { id: "2", title: "VoicePrint scan completed", description: "No deepfake voice activity detected in the last 24 hours", timestamp: "1h ago", type: "success" as const }, + { id: "3", title: "RemoveBroker opt-out confirmed", description: "Your data has been removed from Whitepages", timestamp: "3h ago", type: "info" as const }, + { id: "4", title: "Suspicious call blocked", description: "SpamShield blocked a call from an known scam number", timestamp: "6h ago", type: "warning" as const }, + { id: "5", title: "HomeTitle alert", description: "A document was filed against your property address", timestamp: "1d ago", type: "alert" as const }, +]; + +const quickActions = [ + { label: "Run Scan", href: "/dashboard/darkwatch", icon: () => }, + { label: "View Alerts", href: "/dashboard", icon: () => }, + { label: "Add Member", href: "/dashboard/settings", icon: () => }, + { label: "Run Report", href: "/dashboard", icon: () => }, +]; + +export default function DashboardPage() { + const [sidebarOpen, setSidebarOpen] = createSignal(false); + + return ( +
+ Dashboard — ShieldAI + setSidebarOpen(false)} /> +
+ setSidebarOpen(v => !v)} /> +
+
+

Overview

+ +
+ + {(card) => ( + + )} + +
+ +
+ + +
+
+
+
+
+ ); +} diff --git a/web/src/routes/ads.tsx b/web/src/routes/ads.tsx new file mode 100644 index 0000000..185229f --- /dev/null +++ b/web/src/routes/ads.tsx @@ -0,0 +1,326 @@ +import { createSignal, For, Show } from "solid-js"; +import { Title } from "@solidjs/meta"; +import { A, useSearchParams } from "@solidjs/router"; +import { cn } from "~/lib/utils"; +import { Badge, Button, Card } from "~/components/ui"; +import PageContainer from "~/components/layout/PageContainer"; + +const plans = [ + { + name: "Basic", + price: "$9", + period: "/month", + description: "Essential identity protection for individuals", + features: ["Dark web monitoring", "Email breach alerts", "Basic scam call blocking", "Monthly reports"], + cta: "Start Free Trial", + popular: false, + }, + { + name: "Plus", + price: "$19", + period: "/month", + description: "Advanced protection for you and your family", + features: ["Everything in Basic", "VoicePrint AI detection", "HomeTitle fraud alerts", "RemoveBrokers automation", "Family sharing (up to 5)"], + cta: "Start Free Trial", + popular: true, + }, + { + name: "Premium", + price: "$39", + period: "/month", + description: "Maximum security for the whole household", + features: ["Everything in Plus", "Unlimited family members", "Priority support 24/7", "Real-time alert correlation", "Advanced analytics dashboard", "Data broker suppression"], + cta: "Start Free Trial", + popular: false, + }, +]; + +const faqs = [ + { + q: "How does ShieldAI detect voice clones?", + a: "VoicePrint analyzes over 200 acoustic features in real-time, including micro-tremors and breathing patterns that AI clones can't replicate accurately.", + }, + { + q: "Is my data encrypted?", + a: "Yes. All data is encrypted at rest using AES-256 and in transit using TLS 1.3. We never share or sell your personal information.", + }, + { + q: "Can I protect my whole family?", + a: "Absolutely. Plus and Premium plans include family sharing with centralized monitoring and alert management for all household members.", + }, + { + q: "How does dark web monitoring work?", + a: "DarkWatch continuously scans dark web forums, marketplaces, and data dumps for your email addresses, phone numbers, and other personal data.", + }, + { + q: "What happens after my free trial?", + a: "Your trial includes full access to your selected plan for 14 days. You can cancel anytime before the trial ends with no charge.", + }, + { + q: "Can I remove my data from brokers?", + a: "Yes. RemoveBrokers automates opt-out requests to over 200 data broker sites and verifies removal on your behalf.", + }, +]; + +export default function AdsPage() { + const [searchParams] = useSearchParams(); + const [openFaq, setOpenFaq] = createSignal(null); + + return ( +
+ ShieldAI — Stop AI Scams Before They Reach You + +
+
+ +
+ AI-Powered Protection +

+ Stop AI Scams Before{" "} + They Reach You +

+

+ ShieldAI uses advanced artificial intelligence to detect and block voice clones, dark web leaks, and identity threats in real-time. +

+ +
+ + + 14-day free trial + + + + No credit card required + + + + Cancel anytime + +
+
+
+
+ +
+ +
+
+
99.7%
+

Voice clone detection accuracy

+
+
+
200+
+

Dark web sources monitored

+
+
+
50K+
+

Threats blocked daily

+
+
+
+
+ +
+ +
+

+ Simple, Transparent Pricing +

+

+ Start with a 14-day free trial. No credit card required. Cancel anytime. +

+
+
+ + {(plan) => ( + + +
+ Most Popular +
+
+
+

{plan.name}

+

{plan.description}

+
+ {plan.price} + {plan.period} +
+
+
    + + {(feature) => ( +
  • + + + + {feature} +
  • + )} +
    +
+ + + +
+ )} +
+
+
+
+ +
+ +
+

+ Trusted by Thousands +

+

+ See what our users say about ShieldAI +

+
+
+ +
+ {[1, 2, 3, 4, 5].map(() => ( + + + + ))} +
+

+ "VoicePrint caught a deepfake call from someone impersonating my CEO. This technology is incredible." +

+
+
JD
+
+

James D.

+

CEO, TechStart

+
+
+
+ +
+ {[1, 2, 3, 4, 5].map(() => ( + + + + ))} +
+

+ "DarkWatch alerted me that my email was in a data breach within hours. ShieldAI saved me from a potential hack." +

+
+
MK
+
+

Maria K.

+

Freelancer

+
+
+
+ +
+ {[1, 2, 3, 4, 5].map(() => ( + + + + ))} +
+

+ "The family plan is a game-changer. I can monitor my parents' and kids' accounts from one dashboard." +

+
+
TR
+
+

Tom R.

+

Father of 3

+
+
+
+
+
+
+ +
+ +
+
+

+ Frequently Asked Questions +

+
+
+ + {(faq) => { + const isOpen = () => openFaq() === faq.q; + return ( +
+ + +
+ {faq.a} +
+
+
+ ); + }} +
+
+
+
+
+ +
+ +
+

+ Ready to protect your identity? +

+

+ Join 50,000+ users who trust ShieldAI for AI-powered identity protection. +

+ + + +
+
+
+
+ ); +} diff --git a/web/src/routes/blog.tsx b/web/src/routes/blog.tsx new file mode 100644 index 0000000..89bd312 --- /dev/null +++ b/web/src/routes/blog.tsx @@ -0,0 +1,196 @@ +import { createSignal, For, Show } from "solid-js"; +import { Title } from "@solidjs/meta"; +import { A } from "@solidjs/router"; +import { cn } from "~/lib/utils"; +import { Badge, Button, Card } from "~/components/ui"; +import PageContainer from "~/components/layout/PageContainer"; + +interface BlogPost { + slug: string; + title: string; + excerpt: string; + author: string; + date: string; + readingTime: string; + coverImage: string; + tags: string[]; +} + +const allTags = ["Identity Theft", "AI Safety", "Privacy", "Deepfakes", "Dark Web", "Scam Alerts", "Product News"]; + +const blogPosts: BlogPost[] = [ + { + slug: "ai-scam-trends-2026", + title: "AI Scam Trends to Watch in 2026", + excerpt: "As AI technology advances, scammers are finding new ways to exploit it. Here are the top threats to watch and how to protect yourself.", + author: "Sarah Chen", + date: "May 15, 2026", + readingTime: "5 min read", + coverImage: "", + tags: ["AI Safety", "Scam Alerts", "Deepfakes"], + }, + { + slug: "dark-web-monitoring-guide", + title: "The Complete Guide to Dark Web Monitoring", + excerpt: "Learn how dark web monitoring works, what data gets exposed, and how ShieldAI keeps your information safe from cybercriminals.", + author: "Mike Reynolds", + date: "May 10, 2026", + readingTime: "8 min read", + coverImage: "", + tags: ["Dark Web", "Privacy"], + }, + { + slug: "protecting-family-identity", + title: "Protecting Your Family's Digital Identity", + excerpt: "Your family's personal data is at risk. Discover the steps you can take to protect everyone in your household from identity theft.", + author: "Emily Torres", + date: "May 5, 2026", + readingTime: "6 min read", + coverImage: "", + tags: ["Identity Theft", "Privacy"], + }, + { + slug: "deepfake-voice-scams", + title: "Deepfake Voice Scams: How They Work and How to Spot Them", + excerpt: "AI-generated voice clones are being used to impersonate loved ones. Here's what to listen for and how ShieldAI's VoicePrint can help.", + author: "Sarah Chen", + date: "April 28, 2026", + readingTime: "7 min read", + coverImage: "", + tags: ["Deepfakes", "AI Safety", "Scam Alerts"], + }, + { + slug: "what-is-data-broker", + title: "What Is a Data Broker and How to Remove Your Information", + excerpt: "Data brokers collect and sell your personal information. Find out how to opt out and reclaim your privacy with RemoveBrokers.", + author: "Alex Kim", + date: "April 20, 2026", + readingTime: "4 min read", + coverImage: "", + tags: ["Privacy", "Identity Theft"], + }, + { + slug: "shieldai-product-update-may-2026", + title: "ShieldAI Product Update — May 2026", + excerpt: "New features including improved VoicePrint detection, expanded dark web monitoring, and a redesigned dashboard experience.", + author: "Product Team", + date: "April 15, 2026", + readingTime: "3 min read", + coverImage: "", + tags: ["Product News"], + }, +]; + +const POSTS_PER_PAGE = 4; + +export default function BlogPage() { + const [selectedTag, setSelectedTag] = createSignal(null); + const [visibleCount, setVisibleCount] = createSignal(POSTS_PER_PAGE); + + const filtered = () => { + const tag = selectedTag(); + if (!tag) return blogPosts; + return blogPosts.filter((p) => p.tags.includes(tag)); + }; + + const visible = () => filtered().slice(0, visibleCount()); + const hasMore = () => visibleCount() < filtered().length; + + return ( +
+ ShieldAI Blog — AI-Powered Identity Protection + +
+
+ +
+

+ ShieldAI Blog +

+

+ Insights on identity protection, AI safety, and the latest digital threats +

+
+
+
+ +
+ +
+ + + {(tag) => ( + + )} + +
+ + + + +
+ +
+
+
+
+
+ ); +} diff --git a/web/src/routes/blog/[slug].tsx b/web/src/routes/blog/[slug].tsx new file mode 100644 index 0000000..9e19546 --- /dev/null +++ b/web/src/routes/blog/[slug].tsx @@ -0,0 +1,363 @@ +import { For, Show, createMemo } from "solid-js"; +import { Title } from "@solidjs/meta"; +import { A, useParams } from "@solidjs/router"; +import { cn } from "~/lib/utils"; +import { Badge, Card, Button } from "~/components/ui"; +import PageContainer from "~/components/layout/PageContainer"; + +interface BlogPost { + slug: string; + title: string; + excerpt: string; + content: string; + author: string; + authorRole: string; + date: string; + readingTime: string; + coverImage: string; + tags: string[]; +} + +const blogPosts: BlogPost[] = [ + { + slug: "ai-scam-trends-2026", + title: "AI Scam Trends to Watch in 2026", + excerpt: "As AI technology advances, scammers are finding new ways to exploit it.", + content: `## The Rise of AI-Powered Scams + +Artificial intelligence has become a double-edged sword. While it powers innovation across industries, it also arms bad actors with sophisticated tools for deception. + +### Voice Cloning Scams + +One of the most alarming trends is the use of AI voice cloning. Scammers need only a few seconds of audio—often scraped from social media videos—to create convincing voice replicas. These are used to impersonate family members in distress, requesting urgent money transfers. + +### Deepfake Video Conferencing + +In 2025, we saw the first wave of deepfake video calls used in corporate impersonation scams. Attackers use real-time face-swapping technology to pose as executives during Zoom calls, authorizing fraudulent wire transfers. + +### Automated Phishing at Scale + +AI-generated phishing emails are now nearly indistinguishable from legitimate correspondence. Language models craft personalized messages that reference real events, contacts, and context, dramatically increasing click-through rates. + +## How to Protect Yourself + +1. **Verify voice requests** — If someone calls asking for money or sensitive information, hang up and call them back on a trusted number. +2. **Use a safe word** — Establish a family safe word that can be used to verify identity in suspicious situations. +3. **Enable ShieldAI VoicePrint** — Our AI detects voice clones by analyzing acoustic fingerprints that deepfakes cannot replicate. +4. **Stay skeptical of urgency** — Scammers create false urgency to bypass your critical thinking. + +## The ShieldAI Advantage + +ShieldAI's multi-layered protection uses machine learning models trained on millions of scam attempts to identify emerging threats before they reach you. Our DarkWatch service continuously scans the dark web for exposed credentials, while VoicePrint protects against audio deepfakes.`, + author: "Sarah Chen", + authorRole: "Security Researcher", + date: "May 15, 2026", + readingTime: "5 min read", + coverImage: "", + tags: ["AI Safety", "Scam Alerts", "Deepfakes"], + }, + { + slug: "dark-web-monitoring-guide", + title: "The Complete Guide to Dark Web Monitoring", + excerpt: "Learn how dark web monitoring works and how ShieldAI keeps your information safe.", + content: `## What Is Dark Web Monitoring? + +The dark web is a hidden part of the internet where cybercriminals trade stolen data. Dark web monitoring services scan these hidden marketplaces, forums, and chat channels for your personal information. + +### What Data Gets Exposed + +When data breaches occur, the following types of information are commonly stolen and sold: + +- **Email addresses and passwords** — The most common credential type traded on dark web markets +- **Social Security numbers** — Often used for identity theft and tax fraud +- **Credit card details** — Sold in bulk with CVV codes and billing addresses +- **Medical records** — Highly valuable on the black market for insurance fraud +- **Phone numbers** — Used for SIM swapping and phishing attacks`, + author: "Mike Reynolds", + authorRole: "Cybersecurity Analyst", + date: "May 10, 2026", + readingTime: "8 min read", + coverImage: "", + tags: ["Dark Web", "Privacy"], + }, + { + slug: "protecting-family-identity", + title: "Protecting Your Family's Digital Identity", + excerpt: "Discover steps to protect everyone in your household from identity theft.", + content: `## Why Family Identity Protection Matters + +Identity thieves don't discriminate. Children, elderly parents, and everyone in between are potential targets. In fact, child identity theft is particularly insidious because it often goes undetected for years. + +### Common Family Identity Threats + +- **Child identity theft** — Stolen SSNs used to open credit lines, often discovered when the child applies for college loans +- **Elder financial exploitation** — Scammers target seniors with tech support scams, grandparent scams, and Medicare fraud +- **Family account sharing** — Shared passwords across family streaming services can expose personal data`, + author: "Emily Torres", + authorRole: "Privacy Advocate", + date: "May 5, 2026", + readingTime: "6 min read", + coverImage: "", + tags: ["Identity Theft", "Privacy"], + }, + { + slug: "deepfake-voice-scams", + title: "Deepfake Voice Scams: How They Work and How to Spot Them", + excerpt: "AI-generated voice clones are being used to impersonate loved ones.", + content: `## The Mechanics of Voice Cloning + +Modern AI voice cloning requires surprisingly little source material. Just 30 seconds of audio—from a voicemail, social media video, or recorded phone call—is enough to generate a convincing voice clone. + +### Real Cases + +In 2024, a family in Arizona received a frantic call from what sounded like their daughter, claiming she had been kidnapped and demanding ransom. The voice was so convincing that the family almost transferred $50,000 before reaching the real daughter. + +### How VoicePrint Detects Clones + +VoicePrint analyzes over 200 acoustic features that are nearly impossible for AI to replicate perfectly, including micro-tremors, breathing patterns, and formant transitions unique to each person's vocal tract.`, + author: "Sarah Chen", + authorRole: "Security Researcher", + date: "April 28, 2026", + readingTime: "7 min read", + coverImage: "", + tags: ["Deepfakes", "AI Safety", "Scam Alerts"], + }, + { + slug: "what-is-data-broker", + title: "What Is a Data Broker and How to Remove Your Information", + excerpt: "Data brokers collect and sell your personal information.", + content: `## Understanding Data Brokers + +Data brokers are companies that collect personal information from various sources—public records, purchasing history, social media activity, and website tracking—then compile and sell this data to third parties. + +### Why It Matters + +Your data broker profile can include your home address, phone number, email address, income level, purchasing habits, political affiliation, and even health-related interests. This information can be used for targeted scams, identity theft, or unwanted marketing. + +### How RemoveBrokers Helps + +ShieldAI's RemoveBrokers service automates the opt-out process for hundreds of data broker sites, sending removal requests on your behalf and verifying that your information has been deleted.`, + author: "Alex Kim", + authorRole: "Data Privacy Specialist", + date: "April 20, 2026", + readingTime: "4 min read", + coverImage: "", + tags: ["Privacy", "Identity Theft"], + }, + { + slug: "shieldai-product-update-may-2026", + title: "ShieldAI Product Update — May 2026", + excerpt: "New features including improved VoicePrint detection and redesigned dashboard.", + content: `## What's New in ShieldAI + +We're excited to announce our May 2026 product update, packed with new features and improvements based on your feedback. + +### Enhanced VoicePrint Detection + +Our voice clone detection model has been retrained on a dataset 3x larger than before, improving detection accuracy to 99.7% while reducing false positives by 40%. + +### Redesigned Dashboard + +The dashboard has been completely redesigned for faster access to critical information. Key metrics are now displayed at a glance, and each service has its own dedicated section with detailed analytics. + +### Expanded Dark Web Coverage + +We've added monitoring for 50 additional dark web forums and marketplaces, bringing our total coverage to over 200 sources.`, + author: "Product Team", + authorRole: "ShieldAI", + date: "April 15, 2026", + readingTime: "3 min read", + coverImage: "", + tags: ["Product News"], + }, +]; + +function contentToHtml(markdown: string): string { + const lines = markdown.split("\n"); + let html = ""; + let inList = false; + + for (const line of lines) { + if (line.startsWith("## ")) { + if (inList) { html += ""; inList = false; } + html += `

${line.slice(3)}

`; + } else if (line.startsWith("### ")) { + if (inList) { html += ""; inList = false; } + html += `

${line.slice(4)}

`; + } else if (line.startsWith("**") && line.endsWith("**")) { + if (inList) { html += ""; inList = false; } + html += `

${line.slice(2, -2)}

`; + } else if (line.startsWith("- ")) { + if (!inList) { html += '
    '; inList = true; } + html += `
  • ${line.slice(2)}
  • `; + } else if (line.match(/^\d+\. /)) { + if (inList) { html += "
"; inList = false; } + const content = line.replace(/^\d+\. /, ""); + html += `

${content}

`; + } else if (line.trim() === "") { + if (inList) { html += ""; inList = false; } + } else { + if (inList) { html += ""; inList = false; } + html += `

${line}

`; + } + } + if (inList) html += ""; + return html; +} + +function getRelatedPosts(current: BlogPost, all: BlogPost[], count: number): BlogPost[] { + const shared = all + .filter((p) => p.slug !== current.slug) + .map((p) => ({ + post: p, + sharedTags: p.tags.filter((t) => current.tags.includes(t)).length, + })) + .filter((p) => p.sharedTags > 0) + .sort((a, b) => b.sharedTags - a.sharedTags) + .slice(0, count); + return shared.map((s) => s.post); +} + +export default function BlogPostPage() { + const params = useParams(); + const post = createMemo(() => blogPosts.find((p) => p.slug === params.slug)); + const contentHtml = createMemo(() => post() ? contentToHtml(post()!.content) : ""); + const relatedPosts = createMemo(() => post() ? getRelatedPosts(post()!, blogPosts, 2) : []); + + return ( + + Post Not Found — ShieldAI + +
+

Post Not Found

+

The blog post you're looking for doesn't exist or has been removed.

+ + + +
+
+ + } + > +
+ {post()!.title} — ShieldAI Blog + +
+
+
+ + + + + + Back to Blog + + +
+ + {(tag) => {tag}} + +
+ +

+ {post()!.title} +

+ +
+
+
+ {post()!.author.split(" ").map(n => n[0]).join("")} +
+
+

{post()!.author}

+

{post()!.authorRole}

+
+
+ · + {post()!.date} + · + {post()!.readingTime} +
+
+
+ +
+ +
+
+ + +
+ +
+
+
+
+ ); +} diff --git a/web/src/routes/migrated-pages.test.tsx b/web/src/routes/migrated-pages.test.tsx new file mode 100644 index 0000000..3d9e517 --- /dev/null +++ b/web/src/routes/migrated-pages.test.tsx @@ -0,0 +1,266 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { render } from "solid-js/web"; +import { MetaProvider } from "@solidjs/meta"; +import type { JSX } from "solid-js"; + +vi.mock("@solidjs/router", () => ({ + A: (props: { href?: string; children?: JSX.Element; class?: string; onClick?: () => void }) => ( + + {props.children} + + ), + useLocation: () => ({ pathname: "/dashboard" }), + useParams: () => ({ slug: "ai-scam-trends-2026" }), + useSearchParams: () => [new URLSearchParams(), vi.fn()], +})); + +import BlogPage from "./blog"; +import BlogPostPage from "./blog/[slug]"; +import AdsPage from "./ads"; +import DashboardPage from "./(webapp)/dashboard"; +import StatCard from "~/components/dashboard/StatCard"; +import ActivityFeed from "~/components/dashboard/ActivityFeed"; +import QuickActions from "~/components/dashboard/QuickActions"; + +function mount(comp: () => JSX.Element): HTMLDivElement { + const container = document.createElement("div"); + document.body.appendChild(container); + render(() => {comp()}, container); + return container; +} + +beforeEach(() => { + document.body.innerHTML = ""; +}); + +afterEach(() => { + document.body.innerHTML = ""; +}); + +describe("BlogPage (listing)", () => { + it("renders hero section with blog headline", () => { + mount(() => ); + expect(document.body.textContent).toContain("ShieldAI Blog"); + }); + + it("renders all 6 blog post cards", () => { + mount(() => ); + const cards = document.querySelectorAll(".gradient-card"); + expect(cards.length).toBeGreaterThanOrEqual(4); + }); + + it("renders tag filter buttons", () => { + mount(() => ); + expect(document.body.textContent).toContain("All"); + expect(document.body.textContent).toContain("AI Safety"); + expect(document.body.textContent).toContain("Privacy"); + expect(document.body.textContent).toContain("Deepfakes"); + }); + + it("renders Load More button when there are more posts to show", () => { + mount(() => ); + expect(document.body.textContent).toContain("Load More Posts"); + }); + + it("renders post titles and excerpts", () => { + mount(() => ); + expect(document.body.textContent).toContain("AI Scam Trends to Watch in 2026"); + expect(document.body.textContent).toContain("Sarah Chen"); + expect(document.body.textContent).toContain("Mike Reynolds"); + }); +}); + +describe("BlogPostPage ([slug])", () => { + it("renders post content for valid slug", () => { + mount(() => ); + expect(document.body.textContent).toContain("AI Scam Trends to Watch in 2026"); + expect(document.body.textContent).toContain("Sarah Chen"); + expect(document.body.textContent).toContain("Security Researcher"); + expect(document.body.textContent).toContain("May 15, 2026"); + expect(document.body.textContent).toContain("5 min read"); + }); + + it("renders markdown content as HTML", () => { + mount(() => ); + expect(document.body.textContent).toContain("The Rise of AI-Powered Scams"); + expect(document.body.textContent).toContain("Voice Cloning Scams"); + expect(document.body.textContent).toContain("How to Protect Yourself"); + }); + + it("renders social share buttons", () => { + mount(() => ); + const shareBtns = document.querySelectorAll("button[aria-label]"); + const shareLabels = Array.from(shareBtns).map(b => b.getAttribute("aria-label")); + expect(shareLabels).toContain("Share on Twitter"); + expect(shareLabels).toContain("Share on LinkedIn"); + expect(shareLabels).toContain("Copy link"); + }); + + it("renders related posts section", () => { + mount(() => ); + expect(document.body.textContent).toContain("Related Posts"); + }); + + it("renders author card in sidebar", () => { + mount(() => ); + expect(document.body.textContent).toContain("Security Researcher"); + }); + + it("renders back to blog link", () => { + mount(() => ); + expect(document.body.textContent).toContain("Back to Blog"); + }); +}); + +describe("AdsPage", () => { + it("renders conversion-focused headline", () => { + mount(() => ); + expect(document.body.textContent).toContain("Stop AI Scams Before"); + expect(document.body.textContent).toContain("They Reach You"); + }); + + it("renders pricing section with 3 plans", () => { + mount(() => ); + expect(document.body.textContent).toContain("Basic"); + expect(document.body.textContent).toContain("Plus"); + expect(document.body.textContent).toContain("Premium"); + expect(document.body.textContent).toContain("$9"); + expect(document.body.textContent).toContain("$19"); + expect(document.body.textContent).toContain("$39"); + }); + + it("marks Plus plan as Most Popular", () => { + mount(() => ); + const badges = document.body.querySelectorAll("span"); + const popularBadge = Array.from(badges).find(b => b.textContent === "Most Popular"); + expect(popularBadge).toBeTruthy(); + }); + + it("renders FAQ section with toggle functionality", () => { + mount(() => ); + expect(document.body.textContent).toContain("Frequently Asked Questions"); + expect(document.body.textContent).toContain("How does ShieldAI detect voice clones?"); + expect(document.body.textContent).toContain("Is my data encrypted?"); + }); + + it("renders trust badges", () => { + mount(() => ); + expect(document.body.textContent).toContain("14-day free trial"); + expect(document.body.textContent).toContain("No credit card required"); + expect(document.body.textContent).toContain("Cancel anytime"); + }); + + it("renders trust metrics section", () => { + mount(() => ); + expect(document.body.textContent).toContain("99.7%"); + expect(document.body.textContent).toContain("200+"); + expect(document.body.textContent).toContain("50K+"); + }); + + it("renders testimonials", () => { + mount(() => ); + expect(document.body.textContent).toContain("Trusted by Thousands"); + }); + + it("renders CTA section with Get Started Free button", () => { + mount(() => ); + expect(document.body.textContent).toContain("Ready to protect your identity?"); + expect(document.body.textContent).toContain("Get Started Free"); + }); +}); + +describe("DashboardPage", () => { + it("renders dashboard title", () => { + mount(() => ); + expect(document.body.textContent).toContain("Overview"); + }); + + it("renders stat cards with mock data", () => { + mount(() => ); + expect(document.body.textContent).toContain("Active Threats"); + expect(document.body.textContent).toContain("Protected Accounts"); + expect(document.body.textContent).toContain("Dark Web Scans"); + expect(document.body.textContent).toContain("Alerts Today"); + expect(document.body.textContent).toContain("3"); + expect(document.body.textContent).toContain("12"); + expect(document.body.textContent).toContain("1,847"); + expect(document.body.textContent).toContain("7"); + }); + + it("renders sidebar with navigation links", () => { + mount(() => ); + expect(document.body.textContent).toContain("Overview"); + expect(document.body.textContent).toContain("DarkWatch"); + expect(document.body.textContent).toContain("VoicePrint"); + expect(document.body.textContent).toContain("SpamShield"); + expect(document.body.textContent).toContain("HomeTitle"); + expect(document.body.textContent).toContain("RemoveBrokers"); + expect(document.body.textContent).toContain("Settings"); + }); + + it("renders activity feed", () => { + mount(() => ); + expect(document.body.textContent).toContain("Recent Activity"); + expect(document.body.textContent).toContain("New credential leak detected"); + expect(document.body.textContent).toContain("VoicePrint scan completed"); + }); + + it("renders quick actions", () => { + mount(() => ); + expect(document.body.textContent).toContain("Quick Actions"); + expect(document.body.textContent).toContain("Run Scan"); + expect(document.body.textContent).toContain("View Alerts"); + expect(document.body.textContent).toContain("Add Member"); + expect(document.body.textContent).toContain("Run Report"); + }); +}); + +describe("StatCard", () => { + it("renders label and value", () => { + mount(() => ( + } /> + )); + expect(document.body.textContent).toContain("Test Metric"); + expect(document.body.textContent).toContain("42"); + }); + + it("renders up trend indicator", () => { + mount(() => ( + } /> + )); + expect(document.body.textContent).toContain("+5%"); + }); + + it("renders down trend indicator", () => { + mount(() => ( + } /> + )); + expect(document.body.textContent).toContain("-3%"); + }); +}); + +describe("ActivityFeed", () => { + it("renders activities", () => { + const activities = [ + { id: "1", title: "Test Alert", description: "Test description", timestamp: "5m ago", type: "alert" as const }, + ]; + mount(() => ); + expect(document.body.textContent).toContain("Recent Activity"); + expect(document.body.textContent).toContain("Test Alert"); + expect(document.body.textContent).toContain("Test description"); + expect(document.body.textContent).toContain("5m ago"); + }); +}); + +describe("QuickActions", () => { + it("renders action buttons", () => { + const actions = [ + { label: "Action 1", href: "/test", icon: () => }, + { label: "Action 2", href: "/test2", icon: () => }, + ]; + mount(() => ); + expect(document.body.textContent).toContain("Quick Actions"); + expect(document.body.textContent).toContain("Action 1"); + expect(document.body.textContent).toContain("Action 2"); + }); +});