8 · Engagement & SEO
Objective — wire the growth surfaces, led by GA4 via the universal 3-file Safe Vendor Deviation Pattern (one blade edit, one config key, one env var) — the canonical example whose shape recurs for Sentry JS, PostHog, Pixel, and Turnstile — then run check-first on chat, social login, blog, and sitemap.
Steps at a glance:
- Wire analytics tracking (GA4) — Getting the Measurement ID and confirming Realtime are Google-Analytics-dashboard actions a person performs; the 3-file code change is agent work.
- Configure the chat widget — CHECK:
Admin → Settingsfor a Crisp / Tawk.to / Intercom / generic widget field. - Configure social login — CHECK:
Admin → Config → Login(orSocial Auth/OAuth Settings). Creating provider apps is mixed: the human owns login/consent/secret fields, while the agent can drive non-secret console setup and panel verification after authorization. - Confirm the blog system — CHECK:
Admin → Blog(orPosts/Content). - Verify the sitemap — CHECK: visit
/sitemap.xmlon the domain, and look underAdmin → SEO.
Background
Section titled “Background”The growth surfaces — analytics, chat, social login, blog, sitemap. Most are SHOULD check-first tasks or tracked post-launch non-blockers, but analytics is the canonical example of the universal Safe Vendor Deviation Pattern and earns the most space because the same 3-file shape recurs for Sentry JS, PostHog, Facebook Pixel, Turnstile, and more.
1. Wire analytics tracking (GA4)
Section titled “1. Wire analytics tracking (GA4)”Getting the Measurement ID and confirming Realtime happen in the Google Analytics dashboard. Use the vendor console automation guide when an agent drives the non-secret console steps after the human login; the 3-file code change is agent work.
Most CodeCanyon Laravel scripts ship ZERO built-in analytics — no admin field, no tracking_code column. Pick your path from the capabilities doc.
Path 1 — vendor has a built-in analytics field (rare): Admin → Config → SEO (or Settings → Tracking) → paste the Measurement ID → save → view-source the landing page to confirm the GA4 script is present → check GA4 Realtime.
Path 2 — no built-in field (common): the 3-file Safe Vendor Deviation. The pattern keeps your change small, env-gated, and auditable across vendor updates — one blade edit, one convention-file key, one env var:
flowchart LR BLADE["landing.blade.php<br/>@if(config('services.google.ga4_measurement_id'))<br/>…gtag snippet…"] CONFIG["config/services.php<br/>'google' => ['ga4_measurement_id' => env('GA4_MEASUREMENT_ID')]"] ENV["shared/.env<br/>GA4_MEASUREMENT_ID=\"G-XXXXXXXXXX\""] BLADE -->|reads via config()| CONFIG CONFIG -->|reads via env()| ENV-
Edit the blade — in
resources/views/layouts/landing.blade.php(the public landing layout — notguest.blade.php, which has Jetstream tenant-context issues, and not authenticated app layouts), insert the standardgtag.jssnippet right before</head>, gated on config and cookie consent (Termly/GetTerms autoblock or your Phase 6/7 banner). Example with Termly autoblock — the script stays inert until analytics consent:@if (config('services.google.ga4_measurement_id')){{-- <PROJECT> Phase 6 — GA4 (consent-gated; Termly data-auto-block="on" must load first in <head>) --}}<script type="text/plain" data-cookie-category="analytics"src="https://www.googletagmanager.com/gtag/js?id={{ config('services.google.ga4_measurement_id') }}"></script><script type="text/plain" data-cookie-category="analytics">window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', '{{ config('services.google.ga4_measurement_id') }}');</script>@endif- ✅ The gtag snippet is config-gated, consent-gated (not unconditional), with a project-name comment marker.
-
Edit the config — add
'google' => ['ga4_measurement_id' => env('GA4_MEASUREMENT_ID')]toconfig/services.php. Vendors almost never touch this convention file, which is what makes it a safe home. Read viaconfig(...), neverenv(...)from blade/app code.- ✅
config/services.phpexposesservices.google.ga4_measurement_id.
- ✅
-
Append the env var to
shared/.envon the server (not the ephemeral release.env), then rebuild the config cache.Terminal window PHP_BIN=$(grep "set('bin/php'" deploy.php | grep -oE "/[^'\"]+/php" | head -1)DEPLOY_TARGET="<selected-nonproduction-deploy-target>" # from Zaj-PROJECT.md, e.g. staging-primary/qa/uat/client-demoDEPLOY_PATH="~/domains/<DOMAIN>/deploy"SSH_ALIAS="<non-prod-alias-from-Zaj-PROJECT>"ssh "$SSH_ALIAS" "grep -q '^GA4_MEASUREMENT_ID=' $DEPLOY_PATH/shared/.env \|| echo 'GA4_MEASUREMENT_ID=\"G-XXXXXXXXXX\"' >> $DEPLOY_PATH/shared/.env"ssh "$SSH_ALIAS" "cd $DEPLOY_PATH/current && $PHP_BIN artisan config:clear && $PHP_BIN artisan config:cache"# Expected: the ID is appended once on shared/.env, then the config cache rebuilds- ✅
GA4_MEASUREMENT_IDis set inshared/.envand the config cache is rebuilt.
- ✅
-
Verify the tag fires.
Terminal window curl -sS https://nonprod.example.com/ | grep -c "$GA4_MEASUREMENT_ID" # expect ≥1 (consent-gated tag may show once in HTML)# Expected: ≥1- ✅
curl | grep -creturns ≥1 for the real Measurement ID; GA4 → Reports → Realtime shows your visit within ~30s. Record the deviation inZaj-CUSTOMIZATIONS.md(file / what added / task / comment marker) — that registry is the shopping list for every vendor-update merge.
- ✅
2. Configure the chat widget
Section titled “2. Configure the chat widget”CHECK: Admin → Settings for a Crisp / Tawk.to / Intercom / generic widget field.
- Configure now or capture the widget task.
-
If present — paste the provider’s widget code or API key.
-
If absent — capture the layout-footer widget task in
Zaj-BACKLOG.mdas a post-launch non-blocker. -
✅ The widget is configured, or captured in
Zaj-BACKLOG.mdas a post-launch non-blocker with owner, trigger, and acceptance criterion.
-
3. Configure social login
Section titled “3. Configure social login”CHECK: Admin → Config → Login (or Social Auth / OAuth Settings). Provider login, consent screens, and secret fields are human-owned; the non-secret console setup, callback URL verification, panel save, and browser checks are agent-drivable after the authorized session exists.
- Configure now or capture the social-login task.
-
If present — enter the Google / Facebook / GitHub OAuth client ID + secret and enable the providers you want.
-
If absent — likely needs Laravel Socialite + code. Check for an existing
SocialLoginControllerbefore building from scratch — many vendors ship one that’s just unwired. -
✅ Social login is configured (OAuth app created first), or captured in
Zaj-BACKLOG.mdas a post-launch non-blocker after checking for an existingSocialLoginController.
-
4. Confirm the blog system
Section titled “4. Confirm the blog system”CHECK: Admin → Blog (or Posts / Content).
- Confirm status or capture the blog task.
-
If present — the system is ready; just start writing posts. No setup needed.
-
If absent — adding a blog is a code task; capture it in
Zaj-BACKLOG.md. -
✅ Blog status is confirmed (ready now or captured in
Zaj-BACKLOG.md). Blog content is a growth activity, not a launch blocker.
-
5. Verify the sitemap
Section titled “5. Verify the sitemap”CHECK: visit /sitemap.xml on the domain, and look under Admin → SEO.
- Verify now or capture the sitemap task.
-
If auto-generated — confirm it lists your key pages, then submit it to Google Search Console.
-
If absent — generating a sitemap is a small code/SEO task; capture it in
Zaj-BACKLOG.md. -
✅
/sitemap.xmlis verified and submitted to Search Console, or captured inZaj-BACKLOG.md. Important for SEO but not a launch blocker.
-
Checklist
Section titled “Checklist”Do not mark this step done until every box below is checked.
- 🔀 GA4 wired — built-in field or the 3-file deviation;
curl | grep -creturns ≥1 for the real Measurement ID; Realtime shows traffic; deviation recorded inZaj-CUSTOMIZATIONS.md. - 🔀 Consent dependency queued — GA4 consent-gating (page 6) is in
Zaj-BACKLOG.mdfor production launch. - 🔀 Chat configured — chat widget configured or captured as a post-launch non-blocker in
Zaj-BACKLOG.mdwith owner, trigger, and acceptance criterion. - 🔀 Social login handled — configured or captured as a post-launch non-blocker in
Zaj-BACKLOG.mdwith owner, trigger, and acceptance criterion; existingSocialLoginControllerchecked before any new build. - 🤖 Blog status confirmed — ready now or captured in
Zaj-BACKLOG.mdwith owner, trigger, and acceptance criterion. - 🔀 Sitemap handled —
/sitemap.xmlverified and submitted to Search Console or captured inZaj-BACKLOG.mdwith owner, trigger, and acceptance criterion.
SuperAdmin setup is complete. Continue to Phase 7 · Security & monitoring — harden the app, then wire backups and observability before real users arrive.