feat(seo): react-helmet-async, JSON-LD, robots.txt, sitemap, llms.txt
- HelmetProvider wraps App; PageMeta component for per-page title/description/OG/noindex - Index.tsx: Organization + SoftwareApplication JSON-LD structured data - Login, Register: noindex meta - public/robots.txt: Allow /, Disallow app routes, Sitemap pointer - public/sitemap.xml: static sitemap for landing sections - public/llms.txt: Cohorta description for ChatGPT Search / Perplexity Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
31d25a9293
commit
14f63a3e0c
10 changed files with 193 additions and 0 deletions
27
package-lock.json
generated
27
package-lock.json
generated
|
|
@ -58,6 +58,7 @@
|
|||
"react": "^18.3.1",
|
||||
"react-day-picker": "^8.10.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-helmet-async": "^3.0.0",
|
||||
"react-hook-form": "^7.53.0",
|
||||
"react-i18next": "^17.0.8",
|
||||
"react-resizable-panels": "^2.1.3",
|
||||
|
|
@ -6001,6 +6002,26 @@
|
|||
"react": "^18.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-fast-compare": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
|
||||
"integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-helmet-async": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-3.0.0.tgz",
|
||||
"integrity": "sha512-nA3IEZfXiclgrz4KLxAhqJqIfFDuvzQwlKwpdmzZIuC1KNSghDEIXmyU0TKtbM+NafnkICcwx8CECFrZ/sL/1w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"invariant": "^2.2.4",
|
||||
"react-fast-compare": "^3.2.2",
|
||||
"shallowequal": "^1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-hook-form": {
|
||||
"version": "7.53.1",
|
||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.53.1.tgz",
|
||||
|
|
@ -6364,6 +6385,12 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/shallowequal": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
|
||||
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
"react": "^18.3.1",
|
||||
"react-day-picker": "^8.10.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-helmet-async": "^3.0.0",
|
||||
"react-hook-form": "^7.53.0",
|
||||
"react-i18next": "^17.0.8",
|
||||
"react-resizable-panels": "^2.1.3",
|
||||
|
|
|
|||
41
public/llms.txt
Normal file
41
public/llms.txt
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Cohorta
|
||||
|
||||
> AI-powered synthetic focus groups for product research and user insights
|
||||
|
||||
Cohorta is a SaaS platform that lets product teams, UX researchers, and marketers run AI-moderated focus groups with synthetic personas — without recruiting real participants.
|
||||
|
||||
## What Cohorta does
|
||||
|
||||
- **Synthetic persona generation**: Create detailed AI personas from a single research brief. Each persona has demographics, psychographics, OCEAN personality traits, behavioral attributes, and a realistic backstory.
|
||||
- **AI-moderated focus groups**: Run asynchronous focus group sessions where AI personas discuss your research topics, react to stimuli (mockups, documents, pricing pages), and provide nuanced qualitative feedback.
|
||||
- **Thematic analysis**: Automatically extract key themes, sentiment, and insights from focus group transcripts.
|
||||
- **Discussion guide generation**: AI generates a structured discussion guide based on your research objectives.
|
||||
|
||||
## Use cases
|
||||
|
||||
- Product concept testing before building
|
||||
- Pricing sensitivity research
|
||||
- UX evaluation of wireframes and prototypes
|
||||
- Brand perception testing
|
||||
- Feature prioritisation research
|
||||
- Market segmentation insights
|
||||
|
||||
## Key facts
|
||||
|
||||
- Results in minutes, not weeks — no participant recruitment
|
||||
- UK-hosted infrastructure, GDPR-compliant
|
||||
- Pay-per-project credit system (credits never expire)
|
||||
- Free trial: 50 credits on signup, no card required
|
||||
- Supports Azure OpenAI (GPT-5.4) and compatible models
|
||||
|
||||
## Pricing
|
||||
|
||||
- Starter: $49 / 50 credits — ~25 AI personas or 1 focus group run
|
||||
- Pro: $199 / 220 credits — ~110 AI personas or 5 focus group runs
|
||||
- Scale: $499 / 600 credits — ~300 AI personas or 15 focus group runs
|
||||
|
||||
## Company
|
||||
|
||||
Made by AImpress Ltd, UK.
|
||||
Contact: hello@ai-impress.com
|
||||
Website: https://cohorta.ai-impress.com
|
||||
11
public/robots.txt
Normal file
11
public/robots.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
User-agent: *
|
||||
Allow: /
|
||||
Disallow: /app/
|
||||
Disallow: /dashboard
|
||||
Disallow: /synthetic-users
|
||||
Disallow: /focus-groups
|
||||
Disallow: /admin
|
||||
Disallow: /usage
|
||||
Disallow: /billing
|
||||
|
||||
Sitemap: https://cohorta.ai-impress.com/sitemap.xml
|
||||
33
public/sitemap.xml
Normal file
33
public/sitemap.xml
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://cohorta.ai-impress.com/</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://cohorta.ai-impress.com/#pricing</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://cohorta.ai-impress.com/#product</loc>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://cohorta.ai-impress.com/about</loc>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.5</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://cohorta.ai-impress.com/privacy</loc>
|
||||
<changefreq>yearly</changefreq>
|
||||
<priority>0.3</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://cohorta.ai-impress.com/terms</loc>
|
||||
<changefreq>yearly</changefreq>
|
||||
<priority>0.3</priority>
|
||||
</url>
|
||||
</urlset>
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
import { Toaster } from "@/components/ui/sonner";
|
||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { HelmetProvider } from 'react-helmet-async';
|
||||
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
|
||||
import Index from "./pages/Index";
|
||||
import NotFound from "./pages/NotFound";
|
||||
|
|
@ -35,6 +36,7 @@ import "./styles/backButton.css";
|
|||
const queryClient = new QueryClient();
|
||||
|
||||
const App = () => (
|
||||
<HelmetProvider>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<BrowserRouter basename={import.meta.env.BASE_URL}>
|
||||
<AuthProvider>
|
||||
|
|
@ -96,6 +98,7 @@ const App = () => (
|
|||
</AuthProvider>
|
||||
</BrowserRouter>
|
||||
</QueryClientProvider>
|
||||
</HelmetProvider>
|
||||
);
|
||||
|
||||
export default App;
|
||||
|
|
|
|||
37
src/components/PageMeta.tsx
Normal file
37
src/components/PageMeta.tsx
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import { Helmet } from 'react-helmet-async';
|
||||
|
||||
interface PageMetaProps {
|
||||
title?: string;
|
||||
description?: string;
|
||||
noindex?: boolean;
|
||||
ogImage?: string;
|
||||
}
|
||||
|
||||
const SITE_NAME = 'Cohorta';
|
||||
const BASE_URL = 'https://cohorta.ai-impress.com';
|
||||
const DEFAULT_DESCRIPTION =
|
||||
'Run AI-moderated focus groups with synthetic personas. Get real research insights in minutes, not weeks — no recruits, no scheduling, no bias.';
|
||||
const DEFAULT_OG_IMAGE = `${BASE_URL}/og-image.png`;
|
||||
|
||||
export default function PageMeta({
|
||||
title,
|
||||
description = DEFAULT_DESCRIPTION,
|
||||
noindex = false,
|
||||
ogImage = DEFAULT_OG_IMAGE,
|
||||
}: PageMetaProps) {
|
||||
const fullTitle = title ? `${title} — ${SITE_NAME}` : `${SITE_NAME} — AI-Powered Synthetic Focus Groups`;
|
||||
|
||||
return (
|
||||
<Helmet>
|
||||
<title>{fullTitle}</title>
|
||||
<meta name="description" content={description} />
|
||||
{noindex && <meta name="robots" content="noindex, nofollow" />}
|
||||
<meta property="og:title" content={fullTitle} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:image" content={ogImage} />
|
||||
<meta name="twitter:title" content={fullTitle} />
|
||||
<meta name="twitter:description" content={description} />
|
||||
<meta name="twitter:image" content={ogImage} />
|
||||
</Helmet>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import { Helmet } from 'react-helmet-async';
|
||||
import PageMeta from '@/components/PageMeta';
|
||||
import Hero from '@/components/landing/Hero';
|
||||
import CompassBg from '@/components/landing/CompassBg';
|
||||
import StatsBand from '@/components/landing/StatsBand';
|
||||
|
|
@ -11,9 +13,43 @@ import Pricing from '@/components/landing/Pricing';
|
|||
import FAQ from '@/components/landing/FAQ';
|
||||
import FinalCTA from '@/components/landing/FinalCTA';
|
||||
|
||||
const JSON_LD = [
|
||||
{
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Organization',
|
||||
name: 'AImpress Ltd',
|
||||
url: 'https://cohorta.ai-impress.com',
|
||||
logo: 'https://cohorta.ai-impress.com/favicon.svg',
|
||||
contactPoint: { '@type': 'ContactPoint', email: 'hello@ai-impress.com' },
|
||||
},
|
||||
{
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'SoftwareApplication',
|
||||
name: 'Cohorta',
|
||||
url: 'https://cohorta.ai-impress.com',
|
||||
applicationCategory: 'BusinessApplication',
|
||||
operatingSystem: 'Web',
|
||||
description:
|
||||
'AI-powered synthetic focus groups for product research. Generate AI personas and run moderated sessions in minutes.',
|
||||
offers: [
|
||||
{ '@type': 'Offer', name: 'Starter', price: '49', priceCurrency: 'USD' },
|
||||
{ '@type': 'Offer', name: 'Pro', price: '199', priceCurrency: 'USD' },
|
||||
{ '@type': 'Offer', name: 'Scale', price: '499', priceCurrency: 'USD' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default function Index() {
|
||||
return (
|
||||
<div className="bg-background overflow-hidden relative">
|
||||
<PageMeta />
|
||||
<Helmet>
|
||||
{JSON_LD.map((schema, i) => (
|
||||
<script key={i} type="application/ld+json">
|
||||
{JSON.stringify(schema)}
|
||||
</script>
|
||||
))}
|
||||
</Helmet>
|
||||
<CompassBg />
|
||||
<Hero />
|
||||
<StatsBand />
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import PageMeta from '@/components/PageMeta';
|
||||
import { useNavigate, useLocation, Link } from 'react-router-dom';
|
||||
import { z } from 'zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
|
@ -158,6 +159,7 @@ export default function Login() {
|
|||
|
||||
return (
|
||||
<div className="flex min-h-screen overflow-hidden bg-background">
|
||||
<PageMeta title="Log in" noindex />
|
||||
|
||||
{/* Left: form */}
|
||||
<div className="flex-1 flex items-center justify-center px-6 py-12">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import PageMeta from '@/components/PageMeta';
|
||||
import { useNavigate, Link, useSearchParams } from 'react-router-dom';
|
||||
import { z } from 'zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
|
@ -206,6 +207,7 @@ export default function Register() {
|
|||
|
||||
return (
|
||||
<div className="flex min-h-screen overflow-hidden bg-background">
|
||||
<PageMeta title="Create account" noindex />
|
||||
|
||||
{/* Left: form */}
|
||||
<div className="flex-1 flex items-center justify-center px-6 py-12">
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue