mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 09:19:51 +00:00
Brings the site from bare-bones metadata to production-ready SEO. Covers technical crawlability, rich-result eligibility, social sharing, and duplicate-content prevention. Added: - src/lib/seo/site-config.ts — single source of truth for base URL, brand, description, keywords. Referenced by every consumer so a domain / rename is a one-file edit. - src/lib/seo/faq-data.ts — FAQ Q&A extracted from the component so the UI and the FAQPage JSON-LD render the same content. - src/lib/seo/structured-data.ts — builders for WebSite, Organization, SoftwareApplication, and FAQPage JSON-LD nodes. FAQPage is eligible for Google's FAQ rich result; SoftwareApplication with a free Offer can earn a "Free" badge on SERP. - src/components/seo/json-ld.tsx — emits one <script type="application/ld+json"> per node (Google's preferred shape). - src/app/opengraph-image.tsx — Next auto-generates a 1200×630 PNG for the landing page's OG / Twitter previews. - src/app/sitemap.ts — lists only public, indexable pages. - src/app/robots.ts — Allow /, Disallow every app / auth route, references the sitemap. Updated: - src/app/layout.tsx — metadataBase, title template (%s — WaCRM), default OpenGraph + Twitter Cards (inherited everywhere), canonical, icons config, viewport (themeColor + colorScheme=dark for browser chrome on mobile). - src/app/page.tsx — injects the 4 JSON-LD blocks, sets explicit canonical for /, overrides the title. - src/app/(auth)/layout.tsx — noindex+nofollow for login / signup / forgot-password (no SEO value; would compete with landing page). - src/app/(dashboard)/layout.tsx — split into thin server layout (exports noindex metadata) + DashboardShell client component. App routes won't be indexed even if a URL leaks. - src/components/landing/faq.tsx — consumes the shared FAQ data module (no more drift between UI and JSON-LD). Verified end-to-end against the running dev server: - GET / HTML contains: canonical, description, keywords, robots, og:title/url/image/type, twitter:card/title, theme-color, + 4 JSON-LD blocks (WebSite, Organization, SoftwareApplication, FAQPage). - GET /opengraph-image → 200 OK, content-type image/png. - GET /login HTML contains <meta name="robots" content="noindex, nofollow, nocache">. - GET /sitemap.xml serves valid XML with /. - GET /robots.txt serves Allow /, Disallow all app + auth paths, sitemap reference. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
111 lines
2.9 KiB
TypeScript
111 lines
2.9 KiB
TypeScript
import {
|
|
absoluteUrl,
|
|
ORG_INFO,
|
|
SITE_DESCRIPTION,
|
|
SITE_NAME,
|
|
SITE_URL,
|
|
} from './site-config'
|
|
import { FAQ_ITEMS } from './faq-data'
|
|
|
|
/**
|
|
* Builders for the JSON-LD blobs we inject on the landing page. Each
|
|
* returns a plain object — the consumer wraps it in <script
|
|
* type="application/ld+json">. Keeping them typed as `Record<string,
|
|
* unknown>` so schema.org shapes don't leak into the rest of the app.
|
|
*/
|
|
type Ld = Record<string, unknown>
|
|
|
|
/** Generic site-level node. Helps search engines map the domain. */
|
|
export function websiteLd(): Ld {
|
|
return {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'WebSite',
|
|
name: SITE_NAME,
|
|
url: SITE_URL,
|
|
description: SITE_DESCRIPTION,
|
|
inLanguage: 'en',
|
|
}
|
|
}
|
|
|
|
/** Publisher / brand. Paired with the WebSite node. */
|
|
export function organizationLd(): Ld {
|
|
return {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'Organization',
|
|
name: ORG_INFO.name,
|
|
legalName: ORG_INFO.legalName,
|
|
url: ORG_INFO.url,
|
|
logo: ORG_INFO.logo,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Core product node. SoftwareApplication is the recommended type for
|
|
* web-based SaaS in Google's Rich Results guidelines. The free-tier
|
|
* `offers` block enables a "Free" badge in SERPs where eligible.
|
|
*/
|
|
export function softwareApplicationLd(): Ld {
|
|
return {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'SoftwareApplication',
|
|
name: SITE_NAME,
|
|
description: SITE_DESCRIPTION,
|
|
applicationCategory: 'BusinessApplication',
|
|
operatingSystem: 'Web',
|
|
url: SITE_URL,
|
|
offers: {
|
|
'@type': 'Offer',
|
|
price: '0',
|
|
priceCurrency: 'USD',
|
|
availability: 'https://schema.org/InStock',
|
|
},
|
|
featureList: [
|
|
'Shared WhatsApp inbox',
|
|
'Contact management with tags and custom fields',
|
|
'Sales pipelines with kanban board',
|
|
'Broadcast campaigns via approved WhatsApp templates',
|
|
'No-code workflow automations',
|
|
'Real-time analytics dashboard',
|
|
],
|
|
}
|
|
}
|
|
|
|
/**
|
|
* FAQPage — eligible for the FAQ rich result on Google.
|
|
* We build this from the same source the UI renders, so answers
|
|
* in SERP match answers on the page.
|
|
*/
|
|
export function faqPageLd(): Ld {
|
|
return {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'FAQPage',
|
|
mainEntity: FAQ_ITEMS.map((item) => ({
|
|
'@type': 'Question',
|
|
name: item.q,
|
|
acceptedAnswer: {
|
|
'@type': 'Answer',
|
|
text: item.a,
|
|
},
|
|
})),
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convenience: an array of the nodes we want on the landing page.
|
|
* The consumer component iterates and emits one <script> per node
|
|
* (Google prefers one schema per script tag).
|
|
*/
|
|
export function landingPageLd(): Ld[] {
|
|
return [
|
|
websiteLd(),
|
|
organizationLd(),
|
|
softwareApplicationLd(),
|
|
faqPageLd(),
|
|
]
|
|
}
|
|
|
|
// Kept exported for discoverability; unused by runtime code but handy
|
|
// for anyone building additional pages that need an absolute URL in
|
|
// their JSON-LD.
|
|
export { absoluteUrl }
|