Analytics
התג באתר, התחברות אחת, ודוחות GA4 מהטרמינל. כולל Google Ads ומדידה של דפי נחיתה.
#ga-events
כל האירועים ואיך הם נמדדים
עודכן לאחרונה: 7.11.2026
רק באתר של הסטודיו: בפרודקשן, על הדומיינים של ANALYTICS.hosts, ולא בדמואים, בדוקס וב-beta (הקליקים בודקים את הנתיב). קליק נתפס לפי ה-href של הלינק, לא לפי קלאס או קומפוננטה: כפתור שמשנה צורה ממשיך להיספר כל עוד ה-href שלו אותו href.
קליקים (listener אחד, capture)
| אירוע | לאן | מה מפעיל (סלקטור / קוד) | איך | הקובץ |
|---|---|---|---|---|
| Click to call (ראשית) | Ads · Meta Contact {call} | a[href^="tel:" + STUDIO.phone] | קליק, selector | SiteTags.tsx · ADS_CLICKS |
| Whatsapp (ראשית) | Ads · Meta Contact {whatsapp} | a[href^=STUDIO.whatsapp] | קליק, selector | SiteTags.tsx · ADS_CLICKS |
| Visit contact us page (משנית) | Ads · Meta Contact {contact} | a[href="/contact"], a[href$="#footer-lead"], a[href$="#lead"] | קליק, selector | SiteTags.tsx · ADS_CLICKS |
| המרה של אלמנט אחד | Ads | [data-ads="AW-…/…"] | קליק, אטריביוט | SiteTags.tsx |
המשפך: cta_click (target) ← generate_lead (form_name). וואטסאפ וטלפון לבד (contact): הקליק הוא הליד. ליד מהטופס בתחתית בלי קליק לפניו: מישהו שגלל ומילא לבד. עד 7.11 היו כאן lead_intent, lp_intent ו-sticky_banner_click / close; ההיסטוריה שלהם נשארת ב-GA4 בשמות הישנים.
אירועים ב-GA4
| אירוע | לאן | מה מפעיל (סלקטור / קוד) | איך | הקובץ |
|---|---|---|---|---|
| cta_click {location, target} | GA4 | קליק על a[href="/contact"] (target: popup) או a[href$="#lead"], a[href$="#footer-lead"] (target: footer-form). location: ה-data-cta של הכפתור, אחרת navbar / footer / page | קליק, לפי href | SiteTags.tsx |
| contact {method, location} | GA4 (וב-Meta: Contact) | קליק על tel: (method: call) או wa.me / whatsapp (method: whatsapp). השם של Meta לזה | קליק, לפי href | SiteTags.tsx |
| generate_lead {form_name} | GA4 · Ads (ANALYTICS.adsConversion) · Meta Lead | trackLead(leadName) אחרי שהשרת אישר. form_name: contact, contact-popup, footer, landing | קוד, אחרי שליחה | lib/analytics.ts · LeadForm |
| cta_dismiss {location} | GA4 | סגירת הפס הצף בדפי השירות (location: sticky_banner). מול cta_click באותו location: מפריע או עוזר | קוד, onClose | ServiceLead.tsx |
| search_select | GA4 | בחירת תוצאה בחיפוש של הדוקס | קוד | app/docs/algolia/AlgoliaSearchBox.tsx |
צפיות בדף
| אירוע | לאן | מה מפעיל (סלקטור / קוד) | איך | הקובץ |
|---|---|---|---|---|
| page_view | GA4 | טעינה, ו-GA4 לבד במעבר דף של Next | אוטומטי (gtag config) | SiteTags.tsx |
| PageView | Meta | טעינה, וכל pushState / popstate (מעבר דף של Next) | קוד, עוטף את history.pushState | SiteTags.tsx · metaPixel |
#ga-tag
התג באתר
המזהים של התגים (GA4_ID, ADS_ID) הם const בראשcomponents/studio/SiteTags.tsx; propertyId ו-hosts ב-ANALYTICS ב-lib/site.ts. לקוח חדש = להחליף את כל ה-const בראש SiteTags.tsx, כולל ADS_CLICKS (ההמרות על קליק), ואת כל השדות ב-ANALYTICS, כולל adsConversion ו-searchConsoleSite. אחרת האתר של הלקוח שולח המרות לחשבון ה-Ads של הסטודיו. הקובץ טוען את gtag רק ב-production, רק אחרי שהעמוד נטען, כי הסקריפט שוקל 176KB, ורק כשהכתובת ב-hosts. מעברי עמוד בצד הלקוח נספרים לבד, דרך ה-enhanced measurement של GA.
התג של Google Ads (ADS_ID, ה-AW- של החשבון) נטען על אותו gtag. הוא מודד בלבד כל עוד ADS_REMARKETING כבוי; להדליק אותו = גם לעדכן את הפסקה על פרסום ב-/privacy.
SiteTags.tsx הוא ה-Tag Manager של האתר, בקוד: כל סקריפט חיצוני גר שם ורק שם, מאחורי אותה בדיקה (production, hosts, ?notrack). תג חדש (Hotjar, Clarity, פיקסל) = const של המזהה בראש הקובץ + פונקציה אחת. לקוח שעובד עם Tag Manager אמיתי: הקובץ מוחלף בסניפט של GTM, ושאר הקוד לא משתנה.
לא לספור את עצמנו
קוד שלנו, לא תכונה של GA4פותחים פעם אחת את הקישור בכל דפדפן שלנו, כולל בנייד ובדפדפן שנפתח מתוך וואטסאפ. הוא שומר סימון בדפדפן, ו-components/studio/SiteTags.tsx לא טוען בו את GA יותר. זה מחליף סינון IP בנייד, כי בסלולר אין IP קבוע. מחיקת נתוני גלישה מוחקת גם את הסימון.
?track מבטל את הסימון. בדיקה: לגלוש קצת, ואז npm run ga -- realtime, והדפדפן המסומן לא מופיע.
// components/studio/SiteTags.tsx
const GA4_ID = 'G-XXXXXXXXXX' // Admin > Data streams > the web stream
const ADS_ID = 'AW-XXXXXXXXXX' // Google Ads tag ('' = off)
// lib/site.ts
export const ANALYTICS = {
propertyId: '123456789', // Admin > Property settings, or: npm run ga -- accounts
hosts: ['www.client.co.il', 'client.co.il'], // the tags load nowhere else
} as const;#ga-setup
הגישה (פעם אחת)
משתמש רובוט אחד (service account) קורא גם את GA וגם את Ads. הוא נוצר ב-Google Cloud, והמייל שלו מתווסף בממשק של GA (Viewer) ושל Ads (Standard) כמו כל משתמש. אין קישור התחברות, אין passkey, ואין תוקף. המפתח שלו יושב ב-~/.google-marketing/service-account.json, מחוץ לריפו. לקוח חדש מוסיף את אותו מייל לחשבונות שלו, וה-propertyId נכנס ל-lib/site.ts בריפו שלו. בלי פרויקט חדש.
- בתפריט ☰, הכוכב ☆ ליד Google Auth Platform וליד APIs & Services מצמיד אותם למעלה. אחרת הם קבורים ב-View all products.
- ברשימת Enabled APIs יש כ-20 API שגוגל מפעיל לבד בכל פרויקט (BigQuery, Storage, Logging). לא צריך אותם ולא מזיקים: בלי Billing הם לא יכולים לעלות כסף. Filter: Analytics מראה את שני שלנו.
באנליטיקס
- 01ניהולניהול הגישה לנכס+המייל של הרובוטViewer
המייל נגמר ב-iam.gserviceaccount.com. עד שהוא נוסף, ga.mjs נופל להתחברות הישנה.
- 02ניהולקישורים למוצריםGoogle Ads linksקישור
בלעדיו אין שם קמפיין, עלות או קליקים. הנתונים נכנסים מיום הקישור והלאה.
- 03ניהולאירועים
לסמן כ-key event את מה שנחשב ליד: קליק לוואטסאפ, שליחת טופס.
- 04ניהולמקורות נתוניםהזרםקביעת ההגדרות של התגהצגת הכולהגדרת תנועת גולשים פנימיתCreate
בתנאי: IP address equals, והערך הוא ה-IP שלך. הקישור באותו מסך מראה אותו.
החלופה הישנה, OAuth: npm run google:auth -- --account <email> פותח את מסך ההסכמה של גוגל. הסקריפטים משתמשים בה רק איפה שהרובוט עוד לא נוסף. האפליקציה ב-Testing, אז התוקף 7 ימים.
#ga-reports
הדוחות
scripts/google/ga.mjs. ברירות מחדל: 28 ימים, 20 שורות. הדגלים, לכל דוח:
--days N the range, default 28
--limit N rows, default 20
--path /x pages / landing: only paths that begin with /x
--property <id> another property (a client); default: lib/site.ts
--json the raw API responsenpm run ga -- accountsכל הנכסים שהחשבון רואה, עם ה-ID. כך מוצאים את ה-propertyId של לקוח.
npm run ga -- overview --days 7משתמשים, סשנים, מעורבות, key events: לפי יום ושורת סיכום.
npm run ga -- pagesהעמודים הנצפים ביותר, עם משתמשים וזמן מעורבות.
npm run ga -- landing --path /landing_pages/דפי נחיתה: סשנים, מעורבות ו-key events לכל דף כניסה.
npm run ga -- sourcesמקור / מדיום: גוגל אורגני, ישיר, פייסבוק, cpc.
npm run ga -- campaignsקמפיינים לפי utm_campaign, עם המקור.
npm run ga -- adsקמפיינים של Google Ads: חשיפות, קליקים, עלות, key events. רק אחרי קישור Ads ל-GA.
npm run ga -- eventsכל האירועים וכמה פעמים קרו. כאן רואים אם generate_lead (טופס) ו-click (וואטסאפ) נמדדים.
npm run ga -- devicesמובייל / דסקטופ / טאבלט.
npm run ga -- realtimeמי באתר ב-30 הדקות האחרונות. בדיקה מהירה שהתג עובד או שהסינון הפנימי תופס.
npm run ga -- raw '{…}'כל גוף של runReport כמו שהוא, לשאלה שאין לה דוח מוכן.
#ga-ads
Google Ads
שתי דרכים, ושתיהן מחוברות. Ads מקושר ל-GA (צעד 02 באנליטיקס), אז עלות וקליקים לפי קמפיין נכנסים ל-GA. וה-API של Ads ישירות, דרך הרובוט: הטקסטים של המודעות, ההמרות, מונחי החיפוש. התקנה במדריך, ושם גם מה לא צריך (MCC, developer token).
npm run ga -- ads --days 30 # through GA: campaign, cost, clicks, leads
npm run ads -- campaigns # status, budget, impressions, clicks, cost
npm run ads -- ads # every headline and description, with approval
npm run ads -- conversions # conversion actions: primary or not
npm run ads -- search-terms # what people typed (Search campaigns)
npm run ads -- raw "<GAQL>" # any Google Ads query
npm run ads -- keyword "…" --group "…" # a keyword on an ad group (--negative for a negative)
npm run ads -- adgroup "…" # a new ad group in the Search campaign
npm run ads -- ad --group "…" --url … --h … --d … # a responsive search ad
npm run gsc -- queries # Search Console: organic searches, position
npm run gsc -- pages --page /landing_pages # the same per page
npm run gsc -- sitemaps # the sitemap: submitted, read, indexed
npm run gsc -- sitemap-submit # (re)submit /sitemap.xml
npm run gsc -- inspect --sitemap /guides # in Google's index or not, and why, per page
npm run rank # Google Israel position for rank/keywords.txt (Serper), shown at /beta/rankכלל: לא נוגעים בתקציב, בבידים או בהפעלה של קמפיין, בשום דרך. טקסטים, מילות מפתח ושליליות רק אחרי שהמשתמש ראה את השינוי ואישר. נעילה מוחלטת: הרובוט על Read only ב-Ads.
#ga-form
הטופס באתר
הכלל: כל טופס שמביא ליד, בכל אתר שנבנה מהתבנית, קורא ל-trackLead() אחרי שליחה מוצלחת. גם טופס של דף נחיתה, וגם טופס של דמו ביום שהוא הופך לאתר של לקוח.
שלושה טפסים שולחים לאותו מקום: עמוד צור קשר, הטופס בפוטר והטופס בעמודי השירותים. הם שולחים ל-app/api/contact/route.ts, שם הקוד שולח מייל דרך Resend. אחרי שליחה מוצלחת כל טופס קורא ל-trackLead()מ-lib/analytics.ts, וזה האירוע generate_lead ב-GA. עם ANALYTICS.adsConversion הוא יורה גם את ההמרה של Ads.
המרות על לחיצה (טלפון tel:, וואטסאפ wa.me, "דברו איתנו") הן הטבלהADS_CLICKS בראש SiteTags.tsx: שורה = selector של לינק + ה-send_to מהקוד של ההמרה ב-Ads. listener אחד על הדף, אז כל לינק שתואם נספר, גם כזה שיתווסף מחר. נספרת הלחיצה, לא שיחה או הודעה שנשלחה.
הבאנר הצף בעמודי השירותים: הלחיצה עליו היא cta_click {location: sticky_banner} (לפי ה-href, ב-SiteTags), והסגירה ב-X היא cta_dismiss מאותו location. אם יש יותר סגירות מלחיצות, הוא בעיקר מציק. שם אחד עם פרמטר (7.11): צריך לרשום פעם אחת את location, target ו-method כ-custom dimensions ב-GA4, ואז כל מיקום חדש נראה בדוחות לבד.
const ADS_CLICKS = [
{ selector: 'a[href^="tel:"]', sendTo: 'AW-…/…' }, // phone, primary
{ selector: 'a[href*="wa.me/"]', sendTo: 'AW-…/…' }, // WhatsApp, primary
{ selector: 'a[href="/contact"],…', sendTo: 'AW-…/…' }, // "דברו איתנו", secondary
]המייל: Resend
RESEND_API_KEY חובה, ב-.env.local וגם ב-Vercel (Settings, Environment Variables). בלעדיו הטופס באתר החי מחזיר שגיאה. CONTACT_TO_EMAIL ו-CONTACT_FROM_EMAIL אופציונליים: בלעדיהם הנמען הוא STUDIO.email מ-lib/site.ts. בלי דומיין מאומת ב-Resend המייל יוצא מ-onboarding@resend.dev ומגיע רק לבעל החשבון, וזה מספיק להתראה ללקוח.
ההמרה: generate_lead
ה-enhanced measurement של GA לא קולט את הטפסים שלנו, כי הם שולחים בלי לטעון עמוד (בדוח events יש form_start ואין form_submit). לכן האירוע נשלח מהקוד, ורק אחרי שהמייל באמת יצא. הפרמטר form_name אומר מאיזה טופס: contact, footer או service.
לסמן כהמרה
האירוע מופיע ב-GA רק אחרי שליחה אמיתית באתר החי, עד 24 שעות. אז: צעד 03 באנליטיקס (למעלה), כוכב ליד generate_lead. ב-Google Ads ההמרה של הטופס נשלחת ישירות מהקוד (adsConversion ב-lib/site.ts). ייבוא של generate_lead מ-Google Analytics 4 רק כש-adsConversion ריק, ואף פעם לא שתיהן כ-primary: אחרת כל פנייה נספרת פעמיים.
לקוח חדש
הטפסים וה-route מגיעים עם התבנית, והקריאה ל-trackLead כבר בתוכם. מגדירים את משתני הסביבה של Resend ואת ANALYTICS ב-lib/site.ts. טופס חדש שנבנה לדף נחיתה קורא ל-trackLead עם שם משלו, למשל trackLead('lp-dentist').
בדיקה: שולחים טופס באתר החי, ואז npm run ga -- realtime או npm run ga -- events --days 1. בדפדפן עם ?notrack האירוע לא יישלח, וזה מכוון.
#ga-landing
דפי נחיתה
דף נחיתה נמדד בשלושה דברים. הקוד כבר בתבנית, השאר הגדרות ב-GA וב-Ads:
מאיפה הגיעו
Google Ads מקושר מסמן לבד (gclid). כל מקור אחר מקבל UTM בקישור, למשל ?utm_source=facebook&utm_medium=paid&utm_campaign=lp-dentist. שם הקמפיין באותיות קטנות ובמקפים, אחרת GA סופר כמה גרסאות שלו כקמפיינים שונים.
מה עשו
קליק יוצא, כולל וואטסאפ וטלפון, נמדד לבד כ-click. שליחת טופס נמדדת כ-generate_lead מהקוד שלנו (הטופס, למעלה). את שניהם מסמנים כ-key event, ורק אז הם נספרים כהמרה ו-Ads יכול לייבא אותם.
איך הדף עובד
npm run ga -- landing --path /landing_pages/: סשנים, מעורבות ו-key events לכל דף, ו-campaigns לאותו דבר לפי קמפיין.
הדף עצמו
דף נחיתה של לקוח יושב על הדומיין של הלקוח, אז הוא בתוך ה-hosts שלו ונספר. דף נחיתה על preview לא נספר, וזה מכוון: בודקים עליו, לא מפרסמים אותו.
#ga-traps
מלכודות
CLOUD_PROJECT_NOT_APPROVED_FOR_PRODUCTION
הרובוט נכנס, אבל הפרויקט ב-Cloud ברמת Test: רק חשבונות בדיקה. Google Ads API > Access levels > Manage > Apply for access (Explorer). מאושר תוך דקות.
NOT_ADS_USER
המייל של הרובוט עוד לא משתמש בחשבון ה-Ads. Admin > Access and security > + . בלי אישור, תופס מיד.
SERVICE_DISABLED
אחד משני ה-API לא מופעל בפרויקט. השגיאה כוללת את שם ה-API ואת הקישור להפעלה. Admin עובד ו-Data לא (או הפוך) זה המצב הנפוץ.
הכניסות שלנו נספרות
התג נטען רק על ANALYTICS.hosts, אז גרסאות preview לא נספרות. הגלישה שלנו באתר האמיתי כן, עד שמגדירים IP (צעד 04 באנליטיקס) ופותחים ?notrack בנייד. בדוח sources זה נראה ככה: google / organic.
החשבון רואה גם לקוחות
בהתחברות הישנה accounts מחזיר כל נכס שהמשתמש רואה, כולל לקוחות. הרובוט רואה רק נכסים שהוסיפו אותו אליהם. ברירת המחדל היא propertyId מ-lib/site.ts; לקוח אחר רק עם --property מפורש.
"Add domain for cross-domain measurement"
גוגל ראה את התג על דומיין אחר, כמעט תמיד preview או כתובת vercel.app ישנה. Dismiss, לא Add. מדידה בין דומיינים היא לשני דומיינים אמיתיים של אותו עסק.
