Skip to content
prod 352bb92
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.

Steps at a glance:

  1. 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.
  2. Configure the chat widget — CHECK: Admin → Settings for a Crisp / Tawk.to / Intercom / generic widget field.
  3. Configure social login — CHECK: Admin → Config → Login (or Social 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.
  4. Confirm the blog system — CHECK: Admin → Blog (or Posts / Content).
  5. Verify the sitemap — CHECK: visit /sitemap.xml on the domain, and look under Admin → SEO.

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.

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
  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)
    DEPLOY_TARGET="<selected-nonproduction-deploy-target>" # from Zaj-PROJECT.md, e.g. staging-primary/qa/uat/client-demo
    DEPLOY_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_ID is set in shared/.env and the config cache is rebuilt.
  4. 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 -c returns ≥1 for the real Measurement ID; GA4 → Reports → Realtime shows your visit within ~30s. Record the deviation in Zaj-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 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.md as a post-launch non-blocker.

    • ✅ The widget is configured, or captured in Zaj-BACKLOG.md as a post-launch non-blocker with owner, trigger, and acceptance criterion.

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.

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

    • ✅ Social login is configured (OAuth app created first), or captured in Zaj-BACKLOG.md as a post-launch non-blocker after checking for an existing SocialLoginController.

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

  1. 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.

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

  1. 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.xml is verified and submitted to Search Console, or captured in Zaj-BACKLOG.md. 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 Zaj-CUSTOMIZATIONS.md.
  • 🔀 Consent dependency queued — GA4 consent-gating (page 6) is in Zaj-BACKLOG.md for production launch.
  • 🔀 Chat configured — chat widget configured or captured as a post-launch non-blocker in Zaj-BACKLOG.md with owner, trigger, and acceptance criterion.
  • 🔀 Social login handled — configured or captured as a post-launch non-blocker in Zaj-BACKLOG.md with owner, trigger, and acceptance criterion; existing SocialLoginController checked before any new build.
  • 🤖 Blog status confirmed — ready now or captured in Zaj-BACKLOG.md with owner, trigger, and acceptance criterion.
  • 🔀 Sitemap handled/sitemap.xml verified and submitted to Search Console or captured in Zaj-BACKLOG.md with 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.