Public page SEO and production analytics (#125)
Deploy Beta / unit-tests (push) Successful in 13s
Unit Tests / test (push) Successful in 13s
Deploy Beta / deploy-beta (push) Successful in 3m50s

## Summary
- Closes the public-page gaps from #124: each route gets its own title, description, canonical, heading, links, and JSON-LD, and unknown URLs have a real 404 document.
- Adds HeyCatch and the Google tag (`G-V12DLRLPWR`) only on the production site and the iOS and Android apps. Tianji still loads on beta.
- Needs the server-infra change that stops serving the app shell for missing paths.

## Test plan
- [ ] Build production and confirm `/` and `/pricing/` HTML include a unique title, description, canonical, one h1, social tags, related links, and Organization JSON-LD.
- [ ] Confirm a nonsense path is served as `404.html` once the nginx change is deployed.
- [ ] Confirm beta and local web do not load HeyCatch or gtag, and that a production or native build does.
- [ ] Open the live site after deploy and click around, then mark HeyCatch installed in its dashboard.

Reviewed-on: #125
This commit was merged in pull request #125.
This commit is contained in:
westfarn committed 2026-10-19 08:01:44 -07:00
1 parent 8b58192026
commit 7a8f413483
45 files changed
+1880 -174

No files matched your search

+113
View File
@@ -19,6 +19,7 @@
"@emotion/cache": "^11.14.0",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
"@heycatch/sdk": "^0.8.3",
"@mui/icons-material": "^5.16.11",
"@mui/material": "^5.16.11",
"@testing-library/jest-dom": "^6.6.3",
@@ -3083,6 +3084,103 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@heycatch/sdk": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/@heycatch/sdk/-/sdk-0.8.3.tgz",
"integrity": "sha512-7knqXfptgctAIHu5IXwd/WnivY+wwYAws2Xr0ZuARottBnbQteFbxZP7TyEDWCyz6A5+PcGlzlPxeTdq5uyGoA==",
"license": "MIT",
"dependencies": {
"posthog-react-native": "^4.61.1"
},
"engines": {
"node": ">=22"
},
"peerDependencies": {
"expo-router": "*",
"react": "^18.2.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"expo-router": {
"optional": true
},
"react": {
"optional": true
}
}
},
"node_modules/@heycatch/sdk/node_modules/posthog-react-native": {
"version": "4.81.2",
"resolved": "https://registry.npmjs.org/posthog-react-native/-/posthog-react-native-4.81.2.tgz",
"integrity": "sha512-w9UUocpnS9u+CO+37Q32W/zSgL8/fSFhIB9tUZfSDc1D4rE7XShyfCwwXD6efjmP19Gu941kuXdRgTbuV8Mkig==",
"license": "MIT",
"dependencies": {
"@posthog/core": "^1.59.1",
"@posthog/types": "^1.416.0"
},
"bin": {
"posthog-react-native-xcode": "tooling/posthog-xcode.sh"
},
"peerDependencies": {
"@posthog/react-native-plugin": ">= 2.9.3",
"@react-native-async-storage/async-storage": ">=1.0.0",
"@react-navigation/native": ">= 5.0.0",
"expo-application": ">= 4.0.0",
"expo-device": ">= 4.0.0",
"expo-file-system": ">= 13.0.0",
"expo-localization": ">= 11.0.0",
"expo-updates": ">= 0.25.0",
"posthog-react-native-session-replay": ">= 1.6.0",
"react-native-device-info": ">= 10.0.0",
"react-native-localize": ">= 3.0.0",
"react-native-navigation": ">= 6.0.0",
"react-native-safe-area-context": ">= 4.0.0",
"react-native-svg": ">= 15.0.0"
},
"peerDependenciesMeta": {
"@posthog/react-native-plugin": {
"optional": true
},
"@react-native-async-storage/async-storage": {
"optional": true
},
"@react-navigation/native": {
"optional": true
},
"expo-application": {
"optional": true
},
"expo-device": {
"optional": true
},
"expo-file-system": {
"optional": true
},
"expo-localization": {
"optional": true
},
"expo-updates": {
"optional": true
},
"posthog-react-native-session-replay": {
"optional": true
},
"react-native-device-info": {
"optional": true
},
"react-native-localize": {
"optional": true
},
"react-native-navigation": {
"optional": true
},
"react-native-safe-area-context": {
"optional": true
},
"react-native-svg": {
"optional": true
}
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
@@ -5155,6 +5253,21 @@
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@posthog/core": {
"version": "1.59.1",
"resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.59.1.tgz",
"integrity": "sha512-P39YgzQVDh6oz6wAzQXEnoHgTYpM7gifonmVKq1scAJco79h31XmmNVurRq9tTmmyIrr1uX8FHT0riCsyfV9AQ==",
"license": "MIT",
"dependencies": {
"@posthog/types": "^1.416.0"
}
},
"node_modules/@posthog/types": {
"version": "1.416.0",
"resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.416.0.tgz",
"integrity": "sha512-0/9+02s/0uDyrH4G/qUg1yyl6gXUg8Uuxw/PlDad3KLzo9FFS9zS/kAQaEshx/saVGupj1aAy/4EUBxWisBogw==",
"license": "MIT"
},
"node_modules/@prettier/plugin-xml": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@prettier/plugin-xml/-/plugin-xml-2.2.0.tgz",