Skip to content
prod e051e98
Browse

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.

The growth surfaces — analytics, chat, social login, blog, sitemap. Most are SHOULD / LATER check-first tasks, 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.

Getting the Measurement ID and confirming Realtime are Google-Analytics-dashboard actions a person performs; 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
  1. Edit the blade — in resources/views/layouts/landing.blade.php (the public landing layout — not guest.blade.php, which has Jetstream tenant-context issues, and not authenticated app layouts), insert the standard gtag.js snippet 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.
  2. Edit the config — add 'google' => ['ga4_measurement_id' => env('GA4_MEASUREMENT_ID')] to config/services.php. Vendors almost never touch this convention file, which is what makes it a safe home. Read via config(...), never env(...) from blade/app code.

    • config/services.php exposes services.google.ga4_measurement_id.
  3. Append the env var to shared/.env on 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)
    dep ssh staging "grep -q '^GA4_MEASUREMENT_ID=' ~/domains/<DOMAIN>/deploy/shared/.env \
    || echo 'GA4_MEASUREMENT_ID=\"G-XXXXXXXXXX\"' >> ~/domains/<DOMAIN>/deploy/shared/.env"
    dep ssh staging "cd ~/domains/<DOMAIN>/deploy/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_ID is set in shared/.env and the config cache is rebuilt.
  4. Verify the tag fires.

    Terminal window
    curl -sS https://staging.<DOMAIN>/ | grep -c "$GA4_MEASUREMENT_ID" # expect ≥1 (consent-gated tag may show once in HTML)
    # Expected: ≥1
    • curl | grep -c returns ≥1 for the real Measurement ID; GA4 → Reports → Realtime shows your visit within ~30s. Record the deviation in _CUSTOMIZATIONS.md (file / what added / task / comment marker) — that registry is the shopping list for every vendor-update merge.

CHECK: Admin → Settings for a Crisp / Tawk.to / Intercom / generic widget field.

  1. Configure or defer the widget.
    • If present — paste the provider’s widget code or API key.

    • If absent — add the widget script to the layout footer (a small code task; defer).

    • ✅ The widget is configured, or deferred. Priority: LATER — not needed at launch; enable it when you have users to support.

CHECK: Admin → Config → Login (or Social Auth / OAuth Settings). Creating the OAuth app in each provider’s console and entering the client ID/secret are person-driven.

  1. Configure or defer social login.
    • 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 SocialLoginController before building from scratch — many vendors ship one that’s just unwired.

    • ✅ Social login is configured (OAuth app created first), or deferred after checking for an existing SocialLoginController. Priority: LATER.

CHECK: Admin → Blog (or Posts / Content).

  1. Confirm status or defer.
    • If present — the system is ready; just start writing posts. No setup needed.

    • If absent — adding a blog is a code task; defer.

    • ✅ Blog status is confirmed (ready or deferred). Blog content is a growth activity, not a launch blocker.

CHECK: visit /sitemap.xml on the domain, and look under Admin → SEO.

  1. Verify or defer the sitemap.
    • 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; defer.

    • /sitemap.xml is verified and submitted to Search Console, or deferred. Important for SEO but not a launch blocker.

Do not mark this step done until every box below is checked.

  • 🔀 GA4 wired — built-in field or the 3-file deviation; curl | grep -c returns ≥1 for the real Measurement ID; Realtime shows traffic; deviation recorded in _CUSTOMIZATIONS.md.
  • 🔀 Consent dependency queued — GA4 consent-gating (page 6) is queued for production launch.
  • 👤 Chat configured — chat widget configured or deferred (LATER).
  • 👤 Social login handled — configured or deferred — existing SocialLoginController checked before any new build.
  • 🤖 Blog status confirmed — ready / deferred.
  • 👤 Sitemap handled/sitemap.xml verified and submitted to Search Console or deferred.

SuperAdmin setup is complete. Continue to Phase 7 · Security & monitoring — harden the app, then wire backups and observability before real users arrive.